| 1 | // | 
| 2 | // Copyright (C) 2014-2016 LunarG, Inc. | 
| 3 | // Copyright (C) 2018-2020 Google, Inc. | 
| 4 | // | 
| 5 | // All rights reserved. | 
| 6 | // | 
| 7 | // Redistribution and use in source and binary forms, with or without | 
| 8 | // modification, are permitted provided that the following conditions | 
| 9 | // are met: | 
| 10 | // | 
| 11 | //    Redistributions of source code must retain the above copyright | 
| 12 | //    notice, this list of conditions and the following disclaimer. | 
| 13 | // | 
| 14 | //    Redistributions in binary form must reproduce the above | 
| 15 | //    copyright notice, this list of conditions and the following | 
| 16 | //    disclaimer in the documentation and/or other materials provided | 
| 17 | //    with the distribution. | 
| 18 | // | 
| 19 | //    Neither the name of 3Dlabs Inc. Ltd. nor the names of its | 
| 20 | //    contributors may be used to endorse or promote products derived | 
| 21 | //    from this software without specific prior written permission. | 
| 22 | // | 
| 23 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 
| 24 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 
| 25 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | 
| 26 | // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | 
| 27 | // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | 
| 28 | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, | 
| 29 | // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | 
| 30 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | 
| 31 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | 
| 32 | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN | 
| 33 | // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | 
| 34 | // POSSIBILITY OF SUCH DAMAGE. | 
| 35 |  | 
| 36 | // | 
| 37 | // Call into SPIRV-Tools to disassemble, validate, and optimize. | 
| 38 | // | 
| 39 |  | 
| 40 | #if ENABLE_OPT | 
| 41 |  | 
| 42 | #include <cstdio> | 
| 43 | #include <iostream> | 
| 44 |  | 
| 45 | #include "SpvTools.h" | 
| 46 | #include "spirv-tools/optimizer.hpp" | 
| 47 |  | 
| 48 | namespace QtShaderTools { | 
| 49 | namespace glslang { | 
| 50 |  | 
| 51 | // Translate glslang's view of target versioning to what SPIRV-Tools uses. | 
| 52 | spv_target_env MapToSpirvToolsEnv(const SpvVersion& spvVersion, spv::SpvBuildLogger* logger) | 
| 53 | { | 
| 54 |     switch (spvVersion.vulkan) { | 
| 55 |     case glslang::EShTargetVulkan_1_0: | 
| 56 |         return spv_target_env::SPV_ENV_VULKAN_1_0; | 
| 57 |     case glslang::EShTargetVulkan_1_1: | 
| 58 |         switch (spvVersion.spv) { | 
| 59 |         case EShTargetSpv_1_0: | 
| 60 |         case EShTargetSpv_1_1: | 
| 61 |         case EShTargetSpv_1_2: | 
| 62 |         case EShTargetSpv_1_3: | 
| 63 |             return spv_target_env::SPV_ENV_VULKAN_1_1; | 
| 64 |         case EShTargetSpv_1_4: | 
| 65 |             return spv_target_env::SPV_ENV_VULKAN_1_1_SPIRV_1_4; | 
| 66 |         default: | 
| 67 |             logger->missingFunctionality("Target version for SPIRV-Tools validator" ); | 
| 68 |             return spv_target_env::SPV_ENV_VULKAN_1_1; | 
| 69 |         } | 
| 70 |     case glslang::EShTargetVulkan_1_2: | 
| 71 |         return spv_target_env::SPV_ENV_VULKAN_1_2; | 
| 72 |     case glslang::EShTargetVulkan_1_3: | 
| 73 |         return spv_target_env::SPV_ENV_VULKAN_1_3; | 
| 74 |     default: | 
| 75 |         break; | 
| 76 |     } | 
| 77 |  | 
| 78 |     if (spvVersion.openGl > 0) | 
| 79 |         return spv_target_env::SPV_ENV_OPENGL_4_5; | 
| 80 |  | 
| 81 |     logger->missingFunctionality("Target version for SPIRV-Tools validator" ); | 
| 82 |     return spv_target_env::SPV_ENV_UNIVERSAL_1_0; | 
| 83 | } | 
| 84 |  | 
| 85 | // Callback passed to spvtools::Optimizer::SetMessageConsumer | 
| 86 | void OptimizerMesssageConsumer(spv_message_level_t level, const char *source, | 
| 87 |         const spv_position_t &position, const char *message) | 
| 88 | { | 
| 89 |     auto &out = std::cerr; | 
| 90 |     switch (level) | 
| 91 |     { | 
| 92 |     case SPV_MSG_FATAL: | 
| 93 |     case SPV_MSG_INTERNAL_ERROR: | 
| 94 |     case SPV_MSG_ERROR: | 
| 95 |         out << "error: " ; | 
| 96 |         break; | 
| 97 |     case SPV_MSG_WARNING: | 
| 98 |         out << "warning: " ; | 
| 99 |         break; | 
| 100 |     case SPV_MSG_INFO: | 
| 101 |     case SPV_MSG_DEBUG: | 
| 102 |         out << "info: " ; | 
| 103 |         break; | 
| 104 |     default: | 
| 105 |         break; | 
| 106 |     } | 
| 107 |     if (source) | 
| 108 |     { | 
| 109 |         out << source << ":" ; | 
| 110 |     } | 
| 111 |     out << position.line << ":"  << position.column << ":"  << position.index << ":" ; | 
| 112 |     if (message) | 
| 113 |     { | 
| 114 |         out << " "  << message; | 
| 115 |     } | 
| 116 |     out << std::endl; | 
| 117 | } | 
| 118 |  | 
| 119 | // Use the SPIRV-Tools disassembler to print SPIR-V using a SPV_ENV_UNIVERSAL_1_3 environment. | 
| 120 | void SpirvToolsDisassemble(std::ostream& out, const std::vector<unsigned int>& spirv) | 
| 121 | { | 
| 122 |     SpirvToolsDisassemble(out, spirv, spv_target_env::SPV_ENV_UNIVERSAL_1_3); | 
| 123 | } | 
| 124 |  | 
| 125 | // Use the SPIRV-Tools disassembler to print SPIR-V with a provided SPIR-V environment. | 
| 126 | void SpirvToolsDisassemble(std::ostream& out, const std::vector<unsigned int>& spirv, | 
| 127 |                            spv_target_env requested_context) | 
| 128 | { | 
| 129 |     // disassemble | 
| 130 |     spv_context context = spvContextCreate(requested_context); | 
| 131 |     spv_text text; | 
| 132 |     spv_diagnostic diagnostic = nullptr; | 
| 133 |     spvBinaryToText(context, spirv.data(), spirv.size(), | 
| 134 |         SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES | SPV_BINARY_TO_TEXT_OPTION_INDENT, | 
| 135 |         &text, &diagnostic); | 
| 136 |  | 
| 137 |     // dump | 
| 138 |     if (diagnostic == nullptr) | 
| 139 |         out << text->str; | 
| 140 |     else | 
| 141 |         spvDiagnosticPrint(diagnostic); | 
| 142 |  | 
| 143 |     // teardown | 
| 144 |     spvDiagnosticDestroy(diagnostic); | 
| 145 |     spvContextDestroy(context); | 
| 146 | } | 
| 147 |  | 
| 148 | // Apply the SPIRV-Tools validator to generated SPIR-V. | 
| 149 | void SpirvToolsValidate(const glslang::TIntermediate& intermediate, std::vector<unsigned int>& spirv, | 
| 150 |                         spv::SpvBuildLogger* logger, bool prelegalization) | 
| 151 | { | 
| 152 |     // validate | 
| 153 |     spv_context context = spvContextCreate(MapToSpirvToolsEnv(intermediate.getSpv(), logger)); | 
| 154 |     spv_const_binary_t binary = { spirv.data(), spirv.size() }; | 
| 155 |     spv_diagnostic diagnostic = nullptr; | 
| 156 |     spv_validator_options options = spvValidatorOptionsCreate(); | 
| 157 |     spvValidatorOptionsSetRelaxBlockLayout(options, intermediate.usingHlslOffsets()); | 
| 158 |     spvValidatorOptionsSetBeforeHlslLegalization(options, prelegalization); | 
| 159 |     spvValidatorOptionsSetScalarBlockLayout(options, intermediate.usingScalarBlockLayout()); | 
| 160 |     spvValidatorOptionsSetWorkgroupScalarBlockLayout(options, intermediate.usingScalarBlockLayout()); | 
| 161 |     spvValidateWithOptions(context, options, &binary, &diagnostic); | 
| 162 |  | 
| 163 |     // report | 
| 164 |     if (diagnostic != nullptr) { | 
| 165 |         logger->error("SPIRV-Tools Validation Errors" ); | 
| 166 |         logger->error(diagnostic->error); | 
| 167 |     } | 
| 168 |  | 
| 169 |     // tear down | 
| 170 |     spvValidatorOptionsDestroy(options); | 
| 171 |     spvDiagnosticDestroy(diagnostic); | 
| 172 |     spvContextDestroy(context); | 
| 173 | } | 
| 174 |  | 
| 175 | // Apply the SPIRV-Tools optimizer to generated SPIR-V.  HLSL SPIR-V is legalized in the process. | 
| 176 | void SpirvToolsTransform(const glslang::TIntermediate& intermediate, std::vector<unsigned int>& spirv, | 
| 177 |                          spv::SpvBuildLogger* logger, const SpvOptions* options) | 
| 178 | { | 
| 179 |     spv_target_env target_env = MapToSpirvToolsEnv(intermediate.getSpv(), logger); | 
| 180 |  | 
| 181 |     spvtools::Optimizer optimizer(target_env); | 
| 182 |     optimizer.SetMessageConsumer(OptimizerMesssageConsumer); | 
| 183 |  | 
| 184 |     // If debug (specifically source line info) is being generated, propagate | 
| 185 |     // line information into all SPIR-V instructions. This avoids loss of | 
| 186 |     // information when instructions are deleted or moved. Later, remove | 
| 187 |     // redundant information to minimize final SPRIR-V size. | 
| 188 |     if (options->stripDebugInfo) { | 
| 189 |         optimizer.RegisterPass(spvtools::CreateStripDebugInfoPass()); | 
| 190 |     } | 
| 191 |     optimizer.RegisterPass(spvtools::CreateWrapOpKillPass()); | 
| 192 |     optimizer.RegisterPass(spvtools::CreateDeadBranchElimPass()); | 
| 193 |     optimizer.RegisterPass(spvtools::CreateMergeReturnPass()); | 
| 194 |     optimizer.RegisterPass(spvtools::CreateInlineExhaustivePass()); | 
| 195 |     optimizer.RegisterPass(spvtools::CreateEliminateDeadFunctionsPass()); | 
| 196 |     optimizer.RegisterPass(spvtools::CreateScalarReplacementPass()); | 
| 197 |     optimizer.RegisterPass(spvtools::CreateLocalAccessChainConvertPass()); | 
| 198 |     optimizer.RegisterPass(spvtools::CreateLocalSingleBlockLoadStoreElimPass()); | 
| 199 |     optimizer.RegisterPass(spvtools::CreateLocalSingleStoreElimPass()); | 
| 200 |     optimizer.RegisterPass(spvtools::CreateSimplificationPass()); | 
| 201 |     optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass()); | 
| 202 |     optimizer.RegisterPass(spvtools::CreateVectorDCEPass()); | 
| 203 |     optimizer.RegisterPass(spvtools::CreateDeadInsertElimPass()); | 
| 204 |     optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass()); | 
| 205 |     optimizer.RegisterPass(spvtools::CreateDeadBranchElimPass()); | 
| 206 |     optimizer.RegisterPass(spvtools::CreateBlockMergePass()); | 
| 207 |     optimizer.RegisterPass(spvtools::CreateLocalMultiStoreElimPass()); | 
| 208 |     optimizer.RegisterPass(spvtools::CreateIfConversionPass()); | 
| 209 |     optimizer.RegisterPass(spvtools::CreateSimplificationPass()); | 
| 210 |     optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass()); | 
| 211 |     optimizer.RegisterPass(spvtools::CreateVectorDCEPass()); | 
| 212 |     optimizer.RegisterPass(spvtools::CreateDeadInsertElimPass()); | 
| 213 |     optimizer.RegisterPass(spvtools::CreateInterpolateFixupPass()); | 
| 214 |     if (options->optimizeSize) { | 
| 215 |         optimizer.RegisterPass(spvtools::CreateRedundancyEliminationPass()); | 
| 216 |         optimizer.RegisterPass(spvtools::CreateEliminateDeadInputComponentsSafePass()); | 
| 217 |     } | 
| 218 |     optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass()); | 
| 219 |     optimizer.RegisterPass(spvtools::CreateCFGCleanupPass()); | 
| 220 |  | 
| 221 |     spvtools::OptimizerOptions spvOptOptions; | 
| 222 |     optimizer.SetTargetEnv(MapToSpirvToolsEnv(intermediate.getSpv(), logger)); | 
| 223 |     spvOptOptions.set_run_validator(false); // The validator may run as a separate step later on | 
| 224 |     optimizer.Run(spirv.data(), spirv.size(), &spirv, spvOptOptions); | 
| 225 | } | 
| 226 |  | 
| 227 | bool SpirvToolsAnalyzeDeadOutputStores(spv_target_env target_env, std::vector<unsigned int>& spirv, | 
| 228 |                                        std::unordered_set<uint32_t>* live_locs, | 
| 229 |                                        std::unordered_set<uint32_t>* live_builtins, | 
| 230 |                                        spv::SpvBuildLogger*) | 
| 231 | { | 
| 232 |   spvtools::Optimizer optimizer(target_env); | 
| 233 |   optimizer.SetMessageConsumer(OptimizerMesssageConsumer); | 
| 234 |  | 
| 235 |   optimizer.RegisterPass(spvtools::CreateAnalyzeLiveInputPass(live_locs, live_builtins)); | 
| 236 |  | 
| 237 |   spvtools::OptimizerOptions spvOptOptions; | 
| 238 |   optimizer.SetTargetEnv(target_env); | 
| 239 |   spvOptOptions.set_run_validator(false); | 
| 240 |   return optimizer.Run(spirv.data(), spirv.size(), &spirv, spvOptOptions); | 
| 241 | } | 
| 242 |  | 
| 243 | void SpirvToolsEliminateDeadOutputStores(spv_target_env target_env, std::vector<unsigned int>& spirv, | 
| 244 |                                          std::unordered_set<uint32_t>* live_locs, | 
| 245 |                                          std::unordered_set<uint32_t>* live_builtins, | 
| 246 |                                          spv::SpvBuildLogger*) | 
| 247 | { | 
| 248 |   spvtools::Optimizer optimizer(target_env); | 
| 249 |   optimizer.SetMessageConsumer(OptimizerMesssageConsumer); | 
| 250 |  | 
| 251 |   optimizer.RegisterPass(spvtools::CreateEliminateDeadOutputStoresPass(live_locs, live_builtins)); | 
| 252 |   optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass(false, true)); | 
| 253 |   optimizer.RegisterPass(spvtools::CreateEliminateDeadOutputComponentsPass()); | 
| 254 |   optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass(false, true)); | 
| 255 |  | 
| 256 |   spvtools::OptimizerOptions spvOptOptions; | 
| 257 |   optimizer.SetTargetEnv(target_env); | 
| 258 |   spvOptOptions.set_run_validator(false); | 
| 259 |   optimizer.Run(spirv.data(), spirv.size(), &spirv, spvOptOptions); | 
| 260 | } | 
| 261 |  | 
| 262 | void SpirvToolsEliminateDeadInputComponents(spv_target_env target_env, std::vector<unsigned int>& spirv, | 
| 263 |                                             spv::SpvBuildLogger*) | 
| 264 | { | 
| 265 |   spvtools::Optimizer optimizer(target_env); | 
| 266 |   optimizer.SetMessageConsumer(OptimizerMesssageConsumer); | 
| 267 |  | 
| 268 |   optimizer.RegisterPass(spvtools::CreateEliminateDeadInputComponentsPass()); | 
| 269 |   optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass()); | 
| 270 |  | 
| 271 |   spvtools::OptimizerOptions spvOptOptions; | 
| 272 |   optimizer.SetTargetEnv(target_env); | 
| 273 |   spvOptOptions.set_run_validator(false); | 
| 274 |   optimizer.Run(spirv.data(), spirv.size(), &spirv, spvOptOptions); | 
| 275 | } | 
| 276 |  | 
| 277 | // Apply the SPIRV-Tools optimizer to strip debug info from SPIR-V.  This is implicitly done by | 
| 278 | // SpirvToolsTransform if spvOptions->stripDebugInfo is set, but can be called separately if | 
| 279 | // optimization is disabled. | 
| 280 | void SpirvToolsStripDebugInfo(const glslang::TIntermediate& intermediate, | 
| 281 |         std::vector<unsigned int>& spirv, spv::SpvBuildLogger* logger) | 
| 282 | { | 
| 283 |     spv_target_env target_env = MapToSpirvToolsEnv(intermediate.getSpv(), logger); | 
| 284 |  | 
| 285 |     spvtools::Optimizer optimizer(target_env); | 
| 286 |     optimizer.SetMessageConsumer(OptimizerMesssageConsumer); | 
| 287 |  | 
| 288 |     optimizer.RegisterPass(spvtools::CreateStripDebugInfoPass()); | 
| 289 |  | 
| 290 |     spvtools::OptimizerOptions spvOptOptions; | 
| 291 |     optimizer.SetTargetEnv(MapToSpirvToolsEnv(intermediate.getSpv(), logger)); | 
| 292 |     spvOptOptions.set_run_validator(false); // The validator may run as a separate step later on | 
| 293 |     optimizer.Run(spirv.data(), spirv.size(), &spirv, spvOptOptions); | 
| 294 | } | 
| 295 |  | 
| 296 | } // end namespace glslang | 
| 297 | } // namespace QtShaderTools | 
| 298 |  | 
| 299 | #endif | 
| 300 |  |