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