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. 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 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 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 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 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 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 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 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