1 /*========================== begin_copyright_notice ============================
2
3 Copyright (C) 2017-2021 Intel Corporation
4
5 SPDX-License-Identifier: MIT
6
7 ============================= end_copyright_notice ===========================*/
8
9 /*========================== begin_copyright_notice ============================
10
11 This file is distributed under the University of Illinois Open Source License.
12 See LICENSE.TXT for details.
13
14 ============================= end_copyright_notice ===========================*/
15
16 /*========================== begin_copyright_notice ============================
17
18 Copyright (C) 2014 Advanced Micro Devices, Inc. All rights reserved.
19
20 Permission is hereby granted, free of charge, to any person obtaining a
21 copy of this software and associated documentation files (the "Software"),
22 to deal with the Software without restriction, including without limitation
23 the rights to use, copy, modify, merge, publish, distribute, sublicense,
24 and/or sell copies of the Software, and to permit persons to whom the
25 Software is furnished to do so, subject to the following conditions:
26
27 Redistributions of source code must retain the above copyright notice,
28 this list of conditions and the following disclaimers.
29 Redistributions in binary form must reproduce the above copyright notice,
30 this list of conditions and the following disclaimers in the documentation
31 and/or other materials provided with the distribution.
32 Neither the names of Advanced Micro Devices, Inc., nor the names of its
33 contributors may be used to endorse or promote products derived from this
34 Software without specific prior written permission.
35 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
36 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
37 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
38 CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
39 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
40 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH
41 THE SOFTWARE.
42
43 ============================= end_copyright_notice ===========================*/
44
45 // This file defines utility classes and functions shared by SPIR-V
46 // reader/writer.
47
48 #include "common/LLVMWarningsPush.hpp"
49
50 #include "llvmWrapper/IR/DerivedTypes.h"
51 #include "llvmWrapper/Bitcode/BitcodeWriter.h"
52 #include "llvm/IR/Attributes.h"
53 #include "llvm/Support/ToolOutputFile.h"
54
55 #include <llvm/Support/ScaledNumber.h>
56 #include "llvm/ADT/StringExtras.h"
57 #include "llvmWrapper/IR/IRBuilder.h"
58 #include "llvmWrapper/Transforms/Utils/Cloning.h"
59 #include "common/LLVMWarningsPop.hpp"
60
61 #include "libSPIRV/SPIRVInstruction.h"
62 #include "SPIRVInternal.h"
63 #include "Mangler/ParameterType.h"
64 #include "Probe/Assertion.h"
65
66 namespace igc_spv{
67
68 void
saveLLVMModule(Module * M,const std::string & OutputFile)69 saveLLVMModule(Module *M, const std::string &OutputFile) {
70 std::error_code EC;
71 llvm::ToolOutputFile Out(OutputFile.c_str(), EC, sys::fs::OF_None);
72 IGC_ASSERT_EXIT_MESSAGE((!EC), "Failed to open file");
73 IGCLLVM::WriteBitcodeToFile(M, Out.os());
74 Out.keep();
75 }
76
77 PointerType*
getOrCreateOpaquePtrType(Module * M,const std::string & Name,unsigned AddrSpace)78 getOrCreateOpaquePtrType(Module *M, const std::string &Name,
79 unsigned AddrSpace) {
80 auto OpaqueType = IGCLLVM::getTypeByName(M, Name);
81 if (!OpaqueType)
82 OpaqueType = StructType::create(M->getContext(), Name);
83 return PointerType::get(OpaqueType, AddrSpace);
84 }
85
86 void
getFunctionTypeParameterTypes(llvm::FunctionType * FT,std::vector<Type * > & ArgTys)87 getFunctionTypeParameterTypes(llvm::FunctionType* FT,
88 std::vector<Type*>& ArgTys) {
89 for (auto I = FT->param_begin(), E = FT->param_end(); I != E; ++I) {
90 ArgTys.push_back(*I);
91 }
92 }
93
94 Function *
getOrCreateFunction(Module * M,Type * RetTy,ArrayRef<Type * > ArgTypes,StringRef Name,bool builtin,AttributeList * Attrs,bool takeName)95 getOrCreateFunction(Module *M, Type *RetTy, ArrayRef<Type *> ArgTypes,
96 StringRef Name, bool builtin, AttributeList *Attrs, bool takeName) {
97 std::string FuncName(Name);
98 if (builtin)
99 decorateSPIRVBuiltin(FuncName, ArgTypes);
100
101 FunctionType *FT = FunctionType::get(
102 RetTy,
103 ArgTypes,
104 false);
105 Function *F = M->getFunction(FuncName);
106 if (!F || F->getFunctionType() != FT) {
107 auto NewF = Function::Create(FT,
108 GlobalValue::ExternalLinkage,
109 FuncName,
110 M);
111 if (F && takeName)
112 NewF->takeName(F);
113 F = NewF;
114 F->setCallingConv(CallingConv::SPIR_FUNC);
115 if (Attrs)
116 F->setAttributes(*Attrs);
117 }
118 return F;
119 }
120
121 std::vector<Value *>
getArguments(CallInst * CI)122 getArguments(CallInst* CI) {
123 std::vector<Value*> Args;
124 for (unsigned I = 0, E = CI->getNumArgOperands(); I != E; ++I) {
125 Args.push_back(CI->getArgOperand(I));
126 }
127 return Args;
128 }
129
recursive_mangle(const Type * pType)130 std::string recursive_mangle(const Type* pType)
131 {
132 Type::TypeID ID = pType->getTypeID();
133
134 switch (ID)
135 {
136 case Type::FloatTyID:
137 return "f32";
138 case Type::DoubleTyID:
139 return "f64";
140 case Type::HalfTyID:
141 return "f16";
142 case Type::IntegerTyID:
143 return "i" + utostr(pType->getIntegerBitWidth());
144 case IGCLLVM::VectorTyID:
145 {
146 unsigned vecLen = (unsigned)cast<IGCLLVM::FixedVectorType>(pType)->getNumElements();
147 Type* pEltType = cast<VectorType>(pType)->getElementType();
148 return "v" + utostr(vecLen) + recursive_mangle(pEltType);
149 }
150 case Type::PointerTyID:
151 {
152 unsigned int AS = pType->getPointerAddressSpace();
153 Type* pPointeeType = pType->getPointerElementType();
154
155 StructType* ST = dyn_cast<StructType>(pPointeeType);
156 if (ST && ST->isOpaque())
157 {
158 StringRef structName = ST->getName();
159 bool isImage = structName.startswith(std::string(kSPIRVTypeName::PrefixAndDelim) + std::string(kSPIRVTypeName::Image));
160 if (isImage)
161 {
162 SmallVector<StringRef, 8> matches;
163 Regex regex("([0-6])_([0-2])_([0-1])_([0-1])_([0-2])_([0-9]+)_([0-2])");
164 SPIRVTypeImageDescriptor Desc;
165 if (regex.match(structName, &matches))
166 {
167 uint8_t dimension = 0;
168 matches[1].getAsInteger(0, dimension);
169 Desc.Dim = static_cast<SPIRVImageDimKind>(dimension);
170 matches[2].getAsInteger(0, Desc.Depth);
171 matches[3].getAsInteger(0, Desc.Arrayed);
172 matches[4].getAsInteger(0, Desc.MS);
173 matches[5].getAsInteger(0, Desc.Sampled);
174 matches[6].getAsInteger(0, Desc.Format);
175
176 uint8_t spirvAccess = 0;
177 matches[7].getAsInteger(0, spirvAccess);
178 SPIRVAccessQualifierKind Acc = static_cast<SPIRVAccessQualifierKind>(spirvAccess);
179
180 std::string typeMangling = SPIRVImageManglingMap::map(Desc);
181 switch (Acc)
182 {
183 case AccessQualifierReadOnly:
184 typeMangling += "_ro";
185 break;
186 case AccessQualifierWriteOnly:
187 typeMangling += "_wo";
188 break;
189 case AccessQualifierReadWrite:
190 typeMangling += "_rw";
191 break;
192 default:
193 IGC_ASSERT_MESSAGE(0, "Unsupported access qualifier!");
194 }
195 return typeMangling;
196 }
197 IGC_ASSERT_MESSAGE(0, "Inconsistent SPIRV image!");
198 }
199 bool isPipe_ro = structName.startswith(std::string(kSPIRVTypeName::PrefixAndDelim) + std::string(kSPIRVTypeName::Pipe) + "._0");
200 if (isPipe_ro) return "Pipe_ro";
201 bool isPipe_wo = structName.startswith(std::string(kSPIRVTypeName::PrefixAndDelim) + std::string(kSPIRVTypeName::Pipe) + "._1");
202 if (isPipe_wo) return "Pipe_wo";
203 bool isReserveId = structName.startswith(std::string(kSPIRVTypeName::PrefixAndDelim) + std::string(kSPIRVTypeName::ReserveId));
204 if (isReserveId) return "ReserveId";
205 return "i64";
206 }
207
208 return "p" + utostr(AS) + recursive_mangle(pPointeeType);
209 }
210 case Type::StructTyID:
211 {
212 auto structName = cast<StructType>(pType)->getName().str();
213 auto pointPos = structName.rfind('.');
214 return pointPos != structName.npos ? structName.substr(pointPos + 1) : structName;
215 }
216 case Type::FunctionTyID:
217 {
218 return "func";
219 }
220 case Type::ArrayTyID:
221 {
222 auto elemType = pType->getArrayElementType();
223 auto numElems = pType->getArrayNumElements();
224 return "a" + utostr(numElems) + recursive_mangle(elemType);
225 }
226 default:
227 IGC_ASSERT_EXIT_MESSAGE(0, "unhandled type to mangle!");
228 return "";
229 }
230 }
231
232
Mangler(const std::string & FuncName,const std::vector<Type * > & ArgTypes)233 std::string Mangler(const std::string &FuncName, const std::vector<Type*> &ArgTypes)
234 {
235 std::string str_type;
236 for (auto U : ArgTypes)
237 {
238 str_type += "_" + recursive_mangle(U);
239 }
240 return FuncName + str_type;
241 }
242
243 void
decorateSPIRVBuiltin(std::string & S)244 decorateSPIRVBuiltin(std::string &S)
245 {
246 S = std::string(kLLVMName::builtinPrefix) + S;
247 }
248
249 void
decorateSPIRVBuiltin(std::string & S,std::vector<Type * > ArgTypes)250 decorateSPIRVBuiltin(std::string &S, std::vector<Type*> ArgTypes) {
251 S.assign(std::string(kLLVMName::builtinPrefix) + Mangler(S,ArgTypes));
252 }
253
254 void
decorateSPIRVExtInst(std::string & S,std::vector<Type * > ArgTypes)255 decorateSPIRVExtInst(std::string &S, std::vector<Type*> ArgTypes) {
256 S.assign(std::string(kLLVMName::builtinExtInstPrefixOpenCL) + Mangler(S,ArgTypes));
257 }
258
259 bool
isFunctionBuiltin(llvm::Function * F)260 isFunctionBuiltin(llvm::Function* F) {
261 return F && F->isDeclaration() && F->getName().startswith(kLLVMName::builtinPrefix);
262 }
263
264 std::string
getSPIRVBuiltinName(Op OC,SPIRVInstruction * BI,std::vector<Type * > ArgTypes,std::string suffix)265 getSPIRVBuiltinName(Op OC, SPIRVInstruction *BI, std::vector<Type*> ArgTypes, std::string suffix) {
266 std::string name = "";
267 bool hasI32Postfix = false;
268
269 if (OC == OpSubgroupImageMediaBlockReadINTEL || OC == OpSubgroupImageMediaBlockWriteINTEL) {
270 std::stringstream tmpName;
271 SPIRVType *DataTy = nullptr;
272 switch (OC) {
273 case OpSubgroupImageMediaBlockReadINTEL:
274 tmpName << OCLSPIRVBuiltinMap::map(OC);
275 DataTy = BI->getType();
276 hasI32Postfix = true;
277 break;
278 case OpSubgroupImageMediaBlockWriteINTEL:
279 tmpName << OCLSPIRVBuiltinMap::map(OC);
280 DataTy = (*BI->getOperands().rbegin())->getType();
281 hasI32Postfix = true;
282 break;
283 default:
284 tmpName << OCLSPIRVBuiltinMap::map(OC);
285 }
286 if (DataTy) {
287 if (DataTy->getBitWidth() == 8) {
288 tmpName << "_uc";
289 }
290 else if (DataTy->getBitWidth() == 16) {
291 tmpName << "_us";
292 }
293 else if (DataTy->getBitWidth() == 32 && hasI32Postfix) {
294 tmpName << "_ui";
295 }
296 else if (DataTy->getBitWidth() == 64) {
297 tmpName << "_ul";
298 }
299
300 if (DataTy->isTypeVector()) {
301 if (unsigned ComponentCount = DataTy->getVectorComponentCount())
302 tmpName << ComponentCount;
303 }
304 }
305 name = tmpName.str();
306 }
307 else
308 {
309 name = OCLSPIRVBuiltinMap::map(OC);
310 }
311
312 if (!name.empty()) {
313 name = name + suffix;
314 decorateSPIRVBuiltin(name, ArgTypes);
315 }
316 else
317 {
318 IGC_ASSERT_EXIT_MESSAGE(0, "Couldn't find opcode in map!");
319 }
320 return name;
321 }
322
323 CallInst *
mutateCallInst(Module * M,CallInst * CI,std::function<std::string (CallInst *,std::vector<Value * > &)> ArgMutate,bool Mangle,AttributeList * Attrs,bool TakeFuncName)324 mutateCallInst(Module *M, CallInst *CI,
325 std::function<std::string (CallInst *, std::vector<Value *> &)>ArgMutate,
326 bool Mangle, AttributeList *Attrs, bool TakeFuncName) {
327
328 auto Args = getArguments(CI);
329 auto NewName = ArgMutate(CI, Args);
330 std::string InstName;
331 if (!CI->getType()->isVoidTy() && CI->hasName()) {
332 InstName = CI->getName().str();
333 CI->setName(InstName + ".old");
334 }
335
336 auto NewCI = addCallInst(M, NewName, CI->getType(), Args, Attrs, CI, Mangle,
337 InstName, TakeFuncName);
338
339 Function* OldF = CI->getCalledFunction();
340 Function* NewF = NewCI->getCalledFunction();
341 if (!OldF->isDeclaration() &&
342 NewF->isDeclaration()) {
343 // This means that we need to clone the old function into new one.
344 // It is needed because when Clang is compiling to llvm-bc it does the same thing.
345 // If we want to link with such modules, we need to make the behaviour similar.
346 IGC_ASSERT(OldF->getNumOperands() == NewF->getNumOperands());
347 ValueToValueMapTy VMap;
348 llvm::SmallVector<llvm::ReturnInst*, 8> Returns;
349 BasicBlock* EntryBB = BasicBlock::Create(M->getContext(), "", NewF);
350 IGCLLVM::IRBuilder<> builder(EntryBB);
351
352 for (auto OldArgIt = OldF->arg_begin(), NewArgIt = NewF->arg_begin(); OldArgIt != OldF->arg_end(); ++OldArgIt, ++NewArgIt) {
353 NewArgIt->setName(OldArgIt->getName().str());
354 if (OldArgIt->getType() == NewArgIt->getType()) {
355 VMap[&*OldArgIt] = &*NewArgIt;
356 }
357 else {
358 IGC_ASSERT(NewArgIt->getType()->isPointerTy());
359 LoadInst* Load = builder.CreateLoad(&*NewArgIt);
360 VMap[&*OldArgIt] = Load;
361 }
362 }
363
364 IGCLLVM::CloneFunctionInto(NewF, OldF, VMap, true, Returns);
365
366 // Merge the basic block with Load instruction with the original entry basic block.
367 BasicBlock* ClonedEntryBB = cast<BasicBlock>(VMap[&*OldF->begin()]);
368 builder.CreateBr(ClonedEntryBB);
369 }
370
371 CI->replaceAllUsesWith(NewCI);
372 CI->dropAllReferences();
373 CI->removeFromParent();
374 return NewCI;
375 }
376
377 void
mutateFunction(Function * F,std::function<std::string (CallInst *,std::vector<Value * > &)> ArgMutate,bool Mangle,AttributeList * Attrs,bool TakeFuncName)378 mutateFunction(Function *F,
379 std::function<std::string (CallInst *, std::vector<Value *> &)>ArgMutate,
380 bool Mangle, AttributeList *Attrs, bool TakeFuncName) {
381 auto M = F->getParent();
382 for (auto I = F->user_begin(), E = F->user_end(); I != E;) {
383 if (auto CI = dyn_cast<CallInst>(*I++))
384 mutateCallInst(M, CI, ArgMutate, Mangle, Attrs, TakeFuncName);
385 }
386 if (F->use_empty()) {
387 F->dropAllReferences();
388 F->removeFromParent();
389 }
390 }
391
392 CallInst *
addCallInst(Module * M,StringRef FuncName,Type * RetTy,ArrayRef<Value * > Args,AttributeList * Attrs,Instruction * Pos,bool Mangle,StringRef InstName,bool TakeFuncName)393 addCallInst(Module *M, StringRef FuncName, Type *RetTy, ArrayRef<Value *> Args,
394 AttributeList *Attrs, Instruction *Pos, bool Mangle, StringRef InstName,
395 bool TakeFuncName) {
396 auto F = getOrCreateFunction(M, RetTy, getTypes(Args),
397 FuncName, Mangle, Attrs, TakeFuncName);
398 auto CI = CallInst::Create(F, Args, InstName, Pos);
399 CI->setCallingConv(F->getCallingConv());
400 return CI;
401 }
402
403 ConstantInt *
getInt64(Module * M,int64_t value)404 getInt64(Module *M, int64_t value) {
405 return ConstantInt::get(Type::getInt64Ty(M->getContext()), value, true);
406 }
407
408 ConstantInt *
getInt32(Module * M,int value)409 getInt32(Module *M, int value) {
410 return ConstantInt::get(Type::getInt32Ty(M->getContext()), value, true);
411 }
412
413 std::tuple<unsigned short, unsigned char, unsigned char>
decodeOCLVer(unsigned Ver)414 decodeOCLVer(unsigned Ver) {
415 unsigned short Major = Ver / 100000;
416 unsigned char Minor = (Ver % 100000) / 1000;
417 unsigned char Rev = Ver % 1000;
418 return std::make_tuple(Major, Minor, Rev);
419 }
420
getSPIRVTypeName(StringRef BaseName,StringRef Postfixes)421 std::string getSPIRVTypeName(StringRef BaseName, StringRef Postfixes) {
422 assert(!BaseName.empty() && "Invalid SPIR-V type Name");
423 auto TN = std::string(kSPIRVTypeName::PrefixAndDelim) + BaseName.str();
424 if (Postfixes.empty())
425 return TN;
426 return TN + kSPIRVTypeName::Delimiter + Postfixes.str();
427 }
428
getSPIRVImageTypePostfixes(StringRef SampledType,SPIRVTypeImageDescriptor Desc,SPIRVAccessQualifierKind Acc)429 std::string getSPIRVImageTypePostfixes(StringRef SampledType,
430 SPIRVTypeImageDescriptor Desc,
431 SPIRVAccessQualifierKind Acc) {
432 std::string S;
433 raw_string_ostream OS(S);
434 OS << kSPIRVTypeName::PostfixDelim << SampledType
435 << kSPIRVTypeName::PostfixDelim << Desc.Dim << kSPIRVTypeName::PostfixDelim
436 << Desc.Depth << kSPIRVTypeName::PostfixDelim << Desc.Arrayed
437 << kSPIRVTypeName::PostfixDelim << Desc.MS << kSPIRVTypeName::PostfixDelim
438 << Desc.Sampled << kSPIRVTypeName::PostfixDelim << Desc.Format
439 << kSPIRVTypeName::PostfixDelim << Acc;
440 return OS.str();
441 }
442
getSPIRVImageSampledTypeName(SPIRVType * Ty)443 std::string getSPIRVImageSampledTypeName(SPIRVType* Ty) {
444 switch (Ty->getOpCode()) {
445 case OpTypeVoid:
446 return kSPIRVImageSampledTypeName::Void;
447 case OpTypeInt:
448 if (Ty->getIntegerBitWidth() == 32) {
449 if (static_cast<SPIRVTypeInt*>(Ty)->isSigned())
450 return kSPIRVImageSampledTypeName::Int;
451 else
452 return kSPIRVImageSampledTypeName::UInt;
453 }
454 break;
455 case OpTypeFloat:
456 switch (Ty->getFloatBitWidth()) {
457 case 16:
458 return kSPIRVImageSampledTypeName::Half;
459 case 32:
460 return kSPIRVImageSampledTypeName::Float;
461 default:
462 break;
463 }
464 break;
465 default:
466 break;
467 }
468 llvm_unreachable("Invalid sampled type for image");
469 return std::string();
470 }
471
472 }
473
474