-
Notifications
You must be signed in to change notification settings - Fork 15.4k
[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
base: users/kevinsala/instrumentor-base-pr
Are you sure you want to change the base?
[Instrumentor] Allow printing a runtime stub #138978
Conversation
|
@llvm/pr-subscribers-llvm-transforms Author: Kevin Sala Penades (kevinsala) ChangesThis PR extends the Instrumentor the option Full diff: https://github.com/llvm/llvm-project/pull/138978.diff 8 Files Affected:
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
|
d745402 to
da0eb06
Compare
3f03cb4 to
ee7e77f
Compare
da0eb06 to
371483a
Compare
|
The new version sets the default runtime stub file name as empty, so no runtime stub is printed by default. This approach is safer, as it prevents the Instrumentor from unintentionally generating a file and overwriting an existing one without the user noticing it. |
…ase-pr' into users/kevinsala/instrumentor-stub-rt-pr
This PR extends the Instrumentor the option
configuration.runtime_stubs_fileto generate a runtime stub file with the configured instrumentation. The stub prints all parameters passed to each enabled instrumentation function.