1 // Copyright (c) 2016 Google Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "spirv-tools/optimizer.hpp"
16
17 #include <cassert>
18 #include <memory>
19 #include <string>
20 #include <unordered_map>
21 #include <utility>
22 #include <vector>
23
24 #include "source/opt/build_module.h"
25 #include "source/opt/graphics_robust_access_pass.h"
26 #include "source/opt/log.h"
27 #include "source/opt/pass_manager.h"
28 #include "source/opt/passes.h"
29 #include "source/spirv_optimizer_options.h"
30 #include "source/util/make_unique.h"
31 #include "source/util/string_utils.h"
32
33 namespace spvtools {
34
35 struct Optimizer::PassToken::Impl {
Implspvtools::Optimizer::PassToken::Impl36 Impl(std::unique_ptr<opt::Pass> p) : pass(std::move(p)) {}
37
38 std::unique_ptr<opt::Pass> pass; // Internal implementation pass.
39 };
40
PassToken(std::unique_ptr<Optimizer::PassToken::Impl> impl)41 Optimizer::PassToken::PassToken(
42 std::unique_ptr<Optimizer::PassToken::Impl> impl)
43 : impl_(std::move(impl)) {}
44
PassToken(std::unique_ptr<opt::Pass> && pass)45 Optimizer::PassToken::PassToken(std::unique_ptr<opt::Pass>&& pass)
46 : impl_(MakeUnique<Optimizer::PassToken::Impl>(std::move(pass))) {}
47
PassToken(PassToken && that)48 Optimizer::PassToken::PassToken(PassToken&& that)
49 : impl_(std::move(that.impl_)) {}
50
operator =(PassToken && that)51 Optimizer::PassToken& Optimizer::PassToken::operator=(PassToken&& that) {
52 impl_ = std::move(that.impl_);
53 return *this;
54 }
55
~PassToken()56 Optimizer::PassToken::~PassToken() {}
57
58 struct Optimizer::Impl {
Implspvtools::Optimizer::Impl59 explicit Impl(spv_target_env env) : target_env(env), pass_manager() {}
60
61 spv_target_env target_env; // Target environment.
62 opt::PassManager pass_manager; // Internal implementation pass manager.
63 };
64
Optimizer(spv_target_env env)65 Optimizer::Optimizer(spv_target_env env) : impl_(new Impl(env)) {
66 assert(env != SPV_ENV_WEBGPU_0);
67 }
68
~Optimizer()69 Optimizer::~Optimizer() {}
70
SetMessageConsumer(MessageConsumer c)71 void Optimizer::SetMessageConsumer(MessageConsumer c) {
72 // All passes' message consumer needs to be updated.
73 for (uint32_t i = 0; i < impl_->pass_manager.NumPasses(); ++i) {
74 impl_->pass_manager.GetPass(i)->SetMessageConsumer(c);
75 }
76 impl_->pass_manager.SetMessageConsumer(std::move(c));
77 }
78
consumer() const79 const MessageConsumer& Optimizer::consumer() const {
80 return impl_->pass_manager.consumer();
81 }
82
RegisterPass(PassToken && p)83 Optimizer& Optimizer::RegisterPass(PassToken&& p) {
84 // Change to use the pass manager's consumer.
85 p.impl_->pass->SetMessageConsumer(consumer());
86 impl_->pass_manager.AddPass(std::move(p.impl_->pass));
87 return *this;
88 }
89
90 // The legalization passes take a spir-v shader generated by an HLSL front-end
91 // and turn it into a valid vulkan spir-v shader. There are two ways in which
92 // the code will be invalid at the start:
93 //
94 // 1) There will be opaque objects, like images, which will be passed around
95 // in intermediate objects. Valid spir-v will have to replace the use of
96 // the opaque object with an intermediate object that is the result of the
97 // load of the global opaque object.
98 //
99 // 2) There will be variables that contain pointers to structured or uniform
100 // buffers. It be legal, the variables must be eliminated, and the
101 // references to the structured buffers must use the result of OpVariable
102 // in the Uniform storage class.
103 //
104 // Optimization in this list must accept shaders with these relaxation of the
105 // rules. There is not guarantee that this list of optimizations is able to
106 // legalize all inputs, but it is on a best effort basis.
107 //
108 // The legalization problem is essentially a very general copy propagation
109 // problem. The optimization we use are all used to either do copy propagation
110 // or enable more copy propagation.
RegisterLegalizationPasses()111 Optimizer& Optimizer::RegisterLegalizationPasses() {
112 return
113 // Wrap OpKill instructions so all other code can be inlined.
114 RegisterPass(CreateWrapOpKillPass())
115 // Remove unreachable block so that merge return works.
116 .RegisterPass(CreateDeadBranchElimPass())
117 // Merge the returns so we can inline.
118 .RegisterPass(CreateMergeReturnPass())
119 // Make sure uses and definitions are in the same function.
120 .RegisterPass(CreateInlineExhaustivePass())
121 // Make private variable function scope
122 .RegisterPass(CreateEliminateDeadFunctionsPass())
123 .RegisterPass(CreatePrivateToLocalPass())
124 // Fix up the storage classes that DXC may have purposely generated
125 // incorrectly. All functions are inlined, and a lot of dead code has
126 // been removed.
127 .RegisterPass(CreateFixStorageClassPass())
128 // Propagate the value stored to the loads in very simple cases.
129 .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
130 .RegisterPass(CreateLocalSingleStoreElimPass())
131 .RegisterPass(CreateAggressiveDCEPass())
132 // Split up aggregates so they are easier to deal with.
133 .RegisterPass(CreateScalarReplacementPass(0))
134 // Remove loads and stores so everything is in intermediate values.
135 // Takes care of copy propagation of non-members.
136 .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
137 .RegisterPass(CreateLocalSingleStoreElimPass())
138 .RegisterPass(CreateAggressiveDCEPass())
139 .RegisterPass(CreateLocalMultiStoreElimPass())
140 .RegisterPass(CreateAggressiveDCEPass())
141 // Propagate constants to get as many constant conditions on branches
142 // as possible.
143 .RegisterPass(CreateCCPPass())
144 .RegisterPass(CreateLoopUnrollPass(true))
145 .RegisterPass(CreateDeadBranchElimPass())
146 // Copy propagate members. Cleans up code sequences generated by
147 // scalar replacement. Also important for removing OpPhi nodes.
148 .RegisterPass(CreateSimplificationPass())
149 .RegisterPass(CreateAggressiveDCEPass())
150 .RegisterPass(CreateCopyPropagateArraysPass())
151 // May need loop unrolling here see
152 // https://github.com/Microsoft/DirectXShaderCompiler/pull/930
153 // Get rid of unused code that contain traces of illegal code
154 // or unused references to unbound external objects
155 .RegisterPass(CreateVectorDCEPass())
156 .RegisterPass(CreateDeadInsertElimPass())
157 .RegisterPass(CreateReduceLoadSizePass())
158 .RegisterPass(CreateAggressiveDCEPass())
159 .RegisterPass(CreateInterpolateFixupPass());
160 }
161
RegisterPerformancePasses()162 Optimizer& Optimizer::RegisterPerformancePasses() {
163 return RegisterPass(CreateWrapOpKillPass())
164 .RegisterPass(CreateDeadBranchElimPass())
165 .RegisterPass(CreateMergeReturnPass())
166 .RegisterPass(CreateInlineExhaustivePass())
167 .RegisterPass(CreateEliminateDeadFunctionsPass())
168 .RegisterPass(CreateAggressiveDCEPass())
169 .RegisterPass(CreatePrivateToLocalPass())
170 .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
171 .RegisterPass(CreateLocalSingleStoreElimPass())
172 .RegisterPass(CreateAggressiveDCEPass())
173 .RegisterPass(CreateScalarReplacementPass())
174 .RegisterPass(CreateLocalAccessChainConvertPass())
175 .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
176 .RegisterPass(CreateLocalSingleStoreElimPass())
177 .RegisterPass(CreateAggressiveDCEPass())
178 .RegisterPass(CreateLocalMultiStoreElimPass())
179 .RegisterPass(CreateAggressiveDCEPass())
180 .RegisterPass(CreateCCPPass())
181 .RegisterPass(CreateAggressiveDCEPass())
182 .RegisterPass(CreateLoopUnrollPass(true))
183 .RegisterPass(CreateDeadBranchElimPass())
184 .RegisterPass(CreateRedundancyEliminationPass())
185 .RegisterPass(CreateCombineAccessChainsPass())
186 .RegisterPass(CreateSimplificationPass())
187 .RegisterPass(CreateScalarReplacementPass())
188 .RegisterPass(CreateLocalAccessChainConvertPass())
189 .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
190 .RegisterPass(CreateLocalSingleStoreElimPass())
191 .RegisterPass(CreateAggressiveDCEPass())
192 .RegisterPass(CreateSSARewritePass())
193 .RegisterPass(CreateAggressiveDCEPass())
194 .RegisterPass(CreateVectorDCEPass())
195 .RegisterPass(CreateDeadInsertElimPass())
196 .RegisterPass(CreateDeadBranchElimPass())
197 .RegisterPass(CreateSimplificationPass())
198 .RegisterPass(CreateIfConversionPass())
199 .RegisterPass(CreateCopyPropagateArraysPass())
200 .RegisterPass(CreateReduceLoadSizePass())
201 .RegisterPass(CreateAggressiveDCEPass())
202 .RegisterPass(CreateBlockMergePass())
203 .RegisterPass(CreateRedundancyEliminationPass())
204 .RegisterPass(CreateDeadBranchElimPass())
205 .RegisterPass(CreateBlockMergePass())
206 .RegisterPass(CreateSimplificationPass());
207 }
208
RegisterSizePasses()209 Optimizer& Optimizer::RegisterSizePasses() {
210 return RegisterPass(CreateWrapOpKillPass())
211 .RegisterPass(CreateDeadBranchElimPass())
212 .RegisterPass(CreateMergeReturnPass())
213 .RegisterPass(CreateInlineExhaustivePass())
214 .RegisterPass(CreateEliminateDeadFunctionsPass())
215 .RegisterPass(CreatePrivateToLocalPass())
216 .RegisterPass(CreateScalarReplacementPass(0))
217 .RegisterPass(CreateLocalMultiStoreElimPass())
218 .RegisterPass(CreateCCPPass())
219 .RegisterPass(CreateLoopUnrollPass(true))
220 .RegisterPass(CreateDeadBranchElimPass())
221 .RegisterPass(CreateSimplificationPass())
222 .RegisterPass(CreateScalarReplacementPass(0))
223 .RegisterPass(CreateLocalSingleStoreElimPass())
224 .RegisterPass(CreateIfConversionPass())
225 .RegisterPass(CreateSimplificationPass())
226 .RegisterPass(CreateAggressiveDCEPass())
227 .RegisterPass(CreateDeadBranchElimPass())
228 .RegisterPass(CreateBlockMergePass())
229 .RegisterPass(CreateLocalAccessChainConvertPass())
230 .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
231 .RegisterPass(CreateAggressiveDCEPass())
232 .RegisterPass(CreateCopyPropagateArraysPass())
233 .RegisterPass(CreateVectorDCEPass())
234 .RegisterPass(CreateDeadInsertElimPass())
235 .RegisterPass(CreateEliminateDeadMembersPass())
236 .RegisterPass(CreateLocalSingleStoreElimPass())
237 .RegisterPass(CreateBlockMergePass())
238 .RegisterPass(CreateLocalMultiStoreElimPass())
239 .RegisterPass(CreateRedundancyEliminationPass())
240 .RegisterPass(CreateSimplificationPass())
241 .RegisterPass(CreateAggressiveDCEPass())
242 .RegisterPass(CreateCFGCleanupPass());
243 }
244
RegisterPassesFromFlags(const std::vector<std::string> & flags)245 bool Optimizer::RegisterPassesFromFlags(const std::vector<std::string>& flags) {
246 for (const auto& flag : flags) {
247 if (!RegisterPassFromFlag(flag)) {
248 return false;
249 }
250 }
251
252 return true;
253 }
254
FlagHasValidForm(const std::string & flag) const255 bool Optimizer::FlagHasValidForm(const std::string& flag) const {
256 if (flag == "-O" || flag == "-Os") {
257 return true;
258 } else if (flag.size() > 2 && flag.substr(0, 2) == "--") {
259 return true;
260 }
261
262 Errorf(consumer(), nullptr, {},
263 "%s is not a valid flag. Flag passes should have the form "
264 "'--pass_name[=pass_args]'. Special flag names also accepted: -O "
265 "and -Os.",
266 flag.c_str());
267 return false;
268 }
269
RegisterPassFromFlag(const std::string & flag)270 bool Optimizer::RegisterPassFromFlag(const std::string& flag) {
271 if (!FlagHasValidForm(flag)) {
272 return false;
273 }
274
275 // Split flags of the form --pass_name=pass_args.
276 auto p = utils::SplitFlagArgs(flag);
277 std::string pass_name = p.first;
278 std::string pass_args = p.second;
279
280 // FIXME(dnovillo): This should be re-factored so that pass names can be
281 // automatically checked against Pass::name() and PassToken instances created
282 // via a template function. Additionally, class Pass should have a desc()
283 // method that describes the pass (so it can be used in --help).
284 //
285 // Both Pass::name() and Pass::desc() should be static class members so they
286 // can be invoked without creating a pass instance.
287 if (pass_name == "strip-debug") {
288 RegisterPass(CreateStripDebugInfoPass());
289 } else if (pass_name == "strip-reflect") {
290 RegisterPass(CreateStripReflectInfoPass());
291 } else if (pass_name == "set-spec-const-default-value") {
292 if (pass_args.size() > 0) {
293 auto spec_ids_vals =
294 opt::SetSpecConstantDefaultValuePass::ParseDefaultValuesString(
295 pass_args.c_str());
296 if (!spec_ids_vals) {
297 Errorf(consumer(), nullptr, {},
298 "Invalid argument for --set-spec-const-default-value: %s",
299 pass_args.c_str());
300 return false;
301 }
302 RegisterPass(
303 CreateSetSpecConstantDefaultValuePass(std::move(*spec_ids_vals)));
304 } else {
305 Errorf(consumer(), nullptr, {},
306 "Invalid spec constant value string '%s'. Expected a string of "
307 "<spec id>:<default value> pairs.",
308 pass_args.c_str());
309 return false;
310 }
311 } else if (pass_name == "if-conversion") {
312 RegisterPass(CreateIfConversionPass());
313 } else if (pass_name == "freeze-spec-const") {
314 RegisterPass(CreateFreezeSpecConstantValuePass());
315 } else if (pass_name == "inline-entry-points-exhaustive") {
316 RegisterPass(CreateInlineExhaustivePass());
317 } else if (pass_name == "inline-entry-points-opaque") {
318 RegisterPass(CreateInlineOpaquePass());
319 } else if (pass_name == "combine-access-chains") {
320 RegisterPass(CreateCombineAccessChainsPass());
321 } else if (pass_name == "convert-local-access-chains") {
322 RegisterPass(CreateLocalAccessChainConvertPass());
323 } else if (pass_name == "descriptor-scalar-replacement") {
324 RegisterPass(CreateDescriptorScalarReplacementPass());
325 } else if (pass_name == "eliminate-dead-code-aggressive") {
326 RegisterPass(CreateAggressiveDCEPass());
327 } else if (pass_name == "eliminate-insert-extract") {
328 RegisterPass(CreateInsertExtractElimPass());
329 } else if (pass_name == "eliminate-local-single-block") {
330 RegisterPass(CreateLocalSingleBlockLoadStoreElimPass());
331 } else if (pass_name == "eliminate-local-single-store") {
332 RegisterPass(CreateLocalSingleStoreElimPass());
333 } else if (pass_name == "merge-blocks") {
334 RegisterPass(CreateBlockMergePass());
335 } else if (pass_name == "merge-return") {
336 RegisterPass(CreateMergeReturnPass());
337 } else if (pass_name == "eliminate-dead-branches") {
338 RegisterPass(CreateDeadBranchElimPass());
339 } else if (pass_name == "eliminate-dead-functions") {
340 RegisterPass(CreateEliminateDeadFunctionsPass());
341 } else if (pass_name == "eliminate-local-multi-store") {
342 RegisterPass(CreateLocalMultiStoreElimPass());
343 } else if (pass_name == "eliminate-dead-const") {
344 RegisterPass(CreateEliminateDeadConstantPass());
345 } else if (pass_name == "eliminate-dead-inserts") {
346 RegisterPass(CreateDeadInsertElimPass());
347 } else if (pass_name == "eliminate-dead-variables") {
348 RegisterPass(CreateDeadVariableEliminationPass());
349 } else if (pass_name == "eliminate-dead-members") {
350 RegisterPass(CreateEliminateDeadMembersPass());
351 } else if (pass_name == "fold-spec-const-op-composite") {
352 RegisterPass(CreateFoldSpecConstantOpAndCompositePass());
353 } else if (pass_name == "loop-unswitch") {
354 RegisterPass(CreateLoopUnswitchPass());
355 } else if (pass_name == "scalar-replacement") {
356 if (pass_args.size() == 0) {
357 RegisterPass(CreateScalarReplacementPass());
358 } else {
359 int limit = -1;
360 if (pass_args.find_first_not_of("0123456789") == std::string::npos) {
361 limit = atoi(pass_args.c_str());
362 }
363
364 if (limit >= 0) {
365 RegisterPass(CreateScalarReplacementPass(limit));
366 } else {
367 Error(consumer(), nullptr, {},
368 "--scalar-replacement must have no arguments or a non-negative "
369 "integer argument");
370 return false;
371 }
372 }
373 } else if (pass_name == "strength-reduction") {
374 RegisterPass(CreateStrengthReductionPass());
375 } else if (pass_name == "unify-const") {
376 RegisterPass(CreateUnifyConstantPass());
377 } else if (pass_name == "flatten-decorations") {
378 RegisterPass(CreateFlattenDecorationPass());
379 } else if (pass_name == "compact-ids") {
380 RegisterPass(CreateCompactIdsPass());
381 } else if (pass_name == "cfg-cleanup") {
382 RegisterPass(CreateCFGCleanupPass());
383 } else if (pass_name == "local-redundancy-elimination") {
384 RegisterPass(CreateLocalRedundancyEliminationPass());
385 } else if (pass_name == "loop-invariant-code-motion") {
386 RegisterPass(CreateLoopInvariantCodeMotionPass());
387 } else if (pass_name == "reduce-load-size") {
388 RegisterPass(CreateReduceLoadSizePass());
389 } else if (pass_name == "redundancy-elimination") {
390 RegisterPass(CreateRedundancyEliminationPass());
391 } else if (pass_name == "private-to-local") {
392 RegisterPass(CreatePrivateToLocalPass());
393 } else if (pass_name == "remove-duplicates") {
394 RegisterPass(CreateRemoveDuplicatesPass());
395 } else if (pass_name == "workaround-1209") {
396 RegisterPass(CreateWorkaround1209Pass());
397 } else if (pass_name == "replace-invalid-opcode") {
398 RegisterPass(CreateReplaceInvalidOpcodePass());
399 } else if (pass_name == "inst-bindless-check") {
400 RegisterPass(CreateInstBindlessCheckPass(7, 23, false, false));
401 RegisterPass(CreateSimplificationPass());
402 RegisterPass(CreateDeadBranchElimPass());
403 RegisterPass(CreateBlockMergePass());
404 RegisterPass(CreateAggressiveDCEPass());
405 } else if (pass_name == "inst-desc-idx-check") {
406 RegisterPass(CreateInstBindlessCheckPass(7, 23, true, true));
407 RegisterPass(CreateSimplificationPass());
408 RegisterPass(CreateDeadBranchElimPass());
409 RegisterPass(CreateBlockMergePass());
410 RegisterPass(CreateAggressiveDCEPass());
411 } else if (pass_name == "inst-buff-oob-check") {
412 RegisterPass(CreateInstBindlessCheckPass(7, 23, false, false, true, true));
413 RegisterPass(CreateSimplificationPass());
414 RegisterPass(CreateDeadBranchElimPass());
415 RegisterPass(CreateBlockMergePass());
416 RegisterPass(CreateAggressiveDCEPass());
417 } else if (pass_name == "inst-buff-addr-check") {
418 RegisterPass(CreateInstBuffAddrCheckPass(7, 23));
419 RegisterPass(CreateAggressiveDCEPass());
420 } else if (pass_name == "convert-relaxed-to-half") {
421 RegisterPass(CreateConvertRelaxedToHalfPass());
422 } else if (pass_name == "relax-float-ops") {
423 RegisterPass(CreateRelaxFloatOpsPass());
424 } else if (pass_name == "inst-debug-printf") {
425 RegisterPass(CreateInstDebugPrintfPass(7, 23));
426 } else if (pass_name == "simplify-instructions") {
427 RegisterPass(CreateSimplificationPass());
428 } else if (pass_name == "ssa-rewrite") {
429 RegisterPass(CreateSSARewritePass());
430 } else if (pass_name == "copy-propagate-arrays") {
431 RegisterPass(CreateCopyPropagateArraysPass());
432 } else if (pass_name == "loop-fission") {
433 int register_threshold_to_split =
434 (pass_args.size() > 0) ? atoi(pass_args.c_str()) : -1;
435 if (register_threshold_to_split > 0) {
436 RegisterPass(CreateLoopFissionPass(
437 static_cast<size_t>(register_threshold_to_split)));
438 } else {
439 Error(consumer(), nullptr, {},
440 "--loop-fission must have a positive integer argument");
441 return false;
442 }
443 } else if (pass_name == "loop-fusion") {
444 int max_registers_per_loop =
445 (pass_args.size() > 0) ? atoi(pass_args.c_str()) : -1;
446 if (max_registers_per_loop > 0) {
447 RegisterPass(
448 CreateLoopFusionPass(static_cast<size_t>(max_registers_per_loop)));
449 } else {
450 Error(consumer(), nullptr, {},
451 "--loop-fusion must have a positive integer argument");
452 return false;
453 }
454 } else if (pass_name == "loop-unroll") {
455 RegisterPass(CreateLoopUnrollPass(true));
456 } else if (pass_name == "upgrade-memory-model") {
457 RegisterPass(CreateUpgradeMemoryModelPass());
458 } else if (pass_name == "vector-dce") {
459 RegisterPass(CreateVectorDCEPass());
460 } else if (pass_name == "loop-unroll-partial") {
461 int factor = (pass_args.size() > 0) ? atoi(pass_args.c_str()) : 0;
462 if (factor > 0) {
463 RegisterPass(CreateLoopUnrollPass(false, factor));
464 } else {
465 Error(consumer(), nullptr, {},
466 "--loop-unroll-partial must have a positive integer argument");
467 return false;
468 }
469 } else if (pass_name == "loop-peeling") {
470 RegisterPass(CreateLoopPeelingPass());
471 } else if (pass_name == "loop-peeling-threshold") {
472 int factor = (pass_args.size() > 0) ? atoi(pass_args.c_str()) : 0;
473 if (factor > 0) {
474 opt::LoopPeelingPass::SetLoopPeelingThreshold(factor);
475 } else {
476 Error(consumer(), nullptr, {},
477 "--loop-peeling-threshold must have a positive integer argument");
478 return false;
479 }
480 } else if (pass_name == "ccp") {
481 RegisterPass(CreateCCPPass());
482 } else if (pass_name == "code-sink") {
483 RegisterPass(CreateCodeSinkingPass());
484 } else if (pass_name == "fix-storage-class") {
485 RegisterPass(CreateFixStorageClassPass());
486 } else if (pass_name == "O") {
487 RegisterPerformancePasses();
488 } else if (pass_name == "Os") {
489 RegisterSizePasses();
490 } else if (pass_name == "legalize-hlsl") {
491 RegisterLegalizationPasses();
492 } else if (pass_name == "graphics-robust-access") {
493 RegisterPass(CreateGraphicsRobustAccessPass());
494 } else if (pass_name == "wrap-opkill") {
495 RegisterPass(CreateWrapOpKillPass());
496 } else if (pass_name == "amd-ext-to-khr") {
497 RegisterPass(CreateAmdExtToKhrPass());
498 } else if (pass_name == "interpolate-fixup") {
499 RegisterPass(CreateInterpolateFixupPass());
500 } else {
501 Errorf(consumer(), nullptr, {},
502 "Unknown flag '--%s'. Use --help for a list of valid flags",
503 pass_name.c_str());
504 return false;
505 }
506
507 return true;
508 }
509
SetTargetEnv(const spv_target_env env)510 void Optimizer::SetTargetEnv(const spv_target_env env) {
511 impl_->target_env = env;
512 }
513
Run(const uint32_t * original_binary,const size_t original_binary_size,std::vector<uint32_t> * optimized_binary) const514 bool Optimizer::Run(const uint32_t* original_binary,
515 const size_t original_binary_size,
516 std::vector<uint32_t>* optimized_binary) const {
517 return Run(original_binary, original_binary_size, optimized_binary,
518 OptimizerOptions());
519 }
520
Run(const uint32_t * original_binary,const size_t original_binary_size,std::vector<uint32_t> * optimized_binary,const ValidatorOptions & validator_options,bool skip_validation) const521 bool Optimizer::Run(const uint32_t* original_binary,
522 const size_t original_binary_size,
523 std::vector<uint32_t>* optimized_binary,
524 const ValidatorOptions& validator_options,
525 bool skip_validation) const {
526 OptimizerOptions opt_options;
527 opt_options.set_run_validator(!skip_validation);
528 opt_options.set_validator_options(validator_options);
529 return Run(original_binary, original_binary_size, optimized_binary,
530 opt_options);
531 }
532
Run(const uint32_t * original_binary,const size_t original_binary_size,std::vector<uint32_t> * optimized_binary,const spv_optimizer_options opt_options) const533 bool Optimizer::Run(const uint32_t* original_binary,
534 const size_t original_binary_size,
535 std::vector<uint32_t>* optimized_binary,
536 const spv_optimizer_options opt_options) const {
537 spvtools::SpirvTools tools(impl_->target_env);
538 tools.SetMessageConsumer(impl_->pass_manager.consumer());
539 if (opt_options->run_validator_ &&
540 !tools.Validate(original_binary, original_binary_size,
541 &opt_options->val_options_)) {
542 return false;
543 }
544
545 std::unique_ptr<opt::IRContext> context = BuildModule(
546 impl_->target_env, consumer(), original_binary, original_binary_size);
547 if (context == nullptr) return false;
548
549 context->set_max_id_bound(opt_options->max_id_bound_);
550 context->set_preserve_bindings(opt_options->preserve_bindings_);
551 context->set_preserve_spec_constants(opt_options->preserve_spec_constants_);
552
553 impl_->pass_manager.SetValidatorOptions(&opt_options->val_options_);
554 impl_->pass_manager.SetTargetEnv(impl_->target_env);
555 auto status = impl_->pass_manager.Run(context.get());
556
557 if (status == opt::Pass::Status::Failure) {
558 return false;
559 }
560
561 #ifndef NDEBUG
562 // We do not keep the result id of DebugScope in struct DebugScope.
563 // Instead, we assign random ids for them, which results in integrity
564 // check failures. In addition, propagating the OpLine/OpNoLine to preserve
565 // the debug information through transformations results in integrity
566 // check failures. We want to skip the integrity check when the module
567 // contains DebugScope or OpLine/OpNoLine instructions.
568 if (status == opt::Pass::Status::SuccessWithoutChange &&
569 !context->module()->ContainsDebugInfo()) {
570 std::vector<uint32_t> optimized_binary_with_nop;
571 context->module()->ToBinary(&optimized_binary_with_nop,
572 /* skip_nop = */ false);
573 assert(optimized_binary_with_nop.size() == original_binary_size &&
574 "Binary size unexpectedly changed despite the optimizer saying "
575 "there was no change");
576 assert(memcmp(optimized_binary_with_nop.data(), original_binary,
577 original_binary_size) == 0 &&
578 "Binary content unexpectedly changed despite the optimizer saying "
579 "there was no change");
580 }
581 #endif // !NDEBUG
582
583 // Note that |original_binary| and |optimized_binary| may share the same
584 // buffer and the below will invalidate |original_binary|.
585 optimized_binary->clear();
586 context->module()->ToBinary(optimized_binary, /* skip_nop = */ true);
587
588 return true;
589 }
590
SetPrintAll(std::ostream * out)591 Optimizer& Optimizer::SetPrintAll(std::ostream* out) {
592 impl_->pass_manager.SetPrintAll(out);
593 return *this;
594 }
595
SetTimeReport(std::ostream * out)596 Optimizer& Optimizer::SetTimeReport(std::ostream* out) {
597 impl_->pass_manager.SetTimeReport(out);
598 return *this;
599 }
600
SetValidateAfterAll(bool validate)601 Optimizer& Optimizer::SetValidateAfterAll(bool validate) {
602 impl_->pass_manager.SetValidateAfterAll(validate);
603 return *this;
604 }
605
CreateNullPass()606 Optimizer::PassToken CreateNullPass() {
607 return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::NullPass>());
608 }
609
CreateStripDebugInfoPass()610 Optimizer::PassToken CreateStripDebugInfoPass() {
611 return MakeUnique<Optimizer::PassToken::Impl>(
612 MakeUnique<opt::StripDebugInfoPass>());
613 }
614
CreateStripReflectInfoPass()615 Optimizer::PassToken CreateStripReflectInfoPass() {
616 return MakeUnique<Optimizer::PassToken::Impl>(
617 MakeUnique<opt::StripReflectInfoPass>());
618 }
619
CreateEliminateDeadFunctionsPass()620 Optimizer::PassToken CreateEliminateDeadFunctionsPass() {
621 return MakeUnique<Optimizer::PassToken::Impl>(
622 MakeUnique<opt::EliminateDeadFunctionsPass>());
623 }
624
CreateEliminateDeadMembersPass()625 Optimizer::PassToken CreateEliminateDeadMembersPass() {
626 return MakeUnique<Optimizer::PassToken::Impl>(
627 MakeUnique<opt::EliminateDeadMembersPass>());
628 }
629
CreateSetSpecConstantDefaultValuePass(const std::unordered_map<uint32_t,std::string> & id_value_map)630 Optimizer::PassToken CreateSetSpecConstantDefaultValuePass(
631 const std::unordered_map<uint32_t, std::string>& id_value_map) {
632 return MakeUnique<Optimizer::PassToken::Impl>(
633 MakeUnique<opt::SetSpecConstantDefaultValuePass>(id_value_map));
634 }
635
CreateSetSpecConstantDefaultValuePass(const std::unordered_map<uint32_t,std::vector<uint32_t>> & id_value_map)636 Optimizer::PassToken CreateSetSpecConstantDefaultValuePass(
637 const std::unordered_map<uint32_t, std::vector<uint32_t>>& id_value_map) {
638 return MakeUnique<Optimizer::PassToken::Impl>(
639 MakeUnique<opt::SetSpecConstantDefaultValuePass>(id_value_map));
640 }
641
CreateFlattenDecorationPass()642 Optimizer::PassToken CreateFlattenDecorationPass() {
643 return MakeUnique<Optimizer::PassToken::Impl>(
644 MakeUnique<opt::FlattenDecorationPass>());
645 }
646
CreateFreezeSpecConstantValuePass()647 Optimizer::PassToken CreateFreezeSpecConstantValuePass() {
648 return MakeUnique<Optimizer::PassToken::Impl>(
649 MakeUnique<opt::FreezeSpecConstantValuePass>());
650 }
651
CreateFoldSpecConstantOpAndCompositePass()652 Optimizer::PassToken CreateFoldSpecConstantOpAndCompositePass() {
653 return MakeUnique<Optimizer::PassToken::Impl>(
654 MakeUnique<opt::FoldSpecConstantOpAndCompositePass>());
655 }
656
CreateUnifyConstantPass()657 Optimizer::PassToken CreateUnifyConstantPass() {
658 return MakeUnique<Optimizer::PassToken::Impl>(
659 MakeUnique<opt::UnifyConstantPass>());
660 }
661
CreateEliminateDeadConstantPass()662 Optimizer::PassToken CreateEliminateDeadConstantPass() {
663 return MakeUnique<Optimizer::PassToken::Impl>(
664 MakeUnique<opt::EliminateDeadConstantPass>());
665 }
666
CreateDeadVariableEliminationPass()667 Optimizer::PassToken CreateDeadVariableEliminationPass() {
668 return MakeUnique<Optimizer::PassToken::Impl>(
669 MakeUnique<opt::DeadVariableElimination>());
670 }
671
CreateStrengthReductionPass()672 Optimizer::PassToken CreateStrengthReductionPass() {
673 return MakeUnique<Optimizer::PassToken::Impl>(
674 MakeUnique<opt::StrengthReductionPass>());
675 }
676
CreateBlockMergePass()677 Optimizer::PassToken CreateBlockMergePass() {
678 return MakeUnique<Optimizer::PassToken::Impl>(
679 MakeUnique<opt::BlockMergePass>());
680 }
681
CreateInlineExhaustivePass()682 Optimizer::PassToken CreateInlineExhaustivePass() {
683 return MakeUnique<Optimizer::PassToken::Impl>(
684 MakeUnique<opt::InlineExhaustivePass>());
685 }
686
CreateInlineOpaquePass()687 Optimizer::PassToken CreateInlineOpaquePass() {
688 return MakeUnique<Optimizer::PassToken::Impl>(
689 MakeUnique<opt::InlineOpaquePass>());
690 }
691
CreateLocalAccessChainConvertPass()692 Optimizer::PassToken CreateLocalAccessChainConvertPass() {
693 return MakeUnique<Optimizer::PassToken::Impl>(
694 MakeUnique<opt::LocalAccessChainConvertPass>());
695 }
696
CreateLocalSingleBlockLoadStoreElimPass()697 Optimizer::PassToken CreateLocalSingleBlockLoadStoreElimPass() {
698 return MakeUnique<Optimizer::PassToken::Impl>(
699 MakeUnique<opt::LocalSingleBlockLoadStoreElimPass>());
700 }
701
CreateLocalSingleStoreElimPass()702 Optimizer::PassToken CreateLocalSingleStoreElimPass() {
703 return MakeUnique<Optimizer::PassToken::Impl>(
704 MakeUnique<opt::LocalSingleStoreElimPass>());
705 }
706
CreateInsertExtractElimPass()707 Optimizer::PassToken CreateInsertExtractElimPass() {
708 return MakeUnique<Optimizer::PassToken::Impl>(
709 MakeUnique<opt::SimplificationPass>());
710 }
711
CreateDeadInsertElimPass()712 Optimizer::PassToken CreateDeadInsertElimPass() {
713 return MakeUnique<Optimizer::PassToken::Impl>(
714 MakeUnique<opt::DeadInsertElimPass>());
715 }
716
CreateDeadBranchElimPass()717 Optimizer::PassToken CreateDeadBranchElimPass() {
718 return MakeUnique<Optimizer::PassToken::Impl>(
719 MakeUnique<opt::DeadBranchElimPass>());
720 }
721
CreateLocalMultiStoreElimPass()722 Optimizer::PassToken CreateLocalMultiStoreElimPass() {
723 return MakeUnique<Optimizer::PassToken::Impl>(
724 MakeUnique<opt::SSARewritePass>());
725 }
726
CreateAggressiveDCEPass()727 Optimizer::PassToken CreateAggressiveDCEPass() {
728 return MakeUnique<Optimizer::PassToken::Impl>(
729 MakeUnique<opt::AggressiveDCEPass>());
730 }
731
CreatePropagateLineInfoPass()732 Optimizer::PassToken CreatePropagateLineInfoPass() {
733 return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::EmptyPass>());
734 }
735
CreateRedundantLineInfoElimPass()736 Optimizer::PassToken CreateRedundantLineInfoElimPass() {
737 return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::EmptyPass>());
738 }
739
CreateCompactIdsPass()740 Optimizer::PassToken CreateCompactIdsPass() {
741 return MakeUnique<Optimizer::PassToken::Impl>(
742 MakeUnique<opt::CompactIdsPass>());
743 }
744
CreateMergeReturnPass()745 Optimizer::PassToken CreateMergeReturnPass() {
746 return MakeUnique<Optimizer::PassToken::Impl>(
747 MakeUnique<opt::MergeReturnPass>());
748 }
749
GetPassNames() const750 std::vector<const char*> Optimizer::GetPassNames() const {
751 std::vector<const char*> v;
752 for (uint32_t i = 0; i < impl_->pass_manager.NumPasses(); i++) {
753 v.push_back(impl_->pass_manager.GetPass(i)->name());
754 }
755 return v;
756 }
757
CreateCFGCleanupPass()758 Optimizer::PassToken CreateCFGCleanupPass() {
759 return MakeUnique<Optimizer::PassToken::Impl>(
760 MakeUnique<opt::CFGCleanupPass>());
761 }
762
CreateLocalRedundancyEliminationPass()763 Optimizer::PassToken CreateLocalRedundancyEliminationPass() {
764 return MakeUnique<Optimizer::PassToken::Impl>(
765 MakeUnique<opt::LocalRedundancyEliminationPass>());
766 }
767
CreateLoopFissionPass(size_t threshold)768 Optimizer::PassToken CreateLoopFissionPass(size_t threshold) {
769 return MakeUnique<Optimizer::PassToken::Impl>(
770 MakeUnique<opt::LoopFissionPass>(threshold));
771 }
772
CreateLoopFusionPass(size_t max_registers_per_loop)773 Optimizer::PassToken CreateLoopFusionPass(size_t max_registers_per_loop) {
774 return MakeUnique<Optimizer::PassToken::Impl>(
775 MakeUnique<opt::LoopFusionPass>(max_registers_per_loop));
776 }
777
CreateLoopInvariantCodeMotionPass()778 Optimizer::PassToken CreateLoopInvariantCodeMotionPass() {
779 return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::LICMPass>());
780 }
781
CreateLoopPeelingPass()782 Optimizer::PassToken CreateLoopPeelingPass() {
783 return MakeUnique<Optimizer::PassToken::Impl>(
784 MakeUnique<opt::LoopPeelingPass>());
785 }
786
CreateLoopUnswitchPass()787 Optimizer::PassToken CreateLoopUnswitchPass() {
788 return MakeUnique<Optimizer::PassToken::Impl>(
789 MakeUnique<opt::LoopUnswitchPass>());
790 }
791
CreateRedundancyEliminationPass()792 Optimizer::PassToken CreateRedundancyEliminationPass() {
793 return MakeUnique<Optimizer::PassToken::Impl>(
794 MakeUnique<opt::RedundancyEliminationPass>());
795 }
796
CreateRemoveDuplicatesPass()797 Optimizer::PassToken CreateRemoveDuplicatesPass() {
798 return MakeUnique<Optimizer::PassToken::Impl>(
799 MakeUnique<opt::RemoveDuplicatesPass>());
800 }
801
CreateScalarReplacementPass(uint32_t size_limit)802 Optimizer::PassToken CreateScalarReplacementPass(uint32_t size_limit) {
803 return MakeUnique<Optimizer::PassToken::Impl>(
804 MakeUnique<opt::ScalarReplacementPass>(size_limit));
805 }
806
CreatePrivateToLocalPass()807 Optimizer::PassToken CreatePrivateToLocalPass() {
808 return MakeUnique<Optimizer::PassToken::Impl>(
809 MakeUnique<opt::PrivateToLocalPass>());
810 }
811
CreateCCPPass()812 Optimizer::PassToken CreateCCPPass() {
813 return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::CCPPass>());
814 }
815
CreateWorkaround1209Pass()816 Optimizer::PassToken CreateWorkaround1209Pass() {
817 return MakeUnique<Optimizer::PassToken::Impl>(
818 MakeUnique<opt::Workaround1209>());
819 }
820
CreateIfConversionPass()821 Optimizer::PassToken CreateIfConversionPass() {
822 return MakeUnique<Optimizer::PassToken::Impl>(
823 MakeUnique<opt::IfConversion>());
824 }
825
CreateReplaceInvalidOpcodePass()826 Optimizer::PassToken CreateReplaceInvalidOpcodePass() {
827 return MakeUnique<Optimizer::PassToken::Impl>(
828 MakeUnique<opt::ReplaceInvalidOpcodePass>());
829 }
830
CreateSimplificationPass()831 Optimizer::PassToken CreateSimplificationPass() {
832 return MakeUnique<Optimizer::PassToken::Impl>(
833 MakeUnique<opt::SimplificationPass>());
834 }
835
CreateLoopUnrollPass(bool fully_unroll,int factor)836 Optimizer::PassToken CreateLoopUnrollPass(bool fully_unroll, int factor) {
837 return MakeUnique<Optimizer::PassToken::Impl>(
838 MakeUnique<opt::LoopUnroller>(fully_unroll, factor));
839 }
840
CreateSSARewritePass()841 Optimizer::PassToken CreateSSARewritePass() {
842 return MakeUnique<Optimizer::PassToken::Impl>(
843 MakeUnique<opt::SSARewritePass>());
844 }
845
CreateCopyPropagateArraysPass()846 Optimizer::PassToken CreateCopyPropagateArraysPass() {
847 return MakeUnique<Optimizer::PassToken::Impl>(
848 MakeUnique<opt::CopyPropagateArrays>());
849 }
850
CreateVectorDCEPass()851 Optimizer::PassToken CreateVectorDCEPass() {
852 return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::VectorDCE>());
853 }
854
CreateReduceLoadSizePass()855 Optimizer::PassToken CreateReduceLoadSizePass() {
856 return MakeUnique<Optimizer::PassToken::Impl>(
857 MakeUnique<opt::ReduceLoadSize>());
858 }
859
CreateCombineAccessChainsPass()860 Optimizer::PassToken CreateCombineAccessChainsPass() {
861 return MakeUnique<Optimizer::PassToken::Impl>(
862 MakeUnique<opt::CombineAccessChains>());
863 }
864
CreateUpgradeMemoryModelPass()865 Optimizer::PassToken CreateUpgradeMemoryModelPass() {
866 return MakeUnique<Optimizer::PassToken::Impl>(
867 MakeUnique<opt::UpgradeMemoryModel>());
868 }
869
CreateInstBindlessCheckPass(uint32_t desc_set,uint32_t shader_id,bool desc_length_enable,bool desc_init_enable,bool buff_oob_enable,bool texbuff_oob_enable)870 Optimizer::PassToken CreateInstBindlessCheckPass(
871 uint32_t desc_set, uint32_t shader_id, bool desc_length_enable,
872 bool desc_init_enable, bool buff_oob_enable, bool texbuff_oob_enable) {
873 return MakeUnique<Optimizer::PassToken::Impl>(
874 MakeUnique<opt::InstBindlessCheckPass>(
875 desc_set, shader_id, desc_length_enable, desc_init_enable,
876 buff_oob_enable, texbuff_oob_enable,
877 desc_length_enable || desc_init_enable || buff_oob_enable));
878 }
879
CreateInstDebugPrintfPass(uint32_t desc_set,uint32_t shader_id)880 Optimizer::PassToken CreateInstDebugPrintfPass(uint32_t desc_set,
881 uint32_t shader_id) {
882 return MakeUnique<Optimizer::PassToken::Impl>(
883 MakeUnique<opt::InstDebugPrintfPass>(desc_set, shader_id));
884 }
885
CreateInstBuffAddrCheckPass(uint32_t desc_set,uint32_t shader_id)886 Optimizer::PassToken CreateInstBuffAddrCheckPass(uint32_t desc_set,
887 uint32_t shader_id) {
888 return MakeUnique<Optimizer::PassToken::Impl>(
889 MakeUnique<opt::InstBuffAddrCheckPass>(desc_set, shader_id));
890 }
891
CreateConvertRelaxedToHalfPass()892 Optimizer::PassToken CreateConvertRelaxedToHalfPass() {
893 return MakeUnique<Optimizer::PassToken::Impl>(
894 MakeUnique<opt::ConvertToHalfPass>());
895 }
896
CreateRelaxFloatOpsPass()897 Optimizer::PassToken CreateRelaxFloatOpsPass() {
898 return MakeUnique<Optimizer::PassToken::Impl>(
899 MakeUnique<opt::RelaxFloatOpsPass>());
900 }
901
CreateCodeSinkingPass()902 Optimizer::PassToken CreateCodeSinkingPass() {
903 return MakeUnique<Optimizer::PassToken::Impl>(
904 MakeUnique<opt::CodeSinkingPass>());
905 }
906
CreateFixStorageClassPass()907 Optimizer::PassToken CreateFixStorageClassPass() {
908 return MakeUnique<Optimizer::PassToken::Impl>(
909 MakeUnique<opt::FixStorageClass>());
910 }
911
CreateGraphicsRobustAccessPass()912 Optimizer::PassToken CreateGraphicsRobustAccessPass() {
913 return MakeUnique<Optimizer::PassToken::Impl>(
914 MakeUnique<opt::GraphicsRobustAccessPass>());
915 }
916
CreateDescriptorScalarReplacementPass()917 Optimizer::PassToken CreateDescriptorScalarReplacementPass() {
918 return MakeUnique<Optimizer::PassToken::Impl>(
919 MakeUnique<opt::DescriptorScalarReplacement>());
920 }
921
CreateWrapOpKillPass()922 Optimizer::PassToken CreateWrapOpKillPass() {
923 return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::WrapOpKill>());
924 }
925
CreateAmdExtToKhrPass()926 Optimizer::PassToken CreateAmdExtToKhrPass() {
927 return MakeUnique<Optimizer::PassToken::Impl>(
928 MakeUnique<opt::AmdExtensionToKhrPass>());
929 }
930
CreateInterpolateFixupPass()931 Optimizer::PassToken CreateInterpolateFixupPass() {
932 return MakeUnique<Optimizer::PassToken::Impl>(
933 MakeUnique<opt::InterpFixupPass>());
934 }
935
936 } // namespace spvtools
937