• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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