Skip to content

[Instrumentor] Allow printing a runtime stub #138978

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: users/kevinsala/instrumentor-base-pr
Choose a base branch
from

Conversation

kevinsala
Copy link
Contributor

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.

@llvmbot
Copy link
Member

llvmbot commented May 7, 2025

@llvm/pr-subscribers-llvm-transforms

Author: Kevin Sala Penades (kevinsala)

Changes

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 --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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants