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
27namespace llvm {
28namespace 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.
34static 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.
55static 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
74std::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
186std::pair<std::string, std::string>
187IRTCallDescription::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
237void printRuntimeHeader(const InstrumentationConfig &IConf,
238 StringRef HeaderFileName, 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
257void 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 HeaderFileName = 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