15ffd83dbSDimitry Andric //===- AMDGPUEmitPrintf.cpp -----------------------------------------------===//
25ffd83dbSDimitry Andric //
35ffd83dbSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
45ffd83dbSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
55ffd83dbSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
65ffd83dbSDimitry Andric //
75ffd83dbSDimitry Andric //===----------------------------------------------------------------------===//
85ffd83dbSDimitry Andric //
95ffd83dbSDimitry Andric // Utility function to lower a printf call into a series of device
105ffd83dbSDimitry Andric // library calls on the AMDGPU target.
115ffd83dbSDimitry Andric //
125ffd83dbSDimitry Andric // WARNING: This file knows about certain library functions. It recognizes them
135ffd83dbSDimitry Andric // by name, and hardwires knowledge of their semantics.
145ffd83dbSDimitry Andric //
155ffd83dbSDimitry Andric //===----------------------------------------------------------------------===//
165ffd83dbSDimitry Andric 
175ffd83dbSDimitry Andric #include "llvm/Transforms/Utils/AMDGPUEmitPrintf.h"
185ffd83dbSDimitry Andric #include "llvm/ADT/SparseBitVector.h"
1906c3fb27SDimitry Andric #include "llvm/ADT/StringExtras.h"
205ffd83dbSDimitry Andric #include "llvm/Analysis/ValueTracking.h"
2106c3fb27SDimitry Andric #include "llvm/Support/DataExtractor.h"
2206c3fb27SDimitry Andric #include "llvm/Support/MD5.h"
2306c3fb27SDimitry Andric #include "llvm/Support/MathExtras.h"
245ffd83dbSDimitry Andric 
255ffd83dbSDimitry Andric using namespace llvm;
265ffd83dbSDimitry Andric 
275ffd83dbSDimitry Andric #define DEBUG_TYPE "amdgpu-emit-printf"
285ffd83dbSDimitry Andric 
fitArgInto64Bits(IRBuilder<> & Builder,Value * Arg)295ffd83dbSDimitry Andric static Value *fitArgInto64Bits(IRBuilder<> &Builder, Value *Arg) {
305ffd83dbSDimitry Andric   auto Int64Ty = Builder.getInt64Ty();
315ffd83dbSDimitry Andric   auto Ty = Arg->getType();
325ffd83dbSDimitry Andric 
335ffd83dbSDimitry Andric   if (auto IntTy = dyn_cast<IntegerType>(Ty)) {
345ffd83dbSDimitry Andric     switch (IntTy->getBitWidth()) {
355ffd83dbSDimitry Andric     case 32:
365ffd83dbSDimitry Andric       return Builder.CreateZExt(Arg, Int64Ty);
375ffd83dbSDimitry Andric     case 64:
385ffd83dbSDimitry Andric       return Arg;
395ffd83dbSDimitry Andric     }
405ffd83dbSDimitry Andric   }
415ffd83dbSDimitry Andric 
425ffd83dbSDimitry Andric   if (Ty->getTypeID() == Type::DoubleTyID) {
435ffd83dbSDimitry Andric     return Builder.CreateBitCast(Arg, Int64Ty);
445ffd83dbSDimitry Andric   }
455ffd83dbSDimitry Andric 
465ffd83dbSDimitry Andric   if (isa<PointerType>(Ty)) {
475ffd83dbSDimitry Andric     return Builder.CreatePtrToInt(Arg, Int64Ty);
485ffd83dbSDimitry Andric   }
495ffd83dbSDimitry Andric 
505ffd83dbSDimitry Andric   llvm_unreachable("unexpected type");
515ffd83dbSDimitry Andric }
525ffd83dbSDimitry Andric 
callPrintfBegin(IRBuilder<> & Builder,Value * Version)535ffd83dbSDimitry Andric static Value *callPrintfBegin(IRBuilder<> &Builder, Value *Version) {
545ffd83dbSDimitry Andric   auto Int64Ty = Builder.getInt64Ty();
555ffd83dbSDimitry Andric   auto M = Builder.GetInsertBlock()->getModule();
565ffd83dbSDimitry Andric   auto Fn = M->getOrInsertFunction("__ockl_printf_begin", Int64Ty, Int64Ty);
575ffd83dbSDimitry Andric   return Builder.CreateCall(Fn, Version);
585ffd83dbSDimitry Andric }
595ffd83dbSDimitry Andric 
callAppendArgs(IRBuilder<> & Builder,Value * Desc,int NumArgs,Value * Arg0,Value * Arg1,Value * Arg2,Value * Arg3,Value * Arg4,Value * Arg5,Value * Arg6,bool IsLast)605ffd83dbSDimitry Andric static Value *callAppendArgs(IRBuilder<> &Builder, Value *Desc, int NumArgs,
615ffd83dbSDimitry Andric                              Value *Arg0, Value *Arg1, Value *Arg2, Value *Arg3,
625ffd83dbSDimitry Andric                              Value *Arg4, Value *Arg5, Value *Arg6,
635ffd83dbSDimitry Andric                              bool IsLast) {
645ffd83dbSDimitry Andric   auto Int64Ty = Builder.getInt64Ty();
655ffd83dbSDimitry Andric   auto Int32Ty = Builder.getInt32Ty();
665ffd83dbSDimitry Andric   auto M = Builder.GetInsertBlock()->getModule();
675ffd83dbSDimitry Andric   auto Fn = M->getOrInsertFunction("__ockl_printf_append_args", Int64Ty,
685ffd83dbSDimitry Andric                                    Int64Ty, Int32Ty, Int64Ty, Int64Ty, Int64Ty,
695ffd83dbSDimitry Andric                                    Int64Ty, Int64Ty, Int64Ty, Int64Ty, Int32Ty);
705ffd83dbSDimitry Andric   auto IsLastValue = Builder.getInt32(IsLast);
715ffd83dbSDimitry Andric   auto NumArgsValue = Builder.getInt32(NumArgs);
725ffd83dbSDimitry Andric   return Builder.CreateCall(Fn, {Desc, NumArgsValue, Arg0, Arg1, Arg2, Arg3,
735ffd83dbSDimitry Andric                                  Arg4, Arg5, Arg6, IsLastValue});
745ffd83dbSDimitry Andric }
755ffd83dbSDimitry Andric 
appendArg(IRBuilder<> & Builder,Value * Desc,Value * Arg,bool IsLast)765ffd83dbSDimitry Andric static Value *appendArg(IRBuilder<> &Builder, Value *Desc, Value *Arg,
775ffd83dbSDimitry Andric                         bool IsLast) {
785ffd83dbSDimitry Andric   auto Arg0 = fitArgInto64Bits(Builder, Arg);
795ffd83dbSDimitry Andric   auto Zero = Builder.getInt64(0);
805ffd83dbSDimitry Andric   return callAppendArgs(Builder, Desc, 1, Arg0, Zero, Zero, Zero, Zero, Zero,
815ffd83dbSDimitry Andric                         Zero, IsLast);
825ffd83dbSDimitry Andric }
835ffd83dbSDimitry Andric 
845ffd83dbSDimitry Andric // The device library does not provide strlen, so we build our own loop
855ffd83dbSDimitry Andric // here. While we are at it, we also include the terminating null in the length.
getStrlenWithNull(IRBuilder<> & Builder,Value * Str)865ffd83dbSDimitry Andric static Value *getStrlenWithNull(IRBuilder<> &Builder, Value *Str) {
875ffd83dbSDimitry Andric   auto *Prev = Builder.GetInsertBlock();
885ffd83dbSDimitry Andric   Module *M = Prev->getModule();
895ffd83dbSDimitry Andric 
905ffd83dbSDimitry Andric   auto CharZero = Builder.getInt8(0);
915ffd83dbSDimitry Andric   auto One = Builder.getInt64(1);
925ffd83dbSDimitry Andric   auto Zero = Builder.getInt64(0);
935ffd83dbSDimitry Andric   auto Int64Ty = Builder.getInt64Ty();
945ffd83dbSDimitry Andric 
955ffd83dbSDimitry Andric   // The length is either zero for a null pointer, or the computed value for an
965ffd83dbSDimitry Andric   // actual string. We need a join block for a phi that represents the final
975ffd83dbSDimitry Andric   // value.
985ffd83dbSDimitry Andric   //
995ffd83dbSDimitry Andric   //  Strictly speaking, the zero does not matter since
1005ffd83dbSDimitry Andric   // __ockl_printf_append_string_n ignores the length if the pointer is null.
1015ffd83dbSDimitry Andric   BasicBlock *Join = nullptr;
1025ffd83dbSDimitry Andric   if (Prev->getTerminator()) {
1035ffd83dbSDimitry Andric     Join = Prev->splitBasicBlock(Builder.GetInsertPoint(),
1045ffd83dbSDimitry Andric                                  "strlen.join");
1055ffd83dbSDimitry Andric     Prev->getTerminator()->eraseFromParent();
1065ffd83dbSDimitry Andric   } else {
1075ffd83dbSDimitry Andric     Join = BasicBlock::Create(M->getContext(), "strlen.join",
1085ffd83dbSDimitry Andric                               Prev->getParent());
1095ffd83dbSDimitry Andric   }
1105ffd83dbSDimitry Andric   BasicBlock *While =
1115ffd83dbSDimitry Andric       BasicBlock::Create(M->getContext(), "strlen.while",
1125ffd83dbSDimitry Andric                          Prev->getParent(), Join);
1135ffd83dbSDimitry Andric   BasicBlock *WhileDone = BasicBlock::Create(
1145ffd83dbSDimitry Andric       M->getContext(), "strlen.while.done",
1155ffd83dbSDimitry Andric       Prev->getParent(), Join);
1165ffd83dbSDimitry Andric 
1175ffd83dbSDimitry Andric   // Emit an early return for when the pointer is null.
1185ffd83dbSDimitry Andric   Builder.SetInsertPoint(Prev);
1195ffd83dbSDimitry Andric   auto CmpNull =
1205ffd83dbSDimitry Andric       Builder.CreateICmpEQ(Str, Constant::getNullValue(Str->getType()));
1215ffd83dbSDimitry Andric   BranchInst::Create(Join, While, CmpNull, Prev);
1225ffd83dbSDimitry Andric 
1235ffd83dbSDimitry Andric   // Entry to the while loop.
1245ffd83dbSDimitry Andric   Builder.SetInsertPoint(While);
1255ffd83dbSDimitry Andric 
1265ffd83dbSDimitry Andric   auto PtrPhi = Builder.CreatePHI(Str->getType(), 2);
1275ffd83dbSDimitry Andric   PtrPhi->addIncoming(Str, Prev);
128fe6060f1SDimitry Andric   auto PtrNext = Builder.CreateGEP(Builder.getInt8Ty(), PtrPhi, One);
1295ffd83dbSDimitry Andric   PtrPhi->addIncoming(PtrNext, While);
1305ffd83dbSDimitry Andric 
1315ffd83dbSDimitry Andric   // Condition for the while loop.
132fe6060f1SDimitry Andric   auto Data = Builder.CreateLoad(Builder.getInt8Ty(), PtrPhi);
1335ffd83dbSDimitry Andric   auto Cmp = Builder.CreateICmpEQ(Data, CharZero);
1345ffd83dbSDimitry Andric   Builder.CreateCondBr(Cmp, WhileDone, While);
1355ffd83dbSDimitry Andric 
1365ffd83dbSDimitry Andric   // Add one to the computed length.
1375ffd83dbSDimitry Andric   Builder.SetInsertPoint(WhileDone, WhileDone->begin());
1385ffd83dbSDimitry Andric   auto Begin = Builder.CreatePtrToInt(Str, Int64Ty);
1395ffd83dbSDimitry Andric   auto End = Builder.CreatePtrToInt(PtrPhi, Int64Ty);
1405ffd83dbSDimitry Andric   auto Len = Builder.CreateSub(End, Begin);
1415ffd83dbSDimitry Andric   Len = Builder.CreateAdd(Len, One);
1425ffd83dbSDimitry Andric 
1435ffd83dbSDimitry Andric   // Final join.
1445ffd83dbSDimitry Andric   BranchInst::Create(Join, WhileDone);
1455ffd83dbSDimitry Andric   Builder.SetInsertPoint(Join, Join->begin());
1465ffd83dbSDimitry Andric   auto LenPhi = Builder.CreatePHI(Len->getType(), 2);
1475ffd83dbSDimitry Andric   LenPhi->addIncoming(Len, WhileDone);
1485ffd83dbSDimitry Andric   LenPhi->addIncoming(Zero, Prev);
1495ffd83dbSDimitry Andric 
1505ffd83dbSDimitry Andric   return LenPhi;
1515ffd83dbSDimitry Andric }
1525ffd83dbSDimitry Andric 
callAppendStringN(IRBuilder<> & Builder,Value * Desc,Value * Str,Value * Length,bool isLast)1535ffd83dbSDimitry Andric static Value *callAppendStringN(IRBuilder<> &Builder, Value *Desc, Value *Str,
1545ffd83dbSDimitry Andric                                 Value *Length, bool isLast) {
1555ffd83dbSDimitry Andric   auto Int64Ty = Builder.getInt64Ty();
156*5f757f3fSDimitry Andric   auto PtrTy = Builder.getPtrTy();
1575ffd83dbSDimitry Andric   auto Int32Ty = Builder.getInt32Ty();
1585ffd83dbSDimitry Andric   auto M = Builder.GetInsertBlock()->getModule();
1595ffd83dbSDimitry Andric   auto Fn = M->getOrInsertFunction("__ockl_printf_append_string_n", Int64Ty,
160*5f757f3fSDimitry Andric                                    Int64Ty, PtrTy, Int64Ty, Int32Ty);
1615ffd83dbSDimitry Andric   auto IsLastInt32 = Builder.getInt32(isLast);
1625ffd83dbSDimitry Andric   return Builder.CreateCall(Fn, {Desc, Str, Length, IsLastInt32});
1635ffd83dbSDimitry Andric }
1645ffd83dbSDimitry Andric 
appendString(IRBuilder<> & Builder,Value * Desc,Value * Arg,bool IsLast)1655ffd83dbSDimitry Andric static Value *appendString(IRBuilder<> &Builder, Value *Desc, Value *Arg,
1665ffd83dbSDimitry Andric                            bool IsLast) {
1675ffd83dbSDimitry Andric   auto Length = getStrlenWithNull(Builder, Arg);
1685ffd83dbSDimitry Andric   return callAppendStringN(Builder, Desc, Arg, Length, IsLast);
1695ffd83dbSDimitry Andric }
1705ffd83dbSDimitry Andric 
processArg(IRBuilder<> & Builder,Value * Desc,Value * Arg,bool SpecIsCString,bool IsLast)1715ffd83dbSDimitry Andric static Value *processArg(IRBuilder<> &Builder, Value *Desc, Value *Arg,
1725ffd83dbSDimitry Andric                          bool SpecIsCString, bool IsLast) {
17304eeddc0SDimitry Andric   if (SpecIsCString && isa<PointerType>(Arg->getType())) {
1745ffd83dbSDimitry Andric     return appendString(Builder, Desc, Arg, IsLast);
1755ffd83dbSDimitry Andric   }
1765ffd83dbSDimitry Andric   // If the format specifies a string but the argument is not, the frontend will
1775ffd83dbSDimitry Andric   // have printed a warning. We just rely on undefined behaviour and send the
1785ffd83dbSDimitry Andric   // argument anyway.
1795ffd83dbSDimitry Andric   return appendArg(Builder, Desc, Arg, IsLast);
1805ffd83dbSDimitry Andric }
1815ffd83dbSDimitry Andric 
1825ffd83dbSDimitry Andric // Scan the format string to locate all specifiers, and mark the ones that
1835ffd83dbSDimitry Andric // specify a string, i.e, the "%s" specifier with optional '*' characters.
locateCStrings(SparseBitVector<8> & BV,StringRef Str)18406c3fb27SDimitry Andric static void locateCStrings(SparseBitVector<8> &BV, StringRef Str) {
1855ffd83dbSDimitry Andric   static const char ConvSpecifiers[] = "diouxXfFeEgGaAcspn";
1865ffd83dbSDimitry Andric   size_t SpecPos = 0;
1875ffd83dbSDimitry Andric   // Skip the first argument, the format string.
1885ffd83dbSDimitry Andric   unsigned ArgIdx = 1;
1895ffd83dbSDimitry Andric 
1905ffd83dbSDimitry Andric   while ((SpecPos = Str.find_first_of('%', SpecPos)) != StringRef::npos) {
1915ffd83dbSDimitry Andric     if (Str[SpecPos + 1] == '%') {
1925ffd83dbSDimitry Andric       SpecPos += 2;
1935ffd83dbSDimitry Andric       continue;
1945ffd83dbSDimitry Andric     }
1955ffd83dbSDimitry Andric     auto SpecEnd = Str.find_first_of(ConvSpecifiers, SpecPos);
1965ffd83dbSDimitry Andric     if (SpecEnd == StringRef::npos)
1975ffd83dbSDimitry Andric       return;
1985ffd83dbSDimitry Andric     auto Spec = Str.slice(SpecPos, SpecEnd + 1);
1995ffd83dbSDimitry Andric     ArgIdx += Spec.count('*');
2005ffd83dbSDimitry Andric     if (Str[SpecEnd] == 's') {
2015ffd83dbSDimitry Andric       BV.set(ArgIdx);
2025ffd83dbSDimitry Andric     }
2035ffd83dbSDimitry Andric     SpecPos = SpecEnd + 1;
2045ffd83dbSDimitry Andric     ++ArgIdx;
2055ffd83dbSDimitry Andric   }
2065ffd83dbSDimitry Andric }
2075ffd83dbSDimitry Andric 
20806c3fb27SDimitry Andric // helper struct to package the string related data
20906c3fb27SDimitry Andric struct StringData {
21006c3fb27SDimitry Andric   StringRef Str;
21106c3fb27SDimitry Andric   Value *RealSize = nullptr;
21206c3fb27SDimitry Andric   Value *AlignedSize = nullptr;
21306c3fb27SDimitry Andric   bool IsConst = true;
21406c3fb27SDimitry Andric 
StringDataStringData21506c3fb27SDimitry Andric   StringData(StringRef ST, Value *RS, Value *AS, bool IC)
21606c3fb27SDimitry Andric       : Str(ST), RealSize(RS), AlignedSize(AS), IsConst(IC) {}
21706c3fb27SDimitry Andric };
21806c3fb27SDimitry Andric 
21906c3fb27SDimitry Andric // Calculates frame size required for current printf expansion and allocates
22006c3fb27SDimitry Andric // space on printf buffer. Printf frame includes following contents
22106c3fb27SDimitry Andric // [ ControlDWord , format string/Hash , Arguments (each aligned to 8 byte) ]
callBufferedPrintfStart(IRBuilder<> & Builder,ArrayRef<Value * > Args,Value * Fmt,bool isConstFmtStr,SparseBitVector<8> & SpecIsCString,SmallVectorImpl<StringData> & StringContents,Value * & ArgSize)22206c3fb27SDimitry Andric static Value *callBufferedPrintfStart(
22306c3fb27SDimitry Andric     IRBuilder<> &Builder, ArrayRef<Value *> Args, Value *Fmt,
22406c3fb27SDimitry Andric     bool isConstFmtStr, SparseBitVector<8> &SpecIsCString,
22506c3fb27SDimitry Andric     SmallVectorImpl<StringData> &StringContents, Value *&ArgSize) {
22606c3fb27SDimitry Andric   Module *M = Builder.GetInsertBlock()->getModule();
22706c3fb27SDimitry Andric   Value *NonConstStrLen = nullptr;
22806c3fb27SDimitry Andric   Value *LenWithNull = nullptr;
22906c3fb27SDimitry Andric   Value *LenWithNullAligned = nullptr;
23006c3fb27SDimitry Andric   Value *TempAdd = nullptr;
23106c3fb27SDimitry Andric 
23206c3fb27SDimitry Andric   // First 4 bytes to be reserved for control dword
23306c3fb27SDimitry Andric   size_t BufSize = 4;
23406c3fb27SDimitry Andric   if (isConstFmtStr)
23506c3fb27SDimitry Andric     // First 8 bytes of MD5 hash
23606c3fb27SDimitry Andric     BufSize += 8;
23706c3fb27SDimitry Andric   else {
23806c3fb27SDimitry Andric     LenWithNull = getStrlenWithNull(Builder, Fmt);
23906c3fb27SDimitry Andric 
24006c3fb27SDimitry Andric     // Align the computed length to next 8 byte boundary
24106c3fb27SDimitry Andric     TempAdd = Builder.CreateAdd(LenWithNull,
24206c3fb27SDimitry Andric                                 ConstantInt::get(LenWithNull->getType(), 7U));
24306c3fb27SDimitry Andric     NonConstStrLen = Builder.CreateAnd(
24406c3fb27SDimitry Andric         TempAdd, ConstantInt::get(LenWithNull->getType(), ~7U));
24506c3fb27SDimitry Andric 
24606c3fb27SDimitry Andric     StringContents.push_back(
24706c3fb27SDimitry Andric         StringData(StringRef(), LenWithNull, NonConstStrLen, false));
24806c3fb27SDimitry Andric   }
24906c3fb27SDimitry Andric 
25006c3fb27SDimitry Andric   for (size_t i = 1; i < Args.size(); i++) {
25106c3fb27SDimitry Andric     if (SpecIsCString.test(i)) {
25206c3fb27SDimitry Andric       StringRef ArgStr;
25306c3fb27SDimitry Andric       if (getConstantStringInfo(Args[i], ArgStr)) {
25406c3fb27SDimitry Andric         auto alignedLen = alignTo(ArgStr.size() + 1, 8);
25506c3fb27SDimitry Andric         StringContents.push_back(StringData(
25606c3fb27SDimitry Andric             ArgStr,
25706c3fb27SDimitry Andric             /*RealSize*/ nullptr, /*AlignedSize*/ nullptr, /*IsConst*/ true));
25806c3fb27SDimitry Andric         BufSize += alignedLen;
25906c3fb27SDimitry Andric       } else {
26006c3fb27SDimitry Andric         LenWithNull = getStrlenWithNull(Builder, Args[i]);
26106c3fb27SDimitry Andric 
26206c3fb27SDimitry Andric         // Align the computed length to next 8 byte boundary
26306c3fb27SDimitry Andric         TempAdd = Builder.CreateAdd(
26406c3fb27SDimitry Andric             LenWithNull, ConstantInt::get(LenWithNull->getType(), 7U));
26506c3fb27SDimitry Andric         LenWithNullAligned = Builder.CreateAnd(
26606c3fb27SDimitry Andric             TempAdd, ConstantInt::get(LenWithNull->getType(), ~7U));
26706c3fb27SDimitry Andric 
26806c3fb27SDimitry Andric         if (NonConstStrLen) {
26906c3fb27SDimitry Andric           auto Val = Builder.CreateAdd(LenWithNullAligned, NonConstStrLen,
27006c3fb27SDimitry Andric                                        "cumulativeAdd");
27106c3fb27SDimitry Andric           NonConstStrLen = Val;
27206c3fb27SDimitry Andric         } else
27306c3fb27SDimitry Andric           NonConstStrLen = LenWithNullAligned;
27406c3fb27SDimitry Andric 
27506c3fb27SDimitry Andric         StringContents.push_back(
27606c3fb27SDimitry Andric             StringData(StringRef(), LenWithNull, LenWithNullAligned, false));
27706c3fb27SDimitry Andric       }
27806c3fb27SDimitry Andric     } else {
27906c3fb27SDimitry Andric       int AllocSize = M->getDataLayout().getTypeAllocSize(Args[i]->getType());
28006c3fb27SDimitry Andric       // We end up expanding non string arguments to 8 bytes
28106c3fb27SDimitry Andric       // (args smaller than 8 bytes)
28206c3fb27SDimitry Andric       BufSize += std::max(AllocSize, 8);
28306c3fb27SDimitry Andric     }
28406c3fb27SDimitry Andric   }
28506c3fb27SDimitry Andric 
28606c3fb27SDimitry Andric   // calculate final size value to be passed to printf_alloc
28706c3fb27SDimitry Andric   Value *SizeToReserve = ConstantInt::get(Builder.getInt64Ty(), BufSize, false);
28806c3fb27SDimitry Andric   SmallVector<Value *, 1> Alloc_args;
28906c3fb27SDimitry Andric   if (NonConstStrLen)
29006c3fb27SDimitry Andric     SizeToReserve = Builder.CreateAdd(NonConstStrLen, SizeToReserve);
29106c3fb27SDimitry Andric 
29206c3fb27SDimitry Andric   ArgSize = Builder.CreateTrunc(SizeToReserve, Builder.getInt32Ty());
29306c3fb27SDimitry Andric   Alloc_args.push_back(ArgSize);
29406c3fb27SDimitry Andric 
29506c3fb27SDimitry Andric   // call the printf_alloc function
29606c3fb27SDimitry Andric   AttributeList Attr = AttributeList::get(
29706c3fb27SDimitry Andric       Builder.getContext(), AttributeList::FunctionIndex, Attribute::NoUnwind);
29806c3fb27SDimitry Andric 
29906c3fb27SDimitry Andric   Type *Tys_alloc[1] = {Builder.getInt32Ty()};
300*5f757f3fSDimitry Andric   Type *PtrTy =
301*5f757f3fSDimitry Andric       Builder.getPtrTy(M->getDataLayout().getDefaultGlobalsAddressSpace());
302*5f757f3fSDimitry Andric   FunctionType *FTy_alloc = FunctionType::get(PtrTy, Tys_alloc, false);
30306c3fb27SDimitry Andric   auto PrintfAllocFn =
30406c3fb27SDimitry Andric       M->getOrInsertFunction(StringRef("__printf_alloc"), FTy_alloc, Attr);
30506c3fb27SDimitry Andric 
30606c3fb27SDimitry Andric   return Builder.CreateCall(PrintfAllocFn, Alloc_args, "printf_alloc_fn");
30706c3fb27SDimitry Andric }
30806c3fb27SDimitry Andric 
30906c3fb27SDimitry Andric // Prepare constant string argument to push onto the buffer
processConstantStringArg(StringData * SD,IRBuilder<> & Builder,SmallVectorImpl<Value * > & WhatToStore)31006c3fb27SDimitry Andric static void processConstantStringArg(StringData *SD, IRBuilder<> &Builder,
31106c3fb27SDimitry Andric                                      SmallVectorImpl<Value *> &WhatToStore) {
31206c3fb27SDimitry Andric   std::string Str(SD->Str.str() + '\0');
31306c3fb27SDimitry Andric 
31406c3fb27SDimitry Andric   DataExtractor Extractor(Str, /*IsLittleEndian=*/true, 8);
31506c3fb27SDimitry Andric   DataExtractor::Cursor Offset(0);
31606c3fb27SDimitry Andric   while (Offset && Offset.tell() < Str.size()) {
31706c3fb27SDimitry Andric     const uint64_t ReadSize = 4;
31806c3fb27SDimitry Andric     uint64_t ReadNow = std::min(ReadSize, Str.size() - Offset.tell());
31906c3fb27SDimitry Andric     uint64_t ReadBytes = 0;
32006c3fb27SDimitry Andric     switch (ReadNow) {
32106c3fb27SDimitry Andric     default:
32206c3fb27SDimitry Andric       llvm_unreachable("min(4, X) > 4?");
32306c3fb27SDimitry Andric     case 1:
32406c3fb27SDimitry Andric       ReadBytes = Extractor.getU8(Offset);
32506c3fb27SDimitry Andric       break;
32606c3fb27SDimitry Andric     case 2:
32706c3fb27SDimitry Andric       ReadBytes = Extractor.getU16(Offset);
32806c3fb27SDimitry Andric       break;
32906c3fb27SDimitry Andric     case 3:
33006c3fb27SDimitry Andric       ReadBytes = Extractor.getU24(Offset);
33106c3fb27SDimitry Andric       break;
33206c3fb27SDimitry Andric     case 4:
33306c3fb27SDimitry Andric       ReadBytes = Extractor.getU32(Offset);
33406c3fb27SDimitry Andric       break;
33506c3fb27SDimitry Andric     }
33606c3fb27SDimitry Andric     cantFail(Offset.takeError(), "failed to read bytes from constant array");
33706c3fb27SDimitry Andric 
33806c3fb27SDimitry Andric     APInt IntVal(8 * ReadSize, ReadBytes);
33906c3fb27SDimitry Andric 
34006c3fb27SDimitry Andric     // TODO: Should not bother aligning up.
34106c3fb27SDimitry Andric     if (ReadNow < ReadSize)
34206c3fb27SDimitry Andric       IntVal = IntVal.zext(8 * ReadSize);
34306c3fb27SDimitry Andric 
34406c3fb27SDimitry Andric     Type *IntTy = Type::getIntNTy(Builder.getContext(), IntVal.getBitWidth());
34506c3fb27SDimitry Andric     WhatToStore.push_back(ConstantInt::get(IntTy, IntVal));
34606c3fb27SDimitry Andric   }
34706c3fb27SDimitry Andric   // Additional padding for 8 byte alignment
34806c3fb27SDimitry Andric   int Rem = (Str.size() % 8);
34906c3fb27SDimitry Andric   if (Rem > 0 && Rem <= 4)
35006c3fb27SDimitry Andric     WhatToStore.push_back(ConstantInt::get(Builder.getInt32Ty(), 0));
35106c3fb27SDimitry Andric }
35206c3fb27SDimitry Andric 
processNonStringArg(Value * Arg,IRBuilder<> & Builder)35306c3fb27SDimitry Andric static Value *processNonStringArg(Value *Arg, IRBuilder<> &Builder) {
35406c3fb27SDimitry Andric   const DataLayout &DL = Builder.GetInsertBlock()->getModule()->getDataLayout();
35506c3fb27SDimitry Andric   auto Ty = Arg->getType();
35606c3fb27SDimitry Andric 
35706c3fb27SDimitry Andric   if (auto IntTy = dyn_cast<IntegerType>(Ty)) {
35806c3fb27SDimitry Andric     if (IntTy->getBitWidth() < 64) {
35906c3fb27SDimitry Andric       return Builder.CreateZExt(Arg, Builder.getInt64Ty());
36006c3fb27SDimitry Andric     }
36106c3fb27SDimitry Andric   }
36206c3fb27SDimitry Andric 
36306c3fb27SDimitry Andric   if (Ty->isFloatingPointTy()) {
36406c3fb27SDimitry Andric     if (DL.getTypeAllocSize(Ty) < 8) {
36506c3fb27SDimitry Andric       return Builder.CreateFPExt(Arg, Builder.getDoubleTy());
36606c3fb27SDimitry Andric     }
36706c3fb27SDimitry Andric   }
36806c3fb27SDimitry Andric 
36906c3fb27SDimitry Andric   return Arg;
37006c3fb27SDimitry Andric }
37106c3fb27SDimitry Andric 
37206c3fb27SDimitry Andric static void
callBufferedPrintfArgPush(IRBuilder<> & Builder,ArrayRef<Value * > Args,Value * PtrToStore,SparseBitVector<8> & SpecIsCString,SmallVectorImpl<StringData> & StringContents,bool IsConstFmtStr)37306c3fb27SDimitry Andric callBufferedPrintfArgPush(IRBuilder<> &Builder, ArrayRef<Value *> Args,
37406c3fb27SDimitry Andric                           Value *PtrToStore, SparseBitVector<8> &SpecIsCString,
37506c3fb27SDimitry Andric                           SmallVectorImpl<StringData> &StringContents,
37606c3fb27SDimitry Andric                           bool IsConstFmtStr) {
37706c3fb27SDimitry Andric   Module *M = Builder.GetInsertBlock()->getModule();
37806c3fb27SDimitry Andric   const DataLayout &DL = M->getDataLayout();
37906c3fb27SDimitry Andric   auto StrIt = StringContents.begin();
38006c3fb27SDimitry Andric   size_t i = IsConstFmtStr ? 1 : 0;
38106c3fb27SDimitry Andric   for (; i < Args.size(); i++) {
38206c3fb27SDimitry Andric     SmallVector<Value *, 32> WhatToStore;
38306c3fb27SDimitry Andric     if ((i == 0) || SpecIsCString.test(i)) {
38406c3fb27SDimitry Andric       if (StrIt->IsConst) {
38506c3fb27SDimitry Andric         processConstantStringArg(StrIt, Builder, WhatToStore);
38606c3fb27SDimitry Andric         StrIt++;
38706c3fb27SDimitry Andric       } else {
38806c3fb27SDimitry Andric         // This copies the contents of the string, however the next offset
38906c3fb27SDimitry Andric         // is at aligned length, the extra space that might be created due
39006c3fb27SDimitry Andric         // to alignment padding is not populated with any specific value
39106c3fb27SDimitry Andric         // here. This would be safe as long as runtime is sync with
39206c3fb27SDimitry Andric         // the offsets.
39306c3fb27SDimitry Andric         Builder.CreateMemCpy(PtrToStore, /*DstAlign*/ Align(1), Args[i],
39406c3fb27SDimitry Andric                              /*SrcAlign*/ Args[i]->getPointerAlignment(DL),
39506c3fb27SDimitry Andric                              StrIt->RealSize);
39606c3fb27SDimitry Andric 
39706c3fb27SDimitry Andric         PtrToStore =
39806c3fb27SDimitry Andric             Builder.CreateInBoundsGEP(Builder.getInt8Ty(), PtrToStore,
39906c3fb27SDimitry Andric                                       {StrIt->AlignedSize}, "PrintBuffNextPtr");
40006c3fb27SDimitry Andric         LLVM_DEBUG(dbgs() << "inserting gep to the printf buffer:"
40106c3fb27SDimitry Andric                           << *PtrToStore << '\n');
40206c3fb27SDimitry Andric 
40306c3fb27SDimitry Andric         // done with current argument, move to next
40406c3fb27SDimitry Andric         StrIt++;
40506c3fb27SDimitry Andric         continue;
40606c3fb27SDimitry Andric       }
40706c3fb27SDimitry Andric     } else {
40806c3fb27SDimitry Andric       WhatToStore.push_back(processNonStringArg(Args[i], Builder));
40906c3fb27SDimitry Andric     }
41006c3fb27SDimitry Andric 
41106c3fb27SDimitry Andric     for (unsigned I = 0, E = WhatToStore.size(); I != E; ++I) {
41206c3fb27SDimitry Andric       Value *toStore = WhatToStore[I];
41306c3fb27SDimitry Andric 
41406c3fb27SDimitry Andric       StoreInst *StBuff = Builder.CreateStore(toStore, PtrToStore);
41506c3fb27SDimitry Andric       LLVM_DEBUG(dbgs() << "inserting store to printf buffer:" << *StBuff
41606c3fb27SDimitry Andric                         << '\n');
41706c3fb27SDimitry Andric       (void)StBuff;
41806c3fb27SDimitry Andric       PtrToStore = Builder.CreateConstInBoundsGEP1_32(
41906c3fb27SDimitry Andric           Builder.getInt8Ty(), PtrToStore,
42006c3fb27SDimitry Andric           M->getDataLayout().getTypeAllocSize(toStore->getType()),
42106c3fb27SDimitry Andric           "PrintBuffNextPtr");
42206c3fb27SDimitry Andric       LLVM_DEBUG(dbgs() << "inserting gep to the printf buffer:" << *PtrToStore
42306c3fb27SDimitry Andric                         << '\n');
42406c3fb27SDimitry Andric     }
42506c3fb27SDimitry Andric   }
42606c3fb27SDimitry Andric }
42706c3fb27SDimitry Andric 
emitAMDGPUPrintfCall(IRBuilder<> & Builder,ArrayRef<Value * > Args,bool IsBuffered)42806c3fb27SDimitry Andric Value *llvm::emitAMDGPUPrintfCall(IRBuilder<> &Builder, ArrayRef<Value *> Args,
42906c3fb27SDimitry Andric                                   bool IsBuffered) {
4305ffd83dbSDimitry Andric   auto NumOps = Args.size();
4315ffd83dbSDimitry Andric   assert(NumOps >= 1);
4325ffd83dbSDimitry Andric 
4335ffd83dbSDimitry Andric   auto Fmt = Args[0];
4345ffd83dbSDimitry Andric   SparseBitVector<8> SpecIsCString;
43506c3fb27SDimitry Andric   StringRef FmtStr;
43606c3fb27SDimitry Andric 
43706c3fb27SDimitry Andric   if (getConstantStringInfo(Fmt, FmtStr))
43806c3fb27SDimitry Andric     locateCStrings(SpecIsCString, FmtStr);
43906c3fb27SDimitry Andric 
44006c3fb27SDimitry Andric   if (IsBuffered) {
44106c3fb27SDimitry Andric     SmallVector<StringData, 8> StringContents;
44206c3fb27SDimitry Andric     Module *M = Builder.GetInsertBlock()->getModule();
44306c3fb27SDimitry Andric     LLVMContext &Ctx = Builder.getContext();
44406c3fb27SDimitry Andric     auto Int8Ty = Builder.getInt8Ty();
44506c3fb27SDimitry Andric     auto Int32Ty = Builder.getInt32Ty();
44606c3fb27SDimitry Andric     bool IsConstFmtStr = !FmtStr.empty();
44706c3fb27SDimitry Andric 
44806c3fb27SDimitry Andric     Value *ArgSize = nullptr;
44906c3fb27SDimitry Andric     Value *Ptr =
45006c3fb27SDimitry Andric         callBufferedPrintfStart(Builder, Args, Fmt, IsConstFmtStr,
45106c3fb27SDimitry Andric                                 SpecIsCString, StringContents, ArgSize);
45206c3fb27SDimitry Andric 
45306c3fb27SDimitry Andric     // The buffered version still follows OpenCL printf standards for
45406c3fb27SDimitry Andric     // printf return value, i.e 0 on success, -1 on failure.
45506c3fb27SDimitry Andric     ConstantPointerNull *zeroIntPtr =
45606c3fb27SDimitry Andric         ConstantPointerNull::get(cast<PointerType>(Ptr->getType()));
45706c3fb27SDimitry Andric 
45806c3fb27SDimitry Andric     auto *Cmp = cast<ICmpInst>(Builder.CreateICmpNE(Ptr, zeroIntPtr, ""));
45906c3fb27SDimitry Andric 
46006c3fb27SDimitry Andric     BasicBlock *End = BasicBlock::Create(Ctx, "end.block",
46106c3fb27SDimitry Andric                                          Builder.GetInsertBlock()->getParent());
46206c3fb27SDimitry Andric     BasicBlock *ArgPush = BasicBlock::Create(
46306c3fb27SDimitry Andric         Ctx, "argpush.block", Builder.GetInsertBlock()->getParent());
46406c3fb27SDimitry Andric 
46506c3fb27SDimitry Andric     BranchInst::Create(ArgPush, End, Cmp, Builder.GetInsertBlock());
46606c3fb27SDimitry Andric     Builder.SetInsertPoint(ArgPush);
46706c3fb27SDimitry Andric 
46806c3fb27SDimitry Andric     // Create controlDWord and store as the first entry, format as follows
46906c3fb27SDimitry Andric     // Bit 0 (LSB) -> stream (1 if stderr, 0 if stdout, printf always outputs to
47006c3fb27SDimitry Andric     // stdout) Bit 1 -> constant format string (1 if constant) Bits 2-31 -> size
47106c3fb27SDimitry Andric     // of printf data frame
47206c3fb27SDimitry Andric     auto ConstantTwo = Builder.getInt32(2);
47306c3fb27SDimitry Andric     auto ControlDWord = Builder.CreateShl(ArgSize, ConstantTwo);
47406c3fb27SDimitry Andric     if (IsConstFmtStr)
47506c3fb27SDimitry Andric       ControlDWord = Builder.CreateOr(ControlDWord, ConstantTwo);
47606c3fb27SDimitry Andric 
47706c3fb27SDimitry Andric     Builder.CreateStore(ControlDWord, Ptr);
47806c3fb27SDimitry Andric 
47906c3fb27SDimitry Andric     Ptr = Builder.CreateConstInBoundsGEP1_32(Int8Ty, Ptr, 4);
48006c3fb27SDimitry Andric 
48106c3fb27SDimitry Andric     // Create MD5 hash for costant format string, push low 64 bits of the
48206c3fb27SDimitry Andric     // same onto buffer and metadata.
48306c3fb27SDimitry Andric     NamedMDNode *metaD = M->getOrInsertNamedMetadata("llvm.printf.fmts");
48406c3fb27SDimitry Andric     if (IsConstFmtStr) {
48506c3fb27SDimitry Andric       MD5 Hasher;
48606c3fb27SDimitry Andric       MD5::MD5Result Hash;
48706c3fb27SDimitry Andric       Hasher.update(FmtStr);
48806c3fb27SDimitry Andric       Hasher.final(Hash);
48906c3fb27SDimitry Andric 
49006c3fb27SDimitry Andric       // Try sticking to llvm.printf.fmts format, although we are not going to
49106c3fb27SDimitry Andric       // use the ID and argument size fields while printing,
49206c3fb27SDimitry Andric       std::string MetadataStr =
49306c3fb27SDimitry Andric           "0:0:" + llvm::utohexstr(Hash.low(), /*LowerCase=*/true) + "," +
49406c3fb27SDimitry Andric           FmtStr.str();
49506c3fb27SDimitry Andric       MDString *fmtStrArray = MDString::get(Ctx, MetadataStr);
49606c3fb27SDimitry Andric       MDNode *myMD = MDNode::get(Ctx, fmtStrArray);
49706c3fb27SDimitry Andric       metaD->addOperand(myMD);
49806c3fb27SDimitry Andric 
49906c3fb27SDimitry Andric       Builder.CreateStore(Builder.getInt64(Hash.low()), Ptr);
50006c3fb27SDimitry Andric       Ptr = Builder.CreateConstInBoundsGEP1_32(Int8Ty, Ptr, 8);
50106c3fb27SDimitry Andric     } else {
50206c3fb27SDimitry Andric       // Include a dummy metadata instance in case of only non constant
50306c3fb27SDimitry Andric       // format string usage, This might be an absurd usecase but needs to
50406c3fb27SDimitry Andric       // be done for completeness
50506c3fb27SDimitry Andric       if (metaD->getNumOperands() == 0) {
50606c3fb27SDimitry Andric         MDString *fmtStrArray =
50706c3fb27SDimitry Andric             MDString::get(Ctx, "0:0:ffffffff,\"Non const format string\"");
50806c3fb27SDimitry Andric         MDNode *myMD = MDNode::get(Ctx, fmtStrArray);
50906c3fb27SDimitry Andric         metaD->addOperand(myMD);
51006c3fb27SDimitry Andric       }
51106c3fb27SDimitry Andric     }
51206c3fb27SDimitry Andric 
51306c3fb27SDimitry Andric     // Push The printf arguments onto buffer
51406c3fb27SDimitry Andric     callBufferedPrintfArgPush(Builder, Args, Ptr, SpecIsCString, StringContents,
51506c3fb27SDimitry Andric                               IsConstFmtStr);
51606c3fb27SDimitry Andric 
51706c3fb27SDimitry Andric     // End block, returns -1 on failure
51806c3fb27SDimitry Andric     BranchInst::Create(End, ArgPush);
51906c3fb27SDimitry Andric     Builder.SetInsertPoint(End);
52006c3fb27SDimitry Andric     return Builder.CreateSExt(Builder.CreateNot(Cmp), Int32Ty, "printf_result");
52106c3fb27SDimitry Andric   }
5225ffd83dbSDimitry Andric 
5235ffd83dbSDimitry Andric   auto Desc = callPrintfBegin(Builder, Builder.getIntN(64, 0));
5245ffd83dbSDimitry Andric   Desc = appendString(Builder, Desc, Fmt, NumOps == 1);
5255ffd83dbSDimitry Andric 
5265ffd83dbSDimitry Andric   // FIXME: This invokes hostcall once for each argument. We can pack up to
5275ffd83dbSDimitry Andric   // seven scalar printf arguments in a single hostcall. See the signature of
5285ffd83dbSDimitry Andric   // callAppendArgs().
5295ffd83dbSDimitry Andric   for (unsigned int i = 1; i != NumOps; ++i) {
5305ffd83dbSDimitry Andric     bool IsLast = i == NumOps - 1;
5315ffd83dbSDimitry Andric     bool IsCString = SpecIsCString.test(i);
5325ffd83dbSDimitry Andric     Desc = processArg(Builder, Desc, Args[i], IsCString, IsLast);
5335ffd83dbSDimitry Andric   }
5345ffd83dbSDimitry Andric 
5355ffd83dbSDimitry Andric   return Builder.CreateTrunc(Desc, Builder.getInt32Ty());
5365ffd83dbSDimitry Andric }
537