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