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