1 /*-------------------------------------------------------------------------
2 * Vulkan CTS Framework
3 * --------------------
4 *
5 * Copyright (c) 2015 Google Inc.
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 *//*!
20 * \file
21 * \brief SPIR-V assembly to binary.
22 *//*--------------------------------------------------------------------*/
23
24 #include "vkSpirVAsm.hpp"
25 #include "vkSpirVProgram.hpp"
26 #include "deClock.h"
27
28 #include <algorithm>
29
30 #include "spirv-tools/libspirv.h"
31
32 namespace vk
33 {
34
35 using std::string;
36 using std::vector;
37
38 // Returns the SPIRV-Tools target environment enum for the given dEQP Spirv validator options object.
39 // Do this here instead of as a method on SpirvValidatorOptions because only this file has access to
40 // the SPIRV-Tools headers.
getSpirvToolsEnvForValidatorOptions(SpirvValidatorOptions opts)41 static spv_target_env getSpirvToolsEnvForValidatorOptions(SpirvValidatorOptions opts)
42 {
43 const bool allow_1_4 = opts.supports_VK_KHR_spirv_1_4;
44 switch (opts.vulkanVersion)
45 {
46 case VK_MAKE_API_VERSION(0, 1, 0, 0): return SPV_ENV_VULKAN_1_0;
47 case VK_MAKE_API_VERSION(0, 1, 1, 0): return allow_1_4 ? SPV_ENV_VULKAN_1_1_SPIRV_1_4 : SPV_ENV_VULKAN_1_1;
48 case VK_MAKE_API_VERSION(0, 1, 2, 0): return SPV_ENV_VULKAN_1_2;
49 case VK_MAKE_API_VERSION(1, 1, 0, 0): return SPV_ENV_VULKAN_1_2;
50 case VK_MAKE_API_VERSION(0, 1, 3, 0): return SPV_ENV_VULKAN_1_3;
51 default:
52 break;
53 }
54 TCU_THROW(InternalError, "Unexpected Vulkan Version version requested");
55 return SPV_ENV_VULKAN_1_0;
56 }
57
mapTargetSpvEnvironment(SpirvVersion spirvVersion)58 static spv_target_env mapTargetSpvEnvironment(SpirvVersion spirvVersion)
59 {
60 spv_target_env result = SPV_ENV_UNIVERSAL_1_0;
61
62 switch (spirvVersion)
63 {
64 case SPIRV_VERSION_1_0: result = SPV_ENV_UNIVERSAL_1_0; break; //!< SPIR-V 1.0
65 case SPIRV_VERSION_1_1: result = SPV_ENV_UNIVERSAL_1_1; break; //!< SPIR-V 1.1
66 case SPIRV_VERSION_1_2: result = SPV_ENV_UNIVERSAL_1_2; break; //!< SPIR-V 1.2
67 case SPIRV_VERSION_1_3: result = SPV_ENV_UNIVERSAL_1_3; break; //!< SPIR-V 1.3
68 case SPIRV_VERSION_1_4: result = SPV_ENV_UNIVERSAL_1_4; break; //!< SPIR-V 1.4
69 case SPIRV_VERSION_1_5: result = SPV_ENV_UNIVERSAL_1_5; break; //!< SPIR-V 1.5
70 case SPIRV_VERSION_1_6: result = SPV_ENV_UNIVERSAL_1_6; break; //!< SPIR-V 1.6
71 default: TCU_THROW(InternalError, "Unknown SPIR-V version");
72 }
73
74 return result;
75 }
76
assembleSpirV(const SpirVAsmSource * program,std::vector<deUint32> * dst,SpirVProgramInfo * buildInfo,SpirvVersion spirvVersion)77 bool assembleSpirV (const SpirVAsmSource* program, std::vector<deUint32>* dst, SpirVProgramInfo* buildInfo, SpirvVersion spirvVersion)
78 {
79 const spv_context context = spvContextCreate(mapTargetSpvEnvironment(spirvVersion));
80 spv_binary binary = DE_NULL;
81 spv_diagnostic diagnostic = DE_NULL;
82
83 if (!context)
84 throw std::bad_alloc();
85
86 try
87 {
88 const std::string& spvSource = program->source;
89 const deUint64 compileStartTime = deGetMicroseconds();
90 const deUint32 options = SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS;
91 const spv_result_t compileOk = spvTextToBinaryWithOptions(context, spvSource.c_str(), spvSource.size(), options, &binary, &diagnostic);
92
93 buildInfo->source = spvSource;
94 buildInfo->infoLog = diagnostic? diagnostic->error : ""; // \todo [2015-07-13 pyry] Include debug log?
95 buildInfo->compileTimeUs = deGetMicroseconds() - compileStartTime;
96 buildInfo->compileOk = (compileOk == SPV_SUCCESS);
97
98 if (buildInfo->compileOk)
99 {
100 DE_ASSERT(binary->wordCount > 0);
101 dst->resize(binary->wordCount);
102 std::copy(&binary->code[0], &binary->code[0] + binary->wordCount, dst->begin());
103 }
104
105 spvBinaryDestroy(binary);
106 spvDiagnosticDestroy(diagnostic);
107 spvContextDestroy(context);
108
109 return compileOk == SPV_SUCCESS;
110 }
111 catch (...)
112 {
113 spvBinaryDestroy(binary);
114 spvDiagnosticDestroy(diagnostic);
115 spvContextDestroy(context);
116
117 throw;
118 }
119 }
120
disassembleSpirV(size_t binarySizeInWords,const deUint32 * binary,std::ostream * dst,SpirvVersion spirvVersion)121 void disassembleSpirV (size_t binarySizeInWords, const deUint32* binary, std::ostream* dst, SpirvVersion spirvVersion)
122 {
123 const spv_context context = spvContextCreate(mapTargetSpvEnvironment(spirvVersion));
124 spv_text text = DE_NULL;
125 spv_diagnostic diagnostic = DE_NULL;
126
127 if (!context)
128 throw std::bad_alloc();
129
130 try
131 {
132 const spv_result_t result = spvBinaryToText(context, binary, binarySizeInWords, 0, &text, &diagnostic);
133
134 if (result != SPV_SUCCESS)
135 TCU_THROW(InternalError, "Disassembling SPIR-V failed");
136
137 *dst << text->str;
138
139 spvTextDestroy(text);
140 spvDiagnosticDestroy(diagnostic);
141 spvContextDestroy(context);
142 }
143 catch (...)
144 {
145 spvTextDestroy(text);
146 spvDiagnosticDestroy(diagnostic);
147 spvContextDestroy(context);
148
149 throw;
150 }
151 }
152
validateSpirV(size_t binarySizeInWords,const deUint32 * binary,std::ostream * infoLog,const SpirvValidatorOptions & val_options)153 bool validateSpirV (size_t binarySizeInWords, const deUint32* binary, std::ostream* infoLog, const SpirvValidatorOptions &val_options)
154 {
155 const spv_context context = spvContextCreate(getSpirvToolsEnvForValidatorOptions(val_options));
156 spv_diagnostic diagnostic = DE_NULL;
157 spv_validator_options options = DE_NULL;
158 spv_text disasmText = DE_NULL;
159
160 if (!context)
161 throw std::bad_alloc();
162
163 try
164 {
165 spv_const_binary_t cbinary = { binary, binarySizeInWords };
166
167 options = spvValidatorOptionsCreate();
168
169 if (options == DE_NULL)
170 throw std::bad_alloc();
171
172 switch (val_options.blockLayout)
173 {
174 case SpirvValidatorOptions::kDefaultBlockLayout:
175 break;
176 case SpirvValidatorOptions::kNoneBlockLayout:
177 spvValidatorOptionsSetSkipBlockLayout(options, true);
178 break;
179 case SpirvValidatorOptions::kRelaxedBlockLayout:
180 spvValidatorOptionsSetRelaxBlockLayout(options, true);
181 break;
182 case SpirvValidatorOptions::kUniformStandardLayout:
183 spvValidatorOptionsSetUniformBufferStandardLayout(options, true);
184 break;
185 case SpirvValidatorOptions::kScalarBlockLayout:
186 spvValidatorOptionsSetScalarBlockLayout(options, true);
187 break;
188 }
189
190 if (val_options.flags & SpirvValidatorOptions::FLAG_SPIRV_VALIDATOR_WORKGROUP_SCALAR_BLOCK_LAYOUT)
191 {
192 spvValidatorOptionsSetWorkgroupScalarBlockLayout(options, true);
193 }
194
195 if (val_options.flags & SpirvValidatorOptions::FLAG_SPIRV_VALIDATOR_ALLOW_LOCALSIZEID)
196 spvValidatorOptionsSetAllowLocalSizeId(options, true);
197
198 const spv_result_t valid = spvValidateWithOptions(context, options, &cbinary, &diagnostic);
199 const bool passed = (valid == SPV_SUCCESS);
200
201 *infoLog << "Validation " << (passed ? "PASSED: " : "FAILED: ");
202
203 if (diagnostic && diagnostic->error)
204 {
205 // Print the diagnostic whether validation passes or fails.
206 // In theory we could get a warning even in the pass case, but there are no cases
207 // like that now.
208 *infoLog << diagnostic->error << "\n";
209
210 const deUint32 disasmOptions = SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES
211 | SPV_BINARY_TO_TEXT_OPTION_INDENT;
212 const spv_result_t disasmResult = spvBinaryToText(context, binary, binarySizeInWords, disasmOptions, &disasmText, DE_NULL);
213
214 if (disasmResult != SPV_SUCCESS)
215 *infoLog << "Disassembly failed with code: " << de::toString(disasmResult) << "\n";
216
217 if (disasmText != DE_NULL)
218 *infoLog << disasmText->str << "\n";
219 }
220
221 spvTextDestroy(disasmText);
222 spvValidatorOptionsDestroy(options);
223 spvDiagnosticDestroy(diagnostic);
224 spvContextDestroy(context);
225
226 return passed;
227 }
228 catch (...)
229 {
230 spvTextDestroy(disasmText);
231 spvValidatorOptionsDestroy(options);
232 spvDiagnosticDestroy(diagnostic);
233 spvContextDestroy(context);
234
235 throw;
236 }
237 }
238
239 } // vk
240