[llvm-branch-commits] [llvm] [Instrumentor] Allow printing a runtime stub (PR #138978)

via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Wed May 7 15:08:22 PDT 2025


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-transforms

Author: Kevin Sala Penades (kevinsala)

<details>
<summary>Changes</summary>

This PR extends the Instrumentor the option `configuration.runtime_stubs_file` to generate a runtime stub file with the configured instrumentation. The stub prints all parameters passed to each enabled instrumentation function.

---
Full diff: https://github.com/llvm/llvm-project/pull/138978.diff


8 Files Affected:

- (modified) llvm/include/llvm/Transforms/IPO/Instrumentor.h (+9) 
- (added) llvm/include/llvm/Transforms/IPO/InstrumentorStubPrinter.h (+30) 
- (modified) llvm/lib/Transforms/IPO/CMakeLists.txt (+1) 
- (modified) llvm/lib/Transforms/IPO/Instrumentor.cpp (+3) 
- (added) llvm/lib/Transforms/IPO/InstrumentorStubPrinter.cpp (+214) 
- (modified) llvm/test/Instrumentation/Instrumentor/default_config.json (+2) 
- (added) llvm/test/Instrumentation/Instrumentor/default_rt (+37) 
- (added) llvm/test/Instrumentation/Instrumentor/rt.ll (+3) 


``````````diff
diff --git a/llvm/include/llvm/Transforms/IPO/Instrumentor.h b/llvm/include/llvm/Transforms/IPO/Instrumentor.h
index 6fb5a06305096..7caa2448b70dd 100644
--- a/llvm/include/llvm/Transforms/IPO/Instrumentor.h
+++ b/llvm/include/llvm/Transforms/IPO/Instrumentor.h
@@ -174,6 +174,11 @@ struct InstrumentationCaches {
 struct IRTCallDescription {
   IRTCallDescription(InstrumentationOpportunity &IConf, Type *RetTy = nullptr);
 
+  std::pair<std::string, std::string> createCBodies() const;
+
+  std::pair<std::string, std::string>
+  createCSignature(const InstrumentationConfig &IConf) const;
+
   FunctionType *createLLVMSignature(InstrumentationConfig &IConf,
                                     LLVMContext &Ctx, const DataLayout &DL,
                                     bool ForceIndirection);
@@ -346,6 +351,9 @@ struct InstrumentationConfig {
   InstrumentationConfig() : SS(StringAllocator) {
     RuntimePrefix = BaseConfigurationOpportunity::getStringOption(
         *this, "runtime_prefix", "The runtime API prefix.", "__instrumentor_");
+    RuntimeStubsFile = BaseConfigurationOpportunity::getStringOption(
+        *this, "runtime_stubs_file",
+        "The file into which runtime stubs should be written.", "test.c");
     TargetRegex = BaseConfigurationOpportunity::getStringOption(
         *this, "target_regex",
         "Regular expression to be matched against the module target. "
@@ -373,6 +381,7 @@ struct InstrumentationConfig {
   SmallVector<BaseConfigurationOpportunity *> BaseConfigurationOpportunities;
 
   BaseConfigurationOpportunity *RuntimePrefix;
+  BaseConfigurationOpportunity *RuntimeStubsFile;
   BaseConfigurationOpportunity *TargetRegex;
   BaseConfigurationOpportunity *HostEnabled;
   BaseConfigurationOpportunity *GPUEnabled;
diff --git a/llvm/include/llvm/Transforms/IPO/InstrumentorStubPrinter.h b/llvm/include/llvm/Transforms/IPO/InstrumentorStubPrinter.h
new file mode 100644
index 0000000000000..65cd120d001e8
--- /dev/null
+++ b/llvm/include/llvm/Transforms/IPO/InstrumentorStubPrinter.h
@@ -0,0 +1,30 @@
+//===- Transforms/IPO/InstrumentorStubPrinter.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 generator of Instrumentor's runtime stub.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TRANSFORMS_IPO_INSTRUMENTOR_STUB_PRINTER_H
+#define LLVM_TRANSFORMS_IPO_INSTRUMENTOR_STUB_PRINTER_H
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Transforms/IPO/Instrumentor.h"
+
+namespace llvm {
+namespace instrumentor {
+
+/// Print a runtime stub file with the enabled instrumentation opportunities.
+void printRuntimeStub(const InstrumentationConfig &IConf,
+                      StringRef StubRuntimeName, const Module &M);
+
+} // end namespace instrumentor
+} // end namespace llvm
+
+#endif // LLVM_TRANSFORMS_IPO_INSTRUMENTOR_STUB_PRINTER_H
diff --git a/llvm/lib/Transforms/IPO/CMakeLists.txt b/llvm/lib/Transforms/IPO/CMakeLists.txt
index 824dff527a672..d1d132c51dca9 100644
--- a/llvm/lib/Transforms/IPO/CMakeLists.txt
+++ b/llvm/lib/Transforms/IPO/CMakeLists.txt
@@ -29,6 +29,7 @@ add_llvm_component_library(LLVMipo
   Inliner.cpp
   Instrumentor.cpp
   InstrumentorConfigFile.cpp
+  InstrumentorStubPrinter.cpp
   Internalize.cpp
   LoopExtractor.cpp
   LowerTypeTests.cpp
diff --git a/llvm/lib/Transforms/IPO/Instrumentor.cpp b/llvm/lib/Transforms/IPO/Instrumentor.cpp
index 17657cfae8fb5..9ba92b67085a7 100644
--- a/llvm/lib/Transforms/IPO/Instrumentor.cpp
+++ b/llvm/lib/Transforms/IPO/Instrumentor.cpp
@@ -10,6 +10,7 @@
 
 #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/SmallPtrSet.h"
@@ -256,6 +257,8 @@ PreservedAnalyses InstrumentorPass::run(Module &M, FunctionAnalysisManager &FAM,
 
   writeConfigToJSON(IConf, WriteJSONConfig);
 
+  printRuntimeStub(IConf, IConf.RuntimeStubsFile->getString(), M);
+
   bool Changed = Impl.instrument();
   if (!Changed)
     return PreservedAnalyses::all();
diff --git a/llvm/lib/Transforms/IPO/InstrumentorStubPrinter.cpp b/llvm/lib/Transforms/IPO/InstrumentorStubPrinter.cpp
new file mode 100644
index 0000000000000..d9252cc1008e9
--- /dev/null
+++ b/llvm/lib/Transforms/IPO/InstrumentorStubPrinter.cpp
@@ -0,0 +1,214 @@
+//===-- InstrumentorStubPrinter.cpp ---------------------------------------===//
+//
+// 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/IPO/Instrumentor.h"
+
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <cassert>
+#include <string>
+#include <system_error>
+
+namespace llvm {
+namespace instrumentor {
+
+static std::pair<std::string, std::string> getAsCType(Type *Ty,
+                                                      unsigned Flags) {
+  if (Ty->isIntegerTy()) {
+    auto BW = Ty->getIntegerBitWidth();
+    if (BW == 1)
+      return {"bool ", "bool *"};
+    auto S = "int" + std::to_string(BW) + "_t ";
+    return {S, S + "*"};
+  }
+  if (Ty->isPointerTy())
+    return {Flags & IRTArg::STRING ? "char *" : "void *", "void **"};
+  if (Ty->isFloatTy())
+    return {"float ", "float *"};
+  if (Ty->isDoubleTy())
+    return {"double ", "double *"};
+  return {"<>", "<>"};
+}
+
+static std::string getPrintfFormatString(Type *Ty, unsigned Flags) {
+  if (Ty->isIntegerTy()) {
+    if (Ty->getIntegerBitWidth() > 32) {
+      assert(Ty->getIntegerBitWidth() == 64);
+      return "%lli";
+    }
+    return "%i";
+  }
+  if (Ty->isPointerTy())
+    return Flags & IRTArg::STRING ? "%s" : "%p";
+  if (Ty->isFloatTy())
+    return "%f";
+  if (Ty->isDoubleTy())
+    return "%lf";
+  return "<>";
+}
+
+std::pair<std::string, std::string> IRTCallDescription::createCBodies() const {
+  std::string DirectFormat = "printf(\"" + IO.getName().str() +
+                             (IO.IP.isPRE() ? " pre" : " post") + " -- ";
+  std::string IndirectFormat = DirectFormat;
+  std::string DirectArg, IndirectArg, DirectReturnValue, IndirectReturnValue;
+
+  auto AddToFormats = [&](Twine S) {
+    DirectFormat += S.str();
+    IndirectFormat += S.str();
+  };
+  auto AddToArgs = [&](Twine S) {
+    DirectArg += S.str();
+    IndirectArg += S.str();
+  };
+  bool First = true;
+  for (auto &IRArg : IO.IRTArgs) {
+    if (!IRArg.Enabled)
+      continue;
+    if (!First)
+      AddToFormats(", ");
+    First = false;
+    AddToArgs(", " + IRArg.Name);
+    AddToFormats(IRArg.Name + ": ");
+    if (NumReplaceableArgs == 1 && (IRArg.Flags & IRTArg::REPLACABLE)) {
+      DirectReturnValue = IRArg.Name;
+      if (!isPotentiallyIndirect(IRArg))
+        IndirectReturnValue = IRArg.Name;
+    }
+    if (!isPotentiallyIndirect(IRArg)) {
+      AddToFormats(getPrintfFormatString(IRArg.Ty, IRArg.Flags));
+    } else {
+      DirectFormat += getPrintfFormatString(IRArg.Ty, IRArg.Flags);
+      IndirectFormat += "%p";
+      IndirectArg += "_ptr";
+      // Add the indirect argument size
+      if (!(IRArg.Flags & IRTArg::INDIRECT_HAS_SIZE)) {
+        IndirectFormat += ", " + IRArg.Name.str() + "_size: %i";
+        IndirectArg += ", " + IRArg.Name.str() + "_size";
+      }
+    }
+  }
+
+  std::string DirectBody = DirectFormat + "\\n\"" + DirectArg + ");\n";
+  std::string IndirectBody = IndirectFormat + "\\n\"" + IndirectArg + ");\n";
+  if (RetTy)
+    IndirectReturnValue = DirectReturnValue = "0";
+  if (!DirectReturnValue.empty())
+    DirectBody += "  return " + DirectReturnValue + ";\n";
+  if (!IndirectReturnValue.empty())
+    IndirectBody += "  return " + IndirectReturnValue + ";\n";
+  return {DirectBody, IndirectBody};
+}
+
+std::pair<std::string, std::string>
+IRTCallDescription::createCSignature(const InstrumentationConfig &IConf) const {
+  SmallVector<std::string> DirectArgs, IndirectArgs;
+  std::string DirectRetTy = "void ", IndirectRetTy = "void ";
+  for (auto &IRArg : IO.IRTArgs) {
+    if (!IRArg.Enabled)
+      continue;
+    const auto &[DirectArgTy, IndirectArgTy] =
+        getAsCType(IRArg.Ty, IRArg.Flags);
+    std::string DirectArg = DirectArgTy + IRArg.Name.str();
+    std::string IndirectArg = IndirectArgTy + IRArg.Name.str() + "_ptr";
+    std::string IndirectArgSize = "int32_t " + IRArg.Name.str() + "_size";
+    DirectArgs.push_back(DirectArg);
+    if (NumReplaceableArgs == 1 && (IRArg.Flags & IRTArg::REPLACABLE)) {
+      DirectRetTy = DirectArgTy;
+      if (!isPotentiallyIndirect(IRArg))
+        IndirectRetTy = DirectArgTy;
+    }
+    if (!isPotentiallyIndirect(IRArg)) {
+      IndirectArgs.push_back(DirectArg);
+    } else {
+      IndirectArgs.push_back(IndirectArg);
+      if (!(IRArg.Flags & IRTArg::INDIRECT_HAS_SIZE))
+        IndirectArgs.push_back(IndirectArgSize);
+    }
+  }
+
+  auto DirectName =
+      IConf.getRTName(IO.IP.isPRE() ? "pre_" : "post_", IO.getName(), "");
+  auto IndirectName =
+      IConf.getRTName(IO.IP.isPRE() ? "pre_" : "post_", IO.getName(), "_ind");
+  auto MakeSignature = [&](std::string &RetTy, std::string &Name,
+                           SmallVectorImpl<std::string> &Args) {
+    return RetTy + Name + "(" + join(Args, ", ") + ")";
+  };
+
+  if (RetTy) {
+    auto UserRetTy = getAsCType(RetTy, 0).first;
+    assert((DirectRetTy == UserRetTy || DirectRetTy == "void ") &&
+           (IndirectRetTy == UserRetTy || IndirectRetTy == "void ") &&
+           "Explicit return type but also implicit one!");
+    IndirectRetTy = DirectRetTy = UserRetTy;
+  }
+  if (RequiresIndirection)
+    return {"", MakeSignature(IndirectRetTy, IndirectName, IndirectArgs)};
+  if (!MightRequireIndirection)
+    return {MakeSignature(DirectRetTy, DirectName, DirectArgs), ""};
+  return {MakeSignature(DirectRetTy, DirectName, DirectArgs),
+          MakeSignature(IndirectRetTy, IndirectName, IndirectArgs)};
+}
+
+static raw_fd_ostream *createOutputStream(StringRef Name) {
+  std::error_code EC;
+  auto *Out = new raw_fd_ostream(Name, EC);
+  if (EC) {
+    errs() << "WARNING: Failed to open instrumentor stub runtime file for "
+              "writing: "
+           << EC.message() << "\n";
+    delete Out;
+    Out = nullptr;
+  } else {
+    *Out << "// LLVM Instrumentor stub runtime\n\n";
+    *Out << "#include <stdint.h>\n";
+    *Out << "#include <stdio.h>\n\n";
+  }
+
+  return Out;
+}
+
+void printRuntimeStub(const InstrumentationConfig &IConf,
+                      StringRef StubRuntimeName, const Module &M) {
+  if (StubRuntimeName.empty())
+    return;
+
+  auto *Out = createOutputStream(StubRuntimeName);
+  if (!Out)
+    return;
+
+  for (auto &ChoiceMap : IConf.IChoices) {
+    for (auto &[_, IO] : ChoiceMap) {
+      if (!IO->Enabled)
+        continue;
+      IRTCallDescription IRTCallDesc(*IO, IO->getRetTy(M.getContext()));
+      const auto Signatures = IRTCallDesc.createCSignature(IConf);
+      const auto Bodies = IRTCallDesc.createCBodies();
+      if (!Signatures.first.empty()) {
+        *Out << Signatures.first << " {\n";
+        *Out << "  " << Bodies.first << "}\n\n";
+      }
+      if (!Signatures.second.empty()) {
+        *Out << Signatures.second << " {\n";
+        *Out << "  " << Bodies.second << "}\n\n";
+      }
+    }
+  }
+
+  delete Out;
+}
+
+} // end namespace instrumentor
+} // end namespace llvm
diff --git a/llvm/test/Instrumentation/Instrumentor/default_config.json b/llvm/test/Instrumentation/Instrumentor/default_config.json
index 2765d5c0116d2..69420fd0b01b8 100644
--- a/llvm/test/Instrumentation/Instrumentor/default_config.json
+++ b/llvm/test/Instrumentation/Instrumentor/default_config.json
@@ -2,6 +2,8 @@
   "configuration": {
     "runtime_prefix": "__instrumentor_",
     "runtime_prefix.description": "The runtime API prefix.",
+    "runtime_stubs_file": "test.c",
+    "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",
     "host_enabled": true,
diff --git a/llvm/test/Instrumentation/Instrumentor/default_rt b/llvm/test/Instrumentation/Instrumentor/default_rt
new file mode 100644
index 0000000000000..1e9750b2f6874
--- /dev/null
+++ b/llvm/test/Instrumentation/Instrumentor/default_rt
@@ -0,0 +1,37 @@
+// LLVM Instrumentor stub runtime
+
+#include <stdint.h>
+#include <stdio.h>
+
+void *__instrumentor_pre_load(void *pointer, int32_t pointer_as, int64_t value_size, int64_t alignment, int32_t value_type_id, int32_t atomicity_ordering, int8_t sync_scope_id, int8_t is_volatile, int32_t id) {
+  printf("load pre -- pointer: %p, pointer_as: %i, value_size: %lli, alignment: %lli, value_type_id: %i, atomicity_ordering: %i, sync_scope_id: %i, is_volatile: %i, id: %i\n", pointer, pointer_as, value_size, alignment, value_type_id, atomicity_ordering, sync_scope_id, is_volatile, id);
+  return pointer;
+}
+
+void *__instrumentor_pre_store(void *pointer, int32_t pointer_as, int64_t value, int64_t value_size, int64_t alignment, int32_t value_type_id, int32_t atomicity_ordering, int8_t sync_scope_id, int8_t is_volatile, int32_t id) {
+  printf("store pre -- pointer: %p, pointer_as: %i, value: %lli, value_size: %lli, alignment: %lli, value_type_id: %i, atomicity_ordering: %i, sync_scope_id: %i, is_volatile: %i, id: %i\n", pointer, pointer_as, value, value_size, alignment, value_type_id, atomicity_ordering, sync_scope_id, is_volatile, id);
+  return pointer;
+}
+
+void *__instrumentor_pre_store_ind(void *pointer, int32_t pointer_as, int64_t *value_ptr, int64_t value_size, int64_t alignment, int32_t value_type_id, int32_t atomicity_ordering, int8_t sync_scope_id, int8_t is_volatile, int32_t id) {
+  printf("store pre -- pointer: %p, pointer_as: %i, value: %p, value_size: %lli, alignment: %lli, value_type_id: %i, atomicity_ordering: %i, sync_scope_id: %i, is_volatile: %i, id: %i\n", pointer, pointer_as, value_ptr, value_size, alignment, value_type_id, atomicity_ordering, sync_scope_id, is_volatile, id);
+  return pointer;
+}
+
+int64_t __instrumentor_post_load(void *pointer, int32_t pointer_as, int64_t value, int64_t value_size, int64_t alignment, int32_t value_type_id, int32_t atomicity_ordering, int8_t sync_scope_id, int8_t is_volatile, int32_t id) {
+  printf("load post -- pointer: %p, pointer_as: %i, value: %lli, value_size: %lli, alignment: %lli, value_type_id: %i, atomicity_ordering: %i, sync_scope_id: %i, is_volatile: %i, id: %i\n", pointer, pointer_as, value, value_size, alignment, value_type_id, atomicity_ordering, sync_scope_id, is_volatile, id);
+  return value;
+}
+
+void __instrumentor_post_load_ind(void *pointer, int32_t pointer_as, int64_t *value_ptr, int64_t value_size, int64_t alignment, int32_t value_type_id, int32_t atomicity_ordering, int8_t sync_scope_id, int8_t is_volatile, int32_t id) {
+  printf("load post -- pointer: %p, pointer_as: %i, value: %p, value_size: %lli, alignment: %lli, value_type_id: %i, atomicity_ordering: %i, sync_scope_id: %i, is_volatile: %i, id: %i\n", pointer, pointer_as, value_ptr, value_size, alignment, value_type_id, atomicity_ordering, sync_scope_id, is_volatile, id);
+}
+
+void __instrumentor_post_store(void *pointer, int32_t pointer_as, int64_t value, int64_t value_size, int64_t alignment, int32_t value_type_id, int32_t atomicity_ordering, int8_t sync_scope_id, int8_t is_volatile, int32_t id) {
+  printf("store post -- pointer: %p, pointer_as: %i, value: %lli, value_size: %lli, alignment: %lli, value_type_id: %i, atomicity_ordering: %i, sync_scope_id: %i, is_volatile: %i, id: %i\n", pointer, pointer_as, value, value_size, alignment, value_type_id, atomicity_ordering, sync_scope_id, is_volatile, id);
+}
+
+void __instrumentor_post_store_ind(void *pointer, int32_t pointer_as, int64_t *value_ptr, int64_t value_size, int64_t alignment, int32_t value_type_id, int32_t atomicity_ordering, int8_t sync_scope_id, int8_t is_volatile, int32_t id) {
+  printf("store post -- pointer: %p, pointer_as: %i, value: %p, value_size: %lli, alignment: %lli, value_type_id: %i, atomicity_ordering: %i, sync_scope_id: %i, is_volatile: %i, id: %i\n", pointer, pointer_as, value_ptr, value_size, alignment, value_type_id, atomicity_ordering, sync_scope_id, is_volatile, id);
+}
+
diff --git a/llvm/test/Instrumentation/Instrumentor/rt.ll b/llvm/test/Instrumentation/Instrumentor/rt.ll
new file mode 100644
index 0000000000000..6a8c56084edce
--- /dev/null
+++ b/llvm/test/Instrumentation/Instrumentor/rt.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
+; RUN: diff test.c %S/default_rt

``````````

</details>


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


More information about the llvm-branch-commits mailing list