• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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