[llvm] [Instrumentor] A configurable instrumentation pass (PR #119038)
Johannes Doerfert via llvm-commits
llvm-commits at lists.llvm.org
Fri Dec 6 17:55:55 PST 2024
https://github.com/jdoerfert updated https://github.com/llvm/llvm-project/pull/119038
>From 17d32c479da8b35f96d235780a631a38799f51b7 Mon Sep 17 00:00:00 2001
From: Johannes Doerfert <johannes at jdoerfert.de>
Date: Wed, 4 Dec 2024 17:00:37 -0800
Subject: [PATCH] [Instrumentor] A configurable instrumentation pass
Both internally and externally we instrument code all the time. Usually,
those are hand written traversals that look for some specific
instructions or program points and then provide information to a
runtime. Since the real difference is in the runtime, and the
instrumentation is basically always the same, people can use this
Instrumentor pass and configure it to their needs.
Initial implementation only instruments alloca instructions but shows
the setup, and configurability via a JSON file.
---
.../Transforms/Instrumentation/Instrumentor.h | 56 +++
.../Instrumentation/InstrumentorConfig.def | 56 +++
llvm/lib/Passes/PassBuilder.cpp | 1 +
llvm/lib/Passes/PassRegistry.def | 1 +
.../Transforms/Instrumentation/CMakeLists.txt | 1 +
.../Instrumentation/Instrumentor.cpp | 341 ++++++++++++++++++
.../Instrumentation/Instrumentor/alloca.ll | 62 ++++
.../Instrumentor/default_config.json | 13 +
.../Instrumentor/just_calls_config.json | 14 +
.../Instrumentor/read_config.ll | 22 ++
.../Instrumentor/write_config.ll | 3 +
11 files changed, 570 insertions(+)
create mode 100644 llvm/include/llvm/Transforms/Instrumentation/Instrumentor.h
create mode 100644 llvm/include/llvm/Transforms/Instrumentation/InstrumentorConfig.def
create mode 100644 llvm/lib/Transforms/Instrumentation/Instrumentor.cpp
create mode 100644 llvm/test/Instrumentation/Instrumentor/alloca.ll
create mode 100644 llvm/test/Instrumentation/Instrumentor/default_config.json
create mode 100644 llvm/test/Instrumentation/Instrumentor/just_calls_config.json
create mode 100644 llvm/test/Instrumentation/Instrumentor/read_config.ll
create mode 100644 llvm/test/Instrumentation/Instrumentor/write_config.ll
diff --git a/llvm/include/llvm/Transforms/Instrumentation/Instrumentor.h b/llvm/include/llvm/Transforms/Instrumentation/Instrumentor.h
new file mode 100644
index 00000000000000..0da4562be6a7a7
--- /dev/null
+++ b/llvm/include/llvm/Transforms/Instrumentation/Instrumentor.h
@@ -0,0 +1,56 @@
+//===- Transforms/Instrumentation/Instrumentor.h --------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// A highly configurable instrumentation pass.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TRANSFORMS_INSTRUMENTATION_INSTRUMENTOR_H
+#define LLVM_TRANSFORMS_INSTRUMENTATION_INSTRUMENTOR_H
+
+#include "llvm/IR/Instruction.h"
+#include "llvm/IR/PassManager.h"
+
+#include <functional>
+
+namespace llvm {
+
+/// Configuration for the Instrumentor. First generic configuration, followed by
+/// the selection of what instruction classes and instructions should be
+/// instrumented and how.
+struct InstrumentorConfig {
+
+ /// An optional callback that takes the instruction that is about to be
+ /// instrumented and can return false if it should be skipped.
+ using CallbackTy = std::function<bool(Instruction &)>;
+
+#define SECTION_START(SECTION, CLASS) struct {
+
+#define CONFIG_INTERNAL(SECTION, TYPE, NAME, DEFAULT_VALUE) \
+ TYPE NAME = DEFAULT_VALUE;
+
+#define CONFIG(SECTION, TYPE, NAME, DEFAULT_VALUE) TYPE NAME = DEFAULT_VALUE;
+
+#define SECTION_END(SECTION) \
+ } \
+ SECTION;
+
+#include "llvm/Transforms/Instrumentation/InstrumentorConfig.def"
+};
+
+class InstrumentorPass : public PassInfoMixin<InstrumentorPass> {
+ InstrumentorConfig IC;
+
+public:
+ InstrumentorPass(InstrumentorConfig IC = InstrumentorConfig{}) : IC(IC) {}
+
+ PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM);
+};
+} // end namespace llvm
+
+#endif // LLVM_TRANSFORMS_INSTRUMENTATION_INSTRUMENTOR_H
diff --git a/llvm/include/llvm/Transforms/Instrumentation/InstrumentorConfig.def b/llvm/include/llvm/Transforms/Instrumentation/InstrumentorConfig.def
new file mode 100644
index 00000000000000..1d45919608b322
--- /dev/null
+++ b/llvm/include/llvm/Transforms/Instrumentation/InstrumentorConfig.def
@@ -0,0 +1,56 @@
+//===- Transforms/Instrumentation/InstrumentorConfig.def ------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+//===----------------------------------------------------------------------===//
+
+// No include guards
+
+/// Base configuration
+///{
+SECTION_START(Base, )
+
+/// The base name which defines the runtime call names, i.a.,
+/// <BaseName><instruction/location>(...)
+CONFIG(Base, std::string, RuntimeName, "__instrumentor_")
+
+/// Print the signatures of all used runtime functions.
+CONFIG(Base, bool, PrintRuntimeSignatures, true)
+
+SECTION_END(Base)
+///}
+
+/// AllocaInst
+///{
+SECTION_START(Alloca, AllocaInst)
+
+/// Should allocas be instrumented.
+CONFIG(Alloca, bool, Instrument, true)
+
+/// Selection of information passed to the runtime.
+///{
+/// The actual allocated pointer.
+CONFIG(Alloca, bool, Value, /* PtrTy */ true)
+/// The size of the entire allocation.
+CONFIG(Alloca, bool, AllocationSize, /* I64 */ true)
+/// The minimal alignment requested statically.
+CONFIG(Alloca, bool, Alignment, /* I64 */ true)
+///}
+
+/// Should the value be replaced by the runtime call result.
+CONFIG(Alloca, bool, ReplaceValue, true)
+
+/// Optional callback, see CallbackTy.
+CONFIG_INTERNAL(Alloca, CallbackTy, CB, nullptr)
+
+SECTION_END(Alloca)
+///}
+
+#undef SECTION_START
+#undef CONFIG
+#undef CONFIG_INTERNAL
+#undef SECTION_END
diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp
index 260a34f2e060d6..3997275075e87e 100644
--- a/llvm/lib/Passes/PassBuilder.cpp
+++ b/llvm/lib/Passes/PassBuilder.cpp
@@ -211,6 +211,7 @@
#include "llvm/Transforms/Instrumentation/HWAddressSanitizer.h"
#include "llvm/Transforms/Instrumentation/InstrOrderFile.h"
#include "llvm/Transforms/Instrumentation/InstrProfiling.h"
+#include "llvm/Transforms/Instrumentation/Instrumentor.h"
#include "llvm/Transforms/Instrumentation/KCFI.h"
#include "llvm/Transforms/Instrumentation/LowerAllowCheckPass.h"
#include "llvm/Transforms/Instrumentation/MemProfiler.h"
diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def
index 772ec5fd10e633..268fe1f7c0ab32 100644
--- a/llvm/lib/Passes/PassRegistry.def
+++ b/llvm/lib/Passes/PassRegistry.def
@@ -91,6 +91,7 @@ MODULE_PASS("instrprof", InstrProfilingLoweringPass())
MODULE_PASS("ctx-instr-lower", PGOCtxProfLoweringPass())
MODULE_PASS("print<ctx-prof-analysis>", CtxProfAnalysisPrinterPass(errs()))
MODULE_PASS("invalidate<all>", InvalidateAllAnalysesPass())
+MODULE_PASS("instrumentor", InstrumentorPass())
MODULE_PASS("iroutliner", IROutlinerPass())
MODULE_PASS("jmc-instrumenter", JMCInstrumenterPass())
MODULE_PASS("lower-emutls", LowerEmuTLSPass())
diff --git a/llvm/lib/Transforms/Instrumentation/CMakeLists.txt b/llvm/lib/Transforms/Instrumentation/CMakeLists.txt
index 3e3c3eced4bb9c..279aff368d0712 100644
--- a/llvm/lib/Transforms/Instrumentation/CMakeLists.txt
+++ b/llvm/lib/Transforms/Instrumentation/CMakeLists.txt
@@ -12,6 +12,7 @@ add_llvm_component_library(LLVMInstrumentation
IndirectCallPromotion.cpp
InstrOrderFile.cpp
InstrProfiling.cpp
+ Instrumentor.cpp
KCFI.cpp
LowerAllowCheckPass.cpp
PGOCtxProfFlattening.cpp
diff --git a/llvm/lib/Transforms/Instrumentation/Instrumentor.cpp b/llvm/lib/Transforms/Instrumentation/Instrumentor.cpp
new file mode 100644
index 00000000000000..6c9242126bff70
--- /dev/null
+++ b/llvm/lib/Transforms/Instrumentation/Instrumentor.cpp
@@ -0,0 +1,341 @@
+//===-- 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
+//
+//===----------------------------------------------------------------------===//
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Transforms/Instrumentation/Instrumentor.h"
+
+#include "llvm/ADT/PostOrderIterator.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/IR/BasicBlock.h"
+#include "llvm/IR/CFG.h"
+#include "llvm/IR/ConstantFolder.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/DataLayout.h"
+#include "llvm/IR/DerivedTypes.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/InstIterator.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/Verifier.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/JSON.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/raw_ostream.h"
+#include <type_traits>
+
+using namespace llvm;
+
+#define DEBUG_TYPE "instrumentor"
+
+cl::opt<std::string> WriteJSONConfig(
+ "instrumentor-write-config-file",
+ cl::desc(
+ "Write the instrumentor configuration into the specified JSON file"),
+ cl::init(""));
+cl::opt<std::string> ReadJSONConfig(
+ "instrumentor-read-config-file",
+ cl::desc(
+ "Read the instrumentor configuration from the specified JSON file"),
+ cl::init(""));
+
+namespace {
+
+template <typename... Targs>
+void dumpObject(json::OStream &J, Targs... Fargs) {}
+
+void writeInstrumentorConfig(InstrumentorConfig &IC) {
+ if (WriteJSONConfig.empty())
+ return;
+
+ std::error_code EC;
+ raw_fd_stream OS(WriteJSONConfig, EC);
+ if (EC) {
+ errs() << "WARNING: Failed to open instrumentor configuration file for "
+ "writing: "
+ << EC.message() << "\n";
+ return;
+ }
+
+ json::OStream J(OS, 2);
+ J.objectBegin();
+
+#define SECTION_START(SECTION, CLASS) \
+ J.attributeBegin(#SECTION); \
+ J.objectBegin();
+#define CONFIG_INTERNAL(SECTION, TYPE, NAME, DEFAULT_VALUE)
+#define CONFIG(SECTION, TYPE, NAME, DEFAULT_VALUE) \
+ J.attribute(#NAME, IC.SECTION.NAME);
+#define SECTION_END(SECTION) \
+ J.objectEnd(); \
+ J.attributeEnd();
+
+#include "llvm/Transforms/Instrumentation/InstrumentorConfig.def"
+
+ J.objectEnd();
+}
+
+bool readInstrumentorConfigFromJSON(InstrumentorConfig &IC) {
+ if (ReadJSONConfig.empty())
+ return true;
+
+ std::error_code EC;
+ auto BufferOrErr = MemoryBuffer::getFileOrSTDIN(ReadJSONConfig);
+ if (std::error_code EC = BufferOrErr.getError()) {
+ errs() << "WARNING: Failed to open instrumentor configuration file for "
+ "reading: "
+ << EC.message() << "\n";
+ return false;
+ }
+ auto Buffer = std::move(BufferOrErr.get());
+ json::Path::Root NullRoot;
+ auto Parsed = json::parse(Buffer->getBuffer());
+ if (!Parsed) {
+ errs() << "WARNING: Failed to parse the instrumentor configuration file: "
+ << Parsed.takeError() << "\n";
+ return false;
+ }
+ auto *Config = Parsed->getAsObject();
+ if (!Config) {
+ errs() << "WARNING: Failed to parse the instrumentor configuration file: "
+ "Expected "
+ "an object '{ ... }'\n";
+ return false;
+ }
+
+ auto End = Config->end(), It = Config->begin();
+
+#define CONFIG(SECTION, TYPE, NAME, DEFAULT_VALUE) \
+ It = Config->find(#SECTION); \
+ if (It != End) { \
+ if (auto *InstObj = It->second.getAsObject()) { \
+ if (auto *Val = InstObj->get(#NAME)) { \
+ if (!json::fromJSON(*Val, IC.SECTION.NAME, NullRoot)) \
+ errs() << "WARNING: Failed to read " #SECTION "." #NAME " as " #TYPE \
+ << "\n"; \
+ } \
+ } \
+ }
+
+#define SECTION_START(SECTION, CLASS)
+#define CONFIG_INTERNAL(SECTION, TYPE, NAME, DEFAULT_VALUE)
+#define SECTION_END(SECTION)
+
+#include "llvm/Transforms/Instrumentation/InstrumentorConfig.def"
+
+ return true;
+}
+
+raw_ostream &printAsCType(raw_ostream &OS, Type *T) {
+ if (T->isPointerTy())
+ return OS << "void* ";
+ if (T->isIntegerTy())
+ return OS << "int" << T->getIntegerBitWidth() << "_t ";
+ return OS << *T << " ";
+}
+
+class InstrumentorImpl final {
+public:
+ InstrumentorImpl(const InstrumentorConfig &IC, Module &M)
+ : IC(IC), M(M), Ctx(M.getContext()),
+ IRB(Ctx, ConstantFolder(),
+ IRBuilderCallbackInserter(
+ [&](Instruction *I) { NewInsts[I] = Epoche; })) {}
+
+ /// Instrument the module, public entry point.
+ bool instrument();
+
+private:
+ bool shouldInstrumentFunction(Function *Fn);
+ bool instrumentFunction(Function &Fn);
+ bool instrument(AllocaInst &I);
+
+ template <typename Ty> Constant *getCI(Type *IT, Ty Val) {
+ return ConstantInt::get(IT, Val);
+ }
+
+ std::string getRTName(StringRef Suffix) {
+ return (IC.Base.RuntimeName + Suffix).str();
+ }
+
+ DenseMap<unsigned, FunctionCallee> InstrumentationFunctions;
+ FunctionCallee getCallee(Instruction &I, SmallVectorImpl<Type *> &RTArgTypes,
+ SmallVectorImpl<std::string> &RTArgNames,
+ Type *RT = nullptr) {
+ FunctionCallee &FC = InstrumentationFunctions[I.getOpcode()];
+ if (!FC.getFunctionType()) {
+ FC = M.getOrInsertFunction(
+ getRTName(I.getOpcodeName()),
+ FunctionType::get(RT ? RT : VoidTy, RTArgTypes, /*IsVarArgs*/ false));
+
+ if (IC.Base.PrintRuntimeSignatures) {
+ printAsCType(outs(), FC.getFunctionType()->getReturnType());
+ outs() << FC.getCallee()->getName() << "(";
+ auto *FT = FC.getFunctionType();
+ for (int I = 0, E = RTArgNames.size(); I != E; ++I) {
+ if (I != 0)
+ outs() << ", ";
+ printAsCType(outs(), FT->getParamType(I)) << RTArgNames[I];
+ }
+ outs() << ");\n";
+ }
+ }
+ return FC;
+ }
+
+ /// Each instrumentation, i.a., of an instruction, is happening in a dedicated
+ /// epoche. The epoche allows to determine if instrumentation instructions
+ /// were already around, due to prior instrumentations, or have been
+ /// introduced to support the current instrumentation, i.a., compute
+ /// information about the current instruction.
+ unsigned Epoche = 0;
+
+ /// A mapping from instrumentation instructions to the epoche they have been
+ /// created.
+ DenseMap<Instruction *, unsigned> NewInsts;
+
+ /// The instrumentor configuration.
+ const InstrumentorConfig &IC;
+
+ /// The module and the LLVM context.
+ Module &M;
+ LLVMContext &Ctx;
+
+ /// A special IR builder that keeps track of the inserted instructions.
+ IRBuilder<ConstantFolder, IRBuilderCallbackInserter> IRB;
+
+ /// Commonly used values for IR inspection and creation.
+ ///{
+
+ const DataLayout &DL = M.getDataLayout();
+
+ Type *VoidTy = Type::getVoidTy(Ctx);
+ Type *IntptrTy = M.getDataLayout().getIntPtrType(Ctx);
+ PointerType *PtrTy = PointerType::getUnqual(Ctx);
+ IntegerType *Int8Ty = Type::getInt8Ty(Ctx);
+ IntegerType *Int32Ty = Type::getInt32Ty(Ctx);
+ IntegerType *Int64Ty = Type::getInt64Ty(Ctx);
+ ///}
+};
+
+} // end anonymous namespace
+
+bool InstrumentorImpl::shouldInstrumentFunction(Function *Fn) {
+ if (!Fn || Fn->isDeclaration())
+ return false;
+ return true;
+}
+
+bool InstrumentorImpl::instrument(AllocaInst &I) {
+ if (IC.Alloca.CB && !IC.Alloca.CB(I))
+ return false;
+
+ Instruction *IP = I.getNextNonDebugInstruction();
+ while (isa<AllocaInst>(IP))
+ IP = IP->getNextNonDebugInstruction();
+ IRB.SetInsertPoint(IP);
+
+ SmallVector<Type *> RTArgTypes;
+ SmallVector<Value *> RTArgs;
+ SmallVector<std::string> RTArgNames;
+
+ if (IC.Alloca.Value) {
+ auto *ArgTy = PtrTy;
+ RTArgTypes.push_back(ArgTy);
+ RTArgs.push_back(IRB.CreatePointerBitCastOrAddrSpaceCast(&I, ArgTy));
+ RTArgNames.push_back("Value");
+ }
+
+ if (IC.Alloca.AllocationSize) {
+ auto *ArgTy = Int64Ty;
+ RTArgTypes.push_back(ArgTy);
+ Value *SizeValue = nullptr;
+ TypeSize TypeSize = DL.getTypeAllocSize(I.getAllocatedType());
+ if (TypeSize.isFixed())
+ SizeValue = getCI(ArgTy, TypeSize.getFixedValue());
+ if (!SizeValue) {
+ auto *LHS = IRB.CreatePtrToInt(
+ IRB.CreateGEP(I.getAllocatedType(), &I, {getCI(Int32Ty, 1)}), ArgTy);
+ SizeValue = IRB.CreateSub(LHS, IRB.CreatePtrToInt(&I, ArgTy));
+ }
+ if (I.isArrayAllocation())
+ SizeValue = IRB.CreateMul(
+ SizeValue, IRB.CreateZExtOrBitCast(I.getArraySize(), ArgTy));
+ RTArgs.push_back(SizeValue);
+ RTArgNames.push_back("AllocationSize");
+ }
+
+ if (IC.Alloca.Alignment) {
+ auto *ArgTy = Int64Ty;
+ RTArgTypes.push_back(ArgTy);
+ RTArgs.push_back(getCI(ArgTy, I.getAlign().value()));
+ RTArgNames.push_back("Alignment");
+ }
+
+ Type *RetTy = IC.Alloca.ReplaceValue ? PtrTy : nullptr;
+ FunctionCallee FC = getCallee(I, RTArgTypes, RTArgNames, RetTy);
+ auto *CI = IRB.CreateCall(FC, RTArgs);
+ if (IC.Alloca.ReplaceValue)
+ I.replaceUsesWithIf(
+ IRB.CreatePointerBitCastOrAddrSpaceCast(CI, I.getType()), [&](Use &U) {
+ return NewInsts.lookup(cast<Instruction>(U.getUser())) != Epoche;
+ });
+
+ return true;
+}
+
+bool InstrumentorImpl::instrumentFunction(Function &Fn) {
+ bool Changed = false;
+ if (!shouldInstrumentFunction(&Fn))
+ return Changed;
+
+ ReversePostOrderTraversal<Function *> RPOT(&Fn);
+ for (auto &It : RPOT) {
+ for (auto &I : *It) {
+ // Skip instrumentation instructions.
+ if (NewInsts.contains(&I))
+ continue;
+
+ // Count epochs eagerly.
+ ++Epoche;
+
+ switch (I.getOpcode()) {
+ case Instruction::Alloca:
+ if (IC.Alloca.Instrument)
+ instrument(cast<AllocaInst>(I));
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ return Changed;
+}
+
+bool InstrumentorImpl::instrument() {
+ bool Changed = false;
+
+ for (Function &Fn : M)
+ Changed |= instrumentFunction(Fn);
+
+ return Changed;
+}
+
+PreservedAnalyses InstrumentorPass::run(Module &M, ModuleAnalysisManager &MAM) {
+ InstrumentorImpl Impl(IC, M);
+ if (!readInstrumentorConfigFromJSON(IC))
+ return PreservedAnalyses::all();
+ writeInstrumentorConfig(IC);
+ if (!Impl.instrument())
+ return PreservedAnalyses::all();
+ assert(!verifyModule(M, &errs()));
+ return PreservedAnalyses::none();
+}
diff --git a/llvm/test/Instrumentation/Instrumentor/alloca.ll b/llvm/test/Instrumentation/Instrumentor/alloca.ll
new file mode 100644
index 00000000000000..1cb90c521ff428
--- /dev/null
+++ b/llvm/test/Instrumentation/Instrumentor/alloca.ll
@@ -0,0 +1,62 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt < %s -passes=instrumentor -S | FileCheck %s
+
+target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+
+define ptr @foo() {
+; CHECK-LABEL: define ptr @foo() {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[A:%.*]] = alloca ptr, align 8
+; CHECK-NEXT: [[TMP0:%.*]] = call ptr @__instrumentor_alloca(ptr [[A]], i64 8, i64 8)
+; CHECK-NEXT: ret ptr [[TMP0]]
+;
+entry:
+ %a = alloca ptr
+ ret ptr %a
+}
+define ptr @bar(i1 %c) {
+; CHECK-LABEL: define ptr @bar(
+; CHECK-SAME: i1 [[C:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4
+; CHECK-NEXT: [[B:%.*]] = alloca i32, i32 5, align 4
+; CHECK-NEXT: [[TMP0:%.*]] = call ptr @__instrumentor_alloca(ptr [[B]], i64 20, i64 4)
+; CHECK-NEXT: [[TMP1:%.*]] = call ptr @__instrumentor_alloca(ptr [[A]], i64 4, i64 4)
+; CHECK-NEXT: [[S:%.*]] = select i1 [[C]], ptr [[TMP1]], ptr [[TMP0]]
+; CHECK-NEXT: ret ptr [[S]]
+;
+entry:
+ %a = alloca i32
+ %b = alloca i32, i32 5
+ %s = select i1 %c, ptr %a, ptr %b
+ ret ptr %s
+}
+define ptr @baz(i32 %v) {
+; CHECK-LABEL: define ptr @baz(
+; CHECK-SAME: i32 [[V:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[A:%.*]] = alloca ptr, i32 [[V]], align 8
+; CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[V]] to i64
+; CHECK-NEXT: [[TMP2:%.*]] = mul i64 8, [[TMP0]]
+; CHECK-NEXT: [[TMP1:%.*]] = call ptr @__instrumentor_alloca(ptr [[A]], i64 [[TMP2]], i64 8)
+; CHECK-NEXT: ret ptr [[TMP1]]
+;
+entry:
+ %a = alloca ptr, i32 %v
+ ret ptr %a
+}
+define ptr @fizz() {
+; CHECK-LABEL: define ptr @fizz() {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[A:%.*]] = alloca <vscale x 2 x i32>, align 8
+; CHECK-NEXT: [[TMP1:%.*]] = getelementptr <vscale x 2 x i32>, ptr [[A]], i32 1
+; CHECK-NEXT: [[TMP2:%.*]] = ptrtoint ptr [[TMP1]] to i64
+; CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[A]] to i64
+; CHECK-NEXT: [[TMP3:%.*]] = sub i64 [[TMP2]], [[TMP0]]
+; CHECK-NEXT: [[TMP4:%.*]] = call ptr @__instrumentor_alloca(ptr [[A]], i64 [[TMP3]], i64 8)
+; CHECK-NEXT: ret ptr [[TMP4]]
+;
+entry:
+ %a = alloca <vscale x 2 x i32>
+ ret ptr %a
+}
diff --git a/llvm/test/Instrumentation/Instrumentor/default_config.json b/llvm/test/Instrumentation/Instrumentor/default_config.json
new file mode 100644
index 00000000000000..587931254a0496
--- /dev/null
+++ b/llvm/test/Instrumentation/Instrumentor/default_config.json
@@ -0,0 +1,13 @@
+{
+ "Base": {
+ "RuntimeName": "__instrumentor_",
+ "PrintRuntimeSignatures": true
+ },
+ "Alloca": {
+ "Instrument": true,
+ "Value": true,
+ "AllocationSize": true,
+ "Alignment": true,
+ "ReplaceValue": true
+ }
+}
\ No newline at end of file
diff --git a/llvm/test/Instrumentation/Instrumentor/just_calls_config.json b/llvm/test/Instrumentation/Instrumentor/just_calls_config.json
new file mode 100644
index 00000000000000..4f1608318fc712
--- /dev/null
+++ b/llvm/test/Instrumentation/Instrumentor/just_calls_config.json
@@ -0,0 +1,14 @@
+{
+ "Base": {
+ "RuntimeName": "__just_calls_",
+ "PrintRuntimeSignatures": false
+ },
+ "Alloca": {
+ "Instrument": true,
+ "Value": false,
+ "AllocationSize": false,
+ "Alignment": false,
+ "ArraySize": false,
+ "ReplaceValue": false
+ }
+}
diff --git a/llvm/test/Instrumentation/Instrumentor/read_config.ll b/llvm/test/Instrumentation/Instrumentor/read_config.ll
new file mode 100644
index 00000000000000..d5a29afebb4c7c
--- /dev/null
+++ b/llvm/test/Instrumentation/Instrumentor/read_config.ll
@@ -0,0 +1,22 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt < %s -passes=instrumentor -instrumentor-read-config-file=%S/just_calls_config.json -S | FileCheck %s
+
+target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+
+define ptr @bar(i1 %c) {
+; CHECK-LABEL: define ptr @bar(
+; CHECK-SAME: i1 [[C:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4
+; CHECK-NEXT: [[B:%.*]] = alloca i32, i32 5, align 4
+; CHECK-NEXT: call void @__just_calls_alloca()
+; CHECK-NEXT: call void @__just_calls_alloca()
+; CHECK-NEXT: [[S:%.*]] = select i1 [[C]], ptr [[A]], ptr [[B]]
+; CHECK-NEXT: ret ptr [[S]]
+;
+entry:
+ %a = alloca i32
+ %b = alloca i32, i32 5
+ %s = select i1 %c, ptr %a, ptr %b
+ ret ptr %s
+}
diff --git a/llvm/test/Instrumentation/Instrumentor/write_config.ll b/llvm/test/Instrumentation/Instrumentor/write_config.ll
new file mode 100644
index 00000000000000..0a79ca65fb08e6
--- /dev/null
+++ b/llvm/test/Instrumentation/Instrumentor/write_config.ll
@@ -0,0 +1,3 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt < %s -passes=instrumentor -instrumentor-write-config-file=%t.json
+; RUN: diff %t.json %S/default_config.json
More information about the llvm-commits
mailing list