1ed181efaSSameer Sahasrabuddhe //===- AMDGPUEmitPrintf.cpp -----------------------------------------------===//
2ed181efaSSameer Sahasrabuddhe //
3ed181efaSSameer Sahasrabuddhe // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4ed181efaSSameer Sahasrabuddhe // See https://llvm.org/LICENSE.txt for license information.
5ed181efaSSameer Sahasrabuddhe // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6ed181efaSSameer Sahasrabuddhe //
7ed181efaSSameer Sahasrabuddhe //===----------------------------------------------------------------------===//
8ed181efaSSameer Sahasrabuddhe //
9ed181efaSSameer Sahasrabuddhe // Utility function to lower a printf call into a series of device
10ed181efaSSameer Sahasrabuddhe // library calls on the AMDGPU target.
11ed181efaSSameer Sahasrabuddhe //
12ed181efaSSameer Sahasrabuddhe // WARNING: This file knows about certain library functions. It recognizes them
13ed181efaSSameer Sahasrabuddhe // by name, and hardwires knowledge of their semantics.
14ed181efaSSameer Sahasrabuddhe //
15ed181efaSSameer Sahasrabuddhe //===----------------------------------------------------------------------===//
16ed181efaSSameer Sahasrabuddhe 
17ed181efaSSameer Sahasrabuddhe #include "llvm/Transforms/Utils/AMDGPUEmitPrintf.h"
18ed181efaSSameer Sahasrabuddhe #include "llvm/ADT/SparseBitVector.h"
19ed181efaSSameer Sahasrabuddhe #include "llvm/Analysis/ValueTracking.h"
20ed181efaSSameer Sahasrabuddhe 
21ed181efaSSameer Sahasrabuddhe using namespace llvm;
22ed181efaSSameer Sahasrabuddhe 
23ed181efaSSameer Sahasrabuddhe #define DEBUG_TYPE "amdgpu-emit-printf"
24ed181efaSSameer Sahasrabuddhe 
fitArgInto64Bits(IRBuilder<> & Builder,Value * Arg)25ed181efaSSameer Sahasrabuddhe static Value *fitArgInto64Bits(IRBuilder<> &Builder, Value *Arg) {
26ed181efaSSameer Sahasrabuddhe   auto Int64Ty = Builder.getInt64Ty();
27ed181efaSSameer Sahasrabuddhe   auto Ty = Arg->getType();
28ed181efaSSameer Sahasrabuddhe 
29ed181efaSSameer Sahasrabuddhe   if (auto IntTy = dyn_cast<IntegerType>(Ty)) {
30ed181efaSSameer Sahasrabuddhe     switch (IntTy->getBitWidth()) {
31ed181efaSSameer Sahasrabuddhe     case 32:
32ed181efaSSameer Sahasrabuddhe       return Builder.CreateZExt(Arg, Int64Ty);
33ed181efaSSameer Sahasrabuddhe     case 64:
34ed181efaSSameer Sahasrabuddhe       return Arg;
35ed181efaSSameer Sahasrabuddhe     }
36ed181efaSSameer Sahasrabuddhe   }
37ed181efaSSameer Sahasrabuddhe 
38ed181efaSSameer Sahasrabuddhe   if (Ty->getTypeID() == Type::DoubleTyID) {
39ed181efaSSameer Sahasrabuddhe     return Builder.CreateBitCast(Arg, Int64Ty);
40ed181efaSSameer Sahasrabuddhe   }
41ed181efaSSameer Sahasrabuddhe 
4223a887b0SSimon Pilgrim   if (isa<PointerType>(Ty)) {
43ed181efaSSameer Sahasrabuddhe     return Builder.CreatePtrToInt(Arg, Int64Ty);
44ed181efaSSameer Sahasrabuddhe   }
45ed181efaSSameer Sahasrabuddhe 
46ed181efaSSameer Sahasrabuddhe   llvm_unreachable("unexpected type");
47ed181efaSSameer Sahasrabuddhe }
48ed181efaSSameer Sahasrabuddhe 
callPrintfBegin(IRBuilder<> & Builder,Value * Version)49ed181efaSSameer Sahasrabuddhe static Value *callPrintfBegin(IRBuilder<> &Builder, Value *Version) {
50ed181efaSSameer Sahasrabuddhe   auto Int64Ty = Builder.getInt64Ty();
51ed181efaSSameer Sahasrabuddhe   auto M = Builder.GetInsertBlock()->getModule();
52ed181efaSSameer Sahasrabuddhe   auto Fn = M->getOrInsertFunction("__ockl_printf_begin", Int64Ty, Int64Ty);
53ed181efaSSameer Sahasrabuddhe   return Builder.CreateCall(Fn, Version);
54ed181efaSSameer Sahasrabuddhe }
55ed181efaSSameer Sahasrabuddhe 
callAppendArgs(IRBuilder<> & Builder,Value * Desc,int NumArgs,Value * Arg0,Value * Arg1,Value * Arg2,Value * Arg3,Value * Arg4,Value * Arg5,Value * Arg6,bool IsLast)56ed181efaSSameer Sahasrabuddhe static Value *callAppendArgs(IRBuilder<> &Builder, Value *Desc, int NumArgs,
57ed181efaSSameer Sahasrabuddhe                              Value *Arg0, Value *Arg1, Value *Arg2, Value *Arg3,
58ed181efaSSameer Sahasrabuddhe                              Value *Arg4, Value *Arg5, Value *Arg6,
59ed181efaSSameer Sahasrabuddhe                              bool IsLast) {
60ed181efaSSameer Sahasrabuddhe   auto Int64Ty = Builder.getInt64Ty();
61ed181efaSSameer Sahasrabuddhe   auto Int32Ty = Builder.getInt32Ty();
62ed181efaSSameer Sahasrabuddhe   auto M = Builder.GetInsertBlock()->getModule();
63ed181efaSSameer Sahasrabuddhe   auto Fn = M->getOrInsertFunction("__ockl_printf_append_args", Int64Ty,
64ed181efaSSameer Sahasrabuddhe                                    Int64Ty, Int32Ty, Int64Ty, Int64Ty, Int64Ty,
65ed181efaSSameer Sahasrabuddhe                                    Int64Ty, Int64Ty, Int64Ty, Int64Ty, Int32Ty);
66ed181efaSSameer Sahasrabuddhe   auto IsLastValue = Builder.getInt32(IsLast);
67ed181efaSSameer Sahasrabuddhe   auto NumArgsValue = Builder.getInt32(NumArgs);
68ed181efaSSameer Sahasrabuddhe   return Builder.CreateCall(Fn, {Desc, NumArgsValue, Arg0, Arg1, Arg2, Arg3,
69ed181efaSSameer Sahasrabuddhe                                  Arg4, Arg5, Arg6, IsLastValue});
70ed181efaSSameer Sahasrabuddhe }
71ed181efaSSameer Sahasrabuddhe 
appendArg(IRBuilder<> & Builder,Value * Desc,Value * Arg,bool IsLast)72ed181efaSSameer Sahasrabuddhe static Value *appendArg(IRBuilder<> &Builder, Value *Desc, Value *Arg,
73ed181efaSSameer Sahasrabuddhe                         bool IsLast) {
74ed181efaSSameer Sahasrabuddhe   auto Arg0 = fitArgInto64Bits(Builder, Arg);
75ed181efaSSameer Sahasrabuddhe   auto Zero = Builder.getInt64(0);
76ed181efaSSameer Sahasrabuddhe   return callAppendArgs(Builder, Desc, 1, Arg0, Zero, Zero, Zero, Zero, Zero,
77ed181efaSSameer Sahasrabuddhe                         Zero, IsLast);
78ed181efaSSameer Sahasrabuddhe }
79ed181efaSSameer Sahasrabuddhe 
80ed181efaSSameer Sahasrabuddhe // The device library does not provide strlen, so we build our own loop
81ed181efaSSameer Sahasrabuddhe // here. While we are at it, we also include the terminating null in the length.
getStrlenWithNull(IRBuilder<> & Builder,Value * Str)82ed181efaSSameer Sahasrabuddhe static Value *getStrlenWithNull(IRBuilder<> &Builder, Value *Str) {
83ed181efaSSameer Sahasrabuddhe   auto *Prev = Builder.GetInsertBlock();
84ed181efaSSameer Sahasrabuddhe   Module *M = Prev->getModule();
85ed181efaSSameer Sahasrabuddhe 
86ed181efaSSameer Sahasrabuddhe   auto CharZero = Builder.getInt8(0);
87ed181efaSSameer Sahasrabuddhe   auto One = Builder.getInt64(1);
88ed181efaSSameer Sahasrabuddhe   auto Zero = Builder.getInt64(0);
89ed181efaSSameer Sahasrabuddhe   auto Int64Ty = Builder.getInt64Ty();
90ed181efaSSameer Sahasrabuddhe 
91ed181efaSSameer Sahasrabuddhe   // The length is either zero for a null pointer, or the computed value for an
92ed181efaSSameer Sahasrabuddhe   // actual string. We need a join block for a phi that represents the final
93ed181efaSSameer Sahasrabuddhe   // value.
94ed181efaSSameer Sahasrabuddhe   //
95ed181efaSSameer Sahasrabuddhe   //  Strictly speaking, the zero does not matter since
96ed181efaSSameer Sahasrabuddhe   // __ockl_printf_append_string_n ignores the length if the pointer is null.
97ed181efaSSameer Sahasrabuddhe   BasicBlock *Join = nullptr;
98ed181efaSSameer Sahasrabuddhe   if (Prev->getTerminator()) {
99ed181efaSSameer Sahasrabuddhe     Join = Prev->splitBasicBlock(Builder.GetInsertPoint(),
100ed181efaSSameer Sahasrabuddhe                                  "strlen.join");
101ed181efaSSameer Sahasrabuddhe     Prev->getTerminator()->eraseFromParent();
102ed181efaSSameer Sahasrabuddhe   } else {
103ed181efaSSameer Sahasrabuddhe     Join = BasicBlock::Create(M->getContext(), "strlen.join",
104ed181efaSSameer Sahasrabuddhe                               Prev->getParent());
105ed181efaSSameer Sahasrabuddhe   }
106ed181efaSSameer Sahasrabuddhe   BasicBlock *While =
107ed181efaSSameer Sahasrabuddhe       BasicBlock::Create(M->getContext(), "strlen.while",
108ed181efaSSameer Sahasrabuddhe                          Prev->getParent(), Join);
109ed181efaSSameer Sahasrabuddhe   BasicBlock *WhileDone = BasicBlock::Create(
110ed181efaSSameer Sahasrabuddhe       M->getContext(), "strlen.while.done",
111ed181efaSSameer Sahasrabuddhe       Prev->getParent(), Join);
112ed181efaSSameer Sahasrabuddhe 
113ed181efaSSameer Sahasrabuddhe   // Emit an early return for when the pointer is null.
114ed181efaSSameer Sahasrabuddhe   Builder.SetInsertPoint(Prev);
115ed181efaSSameer Sahasrabuddhe   auto CmpNull =
116ed181efaSSameer Sahasrabuddhe       Builder.CreateICmpEQ(Str, Constant::getNullValue(Str->getType()));
117ed181efaSSameer Sahasrabuddhe   BranchInst::Create(Join, While, CmpNull, Prev);
118ed181efaSSameer Sahasrabuddhe 
119ed181efaSSameer Sahasrabuddhe   // Entry to the while loop.
120ed181efaSSameer Sahasrabuddhe   Builder.SetInsertPoint(While);
121ed181efaSSameer Sahasrabuddhe 
122ed181efaSSameer Sahasrabuddhe   auto PtrPhi = Builder.CreatePHI(Str->getType(), 2);
123ed181efaSSameer Sahasrabuddhe   PtrPhi->addIncoming(Str, Prev);
1242983053dSArthur Eubanks   auto PtrNext = Builder.CreateGEP(Builder.getInt8Ty(), PtrPhi, One);
125ed181efaSSameer Sahasrabuddhe   PtrPhi->addIncoming(PtrNext, While);
126ed181efaSSameer Sahasrabuddhe 
127ed181efaSSameer Sahasrabuddhe   // Condition for the while loop.
12846354bacSNikita Popov   auto Data = Builder.CreateLoad(Builder.getInt8Ty(), PtrPhi);
129ed181efaSSameer Sahasrabuddhe   auto Cmp = Builder.CreateICmpEQ(Data, CharZero);
130ed181efaSSameer Sahasrabuddhe   Builder.CreateCondBr(Cmp, WhileDone, While);
131ed181efaSSameer Sahasrabuddhe 
132ed181efaSSameer Sahasrabuddhe   // Add one to the computed length.
133ed181efaSSameer Sahasrabuddhe   Builder.SetInsertPoint(WhileDone, WhileDone->begin());
134ed181efaSSameer Sahasrabuddhe   auto Begin = Builder.CreatePtrToInt(Str, Int64Ty);
135ed181efaSSameer Sahasrabuddhe   auto End = Builder.CreatePtrToInt(PtrPhi, Int64Ty);
136ed181efaSSameer Sahasrabuddhe   auto Len = Builder.CreateSub(End, Begin);
137ed181efaSSameer Sahasrabuddhe   Len = Builder.CreateAdd(Len, One);
138ed181efaSSameer Sahasrabuddhe 
139ed181efaSSameer Sahasrabuddhe   // Final join.
140ed181efaSSameer Sahasrabuddhe   BranchInst::Create(Join, WhileDone);
141ed181efaSSameer Sahasrabuddhe   Builder.SetInsertPoint(Join, Join->begin());
142ed181efaSSameer Sahasrabuddhe   auto LenPhi = Builder.CreatePHI(Len->getType(), 2);
143ed181efaSSameer Sahasrabuddhe   LenPhi->addIncoming(Len, WhileDone);
144ed181efaSSameer Sahasrabuddhe   LenPhi->addIncoming(Zero, Prev);
145ed181efaSSameer Sahasrabuddhe 
146ed181efaSSameer Sahasrabuddhe   return LenPhi;
147ed181efaSSameer Sahasrabuddhe }
148ed181efaSSameer Sahasrabuddhe 
callAppendStringN(IRBuilder<> & Builder,Value * Desc,Value * Str,Value * Length,bool isLast)149ed181efaSSameer Sahasrabuddhe static Value *callAppendStringN(IRBuilder<> &Builder, Value *Desc, Value *Str,
150ed181efaSSameer Sahasrabuddhe                                 Value *Length, bool isLast) {
151ed181efaSSameer Sahasrabuddhe   auto Int64Ty = Builder.getInt64Ty();
152ed181efaSSameer Sahasrabuddhe   auto CharPtrTy = Builder.getInt8PtrTy();
153ed181efaSSameer Sahasrabuddhe   auto Int32Ty = Builder.getInt32Ty();
154ed181efaSSameer Sahasrabuddhe   auto M = Builder.GetInsertBlock()->getModule();
155ed181efaSSameer Sahasrabuddhe   auto Fn = M->getOrInsertFunction("__ockl_printf_append_string_n", Int64Ty,
156ed181efaSSameer Sahasrabuddhe                                    Int64Ty, CharPtrTy, Int64Ty, Int32Ty);
157ed181efaSSameer Sahasrabuddhe   auto IsLastInt32 = Builder.getInt32(isLast);
158ed181efaSSameer Sahasrabuddhe   return Builder.CreateCall(Fn, {Desc, Str, Length, IsLastInt32});
159ed181efaSSameer Sahasrabuddhe }
160ed181efaSSameer Sahasrabuddhe 
appendString(IRBuilder<> & Builder,Value * Desc,Value * Arg,bool IsLast)161ed181efaSSameer Sahasrabuddhe static Value *appendString(IRBuilder<> &Builder, Value *Desc, Value *Arg,
162ed181efaSSameer Sahasrabuddhe                            bool IsLast) {
163*de8867a0SNikita Popov   Arg = Builder.CreateBitCast(
164*de8867a0SNikita Popov       Arg, Builder.getInt8PtrTy(Arg->getType()->getPointerAddressSpace()));
165ed181efaSSameer Sahasrabuddhe   auto Length = getStrlenWithNull(Builder, Arg);
166ed181efaSSameer Sahasrabuddhe   return callAppendStringN(Builder, Desc, Arg, Length, IsLast);
167ed181efaSSameer Sahasrabuddhe }
168ed181efaSSameer Sahasrabuddhe 
processArg(IRBuilder<> & Builder,Value * Desc,Value * Arg,bool SpecIsCString,bool IsLast)169ed181efaSSameer Sahasrabuddhe static Value *processArg(IRBuilder<> &Builder, Value *Desc, Value *Arg,
170ed181efaSSameer Sahasrabuddhe                          bool SpecIsCString, bool IsLast) {
171*de8867a0SNikita Popov   if (SpecIsCString && isa<PointerType>(Arg->getType())) {
172ed181efaSSameer Sahasrabuddhe     return appendString(Builder, Desc, Arg, IsLast);
173ed181efaSSameer Sahasrabuddhe   }
174ed181efaSSameer Sahasrabuddhe   // If the format specifies a string but the argument is not, the frontend will
175ed181efaSSameer Sahasrabuddhe   // have printed a warning. We just rely on undefined behaviour and send the
176ed181efaSSameer Sahasrabuddhe   // argument anyway.
177ed181efaSSameer Sahasrabuddhe   return appendArg(Builder, Desc, Arg, IsLast);
178ed181efaSSameer Sahasrabuddhe }
179ed181efaSSameer Sahasrabuddhe 
180ed181efaSSameer Sahasrabuddhe // Scan the format string to locate all specifiers, and mark the ones that
181ed181efaSSameer Sahasrabuddhe // specify a string, i.e, the "%s" specifier with optional '*' characters.
locateCStrings(SparseBitVector<8> & BV,Value * Fmt)182ed181efaSSameer Sahasrabuddhe static void locateCStrings(SparseBitVector<8> &BV, Value *Fmt) {
183ed181efaSSameer Sahasrabuddhe   StringRef Str;
184ed181efaSSameer Sahasrabuddhe   if (!getConstantStringInfo(Fmt, Str) || Str.empty())
185ed181efaSSameer Sahasrabuddhe     return;
186ed181efaSSameer Sahasrabuddhe 
187ed181efaSSameer Sahasrabuddhe   static const char ConvSpecifiers[] = "diouxXfFeEgGaAcspn";
188ed181efaSSameer Sahasrabuddhe   size_t SpecPos = 0;
189ed181efaSSameer Sahasrabuddhe   // Skip the first argument, the format string.
190ed181efaSSameer Sahasrabuddhe   unsigned ArgIdx = 1;
191ed181efaSSameer Sahasrabuddhe 
192ed181efaSSameer Sahasrabuddhe   while ((SpecPos = Str.find_first_of('%', SpecPos)) != StringRef::npos) {
193ed181efaSSameer Sahasrabuddhe     if (Str[SpecPos + 1] == '%') {
194ed181efaSSameer Sahasrabuddhe       SpecPos += 2;
195ed181efaSSameer Sahasrabuddhe       continue;
196ed181efaSSameer Sahasrabuddhe     }
197ed181efaSSameer Sahasrabuddhe     auto SpecEnd = Str.find_first_of(ConvSpecifiers, SpecPos);
198ed181efaSSameer Sahasrabuddhe     if (SpecEnd == StringRef::npos)
199ed181efaSSameer Sahasrabuddhe       return;
200ed181efaSSameer Sahasrabuddhe     auto Spec = Str.slice(SpecPos, SpecEnd + 1);
201ed181efaSSameer Sahasrabuddhe     ArgIdx += Spec.count('*');
202ed181efaSSameer Sahasrabuddhe     if (Str[SpecEnd] == 's') {
203ed181efaSSameer Sahasrabuddhe       BV.set(ArgIdx);
204ed181efaSSameer Sahasrabuddhe     }
205ed181efaSSameer Sahasrabuddhe     SpecPos = SpecEnd + 1;
206ed181efaSSameer Sahasrabuddhe     ++ArgIdx;
207ed181efaSSameer Sahasrabuddhe   }
208ed181efaSSameer Sahasrabuddhe }
209ed181efaSSameer Sahasrabuddhe 
emitAMDGPUPrintfCall(IRBuilder<> & Builder,ArrayRef<Value * > Args)210ed181efaSSameer Sahasrabuddhe Value *llvm::emitAMDGPUPrintfCall(IRBuilder<> &Builder,
211ed181efaSSameer Sahasrabuddhe                                   ArrayRef<Value *> Args) {
212ed181efaSSameer Sahasrabuddhe   auto NumOps = Args.size();
213ed181efaSSameer Sahasrabuddhe   assert(NumOps >= 1);
214ed181efaSSameer Sahasrabuddhe 
215ed181efaSSameer Sahasrabuddhe   auto Fmt = Args[0];
216ed181efaSSameer Sahasrabuddhe   SparseBitVector<8> SpecIsCString;
217ed181efaSSameer Sahasrabuddhe   locateCStrings(SpecIsCString, Fmt);
218ed181efaSSameer Sahasrabuddhe 
219ed181efaSSameer Sahasrabuddhe   auto Desc = callPrintfBegin(Builder, Builder.getIntN(64, 0));
220ed181efaSSameer Sahasrabuddhe   Desc = appendString(Builder, Desc, Fmt, NumOps == 1);
221ed181efaSSameer Sahasrabuddhe 
222ed181efaSSameer Sahasrabuddhe   // FIXME: This invokes hostcall once for each argument. We can pack up to
223ed181efaSSameer Sahasrabuddhe   // seven scalar printf arguments in a single hostcall. See the signature of
224ed181efaSSameer Sahasrabuddhe   // callAppendArgs().
225ed181efaSSameer Sahasrabuddhe   for (unsigned int i = 1; i != NumOps; ++i) {
226ed181efaSSameer Sahasrabuddhe     bool IsLast = i == NumOps - 1;
227ed181efaSSameer Sahasrabuddhe     bool IsCString = SpecIsCString.test(i);
228ed181efaSSameer Sahasrabuddhe     Desc = processArg(Builder, Desc, Args[i], IsCString, IsLast);
229ed181efaSSameer Sahasrabuddhe   }
230ed181efaSSameer Sahasrabuddhe 
231ed181efaSSameer Sahasrabuddhe   return Builder.CreateTrunc(Desc, Builder.getInt32Ty());
232ed181efaSSameer Sahasrabuddhe }
233