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