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" 21*0fca6ea1SDimitry Andric #include "llvm/IR/Module.h" 2206c3fb27SDimitry Andric #include "llvm/Support/DataExtractor.h" 2306c3fb27SDimitry Andric #include "llvm/Support/MD5.h" 2406c3fb27SDimitry Andric #include "llvm/Support/MathExtras.h" 255ffd83dbSDimitry Andric 265ffd83dbSDimitry Andric using namespace llvm; 275ffd83dbSDimitry Andric 285ffd83dbSDimitry Andric #define DEBUG_TYPE "amdgpu-emit-printf" 295ffd83dbSDimitry Andric 305ffd83dbSDimitry Andric static Value *fitArgInto64Bits(IRBuilder<> &Builder, Value *Arg) { 315ffd83dbSDimitry Andric auto Int64Ty = Builder.getInt64Ty(); 325ffd83dbSDimitry Andric auto Ty = Arg->getType(); 335ffd83dbSDimitry Andric 345ffd83dbSDimitry Andric if (auto IntTy = dyn_cast<IntegerType>(Ty)) { 355ffd83dbSDimitry Andric switch (IntTy->getBitWidth()) { 365ffd83dbSDimitry Andric case 32: 375ffd83dbSDimitry Andric return Builder.CreateZExt(Arg, Int64Ty); 385ffd83dbSDimitry Andric case 64: 395ffd83dbSDimitry Andric return Arg; 405ffd83dbSDimitry Andric } 415ffd83dbSDimitry Andric } 425ffd83dbSDimitry Andric 435ffd83dbSDimitry Andric if (Ty->getTypeID() == Type::DoubleTyID) { 445ffd83dbSDimitry Andric return Builder.CreateBitCast(Arg, Int64Ty); 455ffd83dbSDimitry Andric } 465ffd83dbSDimitry Andric 475ffd83dbSDimitry Andric if (isa<PointerType>(Ty)) { 485ffd83dbSDimitry Andric return Builder.CreatePtrToInt(Arg, Int64Ty); 495ffd83dbSDimitry Andric } 505ffd83dbSDimitry Andric 515ffd83dbSDimitry Andric llvm_unreachable("unexpected type"); 525ffd83dbSDimitry Andric } 535ffd83dbSDimitry Andric 545ffd83dbSDimitry Andric static Value *callPrintfBegin(IRBuilder<> &Builder, Value *Version) { 555ffd83dbSDimitry Andric auto Int64Ty = Builder.getInt64Ty(); 565ffd83dbSDimitry Andric auto M = Builder.GetInsertBlock()->getModule(); 575ffd83dbSDimitry Andric auto Fn = M->getOrInsertFunction("__ockl_printf_begin", Int64Ty, Int64Ty); 585ffd83dbSDimitry Andric return Builder.CreateCall(Fn, Version); 595ffd83dbSDimitry Andric } 605ffd83dbSDimitry Andric 615ffd83dbSDimitry Andric static Value *callAppendArgs(IRBuilder<> &Builder, Value *Desc, int NumArgs, 625ffd83dbSDimitry Andric Value *Arg0, Value *Arg1, Value *Arg2, Value *Arg3, 635ffd83dbSDimitry Andric Value *Arg4, Value *Arg5, Value *Arg6, 645ffd83dbSDimitry Andric bool IsLast) { 655ffd83dbSDimitry Andric auto Int64Ty = Builder.getInt64Ty(); 665ffd83dbSDimitry Andric auto Int32Ty = Builder.getInt32Ty(); 675ffd83dbSDimitry Andric auto M = Builder.GetInsertBlock()->getModule(); 685ffd83dbSDimitry Andric auto Fn = M->getOrInsertFunction("__ockl_printf_append_args", Int64Ty, 695ffd83dbSDimitry Andric Int64Ty, Int32Ty, Int64Ty, Int64Ty, Int64Ty, 705ffd83dbSDimitry Andric Int64Ty, Int64Ty, Int64Ty, Int64Ty, Int32Ty); 715ffd83dbSDimitry Andric auto IsLastValue = Builder.getInt32(IsLast); 725ffd83dbSDimitry Andric auto NumArgsValue = Builder.getInt32(NumArgs); 735ffd83dbSDimitry Andric return Builder.CreateCall(Fn, {Desc, NumArgsValue, Arg0, Arg1, Arg2, Arg3, 745ffd83dbSDimitry Andric Arg4, Arg5, Arg6, IsLastValue}); 755ffd83dbSDimitry Andric } 765ffd83dbSDimitry Andric 775ffd83dbSDimitry Andric static Value *appendArg(IRBuilder<> &Builder, Value *Desc, Value *Arg, 785ffd83dbSDimitry Andric bool IsLast) { 795ffd83dbSDimitry Andric auto Arg0 = fitArgInto64Bits(Builder, Arg); 805ffd83dbSDimitry Andric auto Zero = Builder.getInt64(0); 815ffd83dbSDimitry Andric return callAppendArgs(Builder, Desc, 1, Arg0, Zero, Zero, Zero, Zero, Zero, 825ffd83dbSDimitry Andric Zero, IsLast); 835ffd83dbSDimitry Andric } 845ffd83dbSDimitry Andric 855ffd83dbSDimitry Andric // The device library does not provide strlen, so we build our own loop 865ffd83dbSDimitry Andric // here. While we are at it, we also include the terminating null in the length. 875ffd83dbSDimitry Andric static Value *getStrlenWithNull(IRBuilder<> &Builder, Value *Str) { 885ffd83dbSDimitry Andric auto *Prev = Builder.GetInsertBlock(); 895ffd83dbSDimitry Andric Module *M = Prev->getModule(); 905ffd83dbSDimitry Andric 915ffd83dbSDimitry Andric auto CharZero = Builder.getInt8(0); 925ffd83dbSDimitry Andric auto One = Builder.getInt64(1); 935ffd83dbSDimitry Andric auto Zero = Builder.getInt64(0); 945ffd83dbSDimitry Andric auto Int64Ty = Builder.getInt64Ty(); 955ffd83dbSDimitry Andric 965ffd83dbSDimitry Andric // The length is either zero for a null pointer, or the computed value for an 975ffd83dbSDimitry Andric // actual string. We need a join block for a phi that represents the final 985ffd83dbSDimitry Andric // value. 995ffd83dbSDimitry Andric // 1005ffd83dbSDimitry Andric // Strictly speaking, the zero does not matter since 1015ffd83dbSDimitry Andric // __ockl_printf_append_string_n ignores the length if the pointer is null. 1025ffd83dbSDimitry Andric BasicBlock *Join = nullptr; 1035ffd83dbSDimitry Andric if (Prev->getTerminator()) { 1045ffd83dbSDimitry Andric Join = Prev->splitBasicBlock(Builder.GetInsertPoint(), 1055ffd83dbSDimitry Andric "strlen.join"); 1065ffd83dbSDimitry Andric Prev->getTerminator()->eraseFromParent(); 1075ffd83dbSDimitry Andric } else { 1085ffd83dbSDimitry Andric Join = BasicBlock::Create(M->getContext(), "strlen.join", 1095ffd83dbSDimitry Andric Prev->getParent()); 1105ffd83dbSDimitry Andric } 1115ffd83dbSDimitry Andric BasicBlock *While = 1125ffd83dbSDimitry Andric BasicBlock::Create(M->getContext(), "strlen.while", 1135ffd83dbSDimitry Andric Prev->getParent(), Join); 1145ffd83dbSDimitry Andric BasicBlock *WhileDone = BasicBlock::Create( 1155ffd83dbSDimitry Andric M->getContext(), "strlen.while.done", 1165ffd83dbSDimitry Andric Prev->getParent(), Join); 1175ffd83dbSDimitry Andric 1185ffd83dbSDimitry Andric // Emit an early return for when the pointer is null. 1195ffd83dbSDimitry Andric Builder.SetInsertPoint(Prev); 1205ffd83dbSDimitry Andric auto CmpNull = 1215ffd83dbSDimitry Andric Builder.CreateICmpEQ(Str, Constant::getNullValue(Str->getType())); 1225ffd83dbSDimitry Andric BranchInst::Create(Join, While, CmpNull, Prev); 1235ffd83dbSDimitry Andric 1245ffd83dbSDimitry Andric // Entry to the while loop. 1255ffd83dbSDimitry Andric Builder.SetInsertPoint(While); 1265ffd83dbSDimitry Andric 1275ffd83dbSDimitry Andric auto PtrPhi = Builder.CreatePHI(Str->getType(), 2); 1285ffd83dbSDimitry Andric PtrPhi->addIncoming(Str, Prev); 129fe6060f1SDimitry Andric auto PtrNext = Builder.CreateGEP(Builder.getInt8Ty(), PtrPhi, One); 1305ffd83dbSDimitry Andric PtrPhi->addIncoming(PtrNext, While); 1315ffd83dbSDimitry Andric 1325ffd83dbSDimitry Andric // Condition for the while loop. 133fe6060f1SDimitry Andric auto Data = Builder.CreateLoad(Builder.getInt8Ty(), PtrPhi); 1345ffd83dbSDimitry Andric auto Cmp = Builder.CreateICmpEQ(Data, CharZero); 1355ffd83dbSDimitry Andric Builder.CreateCondBr(Cmp, WhileDone, While); 1365ffd83dbSDimitry Andric 1375ffd83dbSDimitry Andric // Add one to the computed length. 1385ffd83dbSDimitry Andric Builder.SetInsertPoint(WhileDone, WhileDone->begin()); 1395ffd83dbSDimitry Andric auto Begin = Builder.CreatePtrToInt(Str, Int64Ty); 1405ffd83dbSDimitry Andric auto End = Builder.CreatePtrToInt(PtrPhi, Int64Ty); 1415ffd83dbSDimitry Andric auto Len = Builder.CreateSub(End, Begin); 1425ffd83dbSDimitry Andric Len = Builder.CreateAdd(Len, One); 1435ffd83dbSDimitry Andric 1445ffd83dbSDimitry Andric // Final join. 1455ffd83dbSDimitry Andric BranchInst::Create(Join, WhileDone); 1465ffd83dbSDimitry Andric Builder.SetInsertPoint(Join, Join->begin()); 1475ffd83dbSDimitry Andric auto LenPhi = Builder.CreatePHI(Len->getType(), 2); 1485ffd83dbSDimitry Andric LenPhi->addIncoming(Len, WhileDone); 1495ffd83dbSDimitry Andric LenPhi->addIncoming(Zero, Prev); 1505ffd83dbSDimitry Andric 1515ffd83dbSDimitry Andric return LenPhi; 1525ffd83dbSDimitry Andric } 1535ffd83dbSDimitry Andric 1545ffd83dbSDimitry Andric static Value *callAppendStringN(IRBuilder<> &Builder, Value *Desc, Value *Str, 1555ffd83dbSDimitry Andric Value *Length, bool isLast) { 1565ffd83dbSDimitry Andric auto Int64Ty = Builder.getInt64Ty(); 157*0fca6ea1SDimitry Andric auto IsLastInt32 = Builder.getInt32(isLast); 1585ffd83dbSDimitry Andric auto M = Builder.GetInsertBlock()->getModule(); 1595ffd83dbSDimitry Andric auto Fn = M->getOrInsertFunction("__ockl_printf_append_string_n", Int64Ty, 160*0fca6ea1SDimitry Andric Desc->getType(), Str->getType(), 161*0fca6ea1SDimitry Andric Length->getType(), IsLastInt32->getType()); 1625ffd83dbSDimitry Andric return Builder.CreateCall(Fn, {Desc, Str, Length, IsLastInt32}); 1635ffd83dbSDimitry Andric } 1645ffd83dbSDimitry Andric 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 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. 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 21506c3fb27SDimitry 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) ] 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()}; 3005f757f3fSDimitry Andric Type *PtrTy = 3015f757f3fSDimitry Andric Builder.getPtrTy(M->getDataLayout().getDefaultGlobalsAddressSpace()); 3025f757f3fSDimitry 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 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 35306c3fb27SDimitry Andric static Value *processNonStringArg(Value *Arg, IRBuilder<> &Builder) { 354*0fca6ea1SDimitry Andric const DataLayout &DL = Builder.GetInsertBlock()->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 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 411*0fca6ea1SDimitry Andric for (Value *toStore : WhatToStore) { 41206c3fb27SDimitry Andric StoreInst *StBuff = Builder.CreateStore(toStore, PtrToStore); 41306c3fb27SDimitry Andric LLVM_DEBUG(dbgs() << "inserting store to printf buffer:" << *StBuff 41406c3fb27SDimitry Andric << '\n'); 41506c3fb27SDimitry Andric (void)StBuff; 41606c3fb27SDimitry Andric PtrToStore = Builder.CreateConstInBoundsGEP1_32( 41706c3fb27SDimitry Andric Builder.getInt8Ty(), PtrToStore, 41806c3fb27SDimitry Andric M->getDataLayout().getTypeAllocSize(toStore->getType()), 41906c3fb27SDimitry Andric "PrintBuffNextPtr"); 42006c3fb27SDimitry Andric LLVM_DEBUG(dbgs() << "inserting gep to the printf buffer:" << *PtrToStore 42106c3fb27SDimitry Andric << '\n'); 42206c3fb27SDimitry Andric } 42306c3fb27SDimitry Andric } 42406c3fb27SDimitry Andric } 42506c3fb27SDimitry Andric 42606c3fb27SDimitry Andric Value *llvm::emitAMDGPUPrintfCall(IRBuilder<> &Builder, ArrayRef<Value *> Args, 42706c3fb27SDimitry Andric bool IsBuffered) { 4285ffd83dbSDimitry Andric auto NumOps = Args.size(); 4295ffd83dbSDimitry Andric assert(NumOps >= 1); 4305ffd83dbSDimitry Andric 4315ffd83dbSDimitry Andric auto Fmt = Args[0]; 4325ffd83dbSDimitry Andric SparseBitVector<8> SpecIsCString; 43306c3fb27SDimitry Andric StringRef FmtStr; 43406c3fb27SDimitry Andric 43506c3fb27SDimitry Andric if (getConstantStringInfo(Fmt, FmtStr)) 43606c3fb27SDimitry Andric locateCStrings(SpecIsCString, FmtStr); 43706c3fb27SDimitry Andric 43806c3fb27SDimitry Andric if (IsBuffered) { 43906c3fb27SDimitry Andric SmallVector<StringData, 8> StringContents; 44006c3fb27SDimitry Andric Module *M = Builder.GetInsertBlock()->getModule(); 44106c3fb27SDimitry Andric LLVMContext &Ctx = Builder.getContext(); 44206c3fb27SDimitry Andric auto Int8Ty = Builder.getInt8Ty(); 44306c3fb27SDimitry Andric auto Int32Ty = Builder.getInt32Ty(); 44406c3fb27SDimitry Andric bool IsConstFmtStr = !FmtStr.empty(); 44506c3fb27SDimitry Andric 44606c3fb27SDimitry Andric Value *ArgSize = nullptr; 44706c3fb27SDimitry Andric Value *Ptr = 44806c3fb27SDimitry Andric callBufferedPrintfStart(Builder, Args, Fmt, IsConstFmtStr, 44906c3fb27SDimitry Andric SpecIsCString, StringContents, ArgSize); 45006c3fb27SDimitry Andric 45106c3fb27SDimitry Andric // The buffered version still follows OpenCL printf standards for 45206c3fb27SDimitry Andric // printf return value, i.e 0 on success, -1 on failure. 45306c3fb27SDimitry Andric ConstantPointerNull *zeroIntPtr = 45406c3fb27SDimitry Andric ConstantPointerNull::get(cast<PointerType>(Ptr->getType())); 45506c3fb27SDimitry Andric 45606c3fb27SDimitry Andric auto *Cmp = cast<ICmpInst>(Builder.CreateICmpNE(Ptr, zeroIntPtr, "")); 45706c3fb27SDimitry Andric 45806c3fb27SDimitry Andric BasicBlock *End = BasicBlock::Create(Ctx, "end.block", 45906c3fb27SDimitry Andric Builder.GetInsertBlock()->getParent()); 46006c3fb27SDimitry Andric BasicBlock *ArgPush = BasicBlock::Create( 46106c3fb27SDimitry Andric Ctx, "argpush.block", Builder.GetInsertBlock()->getParent()); 46206c3fb27SDimitry Andric 46306c3fb27SDimitry Andric BranchInst::Create(ArgPush, End, Cmp, Builder.GetInsertBlock()); 46406c3fb27SDimitry Andric Builder.SetInsertPoint(ArgPush); 46506c3fb27SDimitry Andric 46606c3fb27SDimitry Andric // Create controlDWord and store as the first entry, format as follows 46706c3fb27SDimitry Andric // Bit 0 (LSB) -> stream (1 if stderr, 0 if stdout, printf always outputs to 46806c3fb27SDimitry Andric // stdout) Bit 1 -> constant format string (1 if constant) Bits 2-31 -> size 46906c3fb27SDimitry Andric // of printf data frame 47006c3fb27SDimitry Andric auto ConstantTwo = Builder.getInt32(2); 47106c3fb27SDimitry Andric auto ControlDWord = Builder.CreateShl(ArgSize, ConstantTwo); 47206c3fb27SDimitry Andric if (IsConstFmtStr) 47306c3fb27SDimitry Andric ControlDWord = Builder.CreateOr(ControlDWord, ConstantTwo); 47406c3fb27SDimitry Andric 47506c3fb27SDimitry Andric Builder.CreateStore(ControlDWord, Ptr); 47606c3fb27SDimitry Andric 47706c3fb27SDimitry Andric Ptr = Builder.CreateConstInBoundsGEP1_32(Int8Ty, Ptr, 4); 47806c3fb27SDimitry Andric 47906c3fb27SDimitry Andric // Create MD5 hash for costant format string, push low 64 bits of the 48006c3fb27SDimitry Andric // same onto buffer and metadata. 48106c3fb27SDimitry Andric NamedMDNode *metaD = M->getOrInsertNamedMetadata("llvm.printf.fmts"); 48206c3fb27SDimitry Andric if (IsConstFmtStr) { 48306c3fb27SDimitry Andric MD5 Hasher; 48406c3fb27SDimitry Andric MD5::MD5Result Hash; 48506c3fb27SDimitry Andric Hasher.update(FmtStr); 48606c3fb27SDimitry Andric Hasher.final(Hash); 48706c3fb27SDimitry Andric 48806c3fb27SDimitry Andric // Try sticking to llvm.printf.fmts format, although we are not going to 48906c3fb27SDimitry Andric // use the ID and argument size fields while printing, 49006c3fb27SDimitry Andric std::string MetadataStr = 49106c3fb27SDimitry Andric "0:0:" + llvm::utohexstr(Hash.low(), /*LowerCase=*/true) + "," + 49206c3fb27SDimitry Andric FmtStr.str(); 49306c3fb27SDimitry Andric MDString *fmtStrArray = MDString::get(Ctx, MetadataStr); 49406c3fb27SDimitry Andric MDNode *myMD = MDNode::get(Ctx, fmtStrArray); 49506c3fb27SDimitry Andric metaD->addOperand(myMD); 49606c3fb27SDimitry Andric 49706c3fb27SDimitry Andric Builder.CreateStore(Builder.getInt64(Hash.low()), Ptr); 49806c3fb27SDimitry Andric Ptr = Builder.CreateConstInBoundsGEP1_32(Int8Ty, Ptr, 8); 49906c3fb27SDimitry Andric } else { 50006c3fb27SDimitry Andric // Include a dummy metadata instance in case of only non constant 50106c3fb27SDimitry Andric // format string usage, This might be an absurd usecase but needs to 50206c3fb27SDimitry Andric // be done for completeness 50306c3fb27SDimitry Andric if (metaD->getNumOperands() == 0) { 50406c3fb27SDimitry Andric MDString *fmtStrArray = 50506c3fb27SDimitry Andric MDString::get(Ctx, "0:0:ffffffff,\"Non const format string\""); 50606c3fb27SDimitry Andric MDNode *myMD = MDNode::get(Ctx, fmtStrArray); 50706c3fb27SDimitry Andric metaD->addOperand(myMD); 50806c3fb27SDimitry Andric } 50906c3fb27SDimitry Andric } 51006c3fb27SDimitry Andric 51106c3fb27SDimitry Andric // Push The printf arguments onto buffer 51206c3fb27SDimitry Andric callBufferedPrintfArgPush(Builder, Args, Ptr, SpecIsCString, StringContents, 51306c3fb27SDimitry Andric IsConstFmtStr); 51406c3fb27SDimitry Andric 51506c3fb27SDimitry Andric // End block, returns -1 on failure 51606c3fb27SDimitry Andric BranchInst::Create(End, ArgPush); 51706c3fb27SDimitry Andric Builder.SetInsertPoint(End); 51806c3fb27SDimitry Andric return Builder.CreateSExt(Builder.CreateNot(Cmp), Int32Ty, "printf_result"); 51906c3fb27SDimitry Andric } 5205ffd83dbSDimitry Andric 5215ffd83dbSDimitry Andric auto Desc = callPrintfBegin(Builder, Builder.getIntN(64, 0)); 5225ffd83dbSDimitry Andric Desc = appendString(Builder, Desc, Fmt, NumOps == 1); 5235ffd83dbSDimitry Andric 5245ffd83dbSDimitry Andric // FIXME: This invokes hostcall once for each argument. We can pack up to 5255ffd83dbSDimitry Andric // seven scalar printf arguments in a single hostcall. See the signature of 5265ffd83dbSDimitry Andric // callAppendArgs(). 5275ffd83dbSDimitry Andric for (unsigned int i = 1; i != NumOps; ++i) { 5285ffd83dbSDimitry Andric bool IsLast = i == NumOps - 1; 5295ffd83dbSDimitry Andric bool IsCString = SpecIsCString.test(i); 5305ffd83dbSDimitry Andric Desc = processArg(Builder, Desc, Args[i], IsCString, IsLast); 5315ffd83dbSDimitry Andric } 5325ffd83dbSDimitry Andric 5335ffd83dbSDimitry Andric return Builder.CreateTrunc(Desc, Builder.getInt32Ty()); 5345ffd83dbSDimitry Andric } 535