• 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 <charconv>
19 #include <memory>
20 #include <string>
21 #include <unordered_map>
22 #include <utility>
23 #include <vector>
24 
25 #include "source/opt/build_module.h"
26 #include "source/opt/graphics_robust_access_pass.h"
27 #include "source/opt/log.h"
28 #include "source/opt/pass_manager.h"
29 #include "source/opt/passes.h"
30 #include "source/spirv_optimizer_options.h"
31 #include "source/util/make_unique.h"
32 #include "source/util/string_utils.h"
33 
34 namespace spvtools {
35 
GetVectorOfStrings(const char ** strings,const size_t string_count)36 std::vector<std::string> GetVectorOfStrings(const char** strings,
37                                             const size_t string_count) {
38   std::vector<std::string> result;
39   for (uint32_t i = 0; i < string_count; i++) {
40     result.emplace_back(strings[i]);
41   }
42   return result;
43 }
44 
45 struct Optimizer::PassToken::Impl {
Implspvtools::Optimizer::PassToken::Impl46   Impl(std::unique_ptr<opt::Pass> p) : pass(std::move(p)) {}
47 
48   std::unique_ptr<opt::Pass> pass;  // Internal implementation pass.
49 };
50 
PassToken(std::unique_ptr<Optimizer::PassToken::Impl> impl)51 Optimizer::PassToken::PassToken(
52     std::unique_ptr<Optimizer::PassToken::Impl> impl)
53     : impl_(std::move(impl)) {}
54 
PassToken(std::unique_ptr<opt::Pass> && pass)55 Optimizer::PassToken::PassToken(std::unique_ptr<opt::Pass>&& pass)
56     : impl_(MakeUnique<Optimizer::PassToken::Impl>(std::move(pass))) {}
57 
PassToken(PassToken && that)58 Optimizer::PassToken::PassToken(PassToken&& that)
59     : impl_(std::move(that.impl_)) {}
60 
operator =(PassToken && that)61 Optimizer::PassToken& Optimizer::PassToken::operator=(PassToken&& that) {
62   impl_ = std::move(that.impl_);
63   return *this;
64 }
65 
~PassToken()66 Optimizer::PassToken::~PassToken() {}
67 
68 struct Optimizer::Impl {
Implspvtools::Optimizer::Impl69   explicit Impl(spv_target_env env) : target_env(env), pass_manager() {}
70 
71   spv_target_env target_env;      // Target environment.
72   opt::PassManager pass_manager;  // Internal implementation pass manager.
73   std::unordered_set<uint32_t> live_locs;  // Arg to debug dead output passes
74 };
75 
Optimizer(spv_target_env env)76 Optimizer::Optimizer(spv_target_env env) : impl_(new Impl(env)) {
77   assert(env != SPV_ENV_WEBGPU_0);
78 }
79 
~Optimizer()80 Optimizer::~Optimizer() {}
81 
SetMessageConsumer(MessageConsumer c)82 void Optimizer::SetMessageConsumer(MessageConsumer c) {
83   // All passes' message consumer needs to be updated.
84   for (uint32_t i = 0; i < impl_->pass_manager.NumPasses(); ++i) {
85     impl_->pass_manager.GetPass(i)->SetMessageConsumer(c);
86   }
87   impl_->pass_manager.SetMessageConsumer(std::move(c));
88 }
89 
consumer() const90 const MessageConsumer& Optimizer::consumer() const {
91   return impl_->pass_manager.consumer();
92 }
93 
RegisterPass(PassToken && p)94 Optimizer& Optimizer::RegisterPass(PassToken&& p) {
95   // Change to use the pass manager's consumer.
96   p.impl_->pass->SetMessageConsumer(consumer());
97   impl_->pass_manager.AddPass(std::move(p.impl_->pass));
98   return *this;
99 }
100 
101 // The legalization passes take a spir-v shader generated by an HLSL front-end
102 // and turn it into a valid vulkan spir-v shader.  There are two ways in which
103 // the code will be invalid at the start:
104 //
105 // 1) There will be opaque objects, like images, which will be passed around
106 //    in intermediate objects.  Valid spir-v will have to replace the use of
107 //    the opaque object with an intermediate object that is the result of the
108 //    load of the global opaque object.
109 //
110 // 2) There will be variables that contain pointers to structured or uniform
111 //    buffers.  It be legal, the variables must be eliminated, and the
112 //    references to the structured buffers must use the result of OpVariable
113 //    in the Uniform storage class.
114 //
115 // Optimization in this list must accept shaders with these relaxation of the
116 // rules.  There is not guarantee that this list of optimizations is able to
117 // legalize all inputs, but it is on a best effort basis.
118 //
119 // The legalization problem is essentially a very general copy propagation
120 // problem.  The optimization we use are all used to either do copy propagation
121 // or enable more copy propagation.
RegisterLegalizationPasses(bool preserve_interface)122 Optimizer& Optimizer::RegisterLegalizationPasses(bool preserve_interface) {
123   return
124       // Wrap OpKill instructions so all other code can be inlined.
125       RegisterPass(CreateWrapOpKillPass())
126           // Remove unreachable block so that merge return works.
127           .RegisterPass(CreateDeadBranchElimPass())
128           // Merge the returns so we can inline.
129           .RegisterPass(CreateMergeReturnPass())
130           // Make sure uses and definitions are in the same function.
131           .RegisterPass(CreateInlineExhaustivePass())
132           // Make private variable function scope
133           .RegisterPass(CreateEliminateDeadFunctionsPass())
134           .RegisterPass(CreatePrivateToLocalPass())
135           // Fix up the storage classes that DXC may have purposely generated
136           // incorrectly.  All functions are inlined, and a lot of dead code has
137           // been removed.
138           .RegisterPass(CreateFixStorageClassPass())
139           // Propagate the value stored to the loads in very simple cases.
140           .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
141           .RegisterPass(CreateLocalSingleStoreElimPass())
142           .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
143           // Split up aggregates so they are easier to deal with.
144           .RegisterPass(CreateScalarReplacementPass(0))
145           // Remove loads and stores so everything is in intermediate values.
146           // Takes care of copy propagation of non-members.
147           .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
148           .RegisterPass(CreateLocalSingleStoreElimPass())
149           .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
150           .RegisterPass(CreateLocalMultiStoreElimPass())
151           .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
152           // Propagate constants to get as many constant conditions on branches
153           // as possible.
154           .RegisterPass(CreateCCPPass())
155           .RegisterPass(CreateLoopUnrollPass(true))
156           .RegisterPass(CreateDeadBranchElimPass())
157           // Copy propagate members.  Cleans up code sequences generated by
158           // scalar replacement.  Also important for removing OpPhi nodes.
159           .RegisterPass(CreateSimplificationPass())
160           .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
161           .RegisterPass(CreateCopyPropagateArraysPass())
162           // May need loop unrolling here see
163           // https://github.com/Microsoft/DirectXShaderCompiler/pull/930
164           // Get rid of unused code that contain traces of illegal code
165           // or unused references to unbound external objects
166           .RegisterPass(CreateVectorDCEPass())
167           .RegisterPass(CreateDeadInsertElimPass())
168           .RegisterPass(CreateReduceLoadSizePass())
169           .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
170           .RegisterPass(CreateInterpolateFixupPass())
171           .RegisterPass(CreateInvocationInterlockPlacementPass());
172 }
173 
RegisterLegalizationPasses()174 Optimizer& Optimizer::RegisterLegalizationPasses() {
175   return RegisterLegalizationPasses(false);
176 }
177 
RegisterPerformancePasses(bool preserve_interface)178 Optimizer& Optimizer::RegisterPerformancePasses(bool preserve_interface) {
179   return RegisterPass(CreateWrapOpKillPass())
180       .RegisterPass(CreateDeadBranchElimPass())
181       .RegisterPass(CreateMergeReturnPass())
182       .RegisterPass(CreateInlineExhaustivePass())
183       .RegisterPass(CreateEliminateDeadFunctionsPass())
184       .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
185       .RegisterPass(CreatePrivateToLocalPass())
186       .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
187       .RegisterPass(CreateLocalSingleStoreElimPass())
188       .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
189       .RegisterPass(CreateScalarReplacementPass())
190       .RegisterPass(CreateLocalAccessChainConvertPass())
191       .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
192       .RegisterPass(CreateLocalSingleStoreElimPass())
193       .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
194       .RegisterPass(CreateLocalMultiStoreElimPass())
195       .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
196       .RegisterPass(CreateCCPPass())
197       .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
198       .RegisterPass(CreateLoopUnrollPass(true))
199       .RegisterPass(CreateDeadBranchElimPass())
200       .RegisterPass(CreateRedundancyEliminationPass())
201       .RegisterPass(CreateCombineAccessChainsPass())
202       .RegisterPass(CreateSimplificationPass())
203       .RegisterPass(CreateScalarReplacementPass())
204       .RegisterPass(CreateLocalAccessChainConvertPass())
205       .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
206       .RegisterPass(CreateLocalSingleStoreElimPass())
207       .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
208       .RegisterPass(CreateSSARewritePass())
209       .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
210       .RegisterPass(CreateVectorDCEPass())
211       .RegisterPass(CreateDeadInsertElimPass())
212       .RegisterPass(CreateDeadBranchElimPass())
213       .RegisterPass(CreateSimplificationPass())
214       .RegisterPass(CreateIfConversionPass())
215       .RegisterPass(CreateCopyPropagateArraysPass())
216       .RegisterPass(CreateReduceLoadSizePass())
217       .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
218       .RegisterPass(CreateBlockMergePass())
219       .RegisterPass(CreateRedundancyEliminationPass())
220       .RegisterPass(CreateDeadBranchElimPass())
221       .RegisterPass(CreateBlockMergePass())
222       .RegisterPass(CreateSimplificationPass());
223 }
224 
RegisterPerformancePasses()225 Optimizer& Optimizer::RegisterPerformancePasses() {
226   return RegisterPerformancePasses(false);
227 }
228 
RegisterSizePasses(bool preserve_interface)229 Optimizer& Optimizer::RegisterSizePasses(bool preserve_interface) {
230   return RegisterPass(CreateWrapOpKillPass())
231       .RegisterPass(CreateDeadBranchElimPass())
232       .RegisterPass(CreateMergeReturnPass())
233       .RegisterPass(CreateInlineExhaustivePass())
234       .RegisterPass(CreateEliminateDeadFunctionsPass())
235       .RegisterPass(CreatePrivateToLocalPass())
236       .RegisterPass(CreateScalarReplacementPass(0))
237       .RegisterPass(CreateLocalMultiStoreElimPass())
238       .RegisterPass(CreateCCPPass())
239       .RegisterPass(CreateLoopUnrollPass(true))
240       .RegisterPass(CreateDeadBranchElimPass())
241       .RegisterPass(CreateSimplificationPass())
242       .RegisterPass(CreateScalarReplacementPass(0))
243       .RegisterPass(CreateLocalSingleStoreElimPass())
244       .RegisterPass(CreateIfConversionPass())
245       .RegisterPass(CreateSimplificationPass())
246       .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
247       .RegisterPass(CreateDeadBranchElimPass())
248       .RegisterPass(CreateBlockMergePass())
249       .RegisterPass(CreateLocalAccessChainConvertPass())
250       .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
251       .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
252       .RegisterPass(CreateCopyPropagateArraysPass())
253       .RegisterPass(CreateVectorDCEPass())
254       .RegisterPass(CreateDeadInsertElimPass())
255       .RegisterPass(CreateEliminateDeadMembersPass())
256       .RegisterPass(CreateLocalSingleStoreElimPass())
257       .RegisterPass(CreateBlockMergePass())
258       .RegisterPass(CreateLocalMultiStoreElimPass())
259       .RegisterPass(CreateRedundancyEliminationPass())
260       .RegisterPass(CreateSimplificationPass())
261       .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
262       .RegisterPass(CreateCFGCleanupPass());
263 }
264 
RegisterSizePasses()265 Optimizer& Optimizer::RegisterSizePasses() { return RegisterSizePasses(false); }
266 
RegisterPassesFromFlags(const std::vector<std::string> & flags)267 bool Optimizer::RegisterPassesFromFlags(const std::vector<std::string>& flags) {
268   return RegisterPassesFromFlags(flags, false);
269 }
270 
RegisterPassesFromFlags(const std::vector<std::string> & flags,bool preserve_interface)271 bool Optimizer::RegisterPassesFromFlags(const std::vector<std::string>& flags,
272                                         bool preserve_interface) {
273   for (const auto& flag : flags) {
274     if (!RegisterPassFromFlag(flag, preserve_interface)) {
275       return false;
276     }
277   }
278 
279   return true;
280 }
281 
FlagHasValidForm(const std::string & flag) const282 bool Optimizer::FlagHasValidForm(const std::string& flag) const {
283   if (flag == "-O" || flag == "-Os") {
284     return true;
285   } else if (flag.size() > 2 && flag.substr(0, 2) == "--") {
286     return true;
287   }
288 
289   Errorf(consumer(), nullptr, {},
290          "%s is not a valid flag.  Flag passes should have the form "
291          "'--pass_name[=pass_args]'. Special flag names also accepted: -O "
292          "and -Os.",
293          flag.c_str());
294   return false;
295 }
296 
RegisterPassFromFlag(const std::string & flag)297 bool Optimizer::RegisterPassFromFlag(const std::string& flag) {
298   return RegisterPassFromFlag(flag, false);
299 }
300 
RegisterPassFromFlag(const std::string & flag,bool preserve_interface)301 bool Optimizer::RegisterPassFromFlag(const std::string& flag,
302                                      bool preserve_interface) {
303   if (!FlagHasValidForm(flag)) {
304     return false;
305   }
306 
307   // Split flags of the form --pass_name=pass_args.
308   auto p = utils::SplitFlagArgs(flag);
309   std::string pass_name = p.first;
310   std::string pass_args = p.second;
311 
312   // FIXME(dnovillo): This should be re-factored so that pass names can be
313   // automatically checked against Pass::name() and PassToken instances created
314   // via a template function.  Additionally, class Pass should have a desc()
315   // method that describes the pass (so it can be used in --help).
316   //
317   // Both Pass::name() and Pass::desc() should be static class members so they
318   // can be invoked without creating a pass instance.
319   if (pass_name == "strip-debug") {
320     RegisterPass(CreateStripDebugInfoPass());
321   } else if (pass_name == "strip-reflect") {
322     RegisterPass(CreateStripReflectInfoPass());
323   } else if (pass_name == "strip-nonsemantic") {
324     RegisterPass(CreateStripNonSemanticInfoPass());
325   } else if (pass_name == "set-spec-const-default-value") {
326     if (pass_args.size() > 0) {
327       auto spec_ids_vals =
328           opt::SetSpecConstantDefaultValuePass::ParseDefaultValuesString(
329               pass_args.c_str());
330       if (!spec_ids_vals) {
331         Errorf(consumer(), nullptr, {},
332                "Invalid argument for --set-spec-const-default-value: %s",
333                pass_args.c_str());
334         return false;
335       }
336       RegisterPass(
337           CreateSetSpecConstantDefaultValuePass(std::move(*spec_ids_vals)));
338     } else {
339       Errorf(consumer(), nullptr, {},
340              "Invalid spec constant value string '%s'. Expected a string of "
341              "<spec id>:<default value> pairs.",
342              pass_args.c_str());
343       return false;
344     }
345   } else if (pass_name == "if-conversion") {
346     RegisterPass(CreateIfConversionPass());
347   } else if (pass_name == "freeze-spec-const") {
348     RegisterPass(CreateFreezeSpecConstantValuePass());
349   } else if (pass_name == "inline-entry-points-exhaustive") {
350     RegisterPass(CreateInlineExhaustivePass());
351   } else if (pass_name == "inline-entry-points-opaque") {
352     RegisterPass(CreateInlineOpaquePass());
353   } else if (pass_name == "combine-access-chains") {
354     RegisterPass(CreateCombineAccessChainsPass());
355   } else if (pass_name == "convert-local-access-chains") {
356     RegisterPass(CreateLocalAccessChainConvertPass());
357   } else if (pass_name == "replace-desc-array-access-using-var-index") {
358     RegisterPass(CreateReplaceDescArrayAccessUsingVarIndexPass());
359   } else if (pass_name == "spread-volatile-semantics") {
360     RegisterPass(CreateSpreadVolatileSemanticsPass());
361   } else if (pass_name == "descriptor-scalar-replacement") {
362     RegisterPass(CreateDescriptorScalarReplacementPass());
363   } else if (pass_name == "eliminate-dead-code-aggressive") {
364     RegisterPass(CreateAggressiveDCEPass(preserve_interface));
365   } else if (pass_name == "eliminate-insert-extract") {
366     RegisterPass(CreateInsertExtractElimPass());
367   } else if (pass_name == "eliminate-local-single-block") {
368     RegisterPass(CreateLocalSingleBlockLoadStoreElimPass());
369   } else if (pass_name == "eliminate-local-single-store") {
370     RegisterPass(CreateLocalSingleStoreElimPass());
371   } else if (pass_name == "merge-blocks") {
372     RegisterPass(CreateBlockMergePass());
373   } else if (pass_name == "merge-return") {
374     RegisterPass(CreateMergeReturnPass());
375   } else if (pass_name == "eliminate-dead-branches") {
376     RegisterPass(CreateDeadBranchElimPass());
377   } else if (pass_name == "eliminate-dead-functions") {
378     RegisterPass(CreateEliminateDeadFunctionsPass());
379   } else if (pass_name == "eliminate-local-multi-store") {
380     RegisterPass(CreateLocalMultiStoreElimPass());
381   } else if (pass_name == "eliminate-dead-const") {
382     RegisterPass(CreateEliminateDeadConstantPass());
383   } else if (pass_name == "eliminate-dead-inserts") {
384     RegisterPass(CreateDeadInsertElimPass());
385   } else if (pass_name == "eliminate-dead-variables") {
386     RegisterPass(CreateDeadVariableEliminationPass());
387   } else if (pass_name == "eliminate-dead-members") {
388     RegisterPass(CreateEliminateDeadMembersPass());
389   } else if (pass_name == "fold-spec-const-op-composite") {
390     RegisterPass(CreateFoldSpecConstantOpAndCompositePass());
391   } else if (pass_name == "loop-unswitch") {
392     RegisterPass(CreateLoopUnswitchPass());
393   } else if (pass_name == "scalar-replacement") {
394     if (pass_args.size() == 0) {
395       RegisterPass(CreateScalarReplacementPass());
396     } else {
397       int limit = -1;
398       if (pass_args.find_first_not_of("0123456789") == std::string::npos) {
399         limit = atoi(pass_args.c_str());
400       }
401 
402       if (limit >= 0) {
403         RegisterPass(CreateScalarReplacementPass(limit));
404       } else {
405         Error(consumer(), nullptr, {},
406               "--scalar-replacement must have no arguments or a non-negative "
407               "integer argument");
408         return false;
409       }
410     }
411   } else if (pass_name == "strength-reduction") {
412     RegisterPass(CreateStrengthReductionPass());
413   } else if (pass_name == "unify-const") {
414     RegisterPass(CreateUnifyConstantPass());
415   } else if (pass_name == "flatten-decorations") {
416     RegisterPass(CreateFlattenDecorationPass());
417   } else if (pass_name == "compact-ids") {
418     RegisterPass(CreateCompactIdsPass());
419   } else if (pass_name == "cfg-cleanup") {
420     RegisterPass(CreateCFGCleanupPass());
421   } else if (pass_name == "local-redundancy-elimination") {
422     RegisterPass(CreateLocalRedundancyEliminationPass());
423   } else if (pass_name == "loop-invariant-code-motion") {
424     RegisterPass(CreateLoopInvariantCodeMotionPass());
425   } else if (pass_name == "reduce-load-size") {
426     if (pass_args.size() == 0) {
427       RegisterPass(CreateReduceLoadSizePass());
428     } else {
429       double load_replacement_threshold = 0.9;
430       if (pass_args.find_first_not_of(".0123456789") == std::string::npos) {
431         load_replacement_threshold = atof(pass_args.c_str());
432       }
433 
434       if (load_replacement_threshold >= 0) {
435         RegisterPass(CreateReduceLoadSizePass(load_replacement_threshold));
436       } else {
437         Error(consumer(), nullptr, {},
438               "--reduce-load-size must have no arguments or a non-negative "
439               "double argument");
440         return false;
441       }
442     }
443   } else if (pass_name == "redundancy-elimination") {
444     RegisterPass(CreateRedundancyEliminationPass());
445   } else if (pass_name == "private-to-local") {
446     RegisterPass(CreatePrivateToLocalPass());
447   } else if (pass_name == "remove-duplicates") {
448     RegisterPass(CreateRemoveDuplicatesPass());
449   } else if (pass_name == "workaround-1209") {
450     RegisterPass(CreateWorkaround1209Pass());
451   } else if (pass_name == "replace-invalid-opcode") {
452     RegisterPass(CreateReplaceInvalidOpcodePass());
453   } else if (pass_name == "inst-bindless-check" ||
454              pass_name == "inst-desc-idx-check" ||
455              pass_name == "inst-buff-oob-check") {
456     // preserve legacy names
457     RegisterPass(CreateInstBindlessCheckPass(23));
458     RegisterPass(CreateSimplificationPass());
459     RegisterPass(CreateDeadBranchElimPass());
460     RegisterPass(CreateBlockMergePass());
461   } else if (pass_name == "inst-buff-addr-check") {
462     RegisterPass(CreateInstBuffAddrCheckPass(23));
463   } else if (pass_name == "convert-relaxed-to-half") {
464     RegisterPass(CreateConvertRelaxedToHalfPass());
465   } else if (pass_name == "relax-float-ops") {
466     RegisterPass(CreateRelaxFloatOpsPass());
467   } else if (pass_name == "inst-debug-printf") {
468     // This private option is not for user consumption.
469     // It is here to assist in debugging and fixing the debug printf
470     // instrumentation pass.
471     // For users who wish to utilize debug printf, see the white paper at
472     // https://www.lunarg.com/wp-content/uploads/2021/08/Using-Debug-Printf-02August2021.pdf
473     RegisterPass(CreateInstDebugPrintfPass(7, 23));
474   } else if (pass_name == "simplify-instructions") {
475     RegisterPass(CreateSimplificationPass());
476   } else if (pass_name == "ssa-rewrite") {
477     RegisterPass(CreateSSARewritePass());
478   } else if (pass_name == "copy-propagate-arrays") {
479     RegisterPass(CreateCopyPropagateArraysPass());
480   } else if (pass_name == "loop-fission") {
481     int register_threshold_to_split =
482         (pass_args.size() > 0) ? atoi(pass_args.c_str()) : -1;
483     if (register_threshold_to_split > 0) {
484       RegisterPass(CreateLoopFissionPass(
485           static_cast<size_t>(register_threshold_to_split)));
486     } else {
487       Error(consumer(), nullptr, {},
488             "--loop-fission must have a positive integer argument");
489       return false;
490     }
491   } else if (pass_name == "loop-fusion") {
492     int max_registers_per_loop =
493         (pass_args.size() > 0) ? atoi(pass_args.c_str()) : -1;
494     if (max_registers_per_loop > 0) {
495       RegisterPass(
496           CreateLoopFusionPass(static_cast<size_t>(max_registers_per_loop)));
497     } else {
498       Error(consumer(), nullptr, {},
499             "--loop-fusion must have a positive integer argument");
500       return false;
501     }
502   } else if (pass_name == "loop-unroll") {
503     RegisterPass(CreateLoopUnrollPass(true));
504   } else if (pass_name == "upgrade-memory-model") {
505     RegisterPass(CreateUpgradeMemoryModelPass());
506   } else if (pass_name == "vector-dce") {
507     RegisterPass(CreateVectorDCEPass());
508   } else if (pass_name == "loop-unroll-partial") {
509     int factor = (pass_args.size() > 0) ? atoi(pass_args.c_str()) : 0;
510     if (factor > 0) {
511       RegisterPass(CreateLoopUnrollPass(false, factor));
512     } else {
513       Error(consumer(), nullptr, {},
514             "--loop-unroll-partial must have a positive integer argument");
515       return false;
516     }
517   } else if (pass_name == "loop-peeling") {
518     RegisterPass(CreateLoopPeelingPass());
519   } else if (pass_name == "loop-peeling-threshold") {
520     int factor = (pass_args.size() > 0) ? atoi(pass_args.c_str()) : 0;
521     if (factor > 0) {
522       opt::LoopPeelingPass::SetLoopPeelingThreshold(factor);
523     } else {
524       Error(consumer(), nullptr, {},
525             "--loop-peeling-threshold must have a positive integer argument");
526       return false;
527     }
528   } else if (pass_name == "ccp") {
529     RegisterPass(CreateCCPPass());
530   } else if (pass_name == "code-sink") {
531     RegisterPass(CreateCodeSinkingPass());
532   } else if (pass_name == "fix-storage-class") {
533     RegisterPass(CreateFixStorageClassPass());
534   } else if (pass_name == "O") {
535     RegisterPerformancePasses(preserve_interface);
536   } else if (pass_name == "Os") {
537     RegisterSizePasses(preserve_interface);
538   } else if (pass_name == "legalize-hlsl") {
539     RegisterLegalizationPasses(preserve_interface);
540   } else if (pass_name == "remove-unused-interface-variables") {
541     RegisterPass(CreateRemoveUnusedInterfaceVariablesPass());
542   } else if (pass_name == "graphics-robust-access") {
543     RegisterPass(CreateGraphicsRobustAccessPass());
544   } else if (pass_name == "wrap-opkill") {
545     RegisterPass(CreateWrapOpKillPass());
546   } else if (pass_name == "amd-ext-to-khr") {
547     RegisterPass(CreateAmdExtToKhrPass());
548   } else if (pass_name == "interpolate-fixup") {
549     RegisterPass(CreateInterpolateFixupPass());
550   } else if (pass_name == "remove-dont-inline") {
551     RegisterPass(CreateRemoveDontInlinePass());
552   } else if (pass_name == "eliminate-dead-input-components") {
553     RegisterPass(CreateEliminateDeadInputComponentsSafePass());
554   } else if (pass_name == "fix-func-call-param") {
555     RegisterPass(CreateFixFuncCallArgumentsPass());
556   } else if (pass_name == "convert-to-sampled-image") {
557     if (pass_args.size() > 0) {
558       auto descriptor_set_binding_pairs =
559           opt::ConvertToSampledImagePass::ParseDescriptorSetBindingPairsString(
560               pass_args.c_str());
561       if (!descriptor_set_binding_pairs) {
562         Errorf(consumer(), nullptr, {},
563                "Invalid argument for --convert-to-sampled-image: %s",
564                pass_args.c_str());
565         return false;
566       }
567       RegisterPass(CreateConvertToSampledImagePass(
568           std::move(*descriptor_set_binding_pairs)));
569     } else {
570       Errorf(consumer(), nullptr, {},
571              "Invalid pairs of descriptor set and binding '%s'. Expected a "
572              "string of <descriptor set>:<binding> pairs.",
573              pass_args.c_str());
574       return false;
575     }
576   } else if (pass_name == "switch-descriptorset") {
577     if (pass_args.size() == 0) {
578       Error(consumer(), nullptr, {},
579             "--switch-descriptorset requires a from:to argument.");
580       return false;
581     }
582     uint32_t from_set = 0, to_set = 0;
583     const char* start = pass_args.data();
584     const char* end = pass_args.data() + pass_args.size();
585 
586     auto result = std::from_chars(start, end, from_set);
587     if (result.ec != std::errc()) {
588       Errorf(consumer(), nullptr, {},
589              "Invalid argument for --switch-descriptorset: %s",
590              pass_args.c_str());
591       return false;
592     }
593     start = result.ptr;
594     if (start[0] != ':') {
595       Errorf(consumer(), nullptr, {},
596              "Invalid argument for --switch-descriptorset: %s",
597              pass_args.c_str());
598       return false;
599     }
600     start++;
601     result = std::from_chars(start, end, to_set);
602     if (result.ec != std::errc() || result.ptr != end) {
603       Errorf(consumer(), nullptr, {},
604              "Invalid argument for --switch-descriptorset: %s",
605              pass_args.c_str());
606       return false;
607     }
608     RegisterPass(CreateSwitchDescriptorSetPass(from_set, to_set));
609   } else if (pass_name == "modify-maximal-reconvergence") {
610     if (pass_args.size() == 0) {
611       Error(consumer(), nullptr, {},
612             "--modify-maximal-reconvergence requires an argument");
613       return false;
614     }
615     if (pass_args == "add") {
616       RegisterPass(CreateModifyMaximalReconvergencePass(true));
617     } else if (pass_args == "remove") {
618       RegisterPass(CreateModifyMaximalReconvergencePass(false));
619     } else {
620       Errorf(consumer(), nullptr, {},
621              "Invalid argument for --modify-maximal-reconvergence: %s (must be "
622              "'add' or 'remove')",
623              pass_args.c_str());
624       return false;
625     }
626   } else if (pass_name == "trim-capabilities") {
627     RegisterPass(CreateTrimCapabilitiesPass());
628   } else {
629     Errorf(consumer(), nullptr, {},
630            "Unknown flag '--%s'. Use --help for a list of valid flags",
631            pass_name.c_str());
632     return false;
633   }
634 
635   return true;
636 }
637 
SetTargetEnv(const spv_target_env env)638 void Optimizer::SetTargetEnv(const spv_target_env env) {
639   impl_->target_env = env;
640 }
641 
Run(const uint32_t * original_binary,const size_t original_binary_size,std::vector<uint32_t> * optimized_binary) const642 bool Optimizer::Run(const uint32_t* original_binary,
643                     const size_t original_binary_size,
644                     std::vector<uint32_t>* optimized_binary) const {
645   return Run(original_binary, original_binary_size, optimized_binary,
646              OptimizerOptions());
647 }
648 
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) const649 bool Optimizer::Run(const uint32_t* original_binary,
650                     const size_t original_binary_size,
651                     std::vector<uint32_t>* optimized_binary,
652                     const ValidatorOptions& validator_options,
653                     bool skip_validation) const {
654   OptimizerOptions opt_options;
655   opt_options.set_run_validator(!skip_validation);
656   opt_options.set_validator_options(validator_options);
657   return Run(original_binary, original_binary_size, optimized_binary,
658              opt_options);
659 }
660 
Run(const uint32_t * original_binary,const size_t original_binary_size,std::vector<uint32_t> * optimized_binary,const spv_optimizer_options opt_options) const661 bool Optimizer::Run(const uint32_t* original_binary,
662                     const size_t original_binary_size,
663                     std::vector<uint32_t>* optimized_binary,
664                     const spv_optimizer_options opt_options) const {
665   spvtools::SpirvTools tools(impl_->target_env);
666   tools.SetMessageConsumer(impl_->pass_manager.consumer());
667   if (opt_options->run_validator_ &&
668       !tools.Validate(original_binary, original_binary_size,
669                       &opt_options->val_options_)) {
670     return false;
671   }
672 
673   std::unique_ptr<opt::IRContext> context = BuildModule(
674       impl_->target_env, consumer(), original_binary, original_binary_size);
675   if (context == nullptr) return false;
676 
677   context->set_max_id_bound(opt_options->max_id_bound_);
678   context->set_preserve_bindings(opt_options->preserve_bindings_);
679   context->set_preserve_spec_constants(opt_options->preserve_spec_constants_);
680 
681   impl_->pass_manager.SetValidatorOptions(&opt_options->val_options_);
682   impl_->pass_manager.SetTargetEnv(impl_->target_env);
683   auto status = impl_->pass_manager.Run(context.get());
684 
685   if (status == opt::Pass::Status::Failure) {
686     return false;
687   }
688 
689 #ifndef NDEBUG
690   // We do not keep the result id of DebugScope in struct DebugScope.
691   // Instead, we assign random ids for them, which results in integrity
692   // check failures. In addition, propagating the OpLine/OpNoLine to preserve
693   // the debug information through transformations results in integrity
694   // check failures. We want to skip the integrity check when the module
695   // contains DebugScope or OpLine/OpNoLine instructions.
696   if (status == opt::Pass::Status::SuccessWithoutChange &&
697       !context->module()->ContainsDebugInfo()) {
698     std::vector<uint32_t> optimized_binary_with_nop;
699     context->module()->ToBinary(&optimized_binary_with_nop,
700                                 /* skip_nop = */ false);
701     assert(optimized_binary_with_nop.size() == original_binary_size &&
702            "Binary size unexpectedly changed despite the optimizer saying "
703            "there was no change");
704 
705     // Compare the magic number to make sure the binaries were encoded in the
706     // endianness.  If not, the contents of the binaries will be different, so
707     // do not check the contents.
708     if (optimized_binary_with_nop[0] == original_binary[0]) {
709       assert(memcmp(optimized_binary_with_nop.data(), original_binary,
710                     original_binary_size) == 0 &&
711              "Binary content unexpectedly changed despite the optimizer saying "
712              "there was no change");
713     }
714   }
715 #endif  // !NDEBUG
716 
717   // Note that |original_binary| and |optimized_binary| may share the same
718   // buffer and the below will invalidate |original_binary|.
719   optimized_binary->clear();
720   context->module()->ToBinary(optimized_binary, /* skip_nop = */ true);
721 
722   return true;
723 }
724 
SetPrintAll(std::ostream * out)725 Optimizer& Optimizer::SetPrintAll(std::ostream* out) {
726   impl_->pass_manager.SetPrintAll(out);
727   return *this;
728 }
729 
SetTimeReport(std::ostream * out)730 Optimizer& Optimizer::SetTimeReport(std::ostream* out) {
731   impl_->pass_manager.SetTimeReport(out);
732   return *this;
733 }
734 
SetValidateAfterAll(bool validate)735 Optimizer& Optimizer::SetValidateAfterAll(bool validate) {
736   impl_->pass_manager.SetValidateAfterAll(validate);
737   return *this;
738 }
739 
CreateNullPass()740 Optimizer::PassToken CreateNullPass() {
741   return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::NullPass>());
742 }
743 
CreateStripDebugInfoPass()744 Optimizer::PassToken CreateStripDebugInfoPass() {
745   return MakeUnique<Optimizer::PassToken::Impl>(
746       MakeUnique<opt::StripDebugInfoPass>());
747 }
748 
CreateStripReflectInfoPass()749 Optimizer::PassToken CreateStripReflectInfoPass() {
750   return CreateStripNonSemanticInfoPass();
751 }
752 
CreateStripNonSemanticInfoPass()753 Optimizer::PassToken CreateStripNonSemanticInfoPass() {
754   return MakeUnique<Optimizer::PassToken::Impl>(
755       MakeUnique<opt::StripNonSemanticInfoPass>());
756 }
757 
CreateEliminateDeadFunctionsPass()758 Optimizer::PassToken CreateEliminateDeadFunctionsPass() {
759   return MakeUnique<Optimizer::PassToken::Impl>(
760       MakeUnique<opt::EliminateDeadFunctionsPass>());
761 }
762 
CreateEliminateDeadMembersPass()763 Optimizer::PassToken CreateEliminateDeadMembersPass() {
764   return MakeUnique<Optimizer::PassToken::Impl>(
765       MakeUnique<opt::EliminateDeadMembersPass>());
766 }
767 
CreateSetSpecConstantDefaultValuePass(const std::unordered_map<uint32_t,std::string> & id_value_map)768 Optimizer::PassToken CreateSetSpecConstantDefaultValuePass(
769     const std::unordered_map<uint32_t, std::string>& id_value_map) {
770   return MakeUnique<Optimizer::PassToken::Impl>(
771       MakeUnique<opt::SetSpecConstantDefaultValuePass>(id_value_map));
772 }
773 
CreateSetSpecConstantDefaultValuePass(const std::unordered_map<uint32_t,std::vector<uint32_t>> & id_value_map)774 Optimizer::PassToken CreateSetSpecConstantDefaultValuePass(
775     const std::unordered_map<uint32_t, std::vector<uint32_t>>& id_value_map) {
776   return MakeUnique<Optimizer::PassToken::Impl>(
777       MakeUnique<opt::SetSpecConstantDefaultValuePass>(id_value_map));
778 }
779 
CreateFlattenDecorationPass()780 Optimizer::PassToken CreateFlattenDecorationPass() {
781   return MakeUnique<Optimizer::PassToken::Impl>(
782       MakeUnique<opt::FlattenDecorationPass>());
783 }
784 
CreateFreezeSpecConstantValuePass()785 Optimizer::PassToken CreateFreezeSpecConstantValuePass() {
786   return MakeUnique<Optimizer::PassToken::Impl>(
787       MakeUnique<opt::FreezeSpecConstantValuePass>());
788 }
789 
CreateFoldSpecConstantOpAndCompositePass()790 Optimizer::PassToken CreateFoldSpecConstantOpAndCompositePass() {
791   return MakeUnique<Optimizer::PassToken::Impl>(
792       MakeUnique<opt::FoldSpecConstantOpAndCompositePass>());
793 }
794 
CreateUnifyConstantPass()795 Optimizer::PassToken CreateUnifyConstantPass() {
796   return MakeUnique<Optimizer::PassToken::Impl>(
797       MakeUnique<opt::UnifyConstantPass>());
798 }
799 
CreateEliminateDeadConstantPass()800 Optimizer::PassToken CreateEliminateDeadConstantPass() {
801   return MakeUnique<Optimizer::PassToken::Impl>(
802       MakeUnique<opt::EliminateDeadConstantPass>());
803 }
804 
CreateDeadVariableEliminationPass()805 Optimizer::PassToken CreateDeadVariableEliminationPass() {
806   return MakeUnique<Optimizer::PassToken::Impl>(
807       MakeUnique<opt::DeadVariableElimination>());
808 }
809 
CreateStrengthReductionPass()810 Optimizer::PassToken CreateStrengthReductionPass() {
811   return MakeUnique<Optimizer::PassToken::Impl>(
812       MakeUnique<opt::StrengthReductionPass>());
813 }
814 
CreateBlockMergePass()815 Optimizer::PassToken CreateBlockMergePass() {
816   return MakeUnique<Optimizer::PassToken::Impl>(
817       MakeUnique<opt::BlockMergePass>());
818 }
819 
CreateInlineExhaustivePass()820 Optimizer::PassToken CreateInlineExhaustivePass() {
821   return MakeUnique<Optimizer::PassToken::Impl>(
822       MakeUnique<opt::InlineExhaustivePass>());
823 }
824 
CreateInlineOpaquePass()825 Optimizer::PassToken CreateInlineOpaquePass() {
826   return MakeUnique<Optimizer::PassToken::Impl>(
827       MakeUnique<opt::InlineOpaquePass>());
828 }
829 
CreateLocalAccessChainConvertPass()830 Optimizer::PassToken CreateLocalAccessChainConvertPass() {
831   return MakeUnique<Optimizer::PassToken::Impl>(
832       MakeUnique<opt::LocalAccessChainConvertPass>());
833 }
834 
CreateLocalSingleBlockLoadStoreElimPass()835 Optimizer::PassToken CreateLocalSingleBlockLoadStoreElimPass() {
836   return MakeUnique<Optimizer::PassToken::Impl>(
837       MakeUnique<opt::LocalSingleBlockLoadStoreElimPass>());
838 }
839 
CreateLocalSingleStoreElimPass()840 Optimizer::PassToken CreateLocalSingleStoreElimPass() {
841   return MakeUnique<Optimizer::PassToken::Impl>(
842       MakeUnique<opt::LocalSingleStoreElimPass>());
843 }
844 
CreateInsertExtractElimPass()845 Optimizer::PassToken CreateInsertExtractElimPass() {
846   return MakeUnique<Optimizer::PassToken::Impl>(
847       MakeUnique<opt::SimplificationPass>());
848 }
849 
CreateDeadInsertElimPass()850 Optimizer::PassToken CreateDeadInsertElimPass() {
851   return MakeUnique<Optimizer::PassToken::Impl>(
852       MakeUnique<opt::DeadInsertElimPass>());
853 }
854 
CreateDeadBranchElimPass()855 Optimizer::PassToken CreateDeadBranchElimPass() {
856   return MakeUnique<Optimizer::PassToken::Impl>(
857       MakeUnique<opt::DeadBranchElimPass>());
858 }
859 
CreateLocalMultiStoreElimPass()860 Optimizer::PassToken CreateLocalMultiStoreElimPass() {
861   return MakeUnique<Optimizer::PassToken::Impl>(
862       MakeUnique<opt::SSARewritePass>());
863 }
864 
CreateAggressiveDCEPass()865 Optimizer::PassToken CreateAggressiveDCEPass() {
866   return MakeUnique<Optimizer::PassToken::Impl>(
867       MakeUnique<opt::AggressiveDCEPass>(false, false));
868 }
869 
CreateAggressiveDCEPass(bool preserve_interface)870 Optimizer::PassToken CreateAggressiveDCEPass(bool preserve_interface) {
871   return MakeUnique<Optimizer::PassToken::Impl>(
872       MakeUnique<opt::AggressiveDCEPass>(preserve_interface, false));
873 }
874 
CreateAggressiveDCEPass(bool preserve_interface,bool remove_outputs)875 Optimizer::PassToken CreateAggressiveDCEPass(bool preserve_interface,
876                                              bool remove_outputs) {
877   return MakeUnique<Optimizer::PassToken::Impl>(
878       MakeUnique<opt::AggressiveDCEPass>(preserve_interface, remove_outputs));
879 }
880 
CreateRemoveUnusedInterfaceVariablesPass()881 Optimizer::PassToken CreateRemoveUnusedInterfaceVariablesPass() {
882   return MakeUnique<Optimizer::PassToken::Impl>(
883       MakeUnique<opt::RemoveUnusedInterfaceVariablesPass>());
884 }
885 
CreatePropagateLineInfoPass()886 Optimizer::PassToken CreatePropagateLineInfoPass() {
887   return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::EmptyPass>());
888 }
889 
CreateRedundantLineInfoElimPass()890 Optimizer::PassToken CreateRedundantLineInfoElimPass() {
891   return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::EmptyPass>());
892 }
893 
CreateCompactIdsPass()894 Optimizer::PassToken CreateCompactIdsPass() {
895   return MakeUnique<Optimizer::PassToken::Impl>(
896       MakeUnique<opt::CompactIdsPass>());
897 }
898 
CreateMergeReturnPass()899 Optimizer::PassToken CreateMergeReturnPass() {
900   return MakeUnique<Optimizer::PassToken::Impl>(
901       MakeUnique<opt::MergeReturnPass>());
902 }
903 
GetPassNames() const904 std::vector<const char*> Optimizer::GetPassNames() const {
905   std::vector<const char*> v;
906   for (uint32_t i = 0; i < impl_->pass_manager.NumPasses(); i++) {
907     v.push_back(impl_->pass_manager.GetPass(i)->name());
908   }
909   return v;
910 }
911 
CreateCFGCleanupPass()912 Optimizer::PassToken CreateCFGCleanupPass() {
913   return MakeUnique<Optimizer::PassToken::Impl>(
914       MakeUnique<opt::CFGCleanupPass>());
915 }
916 
CreateLocalRedundancyEliminationPass()917 Optimizer::PassToken CreateLocalRedundancyEliminationPass() {
918   return MakeUnique<Optimizer::PassToken::Impl>(
919       MakeUnique<opt::LocalRedundancyEliminationPass>());
920 }
921 
CreateLoopFissionPass(size_t threshold)922 Optimizer::PassToken CreateLoopFissionPass(size_t threshold) {
923   return MakeUnique<Optimizer::PassToken::Impl>(
924       MakeUnique<opt::LoopFissionPass>(threshold));
925 }
926 
CreateLoopFusionPass(size_t max_registers_per_loop)927 Optimizer::PassToken CreateLoopFusionPass(size_t max_registers_per_loop) {
928   return MakeUnique<Optimizer::PassToken::Impl>(
929       MakeUnique<opt::LoopFusionPass>(max_registers_per_loop));
930 }
931 
CreateLoopInvariantCodeMotionPass()932 Optimizer::PassToken CreateLoopInvariantCodeMotionPass() {
933   return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::LICMPass>());
934 }
935 
CreateLoopPeelingPass()936 Optimizer::PassToken CreateLoopPeelingPass() {
937   return MakeUnique<Optimizer::PassToken::Impl>(
938       MakeUnique<opt::LoopPeelingPass>());
939 }
940 
CreateLoopUnswitchPass()941 Optimizer::PassToken CreateLoopUnswitchPass() {
942   return MakeUnique<Optimizer::PassToken::Impl>(
943       MakeUnique<opt::LoopUnswitchPass>());
944 }
945 
CreateRedundancyEliminationPass()946 Optimizer::PassToken CreateRedundancyEliminationPass() {
947   return MakeUnique<Optimizer::PassToken::Impl>(
948       MakeUnique<opt::RedundancyEliminationPass>());
949 }
950 
CreateRemoveDuplicatesPass()951 Optimizer::PassToken CreateRemoveDuplicatesPass() {
952   return MakeUnique<Optimizer::PassToken::Impl>(
953       MakeUnique<opt::RemoveDuplicatesPass>());
954 }
955 
CreateScalarReplacementPass(uint32_t size_limit)956 Optimizer::PassToken CreateScalarReplacementPass(uint32_t size_limit) {
957   return MakeUnique<Optimizer::PassToken::Impl>(
958       MakeUnique<opt::ScalarReplacementPass>(size_limit));
959 }
960 
CreatePrivateToLocalPass()961 Optimizer::PassToken CreatePrivateToLocalPass() {
962   return MakeUnique<Optimizer::PassToken::Impl>(
963       MakeUnique<opt::PrivateToLocalPass>());
964 }
965 
CreateCCPPass()966 Optimizer::PassToken CreateCCPPass() {
967   return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::CCPPass>());
968 }
969 
CreateWorkaround1209Pass()970 Optimizer::PassToken CreateWorkaround1209Pass() {
971   return MakeUnique<Optimizer::PassToken::Impl>(
972       MakeUnique<opt::Workaround1209>());
973 }
974 
CreateIfConversionPass()975 Optimizer::PassToken CreateIfConversionPass() {
976   return MakeUnique<Optimizer::PassToken::Impl>(
977       MakeUnique<opt::IfConversion>());
978 }
979 
CreateReplaceInvalidOpcodePass()980 Optimizer::PassToken CreateReplaceInvalidOpcodePass() {
981   return MakeUnique<Optimizer::PassToken::Impl>(
982       MakeUnique<opt::ReplaceInvalidOpcodePass>());
983 }
984 
CreateSimplificationPass()985 Optimizer::PassToken CreateSimplificationPass() {
986   return MakeUnique<Optimizer::PassToken::Impl>(
987       MakeUnique<opt::SimplificationPass>());
988 }
989 
CreateLoopUnrollPass(bool fully_unroll,int factor)990 Optimizer::PassToken CreateLoopUnrollPass(bool fully_unroll, int factor) {
991   return MakeUnique<Optimizer::PassToken::Impl>(
992       MakeUnique<opt::LoopUnroller>(fully_unroll, factor));
993 }
994 
CreateSSARewritePass()995 Optimizer::PassToken CreateSSARewritePass() {
996   return MakeUnique<Optimizer::PassToken::Impl>(
997       MakeUnique<opt::SSARewritePass>());
998 }
999 
CreateCopyPropagateArraysPass()1000 Optimizer::PassToken CreateCopyPropagateArraysPass() {
1001   return MakeUnique<Optimizer::PassToken::Impl>(
1002       MakeUnique<opt::CopyPropagateArrays>());
1003 }
1004 
CreateVectorDCEPass()1005 Optimizer::PassToken CreateVectorDCEPass() {
1006   return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::VectorDCE>());
1007 }
1008 
CreateReduceLoadSizePass(double load_replacement_threshold)1009 Optimizer::PassToken CreateReduceLoadSizePass(
1010     double load_replacement_threshold) {
1011   return MakeUnique<Optimizer::PassToken::Impl>(
1012       MakeUnique<opt::ReduceLoadSize>(load_replacement_threshold));
1013 }
1014 
CreateCombineAccessChainsPass()1015 Optimizer::PassToken CreateCombineAccessChainsPass() {
1016   return MakeUnique<Optimizer::PassToken::Impl>(
1017       MakeUnique<opt::CombineAccessChains>());
1018 }
1019 
CreateUpgradeMemoryModelPass()1020 Optimizer::PassToken CreateUpgradeMemoryModelPass() {
1021   return MakeUnique<Optimizer::PassToken::Impl>(
1022       MakeUnique<opt::UpgradeMemoryModel>());
1023 }
1024 
CreateInstBindlessCheckPass(uint32_t shader_id)1025 Optimizer::PassToken CreateInstBindlessCheckPass(uint32_t shader_id) {
1026   return MakeUnique<Optimizer::PassToken::Impl>(
1027       MakeUnique<opt::InstBindlessCheckPass>(shader_id));
1028 }
1029 
CreateInstDebugPrintfPass(uint32_t desc_set,uint32_t shader_id)1030 Optimizer::PassToken CreateInstDebugPrintfPass(uint32_t desc_set,
1031                                                uint32_t shader_id) {
1032   return MakeUnique<Optimizer::PassToken::Impl>(
1033       MakeUnique<opt::InstDebugPrintfPass>(desc_set, shader_id));
1034 }
1035 
CreateInstBuffAddrCheckPass(uint32_t shader_id)1036 Optimizer::PassToken CreateInstBuffAddrCheckPass(uint32_t shader_id) {
1037   return MakeUnique<Optimizer::PassToken::Impl>(
1038       MakeUnique<opt::InstBuffAddrCheckPass>(shader_id));
1039 }
1040 
CreateConvertRelaxedToHalfPass()1041 Optimizer::PassToken CreateConvertRelaxedToHalfPass() {
1042   return MakeUnique<Optimizer::PassToken::Impl>(
1043       MakeUnique<opt::ConvertToHalfPass>());
1044 }
1045 
CreateRelaxFloatOpsPass()1046 Optimizer::PassToken CreateRelaxFloatOpsPass() {
1047   return MakeUnique<Optimizer::PassToken::Impl>(
1048       MakeUnique<opt::RelaxFloatOpsPass>());
1049 }
1050 
CreateCodeSinkingPass()1051 Optimizer::PassToken CreateCodeSinkingPass() {
1052   return MakeUnique<Optimizer::PassToken::Impl>(
1053       MakeUnique<opt::CodeSinkingPass>());
1054 }
1055 
CreateFixStorageClassPass()1056 Optimizer::PassToken CreateFixStorageClassPass() {
1057   return MakeUnique<Optimizer::PassToken::Impl>(
1058       MakeUnique<opt::FixStorageClass>());
1059 }
1060 
CreateGraphicsRobustAccessPass()1061 Optimizer::PassToken CreateGraphicsRobustAccessPass() {
1062   return MakeUnique<Optimizer::PassToken::Impl>(
1063       MakeUnique<opt::GraphicsRobustAccessPass>());
1064 }
1065 
CreateReplaceDescArrayAccessUsingVarIndexPass()1066 Optimizer::PassToken CreateReplaceDescArrayAccessUsingVarIndexPass() {
1067   return MakeUnique<Optimizer::PassToken::Impl>(
1068       MakeUnique<opt::ReplaceDescArrayAccessUsingVarIndex>());
1069 }
1070 
CreateSpreadVolatileSemanticsPass()1071 Optimizer::PassToken CreateSpreadVolatileSemanticsPass() {
1072   return MakeUnique<Optimizer::PassToken::Impl>(
1073       MakeUnique<opt::SpreadVolatileSemantics>());
1074 }
1075 
CreateDescriptorScalarReplacementPass()1076 Optimizer::PassToken CreateDescriptorScalarReplacementPass() {
1077   return MakeUnique<Optimizer::PassToken::Impl>(
1078       MakeUnique<opt::DescriptorScalarReplacement>());
1079 }
1080 
CreateWrapOpKillPass()1081 Optimizer::PassToken CreateWrapOpKillPass() {
1082   return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::WrapOpKill>());
1083 }
1084 
CreateAmdExtToKhrPass()1085 Optimizer::PassToken CreateAmdExtToKhrPass() {
1086   return MakeUnique<Optimizer::PassToken::Impl>(
1087       MakeUnique<opt::AmdExtensionToKhrPass>());
1088 }
1089 
CreateInterpolateFixupPass()1090 Optimizer::PassToken CreateInterpolateFixupPass() {
1091   return MakeUnique<Optimizer::PassToken::Impl>(
1092       MakeUnique<opt::InterpFixupPass>());
1093 }
1094 
CreateEliminateDeadInputComponentsPass()1095 Optimizer::PassToken CreateEliminateDeadInputComponentsPass() {
1096   return MakeUnique<Optimizer::PassToken::Impl>(
1097       MakeUnique<opt::EliminateDeadIOComponentsPass>(spv::StorageClass::Input,
1098                                                      /* safe_mode */ false));
1099 }
1100 
CreateEliminateDeadOutputComponentsPass()1101 Optimizer::PassToken CreateEliminateDeadOutputComponentsPass() {
1102   return MakeUnique<Optimizer::PassToken::Impl>(
1103       MakeUnique<opt::EliminateDeadIOComponentsPass>(spv::StorageClass::Output,
1104                                                      /* safe_mode */ false));
1105 }
1106 
CreateEliminateDeadInputComponentsSafePass()1107 Optimizer::PassToken CreateEliminateDeadInputComponentsSafePass() {
1108   return MakeUnique<Optimizer::PassToken::Impl>(
1109       MakeUnique<opt::EliminateDeadIOComponentsPass>(spv::StorageClass::Input,
1110                                                      /* safe_mode */ true));
1111 }
1112 
CreateAnalyzeLiveInputPass(std::unordered_set<uint32_t> * live_locs,std::unordered_set<uint32_t> * live_builtins)1113 Optimizer::PassToken CreateAnalyzeLiveInputPass(
1114     std::unordered_set<uint32_t>* live_locs,
1115     std::unordered_set<uint32_t>* live_builtins) {
1116   return MakeUnique<Optimizer::PassToken::Impl>(
1117       MakeUnique<opt::AnalyzeLiveInputPass>(live_locs, live_builtins));
1118 }
1119 
CreateEliminateDeadOutputStoresPass(std::unordered_set<uint32_t> * live_locs,std::unordered_set<uint32_t> * live_builtins)1120 Optimizer::PassToken CreateEliminateDeadOutputStoresPass(
1121     std::unordered_set<uint32_t>* live_locs,
1122     std::unordered_set<uint32_t>* live_builtins) {
1123   return MakeUnique<Optimizer::PassToken::Impl>(
1124       MakeUnique<opt::EliminateDeadOutputStoresPass>(live_locs, live_builtins));
1125 }
1126 
CreateConvertToSampledImagePass(const std::vector<opt::DescriptorSetAndBinding> & descriptor_set_binding_pairs)1127 Optimizer::PassToken CreateConvertToSampledImagePass(
1128     const std::vector<opt::DescriptorSetAndBinding>&
1129         descriptor_set_binding_pairs) {
1130   return MakeUnique<Optimizer::PassToken::Impl>(
1131       MakeUnique<opt::ConvertToSampledImagePass>(descriptor_set_binding_pairs));
1132 }
1133 
CreateInterfaceVariableScalarReplacementPass()1134 Optimizer::PassToken CreateInterfaceVariableScalarReplacementPass() {
1135   return MakeUnique<Optimizer::PassToken::Impl>(
1136       MakeUnique<opt::InterfaceVariableScalarReplacement>());
1137 }
1138 
CreateRemoveDontInlinePass()1139 Optimizer::PassToken CreateRemoveDontInlinePass() {
1140   return MakeUnique<Optimizer::PassToken::Impl>(
1141       MakeUnique<opt::RemoveDontInline>());
1142 }
1143 
CreateFixFuncCallArgumentsPass()1144 Optimizer::PassToken CreateFixFuncCallArgumentsPass() {
1145   return MakeUnique<Optimizer::PassToken::Impl>(
1146       MakeUnique<opt::FixFuncCallArgumentsPass>());
1147 }
1148 
CreateTrimCapabilitiesPass()1149 Optimizer::PassToken CreateTrimCapabilitiesPass() {
1150   return MakeUnique<Optimizer::PassToken::Impl>(
1151       MakeUnique<opt::TrimCapabilitiesPass>());
1152 }
1153 
CreateSwitchDescriptorSetPass(uint32_t from,uint32_t to)1154 Optimizer::PassToken CreateSwitchDescriptorSetPass(uint32_t from, uint32_t to) {
1155   return MakeUnique<Optimizer::PassToken::Impl>(
1156       MakeUnique<opt::SwitchDescriptorSetPass>(from, to));
1157 }
1158 
CreateInvocationInterlockPlacementPass()1159 Optimizer::PassToken CreateInvocationInterlockPlacementPass() {
1160   return MakeUnique<Optimizer::PassToken::Impl>(
1161       MakeUnique<opt::InvocationInterlockPlacementPass>());
1162 }
1163 
CreateModifyMaximalReconvergencePass(bool add)1164 Optimizer::PassToken CreateModifyMaximalReconvergencePass(bool add) {
1165   return MakeUnique<Optimizer::PassToken::Impl>(
1166       MakeUnique<opt::ModifyMaximalReconvergence>(add));
1167 }
1168 }  // namespace spvtools
1169 
1170 extern "C" {
1171 
spvOptimizerCreate(spv_target_env env)1172 SPIRV_TOOLS_EXPORT spv_optimizer_t* spvOptimizerCreate(spv_target_env env) {
1173   return reinterpret_cast<spv_optimizer_t*>(new spvtools::Optimizer(env));
1174 }
1175 
spvOptimizerDestroy(spv_optimizer_t * optimizer)1176 SPIRV_TOOLS_EXPORT void spvOptimizerDestroy(spv_optimizer_t* optimizer) {
1177   delete reinterpret_cast<spvtools::Optimizer*>(optimizer);
1178 }
1179 
spvOptimizerSetMessageConsumer(spv_optimizer_t * optimizer,spv_message_consumer consumer)1180 SPIRV_TOOLS_EXPORT void spvOptimizerSetMessageConsumer(
1181     spv_optimizer_t* optimizer, spv_message_consumer consumer) {
1182   reinterpret_cast<spvtools::Optimizer*>(optimizer)->
1183       SetMessageConsumer(
1184           [consumer](spv_message_level_t level, const char* source,
1185                      const spv_position_t& position, const char* message) {
1186             return consumer(level, source, &position, message);
1187           });
1188 }
1189 
spvOptimizerRegisterLegalizationPasses(spv_optimizer_t * optimizer)1190 SPIRV_TOOLS_EXPORT void spvOptimizerRegisterLegalizationPasses(
1191     spv_optimizer_t* optimizer) {
1192   reinterpret_cast<spvtools::Optimizer*>(optimizer)->
1193       RegisterLegalizationPasses();
1194 }
1195 
spvOptimizerRegisterPerformancePasses(spv_optimizer_t * optimizer)1196 SPIRV_TOOLS_EXPORT void spvOptimizerRegisterPerformancePasses(
1197     spv_optimizer_t* optimizer) {
1198   reinterpret_cast<spvtools::Optimizer*>(optimizer)->
1199       RegisterPerformancePasses();
1200 }
1201 
spvOptimizerRegisterSizePasses(spv_optimizer_t * optimizer)1202 SPIRV_TOOLS_EXPORT void spvOptimizerRegisterSizePasses(
1203     spv_optimizer_t* optimizer) {
1204   reinterpret_cast<spvtools::Optimizer*>(optimizer)->RegisterSizePasses();
1205 }
1206 
spvOptimizerRegisterPassFromFlag(spv_optimizer_t * optimizer,const char * flag)1207 SPIRV_TOOLS_EXPORT bool spvOptimizerRegisterPassFromFlag(
1208     spv_optimizer_t* optimizer, const char* flag)
1209 {
1210   return reinterpret_cast<spvtools::Optimizer*>(optimizer)->
1211       RegisterPassFromFlag(flag);
1212 }
1213 
spvOptimizerRegisterPassesFromFlags(spv_optimizer_t * optimizer,const char ** flags,const size_t flag_count)1214 SPIRV_TOOLS_EXPORT bool spvOptimizerRegisterPassesFromFlags(
1215     spv_optimizer_t* optimizer, const char** flags, const size_t flag_count) {
1216   std::vector<std::string> opt_flags =
1217       spvtools::GetVectorOfStrings(flags, flag_count);
1218   return reinterpret_cast<spvtools::Optimizer*>(optimizer)
1219       ->RegisterPassesFromFlags(opt_flags, false);
1220 }
1221 
1222 SPIRV_TOOLS_EXPORT bool
spvOptimizerRegisterPassesFromFlagsWhilePreservingTheInterface(spv_optimizer_t * optimizer,const char ** flags,const size_t flag_count)1223 spvOptimizerRegisterPassesFromFlagsWhilePreservingTheInterface(
1224     spv_optimizer_t* optimizer, const char** flags, const size_t flag_count) {
1225   std::vector<std::string> opt_flags =
1226       spvtools::GetVectorOfStrings(flags, flag_count);
1227   return reinterpret_cast<spvtools::Optimizer*>(optimizer)
1228       ->RegisterPassesFromFlags(opt_flags, true);
1229 }
1230 
1231 SPIRV_TOOLS_EXPORT
spvOptimizerRun(spv_optimizer_t * optimizer,const uint32_t * binary,const size_t word_count,spv_binary * optimized_binary,const spv_optimizer_options options)1232 spv_result_t spvOptimizerRun(spv_optimizer_t* optimizer,
1233                              const uint32_t* binary,
1234                              const size_t word_count,
1235                              spv_binary* optimized_binary,
1236                              const spv_optimizer_options options) {
1237   std::vector<uint32_t> optimized;
1238 
1239   if (!reinterpret_cast<spvtools::Optimizer*>(optimizer)->
1240       Run(binary, word_count, &optimized, options)) {
1241     return SPV_ERROR_INTERNAL;
1242   }
1243 
1244   auto result_binary = new spv_binary_t();
1245   if (!result_binary) {
1246       *optimized_binary = nullptr;
1247       return SPV_ERROR_OUT_OF_MEMORY;
1248   }
1249 
1250   result_binary->code = new uint32_t[optimized.size()];
1251   if (!result_binary->code) {
1252       delete result_binary;
1253       *optimized_binary = nullptr;
1254       return SPV_ERROR_OUT_OF_MEMORY;
1255   }
1256   result_binary->wordCount = optimized.size();
1257 
1258   memcpy(result_binary->code, optimized.data(),
1259          optimized.size() * sizeof(uint32_t));
1260 
1261   *optimized_binary = result_binary;
1262 
1263   return SPV_SUCCESS;
1264 }
1265 
1266 }  // extern "C"
1267