llvm-project/llvm/tools/llubi/lib/ExecutorBase.cpp
Zhige Chen 54e5803d02
[llubi] Extract reusable methods of InstExecutor into ExecutorBase (#186976)
This PR extracts the non-visitor methods of class InstExecutor into a
separate class ExecutorAPI. This reorganization allows library functions
(and any future extensions) to reuse the functionality of InstExecutor
without introducing cyclic dependencies.

See also #185645 and #185817.
2026-04-02 02:06:27 +08:00

128 lines
4.4 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()];
}
void ExecutorBase::reportImmediateUB(StringRef Msg) {
// Check if we have already reported an immediate UB.
if (!Status)
return;
Status = false;
// TODO: Provide stack trace information.
Handler.onImmediateUB(Msg);
}
void ExecutorBase::reportError(StringRef Msg) {
// Check if we have already reported an error message.
if (!Status)
return;
Status = false;
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.");
return std::nullopt;
}
if (Address.countr_zero() < Log2(Alignment)) {
reportImmediateUB("Misaligned memory access.");
return std::nullopt;
}
if (AccessSize > MO.getSize() || Address.ult(MO.getAddress())) {
reportImmediateUB("Memory access is out of bounds.");
return std::nullopt;
}
APInt Offset = Address - MO.getAddress();
if (Offset.ugt(MO.getSize() - AccessSize)) {
reportImmediateUB("Memory access is out of bounds.");
return std::nullopt;
}
return Offset.getZExtValue();
}
AnyValue ExecutorBase::load(const AnyValue &Ptr, Align Alignment, Type *ValTy) {
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);
return Ctx.load(*MO, *Offset, ValTy);
}
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;
}
// TODO: pointer capability check
if (auto Offset =
verifyMemAccess(*MO, PtrVal.address(),
Ctx.getEffectiveTypeStoreSize(ValTy), Alignment,
/*IsStore=*/true))
Ctx.store(*MO, *Offset, Val, ValTy);
}
} // namespace llvm::ubi