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
48namespace QtShaderTools {
49namespace glslang {
50
51// Translate glslang's view of target versioning to what SPIRV-Tools uses.
52spv_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
86void 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.
120void 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.
126void 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.
149void 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.
176void 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::CreateEliminateDeadInputComponentsPass());
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// Apply the SPIRV-Tools optimizer to strip debug info from SPIR-V. This is implicitly done by
228// SpirvToolsTransform if spvOptions->stripDebugInfo is set, but can be called separately if
229// optimization is disabled.
230void SpirvToolsStripDebugInfo(const glslang::TIntermediate& intermediate,
231 std::vector<unsigned int>& spirv, spv::SpvBuildLogger* logger)
232{
233 spv_target_env target_env = MapToSpirvToolsEnv(intermediate.getSpv(), logger);
234
235 spvtools::Optimizer optimizer(target_env);
236 optimizer.SetMessageConsumer(OptimizerMesssageConsumer);
237
238 optimizer.RegisterPass(spvtools::CreateStripDebugInfoPass());
239
240 spvtools::OptimizerOptions spvOptOptions;
241 optimizer.SetTargetEnv(MapToSpirvToolsEnv(intermediate.getSpv(), logger));
242 spvOptOptions.set_run_validator(false); // The validator may run as a separate step later on
243 optimizer.Run(spirv.data(), spirv.size(), &spirv, spvOptOptions);
244}
245
246}; // end namespace glslang
247}; // namespace QtShaderTools
248
249#endif
250

source code of qtshadertools/src/3rdparty/glslang/SPIRV/SpvTools.cpp