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