1 //===--- RuntimeDebugBuilder.cpp - Helper to insert prints into LLVM-IR ---===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 //===----------------------------------------------------------------------===//
11 
12 #include "polly/CodeGen/RuntimeDebugBuilder.h"
13 #include "llvm/IR/Intrinsics.h"
14 #include "llvm/IR/Module.h"
15 #include "llvm/Support/Debug.h"
16 #include <string>
17 #include <vector>
18 
19 using namespace llvm;
20 using namespace polly;
21 
22 Function *RuntimeDebugBuilder::getVPrintF(PollyIRBuilder &Builder) {
23   Module *M = Builder.GetInsertBlock()->getParent()->getParent();
24   const char *Name = "vprintf";
25   Function *F = M->getFunction(Name);
26 
27   if (!F) {
28     GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage;
29     FunctionType *Ty = FunctionType::get(
30         Builder.getInt32Ty(), {Builder.getInt8PtrTy(), Builder.getInt8PtrTy()},
31         false);
32     F = Function::Create(Ty, Linkage, Name, M);
33   }
34 
35   return F;
36 }
37 
38 Function *RuntimeDebugBuilder::getAddressSpaceCast(PollyIRBuilder &Builder,
39                                                    unsigned Src, unsigned Dst,
40                                                    unsigned SrcBits,
41                                                    unsigned DstBits) {
42   Module *M = Builder.GetInsertBlock()->getParent()->getParent();
43   auto Name = std::string("llvm.nvvm.ptr.constant.to.gen.p") +
44               std::to_string(Dst) + "i" + std::to_string(DstBits) + ".p" +
45               std::to_string(Src) + "i" + std::to_string(SrcBits);
46   Function *F = M->getFunction(Name);
47 
48   if (!F) {
49     GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage;
50     FunctionType *Ty = FunctionType::get(
51         PointerType::get(Builder.getIntNTy(DstBits), Dst),
52         PointerType::get(Builder.getIntNTy(SrcBits), Src), false);
53     F = Function::Create(Ty, Linkage, Name, M);
54   }
55 
56   return F;
57 }
58 
59 std::vector<Value *>
60 RuntimeDebugBuilder::getGPUThreadIdentifiers(PollyIRBuilder &Builder) {
61   std::vector<Value *> Identifiers;
62 
63   auto M = Builder.GetInsertBlock()->getParent()->getParent();
64 
65   std::vector<Function *> BlockIDs = {
66       Intrinsic::getDeclaration(M, Intrinsic::ptx_read_ctaid_x),
67       Intrinsic::getDeclaration(M, Intrinsic::ptx_read_ctaid_y),
68       Intrinsic::getDeclaration(M, Intrinsic::ptx_read_ctaid_z),
69   };
70 
71   Identifiers.push_back(Builder.CreateGlobalStringPtr("> block-id: ", "", 4));
72   for (auto GetID : BlockIDs) {
73     Value *Id = Builder.CreateCall(GetID, {});
74     Id = Builder.CreateIntCast(Id, Builder.getInt64Ty(), false);
75     Identifiers.push_back(Id);
76     Identifiers.push_back(Builder.CreateGlobalStringPtr(" ", "", 4));
77   }
78 
79   Identifiers.push_back(Builder.CreateGlobalStringPtr("| ", "", 4));
80 
81   std::vector<Function *> ThreadIDs = {
82       Intrinsic::getDeclaration(M, Intrinsic::ptx_read_tid_x),
83       Intrinsic::getDeclaration(M, Intrinsic::ptx_read_tid_y),
84       Intrinsic::getDeclaration(M, Intrinsic::ptx_read_tid_z),
85   };
86 
87   Identifiers.push_back(Builder.CreateGlobalStringPtr("thread-id: ", "", 4));
88   for (auto GetId : ThreadIDs) {
89     Value *Id = Builder.CreateCall(GetId, {});
90     Id = Builder.CreateIntCast(Id, Builder.getInt64Ty(), false);
91     Identifiers.push_back(Id);
92     Identifiers.push_back(Builder.CreateGlobalStringPtr(" ", "", 4));
93   }
94 
95   return Identifiers;
96 }
97 
98 void RuntimeDebugBuilder::createGPUVAPrinter(PollyIRBuilder &Builder,
99                                              ArrayRef<Value *> Values) {
100   std::string str;
101 
102   // Allocate print buffer (assuming 2*32 bit per element)
103   auto T = ArrayType::get(Builder.getInt32Ty(), Values.size() * 2);
104   Value *Data = new AllocaInst(
105       T, "polly.vprint.buffer",
106       Builder.GetInsertBlock()->getParent()->getEntryBlock().begin());
107 
108   auto *Zero = Builder.getInt64(0);
109   auto *DataPtr = Builder.CreateGEP(Data, {Zero, Zero});
110 
111   auto ToPrint = getGPUThreadIdentifiers(Builder);
112 
113   ToPrint.push_back(Builder.CreateGlobalStringPtr("\n  ", "", 4));
114   ToPrint.insert(ToPrint.end(), Values.begin(), Values.end());
115 
116   int Offset = 0;
117   for (auto Val : ToPrint) {
118     auto Ptr = Builder.CreateGEP(DataPtr, Builder.getInt64(Offset));
119     Type *Ty = Val->getType();
120 
121     if (Ty->isFloatingPointTy()) {
122       if (!Ty->isDoubleTy()) {
123         Ty = Builder.getDoubleTy();
124         Val = Builder.CreateFPExt(Val, Ty);
125       }
126     } else if (Ty->isIntegerTy()) {
127       auto Int64Bitwidth = Builder.getInt64Ty()->getIntegerBitWidth();
128       assert(Ty->getIntegerBitWidth() <= Int64Bitwidth);
129       if (Ty->getIntegerBitWidth() < Int64Bitwidth) {
130         Ty = Builder.getInt64Ty();
131         Val = Builder.CreateSExt(Val, Ty);
132       }
133     } else {
134       // If it is not a number, it must be a string type.
135       Val = Builder.CreateGEP(Val, Builder.getInt64(0));
136       assert((Val->getType() == Builder.getInt8PtrTy(4)) &&
137              "Expected i8 ptr placed in constant address space");
138       auto F = RuntimeDebugBuilder::getAddressSpaceCast(Builder, 4, 0);
139       Val = Builder.CreateCall(F, Val);
140       Ty = Val->getType();
141     }
142 
143     Ptr = Builder.CreatePointerBitCastOrAddrSpaceCast(Ptr, Ty->getPointerTo(5));
144     Builder.CreateAlignedStore(Val, Ptr, 4);
145 
146     if (Ty->isFloatingPointTy())
147       str += "%f";
148     else if (Ty->isIntegerTy())
149       str += "%ld";
150     else
151       str += "%s";
152 
153     Offset += 2;
154   }
155 
156   Value *Format = Builder.CreateGlobalStringPtr(str, "polly.vprintf.buffer", 4);
157   Format = Builder.CreateCall(getAddressSpaceCast(Builder, 4, 0), Format);
158 
159   Data = Builder.CreateBitCast(Data, Builder.getInt8PtrTy());
160 
161   Builder.CreateCall(getVPrintF(Builder), {Format, Data});
162 }
163 
164 Function *RuntimeDebugBuilder::getPrintF(PollyIRBuilder &Builder) {
165   Module *M = Builder.GetInsertBlock()->getParent()->getParent();
166   const char *Name = "printf";
167   Function *F = M->getFunction(Name);
168 
169   if (!F) {
170     GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage;
171     FunctionType *Ty =
172         FunctionType::get(Builder.getInt32Ty(), Builder.getInt8PtrTy(), true);
173     F = Function::Create(Ty, Linkage, Name, M);
174   }
175 
176   return F;
177 }
178 
179 void RuntimeDebugBuilder::createFlush(PollyIRBuilder &Builder) {
180   Module *M = Builder.GetInsertBlock()->getParent()->getParent();
181   const char *Name = "fflush";
182   Function *F = M->getFunction(Name);
183 
184   if (!F) {
185     GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage;
186     FunctionType *Ty =
187         FunctionType::get(Builder.getInt32Ty(), Builder.getInt8PtrTy(), false);
188     F = Function::Create(Ty, Linkage, Name, M);
189   }
190 
191   // fflush(NULL) flushes _all_ open output streams.
192   //
193   // fflush is declared as 'int fflush(FILE *stream)'. As we only pass on a NULL
194   // pointer, the type we point to does conceptually not matter. However, if
195   // fflush is already declared in this translation unit, we use the very same
196   // type to ensure that LLVM does not complain about mismatching types.
197   Builder.CreateCall(F, Constant::getNullValue(F->arg_begin()->getType()));
198 }
199 
200 void RuntimeDebugBuilder::createStrPrinter(PollyIRBuilder &Builder,
201                                            const std::string &String) {
202   Value *StringValue = Builder.CreateGlobalStringPtr(String);
203   Builder.CreateCall(getPrintF(Builder), StringValue);
204 
205   createFlush(Builder);
206 }
207 
208 void RuntimeDebugBuilder::createValuePrinter(PollyIRBuilder &Builder,
209                                              Value *V) {
210   const char *Format = nullptr;
211 
212   Type *Ty = V->getType();
213   if (Ty->isIntegerTy())
214     Format = "%ld";
215   else if (Ty->isFloatingPointTy())
216     Format = "%lf";
217   else if (Ty->isPointerTy())
218     Format = "%p";
219 
220   assert(Format && Ty->getPrimitiveSizeInBits() <= 64 && "Bad type to print.");
221 
222   Value *FormatString = Builder.CreateGlobalStringPtr(Format);
223   Builder.CreateCall(getPrintF(Builder), {FormatString, V});
224   createFlush(Builder);
225 }
226