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