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