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
26using namespace llvm;
27
28#define DEBUG_TYPE "amdgpu-emit-printf"
29
30static 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
54static Value *callPrintfBegin(IRBuilder<> &Builder, Value *Version) {
55 auto Int64Ty = Builder.getInt64Ty();
56 auto M = Builder.getModule();
57 auto Fn = M->getOrInsertFunction(Name: "__ockl_printf_begin", RetTy: Int64Ty, Args: Int64Ty);
58 return Builder.CreateCall(Callee: Fn, Args: Version);
59}
60
61static 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.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
77static 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.
87static 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->hasTerminator()) {
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 Builder.CreateCondBr(Cond: CmpNull, True: Join, False: While);
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(WhileDone->begin());
139 auto Len = Builder.CreatePtrDiff(LHS: PtrPhi, RHS: Str);
140 Len = Builder.CreateZExt(V: Len, DestTy: Int64Ty);
141 Len = Builder.CreateAdd(LHS: Len, RHS: One);
142
143 // Final join.
144 UncondBrInst::Create(Target: Join, InsertBefore: WhileDone);
145 Builder.SetInsertPoint(Join->begin());
146 auto LenPhi = Builder.CreatePHI(Ty: Len->getType(), NumReservedValues: 2);
147 LenPhi->addIncoming(V: Len, BB: WhileDone);
148 LenPhi->addIncoming(V: Zero, BB: Prev);
149
150 return LenPhi;
151}
152
153static Value *callAppendStringN(IRBuilder<> &Builder, Value *Desc, Value *Str,
154 Value *Length, bool isLast) {
155 auto Int64Ty = Builder.getInt64Ty();
156 auto IsLastInt32 = Builder.getInt32(C: isLast);
157 auto M = Builder.getModule();
158 auto Fn = M->getOrInsertFunction(Name: "__ockl_printf_append_string_n", RetTy: Int64Ty,
159 Args: Desc->getType(), Args: Str->getType(),
160 Args: Length->getType(), Args: IsLastInt32->getType());
161 return Builder.CreateCall(Callee: Fn, Args: {Desc, Str, Length, IsLastInt32});
162}
163
164static Value *appendString(IRBuilder<> &Builder, Value *Desc, Value *Arg,
165 bool IsLast) {
166 auto Length = getStrlenWithNull(Builder, Str: Arg);
167 return callAppendStringN(Builder, Desc, Str: Arg, Length, isLast: IsLast);
168}
169
170static Value *processArg(IRBuilder<> &Builder, Value *Desc, Value *Arg,
171 bool SpecIsCString, bool IsLast) {
172 if (SpecIsCString && isa<PointerType>(Val: Arg->getType())) {
173 return appendString(Builder, Desc, Arg, IsLast);
174 }
175 // If the format specifies a string but the argument is not, the frontend will
176 // have printed a warning. We just rely on undefined behaviour and send the
177 // argument anyway.
178 return appendArg(Builder, Desc, Arg, IsLast);
179}
180
181// Scan the format string to locate all specifiers, and mark the ones that
182// specify a string, i.e, the "%s" specifier with optional '*' characters.
183static void locateCStrings(SparseBitVector<8> &BV, StringRef Str) {
184 static const char ConvSpecifiers[] = "diouxXfFeEgGaAcspn";
185 // Skip the first argument, the format string.
186 unsigned ArgIdx = 1;
187
188 size_t SpecPos;
189 while ((SpecPos = Str.find(C: '%')) != StringRef::npos) {
190 Str = Str.drop_front(N: SpecPos + 1);
191 // "%%" prints a '%' and takes no argument.
192 if (Str.consume_front(Prefix: "%"))
193 continue;
194 size_t SpecEnd = Str.find_first_of(Chars: ConvSpecifiers);
195 if (SpecEnd == StringRef::npos)
196 return;
197 // Each '*' in the specifier takes an argument of its own.
198 ArgIdx += Str.take_front(N: SpecEnd).count(C: '*');
199 if (Str[SpecEnd] == 's')
200 BV.set(ArgIdx);
201 Str = Str.drop_front(N: SpecEnd + 1);
202 ++ArgIdx;
203 }
204}
205
206// helper struct to package the string related data
207struct StringData {
208 StringRef Str;
209 Value *RealSize = nullptr;
210 Value *AlignedSize = nullptr;
211 bool IsConst = true;
212
213 StringData(StringRef ST, Value *RS, Value *AS, bool IC)
214 : Str(ST), RealSize(RS), AlignedSize(AS), IsConst(IC) {}
215};
216
217// Calculates frame size required for current printf expansion and allocates
218// space on printf buffer. Printf frame includes following contents
219// [ ControlDWord , format string/Hash , Arguments (each aligned to 8 byte) ]
220static Value *callBufferedPrintfStart(
221 IRBuilder<> &Builder, ArrayRef<Value *> Args, Value *Fmt,
222 bool isConstFmtStr, SparseBitVector<8> &SpecIsCString,
223 SmallVectorImpl<StringData> &StringContents, Value *&ArgSize) {
224 Module *M = Builder.getModule();
225 Value *NonConstStrLen = nullptr;
226 Value *LenWithNull = nullptr;
227 Value *LenWithNullAligned = nullptr;
228 Value *TempAdd = nullptr;
229
230 // First 4 bytes to be reserved for control dword
231 size_t BufSize = 4;
232 if (isConstFmtStr)
233 // First 8 bytes of MD5 hash
234 BufSize += 8;
235 else {
236 LenWithNull = getStrlenWithNull(Builder, Str: Fmt);
237
238 // Align the computed length to next 8 byte boundary
239 TempAdd = Builder.CreateAdd(LHS: LenWithNull,
240 RHS: ConstantInt::get(Ty: LenWithNull->getType(), V: 7U));
241 NonConstStrLen = Builder.CreateAnd(
242 LHS: TempAdd, RHS: ConstantInt::get(Ty: LenWithNull->getType(), V: ~7U));
243
244 StringContents.push_back(
245 Elt: StringData(StringRef(), LenWithNull, NonConstStrLen, false));
246 }
247
248 for (size_t i = 1; i < Args.size(); i++) {
249 if (SpecIsCString.test(Idx: i)) {
250 StringRef ArgStr;
251 if (getConstantStringInfo(V: Args[i], Str&: ArgStr)) {
252 auto alignedLen = alignTo(Value: ArgStr.size() + 1, Align: 8);
253 StringContents.push_back(Elt: StringData(
254 ArgStr,
255 /*RealSize*/ nullptr, /*AlignedSize*/ nullptr, /*IsConst*/ true));
256 BufSize += alignedLen;
257 } else {
258 LenWithNull = getStrlenWithNull(Builder, Str: Args[i]);
259
260 // Align the computed length to next 8 byte boundary
261 TempAdd = Builder.CreateAdd(
262 LHS: LenWithNull, RHS: ConstantInt::get(Ty: LenWithNull->getType(), V: 7U));
263 LenWithNullAligned = Builder.CreateAnd(
264 LHS: TempAdd, RHS: ConstantInt::get(Ty: LenWithNull->getType(), V: ~7U));
265
266 if (NonConstStrLen) {
267 auto Val = Builder.CreateAdd(LHS: LenWithNullAligned, RHS: NonConstStrLen,
268 Name: "cumulativeAdd");
269 NonConstStrLen = Val;
270 } else
271 NonConstStrLen = LenWithNullAligned;
272
273 StringContents.push_back(
274 Elt: StringData(StringRef(), LenWithNull, LenWithNullAligned, false));
275 }
276 } else {
277 int AllocSize = M->getDataLayout().getTypeAllocSize(Ty: Args[i]->getType());
278 // We end up expanding non string arguments to 8 bytes
279 // (args smaller than 8 bytes)
280 BufSize += std::max(a: AllocSize, b: 8);
281 }
282 }
283
284 // calculate final size value to be passed to printf_alloc
285 Value *SizeToReserve = ConstantInt::get(Ty: Builder.getInt64Ty(), V: BufSize, IsSigned: false);
286 SmallVector<Value *, 1> Alloc_args;
287 if (NonConstStrLen)
288 SizeToReserve = Builder.CreateAdd(LHS: NonConstStrLen, RHS: SizeToReserve);
289
290 ArgSize = Builder.CreateTrunc(V: SizeToReserve, DestTy: Builder.getInt32Ty());
291 Alloc_args.push_back(Elt: ArgSize);
292
293 // call the printf_alloc function
294 AttributeList Attr = AttributeList::get(
295 C&: Builder.getContext(), Index: AttributeList::FunctionIndex, Kinds: Attribute::NoUnwind);
296
297 Type *Tys_alloc[1] = {Builder.getInt32Ty()};
298 Type *PtrTy =
299 Builder.getPtrTy(AddrSpace: M->getDataLayout().getDefaultGlobalsAddressSpace());
300 FunctionType *FTy_alloc = FunctionType::get(Result: PtrTy, Params: Tys_alloc, isVarArg: false);
301 auto PrintfAllocFn =
302 M->getOrInsertFunction(Name: StringRef("__printf_alloc"), T: FTy_alloc, AttributeList: Attr);
303
304 return Builder.CreateCall(Callee: PrintfAllocFn, Args: Alloc_args, Name: "printf_alloc_fn");
305}
306
307// Prepare constant string argument to push onto the buffer
308static void processConstantStringArg(StringData *SD, IRBuilder<> &Builder,
309 SmallVectorImpl<Value *> &WhatToStore) {
310 std::string Str(SD->Str.str() + '\0');
311
312 DataExtractor Extractor(Str, /*IsLittleEndian=*/true);
313 DataExtractor::Cursor Offset(0);
314 while (Offset && Offset.tell() < Str.size()) {
315 const uint64_t ReadSize = 4;
316 uint64_t ReadNow = std::min(a: ReadSize, b: Str.size() - Offset.tell());
317 uint64_t ReadBytes = 0;
318 switch (ReadNow) {
319 default:
320 llvm_unreachable("min(4, X) > 4?");
321 case 1:
322 ReadBytes = Extractor.getU8(C&: Offset);
323 break;
324 case 2:
325 ReadBytes = Extractor.getU16(C&: Offset);
326 break;
327 case 3:
328 ReadBytes = Extractor.getU24(C&: Offset);
329 break;
330 case 4:
331 ReadBytes = Extractor.getU32(C&: Offset);
332 break;
333 }
334 cantFail(Err: Offset.takeError(), Msg: "failed to read bytes from constant array");
335
336 APInt IntVal(8 * ReadSize, ReadBytes);
337
338 // TODO: Should not bother aligning up.
339 if (ReadNow < ReadSize)
340 IntVal = IntVal.zext(width: 8 * ReadSize);
341
342 Type *IntTy = Type::getIntNTy(C&: Builder.getContext(), N: IntVal.getBitWidth());
343 WhatToStore.push_back(Elt: ConstantInt::get(Ty: IntTy, V: IntVal));
344 }
345 // Additional padding for 8 byte alignment
346 int Rem = (Str.size() % 8);
347 if (Rem > 0 && Rem <= 4)
348 WhatToStore.push_back(Elt: ConstantInt::get(Ty: Builder.getInt32Ty(), V: 0));
349}
350
351static Value *processNonStringArg(Value *Arg, IRBuilder<> &Builder) {
352 const DataLayout &DL = Builder.getDataLayout();
353 auto Ty = Arg->getType();
354
355 if (auto IntTy = dyn_cast<IntegerType>(Val: Ty)) {
356 if (IntTy->getBitWidth() < 64) {
357 return Builder.CreateZExt(V: Arg, DestTy: Builder.getInt64Ty());
358 }
359 }
360
361 if (Ty->isFloatingPointTy()) {
362 if (DL.getTypeAllocSize(Ty) < 8) {
363 return Builder.CreateFPExt(V: Arg, DestTy: Builder.getDoubleTy());
364 }
365 }
366
367 return Arg;
368}
369
370static void
371callBufferedPrintfArgPush(IRBuilder<> &Builder, ArrayRef<Value *> Args,
372 Value *PtrToStore, SparseBitVector<8> &SpecIsCString,
373 SmallVectorImpl<StringData> &StringContents,
374 bool IsConstFmtStr) {
375 Module *M = Builder.getModule();
376 const DataLayout &DL = M->getDataLayout();
377 auto StrIt = StringContents.begin();
378 size_t i = IsConstFmtStr ? 1 : 0;
379 for (; i < Args.size(); i++) {
380 SmallVector<Value *, 32> WhatToStore;
381 if ((i == 0) || SpecIsCString.test(Idx: i)) {
382 if (StrIt->IsConst) {
383 processConstantStringArg(SD: StrIt, Builder, WhatToStore);
384 StrIt++;
385 } else {
386 // This copies the contents of the string, however the next offset
387 // is at aligned length, the extra space that might be created due
388 // to alignment padding is not populated with any specific value
389 // here. This would be safe as long as runtime is sync with
390 // the offsets.
391 Builder.CreateMemCpy(Dst: PtrToStore, /*DstAlign*/ Align(1), Src: Args[i],
392 /*SrcAlign*/ Args[i]->getPointerAlignment(DL),
393 Size: StrIt->RealSize);
394
395 PtrToStore =
396 Builder.CreateInBoundsGEP(Ty: Builder.getInt8Ty(), Ptr: PtrToStore,
397 IdxList: {StrIt->AlignedSize}, Name: "PrintBuffNextPtr");
398 LLVM_DEBUG(dbgs() << "inserting gep to the printf buffer:"
399 << *PtrToStore << '\n');
400
401 // done with current argument, move to next
402 StrIt++;
403 continue;
404 }
405 } else {
406 WhatToStore.push_back(Elt: processNonStringArg(Arg: Args[i], Builder));
407 }
408
409 for (Value *toStore : WhatToStore) {
410 StoreInst *StBuff = Builder.CreateStore(Val: toStore, Ptr: PtrToStore);
411 LLVM_DEBUG(dbgs() << "inserting store to printf buffer:" << *StBuff
412 << '\n');
413 (void)StBuff;
414 PtrToStore = Builder.CreateConstInBoundsGEP1_32(
415 Ty: Builder.getInt8Ty(), Ptr: PtrToStore,
416 Idx0: M->getDataLayout().getTypeAllocSize(Ty: toStore->getType()),
417 Name: "PrintBuffNextPtr");
418 LLVM_DEBUG(dbgs() << "inserting gep to the printf buffer:" << *PtrToStore
419 << '\n');
420 }
421 }
422}
423
424Value *llvm::emitAMDGPUPrintfCall(IRBuilder<> &Builder, ArrayRef<Value *> Args,
425 bool IsBuffered) {
426 auto NumOps = Args.size();
427 assert(NumOps >= 1);
428
429 auto Fmt = Args[0];
430 SparseBitVector<8> SpecIsCString;
431 StringRef FmtStr;
432
433 if (getConstantStringInfo(V: Fmt, Str&: FmtStr))
434 locateCStrings(BV&: SpecIsCString, Str: FmtStr);
435
436 if (IsBuffered) {
437 SmallVector<StringData, 8> StringContents;
438 Module *M = Builder.getModule();
439 LLVMContext &Ctx = Builder.getContext();
440 auto Int8Ty = Builder.getInt8Ty();
441 auto Int32Ty = Builder.getInt32Ty();
442 bool IsConstFmtStr = !FmtStr.empty();
443
444 Value *ArgSize = nullptr;
445 Value *Ptr =
446 callBufferedPrintfStart(Builder, Args, Fmt, isConstFmtStr: IsConstFmtStr,
447 SpecIsCString, StringContents, ArgSize);
448
449 // The buffered version still follows OpenCL printf standards for
450 // printf return value, i.e 0 on success, -1 on failure.
451 ConstantPointerNull *zeroIntPtr =
452 ConstantPointerNull::get(T: cast<PointerType>(Val: Ptr->getType()));
453
454 auto *Cmp = cast<ICmpInst>(Val: Builder.CreateICmpNE(LHS: Ptr, RHS: zeroIntPtr, Name: ""));
455
456 BasicBlock *End = BasicBlock::Create(Context&: Ctx, Name: "end.block",
457 Parent: Builder.GetInsertBlock()->getParent());
458 BasicBlock *ArgPush = BasicBlock::Create(
459 Context&: Ctx, Name: "argpush.block", Parent: Builder.GetInsertBlock()->getParent());
460
461 CondBrInst::Create(Cond: Cmp, IfTrue: ArgPush, IfFalse: End, InsertBefore: Builder.GetInsertBlock());
462 Builder.SetInsertPoint(ArgPush);
463
464 // Create controlDWord and store as the first entry, format as follows
465 // Bit 0 (LSB) -> stream (1 if stderr, 0 if stdout, printf always outputs to
466 // stdout) Bit 1 -> constant format string (1 if constant) Bits 2-31 -> size
467 // of printf data frame
468 auto ConstantTwo = Builder.getInt32(C: 2);
469 auto ControlDWord = Builder.CreateShl(LHS: ArgSize, RHS: ConstantTwo);
470 if (IsConstFmtStr)
471 ControlDWord = Builder.CreateOr(LHS: ControlDWord, RHS: ConstantTwo);
472
473 Builder.CreateStore(Val: ControlDWord, Ptr);
474
475 Ptr = Builder.CreateConstInBoundsGEP1_32(Ty: Int8Ty, Ptr, Idx0: 4);
476
477 // Create MD5 hash for constant format string, push low 64 bits of the
478 // same onto buffer and metadata.
479 NamedMDNode *metaD = M->getOrInsertNamedMetadata(Name: "llvm.printf.fmts");
480 if (IsConstFmtStr) {
481 MD5 Hasher;
482 MD5::MD5Result Hash;
483 Hasher.update(Str: FmtStr);
484 Hasher.final(Result&: Hash);
485
486 // Try sticking to llvm.printf.fmts format, although we are not going to
487 // use the ID and argument size fields while printing,
488 std::string MetadataStr =
489 "0:0:" + llvm::utohexstr(X: Hash.low(), /*LowerCase=*/true) + "," +
490 FmtStr.str();
491 MDString *fmtStrArray = MDString::get(Context&: Ctx, Str: MetadataStr);
492 MDNode *myMD = MDNode::get(Context&: Ctx, MDs: fmtStrArray);
493 metaD->addOperand(M: myMD);
494
495 Builder.CreateStore(Val: Builder.getInt64(C: Hash.low()), Ptr);
496 Ptr = Builder.CreateConstInBoundsGEP1_32(Ty: Int8Ty, Ptr, Idx0: 8);
497 } else {
498 // Include a dummy metadata instance in case of only non constant
499 // format string usage, This might be an absurd usecase but needs to
500 // be done for completeness
501 if (metaD->getNumOperands() == 0) {
502 MDString *fmtStrArray =
503 MDString::get(Context&: Ctx, Str: "0:0:ffffffff,\"Non const format string\"");
504 MDNode *myMD = MDNode::get(Context&: Ctx, MDs: fmtStrArray);
505 metaD->addOperand(M: myMD);
506 }
507 }
508
509 // Push The printf arguments onto buffer
510 callBufferedPrintfArgPush(Builder, Args, PtrToStore: Ptr, SpecIsCString, StringContents,
511 IsConstFmtStr);
512
513 // End block, returns -1 on failure
514 UncondBrInst::Create(Target: End, InsertBefore: ArgPush);
515 Builder.SetInsertPoint(End);
516 return Builder.CreateSExt(V: Builder.CreateNot(V: Cmp), DestTy: Int32Ty, Name: "printf_result");
517 }
518
519 auto Desc = callPrintfBegin(Builder, Version: Builder.getIntN(N: 64, C: 0));
520 Desc = appendString(Builder, Desc, Arg: Fmt, IsLast: NumOps == 1);
521
522 // FIXME: This invokes hostcall once for each argument. We can pack up to
523 // seven scalar printf arguments in a single hostcall. See the signature of
524 // callAppendArgs().
525 for (unsigned int i = 1; i != NumOps; ++i) {
526 bool IsLast = i == NumOps - 1;
527 bool IsCString = SpecIsCString.test(Idx: i);
528 Desc = processArg(Builder, Desc, Arg: Args[i], SpecIsCString: IsCString, IsLast);
529 }
530
531 return Builder.CreateTrunc(V: Desc, DestTy: Builder.getInt32Ty());
532}
533