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