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