blob: ece1abec3ecdd4774073afc65bda5b34c0a9e366 [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())
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());
Ben Claytond552f632019-11-18 11:18:41 +0000418 } else if (pass_name == "convert-relaxed-to-half") {
419 RegisterPass(CreateConvertRelaxedToHalfPass());
420 } else if (pass_name == "relax-float-ops") {
421 RegisterPass(CreateRelaxFloatOpsPass());
Chris Forbescc5697f2019-01-30 11:54:08 -0800422 } else if (pass_name == "simplify-instructions") {
423 RegisterPass(CreateSimplificationPass());
424 } else if (pass_name == "ssa-rewrite") {
425 RegisterPass(CreateSSARewritePass());
426 } else if (pass_name == "copy-propagate-arrays") {
427 RegisterPass(CreateCopyPropagateArraysPass());
428 } else if (pass_name == "loop-fission") {
429 int register_threshold_to_split =
430 (pass_args.size() > 0) ? atoi(pass_args.c_str()) : -1;
431 if (register_threshold_to_split > 0) {
432 RegisterPass(CreateLoopFissionPass(
433 static_cast<size_t>(register_threshold_to_split)));
434 } else {
435 Error(consumer(), nullptr, {},
436 "--loop-fission must have a positive integer argument");
437 return false;
438 }
439 } else if (pass_name == "loop-fusion") {
440 int max_registers_per_loop =
441 (pass_args.size() > 0) ? atoi(pass_args.c_str()) : -1;
442 if (max_registers_per_loop > 0) {
443 RegisterPass(
444 CreateLoopFusionPass(static_cast<size_t>(max_registers_per_loop)));
445 } else {
446 Error(consumer(), nullptr, {},
447 "--loop-fusion must have a positive integer argument");
448 return false;
449 }
450 } else if (pass_name == "loop-unroll") {
451 RegisterPass(CreateLoopUnrollPass(true));
452 } else if (pass_name == "upgrade-memory-model") {
453 RegisterPass(CreateUpgradeMemoryModelPass());
454 } else if (pass_name == "vector-dce") {
455 RegisterPass(CreateVectorDCEPass());
456 } else if (pass_name == "loop-unroll-partial") {
457 int factor = (pass_args.size() > 0) ? atoi(pass_args.c_str()) : 0;
458 if (factor > 0) {
459 RegisterPass(CreateLoopUnrollPass(false, factor));
460 } else {
461 Error(consumer(), nullptr, {},
462 "--loop-unroll-partial must have a positive integer argument");
463 return false;
464 }
465 } else if (pass_name == "loop-peeling") {
466 RegisterPass(CreateLoopPeelingPass());
467 } else if (pass_name == "loop-peeling-threshold") {
468 int factor = (pass_args.size() > 0) ? atoi(pass_args.c_str()) : 0;
469 if (factor > 0) {
470 opt::LoopPeelingPass::SetLoopPeelingThreshold(factor);
471 } else {
472 Error(consumer(), nullptr, {},
473 "--loop-peeling-threshold must have a positive integer argument");
474 return false;
475 }
476 } else if (pass_name == "ccp") {
477 RegisterPass(CreateCCPPass());
478 } else if (pass_name == "code-sink") {
479 RegisterPass(CreateCodeSinkingPass());
Ben Claytonb73b7602019-07-29 13:56:13 +0100480 } else if (pass_name == "fix-storage-class") {
481 RegisterPass(CreateFixStorageClassPass());
Chris Forbescc5697f2019-01-30 11:54:08 -0800482 } else if (pass_name == "O") {
483 RegisterPerformancePasses();
484 } else if (pass_name == "Os") {
485 RegisterSizePasses();
486 } else if (pass_name == "legalize-hlsl") {
487 RegisterLegalizationPasses();
Ben Claytonb73b7602019-07-29 13:56:13 +0100488 } else if (pass_name == "generate-webgpu-initializers") {
489 RegisterPass(CreateGenerateWebGPUInitializersPass());
490 } else if (pass_name == "legalize-vector-shuffle") {
491 RegisterPass(CreateLegalizeVectorShufflePass());
492 } else if (pass_name == "split-invalid-unreachable") {
493 RegisterPass(CreateLegalizeVectorShufflePass());
494 } else if (pass_name == "decompose-initialized-variables") {
495 RegisterPass(CreateDecomposeInitializedVariablesPass());
Ben Claytond0f684e2019-08-30 22:36:08 +0100496 } else if (pass_name == "graphics-robust-access") {
497 RegisterPass(CreateGraphicsRobustAccessPass());
498 } else if (pass_name == "wrap-opkill") {
499 RegisterPass(CreateWrapOpKillPass());
500 } else if (pass_name == "amd-ext-to-khr") {
501 RegisterPass(CreateAmdExtToKhrPass());
Chris Forbescc5697f2019-01-30 11:54:08 -0800502 } else {
503 Errorf(consumer(), nullptr, {},
504 "Unknown flag '--%s'. Use --help for a list of valid flags",
505 pass_name.c_str());
506 return false;
507 }
508
509 return true;
510}
511
512void Optimizer::SetTargetEnv(const spv_target_env env) {
513 impl_->target_env = env;
514}
515
516bool Optimizer::Run(const uint32_t* original_binary,
517 const size_t original_binary_size,
518 std::vector<uint32_t>* optimized_binary) const {
519 return Run(original_binary, original_binary_size, optimized_binary,
520 OptimizerOptions());
521}
522
523bool Optimizer::Run(const uint32_t* original_binary,
524 const size_t original_binary_size,
525 std::vector<uint32_t>* optimized_binary,
526 const ValidatorOptions& validator_options,
527 bool skip_validation) const {
528 OptimizerOptions opt_options;
529 opt_options.set_run_validator(!skip_validation);
530 opt_options.set_validator_options(validator_options);
531 return Run(original_binary, original_binary_size, optimized_binary,
532 opt_options);
533}
534
535bool Optimizer::Run(const uint32_t* original_binary,
536 const size_t original_binary_size,
537 std::vector<uint32_t>* optimized_binary,
538 const spv_optimizer_options opt_options) const {
539 spvtools::SpirvTools tools(impl_->target_env);
540 tools.SetMessageConsumer(impl_->pass_manager.consumer());
541 if (opt_options->run_validator_ &&
542 !tools.Validate(original_binary, original_binary_size,
543 &opt_options->val_options_)) {
544 return false;
545 }
546
547 std::unique_ptr<opt::IRContext> context = BuildModule(
548 impl_->target_env, consumer(), original_binary, original_binary_size);
549 if (context == nullptr) return false;
550
551 context->set_max_id_bound(opt_options->max_id_bound_);
Ben Claytonb73b7602019-07-29 13:56:13 +0100552 context->set_preserve_bindings(opt_options->preserve_bindings_);
553 context->set_preserve_spec_constants(opt_options->preserve_spec_constants_);
Chris Forbescc5697f2019-01-30 11:54:08 -0800554
Ben Claytonb73b7602019-07-29 13:56:13 +0100555 impl_->pass_manager.SetValidatorOptions(&opt_options->val_options_);
556 impl_->pass_manager.SetTargetEnv(impl_->target_env);
Chris Forbescc5697f2019-01-30 11:54:08 -0800557 auto status = impl_->pass_manager.Run(context.get());
Ben Claytonb73b7602019-07-29 13:56:13 +0100558
Ben Claytond0f684e2019-08-30 22:36:08 +0100559 if (status == opt::Pass::Status::Failure) {
560 return false;
Ben Claytonb73b7602019-07-29 13:56:13 +0100561 }
562
Ben Claytond0f684e2019-08-30 22:36:08 +0100563#ifndef NDEBUG
564 if (status == opt::Pass::Status::SuccessWithoutChange) {
Ben Claytond552f632019-11-18 11:18:41 +0000565 std::vector<uint32_t> optimized_binary_with_nop;
566 context->module()->ToBinary(&optimized_binary_with_nop,
567 /* skip_nop = */ false);
568 assert(optimized_binary_with_nop.size() == original_binary_size &&
569 "Binary size unexpectedly changed despite the optimizer saying "
570 "there was no change");
571 assert(memcmp(optimized_binary_with_nop.data(), original_binary,
572 original_binary_size) == 0 &&
573 "Binary content unexpectedly changed despite the optimizer saying "
574 "there was no change");
Ben Claytond0f684e2019-08-30 22:36:08 +0100575 }
576#endif // !NDEBUG
577
Ben Claytond552f632019-11-18 11:18:41 +0000578 // Note that |original_binary| and |optimized_binary| may share the same
579 // buffer and the below will invalidate |original_binary|.
580 optimized_binary->clear();
581 context->module()->ToBinary(optimized_binary, /* skip_nop = */ true);
582
Ben Claytond0f684e2019-08-30 22:36:08 +0100583 return true;
Chris Forbescc5697f2019-01-30 11:54:08 -0800584}
585
586Optimizer& Optimizer::SetPrintAll(std::ostream* out) {
587 impl_->pass_manager.SetPrintAll(out);
588 return *this;
589}
590
591Optimizer& Optimizer::SetTimeReport(std::ostream* out) {
592 impl_->pass_manager.SetTimeReport(out);
593 return *this;
594}
595
Ben Claytonb73b7602019-07-29 13:56:13 +0100596Optimizer& Optimizer::SetValidateAfterAll(bool validate) {
597 impl_->pass_manager.SetValidateAfterAll(validate);
598 return *this;
599}
600
Chris Forbescc5697f2019-01-30 11:54:08 -0800601Optimizer::PassToken CreateNullPass() {
602 return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::NullPass>());
603}
604
Ben Claytonb73b7602019-07-29 13:56:13 +0100605Optimizer::PassToken CreateStripAtomicCounterMemoryPass() {
606 return MakeUnique<Optimizer::PassToken::Impl>(
607 MakeUnique<opt::StripAtomicCounterMemoryPass>());
608}
609
Chris Forbescc5697f2019-01-30 11:54:08 -0800610Optimizer::PassToken CreateStripDebugInfoPass() {
611 return MakeUnique<Optimizer::PassToken::Impl>(
612 MakeUnique<opt::StripDebugInfoPass>());
613}
614
615Optimizer::PassToken CreateStripReflectInfoPass() {
616 return MakeUnique<Optimizer::PassToken::Impl>(
617 MakeUnique<opt::StripReflectInfoPass>());
618}
619
620Optimizer::PassToken CreateEliminateDeadFunctionsPass() {
621 return MakeUnique<Optimizer::PassToken::Impl>(
622 MakeUnique<opt::EliminateDeadFunctionsPass>());
623}
624
Ben Claytonb73b7602019-07-29 13:56:13 +0100625Optimizer::PassToken CreateEliminateDeadMembersPass() {
626 return MakeUnique<Optimizer::PassToken::Impl>(
627 MakeUnique<opt::EliminateDeadMembersPass>());
628}
629
Chris Forbescc5697f2019-01-30 11:54:08 -0800630Optimizer::PassToken CreateSetSpecConstantDefaultValuePass(
631 const std::unordered_map<uint32_t, std::string>& id_value_map) {
632 return MakeUnique<Optimizer::PassToken::Impl>(
633 MakeUnique<opt::SetSpecConstantDefaultValuePass>(id_value_map));
634}
635
636Optimizer::PassToken CreateSetSpecConstantDefaultValuePass(
637 const std::unordered_map<uint32_t, std::vector<uint32_t>>& id_value_map) {
638 return MakeUnique<Optimizer::PassToken::Impl>(
639 MakeUnique<opt::SetSpecConstantDefaultValuePass>(id_value_map));
640}
641
642Optimizer::PassToken CreateFlattenDecorationPass() {
643 return MakeUnique<Optimizer::PassToken::Impl>(
644 MakeUnique<opt::FlattenDecorationPass>());
645}
646
647Optimizer::PassToken CreateFreezeSpecConstantValuePass() {
648 return MakeUnique<Optimizer::PassToken::Impl>(
649 MakeUnique<opt::FreezeSpecConstantValuePass>());
650}
651
652Optimizer::PassToken CreateFoldSpecConstantOpAndCompositePass() {
653 return MakeUnique<Optimizer::PassToken::Impl>(
654 MakeUnique<opt::FoldSpecConstantOpAndCompositePass>());
655}
656
657Optimizer::PassToken CreateUnifyConstantPass() {
658 return MakeUnique<Optimizer::PassToken::Impl>(
659 MakeUnique<opt::UnifyConstantPass>());
660}
661
662Optimizer::PassToken CreateEliminateDeadConstantPass() {
663 return MakeUnique<Optimizer::PassToken::Impl>(
664 MakeUnique<opt::EliminateDeadConstantPass>());
665}
666
667Optimizer::PassToken CreateDeadVariableEliminationPass() {
668 return MakeUnique<Optimizer::PassToken::Impl>(
669 MakeUnique<opt::DeadVariableElimination>());
670}
671
672Optimizer::PassToken CreateStrengthReductionPass() {
673 return MakeUnique<Optimizer::PassToken::Impl>(
674 MakeUnique<opt::StrengthReductionPass>());
675}
676
677Optimizer::PassToken CreateBlockMergePass() {
678 return MakeUnique<Optimizer::PassToken::Impl>(
679 MakeUnique<opt::BlockMergePass>());
680}
681
682Optimizer::PassToken CreateInlineExhaustivePass() {
683 return MakeUnique<Optimizer::PassToken::Impl>(
684 MakeUnique<opt::InlineExhaustivePass>());
685}
686
687Optimizer::PassToken CreateInlineOpaquePass() {
688 return MakeUnique<Optimizer::PassToken::Impl>(
689 MakeUnique<opt::InlineOpaquePass>());
690}
691
692Optimizer::PassToken CreateLocalAccessChainConvertPass() {
693 return MakeUnique<Optimizer::PassToken::Impl>(
694 MakeUnique<opt::LocalAccessChainConvertPass>());
695}
696
697Optimizer::PassToken CreateLocalSingleBlockLoadStoreElimPass() {
698 return MakeUnique<Optimizer::PassToken::Impl>(
699 MakeUnique<opt::LocalSingleBlockLoadStoreElimPass>());
700}
701
702Optimizer::PassToken CreateLocalSingleStoreElimPass() {
703 return MakeUnique<Optimizer::PassToken::Impl>(
704 MakeUnique<opt::LocalSingleStoreElimPass>());
705}
706
707Optimizer::PassToken CreateInsertExtractElimPass() {
708 return MakeUnique<Optimizer::PassToken::Impl>(
709 MakeUnique<opt::SimplificationPass>());
710}
711
712Optimizer::PassToken CreateDeadInsertElimPass() {
713 return MakeUnique<Optimizer::PassToken::Impl>(
714 MakeUnique<opt::DeadInsertElimPass>());
715}
716
717Optimizer::PassToken CreateDeadBranchElimPass() {
718 return MakeUnique<Optimizer::PassToken::Impl>(
719 MakeUnique<opt::DeadBranchElimPass>());
720}
721
722Optimizer::PassToken CreateLocalMultiStoreElimPass() {
723 return MakeUnique<Optimizer::PassToken::Impl>(
Ben Claytond552f632019-11-18 11:18:41 +0000724 MakeUnique<opt::SSARewritePass>());
Chris Forbescc5697f2019-01-30 11:54:08 -0800725}
726
727Optimizer::PassToken CreateAggressiveDCEPass() {
728 return MakeUnique<Optimizer::PassToken::Impl>(
729 MakeUnique<opt::AggressiveDCEPass>());
730}
731
732Optimizer::PassToken CreatePropagateLineInfoPass() {
733 return MakeUnique<Optimizer::PassToken::Impl>(
734 MakeUnique<opt::ProcessLinesPass>(opt::kLinesPropagateLines));
735}
736
737Optimizer::PassToken CreateRedundantLineInfoElimPass() {
738 return MakeUnique<Optimizer::PassToken::Impl>(
739 MakeUnique<opt::ProcessLinesPass>(opt::kLinesEliminateDeadLines));
740}
741
Chris Forbescc5697f2019-01-30 11:54:08 -0800742Optimizer::PassToken CreateCompactIdsPass() {
743 return MakeUnique<Optimizer::PassToken::Impl>(
744 MakeUnique<opt::CompactIdsPass>());
745}
746
747Optimizer::PassToken CreateMergeReturnPass() {
748 return MakeUnique<Optimizer::PassToken::Impl>(
749 MakeUnique<opt::MergeReturnPass>());
750}
751
752std::vector<const char*> Optimizer::GetPassNames() const {
753 std::vector<const char*> v;
754 for (uint32_t i = 0; i < impl_->pass_manager.NumPasses(); i++) {
755 v.push_back(impl_->pass_manager.GetPass(i)->name());
756 }
757 return v;
758}
759
760Optimizer::PassToken CreateCFGCleanupPass() {
761 return MakeUnique<Optimizer::PassToken::Impl>(
762 MakeUnique<opt::CFGCleanupPass>());
763}
764
765Optimizer::PassToken CreateLocalRedundancyEliminationPass() {
766 return MakeUnique<Optimizer::PassToken::Impl>(
767 MakeUnique<opt::LocalRedundancyEliminationPass>());
768}
769
770Optimizer::PassToken CreateLoopFissionPass(size_t threshold) {
771 return MakeUnique<Optimizer::PassToken::Impl>(
772 MakeUnique<opt::LoopFissionPass>(threshold));
773}
774
775Optimizer::PassToken CreateLoopFusionPass(size_t max_registers_per_loop) {
776 return MakeUnique<Optimizer::PassToken::Impl>(
777 MakeUnique<opt::LoopFusionPass>(max_registers_per_loop));
778}
779
780Optimizer::PassToken CreateLoopInvariantCodeMotionPass() {
781 return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::LICMPass>());
782}
783
784Optimizer::PassToken CreateLoopPeelingPass() {
785 return MakeUnique<Optimizer::PassToken::Impl>(
786 MakeUnique<opt::LoopPeelingPass>());
787}
788
789Optimizer::PassToken CreateLoopUnswitchPass() {
790 return MakeUnique<Optimizer::PassToken::Impl>(
791 MakeUnique<opt::LoopUnswitchPass>());
792}
793
794Optimizer::PassToken CreateRedundancyEliminationPass() {
795 return MakeUnique<Optimizer::PassToken::Impl>(
796 MakeUnique<opt::RedundancyEliminationPass>());
797}
798
799Optimizer::PassToken CreateRemoveDuplicatesPass() {
800 return MakeUnique<Optimizer::PassToken::Impl>(
801 MakeUnique<opt::RemoveDuplicatesPass>());
802}
803
804Optimizer::PassToken CreateScalarReplacementPass(uint32_t size_limit) {
805 return MakeUnique<Optimizer::PassToken::Impl>(
806 MakeUnique<opt::ScalarReplacementPass>(size_limit));
807}
808
809Optimizer::PassToken CreatePrivateToLocalPass() {
810 return MakeUnique<Optimizer::PassToken::Impl>(
811 MakeUnique<opt::PrivateToLocalPass>());
812}
813
814Optimizer::PassToken CreateCCPPass() {
815 return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::CCPPass>());
816}
817
818Optimizer::PassToken CreateWorkaround1209Pass() {
819 return MakeUnique<Optimizer::PassToken::Impl>(
820 MakeUnique<opt::Workaround1209>());
821}
822
823Optimizer::PassToken CreateIfConversionPass() {
824 return MakeUnique<Optimizer::PassToken::Impl>(
825 MakeUnique<opt::IfConversion>());
826}
827
828Optimizer::PassToken CreateReplaceInvalidOpcodePass() {
829 return MakeUnique<Optimizer::PassToken::Impl>(
830 MakeUnique<opt::ReplaceInvalidOpcodePass>());
831}
832
833Optimizer::PassToken CreateSimplificationPass() {
834 return MakeUnique<Optimizer::PassToken::Impl>(
835 MakeUnique<opt::SimplificationPass>());
836}
837
838Optimizer::PassToken CreateLoopUnrollPass(bool fully_unroll, int factor) {
839 return MakeUnique<Optimizer::PassToken::Impl>(
840 MakeUnique<opt::LoopUnroller>(fully_unroll, factor));
841}
842
843Optimizer::PassToken CreateSSARewritePass() {
844 return MakeUnique<Optimizer::PassToken::Impl>(
845 MakeUnique<opt::SSARewritePass>());
846}
847
848Optimizer::PassToken CreateCopyPropagateArraysPass() {
849 return MakeUnique<Optimizer::PassToken::Impl>(
850 MakeUnique<opt::CopyPropagateArrays>());
851}
852
853Optimizer::PassToken CreateVectorDCEPass() {
854 return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::VectorDCE>());
855}
856
857Optimizer::PassToken CreateReduceLoadSizePass() {
858 return MakeUnique<Optimizer::PassToken::Impl>(
859 MakeUnique<opt::ReduceLoadSize>());
860}
861
862Optimizer::PassToken CreateCombineAccessChainsPass() {
863 return MakeUnique<Optimizer::PassToken::Impl>(
864 MakeUnique<opt::CombineAccessChains>());
865}
866
867Optimizer::PassToken CreateUpgradeMemoryModelPass() {
868 return MakeUnique<Optimizer::PassToken::Impl>(
869 MakeUnique<opt::UpgradeMemoryModel>());
870}
871
872Optimizer::PassToken CreateInstBindlessCheckPass(uint32_t desc_set,
Ben Claytonb73b7602019-07-29 13:56:13 +0100873 uint32_t shader_id,
874 bool input_length_enable,
875 bool input_init_enable,
876 uint32_t version) {
Chris Forbescc5697f2019-01-30 11:54:08 -0800877 return MakeUnique<Optimizer::PassToken::Impl>(
Ben Claytonb73b7602019-07-29 13:56:13 +0100878 MakeUnique<opt::InstBindlessCheckPass>(desc_set, shader_id,
879 input_length_enable,
880 input_init_enable, version));
Chris Forbescc5697f2019-01-30 11:54:08 -0800881}
882
Ben Claytond0f684e2019-08-30 22:36:08 +0100883Optimizer::PassToken CreateInstBuffAddrCheckPass(uint32_t desc_set,
884 uint32_t shader_id,
885 uint32_t version) {
886 return MakeUnique<Optimizer::PassToken::Impl>(
887 MakeUnique<opt::InstBuffAddrCheckPass>(desc_set, shader_id, version));
888}
889
Ben Claytond552f632019-11-18 11:18:41 +0000890Optimizer::PassToken CreateConvertRelaxedToHalfPass() {
891 return MakeUnique<Optimizer::PassToken::Impl>(
892 MakeUnique<opt::ConvertToHalfPass>());
893}
894
895Optimizer::PassToken CreateRelaxFloatOpsPass() {
896 return MakeUnique<Optimizer::PassToken::Impl>(
897 MakeUnique<opt::RelaxFloatOpsPass>());
898}
899
Chris Forbescc5697f2019-01-30 11:54:08 -0800900Optimizer::PassToken CreateCodeSinkingPass() {
901 return MakeUnique<Optimizer::PassToken::Impl>(
902 MakeUnique<opt::CodeSinkingPass>());
903}
904
Ben Claytonb73b7602019-07-29 13:56:13 +0100905Optimizer::PassToken CreateGenerateWebGPUInitializersPass() {
906 return MakeUnique<Optimizer::PassToken::Impl>(
907 MakeUnique<opt::GenerateWebGPUInitializersPass>());
908}
909
910Optimizer::PassToken CreateFixStorageClassPass() {
911 return MakeUnique<Optimizer::PassToken::Impl>(
912 MakeUnique<opt::FixStorageClass>());
913}
914
915Optimizer::PassToken CreateLegalizeVectorShufflePass() {
916 return MakeUnique<Optimizer::PassToken::Impl>(
917 MakeUnique<opt::LegalizeVectorShufflePass>());
918}
919
920Optimizer::PassToken CreateDecomposeInitializedVariablesPass() {
921 return MakeUnique<Optimizer::PassToken::Impl>(
922 MakeUnique<opt::DecomposeInitializedVariablesPass>());
923}
924
925Optimizer::PassToken CreateSplitInvalidUnreachablePass() {
926 return MakeUnique<Optimizer::PassToken::Impl>(
927 MakeUnique<opt::SplitInvalidUnreachablePass>());
928}
929
Ben Claytond0f684e2019-08-30 22:36:08 +0100930Optimizer::PassToken CreateGraphicsRobustAccessPass() {
931 return MakeUnique<Optimizer::PassToken::Impl>(
932 MakeUnique<opt::GraphicsRobustAccessPass>());
933}
934
935Optimizer::PassToken CreateDescriptorScalarReplacementPass() {
936 return MakeUnique<Optimizer::PassToken::Impl>(
937 MakeUnique<opt::DescriptorScalarReplacement>());
938}
939
940Optimizer::PassToken CreateWrapOpKillPass() {
941 return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::WrapOpKill>());
942}
943
944Optimizer::PassToken CreateAmdExtToKhrPass() {
945 return MakeUnique<Optimizer::PassToken::Impl>(
946 MakeUnique<opt::AmdExtensionToKhrPass>());
947}
948
Chris Forbescc5697f2019-01-30 11:54:08 -0800949} // namespace spvtools