1 | //===- AMDGPUEmitPrintf.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 | // Utility function to lower a printf call into a series of device |
10 | // library calls on the AMDGPU target. |
11 | // |
12 | // WARNING: This file knows about certain library functions. It recognizes them |
13 | // by name, and hardwires knowledge of their semantics. |
14 | // |
15 | //===----------------------------------------------------------------------===// |
16 | |
17 | #include "llvm/Transforms/Utils/AMDGPUEmitPrintf.h" |
18 | #include "llvm/ADT/SparseBitVector.h" |
19 | #include "llvm/ADT/StringExtras.h" |
20 | #include "llvm/Analysis/ValueTracking.h" |
21 | #include "llvm/IR/Module.h" |
22 | #include "llvm/Support/DataExtractor.h" |
23 | #include "llvm/Support/MD5.h" |
24 | #include "llvm/Support/MathExtras.h" |
25 | |
26 | using namespace llvm; |
27 | |
28 | #define DEBUG_TYPE "amdgpu-emit-printf" |
29 | |
30 | static Value *fitArgInto64Bits(IRBuilder<> &Builder, Value *Arg) { |
31 | auto Int64Ty = Builder.getInt64Ty(); |
32 | auto Ty = Arg->getType(); |
33 | |
34 | if (auto IntTy = dyn_cast<IntegerType>(Val: Ty)) { |
35 | switch (IntTy->getBitWidth()) { |
36 | case 32: |
37 | return Builder.CreateZExt(V: Arg, DestTy: Int64Ty); |
38 | case 64: |
39 | return Arg; |
40 | } |
41 | } |
42 | |
43 | if (Ty->getTypeID() == Type::DoubleTyID) { |
44 | return Builder.CreateBitCast(V: Arg, DestTy: Int64Ty); |
45 | } |
46 | |
47 | if (isa<PointerType>(Val: Ty)) { |
48 | return Builder.CreatePtrToInt(V: Arg, DestTy: Int64Ty); |
49 | } |
50 | |
51 | llvm_unreachable("unexpected type" ); |
52 | } |
53 | |
54 | static Value *callPrintfBegin(IRBuilder<> &Builder, Value *Version) { |
55 | auto Int64Ty = Builder.getInt64Ty(); |
56 | auto M = Builder.GetInsertBlock()->getModule(); |
57 | auto Fn = M->getOrInsertFunction(Name: "__ockl_printf_begin" , RetTy: Int64Ty, Args: Int64Ty); |
58 | return Builder.CreateCall(Callee: Fn, Args: Version); |
59 | } |
60 | |
61 | static Value *callAppendArgs(IRBuilder<> &Builder, Value *Desc, int NumArgs, |
62 | Value *Arg0, Value *Arg1, Value *Arg2, Value *Arg3, |
63 | Value *Arg4, Value *Arg5, Value *Arg6, |
64 | bool IsLast) { |
65 | auto Int64Ty = Builder.getInt64Ty(); |
66 | auto Int32Ty = Builder.getInt32Ty(); |
67 | auto M = Builder.GetInsertBlock()->getModule(); |
68 | auto Fn = M->getOrInsertFunction(Name: "__ockl_printf_append_args" , RetTy: Int64Ty, |
69 | Args: Int64Ty, Args: Int32Ty, Args: Int64Ty, Args: Int64Ty, Args: Int64Ty, |
70 | Args: Int64Ty, Args: Int64Ty, Args: Int64Ty, Args: Int64Ty, Args: Int32Ty); |
71 | auto IsLastValue = Builder.getInt32(C: IsLast); |
72 | auto NumArgsValue = Builder.getInt32(C: NumArgs); |
73 | return Builder.CreateCall(Callee: Fn, Args: {Desc, NumArgsValue, Arg0, Arg1, Arg2, Arg3, |
74 | Arg4, Arg5, Arg6, IsLastValue}); |
75 | } |
76 | |
77 | static Value *appendArg(IRBuilder<> &Builder, Value *Desc, Value *Arg, |
78 | bool IsLast) { |
79 | auto Arg0 = fitArgInto64Bits(Builder, Arg); |
80 | auto Zero = Builder.getInt64(C: 0); |
81 | return callAppendArgs(Builder, Desc, NumArgs: 1, Arg0, Arg1: Zero, Arg2: Zero, Arg3: Zero, Arg4: Zero, Arg5: Zero, |
82 | Arg6: Zero, IsLast); |
83 | } |
84 | |
85 | // The device library does not provide strlen, so we build our own loop |
86 | // here. While we are at it, we also include the terminating null in the length. |
87 | static Value *getStrlenWithNull(IRBuilder<> &Builder, Value *Str) { |
88 | auto *Prev = Builder.GetInsertBlock(); |
89 | Module *M = Prev->getModule(); |
90 | |
91 | auto CharZero = Builder.getInt8(C: 0); |
92 | auto One = Builder.getInt64(C: 1); |
93 | auto Zero = Builder.getInt64(C: 0); |
94 | auto Int64Ty = Builder.getInt64Ty(); |
95 | |
96 | // The length is either zero for a null pointer, or the computed value for an |
97 | // actual string. We need a join block for a phi that represents the final |
98 | // value. |
99 | // |
100 | // Strictly speaking, the zero does not matter since |
101 | // __ockl_printf_append_string_n ignores the length if the pointer is null. |
102 | BasicBlock *Join = nullptr; |
103 | if (Prev->getTerminator()) { |
104 | Join = Prev->splitBasicBlock(I: Builder.GetInsertPoint(), |
105 | BBName: "strlen.join" ); |
106 | Prev->getTerminator()->eraseFromParent(); |
107 | } else { |
108 | Join = BasicBlock::Create(Context&: M->getContext(), Name: "strlen.join" , |
109 | Parent: Prev->getParent()); |
110 | } |
111 | BasicBlock *While = |
112 | BasicBlock::Create(Context&: M->getContext(), Name: "strlen.while" , |
113 | Parent: Prev->getParent(), InsertBefore: Join); |
114 | BasicBlock *WhileDone = BasicBlock::Create( |
115 | Context&: M->getContext(), Name: "strlen.while.done" , |
116 | Parent: Prev->getParent(), InsertBefore: Join); |
117 | |
118 | // Emit an early return for when the pointer is null. |
119 | Builder.SetInsertPoint(Prev); |
120 | auto CmpNull = |
121 | Builder.CreateICmpEQ(LHS: Str, RHS: Constant::getNullValue(Ty: Str->getType())); |
122 | BranchInst::Create(IfTrue: Join, IfFalse: While, Cond: CmpNull, InsertBefore: Prev); |
123 | |
124 | // Entry to the while loop. |
125 | Builder.SetInsertPoint(While); |
126 | |
127 | auto PtrPhi = Builder.CreatePHI(Ty: Str->getType(), NumReservedValues: 2); |
128 | PtrPhi->addIncoming(V: Str, BB: Prev); |
129 | auto PtrNext = Builder.CreateGEP(Ty: Builder.getInt8Ty(), Ptr: PtrPhi, IdxList: One); |
130 | PtrPhi->addIncoming(V: PtrNext, BB: While); |
131 | |
132 | // Condition for the while loop. |
133 | auto Data = Builder.CreateLoad(Ty: Builder.getInt8Ty(), Ptr: PtrPhi); |
134 | auto Cmp = Builder.CreateICmpEQ(LHS: Data, RHS: CharZero); |
135 | Builder.CreateCondBr(Cond: Cmp, True: WhileDone, False: While); |
136 | |
137 | // Add one to the computed length. |
138 | Builder.SetInsertPoint(TheBB: WhileDone, IP: WhileDone->begin()); |
139 | auto Begin = Builder.CreatePtrToInt(V: Str, DestTy: Int64Ty); |
140 | auto End = Builder.CreatePtrToInt(V: PtrPhi, DestTy: Int64Ty); |
141 | auto Len = Builder.CreateSub(LHS: End, RHS: Begin); |
142 | Len = Builder.CreateAdd(LHS: Len, RHS: One); |
143 | |
144 | // Final join. |
145 | BranchInst::Create(IfTrue: Join, InsertBefore: WhileDone); |
146 | Builder.SetInsertPoint(TheBB: Join, IP: Join->begin()); |
147 | auto LenPhi = Builder.CreatePHI(Ty: Len->getType(), NumReservedValues: 2); |
148 | LenPhi->addIncoming(V: Len, BB: WhileDone); |
149 | LenPhi->addIncoming(V: Zero, BB: Prev); |
150 | |
151 | return LenPhi; |
152 | } |
153 | |
154 | static Value *callAppendStringN(IRBuilder<> &Builder, Value *Desc, Value *Str, |
155 | Value *Length, bool isLast) { |
156 | auto Int64Ty = Builder.getInt64Ty(); |
157 | auto IsLastInt32 = Builder.getInt32(C: isLast); |
158 | auto M = Builder.GetInsertBlock()->getModule(); |
159 | auto Fn = M->getOrInsertFunction(Name: "__ockl_printf_append_string_n" , RetTy: Int64Ty, |
160 | Args: Desc->getType(), Args: Str->getType(), |
161 | Args: Length->getType(), Args: IsLastInt32->getType()); |
162 | return Builder.CreateCall(Callee: Fn, Args: {Desc, Str, Length, IsLastInt32}); |
163 | } |
164 | |
165 | static Value *appendString(IRBuilder<> &Builder, Value *Desc, Value *Arg, |
166 | bool IsLast) { |
167 | auto Length = getStrlenWithNull(Builder, Str: Arg); |
168 | return callAppendStringN(Builder, Desc, Str: Arg, Length, isLast: IsLast); |
169 | } |
170 | |
171 | static Value *processArg(IRBuilder<> &Builder, Value *Desc, Value *Arg, |
172 | bool SpecIsCString, bool IsLast) { |
173 | if (SpecIsCString && isa<PointerType>(Val: Arg->getType())) { |
174 | return appendString(Builder, Desc, Arg, IsLast); |
175 | } |
176 | // If the format specifies a string but the argument is not, the frontend will |
177 | // have printed a warning. We just rely on undefined behaviour and send the |
178 | // argument anyway. |
179 | return appendArg(Builder, Desc, Arg, IsLast); |
180 | } |
181 | |
182 | // Scan the format string to locate all specifiers, and mark the ones that |
183 | // specify a string, i.e, the "%s" specifier with optional '*' characters. |
184 | static void locateCStrings(SparseBitVector<8> &BV, StringRef Str) { |
185 | static const char ConvSpecifiers[] = "diouxXfFeEgGaAcspn" ; |
186 | size_t SpecPos = 0; |
187 | // Skip the first argument, the format string. |
188 | unsigned ArgIdx = 1; |
189 | |
190 | while ((SpecPos = Str.find_first_of(C: '%', From: SpecPos)) != StringRef::npos) { |
191 | if (Str[SpecPos + 1] == '%') { |
192 | SpecPos += 2; |
193 | continue; |
194 | } |
195 | auto SpecEnd = Str.find_first_of(Chars: ConvSpecifiers, From: SpecPos); |
196 | if (SpecEnd == StringRef::npos) |
197 | return; |
198 | auto Spec = Str.slice(Start: SpecPos, End: SpecEnd + 1); |
199 | ArgIdx += Spec.count(C: '*'); |
200 | if (Str[SpecEnd] == 's') { |
201 | BV.set(ArgIdx); |
202 | } |
203 | SpecPos = SpecEnd + 1; |
204 | ++ArgIdx; |
205 | } |
206 | } |
207 | |
208 | // helper struct to package the string related data |
209 | struct StringData { |
210 | StringRef Str; |
211 | Value *RealSize = nullptr; |
212 | Value *AlignedSize = nullptr; |
213 | bool IsConst = true; |
214 | |
215 | StringData(StringRef ST, Value *RS, Value *AS, bool IC) |
216 | : Str(ST), RealSize(RS), AlignedSize(AS), IsConst(IC) {} |
217 | }; |
218 | |
219 | // Calculates frame size required for current printf expansion and allocates |
220 | // space on printf buffer. Printf frame includes following contents |
221 | // [ ControlDWord , format string/Hash , Arguments (each aligned to 8 byte) ] |
222 | static Value *callBufferedPrintfStart( |
223 | IRBuilder<> &Builder, ArrayRef<Value *> Args, Value *Fmt, |
224 | bool isConstFmtStr, SparseBitVector<8> &SpecIsCString, |
225 | SmallVectorImpl<StringData> &StringContents, Value *&ArgSize) { |
226 | Module *M = Builder.GetInsertBlock()->getModule(); |
227 | Value *NonConstStrLen = nullptr; |
228 | Value *LenWithNull = nullptr; |
229 | Value *LenWithNullAligned = nullptr; |
230 | Value *TempAdd = nullptr; |
231 | |
232 | // First 4 bytes to be reserved for control dword |
233 | size_t BufSize = 4; |
234 | if (isConstFmtStr) |
235 | // First 8 bytes of MD5 hash |
236 | BufSize += 8; |
237 | else { |
238 | LenWithNull = getStrlenWithNull(Builder, Str: Fmt); |
239 | |
240 | // Align the computed length to next 8 byte boundary |
241 | TempAdd = Builder.CreateAdd(LHS: LenWithNull, |
242 | RHS: ConstantInt::get(Ty: LenWithNull->getType(), V: 7U)); |
243 | NonConstStrLen = Builder.CreateAnd( |
244 | LHS: TempAdd, RHS: ConstantInt::get(Ty: LenWithNull->getType(), V: ~7U)); |
245 | |
246 | StringContents.push_back( |
247 | Elt: StringData(StringRef(), LenWithNull, NonConstStrLen, false)); |
248 | } |
249 | |
250 | for (size_t i = 1; i < Args.size(); i++) { |
251 | if (SpecIsCString.test(Idx: i)) { |
252 | StringRef ArgStr; |
253 | if (getConstantStringInfo(V: Args[i], Str&: ArgStr)) { |
254 | auto alignedLen = alignTo(Value: ArgStr.size() + 1, Align: 8); |
255 | StringContents.push_back(Elt: StringData( |
256 | ArgStr, |
257 | /*RealSize*/ nullptr, /*AlignedSize*/ nullptr, /*IsConst*/ true)); |
258 | BufSize += alignedLen; |
259 | } else { |
260 | LenWithNull = getStrlenWithNull(Builder, Str: Args[i]); |
261 | |
262 | // Align the computed length to next 8 byte boundary |
263 | TempAdd = Builder.CreateAdd( |
264 | LHS: LenWithNull, RHS: ConstantInt::get(Ty: LenWithNull->getType(), V: 7U)); |
265 | LenWithNullAligned = Builder.CreateAnd( |
266 | LHS: TempAdd, RHS: ConstantInt::get(Ty: LenWithNull->getType(), V: ~7U)); |
267 | |
268 | if (NonConstStrLen) { |
269 | auto Val = Builder.CreateAdd(LHS: LenWithNullAligned, RHS: NonConstStrLen, |
270 | Name: "cumulativeAdd" ); |
271 | NonConstStrLen = Val; |
272 | } else |
273 | NonConstStrLen = LenWithNullAligned; |
274 | |
275 | StringContents.push_back( |
276 | Elt: StringData(StringRef(), LenWithNull, LenWithNullAligned, false)); |
277 | } |
278 | } else { |
279 | int AllocSize = M->getDataLayout().getTypeAllocSize(Ty: Args[i]->getType()); |
280 | // We end up expanding non string arguments to 8 bytes |
281 | // (args smaller than 8 bytes) |
282 | BufSize += std::max(a: AllocSize, b: 8); |
283 | } |
284 | } |
285 | |
286 | // calculate final size value to be passed to printf_alloc |
287 | Value *SizeToReserve = ConstantInt::get(Ty: Builder.getInt64Ty(), V: BufSize, IsSigned: false); |
288 | SmallVector<Value *, 1> Alloc_args; |
289 | if (NonConstStrLen) |
290 | SizeToReserve = Builder.CreateAdd(LHS: NonConstStrLen, RHS: SizeToReserve); |
291 | |
292 | ArgSize = Builder.CreateTrunc(V: SizeToReserve, DestTy: Builder.getInt32Ty()); |
293 | Alloc_args.push_back(Elt: ArgSize); |
294 | |
295 | // call the printf_alloc function |
296 | AttributeList Attr = AttributeList::get( |
297 | C&: Builder.getContext(), Index: AttributeList::FunctionIndex, Kinds: Attribute::NoUnwind); |
298 | |
299 | Type *Tys_alloc[1] = {Builder.getInt32Ty()}; |
300 | Type *PtrTy = |
301 | Builder.getPtrTy(AddrSpace: M->getDataLayout().getDefaultGlobalsAddressSpace()); |
302 | FunctionType *FTy_alloc = FunctionType::get(Result: PtrTy, Params: Tys_alloc, isVarArg: false); |
303 | auto PrintfAllocFn = |
304 | M->getOrInsertFunction(Name: StringRef("__printf_alloc" ), T: FTy_alloc, AttributeList: Attr); |
305 | |
306 | return Builder.CreateCall(Callee: PrintfAllocFn, Args: Alloc_args, Name: "printf_alloc_fn" ); |
307 | } |
308 | |
309 | // Prepare constant string argument to push onto the buffer |
310 | static void processConstantStringArg(StringData *SD, IRBuilder<> &Builder, |
311 | SmallVectorImpl<Value *> &WhatToStore) { |
312 | std::string Str(SD->Str.str() + '\0'); |
313 | |
314 | DataExtractor (Str, /*IsLittleEndian=*/true, 8); |
315 | DataExtractor::Cursor Offset(0); |
316 | while (Offset && Offset.tell() < Str.size()) { |
317 | const uint64_t ReadSize = 4; |
318 | uint64_t ReadNow = std::min(a: ReadSize, b: Str.size() - Offset.tell()); |
319 | uint64_t ReadBytes = 0; |
320 | switch (ReadNow) { |
321 | default: |
322 | llvm_unreachable("min(4, X) > 4?" ); |
323 | case 1: |
324 | ReadBytes = Extractor.getU8(C&: Offset); |
325 | break; |
326 | case 2: |
327 | ReadBytes = Extractor.getU16(C&: Offset); |
328 | break; |
329 | case 3: |
330 | ReadBytes = Extractor.getU24(C&: Offset); |
331 | break; |
332 | case 4: |
333 | ReadBytes = Extractor.getU32(C&: Offset); |
334 | break; |
335 | } |
336 | cantFail(Err: Offset.takeError(), Msg: "failed to read bytes from constant array" ); |
337 | |
338 | APInt IntVal(8 * ReadSize, ReadBytes); |
339 | |
340 | // TODO: Should not bother aligning up. |
341 | if (ReadNow < ReadSize) |
342 | IntVal = IntVal.zext(width: 8 * ReadSize); |
343 | |
344 | Type *IntTy = Type::getIntNTy(C&: Builder.getContext(), N: IntVal.getBitWidth()); |
345 | WhatToStore.push_back(Elt: ConstantInt::get(Ty: IntTy, V: IntVal)); |
346 | } |
347 | // Additional padding for 8 byte alignment |
348 | int Rem = (Str.size() % 8); |
349 | if (Rem > 0 && Rem <= 4) |
350 | WhatToStore.push_back(Elt: ConstantInt::get(Ty: Builder.getInt32Ty(), V: 0)); |
351 | } |
352 | |
353 | static Value *processNonStringArg(Value *Arg, IRBuilder<> &Builder) { |
354 | const DataLayout &DL = Builder.GetInsertBlock()->getDataLayout(); |
355 | auto Ty = Arg->getType(); |
356 | |
357 | if (auto IntTy = dyn_cast<IntegerType>(Val: Ty)) { |
358 | if (IntTy->getBitWidth() < 64) { |
359 | return Builder.CreateZExt(V: Arg, DestTy: Builder.getInt64Ty()); |
360 | } |
361 | } |
362 | |
363 | if (Ty->isFloatingPointTy()) { |
364 | if (DL.getTypeAllocSize(Ty) < 8) { |
365 | return Builder.CreateFPExt(V: Arg, DestTy: Builder.getDoubleTy()); |
366 | } |
367 | } |
368 | |
369 | return Arg; |
370 | } |
371 | |
372 | static void |
373 | callBufferedPrintfArgPush(IRBuilder<> &Builder, ArrayRef<Value *> Args, |
374 | Value *PtrToStore, SparseBitVector<8> &SpecIsCString, |
375 | SmallVectorImpl<StringData> &StringContents, |
376 | bool IsConstFmtStr) { |
377 | Module *M = Builder.GetInsertBlock()->getModule(); |
378 | const DataLayout &DL = M->getDataLayout(); |
379 | auto StrIt = StringContents.begin(); |
380 | size_t i = IsConstFmtStr ? 1 : 0; |
381 | for (; i < Args.size(); i++) { |
382 | SmallVector<Value *, 32> WhatToStore; |
383 | if ((i == 0) || SpecIsCString.test(Idx: i)) { |
384 | if (StrIt->IsConst) { |
385 | processConstantStringArg(SD: StrIt, Builder, WhatToStore); |
386 | StrIt++; |
387 | } else { |
388 | // This copies the contents of the string, however the next offset |
389 | // is at aligned length, the extra space that might be created due |
390 | // to alignment padding is not populated with any specific value |
391 | // here. This would be safe as long as runtime is sync with |
392 | // the offsets. |
393 | Builder.CreateMemCpy(Dst: PtrToStore, /*DstAlign*/ Align(1), Src: Args[i], |
394 | /*SrcAlign*/ Args[i]->getPointerAlignment(DL), |
395 | Size: StrIt->RealSize); |
396 | |
397 | PtrToStore = |
398 | Builder.CreateInBoundsGEP(Ty: Builder.getInt8Ty(), Ptr: PtrToStore, |
399 | IdxList: {StrIt->AlignedSize}, Name: "PrintBuffNextPtr" ); |
400 | LLVM_DEBUG(dbgs() << "inserting gep to the printf buffer:" |
401 | << *PtrToStore << '\n'); |
402 | |
403 | // done with current argument, move to next |
404 | StrIt++; |
405 | continue; |
406 | } |
407 | } else { |
408 | WhatToStore.push_back(Elt: processNonStringArg(Arg: Args[i], Builder)); |
409 | } |
410 | |
411 | for (Value *toStore : WhatToStore) { |
412 | StoreInst *StBuff = Builder.CreateStore(Val: toStore, Ptr: PtrToStore); |
413 | LLVM_DEBUG(dbgs() << "inserting store to printf buffer:" << *StBuff |
414 | << '\n'); |
415 | (void)StBuff; |
416 | PtrToStore = Builder.CreateConstInBoundsGEP1_32( |
417 | Ty: Builder.getInt8Ty(), Ptr: PtrToStore, |
418 | Idx0: M->getDataLayout().getTypeAllocSize(Ty: toStore->getType()), |
419 | Name: "PrintBuffNextPtr" ); |
420 | LLVM_DEBUG(dbgs() << "inserting gep to the printf buffer:" << *PtrToStore |
421 | << '\n'); |
422 | } |
423 | } |
424 | } |
425 | |
426 | Value *llvm::emitAMDGPUPrintfCall(IRBuilder<> &Builder, ArrayRef<Value *> Args, |
427 | bool IsBuffered) { |
428 | auto NumOps = Args.size(); |
429 | assert(NumOps >= 1); |
430 | |
431 | auto Fmt = Args[0]; |
432 | SparseBitVector<8> SpecIsCString; |
433 | StringRef FmtStr; |
434 | |
435 | if (getConstantStringInfo(V: Fmt, Str&: FmtStr)) |
436 | locateCStrings(BV&: SpecIsCString, Str: FmtStr); |
437 | |
438 | if (IsBuffered) { |
439 | SmallVector<StringData, 8> StringContents; |
440 | Module *M = Builder.GetInsertBlock()->getModule(); |
441 | LLVMContext &Ctx = Builder.getContext(); |
442 | auto Int8Ty = Builder.getInt8Ty(); |
443 | auto Int32Ty = Builder.getInt32Ty(); |
444 | bool IsConstFmtStr = !FmtStr.empty(); |
445 | |
446 | Value *ArgSize = nullptr; |
447 | Value *Ptr = |
448 | callBufferedPrintfStart(Builder, Args, Fmt, isConstFmtStr: IsConstFmtStr, |
449 | SpecIsCString, StringContents, ArgSize); |
450 | |
451 | // The buffered version still follows OpenCL printf standards for |
452 | // printf return value, i.e 0 on success, -1 on failure. |
453 | ConstantPointerNull *zeroIntPtr = |
454 | ConstantPointerNull::get(T: cast<PointerType>(Val: Ptr->getType())); |
455 | |
456 | auto *Cmp = cast<ICmpInst>(Val: Builder.CreateICmpNE(LHS: Ptr, RHS: zeroIntPtr, Name: "" )); |
457 | |
458 | BasicBlock *End = BasicBlock::Create(Context&: Ctx, Name: "end.block" , |
459 | Parent: Builder.GetInsertBlock()->getParent()); |
460 | BasicBlock *ArgPush = BasicBlock::Create( |
461 | Context&: Ctx, Name: "argpush.block" , Parent: Builder.GetInsertBlock()->getParent()); |
462 | |
463 | BranchInst::Create(IfTrue: ArgPush, IfFalse: End, Cond: Cmp, InsertBefore: Builder.GetInsertBlock()); |
464 | Builder.SetInsertPoint(ArgPush); |
465 | |
466 | // Create controlDWord and store as the first entry, format as follows |
467 | // Bit 0 (LSB) -> stream (1 if stderr, 0 if stdout, printf always outputs to |
468 | // stdout) Bit 1 -> constant format string (1 if constant) Bits 2-31 -> size |
469 | // of printf data frame |
470 | auto ConstantTwo = Builder.getInt32(C: 2); |
471 | auto ControlDWord = Builder.CreateShl(LHS: ArgSize, RHS: ConstantTwo); |
472 | if (IsConstFmtStr) |
473 | ControlDWord = Builder.CreateOr(LHS: ControlDWord, RHS: ConstantTwo); |
474 | |
475 | Builder.CreateStore(Val: ControlDWord, Ptr); |
476 | |
477 | Ptr = Builder.CreateConstInBoundsGEP1_32(Ty: Int8Ty, Ptr, Idx0: 4); |
478 | |
479 | // Create MD5 hash for costant format string, push low 64 bits of the |
480 | // same onto buffer and metadata. |
481 | NamedMDNode *metaD = M->getOrInsertNamedMetadata(Name: "llvm.printf.fmts" ); |
482 | if (IsConstFmtStr) { |
483 | MD5 Hasher; |
484 | MD5::MD5Result Hash; |
485 | Hasher.update(Str: FmtStr); |
486 | Hasher.final(Result&: Hash); |
487 | |
488 | // Try sticking to llvm.printf.fmts format, although we are not going to |
489 | // use the ID and argument size fields while printing, |
490 | std::string MetadataStr = |
491 | "0:0:" + llvm::utohexstr(X: Hash.low(), /*LowerCase=*/true) + "," + |
492 | FmtStr.str(); |
493 | MDString *fmtStrArray = MDString::get(Context&: Ctx, Str: MetadataStr); |
494 | MDNode *myMD = MDNode::get(Context&: Ctx, MDs: fmtStrArray); |
495 | metaD->addOperand(M: myMD); |
496 | |
497 | Builder.CreateStore(Val: Builder.getInt64(C: Hash.low()), Ptr); |
498 | Ptr = Builder.CreateConstInBoundsGEP1_32(Ty: Int8Ty, Ptr, Idx0: 8); |
499 | } else { |
500 | // Include a dummy metadata instance in case of only non constant |
501 | // format string usage, This might be an absurd usecase but needs to |
502 | // be done for completeness |
503 | if (metaD->getNumOperands() == 0) { |
504 | MDString *fmtStrArray = |
505 | MDString::get(Context&: Ctx, Str: "0:0:ffffffff,\"Non const format string\"" ); |
506 | MDNode *myMD = MDNode::get(Context&: Ctx, MDs: fmtStrArray); |
507 | metaD->addOperand(M: myMD); |
508 | } |
509 | } |
510 | |
511 | // Push The printf arguments onto buffer |
512 | callBufferedPrintfArgPush(Builder, Args, PtrToStore: Ptr, SpecIsCString, StringContents, |
513 | IsConstFmtStr); |
514 | |
515 | // End block, returns -1 on failure |
516 | BranchInst::Create(IfTrue: End, InsertBefore: ArgPush); |
517 | Builder.SetInsertPoint(End); |
518 | return Builder.CreateSExt(V: Builder.CreateNot(V: Cmp), DestTy: Int32Ty, Name: "printf_result" ); |
519 | } |
520 | |
521 | auto Desc = callPrintfBegin(Builder, Version: Builder.getIntN(N: 64, C: 0)); |
522 | Desc = appendString(Builder, Desc, Arg: Fmt, IsLast: NumOps == 1); |
523 | |
524 | // FIXME: This invokes hostcall once for each argument. We can pack up to |
525 | // seven scalar printf arguments in a single hostcall. See the signature of |
526 | // callAppendArgs(). |
527 | for (unsigned int i = 1; i != NumOps; ++i) { |
528 | bool IsLast = i == NumOps - 1; |
529 | bool IsCString = SpecIsCString.test(Idx: i); |
530 | Desc = processArg(Builder, Desc, Arg: Args[i], SpecIsCString: IsCString, IsLast); |
531 | } |
532 | |
533 | return Builder.CreateTrunc(V: Desc, DestTy: Builder.getInt32Ty()); |
534 | } |
535 | |