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):
47 return SPV_ENV_VULKAN_1_0;
48 case VK_MAKE_API_VERSION(0, 1, 1, 0):
49 return allow_1_4 ? SPV_ENV_VULKAN_1_1_SPIRV_1_4 : SPV_ENV_VULKAN_1_1;
50 case VK_MAKE_API_VERSION(0, 1, 2, 0):
51 return SPV_ENV_VULKAN_1_2;
52 case VK_MAKE_API_VERSION(1, 1, 0, 0):
53 return SPV_ENV_VULKAN_1_2;
54 case VK_MAKE_API_VERSION(0, 1, 3, 0):
55 return SPV_ENV_VULKAN_1_3;
56 default:
57 break;
58 }
59 TCU_THROW(InternalError, "Unexpected Vulkan Version version requested");
60 return SPV_ENV_VULKAN_1_0;
61 }
62
mapTargetSpvEnvironment(SpirvVersion spirvVersion)63 static spv_target_env mapTargetSpvEnvironment(SpirvVersion spirvVersion)
64 {
65 spv_target_env result = SPV_ENV_UNIVERSAL_1_0;
66
67 switch (spirvVersion)
68 {
69 case SPIRV_VERSION_1_0:
70 result = SPV_ENV_UNIVERSAL_1_0;
71 break; //!< SPIR-V 1.0
72 case SPIRV_VERSION_1_1:
73 result = SPV_ENV_UNIVERSAL_1_1;
74 break; //!< SPIR-V 1.1
75 case SPIRV_VERSION_1_2:
76 result = SPV_ENV_UNIVERSAL_1_2;
77 break; //!< SPIR-V 1.2
78 case SPIRV_VERSION_1_3:
79 result = SPV_ENV_UNIVERSAL_1_3;
80 break; //!< SPIR-V 1.3
81 case SPIRV_VERSION_1_4:
82 result = SPV_ENV_UNIVERSAL_1_4;
83 break; //!< SPIR-V 1.4
84 case SPIRV_VERSION_1_5:
85 result = SPV_ENV_UNIVERSAL_1_5;
86 break; //!< SPIR-V 1.5
87 case SPIRV_VERSION_1_6:
88 result = SPV_ENV_UNIVERSAL_1_6;
89 break; //!< SPIR-V 1.6
90 default:
91 TCU_THROW(InternalError, "Unknown SPIR-V version");
92 }
93
94 return result;
95 }
96
assembleSpirV(const SpirVAsmSource * program,std::vector<uint32_t> * dst,SpirVProgramInfo * buildInfo,SpirvVersion spirvVersion)97 bool assembleSpirV(const SpirVAsmSource *program, std::vector<uint32_t> *dst, SpirVProgramInfo *buildInfo,
98 SpirvVersion spirvVersion)
99 {
100 const spv_context context = spvContextCreate(mapTargetSpvEnvironment(spirvVersion));
101 spv_binary binary = DE_NULL;
102 spv_diagnostic diagnostic = DE_NULL;
103
104 if (!context)
105 throw std::bad_alloc();
106
107 try
108 {
109 const std::string &spvSource = program->source;
110 const uint64_t compileStartTime = deGetMicroseconds();
111 const uint32_t options = SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS;
112 const spv_result_t compileOk =
113 spvTextToBinaryWithOptions(context, spvSource.c_str(), spvSource.size(), options, &binary, &diagnostic);
114
115 buildInfo->source = spvSource;
116 buildInfo->infoLog = diagnostic ? diagnostic->error : ""; // \todo [2015-07-13 pyry] Include debug log?
117 buildInfo->compileTimeUs = deGetMicroseconds() - compileStartTime;
118 buildInfo->compileOk = (compileOk == SPV_SUCCESS);
119
120 if (buildInfo->compileOk)
121 {
122 DE_ASSERT(binary->wordCount > 0);
123 dst->resize(binary->wordCount);
124 std::copy(&binary->code[0], &binary->code[0] + binary->wordCount, dst->begin());
125 }
126
127 spvBinaryDestroy(binary);
128 spvDiagnosticDestroy(diagnostic);
129 spvContextDestroy(context);
130
131 return compileOk == SPV_SUCCESS;
132 }
133 catch (...)
134 {
135 spvBinaryDestroy(binary);
136 spvDiagnosticDestroy(diagnostic);
137 spvContextDestroy(context);
138
139 throw;
140 }
141 }
142
disassembleSpirV(size_t binarySizeInWords,const uint32_t * binary,std::ostream * dst,SpirvVersion spirvVersion)143 void disassembleSpirV(size_t binarySizeInWords, const uint32_t *binary, std::ostream *dst, SpirvVersion spirvVersion)
144 {
145 const spv_context context = spvContextCreate(mapTargetSpvEnvironment(spirvVersion));
146 spv_text text = DE_NULL;
147 spv_diagnostic diagnostic = DE_NULL;
148
149 if (!context)
150 throw std::bad_alloc();
151
152 try
153 {
154 const spv_result_t result = spvBinaryToText(context, binary, binarySizeInWords, 0, &text, &diagnostic);
155
156 if (result != SPV_SUCCESS)
157 TCU_THROW(InternalError, "Disassembling SPIR-V failed");
158
159 *dst << text->str;
160
161 spvTextDestroy(text);
162 spvDiagnosticDestroy(diagnostic);
163 spvContextDestroy(context);
164 }
165 catch (...)
166 {
167 spvTextDestroy(text);
168 spvDiagnosticDestroy(diagnostic);
169 spvContextDestroy(context);
170
171 throw;
172 }
173 }
174
validateSpirV(size_t binarySizeInWords,const uint32_t * binary,std::ostream * infoLog,const SpirvValidatorOptions & val_options)175 bool validateSpirV(size_t binarySizeInWords, const uint32_t *binary, std::ostream *infoLog,
176 const SpirvValidatorOptions &val_options)
177 {
178 const spv_context context = spvContextCreate(getSpirvToolsEnvForValidatorOptions(val_options));
179 spv_diagnostic diagnostic = DE_NULL;
180 spv_validator_options options = DE_NULL;
181 spv_text disasmText = DE_NULL;
182
183 if (!context)
184 throw std::bad_alloc();
185
186 try
187 {
188 spv_const_binary_t cbinary = {binary, binarySizeInWords};
189
190 options = spvValidatorOptionsCreate();
191
192 if (options == DE_NULL)
193 throw std::bad_alloc();
194
195 switch (val_options.blockLayout)
196 {
197 case SpirvValidatorOptions::kDefaultBlockLayout:
198 break;
199 case SpirvValidatorOptions::kNoneBlockLayout:
200 spvValidatorOptionsSetSkipBlockLayout(options, true);
201 break;
202 case SpirvValidatorOptions::kRelaxedBlockLayout:
203 spvValidatorOptionsSetRelaxBlockLayout(options, true);
204 break;
205 case SpirvValidatorOptions::kUniformStandardLayout:
206 spvValidatorOptionsSetUniformBufferStandardLayout(options, true);
207 break;
208 case SpirvValidatorOptions::kScalarBlockLayout:
209 spvValidatorOptionsSetScalarBlockLayout(options, true);
210 break;
211 }
212
213 if (val_options.flags & SpirvValidatorOptions::FLAG_SPIRV_VALIDATOR_WORKGROUP_SCALAR_BLOCK_LAYOUT)
214 {
215 spvValidatorOptionsSetWorkgroupScalarBlockLayout(options, true);
216 }
217
218 if (val_options.flags & SpirvValidatorOptions::FLAG_SPIRV_VALIDATOR_ALLOW_LOCALSIZEID)
219 spvValidatorOptionsSetAllowLocalSizeId(options, true);
220
221 const spv_result_t valid = spvValidateWithOptions(context, options, &cbinary, &diagnostic);
222 const bool passed = (valid == SPV_SUCCESS);
223
224 *infoLog << "Validation " << (passed ? "PASSED: " : "FAILED: ");
225
226 if (diagnostic && diagnostic->error)
227 {
228 // Print the diagnostic whether validation passes or fails.
229 // In theory we could get a warning even in the pass case, but there are no cases
230 // like that now.
231 *infoLog << diagnostic->error << "\n";
232
233 const uint32_t disasmOptions = SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES | SPV_BINARY_TO_TEXT_OPTION_INDENT;
234 const spv_result_t disasmResult =
235 spvBinaryToText(context, binary, binarySizeInWords, disasmOptions, &disasmText, DE_NULL);
236
237 if (disasmResult != SPV_SUCCESS)
238 *infoLog << "Disassembly failed with code: " << de::toString(disasmResult) << "\n";
239
240 if (disasmText != DE_NULL)
241 *infoLog << disasmText->str << "\n";
242 }
243
244 spvTextDestroy(disasmText);
245 spvValidatorOptionsDestroy(options);
246 spvDiagnosticDestroy(diagnostic);
247 spvContextDestroy(context);
248
249 return passed;
250 }
251 catch (...)
252 {
253 spvTextDestroy(disasmText);
254 spvValidatorOptionsDestroy(options);
255 spvDiagnosticDestroy(diagnostic);
256 spvContextDestroy(context);
257
258 throw;
259 }
260 }
261
262 } // namespace vk
263