https://github.com/jdoerfert updated 
https://github.com/llvm/llvm-project/pull/195378

>From 9a4824c6613372738a758d7d418ca946df097595 Mon Sep 17 00:00:00 2001
From: Johannes Doerfert <[email protected]>
Date: Fri, 1 May 2026 11:01:07 -0700
Subject: [PATCH] [Instrumentor] Add Alloca and Function support; stack usage
 example

This adds support for alloca instrumentation and function pre/post
instrumentation. Alloca support follows load/store support directly.
Functions require special care to determine the insertion points.

Together, we can showcase how the stack high watermark can be profiled,
see InstrumentorStackUsage.cpp.
---
 .../Instrumentor/InstrumentorStackUsage.cpp   |  37 +++
 clang/test/Instrumentor/StackUsageRT.cpp      |  59 ++++
 clang/test/Instrumentor/StackUsageRT.json     |  54 ++++
 clang/test/Instrumentor/lit.local.cfg         |   2 +
 .../llvm/Transforms/IPO/Instrumentor.h        | 126 +++++++-
 llvm/lib/Transforms/IPO/Instrumentor.cpp      | 303 +++++++++++++++++-
 .../Instrumentor/alloca_and_function.ll       |  56 ++++
 .../Instrumentor/default_config.json          |  59 ++++
 8 files changed, 681 insertions(+), 15 deletions(-)
 create mode 100644 clang/test/Instrumentor/InstrumentorStackUsage.cpp
 create mode 100644 clang/test/Instrumentor/StackUsageRT.cpp
 create mode 100644 clang/test/Instrumentor/StackUsageRT.json
 create mode 100644 clang/test/Instrumentor/lit.local.cfg
 create mode 100644 
llvm/test/Instrumentation/Instrumentor/alloca_and_function.ll

diff --git a/clang/test/Instrumentor/InstrumentorStackUsage.cpp 
b/clang/test/Instrumentor/InstrumentorStackUsage.cpp
new file mode 100644
index 0000000000000..15a2714652a3f
--- /dev/null
+++ b/clang/test/Instrumentor/InstrumentorStackUsage.cpp
@@ -0,0 +1,37 @@
+// NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+// RUN: %clangxx -O0 %S/StackUsageRT.cpp -o %t.StackUsageRT.o -c
+// RUN: %clangxx -O0 -mllvm -enable-instrumentor -mllvm 
-instrumentor-read-config-file=%S/StackUsageRT.json %t.StackUsageRT.o -o %t %s 
+// RUN: %t | FileCheck %s
+
+static void foobar(int *A, int N) {
+  int B[100];
+  for (int i = 0; i < 100; ++i) {
+    B[i] = i + N;
+  }
+  if (N-- > 0)
+    foobar(B, N);
+  for (int i = 0; i < 100; ++i) {
+    A[i] += B[i];
+  }
+}
+
+static void bar(int *A, int N) {
+  foobar(A, N);
+}
+
+int main(void) {
+  int A[100] = {0};
+  foobar(A, 4);
+  bar(A, 3);
+  foobar(A, 5);
+  foobar(A, 2);
+}
+
+// CHECK: Stack usage peaked at 2512 in
+// CHECK: - foobar(int*, int)
+// CHECK: - foobar(int*, int)
+// CHECK: - foobar(int*, int)
+// CHECK: - foobar(int*, int)
+// CHECK: - foobar(int*, int)
+// CHECK: - foobar(int*, int)
+// CHECK: - main
diff --git a/clang/test/Instrumentor/StackUsageRT.cpp 
b/clang/test/Instrumentor/StackUsageRT.cpp
new file mode 100644
index 0000000000000..9f2b29b8f050d
--- /dev/null
+++ b/clang/test/Instrumentor/StackUsageRT.cpp
@@ -0,0 +1,59 @@
+//===-- examples/Instrumentor/stack_usage.c - An example Instrumentor use 
-===//
+//
+// 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 <cstdint>
+#include <cstdio>
+#include <list>
+
+struct StackTracker {
+  std::list<char *> CallStack;
+  int64_t FunctionStackUsage = 0;
+  int64_t TotalStackUsage = 0;
+
+  std::list<char *> HighWaterMarkCallStack;
+  int64_t HighWaterMark = 0;
+
+  ~StackTracker() {
+    printf("Stack usage peaked at %lli in\n", HighWaterMark);
+    HighWaterMarkCallStack.reverse();
+    for (char *Name : HighWaterMarkCallStack)
+      printf("- %s\n", Name);
+  }
+
+  void enter(char *Name) {
+    FunctionStackUsage = 0;
+    CallStack.push_back(Name);
+  }
+  void exit(char *Name) {
+    CallStack.pop_back();
+    TotalStackUsage -= FunctionStackUsage;
+  }
+
+  void allocate(int64_t size) {
+    TotalStackUsage += size;
+    FunctionStackUsage += size;
+    if (TotalStackUsage <= HighWaterMark)
+      return;
+    HighWaterMark = TotalStackUsage;
+    HighWaterMarkCallStack = CallStack;
+  }
+};
+
+static thread_local StackTracker ST;
+
+extern "C" {
+
+void __stack_usage_pre_function(char *Name) { ST.enter(Name); }
+
+void __stack_usage_post_function(char *Name) { ST.exit(Name); }
+
+void __stack_usage_pre_alloca(int64_t size) { ST.allocate(size); }
+}
diff --git a/clang/test/Instrumentor/StackUsageRT.json 
b/clang/test/Instrumentor/StackUsageRT.json
new file mode 100644
index 0000000000000..491ab9cf5ea05
--- /dev/null
+++ b/clang/test/Instrumentor/StackUsageRT.json
@@ -0,0 +1,54 @@
+{
+  "configuration": {
+    "runtime_prefix": "__stack_usage_",
+    "runtime_prefix.description": "The runtime API prefix.",
+    "demangle_function_names": true,
+    "demangle_function_names.description": "Demangle functions names passed to 
the runtime."
+  },
+  "function_pre": {
+    "function": {
+      "enabled": true,
+      "address": false,
+      "address.description": "The function address.",
+      "name": true,
+      "name.description": "The function name.",
+      "num_arguments": false,
+      "num_arguments.description": "Number of function arguments (without 
varargs).",
+      "arguments": false,
+      "arguments.description": "Description of the arguments.",
+      "is_main": false,
+      "is_main.description": "Flag to indicate it is the main function.",
+      "id": false,
+      "id.description": "A unique ID associated with the given instrumentor 
call"
+    }
+  },
+  "function_post": {
+    "function": {
+      "enabled": true,
+      "address": false,
+      "address.description": "The function address.",
+      "name": true,
+      "name.description": "The function name.",
+      "num_arguments": false,
+      "num_arguments.description": "Number of function arguments (without 
varargs).",
+      "arguments": false,
+      "arguments.description": "Description of the arguments.",
+      "is_main": false,
+      "is_main.description": "Flag to indicate it is the main function.",
+      "id": false,
+      "id.description": "A unique ID associated with the given instrumentor 
call"
+    }
+  },
+  "instruction_pre": {
+    "alloca": {
+      "enabled": true,
+      "size": true,
+      "size.replace": false,
+      "size.description": "The allocation size.",
+      "alignment": false,
+      "alignment.description": "The allocation alignment.",
+      "id": false,
+      "id.description": "A unique ID associated with the given instrumentor 
call"
+    }
+  }
+}
diff --git a/clang/test/Instrumentor/lit.local.cfg 
b/clang/test/Instrumentor/lit.local.cfg
new file mode 100644
index 0000000000000..afb6cf1a99e25
--- /dev/null
+++ b/clang/test/Instrumentor/lit.local.cfg
@@ -0,0 +1,2 @@
+config.suffixes.add(".cpp")
+config.excludes = ["StackUsageRT.cpp"]
diff --git a/llvm/include/llvm/Transforms/IPO/Instrumentor.h 
b/llvm/include/llvm/Transforms/IPO/Instrumentor.h
index 7dfc342031579..701498bb830b9 100644
--- a/llvm/include/llvm/Transforms/IPO/Instrumentor.h
+++ b/llvm/include/llvm/Transforms/IPO/Instrumentor.h
@@ -358,6 +358,9 @@ struct InstrumentationConfig {
         "Regular expression to be matched against the module target. "
         "Only targets that match this regex will be instrumented",
         "");
+    DemangleFunctionNames = BaseConfigurationOption::createBoolOption(
+        *this, "demangle_function_names",
+        "Demangle functions names passed to the runtime.", true);
     HostEnabled = BaseConfigurationOption::createBoolOption(
         *this, "host_enabled", "Instrument non-GPU targets", true);
     GPUEnabled = BaseConfigurationOption::createBoolOption(
@@ -394,12 +397,31 @@ struct InstrumentationConfig {
     return Obj;
   }
 
+  /// Mapping to remember global strings passed to the runtime.
+  DenseMap<StringRef, Constant *> GlobalStringsMap;
+
+  /// Mapping from constants to globals with the constant as initializer.
+  DenseMap<Constant *, GlobalVariable *> ConstantGlobalsCache;
+
+  Constant *getGlobalString(StringRef S, InstrumentorIRBuilderTy &IIRB) {
+    Constant *&V = GlobalStringsMap[SS.save(S)];
+    if (!V) {
+      auto &M = *IIRB.IRB.GetInsertBlock()->getModule();
+      V = IIRB.IRB.CreateGlobalString(
+          S, getRTName() + ".str",
+          M.getDataLayout().getDefaultGlobalsAddressSpace(), &M);
+      if (V->getType() != IIRB.IRB.getPtrTy())
+        V = ConstantExpr::getAddrSpaceCast(V, IIRB.IRB.getPtrTy());
+    }
+    return V;
+  }
   /// The list of enabled base configuration options.
   SmallVector<BaseConfigurationOption *> BaseConfigurationOptions;
 
   /// The base configuration options.
   std::unique_ptr<BaseConfigurationOption> RuntimePrefix;
   std::unique_ptr<BaseConfigurationOption> RuntimeStubsFile;
+  std::unique_ptr<BaseConfigurationOption> DemangleFunctionNames;
   std::unique_ptr<BaseConfigurationOption> TargetRegex;
   std::unique_ptr<BaseConfigurationOption> HostEnabled;
   std::unique_ptr<BaseConfigurationOption> GPUEnabled;
@@ -539,6 +561,94 @@ struct InstructionIO : public InstrumentationOpportunity {
   }
 };
 
+/// The instrumentation opportunity for functions.
+struct FunctionIO final : public InstrumentationOpportunity {
+  FunctionIO(bool IsPRE)
+      : InstrumentationOpportunity(
+            InstrumentationLocation(InstrumentationLocation(
+                IsPRE ? InstrumentationLocation::FUNCTION_PRE
+                      : InstrumentationLocation::FUNCTION_POST))) {}
+
+  enum ConfigKind {
+    PassAddress = 0,
+    PassName,
+    PassNumArguments,
+    PassArguments,
+    ReplaceArguments,
+    PassIsMain,
+    PassId,
+    NumConfig,
+  };
+
+  struct ConfigTy final : public BaseConfigTy<ConfigKind> {
+    std::function<bool(Argument &)> ArgFilter;
+
+    ConfigTy(bool Enable = true) : BaseConfigTy(Enable) {}
+  } Config;
+
+  StringRef getName() const override { return "function"; }
+
+  void init(InstrumentationConfig &IConf, LLVMContext &Ctx,
+            ConfigTy *UserConfig = nullptr);
+
+  static Value *getFunctionAddress(Value &V, Type &Ty,
+                                   InstrumentationConfig &IConf,
+                                   InstrumentorIRBuilderTy &IIRB);
+  static Value *getFunctionName(Value &V, Type &Ty,
+                                InstrumentationConfig &IConf,
+                                InstrumentorIRBuilderTy &IIRB);
+  Value *getNumArguments(Value &V, Type &Ty, InstrumentationConfig &IConf,
+                         InstrumentorIRBuilderTy &IIRB);
+  Value *getArguments(Value &V, Type &Ty, InstrumentationConfig &IConf,
+                      InstrumentorIRBuilderTy &IIRB);
+  Value *setArguments(Value &V, Value &NewV, InstrumentationConfig &IConf,
+                      InstrumentorIRBuilderTy &IIRB);
+  static Value *isMainFunction(Value &V, Type &Ty, InstrumentationConfig 
&IConf,
+                               InstrumentorIRBuilderTy &IIRB);
+
+  static void populate(InstrumentationConfig &IConf, LLVMContext &Ctx) {
+    auto *PreIO = IConf.allocate<FunctionIO>(true);
+    PreIO->init(IConf, Ctx);
+    auto *PostIO = IConf.allocate<FunctionIO>(false);
+    PostIO->init(IConf, Ctx);
+  }
+};
+
+/// The instrumentation opportunity for alloca instructions.
+struct AllocaIO final : public InstructionIO<Instruction::Alloca> {
+  AllocaIO(bool IsPRE) : InstructionIO(IsPRE) {}
+
+  enum ConfigKind {
+    PassAddress = 0,
+    ReplaceAddress,
+    PassSize,
+    ReplaceSize,
+    PassAlignment,
+    PassId,
+    NumConfig,
+  };
+
+  using ConfigTy = BaseConfigTy<ConfigKind>;
+  ConfigTy Config;
+
+  void init(InstrumentationConfig &IConf, LLVMContext &Ctx,
+            ConfigTy *UserConfig = nullptr);
+
+  static Value *getSize(Value &V, Type &Ty, InstrumentationConfig &IConf,
+                        InstrumentorIRBuilderTy &IIRB);
+  static Value *setSize(Value &V, Value &NewV, InstrumentationConfig &IConf,
+                        InstrumentorIRBuilderTy &IIRB);
+  static Value *getAlignment(Value &V, Type &Ty, InstrumentationConfig &IConf,
+                             InstrumentorIRBuilderTy &IIRB);
+
+  static void populate(InstrumentationConfig &IConf, LLVMContext &Ctx) {
+    auto *PreIO = IConf.allocate<AllocaIO>(true);
+    PreIO->init(IConf, Ctx);
+    auto *PostIO = IConf.allocate<AllocaIO>(false);
+    PostIO->init(IConf, Ctx);
+  }
+};
+
 /// The instrumentation opportunity for store instructions.
 struct StoreIO : public InstructionIO<Instruction::Store> {
   virtual ~StoreIO() {};
@@ -608,10 +718,10 @@ struct StoreIO : public InstructionIO<Instruction::Store> 
{
   /// instrumentation calls.
   static void populate(InstrumentationConfig &IConf,
                        InstrumentorIRBuilderTy &IIRB) {
-    for (auto IsPRE : {true, false}) {
-      auto *AIC = IConf.allocate<StoreIO>(IsPRE);
-      AIC->init(IConf, IIRB);
-    }
+    auto *PreIO = IConf.allocate<StoreIO>(true);
+    PreIO->init(IConf, IIRB);
+    auto *PostIO = IConf.allocate<StoreIO>(false);
+    PostIO->init(IConf, IIRB);
   }
 };
 
@@ -683,10 +793,10 @@ struct LoadIO : public InstructionIO<Instruction::Load> {
   /// Create the store opportunities for PRE and POST positions.
   static void populate(InstrumentationConfig &IConf,
                        InstrumentorIRBuilderTy &IIRB) {
-    for (auto IsPRE : {true, false}) {
-      auto *AIC = IConf.allocate<LoadIO>(IsPRE);
-      AIC->init(IConf, IIRB);
-    }
+    auto *PreIO = IConf.allocate<LoadIO>(true);
+    PreIO->init(IConf, IIRB);
+    auto *PostIO = IConf.allocate<LoadIO>(false);
+    PostIO->init(IConf, IIRB);
   }
 };
 
diff --git a/llvm/lib/Transforms/IPO/Instrumentor.cpp 
b/llvm/lib/Transforms/IPO/Instrumentor.cpp
index 33f00be11084a..3ba1a2b00a5f8 100644
--- a/llvm/lib/Transforms/IPO/Instrumentor.cpp
+++ b/llvm/lib/Transforms/IPO/Instrumentor.cpp
@@ -21,6 +21,7 @@
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/StringMap.h"
 #include "llvm/ADT/iterator.h"
+#include "llvm/Demangle/Demangle.h"
 #include "llvm/IR/Constant.h"
 #include "llvm/IR/Constants.h"
 #include "llvm/IR/DataLayout.h"
@@ -231,11 +232,45 @@ bool InstrumentorImpl::instrumentFunction(Function &Fn) {
     return Changed;
 
   InstrumentationCaches ICaches;
+  SmallVector<Instruction *> FinalTIs;
   ReversePostOrderTraversal<Function *> RPOT(&Fn);
-  for (auto &It : RPOT)
+  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.SetInsertPointPastAllocas(cast<Function>(FPtr));
+    ensureDbgLoc(IIRB.IRB);
+    IO->instrument(FPtr, 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, IConf, IIRB, ICaches);
+      IIRB.returnAllocas();
+    }
+  }
   return Changed;
 }
 
@@ -244,12 +279,14 @@ bool InstrumentorImpl::instrument() {
   if (!shouldInstrumentTarget())
     return Changed;
 
-  for (auto &It : IConf.IChoices[InstrumentationLocation::INSTRUCTION_PRE])
-    if (It.second->Enabled)
-      InstChoicesPRE[It.second->getOpcode()] = It.second;
-  for (auto &It : IConf.IChoices[InstrumentationLocation::INSTRUCTION_POST])
-    if (It.second->Enabled)
-      InstChoicesPOST[It.second->getOpcode()] = It.second;
+  for (auto &[Name, IO] :
+       IConf.IChoices[InstrumentationLocation::INSTRUCTION_PRE])
+    if (IO->Enabled)
+      InstChoicesPRE[IO->getOpcode()] = IO;
+  for (auto &[Name, IO] :
+       IConf.IChoices[InstrumentationLocation::INSTRUCTION_POST])
+    if (IO->Enabled)
+      InstChoicesPOST[IO->getOpcode()] = IO;
 
   for (Function &Fn : M)
     Changed |= instrumentFunction(Fn);
@@ -315,6 +352,8 @@ 
BaseConfigurationOption::createStringOption(InstrumentationConfig &IConf,
 
 void InstrumentationConfig::populate(InstrumentorIRBuilderTy &IIRB) {
   /// List of all instrumentation opportunities.
+  FunctionIO::populate(*this, IIRB.Ctx);
+  AllocaIO::populate(*this, IIRB.Ctx);
   LoadIO::populate(*this, IIRB);
   StoreIO::populate(*this, IIRB);
 }
@@ -525,6 +564,256 @@ CallInst *IRTCallDescription::createLLVMCall(Value *&V,
   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(PointerType::getUnqual(IIRB.Ctx));
+
+  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, LLVMContext &Ctx,
+                      ConfigTy *UserConfig) {
+  using namespace std::placeholders;
+  if (UserConfig)
+    Config = *UserConfig;
+
+  bool IsPRE = getLocationKind() == InstrumentationLocation::FUNCTION_PRE;
+  if (Config.has(PassAddress))
+    IRTArgs.push_back(IRTArg(PointerType::getUnqual(Ctx), "address",
+                             "The function address.", IRTArg::NONE,
+                             getFunctionAddress));
+  if (Config.has(PassName))
+    IRTArgs.push_back(IRTArg(PointerType::getUnqual(Ctx), "name",
+                             "The function name.", IRTArg::STRING,
+                             getFunctionName));
+  if (Config.has(PassNumArguments))
+    IRTArgs.push_back(
+        IRTArg(IntegerType::getInt32Ty(Ctx), "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(PointerType::getUnqual(Ctx), "arguments",
+               "Description of the arguments.",
+               IsPRE && Config.has(ReplaceArguments) ? 
IRTArg::REPLACABLE_CUSTOM
+                                                     : IRTArg::NONE,
+               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(IntegerType::getInt8Ty(Ctx), "is_main",
+                             "Flag to indicate it is the main function.",
+                             IRTArg::NONE, isMainFunction));
+  addCommonArgs(IConf, Ctx, Config.has(PassId));
+  IConf.addChoice(*this, 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");
+}
+
+///}
+
+/// AllocaIO
+///{
+void AllocaIO::init(InstrumentationConfig &IConf, LLVMContext &Ctx,
+                    ConfigTy *UserConfig) {
+  if (UserConfig)
+    Config = *UserConfig;
+
+  bool IsPRE = getLocationKind() == InstrumentationLocation::INSTRUCTION_PRE;
+  if (!IsPRE && Config.has(PassAddress))
+    IRTArgs.push_back(IRTArg(
+        PointerType::getUnqual(Ctx), "address", "The allocated memory 
address.",
+        Config.has(ReplaceAddress) ? IRTArg::REPLACABLE : IRTArg::NONE,
+        InstrumentationOpportunity::getValue,
+        InstrumentationOpportunity::replaceValue));
+  if (Config.has(PassSize))
+    IRTArgs.push_back(IRTArg(
+        IntegerType::getInt64Ty(Ctx), "size", "The allocation size.",
+        (IsPRE && Config.has(ReplaceSize)) ? IRTArg::REPLACABLE : IRTArg::NONE,
+        getSize, setSize));
+  if (Config.has(PassAlignment))
+    IRTArgs.push_back(IRTArg(IntegerType::getInt64Ty(Ctx), "alignment",
+                             "The allocation alignment.", IRTArg::NONE,
+                             getAlignment));
+
+  addCommonArgs(IConf, Ctx, Config.has(PassId));
+  IConf.addChoice(*this, 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)
diff --git a/llvm/test/Instrumentation/Instrumentor/alloca_and_function.ll 
b/llvm/test/Instrumentation/Instrumentor/alloca_and_function.ll
new file mode 100644
index 0000000000000..e65562bfe8caf
--- /dev/null
+++ b/llvm/test/Instrumentation/Instrumentor/alloca_and_function.ll
@@ -0,0 +1,56 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py 
UTC_ARGS: --check-globals all --include-generated-funcs --version 5
+; RUN: opt < %s -passes=instrumentor -S | FileCheck %s
+
+; Check that we pack the arguments into a value_pack and unpack them again 
after the pre_function call.
+; Check that we replace the argument uses witht he unpacked values.
+; Check that we replace the alloca with the post_alloca returned value.
+
+target datalayout = 
"e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+
+declare void @use(ptr)
+
+define float @foo(i16 %a, float %b) {
+entry:
+  %0 = alloca i16, align 16
+  store i16 %a, ptr %0
+  call void @use(ptr %0)
+  ret float %b
+}
+;.
+; CHECK: @__instrumentor_.str = 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-LABEL: define float @foo(
+; CHECK-SAME: i16 [[A:%.*]], float [[B:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[TMP7:%.*]] = 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 [[TMP7]], ptr 
@__instrumentor_value_pack, i64 32, i1 false)
+; CHECK-NEXT:    [[TMP2:%.*]] = getelementptr inbounds nuw <{ i32, i32, [6 x 
i8], i16, i32, i32, [4 x i8], float }>, ptr [[TMP7]], i32 0, i32 3
+; CHECK-NEXT:    store i16 [[A]], ptr [[TMP2]], align 2
+; CHECK-NEXT:    [[TMP9:%.*]] = getelementptr inbounds nuw <{ i32, i32, [6 x 
i8], i16, i32, i32, [4 x i8], float }>, ptr [[TMP7]], i32 0, i32 7
+; CHECK-NEXT:    store float [[B]], ptr [[TMP9]], align 4
+; CHECK-NEXT:    call void @__instrumentor_pre_function(ptr @foo, ptr 
@__instrumentor_.str, i32 2, ptr [[TMP7]], i8 0, i32 3) #[[ATTR1:[0-9]+]]
+; CHECK-NEXT:    [[TMP3:%.*]] = getelementptr inbounds i8, ptr [[TMP7]], i32 14
+; CHECK-NEXT:    [[TMP4:%.*]] = load i16, ptr [[TMP3]], align 2
+; CHECK-NEXT:    [[TMP5:%.*]] = getelementptr inbounds i8, ptr [[TMP7]], i32 28
+; CHECK-NEXT:    [[TMP6:%.*]] = load float, ptr [[TMP5]], align 4
+; CHECK-NEXT:    [[TMP0:%.*]] = call i64 @__instrumentor_pre_alloca(i64 2, i64 
16, i32 1) #[[ATTR1]]
+; CHECK-NEXT:    [[TMP1:%.*]] = alloca i8, i64 [[TMP0]], align 16
+; CHECK-NEXT:    [[TMP13:%.*]] = call ptr @__instrumentor_post_alloca(ptr 
[[TMP1]], i64 2, i64 16, i32 -1) #[[ATTR1]]
+; CHECK-NEXT:    [[TMP10:%.*]] = zext i16 [[TMP4]] to i64
+; CHECK-NEXT:    [[TMP14:%.*]] = call ptr @__instrumentor_pre_store(ptr 
[[TMP13]], i32 0, i64 [[TMP10]], i64 2, i64 2, i32 12, i32 0, i8 1, i8 0, i32 
2) #[[ATTR1]]
+; CHECK-NEXT:    store i16 [[TMP4]], ptr [[TMP14]], align 2
+; CHECK-NEXT:    call void @__instrumentor_post_store(ptr [[TMP13]], i32 0, 
i64 [[TMP10]], i64 2, i64 2, i32 12, i32 0, i8 1, i8 0, i32 -2) #[[ATTR1]]
+; CHECK-NEXT:    call void @use(ptr [[TMP13]])
+; CHECK-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP7]], ptr 
@__instrumentor_value_pack, i64 32, i1 false)
+; CHECK-NEXT:    [[TMP12:%.*]] = getelementptr inbounds nuw <{ i32, i32, [6 x 
i8], i16, i32, i32, [4 x i8], float }>, ptr [[TMP7]], i32 0, i32 3
+; CHECK-NEXT:    store i16 [[A]], ptr [[TMP12]], align 2
+; CHECK-NEXT:    [[TMP11:%.*]] = getelementptr inbounds nuw <{ i32, i32, [6 x 
i8], i16, i32, i32, [4 x i8], float }>, ptr [[TMP7]], i32 0, i32 7
+; CHECK-NEXT:    store float [[B]], ptr [[TMP11]], align 4
+; CHECK-NEXT:    call void @__instrumentor_post_function(ptr @foo, ptr 
@__instrumentor_.str, i32 2, ptr [[TMP7]], i8 0, i32 -4) #[[ATTR1]]
+; CHECK-NEXT:    ret float [[TMP6]]
+;
+;.
+; CHECK: attributes #[[ATTR0:[0-9]+]] = { nocallback nofree nosync nounwind 
willreturn memory(argmem: readwrite) }
+; CHECK: attributes #[[ATTR1]] = { willreturn }
+;.
diff --git a/llvm/test/Instrumentation/Instrumentor/default_config.json 
b/llvm/test/Instrumentation/Instrumentor/default_config.json
index 263ab58e2566d..336dc20cfd5e0 100644
--- a/llvm/test/Instrumentation/Instrumentor/default_config.json
+++ b/llvm/test/Instrumentation/Instrumentor/default_config.json
@@ -6,11 +6,48 @@
     "runtime_stubs_file.description": "The file into which runtime stubs 
should be written.",
     "target_regex": "",
     "target_regex.description": "Regular expression to be matched against the 
module target. Only targets that match this regex will be instrumented",
+    "demangle_function_names": true,
+    "demangle_function_names.description": "Demangle functions names passed to 
the runtime.",
     "host_enabled": true,
     "host_enabled.description": "Instrument non-GPU targets",
     "gpu_enabled": true,
     "gpu_enabled.description": "Instrument GPU targets"
   },
+  "function_pre": {
+    "function": {
+      "enabled": true,
+      "address": true,
+      "address.description": "The function address.",
+      "name": true,
+      "name.description": "The function name.",
+      "num_arguments": true,
+      "num_arguments.description": "Number of function arguments (without 
varargs).",
+      "arguments": true,
+      "arguments.replace": true,
+      "arguments.description": "Description of the arguments.",
+      "is_main": true,
+      "is_main.description": "Flag to indicate it is the main function.",
+      "id": true,
+      "id.description": "A unique ID associated with the given instrumentor 
call"
+    }
+  },
+  "function_post": {
+    "function": {
+      "enabled": true,
+      "address": true,
+      "address.description": "The function address.",
+      "name": true,
+      "name.description": "The function name.",
+      "num_arguments": true,
+      "num_arguments.description": "Number of function arguments (without 
varargs).",
+      "arguments": true,
+      "arguments.description": "Description of the arguments.",
+      "is_main": true,
+      "is_main.description": "Flag to indicate it is the main function.",
+      "id": true,
+      "id.description": "A unique ID associated with the given instrumentor 
call"
+    }
+  },
   "instruction_pre": {
     "load": {
       "enabled": true,
@@ -34,6 +71,16 @@
       "id": true,
       "id.description": "A unique ID associated with the given instrumentor 
call"
     },
+    "alloca": {
+      "enabled": true,
+      "size": true,
+      "size.replace": true,
+      "size.description": "The allocation size.",
+      "alignment": true,
+      "alignment.description": "The allocation alignment.",
+      "id": true,
+      "id.description": "A unique ID associated with the given instrumentor 
call"
+    },
     "store": {
       "enabled": true,
       "pointer": true,
@@ -84,6 +131,18 @@
       "id": true,
       "id.description": "A unique ID associated with the given instrumentor 
call"
     },
+    "alloca": {
+      "enabled": true,
+      "address": true,
+      "address.replace": true,
+      "address.description": "The allocated memory address.",
+      "size": true,
+      "size.description": "The allocation size.",
+      "alignment": true,
+      "alignment.description": "The allocation alignment.",
+      "id": true,
+      "id.description": "A unique ID associated with the given instrumentor 
call"
+    },
     "store": {
       "enabled": true,
       "pointer": true,

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to