[llvm] [Instrumentor] Add call instrumentation support (PR #206658)

via llvm-commits llvm-commits at lists.llvm.org
Mon Jun 29 23:56:39 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-transforms

Author: Jianjian Guan (jacquesguan)

<details>
<summary>Changes</summary>



---

Patch is 23.59 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/206658.diff


9 Files Affected:

- (modified) llvm/include/llvm/Transforms/IPO/Instrumentor.h (+62) 
- (modified) llvm/lib/Transforms/IPO/Instrumentor.cpp (+115) 
- (modified) llvm/lib/Transforms/IPO/InstrumentorUtils.cpp (+44-5) 
- (modified) llvm/test/Instrumentation/Instrumentor/alloca_and_function.ll (+14-6) 
- (added) llvm/test/Instrumentation/Instrumentor/call-config.json (+29) 
- (added) llvm/test/Instrumentation/Instrumentor/call-name-filter-config.json (+20) 
- (added) llvm/test/Instrumentation/Instrumentor/call-name-filter.ll (+29) 
- (added) llvm/test/Instrumentation/Instrumentor/call.ll (+26) 
- (modified) llvm/test/Instrumentation/Instrumentor/default_config.json (+45-1) 


``````````diff
diff --git a/llvm/include/llvm/Transforms/IPO/Instrumentor.h b/llvm/include/llvm/Transforms/IPO/Instrumentor.h
index fceb9e58b9791..9f86f87151618 100644
--- a/llvm/include/llvm/Transforms/IPO/Instrumentor.h
+++ b/llvm/include/llvm/Transforms/IPO/Instrumentor.h
@@ -1110,6 +1110,68 @@ struct LoadIO : public InstructionIO<Instruction::Load> {
   }
 };
 
+/// The instrumentation opportunity for call instructions.
+struct CallIO final : public InstructionIO<Instruction::Call> {
+  CallIO(InstrumentationLocation::KindTy Kind) : InstructionIO(Kind) {}
+
+  enum ConfigKind {
+    PassCallee = 0,
+    PassCalleeName,
+    PassNumArguments,
+    PassArguments,
+    PassReturnValue,
+    PassReturnValueSize,
+    PassReturnTypeId,
+    PassReturnSubTypeId,
+    PassId,
+    NumConfig,
+  };
+
+  using ConfigTy = BaseConfigTy<ConfigKind>;
+  ConfigTy Config;
+
+  StringRef getName() const override { return "call"; }
+
+  LLVM_ABI void init(InstrumentationConfig &IConf,
+                     InstrumentorIRBuilderTy &IIRB,
+                     ConfigTy *UserConfig = nullptr);
+
+  LLVM_ABI static Value *getCallee(Value &V, Type &Ty,
+                                   InstrumentationConfig &IConf,
+                                   InstrumentorIRBuilderTy &IIRB);
+  LLVM_ABI static Value *getCalleeName(Value &V, Type &Ty,
+                                       InstrumentationConfig &IConf,
+                                       InstrumentorIRBuilderTy &IIRB);
+  LLVM_ABI static Value *getNumArguments(Value &V, Type &Ty,
+                                         InstrumentationConfig &IConf,
+                                         InstrumentorIRBuilderTy &IIRB);
+  LLVM_ABI static Value *getArguments(Value &V, Type &Ty,
+                                      InstrumentationConfig &IConf,
+                                      InstrumentorIRBuilderTy &IIRB);
+  LLVM_ABI static Value *getReturnValue(Value &V, Type &Ty,
+                                        InstrumentationConfig &IConf,
+                                        InstrumentorIRBuilderTy &IIRB);
+  LLVM_ABI static Value *getReturnValueSize(Value &V, Type &Ty,
+                                            InstrumentationConfig &IConf,
+                                            InstrumentorIRBuilderTy &IIRB);
+  LLVM_ABI static Value *getReturnTypeId(Value &V, Type &Ty,
+                                         InstrumentationConfig &IConf,
+                                         InstrumentorIRBuilderTy &IIRB);
+  LLVM_ABI static Value *getReturnSubTypeId(Value &V, Type &Ty,
+                                            InstrumentationConfig &IConf,
+                                            InstrumentorIRBuilderTy &IIRB);
+
+  static void populate(InstrumentationConfig &IConf,
+                       InstrumentorIRBuilderTy &IIRB) {
+    auto *PreIO =
+        IConf.allocate<CallIO>(InstrumentationLocation::INSTRUCTION_PRE);
+    PreIO->init(IConf, IIRB);
+    auto *PostIO =
+        IConf.allocate<CallIO>(InstrumentationLocation::INSTRUCTION_POST);
+    PostIO->init(IConf, IIRB);
+  }
+};
+
 /// The instrumentation opportunity for type cast instructions.
 /// This includes PtrToInt, IntToPtr, Trunc, ZExt, SExt, FPToUI, FPToSI,
 /// UIToFP, SIToFP, FPTrunc, FPExt, AddrSpaceCast, and BitCast.
diff --git a/llvm/lib/Transforms/IPO/Instrumentor.cpp b/llvm/lib/Transforms/IPO/Instrumentor.cpp
index 3b52e3e1f4605..65cdbd0ea418d 100644
--- a/llvm/lib/Transforms/IPO/Instrumentor.cpp
+++ b/llvm/lib/Transforms/IPO/Instrumentor.cpp
@@ -568,6 +568,7 @@ void InstrumentationConfig::populate(InstrumentorIRBuilderTy &IIRB) {
   UnreachableIO::populate(*this, IIRB);
   LoadIO::populate(*this, IIRB);
   StoreIO::populate(*this, IIRB);
+  CallIO::populate(*this, IIRB);
   CastIO::populate(*this, IIRB);
   NumericIO::populate(*this, IIRB);
   CompareIO::populate(*this, IIRB);
@@ -1435,6 +1436,120 @@ Value *LoadIO::isVolatile(Value &V, Type &Ty, InstrumentationConfig &IConf,
   return getCI(&Ty, LI.isVolatile());
 }
 
+void CallIO::init(InstrumentationConfig &IConf, InstrumentorIRBuilderTy &IIRB,
+                  ConfigTy *UserConfig) {
+  if (UserConfig)
+    Config = *UserConfig;
+
+  bool IsPRE = getLocationKind() == InstrumentationLocation::INSTRUCTION_PRE;
+  if (Config.has(PassCallee)) {
+    IRTArgs.push_back(IRTArg(IIRB.PtrTy, "callee",
+                             "The called function pointer.", IRTArg::NONE,
+                             getCallee));
+  }
+  if (Config.has(PassCalleeName)) {
+    IRTArgs.push_back(IRTArg(IIRB.PtrTy, "callee_name",
+                             "The called function name.", IRTArg::STRING,
+                             getCalleeName));
+  }
+  if (Config.has(PassNumArguments)) {
+    IRTArgs.push_back(IRTArg(IIRB.Int32Ty, "num_arguments",
+                             "Number of call arguments.", IRTArg::NONE,
+                             getNumArguments));
+  }
+  if (Config.has(PassArguments)) {
+    IRTArgs.push_back(IRTArg(IIRB.PtrTy, "arguments",
+                             "Description of the call arguments.",
+                             IRTArg::VALUE_PACK, getArguments));
+  }
+  if (!IsPRE && Config.has(PassReturnValue)) {
+    IRTArgs.push_back(
+        IRTArg(IIRB.Int64Ty, "return_value", "The call return value.",
+               IRTArg::POTENTIALLY_INDIRECT |
+                   (Config.has(PassReturnValueSize) ? IRTArg::INDIRECT_HAS_SIZE
+                                                    : IRTArg::NONE),
+               getReturnValue));
+  }
+  if (Config.has(PassReturnValueSize)) {
+    IRTArgs.push_back(IRTArg(IIRB.Int64Ty, "return_value_size",
+                             "The size of the call return value.", IRTArg::NONE,
+                             getReturnValueSize));
+  }
+  if (Config.has(PassReturnTypeId)) {
+    IRTArgs.push_back(IRTArg(IIRB.Int32Ty, "return_type_id",
+                             "The type id of the call return value.",
+                             IRTArg::TYPEID, getReturnTypeId));
+  }
+  if (Config.has(PassReturnSubTypeId)) {
+    IRTArgs.push_back(IRTArg(
+        IIRB.Int32Ty, "return_sub_type_id",
+        "The sub type id of the call return value (for arrays and vectors, or "
+        "-1).",
+        IRTArg::TYPEID, getReturnSubTypeId));
+  }
+
+  addCommonArgs(IConf, IIRB.Ctx, Config.has(PassId));
+  IConf.addChoice(*this, IIRB.Ctx);
+}
+
+Value *CallIO::getCallee(Value &V, Type &Ty, InstrumentationConfig &IConf,
+                         InstrumentorIRBuilderTy &IIRB) {
+  auto &CI = cast<CallInst>(V);
+  return CI.getCalledOperand();
+}
+
+Value *CallIO::getCalleeName(Value &V, Type &Ty, InstrumentationConfig &IConf,
+                             InstrumentorIRBuilderTy &IIRB) {
+  auto &CI = cast<CallInst>(V);
+  auto *Callee = CI.getCalledFunction();
+  if (!Callee)
+    return IConf.getGlobalString("", IIRB);
+  return IConf.getGlobalString(IConf.DemangleFunctionNames->getBool()
+                                   ? demangle(Callee->getName())
+                                   : Callee->getName(),
+                               IIRB);
+}
+
+Value *CallIO::getNumArguments(Value &V, Type &Ty, InstrumentationConfig &IConf,
+                               InstrumentorIRBuilderTy &IIRB) {
+  auto &CI = cast<CallInst>(V);
+  return getCI(&Ty, CI.arg_size());
+}
+
+Value *CallIO::getArguments(Value &V, Type &Ty, InstrumentationConfig &IConf,
+                            InstrumentorIRBuilderTy &IIRB) {
+  auto &CI = cast<CallInst>(V);
+  return createValuePack(CI.args(), IConf, IIRB);
+}
+
+Value *CallIO::getReturnValue(Value &V, Type &Ty, InstrumentationConfig &IConf,
+                              InstrumentorIRBuilderTy &IIRB) {
+  return &V;
+}
+
+Value *CallIO::getReturnValueSize(Value &V, Type &Ty,
+                                  InstrumentationConfig &IConf,
+                                  InstrumentorIRBuilderTy &IIRB) {
+  auto &CI = cast<CallInst>(V);
+  if (CI.getType()->isVoidTy())
+    return getCI(&Ty, 0);
+  auto &DL = CI.getDataLayout();
+  return getCI(&Ty, DL.getTypeStoreSize(CI.getType()));
+}
+
+Value *CallIO::getReturnTypeId(Value &V, Type &Ty, InstrumentationConfig &IConf,
+                               InstrumentorIRBuilderTy &IIRB) {
+  auto &CI = cast<CallInst>(V);
+  return getCI(&Ty, CI.getType()->getTypeID());
+}
+
+Value *CallIO::getReturnSubTypeId(Value &V, Type &Ty,
+                                  InstrumentationConfig &IConf,
+                                  InstrumentorIRBuilderTy &IIRB) {
+  auto &CI = cast<CallInst>(V);
+  return getSubTypeID(*CI.getType(), Ty);
+}
+
 void BasePointerIO::init(InstrumentationConfig &IConf,
                          InstrumentorIRBuilderTy &IIRB, ConfigTy *UserConfig) {
   if (UserConfig)
diff --git a/llvm/lib/Transforms/IPO/InstrumentorUtils.cpp b/llvm/lib/Transforms/IPO/InstrumentorUtils.cpp
index 7030002824761..15fbacd813db0 100644
--- a/llvm/lib/Transforms/IPO/InstrumentorUtils.cpp
+++ b/llvm/lib/Transforms/IPO/InstrumentorUtils.cpp
@@ -20,6 +20,45 @@ using namespace llvm::instrumentor;
 namespace {
 enum PropertyType { INT, STRING, POINTER, UNKNOWN };
 
+static GlobalVariable *findGlobalString(Value *V, unsigned Depth = 0) {
+  if (Depth > 4)
+    return nullptr;
+
+  V = V->stripPointerCasts();
+  if (auto *GV = dyn_cast<GlobalVariable>(V))
+    return GV;
+
+  auto *C = dyn_cast<Constant>(V);
+  if (!C)
+    return nullptr;
+
+  for (Value *Op : C->operands())
+    if (auto *GV = findGlobalString(Op, Depth + 1))
+      return GV;
+  return nullptr;
+}
+
+static bool getConstantCString(Value *V, StringRef &S) {
+  auto *GV = findGlobalString(V);
+  if (!GV || !GV->isConstant() || !GV->hasInitializer())
+    return false;
+
+  if (isa<ConstantAggregateZero>(GV->getInitializer())) {
+    auto *AT = dyn_cast<ArrayType>(GV->getValueType());
+    if (AT && AT->getElementType()->isIntegerTy(8)) {
+      S = "";
+      return true;
+    }
+  }
+
+  auto *CDA = dyn_cast<ConstantDataArray>(GV->getInitializer());
+  if (!CDA || !CDA->isCString())
+    return false;
+
+  S = CDA->getAsCString();
+  return true;
+}
+
 /// Simple filter expression evaluator for instrumentation opportunities.
 /// Supports integer comparisons (==, !=, <, >, <=, >=), string comparisons
 /// (==, !=), pointer comparisons (==, !=) against null, string prefix checks
@@ -438,11 +477,11 @@ bool llvm::instrumentor::evaluateFilter(Value &V, bool &Changed,
       IntPropertyValues[Arg.Name] = CI->getSExtValue();
     } else if ((Arg.Flags & IRTArg::STRING) && isa<Constant>(ArgValue)) {
       // Check for constant string values (marked with STRING flag).
-      if (auto *GV = dyn_cast<GlobalVariable>(ArgValue))
-        if (GV->isConstant() && GV->hasInitializer())
-          if (auto *CDA = dyn_cast<ConstantDataArray>(GV->getInitializer()))
-            if (CDA->isCString())
-              StringPropertyValues[Arg.Name] = CDA->getAsCString();
+      StringRef S;
+      if (getConstantCString(ArgValue, S))
+        StringPropertyValues[Arg.Name] = S;
+      else
+        DynamicProperties[Arg.Name] = STRING;
     } else if (ArgValue->getType()->isPointerTy()) {
       // Check for pointer values (for null comparisons), after the strings.
       PointerPropertyValues[Arg.Name] = ArgValue;
diff --git a/llvm/test/Instrumentation/Instrumentor/alloca_and_function.ll b/llvm/test/Instrumentation/Instrumentor/alloca_and_function.ll
index a2afecda64ebf..75eb8001a6d09 100644
--- a/llvm/test/Instrumentation/Instrumentor/alloca_and_function.ll
+++ b/llvm/test/Instrumentation/Instrumentor/alloca_and_function.ll
@@ -13,19 +13,22 @@ declare void @use(ptr)
 ; CHECK: @__instrumentor_.str = private unnamed_addr constant [8 x i8] c"<stdin>\00", align 1
 ; CHECK: @__instrumentor_.str.1 = private unnamed_addr constant [1 x i8] zeroinitializer, align 1
 ; CHECK: @llvm.global_dtors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 1000, ptr @__instrumentor_dtor, ptr null }]
-; CHECK: @__instrumentor_.str.2 = private unnamed_addr constant [4 x i8] c"foo\00", align 1
-; CHECK: @__instrumentor_value_pack = internal global <{ i32, i32, [6 x i8], i16, i32, i32, [4 x i8], float }> <{ i32 2, i32 12, [6 x i8] zeroinitializer, i16 0, i32 4, i32 2, [4 x i8] zeroinitializer, float 0.000000e+00 }>
+; CHECK: @__instrumentor_.str.2 = private unnamed_addr constant [4 x i8] c"use\00", align 1
+; CHECK: @__instrumentor_value_pack = internal global <{ i32, i32, ptr }> <{ i32 8, i32 15, ptr null }>
+; CHECK: @__instrumentor_.str.3 = private unnamed_addr constant [4 x i8] c"foo\00", align 1
+; CHECK: @__instrumentor_value_pack.4 = internal global <{ i32, i32, [6 x i8], i16, i32, i32, [4 x i8], float }> <{ i32 2, i32 12, [6 x i8] zeroinitializer, i16 0, i32 4, i32 2, [4 x i8] zeroinitializer, float 0.000000e+00 }>
 define float @foo(i16 %a, float %b) {
 ; CHECK-LABEL: define float @foo(
 ; CHECK-SAME: i16 [[A:%.*]], float [[B:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
 ; CHECK-NEXT:    [[TMP0:%.*]] = alloca <{ i32, i32, [6 x i8], i16, i32, i32, [4 x i8], float }>, align 8
-; CHECK-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr @__instrumentor_value_pack, i64 32, i1 false)
+; CHECK-NEXT:    [[CALL_ARGS:%.*]] = alloca <{ i32, i32, ptr }>, align 8
+; CHECK-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr @__instrumentor_value_pack.4, i64 32, i1 false)
 ; CHECK-NEXT:    [[TMP2:%.*]] = getelementptr inbounds nuw <{ i32, i32, [6 x i8], i16, i32, i32, [4 x i8], float }>, ptr [[TMP0]], i32 0, i32 3
 ; CHECK-NEXT:    store i16 [[A]], ptr [[TMP2]], align 2
 ; CHECK-NEXT:    [[TMP14:%.*]] = getelementptr inbounds nuw <{ i32, i32, [6 x i8], i16, i32, i32, [4 x i8], float }>, ptr [[TMP0]], i32 0, i32 7
 ; CHECK-NEXT:    store float [[B]], ptr [[TMP14]], align 4
-; CHECK-NEXT:    call void @__instrumentor_pre_function(ptr @foo, ptr @__instrumentor_.str.2, i32 2, ptr [[TMP0]], i8 0, i32 5) #[[ATTR1:[0-9]+]]
+; CHECK-NEXT:    call void @__instrumentor_pre_function(ptr @foo, ptr @__instrumentor_.str.3, i32 2, ptr [[TMP0]], i8 0, i32 6) #[[ATTR1:[0-9]+]]
 ; CHECK-NEXT:    [[TMP3:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i32 14
 ; CHECK-NEXT:    [[TMP4:%.*]] = load i16, ptr [[TMP3]], align 2
 ; CHECK-NEXT:    [[TMP5:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i32 28
@@ -38,13 +41,18 @@ define float @foo(i16 %a, float %b) {
 ; CHECK-NEXT:    [[TMP11:%.*]] = call ptr @__instrumentor_pre_store(ptr [[TMP9]], i32 0, ptr [[TMP15]], i64 [[TMP10]], i64 2, i64 2, i32 12, i32 -1, i32 0, i8 1, i8 0, i32 4) #[[ATTR1]]
 ; CHECK-NEXT:    store i16 [[TMP4]], ptr [[TMP11]], align 2
 ; CHECK-NEXT:    call void @__instrumentor_post_store(ptr [[TMP9]], i32 0, ptr [[TMP15]], i64 [[TMP10]], i64 2, i64 2, i32 12, i32 -1, i32 0, i8 1, i8 0, i32 -4) #[[ATTR1]]
+; CHECK-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[CALL_ARGS]], ptr @__instrumentor_value_pack, i64 16, i1 false)
+; CHECK-NEXT:    [[CALL_ARG_PTR:%.*]] = getelementptr inbounds nuw <{ i32, i32, ptr }>, ptr [[CALL_ARGS]], i32 0, i32 2
+; CHECK-NEXT:    store ptr [[TMP9]], ptr [[CALL_ARG_PTR]], align 8
+; CHECK-NEXT:    call void @__instrumentor_pre_call(ptr @use, ptr @__instrumentor_.str.2, i32 1, ptr [[CALL_ARGS]], i64 0, i32 7, i32 -1, i32 5) #[[ATTR1]]
 ; CHECK-NEXT:    call void @use(ptr [[TMP9]])
-; CHECK-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr @__instrumentor_value_pack, i64 32, i1 false)
+; CHECK-NEXT:    call void @__instrumentor_post_call(ptr @use, ptr @__instrumentor_.str.2, i32 1, ptr [[CALL_ARGS]], i64 0, i64 0, i32 7, i32 -1, i32 -5) #[[ATTR1]]
+; CHECK-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr @__instrumentor_value_pack.4, i64 32, i1 false)
 ; CHECK-NEXT:    [[TMP12:%.*]] = getelementptr inbounds nuw <{ i32, i32, [6 x i8], i16, i32, i32, [4 x i8], float }>, ptr [[TMP0]], i32 0, i32 3
 ; CHECK-NEXT:    store i16 [[A]], ptr [[TMP12]], align 2
 ; CHECK-NEXT:    [[TMP13:%.*]] = getelementptr inbounds nuw <{ i32, i32, [6 x i8], i16, i32, i32, [4 x i8], float }>, ptr [[TMP0]], i32 0, i32 7
 ; CHECK-NEXT:    store float [[B]], ptr [[TMP13]], align 4
-; CHECK-NEXT:    call void @__instrumentor_post_function(ptr @foo, ptr @__instrumentor_.str.2, i32 2, ptr [[TMP0]], i8 0, i32 -6) #[[ATTR1]]
+; CHECK-NEXT:    call void @__instrumentor_post_function(ptr @foo, ptr @__instrumentor_.str.3, i32 2, ptr [[TMP0]], i8 0, i32 -7) #[[ATTR1]]
 ; CHECK-NEXT:    ret float [[TMP6]]
 ;
 entry:
diff --git a/llvm/test/Instrumentation/Instrumentor/call-config.json b/llvm/test/Instrumentation/Instrumentor/call-config.json
new file mode 100644
index 0000000000000..7316f2a5141f5
--- /dev/null
+++ b/llvm/test/Instrumentation/Instrumentor/call-config.json
@@ -0,0 +1,29 @@
+{
+  "instruction_pre": {
+    "call": {
+      "enabled": true,
+      "callee": true,
+      "callee_name": true,
+      "num_arguments": true,
+      "arguments": true,
+      "return_value_size": true,
+      "return_type_id": true,
+      "return_sub_type_id": true,
+      "id": true
+    }
+  },
+  "instruction_post": {
+    "call": {
+      "enabled": true,
+      "callee": true,
+      "callee_name": true,
+      "num_arguments": true,
+      "arguments": true,
+      "return_value": true,
+      "return_value_size": true,
+      "return_type_id": true,
+      "return_sub_type_id": true,
+      "id": true
+    }
+  }
+}
diff --git a/llvm/test/Instrumentation/Instrumentor/call-name-filter-config.json b/llvm/test/Instrumentation/Instrumentor/call-name-filter-config.json
new file mode 100644
index 0000000000000..58a09e7a68713
--- /dev/null
+++ b/llvm/test/Instrumentation/Instrumentor/call-name-filter-config.json
@@ -0,0 +1,20 @@
+{
+  "instruction_pre": {
+    "call": {
+      "enabled": true,
+      "filter": "callee_name==\"foo\"",
+      "callee": true,
+      "callee_name": true,
+      "id": true
+    }
+  },
+  "instruction_post": {
+    "call": {
+      "enabled": true,
+      "filter": "callee_name==\"foo\"",
+      "callee": true,
+      "callee_name": true,
+      "id": true
+    }
+  }
+}
diff --git a/llvm/test/Instrumentation/Instrumentor/call-name-filter.ll b/llvm/test/Instrumentation/Instrumentor/call-name-filter.ll
new file mode 100644
index 0000000000000..1ed9a0244f04b
--- /dev/null
+++ b/llvm/test/Instrumentation/Instrumentor/call-name-filter.ll
@@ -0,0 +1,29 @@
+; RUN: opt -passes=instrumentor -instrumentor-read-config-files=%S/call-name-filter-config.json -S < %s | FileCheck %s
+
+declare i32 @foo(i32)
+declare i32 @bar(i32)
+
+define i32 @caller(ptr %fp, i32 %x) {
+; CHECK-LABEL: define i32 @caller(
+; CHECK: call void @__instrumentor_pre_call(ptr @foo, ptr @{{.*}}, i32 1)
+; CHECK-NEXT: %foo = call i32 @foo(i32 %x)
+; CHECK-NEXT: call void @__instrumentor_post_call(ptr @foo, ptr @{{.*}}, i32 -1)
+; CHECK-NEXT: %bar = call i32 @bar(i32 %x)
+; CHECK-NEXT: %ind = call i32 %fp(i32 %x)
+; CHECK-NEXT: %sum = add i32 %foo, %bar
+; CHECK-NEXT: %ret = add i32 %sum, %ind
+; CHECK-NEXT: ret i32 %ret
+  %foo = call i32 @foo(i32 %x)
+  %bar = call i32 @bar(i32 %x)
+  %ind = call i32 %fp(i32 %x)
+  %sum = add i32 %foo, %bar
+  %ret = add i32 %sum, %ind
+  ret i32 %ret
+}
+
+; CHECK: declare void @__instrumentor_pre_call(ptr, ptr, i32)
+; CHECK: declare void @__instrumentor_post_call(ptr, ptr, i32)
+; CHECK-NOT: __instrumentor_pre_call(ptr @bar
+; CHECK-NOT: __instrumentor_post_call(ptr @bar
+; CHECK-NOT: __instrumentor_pre_call(ptr %fp
+; CHECK-NOT: __instrumentor_post_call(ptr %fp
diff --git a/llvm/test/Instrumentation/Instrumentor/call.ll b/llvm/test/Instrumentation/Instrumentor/call.ll
new file mode 100644
index 0000000000000..687311252a36d
--- /dev/null
+++ b/llvm/test/Instrumentation/Instrumentor/call.ll
@@ -0,0 +1,26 @@
+; RUN: opt -passes=instrumentor -instrumentor-read-config-files=%S/call-config.json -S < %s | FileCheck %s
+
+declare i32 @foo(i32, ptr)
+declare void @bar()
+
+define i32 @caller(ptr %p, ptr %fp, i32 %x) {
+; CHECK-LABEL: define i32 @caller(
+; CHECK: call void @__instrumentor_pre_call(ptr @foo, ptr @{{.*}}, i32 2, ptr %{{.*}}, i64 4, i32 {{[0-9]+}}, i32 -1, i32 1)
+; CHECK: %res = call i32 @foo(i32 %x, ptr %p)
+; CHECK: call void @__instrumentor_post_call(ptr @foo, ptr @{{.*}}, i32 2, ptr %{{.*}}, i64 %{{.*}}, i64 4, i32 {{[0-9]+}}, i32 -1, i32 -1)
+; CHECK: call void @__instrumentor_pre_call(ptr %fp, ptr @{{.*}}, i32 1, ptr %{{.*}}, i64 4, i32 {{[0-9]+}}, i32 -1, i32 2)
+; CHECK: %ind = call i32 %fp(i32 %x)
+...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/206658


More information about the llvm-commits mailing list