//===-- 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 #include #include 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 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 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 IRTCallDescription::createCSignature(const InstrumentationConfig &IConf) const { SmallVector 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 &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 \n"; OS << "#include \n"; OS << "#include \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