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