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