1 //===--- RuntimeDebugBuilder.cpp - Helper to insert prints into LLVM-IR ---===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 //===----------------------------------------------------------------------===//
10 
11 #include "polly/CodeGen/RuntimeDebugBuilder.h"
12 #include "llvm/IR/IntrinsicsNVPTX.h"
13 #include "llvm/IR/Module.h"
14 #include <string>
15 #include <vector>
16 
17 using namespace llvm;
18 using namespace polly;
19 
getVPrintF(PollyIRBuilder & Builder)20 Function *RuntimeDebugBuilder::getVPrintF(PollyIRBuilder &Builder) {
21   Module *M = Builder.GetInsertBlock()->getParent()->getParent();
22   const char *Name = "vprintf";
23   Function *F = M->getFunction(Name);
24 
25   if (!F) {
26     GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage;
27     FunctionType *Ty = FunctionType::get(
28         Builder.getInt32Ty(), {Builder.getInt8PtrTy(), Builder.getInt8PtrTy()},
29         false);
30     F = Function::Create(Ty, Linkage, Name, M);
31   }
32 
33   return F;
34 }
35 
getAddressSpaceCast(PollyIRBuilder & Builder,unsigned Src,unsigned Dst,unsigned SrcBits,unsigned DstBits)36 Function *RuntimeDebugBuilder::getAddressSpaceCast(PollyIRBuilder &Builder,
37                                                    unsigned Src, unsigned Dst,
38                                                    unsigned SrcBits,
39                                                    unsigned DstBits) {
40   Module *M = Builder.GetInsertBlock()->getParent()->getParent();
41   auto Name = std::string("llvm.nvvm.ptr.constant.to.gen.p") +
42               std::to_string(Dst) + "i" + std::to_string(DstBits) + ".p" +
43               std::to_string(Src) + "i" + std::to_string(SrcBits);
44   Function *F = M->getFunction(Name);
45 
46   if (!F) {
47     GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage;
48     FunctionType *Ty = FunctionType::get(
49         PointerType::get(Builder.getIntNTy(DstBits), Dst),
50         PointerType::get(Builder.getIntNTy(SrcBits), Src), false);
51     F = Function::Create(Ty, Linkage, Name, M);
52   }
53 
54   return F;
55 }
56 
57 std::vector<Value *>
getGPUThreadIdentifiers(PollyIRBuilder & Builder)58 RuntimeDebugBuilder::getGPUThreadIdentifiers(PollyIRBuilder &Builder) {
59   std::vector<Value *> Identifiers;
60 
61   auto M = Builder.GetInsertBlock()->getParent()->getParent();
62 
63   std::vector<Function *> BlockIDs = {
64       Intrinsic::getDeclaration(M, Intrinsic::nvvm_read_ptx_sreg_ctaid_x),
65       Intrinsic::getDeclaration(M, Intrinsic::nvvm_read_ptx_sreg_ctaid_y),
66       Intrinsic::getDeclaration(M, Intrinsic::nvvm_read_ptx_sreg_ctaid_z),
67   };
68 
69   Identifiers.push_back(Builder.CreateGlobalStringPtr("> block-id: ", "", 4));
70   for (auto GetID : BlockIDs) {
71     Value *Id = Builder.CreateCall(GetID, {});
72     Id = Builder.CreateIntCast(Id, Builder.getInt64Ty(), false);
73     Identifiers.push_back(Id);
74     Identifiers.push_back(Builder.CreateGlobalStringPtr(" ", "", 4));
75   }
76 
77   Identifiers.push_back(Builder.CreateGlobalStringPtr("| ", "", 4));
78 
79   std::vector<Function *> ThreadIDs = {
80       Intrinsic::getDeclaration(M, Intrinsic::nvvm_read_ptx_sreg_tid_x),
81       Intrinsic::getDeclaration(M, Intrinsic::nvvm_read_ptx_sreg_tid_y),
82       Intrinsic::getDeclaration(M, Intrinsic::nvvm_read_ptx_sreg_tid_z),
83   };
84 
85   Identifiers.push_back(Builder.CreateGlobalStringPtr("thread-id: ", "", 4));
86   for (auto GetId : ThreadIDs) {
87     Value *Id = Builder.CreateCall(GetId, {});
88     Id = Builder.CreateIntCast(Id, Builder.getInt64Ty(), false);
89     Identifiers.push_back(Id);
90     Identifiers.push_back(Builder.CreateGlobalStringPtr(" ", "", 4));
91   }
92 
93   return Identifiers;
94 }
95 
createPrinter(PollyIRBuilder & Builder,bool IsGPU,ArrayRef<Value * > Values)96 void RuntimeDebugBuilder::createPrinter(PollyIRBuilder &Builder, bool IsGPU,
97                                         ArrayRef<Value *> Values) {
98   if (IsGPU)
99     createGPUPrinterT(Builder, Values);
100   else
101     createCPUPrinterT(Builder, Values);
102 }
103 
isPrintable(Type * Ty)104 bool RuntimeDebugBuilder::isPrintable(Type *Ty) {
105   if (Ty->isFloatingPointTy())
106     return true;
107 
108   if (Ty->isIntegerTy())
109     return Ty->getIntegerBitWidth() <= 64;
110 
111   if (isa<PointerType>(Ty))
112     return true;
113 
114   return false;
115 }
116 
117 static std::tuple<std::string, std::vector<Value *>>
prepareValuesForPrinting(PollyIRBuilder & Builder,ArrayRef<Value * > Values)118 prepareValuesForPrinting(PollyIRBuilder &Builder, ArrayRef<Value *> Values) {
119   std::string FormatString;
120   std::vector<Value *> ValuesToPrint;
121 
122   for (auto Val : Values) {
123     Type *Ty = Val->getType();
124 
125     if (Ty->isFloatingPointTy()) {
126       if (!Ty->isDoubleTy())
127         Val = Builder.CreateFPExt(Val, Builder.getDoubleTy());
128     } else if (Ty->isIntegerTy()) {
129       if (Ty->getIntegerBitWidth() < 64)
130         Val = Builder.CreateSExt(Val, Builder.getInt64Ty());
131       else
132         assert(Ty->getIntegerBitWidth() &&
133                "Integer types larger 64 bit not supported");
134     } else if (isa<PointerType>(Ty)) {
135       if (Ty->getPointerElementType() == Builder.getInt8Ty() &&
136           Ty->getPointerAddressSpace() == 4) {
137         Val = Builder.CreateGEP(Val, Builder.getInt64(0));
138       } else {
139         Val = Builder.CreatePtrToInt(Val, Builder.getInt64Ty());
140       }
141     } else {
142       llvm_unreachable("Unknown type");
143     }
144 
145     Ty = Val->getType();
146 
147     if (Ty->isFloatingPointTy())
148       FormatString += "%f";
149     else if (Ty->isIntegerTy())
150       FormatString += "%ld";
151     else
152       FormatString += "%s";
153 
154     ValuesToPrint.push_back(Val);
155   }
156 
157   return std::make_tuple(FormatString, ValuesToPrint);
158 }
159 
createCPUPrinterT(PollyIRBuilder & Builder,ArrayRef<Value * > Values)160 void RuntimeDebugBuilder::createCPUPrinterT(PollyIRBuilder &Builder,
161                                             ArrayRef<Value *> Values) {
162 
163   std::string FormatString;
164   std::vector<Value *> ValuesToPrint;
165 
166   std::tie(FormatString, ValuesToPrint) =
167       prepareValuesForPrinting(Builder, Values);
168 
169   createPrintF(Builder, FormatString, ValuesToPrint);
170   createFlush(Builder);
171 }
172 
createGPUPrinterT(PollyIRBuilder & Builder,ArrayRef<Value * > Values)173 void RuntimeDebugBuilder::createGPUPrinterT(PollyIRBuilder &Builder,
174                                             ArrayRef<Value *> Values) {
175   std::string str;
176 
177   auto *Zero = Builder.getInt64(0);
178 
179   auto ToPrint = getGPUThreadIdentifiers(Builder);
180 
181   ToPrint.push_back(Builder.CreateGlobalStringPtr("\n  ", "", 4));
182   ToPrint.insert(ToPrint.end(), Values.begin(), Values.end());
183 
184   const DataLayout &DL = Builder.GetInsertBlock()->getModule()->getDataLayout();
185 
186   // Allocate print buffer (assuming 2*32 bit per element)
187   auto T = ArrayType::get(Builder.getInt32Ty(), ToPrint.size() * 2);
188   Value *Data = new AllocaInst(
189       T, DL.getAllocaAddrSpace(), "polly.vprint.buffer",
190       &Builder.GetInsertBlock()->getParent()->getEntryBlock().front());
191   auto *DataPtr = Builder.CreateGEP(Data, {Zero, Zero});
192 
193   int Offset = 0;
194   for (auto Val : ToPrint) {
195     auto Ptr = Builder.CreateGEP(DataPtr, Builder.getInt64(Offset));
196     Type *Ty = Val->getType();
197 
198     if (Ty->isFloatingPointTy()) {
199       if (!Ty->isDoubleTy())
200         Val = Builder.CreateFPExt(Val, Builder.getDoubleTy());
201     } else if (Ty->isIntegerTy()) {
202       if (Ty->getIntegerBitWidth() < 64) {
203         Val = Builder.CreateSExt(Val, Builder.getInt64Ty());
204       } else {
205         assert(Ty->getIntegerBitWidth() == 64 &&
206                "Integer types larger 64 bit not supported");
207         // fallthrough
208       }
209     } else if (auto PtTy = dyn_cast<PointerType>(Ty)) {
210       if (PtTy->getAddressSpace() == 4) {
211         // Pointers in constant address space are printed as strings
212         Val = Builder.CreateGEP(Val, Builder.getInt64(0));
213         auto F = RuntimeDebugBuilder::getAddressSpaceCast(Builder, 4, 0);
214         Val = Builder.CreateCall(F, Val);
215       } else {
216         Val = Builder.CreatePtrToInt(Val, Builder.getInt64Ty());
217       }
218     } else {
219       llvm_unreachable("Unknown type");
220     }
221 
222     Ty = Val->getType();
223     Ptr = Builder.CreatePointerBitCastOrAddrSpaceCast(Ptr, Ty->getPointerTo(5));
224     Builder.CreateAlignedStore(Val, Ptr, 4);
225 
226     if (Ty->isFloatingPointTy())
227       str += "%f";
228     else if (Ty->isIntegerTy())
229       str += "%ld";
230     else
231       str += "%s";
232 
233     Offset += 2;
234   }
235 
236   Value *Format = Builder.CreateGlobalStringPtr(str, "polly.vprintf.buffer", 4);
237   Format = Builder.CreateCall(getAddressSpaceCast(Builder, 4, 0), Format);
238 
239   Data = Builder.CreateBitCast(Data, Builder.getInt8PtrTy());
240 
241   Builder.CreateCall(getVPrintF(Builder), {Format, Data});
242 }
243 
getPrintF(PollyIRBuilder & Builder)244 Function *RuntimeDebugBuilder::getPrintF(PollyIRBuilder &Builder) {
245   Module *M = Builder.GetInsertBlock()->getParent()->getParent();
246   const char *Name = "printf";
247   Function *F = M->getFunction(Name);
248 
249   if (!F) {
250     GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage;
251     FunctionType *Ty = FunctionType::get(Builder.getInt32Ty(), true);
252     F = Function::Create(Ty, Linkage, Name, M);
253   }
254 
255   return F;
256 }
257 
createPrintF(PollyIRBuilder & Builder,std::string Format,ArrayRef<Value * > Values)258 void RuntimeDebugBuilder::createPrintF(PollyIRBuilder &Builder,
259                                        std::string Format,
260                                        ArrayRef<Value *> Values) {
261   Value *FormatString = Builder.CreateGlobalStringPtr(Format);
262   std::vector<Value *> Arguments;
263 
264   Arguments.push_back(FormatString);
265   Arguments.insert(Arguments.end(), Values.begin(), Values.end());
266   Builder.CreateCall(getPrintF(Builder), Arguments);
267 }
268 
createFlush(PollyIRBuilder & Builder)269 void RuntimeDebugBuilder::createFlush(PollyIRBuilder &Builder) {
270   Module *M = Builder.GetInsertBlock()->getParent()->getParent();
271   const char *Name = "fflush";
272   Function *F = M->getFunction(Name);
273 
274   if (!F) {
275     GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage;
276     FunctionType *Ty =
277         FunctionType::get(Builder.getInt32Ty(), Builder.getInt8PtrTy(), false);
278     F = Function::Create(Ty, Linkage, Name, M);
279   }
280 
281   // fflush(NULL) flushes _all_ open output streams.
282   //
283   // fflush is declared as 'int fflush(FILE *stream)'. As we only pass on a NULL
284   // pointer, the type we point to does conceptually not matter. However, if
285   // fflush is already declared in this translation unit, we use the very same
286   // type to ensure that LLVM does not complain about mismatching types.
287   Builder.CreateCall(F, Constant::getNullValue(F->arg_begin()->getType()));
288 }
289