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 _______________________________________________ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits