| //===-- 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/InstrumentorRuntimeHelper.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/Dominators.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/Linker/Linker.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/Cloning.h" | |
| #include "llvm/Transforms/Utils/ModuleUtils.h" | |
| #include "llvm/Transforms/Utils/PromoteMemToReg.h" | |
| #include <cassert> | |
| #include <cstdint> | |
| #include <functional> | |
| #include <iterator> | |
| #include <memory> | |
| #include <string> | |
| #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::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() || VTy->isVectorTy()) | |
| return V; | |
| if (VTy->isPointerTy() && Ty->isPointerTy()) | |
| return IRB.CreatePointerBitCastOrAddrSpaceCast(V, Ty); | |
| 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->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); | |
| } | |
| Constant *getSubTypeID(Type &OpTy, Type &ReqTy) { | |
| switch (OpTy.getTypeID()) { | |
| case Type::TypeID::ArrayTyID: | |
| case Type::TypeID::FixedVectorTyID: | |
| case Type::TypeID::ScalableVectorTyID: | |
| return getCI(&ReqTy, OpTy.getContainedType(0)->getTypeID()); | |
| default: | |
| break; | |
| } | |
| return getCI(&ReqTy, -1, /*IsSigned=*/true); | |
| } | |
| /// 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: | |
| /// Link the runtime bitcodes into the module. Returns true if the module | |
| /// was changed. | |
| bool linkRuntime(); | |
| /// 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::linkRuntime() { | |
| ArrayRef<StringRef> RuntimeBitcodes = IConf.RuntimeBitcodes->getStringList(); | |
| if (RuntimeBitcodes.empty()) | |
| return false; | |
| bool Changed = false; | |
| for (StringRef RuntimeBitcode : RuntimeBitcodes) { | |
| if (RuntimeBitcode.empty()) | |
| continue; | |
| SMDiagnostic Err; | |
| auto RTM = parseIRFile(RuntimeBitcode, Err, M.getContext()); | |
| if (!RTM) { | |
| IIRB.Ctx.diagnose(DiagnosticInfoInstrumentation( | |
| Twine("Failed to parse runtime bitcode file '") + RuntimeBitcode + | |
| Twine("': ") + Err.getMessage(), | |
| DS_Error)); | |
| return Changed; | |
| } | |
| if (Linker::linkModules(M, std::move(RTM))) { | |
| IIRB.Ctx.diagnose(DiagnosticInfoInstrumentation( | |
| Twine("Failed to link in runtime bitcode file '") + RuntimeBitcode + | |
| "'", | |
| DS_Error)); | |
| return Changed; | |
| } | |
| Changed = true; | |
| } | |
| if (!IConf.InlineRuntimeEagerly->getBool()) | |
| return Changed; | |
| for (auto [I, _] : IIRB.NewInsts) { | |
| auto *CI = dyn_cast<CallInst>(I); | |
| if (!CI || isa<IntrinsicInst>(CI)) | |
| continue; | |
| InlineFunctionInfo IFI; | |
| auto InlineResult = InlineFunction(*CI, IFI); | |
| if (!InlineResult.isSuccess()) { | |
| std::string WarnMsg; | |
| raw_string_ostream SS(WarnMsg); | |
| SS << "Inlining of runtime call failed: " | |
| << CI->getCalledFunction()->getName() << "\n"; | |
| SS << "Reason: " << InlineResult.getFailureReason() << "\n"; | |
| SS << "Signatures: " << *CI->getFunctionType() << " vs " | |
| << *CI->getCalledFunction()->getFunctionType() << "\n"; | |
| IIRB.Ctx.diagnose(DiagnosticInfoInstrumentation(WarnMsg, DS_Warning)); | |
| } | |
| } | |
| // Promote any eligible instrumentor-associated allocas to registers. | |
| for (auto It : IIRB.AllocaMap) { | |
| auto *Fn = It.first.first; | |
| DominatorTree DT(*Fn); | |
| auto &Allocas = *It.second; | |
| erase_if(Allocas, | |
| [](const AllocaInst *AI) { return !isAllocaPromotable(AI); }); | |
| PromoteMemToReg(Allocas, DT); | |
| delete It.second; | |
| } | |
| IIRB.AllocaMap.clear(); | |
| return Changed; | |
| } | |
| 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; | |
| if (Fn.hasFnAttribute(Attribute::DisableSanitizerInstrumentation)) | |
| 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().getFirstNonPHIOrAlloca()); | |
| 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->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); | |
| Changed |= linkRuntime(); | |
| 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; | |
| } | |
| std::unique_ptr<BaseConfigurationOption> | |
| BaseConfigurationOption::createStringListOption( | |
| InstrumentationConfig &IConf, StringRef Name, StringRef Description, | |
| ArrayRef<StringRef> DefaultValue) { | |
| auto BCO = | |
| std::make_unique<BaseConfigurationOption>(Name, Description, STRING_LIST); | |
| BCO->setStringList(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); | |
| CompareIO::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.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() || | |
| Param->getType()->isVectorTy() || | |
| 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 = tryToCast(IIRB.IRB, AI, IIRB.PtrTy, DL); | |
| } | |
| } | |
| 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.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, GV->getAlign(), | |
| 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; | |
| } | |
| } | |
| Value *BaseInstructionIO::getOpcode(Value &V, Type &Ty, | |
| InstrumentationConfig &IConf, | |
| InstrumentorIRBuilderTy &IIRB) { | |
| auto &I = cast<Instruction>(V); | |
| return getCI(&Ty, I.getOpcode()); | |
| } | |
| Value *BaseInstructionIO::getTypeSize(Value &V, Type &Ty, | |
| InstrumentationConfig &IConf, | |
| InstrumentorIRBuilderTy &IIRB) { | |
| auto &I = cast<Instruction>(V); | |
| auto &DL = I.getDataLayout(); | |
| return getCI(&Ty, DL.getTypeStoreSize(V.getType())); | |
| } | |
| Value *BaseInstructionIO::getLeftOperand(Value &V, Type &Ty, | |
| InstrumentationConfig &IConf, | |
| InstrumentorIRBuilderTy &IIRB) { | |
| auto &I = cast<Instruction>(V); | |
| return I.getOperand(0); | |
| } | |
| Value *BaseInstructionIO::getRightOperand(Value &V, Type &Ty, | |
| InstrumentationConfig &IConf, | |
| InstrumentorIRBuilderTy &IIRB) { | |
| auto &I = cast<Instruction>(V); | |
| if (I.getNumOperands() > 1) | |
| return I.getOperand(1); | |
| return PoisonValue::get(&Ty); | |
| } | |
| Value *BaseInstructionIO::getTypeId(Value &V, Type &Ty, | |
| InstrumentationConfig &IConf, | |
| InstrumentorIRBuilderTy &IIRB) { | |
| return getCI(&Ty, V.getType()->getTypeID()); | |
| } | |
| Value *BaseInstructionIO::getSubTypeId(Value &V, Type &Ty, | |
| InstrumentationConfig &IConf, | |
| InstrumentorIRBuilderTy &IIRB) { | |
| return getSubTypeID(*V.getType(), Ty); | |
| } | |
| /// 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"); | |
| } | |
| /// 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); | |
| return IIRB.IRB.CreateAllocationSize(&Ty, &AI); | |
| } | |
| 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::TYPEID, | |
| getValueTypeId)); | |
| } | |
| if (Config.has(PassValueSubTypeId)) { | |
| IRTArgs.push_back(IRTArg( | |
| IIRB.Int32Ty, "value_sub_type_id", | |
| "The type id of the stored value (for arrays and vectors, or -1).", | |
| IRTArg::TYPEID, getValueSubTypeId)); | |
| } | |
| 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::getValueSubTypeId(Value &V, Type &Ty, | |
| InstrumentationConfig &IConf, | |
| InstrumentorIRBuilderTy &IIRB) { | |
| auto &SI = cast<StoreInst>(V); | |
| return getSubTypeID(*SI.getValueOperand()->getType(), Ty); | |
| } | |
| 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::TYPEID, | |
| getValueTypeId)); | |
| } | |
| if (Config.has(PassValueSubTypeId)) { | |
| IRTArgs.push_back(IRTArg( | |
| IIRB.Int32Ty, "value_sub_type_id", | |
| "The sub type id of the loaded value (for arrays and vectors, or -1).", | |
| IRTArg::TYPEID, getValueSubTypeId)); | |
| } | |
| 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::getValueSubTypeId(Value &V, Type &Ty, | |
| InstrumentationConfig &IConf, | |
| InstrumentorIRBuilderTy &IIRB) { | |
| auto &LI = cast<LoadInst>(V); | |
| return getSubTypeID(*LI.getType(), Ty); | |
| } | |
| 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().getFirstNonPHIOrAlloca()); | |
| 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); | |
| MaybeAlign Alignment = GV.getAlign(); | |
| return getCI(&Ty, Alignment ? Alignment->value() : 0); | |
| } | |
| 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 | | |
| (Config.has(PassResultSize) ? IRTArg::INDIRECT_HAS_SIZE | |
| : IRTArg::NONE), | |
| getInput)); | |
| if (Config.has(PassInputTypeId)) | |
| IRTArgs.push_back(IRTArg(IIRB.Int32Ty, "input_type_id", | |
| "The type id of the input value.", IRTArg::TYPEID, | |
| getInputTypeId)); | |
| if (Config.has(PassInputSubTypeId)) | |
| IRTArgs.push_back(IRTArg( | |
| IIRB.Int32Ty, "input_sub_type_id", | |
| "The sub type id of the input value (for arrays and vectors, or -1).", | |
| IRTArg::TYPEID, getInputSubTypeId)); | |
| 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) | | |
| (Config.has(PassResultSize) ? IRTArg::INDIRECT_HAS_SIZE | |
| : IRTArg::NONE), | |
| 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::TYPEID, | |
| getResultTypeId)); | |
| if (Config.has(PassResultSubTypeId)) | |
| IRTArgs.push_back(IRTArg( | |
| IIRB.Int32Ty, "result_sub_type_id", | |
| "The sub type id of the result value (for arrays and vectors, or -1).", | |
| IRTArg::TYPEID, getResultSubTypeId)); | |
| 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::getInputSubTypeId(Value &V, Type &Ty, | |
| InstrumentationConfig &IConf, | |
| InstrumentorIRBuilderTy &IIRB) { | |
| auto &CI = cast<CastInst>(V); | |
| return getSubTypeID(*CI.getSrcTy(), Ty); | |
| } | |
| 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::getResultSubTypeId(Value &V, Type &Ty, | |
| InstrumentationConfig &IConf, | |
| InstrumentorIRBuilderTy &IIRB) { | |
| auto &CI = cast<CastInst>(V); | |
| return getSubTypeID(*CI.getDestTy(), Ty); | |
| } | |
| 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::getFlags(Value &V, Type &Ty, InstrumentationConfig &IConf, | |
| InstrumentorIRBuilderTy &IIRB) { | |
| auto &I = cast<Instruction>(V); | |
| uint64_t Flag = NUMERIC_FLAG_NONE; | |
| switch (I.getOpcode()) { | |
| case Instruction::Add: | |
| case Instruction::Sub: | |
| case Instruction::Mul: | |
| case Instruction::Shl: | |
| if (I.hasNoSignedWrap()) | |
| Flag |= NUMERIC_FLAG_NO_SIGNED_WRAP; | |
| if (I.hasNoUnsignedWrap()) | |
| Flag |= NUMERIC_FLAG_NO_UNSIGNED_WRAP; | |
| break; | |
| case Instruction::FAdd: | |
| case Instruction::FSub: | |
| case Instruction::FMul: | |
| case Instruction::FDiv: | |
| case Instruction::FNeg: | |
| if (I.hasNoNaNs()) | |
| Flag |= NUMERIC_FLAG_HAS_NO_NANS; | |
| if (I.hasNoInfs()) | |
| Flag |= NUMERIC_FLAG_HAS_NO_INFS; | |
| if (I.hasNoSignedZeros()) | |
| Flag |= NUMERIC_FLAG_HAS_NO_SIGNED_ZEROS; | |
| break; | |
| case Instruction::AShr: | |
| case Instruction::LShr: | |
| case Instruction::SDiv: | |
| case Instruction::UDiv: | |
| if (I.isExact()) | |
| Flag |= NUMERIC_FLAG_IS_EXACT; | |
| break; | |
| } | |
| if (auto *DI = dyn_cast<PossiblyDisjointInst>(&V)) | |
| if (DI->isDisjoint()) | |
| Flag |= NUMERIC_FLAG_IS_DISJOINT; | |
| return getCI(&Ty, Flag); | |
| } | |
| void NumericIO::addFlagNames() { | |
| FlagNames["nsw"] = NUMERIC_FLAG_NO_SIGNED_WRAP; | |
| FlagNames["nuw"] = NUMERIC_FLAG_NO_UNSIGNED_WRAP; | |
| FlagNames["nnan"] = NUMERIC_FLAG_HAS_NO_NANS; | |
| FlagNames["ninf"] = NUMERIC_FLAG_HAS_NO_INFS; | |
| FlagNames["nsz"] = NUMERIC_FLAG_HAS_NO_SIGNED_ZEROS; | |
| FlagNames["exact"] = NUMERIC_FLAG_IS_EXACT; | |
| } | |
| 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.Int32Ty, "type_id", | |
| "The operation's type id.", IRTArg::TYPEID, | |
| getTypeId)); | |
| if (Config.has(PassSubTypeId)) | |
| IRTArgs.push_back( | |
| IRTArg(IIRB.Int32Ty, "sub_type_id", | |
| "The operation's sub type id (for arrays and vectors, or -1).", | |
| IRTArg::TYPEID, getSubTypeId)); | |
| if (Config.has(PassSize)) | |
| IRTArgs.push_back(IRTArg(IIRB.Int32Ty, "size", "The operation's type size.", | |
| IRTArg::NONE, getTypeSize)); | |
| 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, | |
| getLeftOperand)); | |
| if (Config.has(PassRight)) | |
| IRTArgs.push_back(IRTArg(IIRB.Int64Ty, "right", | |
| "The operation's right operand. This value is " | |
| "poison for unary operations.", | |
| ValArgOpts, getRightOperand)); | |
| 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)); | |
| if (Config.has(PassFlags)) | |
| IRTArgs.push_back( | |
| IRTArg(IIRB.Int64Ty, "flags", | |
| "A bitmask value signaling which instruction flags are present.", | |
| IRTArg::NONE, getFlags)); | |
| addCommonArgs(IConf, IIRB.Ctx, Config.has(PassId)); | |
| addFlagNames(); | |
| IConf.addChoice(*this, IIRB.Ctx); | |
| } | |
| Value *CompareIO::getOperandTypeId(Value &V, Type &Ty, | |
| InstrumentationConfig &IConf, | |
| InstrumentorIRBuilderTy &IIRB) { | |
| auto &I = cast<Instruction>(V); | |
| return getCI(&Ty, I.getOperand(0)->getType()->getTypeID()); | |
| } | |
| Value *CompareIO::getOperandSize(Value &V, Type &Ty, | |
| InstrumentationConfig &IConf, | |
| InstrumentorIRBuilderTy &IIRB) { | |
| auto &I = cast<Instruction>(V); | |
| auto &DL = I.getDataLayout(); | |
| return getCI(&Ty, DL.getTypeStoreSize(I.getOperand(0)->getType())); | |
| } | |
| Value *CompareIO::getPredicate(Value &V, Type &Ty, InstrumentationConfig &IConf, | |
| InstrumentorIRBuilderTy &IIRB) { | |
| auto *CI = dyn_cast<CmpInst>(&V); | |
| return getCI(&Ty, CI->getPredicate()); | |
| } | |
| void CompareIO::addFlagNames() { | |
| FlagNames["samesign"] = COMPARE_FLAG_SAMESIGN; | |
| FlagNames["nnan"] = COMPARE_FLAG_HAS_NO_NANS; | |
| FlagNames["ninf"] = COMPARE_FLAG_HAS_NO_INFS; | |
| FlagNames["nsz"] = COMPARE_FLAG_HAS_NO_SIGNED_ZEROS; | |
| } | |
| Value *CompareIO::getFlags(Value &V, Type &Ty, InstrumentationConfig &IConf, | |
| InstrumentorIRBuilderTy &IIRB) { | |
| auto &I = cast<Instruction>(V); | |
| uint64_t Flag = NUMERIC_FLAG_NONE; | |
| switch (I.getOpcode()) { | |
| case Instruction::ICmp: | |
| if (dyn_cast<ICmpInst>(&V)->hasSameSign()) | |
| Flag |= COMPARE_FLAG_SAMESIGN; | |
| break; | |
| case Instruction::FCmp: | |
| if (I.hasNoNaNs()) | |
| Flag |= COMPARE_FLAG_HAS_NO_NANS; | |
| if (I.hasNoInfs()) | |
| Flag |= COMPARE_FLAG_HAS_NO_INFS; | |
| if (I.hasNoSignedZeros()) | |
| Flag |= COMPARE_FLAG_HAS_NO_SIGNED_ZEROS; | |
| break; | |
| } | |
| return getCI(&Ty, Flag); | |
| } | |
| void CompareIO::init(InstrumentationConfig &IConf, | |
| InstrumentorIRBuilderTy &IIRB, ConfigTy *UserConfig) { | |
| if (UserConfig) | |
| Config = UserConfig; | |
| bool IsPRE = getLocationKind() == InstrumentationLocation::INSTRUCTION_PRE; | |
| const auto OperandArgOpts = | |
| IRTArg::POTENTIALLY_INDIRECT | | |
| (Config.has(PassOpSize) ? IRTArg::INDIRECT_HAS_SIZE : IRTArg::NONE); | |
| if (Config.has(PassOpTypeId)) | |
| IRTArgs.push_back(IRTArg(IIRB.Int32Ty, "operand_type_id", | |
| "The operand type id.", IRTArg::NONE, | |
| getOperandTypeId)); | |
| if (Config.has(PassOpSize)) | |
| IRTArgs.push_back(IRTArg(IIRB.Int32Ty, "operand_size", | |
| "The operand type size.", IRTArg::NONE, | |
| getOperandSize)); | |
| if (Config.has(PassOpcode)) | |
| IRTArgs.push_back(IRTArg(IIRB.Int32Ty, "opcode", "The instruction opcode.", | |
| IRTArg::NONE, getOpcode)); | |
| if (Config.has(PassPredicate)) | |
| IRTArgs.push_back(IRTArg(IIRB.Int32Ty, "predicate", | |
| "The comparison predicate ID.", IRTArg::NONE, | |
| getPredicate)); | |
| if (Config.has(PassLeft)) | |
| IRTArgs.push_back(IRTArg(IIRB.Int64Ty, "left", | |
| "The comparison's left operand.", OperandArgOpts, | |
| getLeftOperand)); | |
| if (Config.has(PassRight)) | |
| IRTArgs.push_back(IRTArg(IIRB.Int64Ty, "right", | |
| "The comparison's right operand.", OperandArgOpts, | |
| getRightOperand)); | |
| if (!IsPRE && Config.has(PassResultSize)) | |
| IRTArgs.push_back(IRTArg(IIRB.Int32Ty, "result_type_id", | |
| "The result value's type ID.", IRTArg::NONE, | |
| getTypeId)); | |
| if (!IsPRE && Config.has(PassResultSize)) | |
| IRTArgs.push_back(IRTArg(IIRB.Int32Ty, "result_size", | |
| "Size of the result value.", IRTArg::NONE, | |
| getTypeSize)); | |
| if (!IsPRE && Config.has(PassResult)) | |
| IRTArgs.push_back( | |
| IRTArg(IIRB.Int64Ty, "result", "Result of the operation.", | |
| IRTArg::REPLACABLE | IRTArg::POTENTIALLY_INDIRECT | | |
| (Config.has(PassResultSize) ? IRTArg::INDIRECT_HAS_SIZE | |
| : IRTArg::NONE), | |
| getValue, Config.has(ReplaceResult) ? replaceValue : nullptr)); | |
| if (Config.has(PassFlags)) | |
| IRTArgs.push_back( | |
| IRTArg(IIRB.Int64Ty, "flags", | |
| "A bitmask value signaling which instruction flags are present.", | |
| IRTArg::NONE, getFlags)); | |
| addFlagNames(); | |
| addCommonArgs(IConf, IIRB.Ctx, Config.has(PassId)); | |
| IConf.addChoice(*this, IIRB.Ctx); | |
| } |