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" 195ffd83dbSDimitry Andric #include "llvm/Analysis/ValueTracking.h" 205ffd83dbSDimitry Andric 215ffd83dbSDimitry Andric using namespace llvm; 225ffd83dbSDimitry Andric 235ffd83dbSDimitry Andric #define DEBUG_TYPE "amdgpu-emit-printf" 245ffd83dbSDimitry Andric 255ffd83dbSDimitry Andric static bool isCString(const Value *Arg) { 265ffd83dbSDimitry Andric auto Ty = Arg->getType(); 275ffd83dbSDimitry Andric auto PtrTy = dyn_cast<PointerType>(Ty); 285ffd83dbSDimitry Andric if (!PtrTy) 295ffd83dbSDimitry Andric return false; 305ffd83dbSDimitry Andric 315ffd83dbSDimitry Andric auto IntTy = dyn_cast<IntegerType>(PtrTy->getElementType()); 325ffd83dbSDimitry Andric if (!IntTy) 335ffd83dbSDimitry Andric return false; 345ffd83dbSDimitry Andric 355ffd83dbSDimitry Andric return IntTy->getBitWidth() == 8; 365ffd83dbSDimitry Andric } 375ffd83dbSDimitry Andric 385ffd83dbSDimitry Andric static Value *fitArgInto64Bits(IRBuilder<> &Builder, Value *Arg) { 395ffd83dbSDimitry Andric auto Int64Ty = Builder.getInt64Ty(); 405ffd83dbSDimitry Andric auto Ty = Arg->getType(); 415ffd83dbSDimitry Andric 425ffd83dbSDimitry Andric if (auto IntTy = dyn_cast<IntegerType>(Ty)) { 435ffd83dbSDimitry Andric switch (IntTy->getBitWidth()) { 445ffd83dbSDimitry Andric case 32: 455ffd83dbSDimitry Andric return Builder.CreateZExt(Arg, Int64Ty); 465ffd83dbSDimitry Andric case 64: 475ffd83dbSDimitry Andric return Arg; 485ffd83dbSDimitry Andric } 495ffd83dbSDimitry Andric } 505ffd83dbSDimitry Andric 515ffd83dbSDimitry Andric if (Ty->getTypeID() == Type::DoubleTyID) { 525ffd83dbSDimitry Andric return Builder.CreateBitCast(Arg, Int64Ty); 535ffd83dbSDimitry Andric } 545ffd83dbSDimitry Andric 555ffd83dbSDimitry Andric if (isa<PointerType>(Ty)) { 565ffd83dbSDimitry Andric return Builder.CreatePtrToInt(Arg, Int64Ty); 575ffd83dbSDimitry Andric } 585ffd83dbSDimitry Andric 595ffd83dbSDimitry Andric llvm_unreachable("unexpected type"); 605ffd83dbSDimitry Andric } 615ffd83dbSDimitry Andric 625ffd83dbSDimitry Andric static Value *callPrintfBegin(IRBuilder<> &Builder, Value *Version) { 635ffd83dbSDimitry Andric auto Int64Ty = Builder.getInt64Ty(); 645ffd83dbSDimitry Andric auto M = Builder.GetInsertBlock()->getModule(); 655ffd83dbSDimitry Andric auto Fn = M->getOrInsertFunction("__ockl_printf_begin", Int64Ty, Int64Ty); 665ffd83dbSDimitry Andric return Builder.CreateCall(Fn, Version); 675ffd83dbSDimitry Andric } 685ffd83dbSDimitry Andric 695ffd83dbSDimitry Andric static Value *callAppendArgs(IRBuilder<> &Builder, Value *Desc, int NumArgs, 705ffd83dbSDimitry Andric Value *Arg0, Value *Arg1, Value *Arg2, Value *Arg3, 715ffd83dbSDimitry Andric Value *Arg4, Value *Arg5, Value *Arg6, 725ffd83dbSDimitry Andric bool IsLast) { 735ffd83dbSDimitry Andric auto Int64Ty = Builder.getInt64Ty(); 745ffd83dbSDimitry Andric auto Int32Ty = Builder.getInt32Ty(); 755ffd83dbSDimitry Andric auto M = Builder.GetInsertBlock()->getModule(); 765ffd83dbSDimitry Andric auto Fn = M->getOrInsertFunction("__ockl_printf_append_args", Int64Ty, 775ffd83dbSDimitry Andric Int64Ty, Int32Ty, Int64Ty, Int64Ty, Int64Ty, 785ffd83dbSDimitry Andric Int64Ty, Int64Ty, Int64Ty, Int64Ty, Int32Ty); 795ffd83dbSDimitry Andric auto IsLastValue = Builder.getInt32(IsLast); 805ffd83dbSDimitry Andric auto NumArgsValue = Builder.getInt32(NumArgs); 815ffd83dbSDimitry Andric return Builder.CreateCall(Fn, {Desc, NumArgsValue, Arg0, Arg1, Arg2, Arg3, 825ffd83dbSDimitry Andric Arg4, Arg5, Arg6, IsLastValue}); 835ffd83dbSDimitry Andric } 845ffd83dbSDimitry Andric 855ffd83dbSDimitry Andric static Value *appendArg(IRBuilder<> &Builder, Value *Desc, Value *Arg, 865ffd83dbSDimitry Andric bool IsLast) { 875ffd83dbSDimitry Andric auto Arg0 = fitArgInto64Bits(Builder, Arg); 885ffd83dbSDimitry Andric auto Zero = Builder.getInt64(0); 895ffd83dbSDimitry Andric return callAppendArgs(Builder, Desc, 1, Arg0, Zero, Zero, Zero, Zero, Zero, 905ffd83dbSDimitry Andric Zero, IsLast); 915ffd83dbSDimitry Andric } 925ffd83dbSDimitry Andric 935ffd83dbSDimitry Andric // The device library does not provide strlen, so we build our own loop 945ffd83dbSDimitry Andric // here. While we are at it, we also include the terminating null in the length. 955ffd83dbSDimitry Andric static Value *getStrlenWithNull(IRBuilder<> &Builder, Value *Str) { 965ffd83dbSDimitry Andric auto *Prev = Builder.GetInsertBlock(); 975ffd83dbSDimitry Andric Module *M = Prev->getModule(); 985ffd83dbSDimitry Andric 995ffd83dbSDimitry Andric auto CharZero = Builder.getInt8(0); 1005ffd83dbSDimitry Andric auto One = Builder.getInt64(1); 1015ffd83dbSDimitry Andric auto Zero = Builder.getInt64(0); 1025ffd83dbSDimitry Andric auto Int64Ty = Builder.getInt64Ty(); 1035ffd83dbSDimitry Andric 1045ffd83dbSDimitry Andric // The length is either zero for a null pointer, or the computed value for an 1055ffd83dbSDimitry Andric // actual string. We need a join block for a phi that represents the final 1065ffd83dbSDimitry Andric // value. 1075ffd83dbSDimitry Andric // 1085ffd83dbSDimitry Andric // Strictly speaking, the zero does not matter since 1095ffd83dbSDimitry Andric // __ockl_printf_append_string_n ignores the length if the pointer is null. 1105ffd83dbSDimitry Andric BasicBlock *Join = nullptr; 1115ffd83dbSDimitry Andric if (Prev->getTerminator()) { 1125ffd83dbSDimitry Andric Join = Prev->splitBasicBlock(Builder.GetInsertPoint(), 1135ffd83dbSDimitry Andric "strlen.join"); 1145ffd83dbSDimitry Andric Prev->getTerminator()->eraseFromParent(); 1155ffd83dbSDimitry Andric } else { 1165ffd83dbSDimitry Andric Join = BasicBlock::Create(M->getContext(), "strlen.join", 1175ffd83dbSDimitry Andric Prev->getParent()); 1185ffd83dbSDimitry Andric } 1195ffd83dbSDimitry Andric BasicBlock *While = 1205ffd83dbSDimitry Andric BasicBlock::Create(M->getContext(), "strlen.while", 1215ffd83dbSDimitry Andric Prev->getParent(), Join); 1225ffd83dbSDimitry Andric BasicBlock *WhileDone = BasicBlock::Create( 1235ffd83dbSDimitry Andric M->getContext(), "strlen.while.done", 1245ffd83dbSDimitry Andric Prev->getParent(), Join); 1255ffd83dbSDimitry Andric 1265ffd83dbSDimitry Andric // Emit an early return for when the pointer is null. 1275ffd83dbSDimitry Andric Builder.SetInsertPoint(Prev); 1285ffd83dbSDimitry Andric auto CmpNull = 1295ffd83dbSDimitry Andric Builder.CreateICmpEQ(Str, Constant::getNullValue(Str->getType())); 1305ffd83dbSDimitry Andric BranchInst::Create(Join, While, CmpNull, Prev); 1315ffd83dbSDimitry Andric 1325ffd83dbSDimitry Andric // Entry to the while loop. 1335ffd83dbSDimitry Andric Builder.SetInsertPoint(While); 1345ffd83dbSDimitry Andric 1355ffd83dbSDimitry Andric auto PtrPhi = Builder.CreatePHI(Str->getType(), 2); 1365ffd83dbSDimitry Andric PtrPhi->addIncoming(Str, Prev); 137*fe6060f1SDimitry Andric auto PtrNext = Builder.CreateGEP(Builder.getInt8Ty(), PtrPhi, One); 1385ffd83dbSDimitry Andric PtrPhi->addIncoming(PtrNext, While); 1395ffd83dbSDimitry Andric 1405ffd83dbSDimitry Andric // Condition for the while loop. 141*fe6060f1SDimitry Andric auto Data = Builder.CreateLoad(Builder.getInt8Ty(), PtrPhi); 1425ffd83dbSDimitry Andric auto Cmp = Builder.CreateICmpEQ(Data, CharZero); 1435ffd83dbSDimitry Andric Builder.CreateCondBr(Cmp, WhileDone, While); 1445ffd83dbSDimitry Andric 1455ffd83dbSDimitry Andric // Add one to the computed length. 1465ffd83dbSDimitry Andric Builder.SetInsertPoint(WhileDone, WhileDone->begin()); 1475ffd83dbSDimitry Andric auto Begin = Builder.CreatePtrToInt(Str, Int64Ty); 1485ffd83dbSDimitry Andric auto End = Builder.CreatePtrToInt(PtrPhi, Int64Ty); 1495ffd83dbSDimitry Andric auto Len = Builder.CreateSub(End, Begin); 1505ffd83dbSDimitry Andric Len = Builder.CreateAdd(Len, One); 1515ffd83dbSDimitry Andric 1525ffd83dbSDimitry Andric // Final join. 1535ffd83dbSDimitry Andric BranchInst::Create(Join, WhileDone); 1545ffd83dbSDimitry Andric Builder.SetInsertPoint(Join, Join->begin()); 1555ffd83dbSDimitry Andric auto LenPhi = Builder.CreatePHI(Len->getType(), 2); 1565ffd83dbSDimitry Andric LenPhi->addIncoming(Len, WhileDone); 1575ffd83dbSDimitry Andric LenPhi->addIncoming(Zero, Prev); 1585ffd83dbSDimitry Andric 1595ffd83dbSDimitry Andric return LenPhi; 1605ffd83dbSDimitry Andric } 1615ffd83dbSDimitry Andric 1625ffd83dbSDimitry Andric static Value *callAppendStringN(IRBuilder<> &Builder, Value *Desc, Value *Str, 1635ffd83dbSDimitry Andric Value *Length, bool isLast) { 1645ffd83dbSDimitry Andric auto Int64Ty = Builder.getInt64Ty(); 1655ffd83dbSDimitry Andric auto CharPtrTy = Builder.getInt8PtrTy(); 1665ffd83dbSDimitry Andric auto Int32Ty = Builder.getInt32Ty(); 1675ffd83dbSDimitry Andric auto M = Builder.GetInsertBlock()->getModule(); 1685ffd83dbSDimitry Andric auto Fn = M->getOrInsertFunction("__ockl_printf_append_string_n", Int64Ty, 1695ffd83dbSDimitry Andric Int64Ty, CharPtrTy, Int64Ty, Int32Ty); 1705ffd83dbSDimitry Andric auto IsLastInt32 = Builder.getInt32(isLast); 1715ffd83dbSDimitry Andric return Builder.CreateCall(Fn, {Desc, Str, Length, IsLastInt32}); 1725ffd83dbSDimitry Andric } 1735ffd83dbSDimitry Andric 1745ffd83dbSDimitry Andric static Value *appendString(IRBuilder<> &Builder, Value *Desc, Value *Arg, 1755ffd83dbSDimitry Andric bool IsLast) { 1765ffd83dbSDimitry Andric auto Length = getStrlenWithNull(Builder, Arg); 1775ffd83dbSDimitry Andric return callAppendStringN(Builder, Desc, Arg, Length, IsLast); 1785ffd83dbSDimitry Andric } 1795ffd83dbSDimitry Andric 1805ffd83dbSDimitry Andric static Value *processArg(IRBuilder<> &Builder, Value *Desc, Value *Arg, 1815ffd83dbSDimitry Andric bool SpecIsCString, bool IsLast) { 1825ffd83dbSDimitry Andric if (SpecIsCString && isCString(Arg)) { 1835ffd83dbSDimitry Andric return appendString(Builder, Desc, Arg, IsLast); 1845ffd83dbSDimitry Andric } 1855ffd83dbSDimitry Andric // If the format specifies a string but the argument is not, the frontend will 1865ffd83dbSDimitry Andric // have printed a warning. We just rely on undefined behaviour and send the 1875ffd83dbSDimitry Andric // argument anyway. 1885ffd83dbSDimitry Andric return appendArg(Builder, Desc, Arg, IsLast); 1895ffd83dbSDimitry Andric } 1905ffd83dbSDimitry Andric 1915ffd83dbSDimitry Andric // Scan the format string to locate all specifiers, and mark the ones that 1925ffd83dbSDimitry Andric // specify a string, i.e, the "%s" specifier with optional '*' characters. 1935ffd83dbSDimitry Andric static void locateCStrings(SparseBitVector<8> &BV, Value *Fmt) { 1945ffd83dbSDimitry Andric StringRef Str; 1955ffd83dbSDimitry Andric if (!getConstantStringInfo(Fmt, Str) || Str.empty()) 1965ffd83dbSDimitry Andric return; 1975ffd83dbSDimitry Andric 1985ffd83dbSDimitry Andric static const char ConvSpecifiers[] = "diouxXfFeEgGaAcspn"; 1995ffd83dbSDimitry Andric size_t SpecPos = 0; 2005ffd83dbSDimitry Andric // Skip the first argument, the format string. 2015ffd83dbSDimitry Andric unsigned ArgIdx = 1; 2025ffd83dbSDimitry Andric 2035ffd83dbSDimitry Andric while ((SpecPos = Str.find_first_of('%', SpecPos)) != StringRef::npos) { 2045ffd83dbSDimitry Andric if (Str[SpecPos + 1] == '%') { 2055ffd83dbSDimitry Andric SpecPos += 2; 2065ffd83dbSDimitry Andric continue; 2075ffd83dbSDimitry Andric } 2085ffd83dbSDimitry Andric auto SpecEnd = Str.find_first_of(ConvSpecifiers, SpecPos); 2095ffd83dbSDimitry Andric if (SpecEnd == StringRef::npos) 2105ffd83dbSDimitry Andric return; 2115ffd83dbSDimitry Andric auto Spec = Str.slice(SpecPos, SpecEnd + 1); 2125ffd83dbSDimitry Andric ArgIdx += Spec.count('*'); 2135ffd83dbSDimitry Andric if (Str[SpecEnd] == 's') { 2145ffd83dbSDimitry Andric BV.set(ArgIdx); 2155ffd83dbSDimitry Andric } 2165ffd83dbSDimitry Andric SpecPos = SpecEnd + 1; 2175ffd83dbSDimitry Andric ++ArgIdx; 2185ffd83dbSDimitry Andric } 2195ffd83dbSDimitry Andric } 2205ffd83dbSDimitry Andric 2215ffd83dbSDimitry Andric Value *llvm::emitAMDGPUPrintfCall(IRBuilder<> &Builder, 2225ffd83dbSDimitry Andric ArrayRef<Value *> Args) { 2235ffd83dbSDimitry Andric auto NumOps = Args.size(); 2245ffd83dbSDimitry Andric assert(NumOps >= 1); 2255ffd83dbSDimitry Andric 2265ffd83dbSDimitry Andric auto Fmt = Args[0]; 2275ffd83dbSDimitry Andric SparseBitVector<8> SpecIsCString; 2285ffd83dbSDimitry Andric locateCStrings(SpecIsCString, Fmt); 2295ffd83dbSDimitry Andric 2305ffd83dbSDimitry Andric auto Desc = callPrintfBegin(Builder, Builder.getIntN(64, 0)); 2315ffd83dbSDimitry Andric Desc = appendString(Builder, Desc, Fmt, NumOps == 1); 2325ffd83dbSDimitry Andric 2335ffd83dbSDimitry Andric // FIXME: This invokes hostcall once for each argument. We can pack up to 2345ffd83dbSDimitry Andric // seven scalar printf arguments in a single hostcall. See the signature of 2355ffd83dbSDimitry Andric // callAppendArgs(). 2365ffd83dbSDimitry Andric for (unsigned int i = 1; i != NumOps; ++i) { 2375ffd83dbSDimitry Andric bool IsLast = i == NumOps - 1; 2385ffd83dbSDimitry Andric bool IsCString = SpecIsCString.test(i); 2395ffd83dbSDimitry Andric Desc = processArg(Builder, Desc, Args[i], IsCString, IsLast); 2405ffd83dbSDimitry Andric } 2415ffd83dbSDimitry Andric 2425ffd83dbSDimitry Andric return Builder.CreateTrunc(Desc, Builder.getInt32Ty()); 2435ffd83dbSDimitry Andric } 244