| 1 | //===-- InstrumentorStubPrinter.cpp ---------------------------------------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | // |
| 9 | // The implementation of a generator of Instrumentor's runtime stubs. |
| 10 | // |
| 11 | //===----------------------------------------------------------------------===// |
| 12 | |
| 13 | #include "llvm/Transforms/IPO/Instrumentor.h" |
| 14 | #include "llvm/Transforms/IPO/InstrumentorVariables.inc" |
| 15 | |
| 16 | #include "llvm/ADT/SmallVector.h" |
| 17 | #include "llvm/ADT/StringExtras.h" |
| 18 | #include "llvm/ADT/StringRef.h" |
| 19 | #include "llvm/IR/LLVMContext.h" |
| 20 | #include "llvm/Support/Path.h" |
| 21 | #include "llvm/Support/raw_ostream.h" |
| 22 | |
| 23 | #include <cassert> |
| 24 | #include <string> |
| 25 | #include <system_error> |
| 26 | |
| 27 | namespace llvm { |
| 28 | namespace instrumentor { |
| 29 | |
| 30 | /// Get the string representation of an argument with type \p Ty. Two strings |
| 31 | /// are returned: one for direct arguments and another for indirect arguments. |
| 32 | /// The flags in \p Flags describe the properties of the argument. See |
| 33 | /// IRTArg::IRArgFlagTy. |
| 34 | static std::pair<std::string, std::string> getAsCType(Type *Ty, |
| 35 | unsigned Flags) { |
| 36 | if (Ty->isIntegerTy()) { |
| 37 | auto BW = Ty->getIntegerBitWidth(); |
| 38 | if (BW == 1) |
| 39 | return {"bool " , "bool *" }; |
| 40 | auto S = "int" + std::to_string(val: BW) + "_t " ; |
| 41 | return {S, S + "*" }; |
| 42 | } |
| 43 | if (Ty->isPointerTy()) |
| 44 | return {Flags & IRTArg::STRING ? "char *" : "void *" , "void **" }; |
| 45 | if (Ty->isFloatTy()) |
| 46 | return {"float " , "float *" }; |
| 47 | if (Ty->isDoubleTy()) |
| 48 | return {"double " , "double *" }; |
| 49 | return {"<>" , "<>" }; |
| 50 | } |
| 51 | |
| 52 | /// Get the string representation of the C printf format of an argument with |
| 53 | /// type \p Ty. The flags in \p Flags describe the properties of the argument. |
| 54 | /// See IRTArg::IRArgFlagTy. |
| 55 | static std::string getPrintfFormatString(Type *Ty, unsigned Flags) { |
| 56 | if (Flags & IRTArg::TYPEID) |
| 57 | return "%s" ; |
| 58 | if (Ty->isIntegerTy()) { |
| 59 | if (Ty->getIntegerBitWidth() > 32) { |
| 60 | assert(Ty->getIntegerBitWidth() == 64); |
| 61 | return "%\" PRId64 \"" ; |
| 62 | } |
| 63 | return "%\" PRId32 \"" ; |
| 64 | } |
| 65 | if (Ty->isPointerTy()) |
| 66 | return Flags & IRTArg::STRING ? "%s" : "%p" ; |
| 67 | if (Ty->isFloatTy()) |
| 68 | return "%f" ; |
| 69 | if (Ty->isDoubleTy()) |
| 70 | return "%lf" ; |
| 71 | return "<>" ; |
| 72 | } |
| 73 | |
| 74 | std::pair<std::string, std::string> IRTCallDescription::createCBodies() const { |
| 75 | std::string DirectFormat = "printf(\"" + IO.getName().str() + |
| 76 | (IO.IP.isPRE() ? " pre" : " post" ) + " -- " ; |
| 77 | std::string IndirectFormat = DirectFormat; |
| 78 | std::string DirectArg, IndirectArg, DirectReturnValue, IndirectReturnValue; |
| 79 | |
| 80 | auto AddToFormats = [&](Twine S) { |
| 81 | DirectFormat += S.str(); |
| 82 | IndirectFormat += S.str(); |
| 83 | }; |
| 84 | auto AddToArgs = [&](Twine S) { |
| 85 | DirectArg += S.str(); |
| 86 | IndirectArg += S.str(); |
| 87 | }; |
| 88 | bool First = true; |
| 89 | for (auto &IRArg : IO.IRTArgs) { |
| 90 | if (!IRArg.Enabled) |
| 91 | continue; |
| 92 | if (!First) |
| 93 | AddToFormats(", " ); |
| 94 | First = false; |
| 95 | |
| 96 | if (!(IRArg.Flags & IRTArg::TYPEID)) { |
| 97 | AddToArgs(", " + IRArg.Name); |
| 98 | } else { |
| 99 | AddToArgs(", getLLVMTypeIDName(" + IRArg.Name + ")" ); |
| 100 | } |
| 101 | AddToFormats(IRArg.Name + ": " ); |
| 102 | if (NumReplaceableArgs == 1 && (IRArg.Flags & IRTArg::REPLACABLE)) { |
| 103 | DirectReturnValue = IRArg.Name; |
| 104 | if (!isPotentiallyIndirect(IRTA&: IRArg)) |
| 105 | IndirectReturnValue = IRArg.Name; |
| 106 | } |
| 107 | |
| 108 | // Handle value pack arguments specially |
| 109 | if (IRArg.Flags & IRTArg::VALUE_PACK) { |
| 110 | DirectFormat += "[value pack at %p]" ; |
| 111 | IndirectFormat += "[value pack at %p]" ; |
| 112 | continue; |
| 113 | } |
| 114 | |
| 115 | if (!isPotentiallyIndirect(IRTA&: IRArg)) { |
| 116 | AddToFormats(getPrintfFormatString(Ty: IRArg.Ty, Flags: IRArg.Flags)); |
| 117 | } else { |
| 118 | DirectFormat += getPrintfFormatString(Ty: IRArg.Ty, Flags: IRArg.Flags); |
| 119 | IndirectFormat += "%p" ; |
| 120 | IndirectArg += "_ptr" ; |
| 121 | // Add the indirect argument size |
| 122 | if (!(IRArg.Flags & IRTArg::INDIRECT_HAS_SIZE)) { |
| 123 | IndirectFormat += ", " + IRArg.Name.str() + "_size: %\" PRId32 \"" ; |
| 124 | IndirectArg += ", " + IRArg.Name.str() + "_size" ; |
| 125 | } |
| 126 | } |
| 127 | } |
| 128 | |
| 129 | std::string DirectBody = DirectFormat + "\\n\"" + DirectArg + ");\n" ; |
| 130 | std::string IndirectBody = IndirectFormat + "\\n\"" + IndirectArg + ");\n" ; |
| 131 | |
| 132 | // Add value pack element printing |
| 133 | for (size_t ArgIdx = 0; ArgIdx < IO.IRTArgs.size(); ++ArgIdx) { |
| 134 | auto &IRArg = IO.IRTArgs[ArgIdx]; |
| 135 | if (!IRArg.Enabled || !(IRArg.Flags & IRTArg::VALUE_PACK)) |
| 136 | continue; |
| 137 | |
| 138 | // Find the count parameter - it should be the previous enabled argument |
| 139 | std::string CountParam; |
| 140 | for (int PrevIdx = ArgIdx - 1; PrevIdx >= 0; --PrevIdx) { |
| 141 | if (IO.IRTArgs[PrevIdx].Enabled && |
| 142 | IO.IRTArgs[PrevIdx].Name.equals_insensitive( |
| 143 | RHS: ("num_" + IRArg.Name).str())) { |
| 144 | CountParam = IO.IRTArgs[PrevIdx].Name.str(); |
| 145 | break; |
| 146 | } |
| 147 | } |
| 148 | |
| 149 | // If no count parameter found, use 0 (will skip iteration) |
| 150 | if (CountParam.empty()) |
| 151 | CountParam = "0 /* count not enabled! */" ; |
| 152 | |
| 153 | auto AddToBodies = [&](Twine T) { |
| 154 | DirectBody += T.str(); |
| 155 | IndirectBody += T.str(); |
| 156 | }; |
| 157 | |
| 158 | // Direct version: iterate through the value pack at the pointer |
| 159 | AddToBodies(" ValuePackIterator iter_" + IRArg.Name.str() + ";\n" ); |
| 160 | AddToBodies(" initValuePackIterator(&iter_" + IRArg.Name.str() + ", " + |
| 161 | IRArg.Name.str() + ", " + CountParam + ");\n" ); |
| 162 | AddToBodies(" while (iter_" + IRArg.Name.str() + ".index < iter_" + |
| 163 | IRArg.Name.str() + ".count) {\n" ); |
| 164 | AddToBodies(" ValuePackHeader header_" + IRArg.Name.str() + |
| 165 | " = getValuePackHeader(&iter_" + IRArg.Name.str() + ");\n" ); |
| 166 | AddToBodies(" const void *data_" + IRArg.Name.str() + |
| 167 | " = getValuePackData(&iter_" + IRArg.Name.str() + ");\n" ); |
| 168 | AddToBodies(" printf(\" [%" PRIu32 "] type=%s size=%" PRIu32 |
| 169 | " data=%p\\n\", iter_" + |
| 170 | IRArg.Name.str() + ".index, getLLVMTypeIDName(header_" + |
| 171 | IRArg.Name.str() + ".type_id), header_" + IRArg.Name.str() + |
| 172 | ".size, data_" + IRArg.Name.str() + ");\n" ); |
| 173 | AddToBodies(" nextValuePack(&iter_" + IRArg.Name.str() + ");\n" ); |
| 174 | AddToBodies(" }\n" ); |
| 175 | } |
| 176 | |
| 177 | if (RetTy) |
| 178 | IndirectReturnValue = DirectReturnValue = "0" ; |
| 179 | if (!DirectReturnValue.empty()) |
| 180 | DirectBody += " return " + DirectReturnValue + ";\n" ; |
| 181 | if (!IndirectReturnValue.empty()) |
| 182 | IndirectBody += " return " + IndirectReturnValue + ";\n" ; |
| 183 | return {DirectBody, IndirectBody}; |
| 184 | } |
| 185 | |
| 186 | std::pair<std::string, std::string> |
| 187 | IRTCallDescription::createCSignature(const InstrumentationConfig &IConf) const { |
| 188 | SmallVector<std::string> DirectArgs, IndirectArgs; |
| 189 | std::string DirectRetTy = "void " , IndirectRetTy = "void " ; |
| 190 | for (auto &IRArg : IO.IRTArgs) { |
| 191 | if (!IRArg.Enabled) |
| 192 | continue; |
| 193 | const auto &[DirectArgTy, IndirectArgTy] = |
| 194 | getAsCType(Ty: IRArg.Ty, Flags: IRArg.Flags); |
| 195 | std::string DirectArg = DirectArgTy + IRArg.Name.str(); |
| 196 | std::string IndirectArg = IndirectArgTy + IRArg.Name.str() + "_ptr" ; |
| 197 | std::string IndirectArgSize = "int32_t " + IRArg.Name.str() + "_size" ; |
| 198 | DirectArgs.push_back(Elt: DirectArg); |
| 199 | if (NumReplaceableArgs == 1 && (IRArg.Flags & IRTArg::REPLACABLE)) { |
| 200 | DirectRetTy = DirectArgTy; |
| 201 | if (!isPotentiallyIndirect(IRTA&: IRArg)) |
| 202 | IndirectRetTy = DirectArgTy; |
| 203 | } |
| 204 | if (!isPotentiallyIndirect(IRTA&: IRArg)) { |
| 205 | IndirectArgs.push_back(Elt: DirectArg); |
| 206 | } else { |
| 207 | IndirectArgs.push_back(Elt: IndirectArg); |
| 208 | if (!(IRArg.Flags & IRTArg::INDIRECT_HAS_SIZE)) |
| 209 | IndirectArgs.push_back(Elt: IndirectArgSize); |
| 210 | } |
| 211 | } |
| 212 | |
| 213 | auto DirectName = |
| 214 | IConf.getRTName(Prefix: IO.IP.isPRE() ? "pre_" : "post_" , Name: IO.getName(), Suffix1: "" ); |
| 215 | auto IndirectName = |
| 216 | IConf.getRTName(Prefix: IO.IP.isPRE() ? "pre_" : "post_" , Name: IO.getName(), Suffix1: "_ind" ); |
| 217 | auto MakeSignature = [&](std::string &RetTy, std::string &Name, |
| 218 | SmallVectorImpl<std::string> &Args) { |
| 219 | return RetTy + Name + "(" + join(R&: Args, Separator: ", " ) + ")" ; |
| 220 | }; |
| 221 | |
| 222 | if (RetTy) { |
| 223 | auto UserRetTy = getAsCType(Ty: RetTy, Flags: 0).first; |
| 224 | assert((DirectRetTy == UserRetTy || DirectRetTy == "void " ) && |
| 225 | (IndirectRetTy == UserRetTy || IndirectRetTy == "void " ) && |
| 226 | "Explicit return type but also implicit one!" ); |
| 227 | IndirectRetTy = DirectRetTy = UserRetTy; |
| 228 | } |
| 229 | if (RequiresIndirection) |
| 230 | return {"" , MakeSignature(IndirectRetTy, IndirectName, IndirectArgs)}; |
| 231 | if (!MightRequireIndirection) |
| 232 | return {MakeSignature(DirectRetTy, DirectName, DirectArgs), "" }; |
| 233 | return {MakeSignature(DirectRetTy, DirectName, DirectArgs), |
| 234 | MakeSignature(IndirectRetTy, IndirectName, IndirectArgs)}; |
| 235 | } |
| 236 | |
| 237 | void (const InstrumentationConfig &IConf, |
| 238 | StringRef , LLVMContext &Ctx) { |
| 239 | if (HeaderFileName.empty()) |
| 240 | return; |
| 241 | |
| 242 | std::error_code EC; |
| 243 | raw_fd_ostream OS(HeaderFileName, EC); |
| 244 | if (EC) { |
| 245 | Ctx.emitError( |
| 246 | ErrorStr: Twine("failed to open instrumentor runtime header file for writing: " ) + |
| 247 | EC.message()); |
| 248 | return; |
| 249 | } |
| 250 | |
| 251 | StringRef Prefix = IConf.getRTName(); |
| 252 | |
| 253 | OS << InstrumentorRuntimeHelper; |
| 254 | OS << "\n// Generated with runtime prefix: " << Prefix << "\n" ; |
| 255 | } |
| 256 | |
| 257 | void printRuntimeStub(const InstrumentationConfig &IConf, |
| 258 | StringRef StubRuntimeName, LLVMContext &Ctx) { |
| 259 | if (StubRuntimeName.empty()) |
| 260 | return; |
| 261 | |
| 262 | std::error_code EC; |
| 263 | raw_fd_ostream OS(StubRuntimeName, EC); |
| 264 | if (EC) { |
| 265 | Ctx.emitError( |
| 266 | ErrorStr: Twine("failed to open instrumentor stub runtime file for writing: " ) + |
| 267 | EC.message()); |
| 268 | return; |
| 269 | } |
| 270 | |
| 271 | // Generate the header file alongside the stub |
| 272 | StringRef Prefix = IConf.getRTName(); |
| 273 | std::string = StubRuntimeName.str(); |
| 274 | size_t DotPos = HeaderFileName.rfind(c: '.'); |
| 275 | if (DotPos != std::string::npos) |
| 276 | HeaderFileName = HeaderFileName.substr(pos: 0, n: DotPos); |
| 277 | HeaderFileName += ".h" ; |
| 278 | printRuntimeHeader(IConf, HeaderFileName, Ctx); |
| 279 | |
| 280 | OS << "//===-- Instrumentor Runtime Stub " |
| 281 | "-----------------------------------------===//\n" ; |
| 282 | OS << "//\n" ; |
| 283 | OS << "// Part of the LLVM Project, under the Apache License v2.0 with LLVM " |
| 284 | "Exceptions.\n" ; |
| 285 | OS << "// See https://llvm.org/LICENSE.txt for license information.\n" ; |
| 286 | OS << "// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n" ; |
| 287 | OS << "//\n" ; |
| 288 | OS << "//" |
| 289 | "===-------------------------------------------------------------------" |
| 290 | "---===//\n" ; |
| 291 | OS << "//\n" ; |
| 292 | OS << "// This file is auto-generated by the LLVM Instrumentor pass.\n" ; |
| 293 | OS << "// It provides stub implementations of instrumentation runtime " |
| 294 | "functions\n" ; |
| 295 | OS << "// that print human-readable information about instrumentation " |
| 296 | "events.\n" ; |
| 297 | OS << "//\n" ; |
| 298 | OS << "// Generated with runtime prefix: " << Prefix << "\n" ; |
| 299 | OS << "//\n" ; |
| 300 | OS << "//" |
| 301 | "===-------------------------------------------------------------------" |
| 302 | "---===//\n\n" ; |
| 303 | OS << "#include <inttypes.h>\n" ; |
| 304 | OS << "#include <stdint.h>\n" ; |
| 305 | OS << "#include <stdio.h>\n" ; |
| 306 | OS << "#include \"" << llvm::sys::path::filename(path: HeaderFileName) << "\"\n\n" ; |
| 307 | OS << "#ifdef __cplusplus\n" ; |
| 308 | OS << "extern \"C\" {\n" ; |
| 309 | OS << "#endif\n\n" ; |
| 310 | |
| 311 | for (auto &ChoiceMap : IConf.IChoices) { |
| 312 | for (auto &[_, IO] : ChoiceMap) { |
| 313 | if (!IO->Enabled) |
| 314 | continue; |
| 315 | IRTCallDescription IRTCallDesc(*IO, IO->getRetTy(Ctx)); |
| 316 | const auto Signatures = IRTCallDesc.createCSignature(IConf); |
| 317 | const auto Bodies = IRTCallDesc.createCBodies(); |
| 318 | if (!Signatures.first.empty()) { |
| 319 | OS << Signatures.first << " {\n" ; |
| 320 | OS << " " << Bodies.first << "}\n\n" ; |
| 321 | } |
| 322 | if (!Signatures.second.empty()) { |
| 323 | OS << Signatures.second << " {\n" ; |
| 324 | OS << " " << Bodies.second << "}\n\n" ; |
| 325 | } |
| 326 | } |
| 327 | } |
| 328 | |
| 329 | OS << "#ifdef __cplusplus\n" ; |
| 330 | OS << "}\n" ; |
| 331 | OS << "#endif\n" ; |
| 332 | } |
| 333 | |
| 334 | } // end namespace instrumentor |
| 335 | } // end namespace llvm |
| 336 | |