| //===-- 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 |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // The implementation of a generator of Instrumentor's runtime stubs. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "llvm/Transforms/IPO/Instrumentor.h" |
| #include "llvm/Transforms/IPO/InstrumentorVariables.inc" |
| |
| #include "llvm/ADT/SmallVector.h" |
| #include "llvm/ADT/StringExtras.h" |
| #include "llvm/ADT/StringRef.h" |
| #include "llvm/IR/LLVMContext.h" |
| #include "llvm/Support/Path.h" |
| #include "llvm/Support/raw_ostream.h" |
| |
| #include <cassert> |
| #include <string> |
| #include <system_error> |
| |
| namespace llvm { |
| namespace instrumentor { |
| |
| /// Get the string representation of an argument with type \p Ty. Two strings |
| /// are returned: one for direct arguments and another for indirect arguments. |
| /// The flags in \p Flags describe the properties of the argument. See |
| /// IRTArg::IRArgFlagTy. |
| 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 {"<>", "<>"}; |
| } |
| |
| /// Get the string representation of the C printf format of an argument with |
| /// type \p Ty. The flags in \p Flags describe the properties of the argument. |
| /// See IRTArg::IRArgFlagTy. |
| static std::string getPrintfFormatString(Type *Ty, unsigned Flags) { |
| if (Ty->isIntegerTy()) { |
| if (Ty->getIntegerBitWidth() > 32) { |
| assert(Ty->getIntegerBitWidth() == 64); |
| return "%\" PRId64 \""; |
| } |
| return "%\" PRId32 \""; |
| } |
| 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; |
| } |
| |
| // Handle value pack arguments specially |
| if (IRArg.Flags & IRTArg::VALUE_PACK) { |
| DirectFormat += "[value pack at %p]"; |
| IndirectFormat += "[value pack at %p]"; |
| continue; |
| } |
| |
| 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: %\" PRId32 \""; |
| IndirectArg += ", " + IRArg.Name.str() + "_size"; |
| } |
| } |
| } |
| |
| std::string DirectBody = DirectFormat + "\\n\"" + DirectArg + ");\n"; |
| std::string IndirectBody = IndirectFormat + "\\n\"" + IndirectArg + ");\n"; |
| |
| // Add value pack element printing |
| for (size_t ArgIdx = 0; ArgIdx < IO.IRTArgs.size(); ++ArgIdx) { |
| auto &IRArg = IO.IRTArgs[ArgIdx]; |
| if (!IRArg.Enabled || !(IRArg.Flags & IRTArg::VALUE_PACK)) |
| continue; |
| |
| // Find the count parameter - it should be the previous enabled argument |
| std::string CountParam; |
| for (int PrevIdx = ArgIdx - 1; PrevIdx >= 0; --PrevIdx) { |
| if (IO.IRTArgs[PrevIdx].Enabled && |
| IO.IRTArgs[PrevIdx].Name.equals_insensitive( |
| ("num_" + IRArg.Name).str())) { |
| CountParam = IO.IRTArgs[PrevIdx].Name.str(); |
| break; |
| } |
| } |
| |
| // If no count parameter found, use 0 (will skip iteration) |
| if (CountParam.empty()) |
| CountParam = "0 /* count not enabled! */"; |
| |
| auto AddToBodies = [&](Twine T) { |
| DirectBody += T.str(); |
| IndirectBody += T.str(); |
| }; |
| |
| // Direct version: iterate through the value pack at the pointer |
| AddToBodies(" ValuePackIterator iter_" + IRArg.Name.str() + ";\n"); |
| AddToBodies(" initValuePackIterator(&iter_" + IRArg.Name.str() + ", " + |
| IRArg.Name.str() + ", " + CountParam + ");\n"); |
| AddToBodies(" while (iter_" + IRArg.Name.str() + ".index < iter_" + |
| IRArg.Name.str() + ".count) {\n"); |
| AddToBodies(" ValuePackHeader header_" + IRArg.Name.str() + |
| " = getValuePackHeader(&iter_" + IRArg.Name.str() + ");\n"); |
| AddToBodies(" const void *data_" + IRArg.Name.str() + |
| " = getValuePackData(&iter_" + IRArg.Name.str() + ");\n"); |
| AddToBodies(" printf(\" [%" PRIu32 "] type=%s size=%" PRIu32 |
| " data=%p\\n\", iter_" + |
| IRArg.Name.str() + ".index, getLLVMTypeIDName(header_" + |
| IRArg.Name.str() + ".type_id), header_" + IRArg.Name.str() + |
| ".size, data_" + IRArg.Name.str() + ");\n"); |
| AddToBodies(" nextValuePack(&iter_" + IRArg.Name.str() + ");\n"); |
| AddToBodies(" }\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)}; |
| } |
| |
| void printRuntimeHeader(const InstrumentationConfig &IConf, |
| StringRef HeaderFileName, LLVMContext &Ctx) { |
| if (HeaderFileName.empty()) |
| return; |
| |
| std::error_code EC; |
| raw_fd_ostream OS(HeaderFileName, EC); |
| if (EC) { |
| Ctx.emitError( |
| Twine("failed to open instrumentor runtime header file for writing: ") + |
| EC.message()); |
| return; |
| } |
| |
| StringRef Prefix = IConf.getRTName(); |
| |
| OS << InstrumentorRuntimeHelper; |
| OS << "\n// Generated with runtime prefix: " << Prefix << "\n"; |
| } |
| |
| void printRuntimeStub(const InstrumentationConfig &IConf, |
| StringRef StubRuntimeName, LLVMContext &Ctx) { |
| if (StubRuntimeName.empty()) |
| return; |
| |
| std::error_code EC; |
| raw_fd_ostream OS(StubRuntimeName, EC); |
| if (EC) { |
| Ctx.emitError( |
| Twine("failed to open instrumentor stub runtime file for writing: ") + |
| EC.message()); |
| return; |
| } |
| |
| // Generate the header file alongside the stub |
| StringRef Prefix = IConf.getRTName(); |
| std::string HeaderFileName = StubRuntimeName.str(); |
| size_t DotPos = HeaderFileName.rfind('.'); |
| if (DotPos != std::string::npos) |
| HeaderFileName = HeaderFileName.substr(0, DotPos); |
| HeaderFileName += ".h"; |
| printRuntimeHeader(IConf, HeaderFileName, Ctx); |
| |
| OS << "//===-- Instrumentor Runtime Stub " |
| "-----------------------------------------===//\n"; |
| OS << "//\n"; |
| OS << "// Part of the LLVM Project, under the Apache License v2.0 with LLVM " |
| "Exceptions.\n"; |
| OS << "// See https://llvm.org/LICENSE.txt for license information.\n"; |
| OS << "// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n"; |
| OS << "//\n"; |
| OS << "//" |
| "===-------------------------------------------------------------------" |
| "---===//\n"; |
| OS << "//\n"; |
| OS << "// This file is auto-generated by the LLVM Instrumentor pass.\n"; |
| OS << "// It provides stub implementations of instrumentation runtime " |
| "functions\n"; |
| OS << "// that print human-readable information about instrumentation " |
| "events.\n"; |
| OS << "//\n"; |
| OS << "// Generated with runtime prefix: " << Prefix << "\n"; |
| OS << "//\n"; |
| OS << "//" |
| "===-------------------------------------------------------------------" |
| "---===//\n\n"; |
| OS << "#include <inttypes.h>\n"; |
| OS << "#include <stdint.h>\n"; |
| OS << "#include <stdio.h>\n"; |
| OS << "#include \"" << llvm::sys::path::filename(HeaderFileName) << "\"\n\n"; |
| OS << "#ifdef __cplusplus\n"; |
| OS << "extern \"C\" {\n"; |
| OS << "#endif\n\n"; |
| |
| for (auto &ChoiceMap : IConf.IChoices) { |
| for (auto &[_, IO] : ChoiceMap) { |
| if (!IO->Enabled) |
| continue; |
| IRTCallDescription IRTCallDesc(*IO, IO->getRetTy(Ctx)); |
| const auto Signatures = IRTCallDesc.createCSignature(IConf); |
| const auto Bodies = IRTCallDesc.createCBodies(); |
| if (!Signatures.first.empty()) { |
| OS << Signatures.first << " {\n"; |
| OS << " " << Bodies.first << "}\n\n"; |
| } |
| if (!Signatures.second.empty()) { |
| OS << Signatures.second << " {\n"; |
| OS << " " << Bodies.second << "}\n\n"; |
| } |
| } |
| } |
| |
| OS << "#ifdef __cplusplus\n"; |
| OS << "}\n"; |
| OS << "#endif\n"; |
| } |
| |
| } // end namespace instrumentor |
| } // end namespace llvm |