[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