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