blob: 241aa75bef3da3c2914eb8245efc23b76206e40d [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
Chris Forbescc5697f2019-01-30 11:54:08 -080024#include "source/opt/build_module.h"
Ben Claytond0f684e2019-08-30 22:36:08 +010025#include "source/opt/graphics_robust_access_pass.h"
Chris Forbescc5697f2019-01-30 11:54:08 -080026#include "source/opt/log.h"
27#include "source/opt/pass_manager.h"
28#include "source/opt/passes.h"
Ben Claytond552f632019-11-18 11:18:41 +000029#include "source/spirv_optimizer_options.h"
Chris Forbescc5697f2019-01-30 11:54:08 -080030#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())
Ben Clayton0b54f132020-01-06 13:38:54 +0000201 .RegisterPass(CreateEliminateDeadFunctionsPass())
Chris Forbescc5697f2019-01-30 11:54:08 -0800202 .RegisterPass(CreatePrivateToLocalPass())
Ben Clayton0b54f132020-01-06 13:38:54 +0000203 .RegisterPass(CreateScalarReplacementPass(0))
Chris Forbescc5697f2019-01-30 11:54:08 -0800204 .RegisterPass(CreateLocalMultiStoreElimPass())
Chris Forbescc5697f2019-01-30 11:54:08 -0800205 .RegisterPass(CreateCCPPass())
Ben Clayton0b54f132020-01-06 13:38:54 +0000206 .RegisterPass(CreateLoopUnrollPass(true))
207 .RegisterPass(CreateDeadBranchElimPass())
208 .RegisterPass(CreateSimplificationPass())
209 .RegisterPass(CreateScalarReplacementPass(0))
210 .RegisterPass(CreateLocalSingleStoreElimPass())
211 .RegisterPass(CreateIfConversionPass())
212 .RegisterPass(CreateSimplificationPass())
Chris Forbescc5697f2019-01-30 11:54:08 -0800213 .RegisterPass(CreateAggressiveDCEPass())
214 .RegisterPass(CreateDeadBranchElimPass())
Chris Forbescc5697f2019-01-30 11:54:08 -0800215 .RegisterPass(CreateBlockMergePass())
Ben Clayton0b54f132020-01-06 13:38:54 +0000216 .RegisterPass(CreateLocalAccessChainConvertPass())
217 .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
218 .RegisterPass(CreateAggressiveDCEPass())
219 .RegisterPass(CreateCopyPropagateArraysPass())
220 .RegisterPass(CreateVectorDCEPass())
Chris Forbescc5697f2019-01-30 11:54:08 -0800221 .RegisterPass(CreateDeadInsertElimPass())
Ben Clayton0b54f132020-01-06 13:38:54 +0000222 .RegisterPass(CreateEliminateDeadMembersPass())
223 .RegisterPass(CreateLocalSingleStoreElimPass())
224 .RegisterPass(CreateBlockMergePass())
225 .RegisterPass(CreateLocalMultiStoreElimPass())
Chris Forbescc5697f2019-01-30 11:54:08 -0800226 .RegisterPass(CreateRedundancyEliminationPass())
Ben Clayton0b54f132020-01-06 13:38:54 +0000227 .RegisterPass(CreateSimplificationPass())
228 .RegisterPass(CreateAggressiveDCEPass())
229 .RegisterPass(CreateCFGCleanupPass());
Chris Forbescc5697f2019-01-30 11:54:08 -0800230}
231
Ben Claytonb73b7602019-07-29 13:56:13 +0100232Optimizer& Optimizer::RegisterVulkanToWebGPUPasses() {
Ben Clayton0b54f132020-01-06 13:38:54 +0000233 return RegisterPass(CreateStripAtomicCounterMemoryPass())
Ben Claytonb73b7602019-07-29 13:56:13 +0100234 .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());
Chris Forbescc5697f2019-01-30 11:54:08 -0800247}
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.
Ben Claytonb73b7602019-07-29 13:56:13 +0100291 if (pass_name == "strip-atomic-counter-memory") {
292 RegisterPass(CreateStripAtomicCounterMemoryPass());
293 } else if (pass_name == "strip-debug") {
Chris Forbescc5697f2019-01-30 11:54:08 -0800294 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());
Ben Claytond0f684e2019-08-30 22:36:08 +0100329 } else if (pass_name == "descriptor-scalar-replacement") {
330 RegisterPass(CreateDescriptorScalarReplacementPass());
Chris Forbescc5697f2019-01-30 11:54:08 -0800331 } 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());
Chris Forbescc5697f2019-01-30 11:54:08 -0800353 } 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());
Ben Claytonb73b7602019-07-29 13:56:13 +0100359 } else if (pass_name == "eliminate-dead-members") {
360 RegisterPass(CreateEliminateDeadMembersPass());
Chris Forbescc5697f2019-01-30 11:54:08 -0800361 } 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") {
Ben Claytond0f684e2019-08-30 22:36:08 +0100410 RegisterPass(CreateInstBindlessCheckPass(7, 23, false, false, 2));
Chris Forbescc5697f2019-01-30 11:54:08 -0800411 RegisterPass(CreateSimplificationPass());
412 RegisterPass(CreateDeadBranchElimPass());
413 RegisterPass(CreateBlockMergePass());
414 RegisterPass(CreateAggressiveDCEPass());
Ben Claytond0f684e2019-08-30 22:36:08 +0100415 } 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());
Ben Claytond552f632019-11-18 11:18:41 +0000424 } else if (pass_name == "convert-relaxed-to-half") {
425 RegisterPass(CreateConvertRelaxedToHalfPass());
426 } else if (pass_name == "relax-float-ops") {
427 RegisterPass(CreateRelaxFloatOpsPass());
Chris Forbescc5697f2019-01-30 11:54:08 -0800428 } else if (pass_name == "simplify-instructions") {
429 RegisterPass(CreateSimplificationPass());
430 } else if (pass_name == "ssa-rewrite") {
431 RegisterPass(CreateSSARewritePass());
432 } else if (pass_name == "copy-propagate-arrays") {
433 RegisterPass(CreateCopyPropagateArraysPass());
434 } else if (pass_name == "loop-fission") {
435 int register_threshold_to_split =
436 (pass_args.size() > 0) ? atoi(pass_args.c_str()) : -1;
437 if (register_threshold_to_split > 0) {
438 RegisterPass(CreateLoopFissionPass(
439 static_cast<size_t>(register_threshold_to_split)));
440 } else {
441 Error(consumer(), nullptr, {},
442 "--loop-fission must have a positive integer argument");
443 return false;
444 }
445 } else if (pass_name == "loop-fusion") {
446 int max_registers_per_loop =
447 (pass_args.size() > 0) ? atoi(pass_args.c_str()) : -1;
448 if (max_registers_per_loop > 0) {
449 RegisterPass(
450 CreateLoopFusionPass(static_cast<size_t>(max_registers_per_loop)));
451 } else {
452 Error(consumer(), nullptr, {},
453 "--loop-fusion must have a positive integer argument");
454 return false;
455 }
456 } else if (pass_name == "loop-unroll") {
457 RegisterPass(CreateLoopUnrollPass(true));
458 } else if (pass_name == "upgrade-memory-model") {
459 RegisterPass(CreateUpgradeMemoryModelPass());
460 } else if (pass_name == "vector-dce") {
461 RegisterPass(CreateVectorDCEPass());
462 } else if (pass_name == "loop-unroll-partial") {
463 int factor = (pass_args.size() > 0) ? atoi(pass_args.c_str()) : 0;
464 if (factor > 0) {
465 RegisterPass(CreateLoopUnrollPass(false, factor));
466 } else {
467 Error(consumer(), nullptr, {},
468 "--loop-unroll-partial must have a positive integer argument");
469 return false;
470 }
471 } else if (pass_name == "loop-peeling") {
472 RegisterPass(CreateLoopPeelingPass());
473 } else if (pass_name == "loop-peeling-threshold") {
474 int factor = (pass_args.size() > 0) ? atoi(pass_args.c_str()) : 0;
475 if (factor > 0) {
476 opt::LoopPeelingPass::SetLoopPeelingThreshold(factor);
477 } else {
478 Error(consumer(), nullptr, {},
479 "--loop-peeling-threshold must have a positive integer argument");
480 return false;
481 }
482 } else if (pass_name == "ccp") {
483 RegisterPass(CreateCCPPass());
484 } else if (pass_name == "code-sink") {
485 RegisterPass(CreateCodeSinkingPass());
Ben Claytonb73b7602019-07-29 13:56:13 +0100486 } else if (pass_name == "fix-storage-class") {
487 RegisterPass(CreateFixStorageClassPass());
Chris Forbescc5697f2019-01-30 11:54:08 -0800488 } else if (pass_name == "O") {
489 RegisterPerformancePasses();
490 } else if (pass_name == "Os") {
491 RegisterSizePasses();
492 } else if (pass_name == "legalize-hlsl") {
493 RegisterLegalizationPasses();
Ben Claytonb73b7602019-07-29 13:56:13 +0100494 } else if (pass_name == "generate-webgpu-initializers") {
495 RegisterPass(CreateGenerateWebGPUInitializersPass());
496 } else if (pass_name == "legalize-vector-shuffle") {
497 RegisterPass(CreateLegalizeVectorShufflePass());
498 } else if (pass_name == "split-invalid-unreachable") {
499 RegisterPass(CreateLegalizeVectorShufflePass());
500 } else if (pass_name == "decompose-initialized-variables") {
501 RegisterPass(CreateDecomposeInitializedVariablesPass());
Ben Claytond0f684e2019-08-30 22:36:08 +0100502 } else if (pass_name == "graphics-robust-access") {
503 RegisterPass(CreateGraphicsRobustAccessPass());
504 } else if (pass_name == "wrap-opkill") {
505 RegisterPass(CreateWrapOpKillPass());
506 } else if (pass_name == "amd-ext-to-khr") {
507 RegisterPass(CreateAmdExtToKhrPass());
Chris Forbescc5697f2019-01-30 11:54:08 -0800508 } else {
509 Errorf(consumer(), nullptr, {},
510 "Unknown flag '--%s'. Use --help for a list of valid flags",
511 pass_name.c_str());
512 return false;
513 }
514
515 return true;
516}
517
518void Optimizer::SetTargetEnv(const spv_target_env env) {
519 impl_->target_env = env;
520}
521
522bool Optimizer::Run(const uint32_t* original_binary,
523 const size_t original_binary_size,
524 std::vector<uint32_t>* optimized_binary) const {
525 return Run(original_binary, original_binary_size, optimized_binary,
526 OptimizerOptions());
527}
528
529bool Optimizer::Run(const uint32_t* original_binary,
530 const size_t original_binary_size,
531 std::vector<uint32_t>* optimized_binary,
532 const ValidatorOptions& validator_options,
533 bool skip_validation) const {
534 OptimizerOptions opt_options;
535 opt_options.set_run_validator(!skip_validation);
536 opt_options.set_validator_options(validator_options);
537 return Run(original_binary, original_binary_size, optimized_binary,
538 opt_options);
539}
540
541bool Optimizer::Run(const uint32_t* original_binary,
542 const size_t original_binary_size,
543 std::vector<uint32_t>* optimized_binary,
544 const spv_optimizer_options opt_options) const {
545 spvtools::SpirvTools tools(impl_->target_env);
546 tools.SetMessageConsumer(impl_->pass_manager.consumer());
547 if (opt_options->run_validator_ &&
548 !tools.Validate(original_binary, original_binary_size,
549 &opt_options->val_options_)) {
550 return false;
551 }
552
553 std::unique_ptr<opt::IRContext> context = BuildModule(
554 impl_->target_env, consumer(), original_binary, original_binary_size);
555 if (context == nullptr) return false;
556
557 context->set_max_id_bound(opt_options->max_id_bound_);
Ben Claytonb73b7602019-07-29 13:56:13 +0100558 context->set_preserve_bindings(opt_options->preserve_bindings_);
559 context->set_preserve_spec_constants(opt_options->preserve_spec_constants_);
Chris Forbescc5697f2019-01-30 11:54:08 -0800560
Ben Claytonb73b7602019-07-29 13:56:13 +0100561 impl_->pass_manager.SetValidatorOptions(&opt_options->val_options_);
562 impl_->pass_manager.SetTargetEnv(impl_->target_env);
Chris Forbescc5697f2019-01-30 11:54:08 -0800563 auto status = impl_->pass_manager.Run(context.get());
Ben Claytonb73b7602019-07-29 13:56:13 +0100564
Ben Claytond0f684e2019-08-30 22:36:08 +0100565 if (status == opt::Pass::Status::Failure) {
566 return false;
Ben Claytonb73b7602019-07-29 13:56:13 +0100567 }
568
Ben Claytond0f684e2019-08-30 22:36:08 +0100569#ifndef NDEBUG
570 if (status == opt::Pass::Status::SuccessWithoutChange) {
Ben Claytond552f632019-11-18 11:18:41 +0000571 std::vector<uint32_t> optimized_binary_with_nop;
572 context->module()->ToBinary(&optimized_binary_with_nop,
573 /* skip_nop = */ false);
574 assert(optimized_binary_with_nop.size() == original_binary_size &&
575 "Binary size unexpectedly changed despite the optimizer saying "
576 "there was no change");
577 assert(memcmp(optimized_binary_with_nop.data(), original_binary,
578 original_binary_size) == 0 &&
579 "Binary content unexpectedly changed despite the optimizer saying "
580 "there was no change");
Ben Claytond0f684e2019-08-30 22:36:08 +0100581 }
582#endif // !NDEBUG
583
Ben Claytond552f632019-11-18 11:18:41 +0000584 // Note that |original_binary| and |optimized_binary| may share the same
585 // buffer and the below will invalidate |original_binary|.
586 optimized_binary->clear();
587 context->module()->ToBinary(optimized_binary, /* skip_nop = */ true);
588
Ben Claytond0f684e2019-08-30 22:36:08 +0100589 return true;
Chris Forbescc5697f2019-01-30 11:54:08 -0800590}
591
592Optimizer& Optimizer::SetPrintAll(std::ostream* out) {
593 impl_->pass_manager.SetPrintAll(out);
594 return *this;
595}
596
597Optimizer& Optimizer::SetTimeReport(std::ostream* out) {
598 impl_->pass_manager.SetTimeReport(out);
599 return *this;
600}
601
Ben Claytonb73b7602019-07-29 13:56:13 +0100602Optimizer& Optimizer::SetValidateAfterAll(bool validate) {
603 impl_->pass_manager.SetValidateAfterAll(validate);
604 return *this;
605}
606
Chris Forbescc5697f2019-01-30 11:54:08 -0800607Optimizer::PassToken CreateNullPass() {
608 return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::NullPass>());
609}
610
Ben Claytonb73b7602019-07-29 13:56:13 +0100611Optimizer::PassToken CreateStripAtomicCounterMemoryPass() {
612 return MakeUnique<Optimizer::PassToken::Impl>(
613 MakeUnique<opt::StripAtomicCounterMemoryPass>());
614}
615
Chris Forbescc5697f2019-01-30 11:54:08 -0800616Optimizer::PassToken CreateStripDebugInfoPass() {
617 return MakeUnique<Optimizer::PassToken::Impl>(
618 MakeUnique<opt::StripDebugInfoPass>());
619}
620
621Optimizer::PassToken CreateStripReflectInfoPass() {
622 return MakeUnique<Optimizer::PassToken::Impl>(
623 MakeUnique<opt::StripReflectInfoPass>());
624}
625
626Optimizer::PassToken CreateEliminateDeadFunctionsPass() {
627 return MakeUnique<Optimizer::PassToken::Impl>(
628 MakeUnique<opt::EliminateDeadFunctionsPass>());
629}
630
Ben Claytonb73b7602019-07-29 13:56:13 +0100631Optimizer::PassToken CreateEliminateDeadMembersPass() {
632 return MakeUnique<Optimizer::PassToken::Impl>(
633 MakeUnique<opt::EliminateDeadMembersPass>());
634}
635
Chris Forbescc5697f2019-01-30 11:54:08 -0800636Optimizer::PassToken CreateSetSpecConstantDefaultValuePass(
637 const std::unordered_map<uint32_t, std::string>& id_value_map) {
638 return MakeUnique<Optimizer::PassToken::Impl>(
639 MakeUnique<opt::SetSpecConstantDefaultValuePass>(id_value_map));
640}
641
642Optimizer::PassToken CreateSetSpecConstantDefaultValuePass(
643 const std::unordered_map<uint32_t, std::vector<uint32_t>>& id_value_map) {
644 return MakeUnique<Optimizer::PassToken::Impl>(
645 MakeUnique<opt::SetSpecConstantDefaultValuePass>(id_value_map));
646}
647
648Optimizer::PassToken CreateFlattenDecorationPass() {
649 return MakeUnique<Optimizer::PassToken::Impl>(
650 MakeUnique<opt::FlattenDecorationPass>());
651}
652
653Optimizer::PassToken CreateFreezeSpecConstantValuePass() {
654 return MakeUnique<Optimizer::PassToken::Impl>(
655 MakeUnique<opt::FreezeSpecConstantValuePass>());
656}
657
658Optimizer::PassToken CreateFoldSpecConstantOpAndCompositePass() {
659 return MakeUnique<Optimizer::PassToken::Impl>(
660 MakeUnique<opt::FoldSpecConstantOpAndCompositePass>());
661}
662
663Optimizer::PassToken CreateUnifyConstantPass() {
664 return MakeUnique<Optimizer::PassToken::Impl>(
665 MakeUnique<opt::UnifyConstantPass>());
666}
667
668Optimizer::PassToken CreateEliminateDeadConstantPass() {
669 return MakeUnique<Optimizer::PassToken::Impl>(
670 MakeUnique<opt::EliminateDeadConstantPass>());
671}
672
673Optimizer::PassToken CreateDeadVariableEliminationPass() {
674 return MakeUnique<Optimizer::PassToken::Impl>(
675 MakeUnique<opt::DeadVariableElimination>());
676}
677
678Optimizer::PassToken CreateStrengthReductionPass() {
679 return MakeUnique<Optimizer::PassToken::Impl>(
680 MakeUnique<opt::StrengthReductionPass>());
681}
682
683Optimizer::PassToken CreateBlockMergePass() {
684 return MakeUnique<Optimizer::PassToken::Impl>(
685 MakeUnique<opt::BlockMergePass>());
686}
687
688Optimizer::PassToken CreateInlineExhaustivePass() {
689 return MakeUnique<Optimizer::PassToken::Impl>(
690 MakeUnique<opt::InlineExhaustivePass>());
691}
692
693Optimizer::PassToken CreateInlineOpaquePass() {
694 return MakeUnique<Optimizer::PassToken::Impl>(
695 MakeUnique<opt::InlineOpaquePass>());
696}
697
698Optimizer::PassToken CreateLocalAccessChainConvertPass() {
699 return MakeUnique<Optimizer::PassToken::Impl>(
700 MakeUnique<opt::LocalAccessChainConvertPass>());
701}
702
703Optimizer::PassToken CreateLocalSingleBlockLoadStoreElimPass() {
704 return MakeUnique<Optimizer::PassToken::Impl>(
705 MakeUnique<opt::LocalSingleBlockLoadStoreElimPass>());
706}
707
708Optimizer::PassToken CreateLocalSingleStoreElimPass() {
709 return MakeUnique<Optimizer::PassToken::Impl>(
710 MakeUnique<opt::LocalSingleStoreElimPass>());
711}
712
713Optimizer::PassToken CreateInsertExtractElimPass() {
714 return MakeUnique<Optimizer::PassToken::Impl>(
715 MakeUnique<opt::SimplificationPass>());
716}
717
718Optimizer::PassToken CreateDeadInsertElimPass() {
719 return MakeUnique<Optimizer::PassToken::Impl>(
720 MakeUnique<opt::DeadInsertElimPass>());
721}
722
723Optimizer::PassToken CreateDeadBranchElimPass() {
724 return MakeUnique<Optimizer::PassToken::Impl>(
725 MakeUnique<opt::DeadBranchElimPass>());
726}
727
728Optimizer::PassToken CreateLocalMultiStoreElimPass() {
729 return MakeUnique<Optimizer::PassToken::Impl>(
Ben Claytond552f632019-11-18 11:18:41 +0000730 MakeUnique<opt::SSARewritePass>());
Chris Forbescc5697f2019-01-30 11:54:08 -0800731}
732
733Optimizer::PassToken CreateAggressiveDCEPass() {
734 return MakeUnique<Optimizer::PassToken::Impl>(
735 MakeUnique<opt::AggressiveDCEPass>());
736}
737
738Optimizer::PassToken CreatePropagateLineInfoPass() {
739 return MakeUnique<Optimizer::PassToken::Impl>(
740 MakeUnique<opt::ProcessLinesPass>(opt::kLinesPropagateLines));
741}
742
743Optimizer::PassToken CreateRedundantLineInfoElimPass() {
744 return MakeUnique<Optimizer::PassToken::Impl>(
745 MakeUnique<opt::ProcessLinesPass>(opt::kLinesEliminateDeadLines));
746}
747
Chris Forbescc5697f2019-01-30 11:54:08 -0800748Optimizer::PassToken CreateCompactIdsPass() {
749 return MakeUnique<Optimizer::PassToken::Impl>(
750 MakeUnique<opt::CompactIdsPass>());
751}
752
753Optimizer::PassToken CreateMergeReturnPass() {
754 return MakeUnique<Optimizer::PassToken::Impl>(
755 MakeUnique<opt::MergeReturnPass>());
756}
757
758std::vector<const char*> Optimizer::GetPassNames() const {
759 std::vector<const char*> v;
760 for (uint32_t i = 0; i < impl_->pass_manager.NumPasses(); i++) {
761 v.push_back(impl_->pass_manager.GetPass(i)->name());
762 }
763 return v;
764}
765
766Optimizer::PassToken CreateCFGCleanupPass() {
767 return MakeUnique<Optimizer::PassToken::Impl>(
768 MakeUnique<opt::CFGCleanupPass>());
769}
770
771Optimizer::PassToken CreateLocalRedundancyEliminationPass() {
772 return MakeUnique<Optimizer::PassToken::Impl>(
773 MakeUnique<opt::LocalRedundancyEliminationPass>());
774}
775
776Optimizer::PassToken CreateLoopFissionPass(size_t threshold) {
777 return MakeUnique<Optimizer::PassToken::Impl>(
778 MakeUnique<opt::LoopFissionPass>(threshold));
779}
780
781Optimizer::PassToken CreateLoopFusionPass(size_t max_registers_per_loop) {
782 return MakeUnique<Optimizer::PassToken::Impl>(
783 MakeUnique<opt::LoopFusionPass>(max_registers_per_loop));
784}
785
786Optimizer::PassToken CreateLoopInvariantCodeMotionPass() {
787 return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::LICMPass>());
788}
789
790Optimizer::PassToken CreateLoopPeelingPass() {
791 return MakeUnique<Optimizer::PassToken::Impl>(
792 MakeUnique<opt::LoopPeelingPass>());
793}
794
795Optimizer::PassToken CreateLoopUnswitchPass() {
796 return MakeUnique<Optimizer::PassToken::Impl>(
797 MakeUnique<opt::LoopUnswitchPass>());
798}
799
800Optimizer::PassToken CreateRedundancyEliminationPass() {
801 return MakeUnique<Optimizer::PassToken::Impl>(
802 MakeUnique<opt::RedundancyEliminationPass>());
803}
804
805Optimizer::PassToken CreateRemoveDuplicatesPass() {
806 return MakeUnique<Optimizer::PassToken::Impl>(
807 MakeUnique<opt::RemoveDuplicatesPass>());
808}
809
810Optimizer::PassToken CreateScalarReplacementPass(uint32_t size_limit) {
811 return MakeUnique<Optimizer::PassToken::Impl>(
812 MakeUnique<opt::ScalarReplacementPass>(size_limit));
813}
814
815Optimizer::PassToken CreatePrivateToLocalPass() {
816 return MakeUnique<Optimizer::PassToken::Impl>(
817 MakeUnique<opt::PrivateToLocalPass>());
818}
819
820Optimizer::PassToken CreateCCPPass() {
821 return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::CCPPass>());
822}
823
824Optimizer::PassToken CreateWorkaround1209Pass() {
825 return MakeUnique<Optimizer::PassToken::Impl>(
826 MakeUnique<opt::Workaround1209>());
827}
828
829Optimizer::PassToken CreateIfConversionPass() {
830 return MakeUnique<Optimizer::PassToken::Impl>(
831 MakeUnique<opt::IfConversion>());
832}
833
834Optimizer::PassToken CreateReplaceInvalidOpcodePass() {
835 return MakeUnique<Optimizer::PassToken::Impl>(
836 MakeUnique<opt::ReplaceInvalidOpcodePass>());
837}
838
839Optimizer::PassToken CreateSimplificationPass() {
840 return MakeUnique<Optimizer::PassToken::Impl>(
841 MakeUnique<opt::SimplificationPass>());
842}
843
844Optimizer::PassToken CreateLoopUnrollPass(bool fully_unroll, int factor) {
845 return MakeUnique<Optimizer::PassToken::Impl>(
846 MakeUnique<opt::LoopUnroller>(fully_unroll, factor));
847}
848
849Optimizer::PassToken CreateSSARewritePass() {
850 return MakeUnique<Optimizer::PassToken::Impl>(
851 MakeUnique<opt::SSARewritePass>());
852}
853
854Optimizer::PassToken CreateCopyPropagateArraysPass() {
855 return MakeUnique<Optimizer::PassToken::Impl>(
856 MakeUnique<opt::CopyPropagateArrays>());
857}
858
859Optimizer::PassToken CreateVectorDCEPass() {
860 return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::VectorDCE>());
861}
862
863Optimizer::PassToken CreateReduceLoadSizePass() {
864 return MakeUnique<Optimizer::PassToken::Impl>(
865 MakeUnique<opt::ReduceLoadSize>());
866}
867
868Optimizer::PassToken CreateCombineAccessChainsPass() {
869 return MakeUnique<Optimizer::PassToken::Impl>(
870 MakeUnique<opt::CombineAccessChains>());
871}
872
873Optimizer::PassToken CreateUpgradeMemoryModelPass() {
874 return MakeUnique<Optimizer::PassToken::Impl>(
875 MakeUnique<opt::UpgradeMemoryModel>());
876}
877
878Optimizer::PassToken CreateInstBindlessCheckPass(uint32_t desc_set,
Ben Claytonb73b7602019-07-29 13:56:13 +0100879 uint32_t shader_id,
880 bool input_length_enable,
881 bool input_init_enable,
882 uint32_t version) {
Chris Forbescc5697f2019-01-30 11:54:08 -0800883 return MakeUnique<Optimizer::PassToken::Impl>(
Ben Claytonb73b7602019-07-29 13:56:13 +0100884 MakeUnique<opt::InstBindlessCheckPass>(desc_set, shader_id,
885 input_length_enable,
886 input_init_enable, version));
Chris Forbescc5697f2019-01-30 11:54:08 -0800887}
888
Ben Claytond0f684e2019-08-30 22:36:08 +0100889Optimizer::PassToken CreateInstBuffAddrCheckPass(uint32_t desc_set,
890 uint32_t shader_id,
891 uint32_t version) {
892 return MakeUnique<Optimizer::PassToken::Impl>(
893 MakeUnique<opt::InstBuffAddrCheckPass>(desc_set, shader_id, version));
894}
895
Ben Claytond552f632019-11-18 11:18:41 +0000896Optimizer::PassToken CreateConvertRelaxedToHalfPass() {
897 return MakeUnique<Optimizer::PassToken::Impl>(
898 MakeUnique<opt::ConvertToHalfPass>());
899}
900
901Optimizer::PassToken CreateRelaxFloatOpsPass() {
902 return MakeUnique<Optimizer::PassToken::Impl>(
903 MakeUnique<opt::RelaxFloatOpsPass>());
904}
905
Chris Forbescc5697f2019-01-30 11:54:08 -0800906Optimizer::PassToken CreateCodeSinkingPass() {
907 return MakeUnique<Optimizer::PassToken::Impl>(
908 MakeUnique<opt::CodeSinkingPass>());
909}
910
Ben Claytonb73b7602019-07-29 13:56:13 +0100911Optimizer::PassToken CreateGenerateWebGPUInitializersPass() {
912 return MakeUnique<Optimizer::PassToken::Impl>(
913 MakeUnique<opt::GenerateWebGPUInitializersPass>());
914}
915
916Optimizer::PassToken CreateFixStorageClassPass() {
917 return MakeUnique<Optimizer::PassToken::Impl>(
918 MakeUnique<opt::FixStorageClass>());
919}
920
921Optimizer::PassToken CreateLegalizeVectorShufflePass() {
922 return MakeUnique<Optimizer::PassToken::Impl>(
923 MakeUnique<opt::LegalizeVectorShufflePass>());
924}
925
926Optimizer::PassToken CreateDecomposeInitializedVariablesPass() {
927 return MakeUnique<Optimizer::PassToken::Impl>(
928 MakeUnique<opt::DecomposeInitializedVariablesPass>());
929}
930
931Optimizer::PassToken CreateSplitInvalidUnreachablePass() {
932 return MakeUnique<Optimizer::PassToken::Impl>(
933 MakeUnique<opt::SplitInvalidUnreachablePass>());
934}
935
Ben Claytond0f684e2019-08-30 22:36:08 +0100936Optimizer::PassToken CreateGraphicsRobustAccessPass() {
937 return MakeUnique<Optimizer::PassToken::Impl>(
938 MakeUnique<opt::GraphicsRobustAccessPass>());
939}
940
941Optimizer::PassToken CreateDescriptorScalarReplacementPass() {
942 return MakeUnique<Optimizer::PassToken::Impl>(
943 MakeUnique<opt::DescriptorScalarReplacement>());
944}
945
946Optimizer::PassToken CreateWrapOpKillPass() {
947 return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::WrapOpKill>());
948}
949
950Optimizer::PassToken CreateAmdExtToKhrPass() {
951 return MakeUnique<Optimizer::PassToken::Impl>(
952 MakeUnique<opt::AmdExtensionToKhrPass>());
953}
954
Chris Forbescc5697f2019-01-30 11:54:08 -0800955} // namespace spvtools