CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
202 lines
7.3 KiB
C++
202 lines
7.3 KiB
C++
//===- ExecutorBase.cpp - Non-visitor methods of InstExecutor -------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file implements non-visitor methods of InstExecutor for code reuse.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "ExecutorBase.h"
|
|
|
|
namespace llvm::ubi {
|
|
Frame::Frame(Function &F, CallBase *CallSite, Frame *LastFrame,
|
|
ArrayRef<AnyValue> Args, AnyValue &RetVal,
|
|
const TargetLibraryInfoImpl &TLIImpl)
|
|
: Func(F), LastFrame(LastFrame), CallSite(CallSite), Args(Args),
|
|
RetVal(RetVal), TLI(TLIImpl, &F) {
|
|
assert((Args.size() == F.arg_size() ||
|
|
(F.isVarArg() && Args.size() >= F.arg_size())) &&
|
|
"Expected enough arguments to call the function.");
|
|
BB = &Func.getEntryBlock();
|
|
PC = BB->begin();
|
|
for (Argument &Arg : F.args())
|
|
ValueMap[&Arg] = Args[Arg.getArgNo()];
|
|
}
|
|
|
|
DiagnosticReporter ExecutorBase::reportImmediateUB() {
|
|
return DiagnosticReporter(*this, DiagnosticKind::ImmediateUB);
|
|
}
|
|
|
|
DiagnosticReporter ExecutorBase::reportError() {
|
|
return DiagnosticReporter(*this, DiagnosticKind::Error);
|
|
}
|
|
|
|
void ExecutorBase::reportImmediateUBString(StringRef Msg) {
|
|
// Check if we have already reported an immediate UB.
|
|
if (hasProgramExited())
|
|
return;
|
|
dumpStackTrace();
|
|
requestProgramExit(ProgramExitInfo::ProgramExitKind::Failed);
|
|
Handler.onImmediateUB(Msg);
|
|
}
|
|
|
|
void ExecutorBase::reportErrorString(StringRef Msg) {
|
|
// Check if we have already reported an error message.
|
|
if (hasProgramExited())
|
|
return;
|
|
dumpStackTrace();
|
|
requestProgramExit(ProgramExitInfo::ProgramExitKind::Failed);
|
|
Handler.onError(Msg);
|
|
}
|
|
|
|
std::optional<uint64_t> ExecutorBase::verifyMemAccess(const MemoryObject &MO,
|
|
const APInt &Address,
|
|
uint64_t AccessSize,
|
|
Align Alignment,
|
|
bool IsStore) {
|
|
// Loading from a stack object outside its lifetime is not undefined
|
|
// behavior and returns a poison value instead. Storing to it is still
|
|
// undefined behavior.
|
|
if (IsStore ? MO.getState() != MemoryObjectState::Alive
|
|
: MO.getState() == MemoryObjectState::Freed) {
|
|
reportImmediateUB() << "Try to access a dead memory object at address 0x"
|
|
<< Twine::utohexstr(Address.getZExtValue()) << ".";
|
|
return std::nullopt;
|
|
}
|
|
|
|
if (Address.countr_zero() < Log2(Alignment)) {
|
|
reportImmediateUB() << "Misaligned memory access. Address: 0x"
|
|
<< Twine::utohexstr(Address.getZExtValue())
|
|
<< ", Required alignment: " << Alignment.value() << ".";
|
|
return std::nullopt;
|
|
}
|
|
|
|
if (AccessSize > MO.getSize() || Address.ult(MO.getAddress())) {
|
|
reportImmediateUB() << "Memory access is out of bounds. Accessed size: "
|
|
<< AccessSize << ", Address: 0x"
|
|
<< Twine::utohexstr(Address.getZExtValue())
|
|
<< ", Object base: 0x"
|
|
<< Twine::utohexstr(MO.getAddress())
|
|
<< ", Object size: " << MO.getSize() << ".";
|
|
return std::nullopt;
|
|
}
|
|
|
|
APInt Offset = Address - MO.getAddress();
|
|
|
|
if (Offset.ugt(MO.getSize() - AccessSize)) {
|
|
reportImmediateUB() << "Memory access is out of bounds. Accessed size: "
|
|
<< AccessSize << ", Address: 0x"
|
|
<< Twine::utohexstr(Address.getZExtValue())
|
|
<< ", Object base: 0x"
|
|
<< Twine::utohexstr(MO.getAddress())
|
|
<< ", Object size: " << MO.getSize() << ".";
|
|
return std::nullopt;
|
|
}
|
|
|
|
return Offset.getZExtValue();
|
|
}
|
|
|
|
AnyValue ExecutorBase::load(const AnyValue &Ptr, Align Alignment, Type *ValTy,
|
|
bool NoUndef) {
|
|
if (Ptr.isPoison()) {
|
|
reportImmediateUB() << "Invalid memory access with a poison pointer.";
|
|
return AnyValue::getPoisonValue(Ctx, ValTy);
|
|
}
|
|
auto &PtrVal = Ptr.asPointer();
|
|
auto *MO = PtrVal.getMemoryObject();
|
|
if (!MO) {
|
|
reportImmediateUB()
|
|
<< "Invalid memory access via a pointer with nullary provenance.";
|
|
return AnyValue::getPoisonValue(Ctx, ValTy);
|
|
}
|
|
// TODO: pointer capability check
|
|
if (auto Offset =
|
|
verifyMemAccess(*MO, PtrVal.address(),
|
|
Ctx.getEffectiveTypeStoreSize(ValTy), Alignment,
|
|
/*IsStore=*/false)) {
|
|
// Load from a dead stack object yields poison value.
|
|
if (MO->getState() == MemoryObjectState::Dead)
|
|
return AnyValue::getPoisonValue(Ctx, ValTy);
|
|
|
|
bool ContainsUndefinedBits = false;
|
|
AnyValue Res = Ctx.load(*MO, *Offset, ValTy,
|
|
NoUndef ? &ContainsUndefinedBits : nullptr);
|
|
if (NoUndef && ContainsUndefinedBits)
|
|
reportImmediateUB() << "The value loaded contains undefined bits.";
|
|
return Res;
|
|
}
|
|
return AnyValue::getPoisonValue(Ctx, ValTy);
|
|
}
|
|
|
|
void ExecutorBase::store(const AnyValue &Ptr, Align Alignment,
|
|
const AnyValue &Val, Type *ValTy) {
|
|
if (Ptr.isPoison()) {
|
|
reportImmediateUB() << "Invalid memory access with a poison pointer.";
|
|
return;
|
|
}
|
|
auto &PtrVal = Ptr.asPointer();
|
|
auto *MO = PtrVal.getMemoryObject();
|
|
if (!MO) {
|
|
reportImmediateUB()
|
|
<< "Invalid memory access via a pointer with nullary provenance.";
|
|
return;
|
|
}
|
|
if (MO->isConstant()) {
|
|
reportImmediateUB() << "Try to write to a constant memory object: "
|
|
<< PtrVal << ".";
|
|
return;
|
|
}
|
|
// TODO: pointer capability check
|
|
if (auto Offset =
|
|
verifyMemAccess(*MO, PtrVal.address(),
|
|
Ctx.getEffectiveTypeStoreSize(ValTy), Alignment,
|
|
/*IsStore=*/true))
|
|
Ctx.store(*MO, *Offset, Val, ValTy);
|
|
}
|
|
|
|
void ExecutorBase::requestProgramExit(ProgramExitInfo::ProgramExitKind Kind,
|
|
uint64_t ExitCode) {
|
|
ExitInfo.emplace(Kind, ExitCode);
|
|
Handler.onProgramExit(*ExitInfo);
|
|
}
|
|
|
|
void ExecutorBase::setFailed() {
|
|
requestProgramExit(ProgramExitInfo::ProgramExitKind::Failed);
|
|
}
|
|
|
|
bool ExecutorBase::hasProgramExited() const { return ExitInfo.has_value(); }
|
|
|
|
std::optional<ProgramExitInfo> ExecutorBase::getExitInfo() const {
|
|
return ExitInfo;
|
|
}
|
|
|
|
unsigned ExecutorBase::getIntSize() const {
|
|
return CurrentFrame->TLI.getIntSize();
|
|
}
|
|
|
|
void ExecutorBase::dumpStackTrace() const {
|
|
errs() << "Stacktrace:\n";
|
|
const Frame *TheFrame = CurrentFrame;
|
|
unsigned Index = 0;
|
|
const AsmParserContext *ParserContext = Ctx.getParserContext();
|
|
StringRef ModuleFileName = Ctx.getModule().getModuleIdentifier();
|
|
while (TheFrame != nullptr) {
|
|
if (TheFrame->BB) {
|
|
Instruction &Inst = *TheFrame->PC;
|
|
errs() << "#" << Index++ << " " << Inst << " at ";
|
|
Inst.getFunction()->printAsOperand(errs(), /*PrintType=*/false);
|
|
if (ParserContext) {
|
|
if (auto Loc = ParserContext->getInstructionOrArgumentLocation(&Inst))
|
|
errs() << ' ' << ModuleFileName << ':' << Loc->Start.Line + 1;
|
|
}
|
|
errs() << "\n";
|
|
}
|
|
TheFrame = TheFrame->LastFrame;
|
|
}
|
|
}
|
|
} // namespace llvm::ubi
|