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
1743 lines
63 KiB
C++
1743 lines
63 KiB
C++
//===-- Instrumentor.cpp - Highly configurable instrumentation pass -------===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// The implementation of the Instrumentor, a highly configurable instrumentation
|
|
// pass.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/Transforms/IPO/Instrumentor.h"
|
|
#include "llvm/Transforms/IPO/InstrumentorConfigFile.h"
|
|
#include "llvm/Transforms/IPO/InstrumentorStubPrinter.h"
|
|
|
|
#include "llvm/ADT/PostOrderIterator.h"
|
|
#include "llvm/ADT/STLExtras.h"
|
|
#include "llvm/ADT/SmallPtrSet.h"
|
|
#include "llvm/ADT/SmallVector.h"
|
|
#include "llvm/ADT/StringExtras.h"
|
|
#include "llvm/ADT/StringMap.h"
|
|
#include "llvm/ADT/iterator.h"
|
|
#include "llvm/Analysis/ValueTracking.h"
|
|
#include "llvm/Demangle/Demangle.h"
|
|
#include "llvm/IR/Constant.h"
|
|
#include "llvm/IR/Constants.h"
|
|
#include "llvm/IR/DataLayout.h"
|
|
#include "llvm/IR/DebugInfoMetadata.h"
|
|
#include "llvm/IR/DiagnosticInfo.h"
|
|
#include "llvm/IR/Function.h"
|
|
#include "llvm/IR/IRBuilder.h"
|
|
#include "llvm/IR/InstrTypes.h"
|
|
#include "llvm/IR/Instruction.h"
|
|
#include "llvm/IR/Instructions.h"
|
|
#include "llvm/IR/IntrinsicInst.h"
|
|
#include "llvm/IR/Intrinsics.h"
|
|
#include "llvm/IR/LLVMContext.h"
|
|
#include "llvm/IR/Metadata.h"
|
|
#include "llvm/IR/Module.h"
|
|
#include "llvm/IR/PassManager.h"
|
|
#include "llvm/IR/Verifier.h"
|
|
#include "llvm/IRReader/IRReader.h"
|
|
#include "llvm/Support/CommandLine.h"
|
|
#include "llvm/Support/ErrorHandling.h"
|
|
#include "llvm/Support/Regex.h"
|
|
#include "llvm/Support/VirtualFileSystem.h"
|
|
#include "llvm/Transforms/IPO/InstrumentorUtils.h"
|
|
#include "llvm/Transforms/Utils/ModuleUtils.h"
|
|
|
|
#include <cassert>
|
|
#include <cstdint>
|
|
#include <functional>
|
|
#include <iterator>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <system_error>
|
|
#include <type_traits>
|
|
|
|
using namespace llvm;
|
|
using namespace llvm::instrumentor;
|
|
|
|
#define DEBUG_TYPE "instrumentor"
|
|
|
|
namespace {
|
|
|
|
/// The user option to specify an output JSON file to write the configuration.
|
|
static cl::opt<std::string> OutputConfigFile(
|
|
"instrumentor-write-config-file",
|
|
cl::desc(
|
|
"Write the instrumentor configuration into the specified JSON file"),
|
|
cl::init(""));
|
|
|
|
/// The user option to specify input JSON files to read the configuration from.
|
|
static cl::list<std::string>
|
|
ConfigFiles("instrumentor-read-config-files",
|
|
cl::desc("Read the instrumentor configuration from the "
|
|
"specified JSON files (comma separated)"),
|
|
cl::ZeroOrMore, cl::CommaSeparated);
|
|
|
|
/// The user option to specify an input file to read the configuration file
|
|
/// paths from.
|
|
static cl::opt<std::string> ConfigPathsFile(
|
|
"instrumentor-read-config-paths-file",
|
|
cl::desc("Read the instrumentor configuration file "
|
|
"paths from the specified file (newline separated)"),
|
|
cl::init(""));
|
|
|
|
/// Set the debug location, if not set, after changing the insertion point of
|
|
/// the IR builder \p IRB.
|
|
template <typename IRBuilderTy> void ensureDbgLoc(IRBuilderTy &IRB) {
|
|
if (IRB.getCurrentDebugLocation())
|
|
return;
|
|
auto *BB = IRB.GetInsertBlock();
|
|
if (auto *SP = BB->getParent()->getSubprogram())
|
|
IRB.SetCurrentDebugLocation(DILocation::get(BB->getContext(), 0, 0, SP));
|
|
}
|
|
|
|
/// Attempt to cast \p V to type \p Ty using only bit-preserving casts.
|
|
/// This ensures that floating-point values are converted via bitcast (not
|
|
/// fptosi/fptoui) to preserve their exact bit representation.
|
|
template <typename IRBTy>
|
|
Value *tryToCast(IRBTy &IRB, Value *V, Type *Ty, const DataLayout &DL,
|
|
bool AllowTruncate = false) {
|
|
if (!V)
|
|
return Constant::getAllOnesValue(Ty);
|
|
Type *VTy = V->getType();
|
|
if (VTy == Ty)
|
|
return V;
|
|
if (VTy->isAggregateType())
|
|
return V;
|
|
TypeSize RequestedSize = DL.getTypeSizeInBits(Ty);
|
|
TypeSize ValueSize = DL.getTypeSizeInBits(VTy);
|
|
bool ShouldTruncate = RequestedSize < ValueSize;
|
|
if (ShouldTruncate && !AllowTruncate)
|
|
return V;
|
|
if (ShouldTruncate && AllowTruncate) {
|
|
// First convert to integer of the same size if needed.
|
|
Value *IntV = V;
|
|
if (VTy->isFloatingPointTy())
|
|
IntV = IRB.CreateBitCast(V, IRB.getIntNTy(ValueSize));
|
|
return tryToCast(IRB,
|
|
IRB.CreateIntCast(IntV, IRB.getIntNTy(RequestedSize),
|
|
/*IsSigned=*/false),
|
|
Ty, DL, AllowTruncate);
|
|
}
|
|
if (VTy->isPointerTy() && Ty->isPointerTy())
|
|
return IRB.CreatePointerBitCastOrAddrSpaceCast(V, Ty);
|
|
if (VTy->isIntegerTy() && Ty->isIntegerTy())
|
|
return IRB.CreateIntCast(V, Ty, /*IsSigned=*/false);
|
|
// Use bit-preserving casts for floating-point values: convert float to int
|
|
// of the same size via bitcast, then extend/truncate the integer if needed.
|
|
if (VTy->isFloatingPointTy() && Ty->isIntOrPtrTy()) {
|
|
return tryToCast(IRB, IRB.CreateBitCast(V, IRB.getIntNTy(ValueSize)), Ty,
|
|
DL, AllowTruncate);
|
|
}
|
|
// When converting int to float, never use sitofp/uitofp as they perform value
|
|
// conversion, not bit-preserving cast.
|
|
if (VTy->isIntegerTy() && Ty->isFloatingPointTy()) {
|
|
if (ValueSize == RequestedSize)
|
|
return IRB.CreateBitCast(V, Ty);
|
|
return tryToCast(
|
|
IRB,
|
|
IRB.CreateIntCast(V, IRB.getIntNTy(RequestedSize), /*IsSigned=*/false),
|
|
Ty, DL, AllowTruncate);
|
|
}
|
|
return IRB.CreateBitOrPointerCast(V, Ty);
|
|
}
|
|
|
|
/// Get a constant integer/boolean of type \p IT and value \p Val.
|
|
template <typename Ty>
|
|
Constant *getCI(Type *IT, Ty Val, bool IsSigned = false) {
|
|
return ConstantInt::get(IT, Val, IsSigned);
|
|
}
|
|
|
|
/// The core of the instrumentor pass, which instruments the module as the
|
|
/// instrumentation configuration mandates.
|
|
class InstrumentorImpl final {
|
|
public:
|
|
/// Construct an instrumentor implementation using the configuration \p IConf.
|
|
InstrumentorImpl(InstrumentationConfig &IConf, InstrumentorIRBuilderTy &IIRB,
|
|
Module &M)
|
|
: IConf(IConf), M(M), IIRB(IIRB) {}
|
|
|
|
/// Instrument the module, public entry point.
|
|
bool instrument();
|
|
|
|
// Reset the state to allow reuse of the instrumentor with a different
|
|
// configuration.
|
|
void clear() {
|
|
InstChoicesPRE.clear();
|
|
InstChoicesPOST.clear();
|
|
ParsedFunctionRegex = Regex();
|
|
}
|
|
|
|
private:
|
|
/// Indicate if the module should be instrumented based on the target.
|
|
bool shouldInstrumentTarget();
|
|
|
|
/// Indicate if the function \p Fn should be instrumented.
|
|
bool shouldInstrumentFunction(Function &Fn);
|
|
bool shouldInstrumentGlobalVariable(GlobalVariable &GV);
|
|
|
|
/// Instrument instruction \p I if needed, and use the argument caches in \p
|
|
/// ICaches.
|
|
bool instrumentInstruction(Instruction &I, InstrumentationCaches &ICaches);
|
|
|
|
/// Instrument function \p Fn.
|
|
bool instrumentFunction(Function &Fn);
|
|
bool instrumentModule();
|
|
|
|
/// The instrumentation opportunities for instructions indexed by
|
|
/// their opcode.
|
|
DenseMap<unsigned, InstrumentationOpportunity *> InstChoicesPRE,
|
|
InstChoicesPOST;
|
|
|
|
/// The instrumentor configuration.
|
|
InstrumentationConfig &IConf;
|
|
|
|
/// The function regex filter, if any.
|
|
Regex ParsedFunctionRegex;
|
|
|
|
/// The underlying module.
|
|
Module &M;
|
|
|
|
protected:
|
|
/// A special IR builder that keeps track of the inserted instructions.
|
|
InstrumentorIRBuilderTy &IIRB;
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
static Regex createRegex(StringRef Str, StringRef Name, LLVMContext &Ctx) {
|
|
if (!Str.empty()) {
|
|
Regex RX(Str);
|
|
std::string ErrMsg;
|
|
if (!RX.isValid(ErrMsg)) {
|
|
Ctx.diagnose(DiagnosticInfoInstrumentation(
|
|
Twine("failed to parse ") + Name + " regex: " + ErrMsg, DS_Error));
|
|
return Regex();
|
|
}
|
|
return RX;
|
|
}
|
|
return Regex();
|
|
}
|
|
|
|
bool InstrumentorImpl::shouldInstrumentTarget() {
|
|
const Triple &T = M.getTargetTriple();
|
|
const bool IsGPU = T.isAMDGPU() || T.isNVPTX();
|
|
|
|
bool RegexMatches = true;
|
|
Regex RX = createRegex(IConf.TargetRegex->getString(), "target", IIRB.Ctx);
|
|
if (RX.isValid())
|
|
RegexMatches = RX.match(T.str());
|
|
|
|
// Only instrument the module if the target has to be instrumented.
|
|
return ((IsGPU && IConf.GPUEnabled->getBool()) ||
|
|
(!IsGPU && IConf.HostEnabled->getBool())) &&
|
|
RegexMatches;
|
|
}
|
|
|
|
bool InstrumentorImpl::shouldInstrumentFunction(Function &Fn) {
|
|
if (Fn.isDeclaration())
|
|
return false;
|
|
bool RegexMatches = true;
|
|
if (ParsedFunctionRegex.isValid())
|
|
RegexMatches = ParsedFunctionRegex.match(Fn.getName());
|
|
return (RegexMatches && !Fn.getName().starts_with(IConf.getRTName())) ||
|
|
Fn.hasFnAttribute("instrument");
|
|
}
|
|
|
|
bool InstrumentorImpl::shouldInstrumentGlobalVariable(GlobalVariable &GV) {
|
|
return !GV.getName().starts_with("llvm.") &&
|
|
!GV.getName().starts_with(IConf.getRTName());
|
|
}
|
|
|
|
bool InstrumentorImpl::instrumentInstruction(Instruction &I,
|
|
InstrumentationCaches &ICaches) {
|
|
bool Changed = false;
|
|
|
|
// Skip instrumentation instructions.
|
|
if (IIRB.NewInsts.contains(&I))
|
|
return Changed;
|
|
|
|
// Count epochs eagerly.
|
|
++IIRB.Epoch;
|
|
|
|
Value *IPtr = &I;
|
|
if (auto *IO = InstChoicesPRE.lookup(I.getOpcode())) {
|
|
IIRB.IRB.SetInsertPoint(&I);
|
|
ensureDbgLoc(IIRB.IRB);
|
|
IO->instrument(IPtr, Changed, IConf, IIRB, ICaches);
|
|
}
|
|
|
|
if (auto *IO = InstChoicesPOST.lookup(I.getOpcode())) {
|
|
IIRB.IRB.SetInsertPoint(I.getNextNode());
|
|
ensureDbgLoc(IIRB.IRB);
|
|
IO->instrument(IPtr, Changed, IConf, IIRB, ICaches);
|
|
}
|
|
IIRB.returnAllocas();
|
|
|
|
return Changed;
|
|
}
|
|
|
|
bool InstrumentorImpl::instrumentFunction(Function &Fn) {
|
|
bool Changed = false;
|
|
if (!shouldInstrumentFunction(Fn))
|
|
return Changed;
|
|
|
|
InstrumentationCaches ICaches;
|
|
SmallVector<Instruction *> FinalTIs;
|
|
ReversePostOrderTraversal<Function *> RPOT(&Fn);
|
|
for (auto &It : RPOT) {
|
|
for (auto &I : *It)
|
|
Changed |= instrumentInstruction(I, ICaches);
|
|
|
|
auto *TI = It->getTerminator();
|
|
if (!TI->getNumSuccessors())
|
|
FinalTIs.push_back(TI);
|
|
}
|
|
|
|
Value *FPtr = &Fn;
|
|
for (auto &[Name, IO] :
|
|
IConf.IChoices[InstrumentationLocation::FUNCTION_PRE]) {
|
|
if (!IO->Enabled)
|
|
continue;
|
|
// Count epochs eagerly.
|
|
++IIRB.Epoch;
|
|
|
|
IIRB.IRB.SetInsertPoint(
|
|
cast<Function>(FPtr)->getEntryBlock().getFirstNonPHIOrDbgOrAlloca());
|
|
ensureDbgLoc(IIRB.IRB);
|
|
IO->instrument(FPtr, Changed, IConf, IIRB, ICaches);
|
|
IIRB.returnAllocas();
|
|
}
|
|
|
|
for (auto &[Name, IO] :
|
|
IConf.IChoices[InstrumentationLocation::FUNCTION_POST]) {
|
|
if (!IO->Enabled)
|
|
continue;
|
|
// Count epochs eagerly.
|
|
++IIRB.Epoch;
|
|
|
|
for (Instruction *FinalTI : FinalTIs) {
|
|
IIRB.IRB.SetInsertPoint(FinalTI);
|
|
ensureDbgLoc(IIRB.IRB);
|
|
IO->instrument(FPtr, Changed, IConf, IIRB, ICaches);
|
|
IIRB.returnAllocas();
|
|
}
|
|
}
|
|
return Changed;
|
|
}
|
|
|
|
bool InstrumentorImpl::instrumentModule() {
|
|
SmallVector<GlobalVariable *> Globals;
|
|
Globals.reserve(M.global_size());
|
|
for (GlobalVariable &GV : M.globals()) {
|
|
// llvm.metadata contains globals such as llvm.used.
|
|
if (GV.getSection() == "llvm.metadata" ||
|
|
GV.getName() == "llvm.global_dtors" ||
|
|
GV.getName() == "llvm.global_ctors")
|
|
continue;
|
|
Globals.push_back(&GV);
|
|
}
|
|
|
|
auto CreateYtor = [&](bool Ctor) {
|
|
Function *YtorFn = Function::Create(
|
|
FunctionType::get(IIRB.VoidTy, false), GlobalValue::PrivateLinkage,
|
|
IConf.getRTName(Ctor ? "ctor" : "dtor", ""), M);
|
|
|
|
auto *EntryBB = BasicBlock::Create(IIRB.Ctx, "entry", YtorFn);
|
|
IIRB.IRB.SetInsertPoint(EntryBB, EntryBB->begin());
|
|
ensureDbgLoc(IIRB.IRB);
|
|
IIRB.IRB.CreateRetVoid();
|
|
|
|
if (Ctor)
|
|
appendToGlobalCtors(M, YtorFn, 1000);
|
|
else
|
|
appendToGlobalDtors(M, YtorFn, 1000);
|
|
return YtorFn;
|
|
};
|
|
|
|
InstrumentationCaches ICaches;
|
|
|
|
Function *CtorFn = nullptr, *DtorFn = nullptr;
|
|
bool Changed = false;
|
|
for (auto Loc : {InstrumentationLocation::MODULE_PRE,
|
|
InstrumentationLocation::MODULE_POST}) {
|
|
bool IsPRE = InstrumentationLocation::isPRE(Loc);
|
|
Function *&YtorFn = IsPRE ? CtorFn : DtorFn;
|
|
for (auto &ChoiceIt : IConf.IChoices[Loc]) {
|
|
auto *IO = ChoiceIt.second;
|
|
if (!IO->Enabled)
|
|
continue;
|
|
if (!YtorFn) {
|
|
YtorFn = CreateYtor(IsPRE);
|
|
Changed = true;
|
|
}
|
|
IIRB.IRB.SetInsertPointPastAllocas(YtorFn);
|
|
ensureDbgLoc(IIRB.IRB);
|
|
Value *YtorPtr = YtorFn;
|
|
|
|
// Count epochs eagerly.
|
|
++IIRB.Epoch;
|
|
|
|
IO->instrument(YtorPtr, Changed, IConf, IIRB, ICaches);
|
|
IIRB.returnAllocas();
|
|
}
|
|
}
|
|
|
|
for (auto Loc : {InstrumentationLocation::GLOBAL_PRE,
|
|
InstrumentationLocation::GLOBAL_POST}) {
|
|
bool IsPRE = InstrumentationLocation::isPRE(Loc);
|
|
Function *&YtorFn = IsPRE ? CtorFn : DtorFn;
|
|
for (auto &ChoiceIt : IConf.IChoices[Loc]) {
|
|
auto *IO = ChoiceIt.second;
|
|
if (!IO->Enabled)
|
|
continue;
|
|
if (!YtorFn) {
|
|
YtorFn = CreateYtor(IsPRE);
|
|
Changed = true;
|
|
}
|
|
for (GlobalVariable *GV : Globals) {
|
|
if (!shouldInstrumentGlobalVariable(*GV))
|
|
continue;
|
|
if (IsPRE)
|
|
IIRB.IRB.SetInsertPoint(YtorFn->getEntryBlock().getTerminator());
|
|
else
|
|
IIRB.IRB.SetInsertPointPastAllocas(YtorFn);
|
|
ensureDbgLoc(IIRB.IRB);
|
|
Value *GVPtr = GV;
|
|
|
|
// Count epochs eagerly.
|
|
++IIRB.Epoch;
|
|
|
|
IO->instrument(GVPtr, Changed, IConf, IIRB, ICaches);
|
|
IIRB.returnAllocas();
|
|
}
|
|
}
|
|
}
|
|
|
|
return Changed;
|
|
}
|
|
|
|
bool InstrumentorImpl::instrument() {
|
|
bool Changed = false;
|
|
if (!shouldInstrumentTarget())
|
|
return Changed;
|
|
|
|
StringRef FunctionRegexStr = IConf.FunctionRegex->getString();
|
|
ParsedFunctionRegex = createRegex(FunctionRegexStr, "function", IIRB.Ctx);
|
|
|
|
// Helper to register an IO for all its opcodes.
|
|
auto RegisterForAllOpcodes = [](auto &InstChoices,
|
|
InstrumentationOpportunity *IO) {
|
|
ArrayRef<unsigned> Opcodes = IO->getAllOpcodes();
|
|
// Register for all opcodes.
|
|
for (unsigned Opcode : Opcodes)
|
|
InstChoices[Opcode] = IO;
|
|
};
|
|
|
|
for (auto &[Name, IO] :
|
|
IConf.IChoices[InstrumentationLocation::INSTRUCTION_PRE])
|
|
if (IO->Enabled)
|
|
RegisterForAllOpcodes(InstChoicesPRE, IO);
|
|
for (auto &[Name, IO] :
|
|
IConf.IChoices[InstrumentationLocation::INSTRUCTION_POST])
|
|
if (IO->Enabled)
|
|
RegisterForAllOpcodes(InstChoicesPOST, IO);
|
|
Changed |= instrumentModule();
|
|
|
|
for (Function &Fn : M)
|
|
Changed |= instrumentFunction(Fn);
|
|
|
|
return Changed;
|
|
}
|
|
|
|
InstrumentorPass::InstrumentorPass(IntrusiveRefCntPtr<vfs::FileSystem> FS,
|
|
InstrumentationConfig *IC,
|
|
InstrumentorIRBuilderTy *IIRB)
|
|
: FS(FS), UserIConf(IC), UserIIRB(IIRB) {
|
|
if (!FS)
|
|
this->FS = vfs::getRealFileSystem();
|
|
}
|
|
|
|
PreservedAnalyses InstrumentorPass::run(Module &M, InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB,
|
|
bool ReadConfig) {
|
|
bool Changed = false;
|
|
InstrumentorImpl Impl(IConf, IIRB, M);
|
|
|
|
// If this is a configuration driven run, iterate over all configurations
|
|
// provided by the user, if not, use the config as is and run the instrumentor
|
|
// once.
|
|
if (ReadConfig)
|
|
readConfigPathsFile(ConfigPathsFile, ConfigFiles, IIRB.Ctx, *FS);
|
|
|
|
bool MultipleConfigs = ConfigFiles.size() > 1;
|
|
unsigned Idx = 0;
|
|
do {
|
|
std::string ConfigFile =
|
|
ReadConfig && !ConfigFiles.empty() ? ConfigFiles[Idx] : "";
|
|
|
|
// Initialize the config to the base state but keep the caches around.
|
|
Impl.clear();
|
|
IConf.init(IIRB);
|
|
|
|
if (!readConfigFromJSON(IConf, ConfigFile, IIRB.Ctx, *FS))
|
|
continue;
|
|
|
|
writeConfigToJSON(IConf,
|
|
MultipleConfigs
|
|
? OutputConfigFile + "." + std::to_string(Idx)
|
|
: OutputConfigFile,
|
|
IIRB.Ctx);
|
|
|
|
printRuntimeStub(IConf, IConf.RuntimeStubsFile->getString(), IIRB.Ctx);
|
|
|
|
Changed |= Impl.instrument();
|
|
} while (++Idx < ConfigFiles.size());
|
|
|
|
if (!Changed)
|
|
return PreservedAnalyses::all();
|
|
return PreservedAnalyses::none();
|
|
}
|
|
|
|
PreservedAnalyses InstrumentorPass::run(Module &M, ModuleAnalysisManager &MAM) {
|
|
// Only create them if the user did not provide them.
|
|
std::unique_ptr<InstrumentationConfig> IConfInt(
|
|
!UserIConf ? new InstrumentationConfig() : nullptr);
|
|
std::unique_ptr<InstrumentorIRBuilderTy> IIRBInt(
|
|
!UserIIRB ? new InstrumentorIRBuilderTy(M) : nullptr);
|
|
|
|
auto *IConf = IConfInt ? IConfInt.get() : UserIConf;
|
|
auto *IIRB = IIRBInt ? IIRBInt.get() : UserIIRB;
|
|
|
|
auto PA = run(M, *IConf, *IIRB, !UserIConf);
|
|
|
|
assert(!verifyModule(M, &errs()));
|
|
return PA;
|
|
}
|
|
|
|
std::unique_ptr<BaseConfigurationOption>
|
|
BaseConfigurationOption::createBoolOption(InstrumentationConfig &IConf,
|
|
StringRef Name, StringRef Description,
|
|
bool DefaultValue) {
|
|
auto BCO =
|
|
std::make_unique<BaseConfigurationOption>(Name, Description, BOOLEAN);
|
|
BCO->setBool(DefaultValue);
|
|
IConf.addBaseChoice(BCO.get());
|
|
return BCO;
|
|
}
|
|
|
|
std::unique_ptr<BaseConfigurationOption>
|
|
BaseConfigurationOption::createStringOption(InstrumentationConfig &IConf,
|
|
StringRef Name,
|
|
StringRef Description,
|
|
StringRef DefaultValue) {
|
|
auto BCO =
|
|
std::make_unique<BaseConfigurationOption>(Name, Description, STRING);
|
|
BCO->setString(DefaultValue);
|
|
IConf.addBaseChoice(BCO.get());
|
|
return BCO;
|
|
}
|
|
|
|
void InstrumentationConfig::populate(InstrumentorIRBuilderTy &IIRB) {
|
|
/// List of all instrumentation opportunities.
|
|
BasePointerIO::populate(*this, IIRB);
|
|
ModuleIO::populate(*this, IIRB);
|
|
GlobalVarIO::populate(*this, IIRB);
|
|
FunctionIO::populate(*this, IIRB);
|
|
AllocaIO::populate(*this, IIRB);
|
|
UnreachableIO::populate(*this, IIRB);
|
|
LoadIO::populate(*this, IIRB);
|
|
StoreIO::populate(*this, IIRB);
|
|
CastIO::populate(*this, IIRB);
|
|
NumericIO::populate(*this, IIRB);
|
|
}
|
|
|
|
void InstrumentationConfig::addChoice(InstrumentationOpportunity &IO,
|
|
LLVMContext &Ctx) {
|
|
auto *&ICPtr = IChoices[IO.getLocationKind()][IO.getName()];
|
|
if (ICPtr) {
|
|
Ctx.diagnose(DiagnosticInfoInstrumentation(
|
|
Twine("registered two instrumentation opportunities for the same "
|
|
"location (") +
|
|
ICPtr->getName() + Twine(" vs ") + IO.getName() + Twine(")"),
|
|
DS_Warning));
|
|
}
|
|
ICPtr = &IO;
|
|
}
|
|
|
|
Value *
|
|
InstrumentationConfig::getBasePointerInfo(Value &V,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
Function *Fn = IIRB.IRB.GetInsertBlock()->getParent();
|
|
|
|
Value *Obj;
|
|
{
|
|
Value *&UnderlyingObj = UnderlyingObjsMap[&V];
|
|
if (!UnderlyingObj)
|
|
UnderlyingObj = const_cast<Value *>(getUnderlyingObjectAggressive(&V));
|
|
Obj = UnderlyingObj;
|
|
}
|
|
|
|
Value *&BPI = BasePointerInfoMap[{Obj, Fn}];
|
|
if (BPI)
|
|
return BPI;
|
|
|
|
auto *BPIO =
|
|
IChoices[InstrumentationLocation::SPECIAL_VALUE]["base_pointer_info"];
|
|
if (!BPIO || !BPIO->Enabled) {
|
|
IIRB.Ctx.diagnose(DiagnosticInfoInstrumentation(
|
|
"Base pointer info disabled but required, passing nullptr.",
|
|
DS_Warning));
|
|
return BPI = Constant::getNullValue(BPIO->getRetTy(IIRB.Ctx));
|
|
}
|
|
|
|
IRBuilderBase::InsertPointGuard IP(IIRB.IRB);
|
|
if (auto *BasePtrI = dyn_cast<Instruction>(Obj)) {
|
|
std::optional<BasicBlock::iterator> IP =
|
|
BasePtrI->getInsertionPointAfterDef();
|
|
if (IP) {
|
|
IIRB.IRB.SetInsertPoint(*IP);
|
|
} else {
|
|
IIRB.Ctx.diagnose(DiagnosticInfoInstrumentation(
|
|
"Base pointer info could not be placed, passing nullptr.",
|
|
DS_Warning));
|
|
return BPI = Constant::getNullValue(BPIO->getRetTy(IIRB.Ctx));
|
|
}
|
|
} else if (isa<Constant>(Obj) || isa<Argument>(Obj)) {
|
|
IIRB.IRB.SetInsertPointPastAllocas(IIRB.IRB.GetInsertBlock()->getParent());
|
|
} else {
|
|
LLVM_DEBUG(Obj->dump());
|
|
llvm_unreachable("Unexpected base pointer!");
|
|
}
|
|
ensureDbgLoc(IIRB.IRB);
|
|
|
|
// Use fresh caches for safety, as this function may be called from
|
|
// another instrumentation opportunity.
|
|
bool Changed;
|
|
InstrumentationCaches ICaches;
|
|
BPI = BPIO->instrument(Obj, Changed, *this, IIRB, ICaches);
|
|
IIRB.returnAllocas();
|
|
if (!BPI)
|
|
BPI = Constant::getNullValue(BPIO->getRetTy(IIRB.Ctx));
|
|
return BPI;
|
|
}
|
|
|
|
Value *InstrumentationOpportunity::getIdPre(Value &V, Type &Ty,
|
|
InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
return getCI(&Ty, getIdFromEpoch(IIRB.Epoch));
|
|
}
|
|
|
|
Value *InstrumentationOpportunity::getIdPost(Value &V, Type &Ty,
|
|
InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
return getCI(&Ty, -getIdFromEpoch(IIRB.Epoch), /*IsSigned=*/true);
|
|
}
|
|
|
|
Value *InstrumentationOpportunity::forceCast(Value &V, Type &Ty,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
if (V.getType()->isVoidTy())
|
|
return Ty.isVoidTy() ? &V : Constant::getNullValue(&Ty);
|
|
return tryToCast(IIRB.IRB, &V, &Ty,
|
|
IIRB.IRB.GetInsertBlock()->getDataLayout());
|
|
}
|
|
|
|
Value *InstrumentationOpportunity::replaceValue(Value &V, Value &NewV,
|
|
InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
if (V.getType()->isVoidTy())
|
|
return &V;
|
|
|
|
auto *NewVCasted = &NewV;
|
|
if (auto *I = dyn_cast<Instruction>(&NewV)) {
|
|
IRBuilderBase::InsertPointGuard IPG(IIRB.IRB);
|
|
IIRB.IRB.SetInsertPoint(I->getNextNode());
|
|
ensureDbgLoc(IIRB.IRB);
|
|
NewVCasted = tryToCast(IIRB.IRB, &NewV, V.getType(), IIRB.DL,
|
|
/*AllowTruncate=*/true);
|
|
}
|
|
V.replaceUsesWithIf(NewVCasted, [&](Use &U) {
|
|
if (IIRB.NewInsts.lookup(cast<Instruction>(U.getUser())) == IIRB.Epoch)
|
|
return false;
|
|
return !isa<LifetimeIntrinsic>(U.getUser()) && !U.getUser()->isDroppable();
|
|
});
|
|
|
|
return &V;
|
|
}
|
|
|
|
IRTCallDescription::IRTCallDescription(InstrumentationOpportunity &IO,
|
|
Type *RetTy)
|
|
: IO(IO), RetTy(RetTy) {
|
|
for (auto &It : IO.IRTArgs) {
|
|
if (!It.Enabled)
|
|
continue;
|
|
NumReplaceableArgs += bool(It.Flags & IRTArg::REPLACABLE);
|
|
MightRequireIndirection |= It.Flags & IRTArg::POTENTIALLY_INDIRECT;
|
|
}
|
|
if (NumReplaceableArgs > 1)
|
|
MightRequireIndirection = RequiresIndirection = true;
|
|
}
|
|
|
|
FunctionType *IRTCallDescription::createLLVMSignature(
|
|
InstrumentationConfig &IConf, InstrumentorIRBuilderTy &IIRB,
|
|
const DataLayout &DL, bool ForceIndirection) {
|
|
assert(((ForceIndirection && MightRequireIndirection) ||
|
|
(!ForceIndirection && !RequiresIndirection)) &&
|
|
"Wrong indirection setting!");
|
|
|
|
SmallVector<Type *> ParamTypes;
|
|
for (auto &It : IO.IRTArgs) {
|
|
if (!It.Enabled)
|
|
continue;
|
|
if (!ForceIndirection || !isPotentiallyIndirect(It)) {
|
|
ParamTypes.push_back(It.Ty);
|
|
if (!RetTy && NumReplaceableArgs == 1 && (It.Flags & IRTArg::REPLACABLE))
|
|
RetTy = It.Ty;
|
|
continue;
|
|
}
|
|
|
|
// The indirection pointer and the size of the value.
|
|
ParamTypes.push_back(IIRB.PtrTy);
|
|
if (!(It.Flags & IRTArg::INDIRECT_HAS_SIZE))
|
|
ParamTypes.push_back(IIRB.Int32Ty);
|
|
}
|
|
if (!RetTy)
|
|
RetTy = IIRB.VoidTy;
|
|
|
|
return FunctionType::get(RetTy, ParamTypes, /*isVarArg=*/false);
|
|
}
|
|
|
|
CallInst *IRTCallDescription::createLLVMCall(Value *&V,
|
|
InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB,
|
|
const DataLayout &DL,
|
|
InstrumentationCaches &ICaches) {
|
|
SmallVector<Value *> CallParams;
|
|
|
|
IRBuilderBase::InsertPointGuard IRP(IIRB.IRB);
|
|
auto IP = IIRB.IRB.GetInsertPoint();
|
|
|
|
bool ForceIndirection = RequiresIndirection;
|
|
for (auto &It : IO.IRTArgs) {
|
|
if (!It.Enabled)
|
|
continue;
|
|
auto *&Param = ICaches.DirectArgCache[{IIRB.Epoch, IO.getName(), It.Name}];
|
|
if (!Param || It.NoCache)
|
|
// Avoid passing the caches to the getter.
|
|
Param = It.GetterCB(*V, *It.Ty, IConf, IIRB);
|
|
assert(Param);
|
|
|
|
if (Param->getType()->isVoidTy()) {
|
|
Param = Constant::getNullValue(It.Ty);
|
|
} else if (Param->getType()->isAggregateType() ||
|
|
DL.getTypeSizeInBits(Param->getType()) >
|
|
DL.getTypeSizeInBits(It.Ty)) {
|
|
if (!isPotentiallyIndirect(It)) {
|
|
IIRB.Ctx.diagnose(DiagnosticInfoInstrumentation(
|
|
Twine("indirection needed for ") + It.Name + Twine(" in ") +
|
|
IO.getName() +
|
|
Twine(", but not indicated. Instrumentation is skipped"),
|
|
DS_Warning));
|
|
return nullptr;
|
|
}
|
|
ForceIndirection = true;
|
|
} else {
|
|
Param = tryToCast(IIRB.IRB, Param, It.Ty, DL);
|
|
}
|
|
CallParams.push_back(Param);
|
|
}
|
|
|
|
if (ForceIndirection) {
|
|
Function *Fn = IIRB.IRB.GetInsertBlock()->getParent();
|
|
|
|
unsigned Offset = 0;
|
|
for (auto &It : IO.IRTArgs) {
|
|
if (!It.Enabled)
|
|
continue;
|
|
|
|
if (!isPotentiallyIndirect(It)) {
|
|
++Offset;
|
|
continue;
|
|
}
|
|
auto *&CallParam = CallParams[Offset++];
|
|
if (!(It.Flags & IRTArg::INDIRECT_HAS_SIZE)) {
|
|
CallParams.insert(&CallParam + 1, IIRB.IRB.getInt32(DL.getTypeStoreSize(
|
|
CallParam->getType())));
|
|
Offset += 1;
|
|
}
|
|
|
|
auto *&CachedParam =
|
|
ICaches.IndirectArgCache[{IIRB.Epoch, IO.getName(), It.Name}];
|
|
if (CachedParam) {
|
|
CallParam = CachedParam;
|
|
continue;
|
|
}
|
|
|
|
auto *AI = IIRB.getAlloca(Fn, CallParam->getType());
|
|
IIRB.IRB.CreateStore(CallParam, AI);
|
|
CallParam = CachedParam = AI;
|
|
}
|
|
}
|
|
|
|
if (!ForceIndirection)
|
|
IIRB.IRB.SetInsertPoint(IP);
|
|
ensureDbgLoc(IIRB.IRB);
|
|
|
|
auto *FnTy = createLLVMSignature(IConf, IIRB, DL, ForceIndirection);
|
|
auto CompleteName =
|
|
IConf.getRTName(IO.IP.isPRE() ? "pre_" : "post_", IO.getName(),
|
|
ForceIndirection ? "_ind" : "");
|
|
auto FC = IIRB.IRB.GetInsertBlock()->getModule()->getOrInsertFunction(
|
|
CompleteName, FnTy);
|
|
auto *CI = IIRB.IRB.CreateCall(FC, CallParams);
|
|
CI->addFnAttr(Attribute::get(IIRB.Ctx, Attribute::WillReturn));
|
|
|
|
for (unsigned I = 0, E = IO.IRTArgs.size(); I < E; ++I) {
|
|
if (!IO.IRTArgs[I].Enabled)
|
|
continue;
|
|
if (!isReplacable(IO.IRTArgs[I]))
|
|
continue;
|
|
bool IsCustomReplaceable = IO.IRTArgs[I].Flags & IRTArg::REPLACABLE_CUSTOM;
|
|
Value *NewValue = FnTy->isVoidTy() || IsCustomReplaceable
|
|
? ICaches.DirectArgCache[{IIRB.Epoch, IO.getName(),
|
|
IO.IRTArgs[I].Name}]
|
|
: CI;
|
|
assert(NewValue);
|
|
if (ForceIndirection && !IsCustomReplaceable &&
|
|
isPotentiallyIndirect(IO.IRTArgs[I])) {
|
|
auto *Q =
|
|
ICaches
|
|
.IndirectArgCache[{IIRB.Epoch, IO.getName(), IO.IRTArgs[I].Name}];
|
|
NewValue = IIRB.IRB.CreateLoad(V->getType(), Q);
|
|
}
|
|
V = IO.IRTArgs[I].SetterCB(*V, *NewValue, IConf, IIRB);
|
|
}
|
|
return CI;
|
|
}
|
|
|
|
template <typename Ty> constexpr static Value *getValue(Ty &ValueOrUse) {
|
|
if constexpr (std::is_same<Ty, Use>::value)
|
|
return ValueOrUse.get();
|
|
else
|
|
return static_cast<Value *>(&ValueOrUse);
|
|
}
|
|
|
|
template <typename Range>
|
|
static Value *createValuePack(const Range &R, InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
auto *Fn = IIRB.IRB.GetInsertBlock()->getParent();
|
|
auto *I32Ty = IIRB.IRB.getInt32Ty();
|
|
SmallVector<Constant *> ConstantValues;
|
|
SmallVector<std::pair<Value *, uint32_t>> Values;
|
|
SmallVector<Type *> Types;
|
|
for (auto &RE : R) {
|
|
Value *V = getValue(RE);
|
|
if (!V->getType()->isSized())
|
|
continue;
|
|
auto VSize = IIRB.DL.getTypeAllocSize(V->getType());
|
|
ConstantValues.push_back(getCI(I32Ty, VSize));
|
|
Types.push_back(I32Ty);
|
|
ConstantValues.push_back(getCI(I32Ty, V->getType()->getTypeID()));
|
|
Types.push_back(I32Ty);
|
|
if (uint32_t MisAlign = VSize % 8) {
|
|
Types.push_back(ArrayType::get(IIRB.Int8Ty, 8 - MisAlign));
|
|
ConstantValues.push_back(ConstantArray::getNullValue(Types.back()));
|
|
}
|
|
Types.push_back(V->getType());
|
|
if (auto *C = dyn_cast<Constant>(V)) {
|
|
ConstantValues.push_back(C);
|
|
continue;
|
|
}
|
|
Values.push_back({V, ConstantValues.size()});
|
|
ConstantValues.push_back(Constant::getNullValue(V->getType()));
|
|
}
|
|
if (Types.empty())
|
|
return ConstantPointerNull::get(IIRB.PtrTy);
|
|
|
|
StructType *STy = StructType::get(Fn->getContext(), Types, /*isPacked=*/true);
|
|
Constant *Initializer = ConstantStruct::get(STy, ConstantValues);
|
|
|
|
GlobalVariable *&GV = IConf.ConstantGlobalsCache[Initializer];
|
|
if (!GV)
|
|
GV = new GlobalVariable(*Fn->getParent(), STy, false,
|
|
GlobalValue::InternalLinkage, Initializer,
|
|
IConf.getRTName("", "value_pack"));
|
|
|
|
auto *AI = IIRB.getAlloca(Fn, STy);
|
|
IIRB.IRB.CreateMemCpy(AI, AI->getAlign(), GV, MaybeAlign(GV->getAlignment()),
|
|
IIRB.DL.getTypeAllocSize(STy));
|
|
for (auto [Param, Idx] : Values) {
|
|
auto *Ptr = IIRB.IRB.CreateStructGEP(STy, AI, Idx);
|
|
IIRB.IRB.CreateStore(Param, Ptr);
|
|
}
|
|
return AI;
|
|
}
|
|
|
|
template <typename Range>
|
|
static void readValuePack(const Range &R, Value &Pack,
|
|
InstrumentorIRBuilderTy &IIRB,
|
|
function_ref<void(int, Value *)> SetterCB) {
|
|
auto *Fn = IIRB.IRB.GetInsertBlock()->getParent();
|
|
auto &DL = Fn->getDataLayout();
|
|
SmallVector<Value *> ParameterValues;
|
|
unsigned Offset = 0;
|
|
for (const auto &[Idx, RE] : enumerate(R)) {
|
|
Value *V = getValue(RE);
|
|
if (!V->getType()->isSized())
|
|
continue;
|
|
Offset += 8;
|
|
auto VSize = DL.getTypeAllocSize(V->getType());
|
|
auto Padding = alignTo(VSize, 8) - VSize;
|
|
Offset += Padding;
|
|
auto *Ptr = IIRB.IRB.CreateConstInBoundsGEP1_32(IIRB.Int8Ty, &Pack, Offset);
|
|
auto *NewV = IIRB.IRB.CreateLoad(V->getType(), Ptr);
|
|
SetterCB(Idx, NewV);
|
|
Offset += VSize;
|
|
}
|
|
}
|
|
|
|
/// FunctionIO
|
|
/// {
|
|
void FunctionIO::init(InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB, ConfigTy *UserConfig) {
|
|
using namespace std::placeholders;
|
|
if (UserConfig)
|
|
Config = *UserConfig;
|
|
|
|
bool IsPRE = getLocationKind() == InstrumentationLocation::FUNCTION_PRE;
|
|
if (Config.has(PassAddress))
|
|
IRTArgs.push_back(IRTArg(IIRB.PtrTy, "address", "The function address.",
|
|
IRTArg::NONE, getFunctionAddress));
|
|
if (Config.has(PassName))
|
|
IRTArgs.push_back(IRTArg(IIRB.PtrTy, "name", "The function name.",
|
|
IRTArg::STRING, getFunctionName));
|
|
if (Config.has(PassNumArguments))
|
|
IRTArgs.push_back(
|
|
IRTArg(IIRB.Int32Ty, "num_arguments",
|
|
"Number of function arguments (without varargs).", IRTArg::NONE,
|
|
std::bind(&FunctionIO::getNumArguments, this, _1, _2, _3, _4)));
|
|
if (Config.has(PassArguments))
|
|
IRTArgs.push_back(IRTArg(
|
|
IIRB.PtrTy, "arguments", "Description of the arguments.",
|
|
(IsPRE && Config.has(ReplaceArguments) ? IRTArg::REPLACABLE_CUSTOM
|
|
: IRTArg::NONE) |
|
|
IRTArg::VALUE_PACK,
|
|
std::bind(&FunctionIO::getArguments, this, _1, _2, _3, _4),
|
|
std::bind(&FunctionIO::setArguments, this, _1, _2, _3, _4)));
|
|
if (Config.has(PassIsMain))
|
|
IRTArgs.push_back(IRTArg(IIRB.Int8Ty, "is_main",
|
|
"Flag to indicate it is the main function.",
|
|
IRTArg::NONE, isMainFunction));
|
|
addCommonArgs(IConf, IIRB.Ctx, Config.has(PassId));
|
|
IConf.addChoice(*this, IIRB.Ctx);
|
|
}
|
|
|
|
Value *FunctionIO::getFunctionAddress(Value &V, Type &Ty,
|
|
InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
auto &Fn = cast<Function>(V);
|
|
if (Fn.isIntrinsic())
|
|
return Constant::getNullValue(&Ty);
|
|
return &V;
|
|
}
|
|
Value *FunctionIO::getFunctionName(Value &V, Type &Ty,
|
|
InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
auto &Fn = cast<Function>(V);
|
|
return IConf.getGlobalString(IConf.DemangleFunctionNames->getBool()
|
|
? demangle(Fn.getName())
|
|
: Fn.getName(),
|
|
IIRB);
|
|
}
|
|
Value *FunctionIO::getNumArguments(Value &V, Type &Ty,
|
|
InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
auto &Fn = cast<Function>(V);
|
|
if (!Config.ArgFilter)
|
|
return getCI(&Ty, Fn.arg_size());
|
|
auto FRange = make_filter_range(Fn.args(), Config.ArgFilter);
|
|
return getCI(&Ty, std::distance(FRange.begin(), FRange.end()));
|
|
}
|
|
Value *FunctionIO::getArguments(Value &V, Type &Ty,
|
|
InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
auto &Fn = cast<Function>(V);
|
|
if (!Config.ArgFilter)
|
|
return createValuePack(Fn.args(), IConf, IIRB);
|
|
return createValuePack(make_filter_range(Fn.args(), Config.ArgFilter), IConf,
|
|
IIRB);
|
|
}
|
|
Value *FunctionIO::setArguments(Value &V, Value &NewV,
|
|
InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
auto &Fn = cast<Function>(V);
|
|
auto *AIt = Fn.arg_begin();
|
|
auto CB = [&](int Idx, Value *ReplV) {
|
|
while (Config.ArgFilter && !Config.ArgFilter(*AIt))
|
|
++AIt;
|
|
Fn.getArg(Idx)->replaceUsesWithIf(ReplV, [&](Use &U) {
|
|
return IIRB.NewInsts.lookup(cast<Instruction>(U.getUser())) != IIRB.Epoch;
|
|
});
|
|
++AIt;
|
|
};
|
|
if (!Config.ArgFilter)
|
|
readValuePack(Fn.args(), NewV, IIRB, CB);
|
|
else
|
|
readValuePack(make_filter_range(Fn.args(), Config.ArgFilter), NewV, IIRB,
|
|
CB);
|
|
return &Fn;
|
|
}
|
|
Value *FunctionIO::isMainFunction(Value &V, Type &Ty,
|
|
InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
auto &Fn = cast<Function>(V);
|
|
return getCI(&Ty, Fn.getName() == "main");
|
|
}
|
|
|
|
static Value *getOpcode(Value &V, Type &Ty, InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
auto &I = cast<Instruction>(V);
|
|
return getCI(&Ty, I.getOpcode());
|
|
}
|
|
|
|
static Value *getTypeId(Value &V, Type &Ty, InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
return getCI(&Ty, V.getType()->getTypeID());
|
|
}
|
|
|
|
static Value *getSize(Value &V, Type &Ty, InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
auto &I = cast<Instruction>(V);
|
|
auto &DL = I.getDataLayout();
|
|
return getCI(&Ty, DL.getTypeStoreSize(V.getType()));
|
|
}
|
|
|
|
/// UnreachableIO
|
|
///{
|
|
void UnreachableIO::init(InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB, ConfigTy *UserConfig) {
|
|
if (UserConfig)
|
|
Config = *UserConfig;
|
|
addCommonArgs(IConf, IIRB.Ctx, Config.has(PassId));
|
|
IConf.addChoice(*this, IIRB.Ctx);
|
|
}
|
|
///}
|
|
|
|
/// AllocaIO
|
|
///{
|
|
void AllocaIO::init(InstrumentationConfig &IConf, InstrumentorIRBuilderTy &IIRB,
|
|
ConfigTy *UserConfig) {
|
|
if (UserConfig)
|
|
Config = *UserConfig;
|
|
|
|
bool IsPRE = getLocationKind() == InstrumentationLocation::INSTRUCTION_PRE;
|
|
if (!IsPRE && Config.has(PassAddress))
|
|
IRTArgs.push_back(
|
|
IRTArg(IIRB.PtrTy, "address", "The allocated memory address.",
|
|
Config.has(ReplaceAddress) ? IRTArg::REPLACABLE : IRTArg::NONE,
|
|
InstrumentationOpportunity::getValue,
|
|
InstrumentationOpportunity::replaceValue));
|
|
if (Config.has(PassSize))
|
|
IRTArgs.push_back(IRTArg(
|
|
IIRB.Int64Ty, "size", "The allocation size.",
|
|
(IsPRE && Config.has(ReplaceSize)) ? IRTArg::REPLACABLE : IRTArg::NONE,
|
|
getSize, setSize));
|
|
if (Config.has(PassAlignment))
|
|
IRTArgs.push_back(IRTArg(IIRB.Int64Ty, "alignment",
|
|
"The allocation alignment.", IRTArg::NONE,
|
|
getAlignment));
|
|
|
|
addCommonArgs(IConf, IIRB.Ctx, Config.has(PassId));
|
|
IConf.addChoice(*this, IIRB.Ctx);
|
|
}
|
|
|
|
Value *AllocaIO::getSize(Value &V, Type &Ty, InstrumentationConfig &IO,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
auto &AI = cast<AllocaInst>(V);
|
|
const DataLayout &DL = AI.getDataLayout();
|
|
Value *SizeValue = nullptr;
|
|
TypeSize TypeSize = DL.getTypeAllocSize(AI.getAllocatedType());
|
|
if (TypeSize.isFixed()) {
|
|
SizeValue = getCI(&Ty, TypeSize.getFixedValue());
|
|
} else {
|
|
auto *NullPtr = ConstantPointerNull::get(AI.getType());
|
|
SizeValue = IIRB.IRB.CreatePtrToInt(
|
|
IIRB.IRB.CreateGEP(AI.getAllocatedType(), NullPtr,
|
|
{IIRB.IRB.getInt32(1)}),
|
|
&Ty);
|
|
}
|
|
if (AI.isArrayAllocation())
|
|
SizeValue = IIRB.IRB.CreateMul(
|
|
SizeValue, IIRB.IRB.CreateZExtOrBitCast(AI.getArraySize(), &Ty));
|
|
return SizeValue;
|
|
}
|
|
|
|
Value *AllocaIO::setSize(Value &V, Value &NewV, InstrumentationConfig &IO,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
auto &AI = cast<AllocaInst>(V);
|
|
const DataLayout &DL = AI.getDataLayout();
|
|
auto *NewAI = IIRB.IRB.CreateAlloca(IIRB.IRB.getInt8Ty(),
|
|
DL.getAllocaAddrSpace(), &NewV);
|
|
NewAI->setAlignment(AI.getAlign());
|
|
AI.replaceAllUsesWith(NewAI);
|
|
IIRB.eraseLater(&AI);
|
|
return NewAI;
|
|
}
|
|
|
|
Value *AllocaIO::getAlignment(Value &V, Type &Ty, InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
return getCI(&Ty, cast<AllocaInst>(V).getAlign().value());
|
|
}
|
|
///}
|
|
|
|
void StoreIO::init(InstrumentationConfig &IConf, InstrumentorIRBuilderTy &IIRB,
|
|
ConfigTy *UserConfig) {
|
|
if (UserConfig)
|
|
Config = *UserConfig;
|
|
|
|
bool IsPRE = getLocationKind() == InstrumentationLocation::INSTRUCTION_PRE;
|
|
if (Config.has(PassPointer)) {
|
|
IRTArgs.push_back(
|
|
IRTArg(IIRB.PtrTy, "pointer", "The accessed pointer.",
|
|
((IsPRE && Config.has(ReplacePointer)) ? IRTArg::REPLACABLE
|
|
: IRTArg::NONE),
|
|
getPointer, setPointer));
|
|
}
|
|
if (Config.has(PassPointerAS)) {
|
|
IRTArgs.push_back(IRTArg(IIRB.Int32Ty, "pointer_as",
|
|
"The address space of the accessed pointer.",
|
|
IRTArg::NONE, getPointerAS));
|
|
}
|
|
if (Config.has(PassBasePointerInfo)) {
|
|
IRTArgs.push_back(IRTArg(IIRB.PtrTy, "base_pointer_info",
|
|
"The runtime provided base pointer info.",
|
|
IRTArg::NONE, getBasePointerInfo));
|
|
}
|
|
if (Config.has(PassStoredValue)) {
|
|
IRTArgs.push_back(
|
|
IRTArg(getValueType(IIRB), "value", "The stored value.",
|
|
IRTArg::POTENTIALLY_INDIRECT |
|
|
(Config.has(PassStoredValueSize) ? IRTArg::INDIRECT_HAS_SIZE
|
|
: IRTArg::NONE),
|
|
getValue));
|
|
}
|
|
if (Config.has(PassStoredValueSize)) {
|
|
IRTArgs.push_back(IRTArg(IIRB.Int64Ty, "value_size",
|
|
"The size of the stored value.", IRTArg::NONE,
|
|
getValueSize));
|
|
}
|
|
if (Config.has(PassAlignment)) {
|
|
IRTArgs.push_back(IRTArg(IIRB.Int64Ty, "alignment",
|
|
"The known access alignment.", IRTArg::NONE,
|
|
getAlignment));
|
|
}
|
|
if (Config.has(PassValueTypeId)) {
|
|
IRTArgs.push_back(IRTArg(IIRB.Int32Ty, "value_type_id",
|
|
"The type id of the stored value.", IRTArg::NONE,
|
|
getValueTypeId));
|
|
}
|
|
if (Config.has(PassAtomicityOrdering)) {
|
|
IRTArgs.push_back(IRTArg(IIRB.Int32Ty, "atomicity_ordering",
|
|
"The atomicity ordering of the store.",
|
|
IRTArg::NONE, getAtomicityOrdering));
|
|
}
|
|
if (Config.has(PassSyncScopeId)) {
|
|
IRTArgs.push_back(IRTArg(IIRB.Int8Ty, "sync_scope_id",
|
|
"The sync scope id of the store.", IRTArg::NONE,
|
|
getSyncScopeId));
|
|
}
|
|
if (Config.has(PassIsVolatile)) {
|
|
IRTArgs.push_back(IRTArg(IIRB.Int8Ty, "is_volatile",
|
|
"Flag indicating a volatile store.", IRTArg::NONE,
|
|
isVolatile));
|
|
}
|
|
|
|
addCommonArgs(IConf, IIRB.Ctx, Config.has(PassId));
|
|
IConf.addChoice(*this, IIRB.Ctx);
|
|
}
|
|
|
|
Value *StoreIO::getPointer(Value &V, Type &Ty, InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
auto &SI = cast<StoreInst>(V);
|
|
return SI.getPointerOperand();
|
|
}
|
|
|
|
Value *StoreIO::setPointer(Value &V, Value &NewV, InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
auto &SI = cast<StoreInst>(V);
|
|
SI.setOperand(SI.getPointerOperandIndex(), &NewV);
|
|
return &SI;
|
|
}
|
|
|
|
Value *StoreIO::getPointerAS(Value &V, Type &Ty, InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
auto &SI = cast<StoreInst>(V);
|
|
return getCI(&Ty, SI.getPointerAddressSpace());
|
|
}
|
|
|
|
Value *StoreIO::getBasePointerInfo(Value &V, Type &Ty,
|
|
InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
auto &SI = cast<StoreInst>(V);
|
|
return IConf.getBasePointerInfo(*SI.getPointerOperand(), IIRB);
|
|
}
|
|
|
|
Value *StoreIO::getValue(Value &V, Type &Ty, InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
auto &SI = cast<StoreInst>(V);
|
|
return SI.getValueOperand();
|
|
}
|
|
|
|
Value *StoreIO::getValueSize(Value &V, Type &Ty, InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
auto &SI = cast<StoreInst>(V);
|
|
auto &DL = SI.getDataLayout();
|
|
return getCI(&Ty, DL.getTypeStoreSize(SI.getValueOperand()->getType()));
|
|
}
|
|
|
|
Value *StoreIO::getAlignment(Value &V, Type &Ty, InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
auto &SI = cast<StoreInst>(V);
|
|
return getCI(&Ty, SI.getAlign().value());
|
|
}
|
|
|
|
Value *StoreIO::getValueTypeId(Value &V, Type &Ty, InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
auto &SI = cast<StoreInst>(V);
|
|
return getCI(&Ty, SI.getValueOperand()->getType()->getTypeID());
|
|
}
|
|
|
|
Value *StoreIO::getAtomicityOrdering(Value &V, Type &Ty,
|
|
InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
auto &SI = cast<StoreInst>(V);
|
|
return getCI(&Ty, uint64_t(SI.getOrdering()));
|
|
}
|
|
|
|
Value *StoreIO::getSyncScopeId(Value &V, Type &Ty, InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
auto &SI = cast<StoreInst>(V);
|
|
return getCI(&Ty, uint64_t(SI.getSyncScopeID()));
|
|
}
|
|
|
|
Value *StoreIO::isVolatile(Value &V, Type &Ty, InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
auto &SI = cast<StoreInst>(V);
|
|
return getCI(&Ty, SI.isVolatile());
|
|
}
|
|
|
|
void LoadIO::init(InstrumentationConfig &IConf, InstrumentorIRBuilderTy &IIRB,
|
|
ConfigTy *UserConfig) {
|
|
bool IsPRE = getLocationKind() == InstrumentationLocation::INSTRUCTION_PRE;
|
|
if (UserConfig)
|
|
Config = *UserConfig;
|
|
if (Config.has(PassPointer)) {
|
|
IRTArgs.push_back(
|
|
IRTArg(IIRB.PtrTy, "pointer", "The accessed pointer.",
|
|
((IsPRE && Config.has(ReplacePointer)) ? IRTArg::REPLACABLE
|
|
: IRTArg::NONE),
|
|
getPointer, setPointer));
|
|
}
|
|
if (Config.has(PassPointerAS)) {
|
|
IRTArgs.push_back(IRTArg(IIRB.Int32Ty, "pointer_as",
|
|
"The address space of the accessed pointer.",
|
|
IRTArg::NONE, getPointerAS));
|
|
}
|
|
if (Config.has(PassBasePointerInfo)) {
|
|
IRTArgs.push_back(IRTArg(IIRB.PtrTy, "base_pointer_info",
|
|
"The runtime provided base pointer info.",
|
|
IRTArg::NONE, getBasePointerInfo));
|
|
}
|
|
if (!IsPRE && Config.has(PassValue)) {
|
|
IRTArgs.push_back(
|
|
IRTArg(getValueType(IIRB), "value", "The loaded value.",
|
|
Config.has(ReplaceValue)
|
|
? IRTArg::REPLACABLE | IRTArg::POTENTIALLY_INDIRECT |
|
|
(Config.has(PassValueSize) ? IRTArg::INDIRECT_HAS_SIZE
|
|
: IRTArg::NONE)
|
|
: IRTArg::NONE,
|
|
getValue, Config.has(ReplaceValue) ? replaceValue : nullptr));
|
|
}
|
|
if (Config.has(PassValueSize)) {
|
|
IRTArgs.push_back(IRTArg(IIRB.Int64Ty, "value_size",
|
|
"The size of the loaded value.", IRTArg::NONE,
|
|
getValueSize));
|
|
}
|
|
if (Config.has(PassAlignment)) {
|
|
IRTArgs.push_back(IRTArg(IIRB.Int64Ty, "alignment",
|
|
"The known access alignment.", IRTArg::NONE,
|
|
getAlignment));
|
|
}
|
|
if (Config.has(PassValueTypeId)) {
|
|
IRTArgs.push_back(IRTArg(IIRB.Int32Ty, "value_type_id",
|
|
"The type id of the loaded value.", IRTArg::NONE,
|
|
getValueTypeId));
|
|
}
|
|
if (Config.has(PassAtomicityOrdering)) {
|
|
IRTArgs.push_back(IRTArg(IIRB.Int32Ty, "atomicity_ordering",
|
|
"The atomicity ordering of the load.",
|
|
IRTArg::NONE, getAtomicityOrdering));
|
|
}
|
|
if (Config.has(PassSyncScopeId)) {
|
|
IRTArgs.push_back(IRTArg(IIRB.Int8Ty, "sync_scope_id",
|
|
"The sync scope id of the load.", IRTArg::NONE,
|
|
getSyncScopeId));
|
|
}
|
|
if (Config.has(PassIsVolatile)) {
|
|
IRTArgs.push_back(IRTArg(IIRB.Int8Ty, "is_volatile",
|
|
"Flag indicating a volatile load.", IRTArg::NONE,
|
|
isVolatile));
|
|
}
|
|
|
|
addCommonArgs(IConf, IIRB.Ctx, Config.has(PassId));
|
|
IConf.addChoice(*this, IIRB.Ctx);
|
|
}
|
|
|
|
Value *LoadIO::getPointer(Value &V, Type &Ty, InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
auto &LI = cast<LoadInst>(V);
|
|
return LI.getPointerOperand();
|
|
}
|
|
|
|
Value *LoadIO::setPointer(Value &V, Value &NewV, InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
auto &LI = cast<LoadInst>(V);
|
|
LI.setOperand(LI.getPointerOperandIndex(), &NewV);
|
|
return &LI;
|
|
}
|
|
|
|
Value *LoadIO::getPointerAS(Value &V, Type &Ty, InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
auto &LI = cast<LoadInst>(V);
|
|
return getCI(&Ty, LI.getPointerAddressSpace());
|
|
}
|
|
|
|
Value *LoadIO::getBasePointerInfo(Value &V, Type &Ty,
|
|
InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
auto &LI = cast<LoadInst>(V);
|
|
return IConf.getBasePointerInfo(*LI.getPointerOperand(), IIRB);
|
|
}
|
|
|
|
Value *LoadIO::getValue(Value &V, Type &Ty, InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
return &V;
|
|
}
|
|
|
|
Value *LoadIO::getValueSize(Value &V, Type &Ty, InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
auto &LI = cast<LoadInst>(V);
|
|
auto &DL = LI.getDataLayout();
|
|
return getCI(&Ty, DL.getTypeStoreSize(LI.getType()));
|
|
}
|
|
|
|
Value *LoadIO::getAlignment(Value &V, Type &Ty, InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
auto &LI = cast<LoadInst>(V);
|
|
return getCI(&Ty, LI.getAlign().value());
|
|
}
|
|
|
|
Value *LoadIO::getValueTypeId(Value &V, Type &Ty, InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
auto &LI = cast<LoadInst>(V);
|
|
return getCI(&Ty, LI.getType()->getTypeID());
|
|
}
|
|
|
|
Value *LoadIO::getAtomicityOrdering(Value &V, Type &Ty,
|
|
InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
auto &LI = cast<LoadInst>(V);
|
|
return getCI(&Ty, uint64_t(LI.getOrdering()));
|
|
}
|
|
|
|
Value *LoadIO::getSyncScopeId(Value &V, Type &Ty, InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
auto &LI = cast<LoadInst>(V);
|
|
return getCI(&Ty, uint64_t(LI.getSyncScopeID()));
|
|
}
|
|
|
|
Value *LoadIO::isVolatile(Value &V, Type &Ty, InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
auto &LI = cast<LoadInst>(V);
|
|
return getCI(&Ty, LI.isVolatile());
|
|
}
|
|
|
|
void BasePointerIO::init(InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB, ConfigTy *UserConfig) {
|
|
if (UserConfig)
|
|
Config = *UserConfig;
|
|
if (Config.has(PassPointer))
|
|
IRTArgs.push_back(IRTArg(IIRB.PtrTy, "base_pointer",
|
|
"The base pointer in question.",
|
|
IRTArg::REPLACABLE, getValue, setValueNoop));
|
|
if (Config.has(PassPointerKind))
|
|
IRTArgs.push_back(IRTArg(
|
|
IIRB.Int32Ty, "base_pointer_kind",
|
|
"The base pointer kind (argument, global, instruction, unknown).",
|
|
IRTArg::NONE, getPointerKind));
|
|
addCommonArgs(IConf, IIRB.Ctx, Config.has(PassId));
|
|
IConf.addChoice(*this, IIRB.Ctx);
|
|
}
|
|
|
|
Value *BasePointerIO::getPointerKind(Value &V, Type &Ty,
|
|
InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
if (isa<Argument>(V))
|
|
return getCI(&Ty, 0);
|
|
if (isa<GlobalValue>(V))
|
|
return getCI(&Ty, 1);
|
|
if (isa<Instruction>(V))
|
|
return getCI(&Ty, 2);
|
|
return getCI(&Ty, 3);
|
|
}
|
|
|
|
void ModuleIO::init(InstrumentationConfig &IConf, InstrumentorIRBuilderTy &IIRB,
|
|
ConfigTy *UserConfig) {
|
|
if (UserConfig)
|
|
Config = *UserConfig;
|
|
|
|
if (Config.has(PassName))
|
|
IRTArgs.push_back(IRTArg(IIRB.PtrTy, "module_name",
|
|
"The module/translation unit name.",
|
|
IRTArg::STRING, getModuleName));
|
|
if (Config.has(PassTargetTriple))
|
|
IRTArgs.push_back(IRTArg(IIRB.PtrTy, "target_triple", "The target triple.",
|
|
IRTArg::STRING, getTargetTriple));
|
|
|
|
addCommonArgs(IConf, IIRB.Ctx, Config.has(PassId));
|
|
IConf.addChoice(*this, IIRB.Ctx);
|
|
}
|
|
Value *ModuleIO::getModuleName(Value &V, Type &Ty, InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
// V is a constructor or destructor of the module we can place code in.
|
|
auto &Fn = cast<Function>(V);
|
|
return IConf.getGlobalString(Fn.getParent()->getName(), IIRB);
|
|
}
|
|
Value *ModuleIO::getTargetTriple(Value &V, Type &Ty,
|
|
InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
// V is a constructor or destructor of the module we can place code in.
|
|
auto &Fn = cast<Function>(V);
|
|
return IConf.getGlobalString(Fn.getParent()->getTargetTriple().getTriple(),
|
|
IIRB);
|
|
}
|
|
|
|
void GlobalVarIO::init(InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB, ConfigTy *UserConfig) {
|
|
if (UserConfig)
|
|
Config = *UserConfig;
|
|
bool IsPRE = InstrumentationLocation::isPRE(getLocationKind());
|
|
if (Config.has(PassAddress))
|
|
IRTArgs.push_back(IRTArg(
|
|
IIRB.PtrTy, "address",
|
|
"The address of the global (replaceable for definitions).",
|
|
IsPRE && Config.has(ReplaceAddress) ? IRTArg::REPLACABLE : IRTArg::NONE,
|
|
getAddress, setAddress));
|
|
if (Config.has(PassAS))
|
|
IRTArgs.push_back(IRTArg(IIRB.Int32Ty, "address_space",
|
|
"The address space of the global.", IRTArg::NONE,
|
|
getAS));
|
|
if (Config.has(PassDeclaredSize))
|
|
IRTArgs.push_back(IRTArg(IIRB.Int64Ty, "declared_size",
|
|
"The size of the declared type of the global.",
|
|
IRTArg::NONE, getDeclaredSize));
|
|
if (Config.has(PassAlignment))
|
|
IRTArgs.push_back(IRTArg(IIRB.Int64Ty, "alignment",
|
|
"The allocation alignment.", IRTArg::NONE,
|
|
getAlignment));
|
|
if (Config.has(PassName))
|
|
IRTArgs.push_back(IRTArg(IIRB.PtrTy, "name", "The name of the global.",
|
|
IRTArg::STRING, getSymbolName));
|
|
if (Config.has(PassInitialValue))
|
|
IRTArgs.push_back(IRTArg(
|
|
IIRB.Int64Ty, "initial_value", "The initial value of the global.",
|
|
IRTArg::POTENTIALLY_INDIRECT | IRTArg::INDIRECT_HAS_SIZE,
|
|
getInitialValue));
|
|
if (Config.has(PassIsConstant))
|
|
IRTArgs.push_back(IRTArg(IIRB.Int8Ty, "is_constant",
|
|
"Flag to indicate constant globals.", IRTArg::NONE,
|
|
isConstant));
|
|
if (Config.has(PassIsDefinition))
|
|
IRTArgs.push_back(IRTArg(IIRB.Int8Ty, "is_definition",
|
|
"Flag to indicate global definitions.",
|
|
IRTArg::NONE, isDefinition));
|
|
addCommonArgs(IConf, IIRB.Ctx, Config.has(PassId));
|
|
IConf.addChoice(*this, IIRB.Ctx);
|
|
}
|
|
Value *GlobalVarIO::getAddress(Value &V, Type &Ty, InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
GlobalVariable &GV = cast<GlobalVariable>(V);
|
|
if (GV.getAddressSpace())
|
|
return ConstantExpr::getAddrSpaceCast(&GV, IIRB.PtrTy);
|
|
return &GV;
|
|
}
|
|
Value *GlobalVarIO::setAddress(Value &V, Value &NewV,
|
|
InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
GlobalVariable &GV = cast<GlobalVariable>(V);
|
|
|
|
GlobalVariable *ShadowGV = nullptr;
|
|
auto ShadowName = IConf.getRTName("shadow.", GV.getName());
|
|
auto &DL = GV.getDataLayout();
|
|
if (GV.isDeclaration()) {
|
|
ShadowGV = new GlobalVariable(*GV.getParent(), GV.getType(), false,
|
|
GlobalVariable::WeakODRLinkage, &GV,
|
|
ShadowName, &GV, GV.getThreadLocalMode(),
|
|
DL.getDefaultGlobalsAddressSpace());
|
|
} else {
|
|
ShadowGV = new GlobalVariable(
|
|
*GV.getParent(), NewV.getType(), false, GV.getLinkage(),
|
|
PoisonValue::get(NewV.getType()), ShadowName, &GV);
|
|
IIRB.IRB.CreateStore(&NewV, ShadowGV);
|
|
}
|
|
|
|
SmallVector<Use *> Worklist(make_pointer_range(GV.uses()));
|
|
SmallPtrSet<Use *, 32> Done;
|
|
DenseMap<std::pair<Value *, Function *>, Instruction *> VMap;
|
|
DenseMap<Value *, Instruction *> ConstToInstMap;
|
|
DenseMap<Function *, Instruction *> ReloadMap;
|
|
|
|
auto MakeInstForConst = [&](Use &U) {
|
|
Instruction *&I = ConstToInstMap[U];
|
|
if (I)
|
|
return;
|
|
if (U == &GV) {
|
|
} else if (auto *CE = dyn_cast<ConstantExpr>(U)) {
|
|
I = CE->getAsInstruction();
|
|
}
|
|
};
|
|
|
|
auto InsertConsts = [&](Instruction *UserI, Use &UserU) {
|
|
SmallVector<std::pair<Instruction *, Use *>> Worklist;
|
|
auto *&Reload = ReloadMap[UserI->getFunction()];
|
|
if (!Reload) {
|
|
Reload = new LoadInst(
|
|
GV.getType(), ShadowGV, GV.getName() + ".shadow_load",
|
|
UserI->getFunction()->getEntryBlock().getFirstNonPHIOrDbgOrAlloca());
|
|
IIRB.NewInsts.insert({Reload, IIRB.Epoch});
|
|
}
|
|
Worklist.push_back({UserI, &UserU});
|
|
while (!Worklist.empty()) {
|
|
auto [I, U] = Worklist.pop_back_val();
|
|
if (*U == &GV) {
|
|
U->set(ReloadMap[I->getFunction()]);
|
|
continue;
|
|
}
|
|
if (auto *CI = ConstToInstMap[*U]) {
|
|
auto *CIClone = CI->clone();
|
|
IIRB.NewInsts.insert({CIClone, IIRB.Epoch});
|
|
if (auto *PHI = dyn_cast<PHINode>(I)) {
|
|
auto *BB = PHI->getIncomingBlock(U->getOperandNo());
|
|
CIClone->insertBefore(BB->getTerminator()->getIterator());
|
|
} else {
|
|
CIClone->insertBefore(I->getIterator());
|
|
}
|
|
U->set(CIClone);
|
|
for (auto &CICUse : CIClone->operands()) {
|
|
Worklist.push_back({CIClone, &CICUse});
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
SmallPtrSet<Use *, 8> Visited;
|
|
while (!Worklist.empty()) {
|
|
Use *U = Worklist.pop_back_val();
|
|
if (!Done.insert(U).second)
|
|
continue;
|
|
MakeInstForConst(*U);
|
|
auto *I = dyn_cast<Instruction>(U->getUser());
|
|
if (!I) {
|
|
append_range(Worklist, make_pointer_range(U->getUser()->uses()));
|
|
continue;
|
|
}
|
|
if (IIRB.NewInsts.lookup(I) == IIRB.Epoch)
|
|
continue;
|
|
if (isa<LandingPadInst>(I))
|
|
continue;
|
|
if (auto *II = dyn_cast<IntrinsicInst>(I))
|
|
if (II->getIntrinsicID() == Intrinsic::eh_typeid_for)
|
|
continue;
|
|
if (I->getParent())
|
|
InsertConsts(I, *U);
|
|
}
|
|
|
|
for (auto &It : ConstToInstMap)
|
|
if (It.second)
|
|
It.second->deleteValue();
|
|
|
|
return &V;
|
|
}
|
|
Value *GlobalVarIO::getAS(Value &V, Type &Ty, InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
GlobalVariable &GV = cast<GlobalVariable>(V);
|
|
return getCI(&Ty, GV.getAddressSpace());
|
|
}
|
|
Value *GlobalVarIO::getAlignment(Value &V, Type &Ty,
|
|
InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
GlobalVariable &GV = cast<GlobalVariable>(V);
|
|
return getCI(&Ty, GV.getAlignment());
|
|
}
|
|
Value *GlobalVarIO::getDeclaredSize(Value &V, Type &Ty,
|
|
InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
GlobalVariable &GV = cast<GlobalVariable>(V);
|
|
auto &DL = GV.getDataLayout();
|
|
return getCI(&Ty, DL.getTypeAllocSize(GV.getValueType()));
|
|
}
|
|
Value *GlobalVarIO::getSymbolName(Value &V, Type &Ty,
|
|
InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
GlobalVariable &GV = cast<GlobalVariable>(V);
|
|
return IConf.getGlobalString(GV.getName(), IIRB);
|
|
}
|
|
Value *GlobalVarIO::getInitialValue(Value &V, Type &Ty,
|
|
InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
GlobalVariable &GV = cast<GlobalVariable>(V);
|
|
return GV.hasInitializer() ? GV.getInitializer()
|
|
: Constant::getNullValue(&Ty);
|
|
}
|
|
Value *GlobalVarIO::isConstant(Value &V, Type &Ty, InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
GlobalVariable &GV = cast<GlobalVariable>(V);
|
|
return getCI(&Ty, GV.isConstant());
|
|
}
|
|
Value *GlobalVarIO::isDefinition(Value &V, Type &Ty,
|
|
InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
GlobalVariable &GV = cast<GlobalVariable>(V);
|
|
return getCI(&Ty, !GV.isDeclaration());
|
|
}
|
|
|
|
/// CastIO
|
|
/// {
|
|
void CastIO::init(InstrumentationConfig &IConf, InstrumentorIRBuilderTy &IIRB,
|
|
ConfigTy *UserConfig) {
|
|
if (UserConfig)
|
|
Config = *UserConfig;
|
|
bool IsPRE = getLocationKind() == InstrumentationLocation::INSTRUCTION_PRE;
|
|
if (Config.has(PassInput))
|
|
IRTArgs.push_back(IRTArg(IIRB.Int64Ty, "input", "Input value of the cast.",
|
|
IRTArg::POTENTIALLY_INDIRECT, getInput));
|
|
if (Config.has(PassInputTypeId))
|
|
IRTArgs.push_back(IRTArg(IIRB.Int32Ty, "input_type_id",
|
|
"The type id of the input value.", IRTArg::NONE,
|
|
getInputTypeId));
|
|
if (Config.has(PassInputSize))
|
|
IRTArgs.push_back(IRTArg(IIRB.Int32Ty, "input_size",
|
|
"The size of the input value.", IRTArg::NONE,
|
|
getInputSize));
|
|
if (!IsPRE && Config.has(PassResult))
|
|
IRTArgs.push_back(
|
|
IRTArg(IIRB.Int64Ty, "result", "Result of the cast.",
|
|
IRTArg::REPLACABLE | IRTArg::POTENTIALLY_INDIRECT, getValue,
|
|
Config.has(ReplaceResult) ? replaceValue : nullptr));
|
|
if (Config.has(PassResultTypeId))
|
|
IRTArgs.push_back(IRTArg(IIRB.Int32Ty, "result_type_id",
|
|
"The type id of the result value.", IRTArg::NONE,
|
|
getResultTypeId));
|
|
if (Config.has(PassResultSize))
|
|
IRTArgs.push_back(IRTArg(IIRB.Int32Ty, "result_size",
|
|
"The size of the result value.", IRTArg::NONE,
|
|
getResultSize));
|
|
if (Config.has(PassOpcode))
|
|
IRTArgs.push_back(IRTArg(IIRB.Int32Ty, "opcode",
|
|
"The opcode of the cast instruction.",
|
|
IRTArg::NONE, getOpcode));
|
|
|
|
addCommonArgs(IConf, IIRB.Ctx, Config.has(PassId));
|
|
IConf.addChoice(*this, IIRB.Ctx);
|
|
}
|
|
|
|
Value *CastIO::getInput(Value &V, Type &Ty, InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
auto &CI = cast<CastInst>(V);
|
|
return CI.getOperand(0);
|
|
}
|
|
|
|
Value *CastIO::getInputTypeId(Value &V, Type &Ty, InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
auto &CI = cast<CastInst>(V);
|
|
return getCI(&Ty, CI.getSrcTy()->getTypeID());
|
|
}
|
|
|
|
Value *CastIO::getInputSize(Value &V, Type &Ty, InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
auto &CI = cast<CastInst>(V);
|
|
auto &DL = CI.getDataLayout();
|
|
return getCI(&Ty, DL.getTypeStoreSize(CI.getSrcTy()));
|
|
}
|
|
|
|
Value *CastIO::getResultTypeId(Value &V, Type &Ty, InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
auto &CI = cast<CastInst>(V);
|
|
return getCI(&Ty, CI.getDestTy()->getTypeID());
|
|
}
|
|
|
|
Value *CastIO::getResultSize(Value &V, Type &Ty, InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
auto &CI = cast<CastInst>(V);
|
|
auto &DL = CI.getDataLayout();
|
|
return getCI(&Ty, DL.getTypeStoreSize(CI.getDestTy()));
|
|
}
|
|
///}
|
|
|
|
Value *NumericIO::getLeft(Value &V, Type &Ty, InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
auto &I = cast<Instruction>(V);
|
|
return I.getOperand(0);
|
|
}
|
|
|
|
Value *NumericIO::getRight(Value &V, Type &Ty, InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB) {
|
|
auto &I = cast<Instruction>(V);
|
|
if (I.getNumOperands() > 1)
|
|
return I.getOperand(1);
|
|
else
|
|
return PoisonValue::get(&Ty);
|
|
}
|
|
|
|
void NumericIO::init(InstrumentationConfig &IConf,
|
|
InstrumentorIRBuilderTy &IIRB, ConfigTy *UserConfig) {
|
|
if (UserConfig)
|
|
Config = UserConfig;
|
|
bool IsPRE = getLocationKind() == InstrumentationLocation::INSTRUCTION_PRE;
|
|
const auto ValArgOpts =
|
|
IRTArg::POTENTIALLY_INDIRECT |
|
|
(Config.has(PassSize) ? IRTArg::INDIRECT_HAS_SIZE : IRTArg::NONE);
|
|
if (Config.has(PassTypeId))
|
|
IRTArgs.push_back(IRTArg(IIRB.Int64Ty, "type_id",
|
|
"The operation's type id.", IRTArg::NONE,
|
|
getTypeId));
|
|
if (Config.has(PassSize))
|
|
IRTArgs.push_back(IRTArg(IIRB.Int32Ty, "size", "The operation's type size.",
|
|
IRTArg::NONE, getSize));
|
|
if (Config.has(PassOpcode))
|
|
IRTArgs.push_back(IRTArg(IIRB.Int32Ty, "opcode", "The instruction opcode.",
|
|
IRTArg::NONE, getOpcode));
|
|
if (Config.has(PassLeft))
|
|
IRTArgs.push_back(IRTArg(IIRB.Int64Ty, "left",
|
|
"The operation's left operand.", ValArgOpts,
|
|
getLeft));
|
|
if (Config.has(PassRight))
|
|
IRTArgs.push_back(IRTArg(IIRB.Int64Ty, "right",
|
|
"The operation's right operand. This value is "
|
|
"poison for unary operations.",
|
|
ValArgOpts, getRight));
|
|
if (!IsPRE && Config.has(PassResult))
|
|
IRTArgs.push_back(
|
|
IRTArg(IIRB.Int64Ty, "result", "Result of the operation.",
|
|
IRTArg::REPLACABLE | ValArgOpts, getValue,
|
|
Config.has(ReplaceResult) ? replaceValue : nullptr));
|
|
addCommonArgs(IConf, IIRB.Ctx, Config.has(PassId));
|
|
IConf.addChoice(*this, IIRB.Ctx);
|
|
}
|