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 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 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 *> 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 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 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 *>> 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 == Builder.getInt8PtrTy(4)) { 136 Val = Builder.CreateGEP(Builder.getInt8Ty(), Val, Builder.getInt64(0)); 137 } else { 138 Val = Builder.CreatePtrToInt(Val, Builder.getInt64Ty()); 139 } 140 } else { 141 llvm_unreachable("Unknown type"); 142 } 143 144 Ty = Val->getType(); 145 146 if (Ty->isFloatingPointTy()) 147 FormatString += "%f"; 148 else if (Ty->isIntegerTy()) 149 FormatString += "%ld"; 150 else 151 FormatString += "%s"; 152 153 ValuesToPrint.push_back(Val); 154 } 155 156 return std::make_tuple(FormatString, ValuesToPrint); 157 } 158 159 void RuntimeDebugBuilder::createCPUPrinterT(PollyIRBuilder &Builder, 160 ArrayRef<Value *> Values) { 161 162 std::string FormatString; 163 std::vector<Value *> ValuesToPrint; 164 165 std::tie(FormatString, ValuesToPrint) = 166 prepareValuesForPrinting(Builder, Values); 167 168 createPrintF(Builder, FormatString, ValuesToPrint); 169 createFlush(Builder); 170 } 171 172 void RuntimeDebugBuilder::createGPUPrinterT(PollyIRBuilder &Builder, 173 ArrayRef<Value *> Values) { 174 std::string str; 175 176 auto *Zero = Builder.getInt64(0); 177 178 auto ToPrint = getGPUThreadIdentifiers(Builder); 179 180 ToPrint.push_back(Builder.CreateGlobalStringPtr("\n ", "", 4)); 181 ToPrint.insert(ToPrint.end(), Values.begin(), Values.end()); 182 183 const DataLayout &DL = Builder.GetInsertBlock()->getModule()->getDataLayout(); 184 185 // Allocate print buffer (assuming 2*32 bit per element) 186 auto T = ArrayType::get(Builder.getInt32Ty(), ToPrint.size() * 2); 187 Value *Data = new AllocaInst( 188 T, DL.getAllocaAddrSpace(), "polly.vprint.buffer", 189 &Builder.GetInsertBlock()->getParent()->getEntryBlock().front()); 190 auto *DataPtr = Builder.CreateGEP(T, Data, {Zero, Zero}); 191 192 int Offset = 0; 193 for (auto Val : ToPrint) { 194 auto Ptr = Builder.CreateGEP(Builder.getInt32Ty(), DataPtr, 195 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 (isa<PointerType>(Ty)) { 210 if (Ty == Builder.getInt8PtrTy(4)) { 211 // Pointers in constant address space are printed as strings 212 Val = Builder.CreateGEP(Builder.getInt8Ty(), 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 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 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 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