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 #if defined(DEQP_HAVE_SPIRV_TOOLS)
31 # include "spirv-tools/libspirv.h"
32 #endif
33
34 namespace vk
35 {
36
37 using std::string;
38 using std::vector;
39
40 #if defined(DEQP_HAVE_SPIRV_TOOLS)
41
42 // Convert a Vulkan version number to a SPIRV-Tools target environment enum.
mapVulkanVersionToSpirvToolsEnv(deUint32 vulkanVersion)43 static spv_target_env mapVulkanVersionToSpirvToolsEnv(deUint32 vulkanVersion)
44 {
45 switch (vulkanVersion)
46 {
47 case VK_MAKE_VERSION(1, 0, 0): return SPV_ENV_VULKAN_1_0;
48 case VK_MAKE_VERSION(1, 1, 0): return SPV_ENV_VULKAN_1_1;
49 default:
50 break;
51 }
52 TCU_THROW(InternalError, "Unexpected Vulkan Version version requested");
53 return SPV_ENV_VULKAN_1_0;
54 }
55
mapTargetSpvEnvironment(SpirvVersion spirvVersion)56 static spv_target_env mapTargetSpvEnvironment(SpirvVersion spirvVersion)
57 {
58 spv_target_env result = SPV_ENV_UNIVERSAL_1_0;
59
60 switch (spirvVersion)
61 {
62 case SPIRV_VERSION_1_0: result = SPV_ENV_UNIVERSAL_1_0; break; //!< SPIR-V 1.0
63 case SPIRV_VERSION_1_1: result = SPV_ENV_UNIVERSAL_1_1; break; //!< SPIR-V 1.1
64 case SPIRV_VERSION_1_2: result = SPV_ENV_UNIVERSAL_1_2; break; //!< SPIR-V 1.2
65 case SPIRV_VERSION_1_3: result = SPV_ENV_UNIVERSAL_1_3; break; //!< SPIR-V 1.3
66 default: TCU_THROW(InternalError, "Unknown SPIR-V version");
67 }
68
69 return result;
70 }
71
assembleSpirV(const SpirVAsmSource * program,std::vector<deUint32> * dst,SpirVProgramInfo * buildInfo,SpirvVersion spirvVersion)72 bool assembleSpirV (const SpirVAsmSource* program, std::vector<deUint32>* dst, SpirVProgramInfo* buildInfo, SpirvVersion spirvVersion)
73 {
74 const spv_context context = spvContextCreate(mapTargetSpvEnvironment(spirvVersion));
75 spv_binary binary = DE_NULL;
76 spv_diagnostic diagnostic = DE_NULL;
77
78 if (!context)
79 throw std::bad_alloc();
80
81 try
82 {
83 const std::string& spvSource = program->source;
84 const deUint64 compileStartTime = deGetMicroseconds();
85 const spv_result_t compileOk = spvTextToBinary(context, spvSource.c_str(), spvSource.size(), &binary, &diagnostic);
86
87 buildInfo->source = spvSource;
88 buildInfo->infoLog = diagnostic? diagnostic->error : ""; // \todo [2015-07-13 pyry] Include debug log?
89 buildInfo->compileTimeUs = deGetMicroseconds() - compileStartTime;
90 buildInfo->compileOk = (compileOk == SPV_SUCCESS);
91
92 if (buildInfo->compileOk)
93 {
94 DE_ASSERT(binary->wordCount > 0);
95 dst->resize(binary->wordCount);
96 std::copy(&binary->code[0], &binary->code[0] + binary->wordCount, dst->begin());
97 }
98
99 spvBinaryDestroy(binary);
100 spvDiagnosticDestroy(diagnostic);
101 spvContextDestroy(context);
102
103 return compileOk == SPV_SUCCESS;
104 }
105 catch (...)
106 {
107 spvBinaryDestroy(binary);
108 spvDiagnosticDestroy(diagnostic);
109 spvContextDestroy(context);
110
111 throw;
112 }
113 }
114
disassembleSpirV(size_t binarySizeInWords,const deUint32 * binary,std::ostream * dst,SpirvVersion spirvVersion)115 void disassembleSpirV (size_t binarySizeInWords, const deUint32* binary, std::ostream* dst, SpirvVersion spirvVersion)
116 {
117 const spv_context context = spvContextCreate(mapTargetSpvEnvironment(spirvVersion));
118 spv_text text = DE_NULL;
119 spv_diagnostic diagnostic = DE_NULL;
120
121 if (!context)
122 throw std::bad_alloc();
123
124 try
125 {
126 const spv_result_t result = spvBinaryToText(context, binary, binarySizeInWords, 0, &text, &diagnostic);
127
128 if (result != SPV_SUCCESS)
129 TCU_THROW(InternalError, "Disassembling SPIR-V failed");
130
131 *dst << text->str;
132
133 spvTextDestroy(text);
134 spvDiagnosticDestroy(diagnostic);
135 spvContextDestroy(context);
136 }
137 catch (...)
138 {
139 spvTextDestroy(text);
140 spvDiagnosticDestroy(diagnostic);
141 spvContextDestroy(context);
142
143 throw;
144 }
145 }
146
validateSpirV(size_t binarySizeInWords,const deUint32 * binary,std::ostream * infoLog,const SpirvValidatorOptions & val_options)147 bool validateSpirV (size_t binarySizeInWords, const deUint32* binary, std::ostream* infoLog, const SpirvValidatorOptions &val_options)
148 {
149 const spv_context context = spvContextCreate(mapVulkanVersionToSpirvToolsEnv(val_options.vulkanVersion));
150 spv_diagnostic diagnostic = DE_NULL;
151
152 try
153 {
154 spv_const_binary_t cbinary = { binary, binarySizeInWords };
155
156 spv_validator_options options = spvValidatorOptionsCreate();
157
158 switch (val_options.blockLayout)
159 {
160 case SpirvValidatorOptions::kDefaultBlockLayout:
161 break;
162 case SpirvValidatorOptions::kNoneBlockLayout:
163 spvValidatorOptionsSetSkipBlockLayout(options, true);
164 break;
165 case SpirvValidatorOptions::kRelaxedBlockLayout:
166 spvValidatorOptionsSetRelaxBlockLayout(options, true);
167 break;
168 case SpirvValidatorOptions::kScalarBlockLayout:
169 spvValidatorOptionsSetScalarBlockLayout(options, true);
170 break;
171 }
172
173 const spv_result_t valid = spvValidateWithOptions(context, options, &cbinary, &diagnostic);
174 const bool passed = (valid == SPV_SUCCESS);
175
176 if (diagnostic)
177 {
178 // Print the diagnostic whether validation passes or fails.
179 // In theory we could get a warning even in the pass case, but there are no cases
180 // like that now.
181 *infoLog << "Validation " << (passed ? "PASSED: " : "FAILED: ") << diagnostic->error << "\n";
182
183 spv_text text;
184 spvBinaryToText(context, binary, binarySizeInWords, SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES | SPV_BINARY_TO_TEXT_OPTION_INDENT, &text, DE_NULL);
185
186 *infoLog << text->str << "\n";
187 spvTextDestroy(text);
188 }
189
190 spvValidatorOptionsDestroy(options);
191 spvDiagnosticDestroy(diagnostic);
192 spvContextDestroy(context);
193
194 return passed;
195 }
196 catch (...)
197 {
198 spvDiagnosticDestroy(diagnostic);
199 spvContextDestroy(context);
200
201 throw;
202 }
203 }
204
205 #else // defined(DEQP_HAVE_SPIRV_TOOLS)
206
assembleSpirV(const SpirVAsmSource *,std::vector<deUint32> *,SpirVProgramInfo *,SpirvVersion)207 bool assembleSpirV (const SpirVAsmSource*, std::vector<deUint32>*, SpirVProgramInfo*, SpirvVersion)
208 {
209 TCU_THROW(NotSupportedError, "SPIR-V assembly not supported (DEQP_HAVE_SPIRV_TOOLS not defined)");
210 }
211
disassembleSpirV(size_t,const deUint32 *,std::ostream *,SpirvVersion)212 void disassembleSpirV (size_t, const deUint32*, std::ostream*, SpirvVersion)
213 {
214 TCU_THROW(NotSupportedError, "SPIR-V disassembling not supported (DEQP_HAVE_SPIRV_TOOLS not defined)");
215 }
216
validateSpirV(size_t,const deUint32 *,std::ostream *,const SpirvValidatorOptions &)217 bool validateSpirV (size_t, const deUint32*, std::ostream*, const SpirvValidatorOptions&)
218 {
219 TCU_THROW(NotSupportedError, "SPIR-V validation not supported (DEQP_HAVE_SPIRV_TOOLS not defined)");
220 }
221
222 #endif
223
224 } // vk
225