1 /*========================== begin_copyright_notice ============================
2 
3 Copyright (C) 2021 Intel Corporation
4 
5 SPDX-License-Identifier: MIT
6 
7 ============================= end_copyright_notice ===========================*/
8 
9 //
10 // Currently VectorCompiler uses a special type of LLVM-SPIRV-Translator
11 // in a form of shared library called "SPIRVDLL"
12 // It is expected to move from this solution in favour of original Khronos
13 // LLVMSPIRVLib library.
14 // This file was created for the purpose of smooth transit between these two
15 // library versions.
16 //
17 //===----------------------------------------------------------------------===//
18 
19 #include <sstream>
20 
21 #include "SPIRVWrapper.h"
22 
23 #include "Probe/Assertion.h"
24 #include "vc/Support/Status.h"
25 #include "llvm/IR/Verifier.h"
26 
27 #ifdef IGC_VECTOR_USE_KHRONOS_SPIRV_TRANSLATOR
28 #include "LLVMSPIRVLib.h"
29 #include "llvm/Bitcode/BitcodeWriter.h"
30 #else // IGC_VECTOR_USE_KHRONOS_SPIRV_TRANSLATOR
31 #include "llvm/Support/DynamicLibrary.h"
32 #include "llvm/Support/Path.h"
33 #include "llvm/Support/Process.h"
34 #if !defined(_WIN32)
35 #include <dlfcn.h>
36 #endif // __linux__
37 #if defined(_WIN32)
38 #include <Windows.h>
39 #include "inc/common/DriverStore.h"
40 #endif // _WIN32
41 #endif // IGC_VECTOR_USE_KHRONOS_SPIRV_TRANSLATOR
42 
43 using namespace llvm;
44 
45 namespace {
46 
47 using SpirvReadVerifyType = int(
48     const char *pIn, size_t InSz, const uint32_t *SpecConstIds,
49     const uint64_t *SpecConstVals, unsigned SpecConstSz,
50     void (*OutSaver)(const char *pOut, size_t OutSize, void *OutUserData),
51     void *OutUserData, void (*ErrSaver)(const char *pErrMsg, void *ErrUserData),
52     void *ErrUserData);
53 
54 #ifdef IGC_VECTOR_USE_KHRONOS_SPIRV_TRANSLATOR
55 
spirvReadVerify(const char * pIn,size_t InSz,const uint32_t * SpecConstIds,const uint64_t * SpecConstVals,unsigned SpecConstSz,void (* OutSaver)(const char * pOut,size_t OutSize,void * OutUserData),void * OutUserData,void (* ErrSaver)(const char * pErrMsg,void * ErrUserData),void * ErrUserData)56 int spirvReadVerify(const char *pIn, size_t InSz, const uint32_t *SpecConstIds,
57                     const uint64_t *SpecConstVals, unsigned SpecConstSz,
58                     void (*OutSaver)(const char *pOut, size_t OutSize,
59                                      void *OutUserData),
60                     void *OutUserData,
61                     void (*ErrSaver)(const char *pErrMsg, void *ErrUserData),
62                     void *ErrUserData) {
63   llvm::LLVMContext Context;
64   llvm::StringRef SpirvInput = llvm::StringRef(pIn, InSz);
65   std::istringstream IS(SpirvInput.str());
66   std::unique_ptr<llvm::Module> M;
67   {
68     llvm::Module *SpirM;
69     std::string ErrMsg;
70 #if LLVM_VERSION_MAJOR > 7
71     SPIRV::TranslatorOpts Opts;
72     Opts.enableAllExtensions();
73     Opts.setFPContractMode(SPIRV::FPContractMode::On);
74     Opts.setDesiredBIsRepresentation(SPIRV::BIsRepresentation::SPIRVFriendlyIR);
75     // Add specialization constants
76     for (unsigned i = 0; i < SpecConstSz; ++i)
77       Opts.setSpecConst(SpecConstIds[i], SpecConstVals[i]);
78 
79     // This returns true on success...
80     bool Status = llvm::readSpirv(Context, Opts, IS, SpirM, ErrMsg);
81 #else
82     if (SpecConstSz != 0) {
83       std::ostringstream OSS;
84       OSS << "spirv_read_verify: Specialization constants are not supported in "
85              "this translator version (700) "
86           << ErrMsg;
87       ErrSaver(OSS.str().c_str(), ErrUserData);
88       return -1;
89     }
90     // This returns true on success...
91     bool Status = llvm::readSpirv(Context, IS, SpirM, ErrMsg);
92 #endif
93     if (!Status) {
94       std::ostringstream OSS;
95       OSS << "spirv_read_verify: readSpirv failed: " << ErrMsg;
96       ErrSaver(OSS.str().c_str(), ErrUserData);
97       return -1;
98     }
99     Status = llvm::verifyModule(*SpirM);
100     if (Status) {
101       ErrSaver("spirv_read_verify: verify Module failed", ErrUserData);
102       return -1;
103     }
104     M.reset(SpirM);
105   }
106 
107   llvm::SmallVector<char, 16> CloneBuffer;
108   llvm::raw_svector_ostream CloneOstream(CloneBuffer);
109   WriteBitcodeToFile(*M, CloneOstream);
110   IGC_ASSERT(CloneBuffer.size() > 0);
111 
112   OutSaver(CloneBuffer.data(), CloneBuffer.size(), OutUserData);
113   return 0;
114 }
115 
getSpirvReadVerifyFunction()116 Expected<SpirvReadVerifyType *> getSpirvReadVerifyFunction() {
117   return &spirvReadVerify;
118 }
119 
120 #else // IGC_VECTOR_USE_KHRONOS_SPIRV_TRANSLATOR
121 
122 // Get appropriate path to SPIRV DLL library for subsequent loading.
findSpirvDLL()123 std::string findSpirvDLL() {
124 #if defined(_WIN64)
125   // TODO: rename to SPIRVDLL64.dll when binary components are fixed.
126   static constexpr char *SpirvLibName = "SPIRVDLL.dll";
127 #elif defined(_WIN32)
128   static constexpr char *SpirvLibName = "SPIRVDLL32.dll";
129 #else
130   static constexpr char *SpirvLibName = "libSPIRVDLL.so";
131 #endif
132 
133   auto EnvSpirv = llvm::sys::Process::GetEnv("VC_SPIRVDLL_DIR");
134   if (EnvSpirv) {
135     SmallString<32> Path;
136     llvm::sys::path::append(Path, EnvSpirv.getValue(), SpirvLibName);
137     return std::string{Path.str()};
138   }
139 
140 // Return standard path for SPIRVDLL for current build.
141 // Linux: plain library name.
142 // Windows: library name prefixed with current module
143 // location (installed driver location).
144 #if defined(_WIN32)
145   // Expand libname to full driver path on windows.
146   char TmpPath[MAX_PATH] = {};
147   GetDependencyPath(TmpPath, SpirvLibName);
148   return TmpPath;
149 #else
150   return SpirvLibName;
151 #endif
152 }
153 
getSpirvReadVerifyFunction()154 Expected<SpirvReadVerifyType *> getSpirvReadVerifyFunction() {
155   constexpr char *SpirvReadVerifyName = "spirv_read_verify_module";
156 
157   const std::string SpirvLibPath = findSpirvDLL();
158 #if defined(RTLD_DEEPBIND)
159   // Hack to workaround cmoc crashes during loading of SPIRV library
160   static auto DeepBindHack =
161       dlopen(SpirvLibPath.c_str(), RTLD_NOW | RTLD_DEEPBIND);
162 #endif // __linux__
163 
164   std::string ErrMsg;
165   using DL = sys::DynamicLibrary;
166   DL DyLib = DL::getPermanentLibrary(SpirvLibPath.c_str(), &ErrMsg);
167   if (!DyLib.isValid())
168     return make_error<vc::DynLoadError>(ErrMsg);
169 
170   auto *SpirvReadVerify = reinterpret_cast<SpirvReadVerifyType *>(
171       DyLib.getAddressOfSymbol(SpirvReadVerifyName));
172   if (!SpirvReadVerify)
173     return make_error<vc::SymbolLookupError>(SpirvLibPath, SpirvReadVerifyName);
174   return SpirvReadVerify;
175 }
176 
177 #endif // IGC_VECTOR_USE_KHRONOS_SPIRV_TRANSLATOR
178 
179 } // namespace
180 
181 Expected<std::vector<char>>
translateSPIRVToIR(ArrayRef<char> Input,ArrayRef<uint32_t> SpecConstIds,ArrayRef<uint64_t> SpecConstValues)182 vc::translateSPIRVToIR(ArrayRef<char> Input, ArrayRef<uint32_t> SpecConstIds,
183                        ArrayRef<uint64_t> SpecConstValues) {
184   IGC_ASSERT(SpecConstIds.size() == SpecConstValues.size());
185   auto OutSaver = [](const char *pOut, size_t OutSize, void *OutData) {
186     auto *Vec = reinterpret_cast<std::vector<char> *>(OutData);
187     Vec->assign(pOut, pOut + OutSize);
188   };
189   auto ErrSaver = [](const char *pErrMsg, void *ErrData) {
190     auto *ErrStr = reinterpret_cast<std::string *>(ErrData);
191     *ErrStr = pErrMsg;
192   };
193   std::string ErrMsg;
194   std::vector<char> Result;
195   auto SpirvReadVerifyFunctionExp = getSpirvReadVerifyFunction();
196   if (!SpirvReadVerifyFunctionExp)
197     return SpirvReadVerifyFunctionExp.takeError();
198   auto *SpirvReadVerifyFunction = SpirvReadVerifyFunctionExp.get();
199 
200   int Status = SpirvReadVerifyFunction(
201       Input.data(), Input.size(), SpecConstIds.data(), SpecConstValues.data(),
202       SpecConstValues.size(), OutSaver, &Result, ErrSaver, &ErrMsg);
203 
204   if (Status != 0)
205     return make_error<vc::BadSpirvError>(ErrMsg);
206   return {std::move(Result)};
207 }
208