| //===-- 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); |
| } |