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