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#include "glslang/MachineIndependent/localintermediate.h"
48
49namespace QtShaderTools {
50namespace glslang {
51
52// Translate glslang's view of target versioning to what SPIRV-Tools uses.
53spv_target_env MapToSpirvToolsEnv(const SpvVersion& spvVersion, spv::SpvBuildLogger* logger)
54{
55 switch (spvVersion.vulkan) {
56 case glslang::EShTargetVulkan_1_0:
57 return spv_target_env::SPV_ENV_VULKAN_1_0;
58 case glslang::EShTargetVulkan_1_1:
59 switch (spvVersion.spv) {
60 case EShTargetSpv_1_0:
61 case EShTargetSpv_1_1:
62 case EShTargetSpv_1_2:
63 case EShTargetSpv_1_3:
64 return spv_target_env::SPV_ENV_VULKAN_1_1;
65 case EShTargetSpv_1_4:
66 return spv_target_env::SPV_ENV_VULKAN_1_1_SPIRV_1_4;
67 default:
68 logger->missingFunctionality("Target version for SPIRV-Tools validator");
69 return spv_target_env::SPV_ENV_VULKAN_1_1;
70 }
71 case glslang::EShTargetVulkan_1_2:
72 return spv_target_env::SPV_ENV_VULKAN_1_2;
73 case glslang::EShTargetVulkan_1_3:
74 return spv_target_env::SPV_ENV_VULKAN_1_3;
75 case glslang::EShTargetVulkan_1_4:
76 return spv_target_env::SPV_ENV_VULKAN_1_4;
77 default:
78 break;
79 }
80
81 if (spvVersion.openGl > 0)
82 return spv_target_env::SPV_ENV_OPENGL_4_5;
83
84 logger->missingFunctionality("Target version for SPIRV-Tools validator");
85 return spv_target_env::SPV_ENV_UNIVERSAL_1_0;
86}
87
88spv_target_env MapToSpirvToolsEnv(const glslang::TIntermediate& intermediate, spv::SpvBuildLogger* logger)
89{
90 return MapToSpirvToolsEnv(intermediate.getSpv(), logger);
91}
92
93// Callback passed to spvtools::Optimizer::SetMessageConsumer
94void OptimizerMesssageConsumer(spv_message_level_t level, const char *source,
95 const spv_position_t &position, const char *message)
96{
97 auto &out = std::cerr;
98 switch (level)
99 {
100 case SPV_MSG_FATAL:
101 case SPV_MSG_INTERNAL_ERROR:
102 case SPV_MSG_ERROR:
103 out << "error: ";
104 break;
105 case SPV_MSG_WARNING:
106 out << "warning: ";
107 break;
108 case SPV_MSG_INFO:
109 case SPV_MSG_DEBUG:
110 out << "info: ";
111 break;
112 default:
113 break;
114 }
115 if (source)
116 {
117 out << source << ":";
118 }
119 out << position.line << ":" << position.column << ":" << position.index << ":";
120 if (message)
121 {
122 out << " " << message;
123 }
124 out << std::endl;
125}
126
127// Use the SPIRV-Tools disassembler to print SPIR-V using a SPV_ENV_UNIVERSAL_1_3 environment.
128void SpirvToolsDisassemble(std::ostream& out, const std::vector<unsigned int>& spirv)
129{
130 SpirvToolsDisassemble(out, spirv, spv_target_env::SPV_ENV_UNIVERSAL_1_3);
131}
132
133// Use the SPIRV-Tools disassembler to print SPIR-V with a provided SPIR-V environment.
134void SpirvToolsDisassemble(std::ostream& out, const std::vector<unsigned int>& spirv,
135 spv_target_env requested_context)
136{
137 // disassemble
138 spv_context context = spvContextCreate(requested_context);
139 spv_text text;
140 spv_diagnostic diagnostic = nullptr;
141 spvBinaryToText(context, spirv.data(), spirv.size(),
142 SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES | SPV_BINARY_TO_TEXT_OPTION_INDENT,
143 &text, &diagnostic);
144
145 // dump
146 if (diagnostic == nullptr)
147 out << text->str;
148 else
149 spvDiagnosticPrint(diagnostic);
150
151 // teardown
152 spvDiagnosticDestroy(diagnostic);
153 spvContextDestroy(context);
154}
155
156// Apply the SPIRV-Tools validator to generated SPIR-V.
157void SpirvToolsValidate(const glslang::TIntermediate& intermediate, std::vector<unsigned int>& spirv,
158 spv::SpvBuildLogger* logger, bool prelegalization)
159{
160 // validate
161 spv_context context = spvContextCreate(MapToSpirvToolsEnv(intermediate.getSpv(), logger));
162 spv_const_binary_t binary = { spirv.data(), spirv.size() };
163 spv_diagnostic diagnostic = nullptr;
164 spv_validator_options options = spvValidatorOptionsCreate();
165 spvValidatorOptionsSetRelaxBlockLayout(options, intermediate.usingHlslOffsets());
166 spvValidatorOptionsSetBeforeHlslLegalization(options, prelegalization);
167 spvValidatorOptionsSetScalarBlockLayout(options, intermediate.usingScalarBlockLayout());
168 spvValidatorOptionsSetWorkgroupScalarBlockLayout(options, intermediate.usingScalarBlockLayout());
169 spvValidateWithOptions(context, options, &binary, &diagnostic);
170
171 // report
172 if (diagnostic != nullptr) {
173 logger->error("SPIRV-Tools Validation Errors");
174 logger->error(diagnostic->error);
175 }
176
177 // tear down
178 spvValidatorOptionsDestroy(options);
179 spvDiagnosticDestroy(diagnostic);
180 spvContextDestroy(context);
181}
182
183// Apply the SPIRV-Tools optimizer to generated SPIR-V. HLSL SPIR-V is legalized in the process.
184void SpirvToolsTransform(const glslang::TIntermediate& intermediate, std::vector<unsigned int>& spirv,
185 spv::SpvBuildLogger* logger, const SpvOptions* options)
186{
187 spv_target_env target_env = MapToSpirvToolsEnv(intermediate.getSpv(), logger);
188
189 spvtools::Optimizer optimizer(target_env);
190 optimizer.SetMessageConsumer(OptimizerMesssageConsumer);
191
192 // If debug (specifically source line info) is being generated, propagate
193 // line information into all SPIR-V instructions. This avoids loss of
194 // information when instructions are deleted or moved. Later, remove
195 // redundant information to minimize final SPRIR-V size.
196 if (options->stripDebugInfo) {
197 optimizer.RegisterPass(spvtools::CreateStripDebugInfoPass());
198 }
199 optimizer.RegisterPass(spvtools::CreateWrapOpKillPass());
200 optimizer.RegisterPass(spvtools::CreateDeadBranchElimPass());
201 optimizer.RegisterPass(spvtools::CreateMergeReturnPass());
202 optimizer.RegisterPass(spvtools::CreateInlineExhaustivePass());
203 optimizer.RegisterPass(spvtools::CreateEliminateDeadFunctionsPass());
204 optimizer.RegisterPass(spvtools::CreateScalarReplacementPass());
205 optimizer.RegisterPass(spvtools::CreateLocalAccessChainConvertPass());
206 optimizer.RegisterPass(spvtools::CreateLocalSingleBlockLoadStoreElimPass());
207 optimizer.RegisterPass(spvtools::CreateLocalSingleStoreElimPass());
208 optimizer.RegisterPass(spvtools::CreateSimplificationPass());
209 optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
210 optimizer.RegisterPass(spvtools::CreateVectorDCEPass());
211 optimizer.RegisterPass(spvtools::CreateDeadInsertElimPass());
212 optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
213 optimizer.RegisterPass(spvtools::CreateDeadBranchElimPass());
214 optimizer.RegisterPass(spvtools::CreateBlockMergePass());
215 optimizer.RegisterPass(spvtools::CreateLocalMultiStoreElimPass());
216 optimizer.RegisterPass(spvtools::CreateIfConversionPass());
217 optimizer.RegisterPass(spvtools::CreateSimplificationPass());
218 optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
219 optimizer.RegisterPass(spvtools::CreateVectorDCEPass());
220 optimizer.RegisterPass(spvtools::CreateDeadInsertElimPass());
221 optimizer.RegisterPass(spvtools::CreateInterpolateFixupPass());
222 if (options->optimizeSize) {
223 optimizer.RegisterPass(spvtools::CreateRedundancyEliminationPass());
224 optimizer.RegisterPass(spvtools::CreateEliminateDeadInputComponentsSafePass());
225 }
226 optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
227 optimizer.RegisterPass(spvtools::CreateCFGCleanupPass());
228
229 spvtools::OptimizerOptions spvOptOptions;
230 if (options->optimizerAllowExpandedIDBound)
231 spvOptOptions.set_max_id_bound(0x3FFFFFFF);
232 optimizer.SetTargetEnv(MapToSpirvToolsEnv(intermediate.getSpv(), logger));
233 spvOptOptions.set_run_validator(false); // The validator may run as a separate step later on
234 optimizer.Run(spirv.data(), spirv.size(), &spirv, spvOptOptions);
235
236 if (options->optimizerAllowExpandedIDBound) {
237 if (spirv.size() > 3 && spirv[3] > kDefaultMaxIdBound) {
238 spvtools::Optimizer optimizer2(target_env);
239 optimizer2.SetMessageConsumer(OptimizerMesssageConsumer);
240 optimizer2.RegisterPass(spvtools::CreateCompactIdsPass());
241 optimizer2.Run(spirv.data(), spirv.size(), &spirv, spvOptOptions);
242 }
243 }
244}
245
246bool SpirvToolsAnalyzeDeadOutputStores(spv_target_env target_env, std::vector<unsigned int>& spirv,
247 std::unordered_set<uint32_t>* live_locs,
248 std::unordered_set<uint32_t>* live_builtins,
249 spv::SpvBuildLogger*)
250{
251 spvtools::Optimizer optimizer(target_env);
252 optimizer.SetMessageConsumer(OptimizerMesssageConsumer);
253
254 optimizer.RegisterPass(spvtools::CreateAnalyzeLiveInputPass(live_locs, live_builtins));
255
256 spvtools::OptimizerOptions spvOptOptions;
257 optimizer.SetTargetEnv(target_env);
258 spvOptOptions.set_run_validator(false);
259 return optimizer.Run(spirv.data(), spirv.size(), &spirv, spvOptOptions);
260}
261
262void SpirvToolsEliminateDeadOutputStores(spv_target_env target_env, std::vector<unsigned int>& spirv,
263 std::unordered_set<uint32_t>* live_locs,
264 std::unordered_set<uint32_t>* live_builtins,
265 spv::SpvBuildLogger*)
266{
267 spvtools::Optimizer optimizer(target_env);
268 optimizer.SetMessageConsumer(OptimizerMesssageConsumer);
269
270 optimizer.RegisterPass(spvtools::CreateEliminateDeadOutputStoresPass(live_locs, live_builtins));
271 optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass(false, true));
272 optimizer.RegisterPass(spvtools::CreateEliminateDeadOutputComponentsPass());
273 optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass(false, true));
274
275 spvtools::OptimizerOptions spvOptOptions;
276 optimizer.SetTargetEnv(target_env);
277 spvOptOptions.set_run_validator(false);
278 optimizer.Run(spirv.data(), spirv.size(), &spirv, spvOptOptions);
279}
280
281void SpirvToolsEliminateDeadInputComponents(spv_target_env target_env, std::vector<unsigned int>& spirv,
282 spv::SpvBuildLogger*)
283{
284 spvtools::Optimizer optimizer(target_env);
285 optimizer.SetMessageConsumer(OptimizerMesssageConsumer);
286
287 optimizer.RegisterPass(spvtools::CreateEliminateDeadInputComponentsPass());
288 optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
289
290 spvtools::OptimizerOptions spvOptOptions;
291 optimizer.SetTargetEnv(target_env);
292 spvOptOptions.set_run_validator(false);
293 optimizer.Run(spirv.data(), spirv.size(), &spirv, spvOptOptions);
294}
295
296// Apply the SPIRV-Tools optimizer to strip debug info from SPIR-V. This is implicitly done by
297// SpirvToolsTransform if spvOptions->stripDebugInfo is set, but can be called separately if
298// optimization is disabled.
299void SpirvToolsStripDebugInfo(const glslang::TIntermediate& intermediate,
300 std::vector<unsigned int>& spirv, spv::SpvBuildLogger* logger)
301{
302 spv_target_env target_env = MapToSpirvToolsEnv(intermediate.getSpv(), logger);
303
304 spvtools::Optimizer optimizer(target_env);
305 optimizer.SetMessageConsumer(OptimizerMesssageConsumer);
306
307 optimizer.RegisterPass(spvtools::CreateStripDebugInfoPass());
308
309 spvtools::OptimizerOptions spvOptOptions;
310 optimizer.SetTargetEnv(MapToSpirvToolsEnv(intermediate.getSpv(), logger));
311 spvOptOptions.set_run_validator(false); // The validator may run as a separate step later on
312 optimizer.Run(spirv.data(), spirv.size(), &spirv, spvOptOptions);
313}
314
315} // end namespace glslang
316} // namespace QtShaderTools
317
318#endif
319

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