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, Align(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