blob: 635b075ff252eef2cc904bef8bbd23374cf48be5 [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
Ben Claytond0f684e2019-08-30 22:36:08 +010017#include <cassert>
Chris Forbescc5697f2019-01-30 11:54:08 -080018#include <memory>
19#include <string>
20#include <unordered_map>
21#include <utility>
22#include <vector>
23
24#include <source/spirv_optimizer_options.h>
Chris Forbescc5697f2019-01-30 11:54:08 -080025#include "source/opt/build_module.h"
Ben Claytond0f684e2019-08-30 22:36:08 +010026#include "source/opt/graphics_robust_access_pass.h"
Chris Forbescc5697f2019-01-30 11:54:08 -080027#include "source/opt/log.h"
28#include "source/opt/pass_manager.h"
29#include "source/opt/passes.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
Ben Claytonb73b7602019-07-29 13:56:13 +010061 spv_target_env target_env; // Target environment.
62 opt::PassManager pass_manager; // Internal implementation pass manager.
Chris Forbescc5697f2019-01-30 11:54:08 -080063};
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
Ben Claytond0f684e2019-08-30 22:36:08 +0100111 // 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())
Chris Forbescc5697f2019-01-30 11:54:08 -0800115 // 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())
Ben Claytonb73b7602019-07-29 13:56:13 +0100122 // 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())
Chris Forbescc5697f2019-01-30 11:54:08 -0800126 // 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() {
Ben Claytond0f684e2019-08-30 22:36:08 +0100160 return RegisterPass(CreateWrapOpKillPass())
161 .RegisterPass(CreateDeadBranchElimPass())
Chris Forbescc5697f2019-01-30 11:54:08 -0800162 .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())
Ben Claytonb73b7602019-07-29 13:56:13 +0100193 .RegisterPass(CreateSimplificationPass());
Chris Forbescc5697f2019-01-30 11:54:08 -0800194}
195
196Optimizer& Optimizer::RegisterSizePasses() {
Ben Claytond0f684e2019-08-30 22:36:08 +0100197 return RegisterPass(CreateWrapOpKillPass())
198 .RegisterPass(CreateDeadBranchElimPass())
Chris Forbescc5697f2019-01-30 11:54:08 -0800199 .RegisterPass(CreateMergeReturnPass())
200 .RegisterPass(CreateInlineExhaustivePass())
201 .RegisterPass(CreateAggressiveDCEPass())
202 .RegisterPass(CreatePrivateToLocalPass())
203 .RegisterPass(CreateScalarReplacementPass())
204 .RegisterPass(CreateLocalAccessChainConvertPass())
205 .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
206 .RegisterPass(CreateLocalSingleStoreElimPass())
207 .RegisterPass(CreateAggressiveDCEPass())
208 .RegisterPass(CreateSimplificationPass())
209 .RegisterPass(CreateDeadInsertElimPass())
210 .RegisterPass(CreateLocalMultiStoreElimPass())
211 .RegisterPass(CreateAggressiveDCEPass())
212 .RegisterPass(CreateCCPPass())
213 .RegisterPass(CreateAggressiveDCEPass())
214 .RegisterPass(CreateDeadBranchElimPass())
215 .RegisterPass(CreateIfConversionPass())
216 .RegisterPass(CreateAggressiveDCEPass())
217 .RegisterPass(CreateBlockMergePass())
218 .RegisterPass(CreateSimplificationPass())
219 .RegisterPass(CreateDeadInsertElimPass())
220 .RegisterPass(CreateRedundancyEliminationPass())
221 .RegisterPass(CreateCFGCleanupPass())
Chris Forbescc5697f2019-01-30 11:54:08 -0800222 .RegisterPass(CreateAggressiveDCEPass());
223}
224
Ben Claytonb73b7602019-07-29 13:56:13 +0100225Optimizer& Optimizer::RegisterVulkanToWebGPUPasses() {
226 return RegisterPass(CreateStripDebugInfoPass())
227 .RegisterPass(CreateStripAtomicCounterMemoryPass())
228 .RegisterPass(CreateGenerateWebGPUInitializersPass())
229 .RegisterPass(CreateLegalizeVectorShufflePass())
230 .RegisterPass(CreateSplitInvalidUnreachablePass())
231 .RegisterPass(CreateEliminateDeadConstantPass())
232 .RegisterPass(CreateFlattenDecorationPass())
233 .RegisterPass(CreateAggressiveDCEPass())
234 .RegisterPass(CreateDeadBranchElimPass())
235 .RegisterPass(CreateCompactIdsPass());
236}
237
238Optimizer& Optimizer::RegisterWebGPUToVulkanPasses() {
239 return RegisterPass(CreateDecomposeInitializedVariablesPass())
240 .RegisterPass(CreateCompactIdsPass());
Chris Forbescc5697f2019-01-30 11:54:08 -0800241}
242
243bool Optimizer::RegisterPassesFromFlags(const std::vector<std::string>& flags) {
244 for (const auto& flag : flags) {
245 if (!RegisterPassFromFlag(flag)) {
246 return false;
247 }
248 }
249
250 return true;
251}
252
253bool Optimizer::FlagHasValidForm(const std::string& flag) const {
254 if (flag == "-O" || flag == "-Os") {
255 return true;
256 } else if (flag.size() > 2 && flag.substr(0, 2) == "--") {
257 return true;
258 }
259
260 Errorf(consumer(), nullptr, {},
261 "%s is not a valid flag. Flag passes should have the form "
262 "'--pass_name[=pass_args]'. Special flag names also accepted: -O "
263 "and -Os.",
264 flag.c_str());
265 return false;
266}
267
268bool Optimizer::RegisterPassFromFlag(const std::string& flag) {
269 if (!FlagHasValidForm(flag)) {
270 return false;
271 }
272
273 // Split flags of the form --pass_name=pass_args.
274 auto p = utils::SplitFlagArgs(flag);
275 std::string pass_name = p.first;
276 std::string pass_args = p.second;
277
278 // FIXME(dnovillo): This should be re-factored so that pass names can be
279 // automatically checked against Pass::name() and PassToken instances created
280 // via a template function. Additionally, class Pass should have a desc()
281 // method that describes the pass (so it can be used in --help).
282 //
283 // Both Pass::name() and Pass::desc() should be static class members so they
284 // can be invoked without creating a pass instance.
Ben Claytonb73b7602019-07-29 13:56:13 +0100285 if (pass_name == "strip-atomic-counter-memory") {
286 RegisterPass(CreateStripAtomicCounterMemoryPass());
287 } else if (pass_name == "strip-debug") {
Chris Forbescc5697f2019-01-30 11:54:08 -0800288 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());
Ben Claytond0f684e2019-08-30 22:36:08 +0100323 } else if (pass_name == "descriptor-scalar-replacement") {
324 RegisterPass(CreateDescriptorScalarReplacementPass());
Chris Forbescc5697f2019-01-30 11:54:08 -0800325 } else if (pass_name == "eliminate-dead-code-aggressive") {
326 RegisterPass(CreateAggressiveDCEPass());
327 } else if (pass_name == "propagate-line-info") {
328 RegisterPass(CreatePropagateLineInfoPass());
329 } else if (pass_name == "eliminate-redundant-line-info") {
330 RegisterPass(CreateRedundantLineInfoElimPass());
331 } else if (pass_name == "eliminate-insert-extract") {
332 RegisterPass(CreateInsertExtractElimPass());
333 } else if (pass_name == "eliminate-local-single-block") {
334 RegisterPass(CreateLocalSingleBlockLoadStoreElimPass());
335 } else if (pass_name == "eliminate-local-single-store") {
336 RegisterPass(CreateLocalSingleStoreElimPass());
337 } else if (pass_name == "merge-blocks") {
338 RegisterPass(CreateBlockMergePass());
339 } else if (pass_name == "merge-return") {
340 RegisterPass(CreateMergeReturnPass());
341 } else if (pass_name == "eliminate-dead-branches") {
342 RegisterPass(CreateDeadBranchElimPass());
343 } else if (pass_name == "eliminate-dead-functions") {
344 RegisterPass(CreateEliminateDeadFunctionsPass());
345 } else if (pass_name == "eliminate-local-multi-store") {
346 RegisterPass(CreateLocalMultiStoreElimPass());
Chris Forbescc5697f2019-01-30 11:54:08 -0800347 } else if (pass_name == "eliminate-dead-const") {
348 RegisterPass(CreateEliminateDeadConstantPass());
349 } else if (pass_name == "eliminate-dead-inserts") {
350 RegisterPass(CreateDeadInsertElimPass());
351 } else if (pass_name == "eliminate-dead-variables") {
352 RegisterPass(CreateDeadVariableEliminationPass());
Ben Claytonb73b7602019-07-29 13:56:13 +0100353 } else if (pass_name == "eliminate-dead-members") {
354 RegisterPass(CreateEliminateDeadMembersPass());
Chris Forbescc5697f2019-01-30 11:54:08 -0800355 } else if (pass_name == "fold-spec-const-op-composite") {
356 RegisterPass(CreateFoldSpecConstantOpAndCompositePass());
357 } else if (pass_name == "loop-unswitch") {
358 RegisterPass(CreateLoopUnswitchPass());
359 } else if (pass_name == "scalar-replacement") {
360 if (pass_args.size() == 0) {
361 RegisterPass(CreateScalarReplacementPass());
362 } else {
363 int limit = -1;
364 if (pass_args.find_first_not_of("0123456789") == std::string::npos) {
365 limit = atoi(pass_args.c_str());
366 }
367
368 if (limit >= 0) {
369 RegisterPass(CreateScalarReplacementPass(limit));
370 } else {
371 Error(consumer(), nullptr, {},
372 "--scalar-replacement must have no arguments or a non-negative "
373 "integer argument");
374 return false;
375 }
376 }
377 } else if (pass_name == "strength-reduction") {
378 RegisterPass(CreateStrengthReductionPass());
379 } else if (pass_name == "unify-const") {
380 RegisterPass(CreateUnifyConstantPass());
381 } else if (pass_name == "flatten-decorations") {
382 RegisterPass(CreateFlattenDecorationPass());
383 } else if (pass_name == "compact-ids") {
384 RegisterPass(CreateCompactIdsPass());
385 } else if (pass_name == "cfg-cleanup") {
386 RegisterPass(CreateCFGCleanupPass());
387 } else if (pass_name == "local-redundancy-elimination") {
388 RegisterPass(CreateLocalRedundancyEliminationPass());
389 } else if (pass_name == "loop-invariant-code-motion") {
390 RegisterPass(CreateLoopInvariantCodeMotionPass());
391 } else if (pass_name == "reduce-load-size") {
392 RegisterPass(CreateReduceLoadSizePass());
393 } else if (pass_name == "redundancy-elimination") {
394 RegisterPass(CreateRedundancyEliminationPass());
395 } else if (pass_name == "private-to-local") {
396 RegisterPass(CreatePrivateToLocalPass());
397 } else if (pass_name == "remove-duplicates") {
398 RegisterPass(CreateRemoveDuplicatesPass());
399 } else if (pass_name == "workaround-1209") {
400 RegisterPass(CreateWorkaround1209Pass());
401 } else if (pass_name == "replace-invalid-opcode") {
402 RegisterPass(CreateReplaceInvalidOpcodePass());
403 } else if (pass_name == "inst-bindless-check") {
Ben Claytond0f684e2019-08-30 22:36:08 +0100404 RegisterPass(CreateInstBindlessCheckPass(7, 23, false, false, 2));
Chris Forbescc5697f2019-01-30 11:54:08 -0800405 RegisterPass(CreateSimplificationPass());
406 RegisterPass(CreateDeadBranchElimPass());
407 RegisterPass(CreateBlockMergePass());
408 RegisterPass(CreateAggressiveDCEPass());
Ben Claytond0f684e2019-08-30 22:36:08 +0100409 } else if (pass_name == "inst-desc-idx-check") {
410 RegisterPass(CreateInstBindlessCheckPass(7, 23, true, true, 2));
411 RegisterPass(CreateSimplificationPass());
412 RegisterPass(CreateDeadBranchElimPass());
413 RegisterPass(CreateBlockMergePass());
414 RegisterPass(CreateAggressiveDCEPass());
415 } else if (pass_name == "inst-buff-addr-check") {
416 RegisterPass(CreateInstBuffAddrCheckPass(7, 23, 2));
417 RegisterPass(CreateAggressiveDCEPass());
Chris Forbescc5697f2019-01-30 11:54:08 -0800418 } else if (pass_name == "simplify-instructions") {
419 RegisterPass(CreateSimplificationPass());
420 } else if (pass_name == "ssa-rewrite") {
421 RegisterPass(CreateSSARewritePass());
422 } else if (pass_name == "copy-propagate-arrays") {
423 RegisterPass(CreateCopyPropagateArraysPass());
424 } else if (pass_name == "loop-fission") {
425 int register_threshold_to_split =
426 (pass_args.size() > 0) ? atoi(pass_args.c_str()) : -1;
427 if (register_threshold_to_split > 0) {
428 RegisterPass(CreateLoopFissionPass(
429 static_cast<size_t>(register_threshold_to_split)));
430 } else {
431 Error(consumer(), nullptr, {},
432 "--loop-fission must have a positive integer argument");
433 return false;
434 }
435 } else if (pass_name == "loop-fusion") {
436 int max_registers_per_loop =
437 (pass_args.size() > 0) ? atoi(pass_args.c_str()) : -1;
438 if (max_registers_per_loop > 0) {
439 RegisterPass(
440 CreateLoopFusionPass(static_cast<size_t>(max_registers_per_loop)));
441 } else {
442 Error(consumer(), nullptr, {},
443 "--loop-fusion must have a positive integer argument");
444 return false;
445 }
446 } else if (pass_name == "loop-unroll") {
447 RegisterPass(CreateLoopUnrollPass(true));
448 } else if (pass_name == "upgrade-memory-model") {
449 RegisterPass(CreateUpgradeMemoryModelPass());
450 } else if (pass_name == "vector-dce") {
451 RegisterPass(CreateVectorDCEPass());
452 } else if (pass_name == "loop-unroll-partial") {
453 int factor = (pass_args.size() > 0) ? atoi(pass_args.c_str()) : 0;
454 if (factor > 0) {
455 RegisterPass(CreateLoopUnrollPass(false, factor));
456 } else {
457 Error(consumer(), nullptr, {},
458 "--loop-unroll-partial must have a positive integer argument");
459 return false;
460 }
461 } else if (pass_name == "loop-peeling") {
462 RegisterPass(CreateLoopPeelingPass());
463 } else if (pass_name == "loop-peeling-threshold") {
464 int factor = (pass_args.size() > 0) ? atoi(pass_args.c_str()) : 0;
465 if (factor > 0) {
466 opt::LoopPeelingPass::SetLoopPeelingThreshold(factor);
467 } else {
468 Error(consumer(), nullptr, {},
469 "--loop-peeling-threshold must have a positive integer argument");
470 return false;
471 }
472 } else if (pass_name == "ccp") {
473 RegisterPass(CreateCCPPass());
474 } else if (pass_name == "code-sink") {
475 RegisterPass(CreateCodeSinkingPass());
Ben Claytonb73b7602019-07-29 13:56:13 +0100476 } else if (pass_name == "fix-storage-class") {
477 RegisterPass(CreateFixStorageClassPass());
Chris Forbescc5697f2019-01-30 11:54:08 -0800478 } else if (pass_name == "O") {
479 RegisterPerformancePasses();
480 } else if (pass_name == "Os") {
481 RegisterSizePasses();
482 } else if (pass_name == "legalize-hlsl") {
483 RegisterLegalizationPasses();
Ben Claytonb73b7602019-07-29 13:56:13 +0100484 } else if (pass_name == "generate-webgpu-initializers") {
485 RegisterPass(CreateGenerateWebGPUInitializersPass());
486 } else if (pass_name == "legalize-vector-shuffle") {
487 RegisterPass(CreateLegalizeVectorShufflePass());
488 } else if (pass_name == "split-invalid-unreachable") {
489 RegisterPass(CreateLegalizeVectorShufflePass());
490 } else if (pass_name == "decompose-initialized-variables") {
491 RegisterPass(CreateDecomposeInitializedVariablesPass());
Ben Claytond0f684e2019-08-30 22:36:08 +0100492 } else if (pass_name == "graphics-robust-access") {
493 RegisterPass(CreateGraphicsRobustAccessPass());
494 } else if (pass_name == "wrap-opkill") {
495 RegisterPass(CreateWrapOpKillPass());
496 } else if (pass_name == "amd-ext-to-khr") {
497 RegisterPass(CreateAmdExtToKhrPass());
Chris Forbescc5697f2019-01-30 11:54:08 -0800498 } else {
499 Errorf(consumer(), nullptr, {},
500 "Unknown flag '--%s'. Use --help for a list of valid flags",
501 pass_name.c_str());
502 return false;
503 }
504
505 return true;
506}
507
508void Optimizer::SetTargetEnv(const spv_target_env env) {
509 impl_->target_env = env;
510}
511
512bool Optimizer::Run(const uint32_t* original_binary,
513 const size_t original_binary_size,
514 std::vector<uint32_t>* optimized_binary) const {
515 return Run(original_binary, original_binary_size, optimized_binary,
516 OptimizerOptions());
517}
518
519bool Optimizer::Run(const uint32_t* original_binary,
520 const size_t original_binary_size,
521 std::vector<uint32_t>* optimized_binary,
522 const ValidatorOptions& validator_options,
523 bool skip_validation) const {
524 OptimizerOptions opt_options;
525 opt_options.set_run_validator(!skip_validation);
526 opt_options.set_validator_options(validator_options);
527 return Run(original_binary, original_binary_size, optimized_binary,
528 opt_options);
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 spv_optimizer_options opt_options) const {
535 spvtools::SpirvTools tools(impl_->target_env);
536 tools.SetMessageConsumer(impl_->pass_manager.consumer());
537 if (opt_options->run_validator_ &&
538 !tools.Validate(original_binary, original_binary_size,
539 &opt_options->val_options_)) {
540 return false;
541 }
542
543 std::unique_ptr<opt::IRContext> context = BuildModule(
544 impl_->target_env, consumer(), original_binary, original_binary_size);
545 if (context == nullptr) return false;
546
547 context->set_max_id_bound(opt_options->max_id_bound_);
Ben Claytonb73b7602019-07-29 13:56:13 +0100548 context->set_preserve_bindings(opt_options->preserve_bindings_);
549 context->set_preserve_spec_constants(opt_options->preserve_spec_constants_);
Chris Forbescc5697f2019-01-30 11:54:08 -0800550
Ben Claytonb73b7602019-07-29 13:56:13 +0100551 impl_->pass_manager.SetValidatorOptions(&opt_options->val_options_);
552 impl_->pass_manager.SetTargetEnv(impl_->target_env);
Chris Forbescc5697f2019-01-30 11:54:08 -0800553 auto status = impl_->pass_manager.Run(context.get());
Ben Claytonb73b7602019-07-29 13:56:13 +0100554
Ben Claytond0f684e2019-08-30 22:36:08 +0100555 if (status == opt::Pass::Status::Failure) {
556 return false;
Ben Claytonb73b7602019-07-29 13:56:13 +0100557 }
558
Ben Claytond0f684e2019-08-30 22:36:08 +0100559 optimized_binary->clear();
560 context->module()->ToBinary(optimized_binary, /* skip_nop = */ true);
Chris Forbescc5697f2019-01-30 11:54:08 -0800561
Ben Claytond0f684e2019-08-30 22:36:08 +0100562#ifndef NDEBUG
563 if (status == opt::Pass::Status::SuccessWithoutChange) {
564 auto changed = optimized_binary->size() != original_binary_size ||
565 memcmp(optimized_binary->data(), original_binary,
566 original_binary_size) != 0;
567 assert(!changed &&
568 "Binary unexpectedly changed despite optimizer saying there was no "
569 "change");
570 }
571#endif // !NDEBUG
572
573 return true;
Chris Forbescc5697f2019-01-30 11:54:08 -0800574}
575
576Optimizer& Optimizer::SetPrintAll(std::ostream* out) {
577 impl_->pass_manager.SetPrintAll(out);
578 return *this;
579}
580
581Optimizer& Optimizer::SetTimeReport(std::ostream* out) {
582 impl_->pass_manager.SetTimeReport(out);
583 return *this;
584}
585
Ben Claytonb73b7602019-07-29 13:56:13 +0100586Optimizer& Optimizer::SetValidateAfterAll(bool validate) {
587 impl_->pass_manager.SetValidateAfterAll(validate);
588 return *this;
589}
590
Chris Forbescc5697f2019-01-30 11:54:08 -0800591Optimizer::PassToken CreateNullPass() {
592 return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::NullPass>());
593}
594
Ben Claytonb73b7602019-07-29 13:56:13 +0100595Optimizer::PassToken CreateStripAtomicCounterMemoryPass() {
596 return MakeUnique<Optimizer::PassToken::Impl>(
597 MakeUnique<opt::StripAtomicCounterMemoryPass>());
598}
599
Chris Forbescc5697f2019-01-30 11:54:08 -0800600Optimizer::PassToken CreateStripDebugInfoPass() {
601 return MakeUnique<Optimizer::PassToken::Impl>(
602 MakeUnique<opt::StripDebugInfoPass>());
603}
604
605Optimizer::PassToken CreateStripReflectInfoPass() {
606 return MakeUnique<Optimizer::PassToken::Impl>(
607 MakeUnique<opt::StripReflectInfoPass>());
608}
609
610Optimizer::PassToken CreateEliminateDeadFunctionsPass() {
611 return MakeUnique<Optimizer::PassToken::Impl>(
612 MakeUnique<opt::EliminateDeadFunctionsPass>());
613}
614
Ben Claytonb73b7602019-07-29 13:56:13 +0100615Optimizer::PassToken CreateEliminateDeadMembersPass() {
616 return MakeUnique<Optimizer::PassToken::Impl>(
617 MakeUnique<opt::EliminateDeadMembersPass>());
618}
619
Chris Forbescc5697f2019-01-30 11:54:08 -0800620Optimizer::PassToken CreateSetSpecConstantDefaultValuePass(
621 const std::unordered_map<uint32_t, std::string>& id_value_map) {
622 return MakeUnique<Optimizer::PassToken::Impl>(
623 MakeUnique<opt::SetSpecConstantDefaultValuePass>(id_value_map));
624}
625
626Optimizer::PassToken CreateSetSpecConstantDefaultValuePass(
627 const std::unordered_map<uint32_t, std::vector<uint32_t>>& id_value_map) {
628 return MakeUnique<Optimizer::PassToken::Impl>(
629 MakeUnique<opt::SetSpecConstantDefaultValuePass>(id_value_map));
630}
631
632Optimizer::PassToken CreateFlattenDecorationPass() {
633 return MakeUnique<Optimizer::PassToken::Impl>(
634 MakeUnique<opt::FlattenDecorationPass>());
635}
636
637Optimizer::PassToken CreateFreezeSpecConstantValuePass() {
638 return MakeUnique<Optimizer::PassToken::Impl>(
639 MakeUnique<opt::FreezeSpecConstantValuePass>());
640}
641
642Optimizer::PassToken CreateFoldSpecConstantOpAndCompositePass() {
643 return MakeUnique<Optimizer::PassToken::Impl>(
644 MakeUnique<opt::FoldSpecConstantOpAndCompositePass>());
645}
646
647Optimizer::PassToken CreateUnifyConstantPass() {
648 return MakeUnique<Optimizer::PassToken::Impl>(
649 MakeUnique<opt::UnifyConstantPass>());
650}
651
652Optimizer::PassToken CreateEliminateDeadConstantPass() {
653 return MakeUnique<Optimizer::PassToken::Impl>(
654 MakeUnique<opt::EliminateDeadConstantPass>());
655}
656
657Optimizer::PassToken CreateDeadVariableEliminationPass() {
658 return MakeUnique<Optimizer::PassToken::Impl>(
659 MakeUnique<opt::DeadVariableElimination>());
660}
661
662Optimizer::PassToken CreateStrengthReductionPass() {
663 return MakeUnique<Optimizer::PassToken::Impl>(
664 MakeUnique<opt::StrengthReductionPass>());
665}
666
667Optimizer::PassToken CreateBlockMergePass() {
668 return MakeUnique<Optimizer::PassToken::Impl>(
669 MakeUnique<opt::BlockMergePass>());
670}
671
672Optimizer::PassToken CreateInlineExhaustivePass() {
673 return MakeUnique<Optimizer::PassToken::Impl>(
674 MakeUnique<opt::InlineExhaustivePass>());
675}
676
677Optimizer::PassToken CreateInlineOpaquePass() {
678 return MakeUnique<Optimizer::PassToken::Impl>(
679 MakeUnique<opt::InlineOpaquePass>());
680}
681
682Optimizer::PassToken CreateLocalAccessChainConvertPass() {
683 return MakeUnique<Optimizer::PassToken::Impl>(
684 MakeUnique<opt::LocalAccessChainConvertPass>());
685}
686
687Optimizer::PassToken CreateLocalSingleBlockLoadStoreElimPass() {
688 return MakeUnique<Optimizer::PassToken::Impl>(
689 MakeUnique<opt::LocalSingleBlockLoadStoreElimPass>());
690}
691
692Optimizer::PassToken CreateLocalSingleStoreElimPass() {
693 return MakeUnique<Optimizer::PassToken::Impl>(
694 MakeUnique<opt::LocalSingleStoreElimPass>());
695}
696
697Optimizer::PassToken CreateInsertExtractElimPass() {
698 return MakeUnique<Optimizer::PassToken::Impl>(
699 MakeUnique<opt::SimplificationPass>());
700}
701
702Optimizer::PassToken CreateDeadInsertElimPass() {
703 return MakeUnique<Optimizer::PassToken::Impl>(
704 MakeUnique<opt::DeadInsertElimPass>());
705}
706
707Optimizer::PassToken CreateDeadBranchElimPass() {
708 return MakeUnique<Optimizer::PassToken::Impl>(
709 MakeUnique<opt::DeadBranchElimPass>());
710}
711
712Optimizer::PassToken CreateLocalMultiStoreElimPass() {
713 return MakeUnique<Optimizer::PassToken::Impl>(
714 MakeUnique<opt::LocalMultiStoreElimPass>());
715}
716
717Optimizer::PassToken CreateAggressiveDCEPass() {
718 return MakeUnique<Optimizer::PassToken::Impl>(
719 MakeUnique<opt::AggressiveDCEPass>());
720}
721
722Optimizer::PassToken CreatePropagateLineInfoPass() {
723 return MakeUnique<Optimizer::PassToken::Impl>(
724 MakeUnique<opt::ProcessLinesPass>(opt::kLinesPropagateLines));
725}
726
727Optimizer::PassToken CreateRedundantLineInfoElimPass() {
728 return MakeUnique<Optimizer::PassToken::Impl>(
729 MakeUnique<opt::ProcessLinesPass>(opt::kLinesEliminateDeadLines));
730}
731
Chris Forbescc5697f2019-01-30 11:54:08 -0800732Optimizer::PassToken CreateCompactIdsPass() {
733 return MakeUnique<Optimizer::PassToken::Impl>(
734 MakeUnique<opt::CompactIdsPass>());
735}
736
737Optimizer::PassToken CreateMergeReturnPass() {
738 return MakeUnique<Optimizer::PassToken::Impl>(
739 MakeUnique<opt::MergeReturnPass>());
740}
741
742std::vector<const char*> Optimizer::GetPassNames() const {
743 std::vector<const char*> v;
744 for (uint32_t i = 0; i < impl_->pass_manager.NumPasses(); i++) {
745 v.push_back(impl_->pass_manager.GetPass(i)->name());
746 }
747 return v;
748}
749
750Optimizer::PassToken CreateCFGCleanupPass() {
751 return MakeUnique<Optimizer::PassToken::Impl>(
752 MakeUnique<opt::CFGCleanupPass>());
753}
754
755Optimizer::PassToken CreateLocalRedundancyEliminationPass() {
756 return MakeUnique<Optimizer::PassToken::Impl>(
757 MakeUnique<opt::LocalRedundancyEliminationPass>());
758}
759
760Optimizer::PassToken CreateLoopFissionPass(size_t threshold) {
761 return MakeUnique<Optimizer::PassToken::Impl>(
762 MakeUnique<opt::LoopFissionPass>(threshold));
763}
764
765Optimizer::PassToken CreateLoopFusionPass(size_t max_registers_per_loop) {
766 return MakeUnique<Optimizer::PassToken::Impl>(
767 MakeUnique<opt::LoopFusionPass>(max_registers_per_loop));
768}
769
770Optimizer::PassToken CreateLoopInvariantCodeMotionPass() {
771 return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::LICMPass>());
772}
773
774Optimizer::PassToken CreateLoopPeelingPass() {
775 return MakeUnique<Optimizer::PassToken::Impl>(
776 MakeUnique<opt::LoopPeelingPass>());
777}
778
779Optimizer::PassToken CreateLoopUnswitchPass() {
780 return MakeUnique<Optimizer::PassToken::Impl>(
781 MakeUnique<opt::LoopUnswitchPass>());
782}
783
784Optimizer::PassToken CreateRedundancyEliminationPass() {
785 return MakeUnique<Optimizer::PassToken::Impl>(
786 MakeUnique<opt::RedundancyEliminationPass>());
787}
788
789Optimizer::PassToken CreateRemoveDuplicatesPass() {
790 return MakeUnique<Optimizer::PassToken::Impl>(
791 MakeUnique<opt::RemoveDuplicatesPass>());
792}
793
794Optimizer::PassToken CreateScalarReplacementPass(uint32_t size_limit) {
795 return MakeUnique<Optimizer::PassToken::Impl>(
796 MakeUnique<opt::ScalarReplacementPass>(size_limit));
797}
798
799Optimizer::PassToken CreatePrivateToLocalPass() {
800 return MakeUnique<Optimizer::PassToken::Impl>(
801 MakeUnique<opt::PrivateToLocalPass>());
802}
803
804Optimizer::PassToken CreateCCPPass() {
805 return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::CCPPass>());
806}
807
808Optimizer::PassToken CreateWorkaround1209Pass() {
809 return MakeUnique<Optimizer::PassToken::Impl>(
810 MakeUnique<opt::Workaround1209>());
811}
812
813Optimizer::PassToken CreateIfConversionPass() {
814 return MakeUnique<Optimizer::PassToken::Impl>(
815 MakeUnique<opt::IfConversion>());
816}
817
818Optimizer::PassToken CreateReplaceInvalidOpcodePass() {
819 return MakeUnique<Optimizer::PassToken::Impl>(
820 MakeUnique<opt::ReplaceInvalidOpcodePass>());
821}
822
823Optimizer::PassToken CreateSimplificationPass() {
824 return MakeUnique<Optimizer::PassToken::Impl>(
825 MakeUnique<opt::SimplificationPass>());
826}
827
828Optimizer::PassToken CreateLoopUnrollPass(bool fully_unroll, int factor) {
829 return MakeUnique<Optimizer::PassToken::Impl>(
830 MakeUnique<opt::LoopUnroller>(fully_unroll, factor));
831}
832
833Optimizer::PassToken CreateSSARewritePass() {
834 return MakeUnique<Optimizer::PassToken::Impl>(
835 MakeUnique<opt::SSARewritePass>());
836}
837
838Optimizer::PassToken CreateCopyPropagateArraysPass() {
839 return MakeUnique<Optimizer::PassToken::Impl>(
840 MakeUnique<opt::CopyPropagateArrays>());
841}
842
843Optimizer::PassToken CreateVectorDCEPass() {
844 return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::VectorDCE>());
845}
846
847Optimizer::PassToken CreateReduceLoadSizePass() {
848 return MakeUnique<Optimizer::PassToken::Impl>(
849 MakeUnique<opt::ReduceLoadSize>());
850}
851
852Optimizer::PassToken CreateCombineAccessChainsPass() {
853 return MakeUnique<Optimizer::PassToken::Impl>(
854 MakeUnique<opt::CombineAccessChains>());
855}
856
857Optimizer::PassToken CreateUpgradeMemoryModelPass() {
858 return MakeUnique<Optimizer::PassToken::Impl>(
859 MakeUnique<opt::UpgradeMemoryModel>());
860}
861
862Optimizer::PassToken CreateInstBindlessCheckPass(uint32_t desc_set,
Ben Claytonb73b7602019-07-29 13:56:13 +0100863 uint32_t shader_id,
864 bool input_length_enable,
865 bool input_init_enable,
866 uint32_t version) {
Chris Forbescc5697f2019-01-30 11:54:08 -0800867 return MakeUnique<Optimizer::PassToken::Impl>(
Ben Claytonb73b7602019-07-29 13:56:13 +0100868 MakeUnique<opt::InstBindlessCheckPass>(desc_set, shader_id,
869 input_length_enable,
870 input_init_enable, version));
Chris Forbescc5697f2019-01-30 11:54:08 -0800871}
872
Ben Claytond0f684e2019-08-30 22:36:08 +0100873Optimizer::PassToken CreateInstBuffAddrCheckPass(uint32_t desc_set,
874 uint32_t shader_id,
875 uint32_t version) {
876 return MakeUnique<Optimizer::PassToken::Impl>(
877 MakeUnique<opt::InstBuffAddrCheckPass>(desc_set, shader_id, version));
878}
879
Chris Forbescc5697f2019-01-30 11:54:08 -0800880Optimizer::PassToken CreateCodeSinkingPass() {
881 return MakeUnique<Optimizer::PassToken::Impl>(
882 MakeUnique<opt::CodeSinkingPass>());
883}
884
Ben Claytonb73b7602019-07-29 13:56:13 +0100885Optimizer::PassToken CreateGenerateWebGPUInitializersPass() {
886 return MakeUnique<Optimizer::PassToken::Impl>(
887 MakeUnique<opt::GenerateWebGPUInitializersPass>());
888}
889
890Optimizer::PassToken CreateFixStorageClassPass() {
891 return MakeUnique<Optimizer::PassToken::Impl>(
892 MakeUnique<opt::FixStorageClass>());
893}
894
895Optimizer::PassToken CreateLegalizeVectorShufflePass() {
896 return MakeUnique<Optimizer::PassToken::Impl>(
897 MakeUnique<opt::LegalizeVectorShufflePass>());
898}
899
900Optimizer::PassToken CreateDecomposeInitializedVariablesPass() {
901 return MakeUnique<Optimizer::PassToken::Impl>(
902 MakeUnique<opt::DecomposeInitializedVariablesPass>());
903}
904
905Optimizer::PassToken CreateSplitInvalidUnreachablePass() {
906 return MakeUnique<Optimizer::PassToken::Impl>(
907 MakeUnique<opt::SplitInvalidUnreachablePass>());
908}
909
Ben Claytond0f684e2019-08-30 22:36:08 +0100910Optimizer::PassToken CreateGraphicsRobustAccessPass() {
911 return MakeUnique<Optimizer::PassToken::Impl>(
912 MakeUnique<opt::GraphicsRobustAccessPass>());
913}
914
915Optimizer::PassToken CreateDescriptorScalarReplacementPass() {
916 return MakeUnique<Optimizer::PassToken::Impl>(
917 MakeUnique<opt::DescriptorScalarReplacement>());
918}
919
920Optimizer::PassToken CreateWrapOpKillPass() {
921 return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::WrapOpKill>());
922}
923
924Optimizer::PassToken CreateAmdExtToKhrPass() {
925 return MakeUnique<Optimizer::PassToken::Impl>(
926 MakeUnique<opt::AmdExtensionToKhrPass>());
927}
928
Chris Forbescc5697f2019-01-30 11:54:08 -0800929} // namespace spvtools