1 // Copyright (c) 2016 Google Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "spirv-tools/optimizer.hpp"
16
17 #include <cassert>
18 #include <charconv>
19 #include <memory>
20 #include <string>
21 #include <system_error>
22 #include <unordered_map>
23 #include <utility>
24 #include <vector>
25
26 #include "source/opt/build_module.h"
27 #include "source/opt/graphics_robust_access_pass.h"
28 #include "source/opt/log.h"
29 #include "source/opt/pass_manager.h"
30 #include "source/opt/passes.h"
31 #include "source/spirv_optimizer_options.h"
32 #include "source/util/make_unique.h"
33 #include "source/util/string_utils.h"
34
35 namespace spvtools {
36
GetVectorOfStrings(const char ** strings,const size_t string_count)37 std::vector<std::string> GetVectorOfStrings(const char** strings,
38 const size_t string_count) {
39 std::vector<std::string> result;
40 for (uint32_t i = 0; i < string_count; i++) {
41 result.emplace_back(strings[i]);
42 }
43 return result;
44 }
45
46 struct Optimizer::PassToken::Impl {
Implspvtools::Optimizer::PassToken::Impl47 Impl(std::unique_ptr<opt::Pass> p) : pass(std::move(p)) {}
48
49 std::unique_ptr<opt::Pass> pass; // Internal implementation pass.
50 };
51
PassToken(std::unique_ptr<Optimizer::PassToken::Impl> impl)52 Optimizer::PassToken::PassToken(
53 std::unique_ptr<Optimizer::PassToken::Impl> impl)
54 : impl_(std::move(impl)) {}
55
PassToken(std::unique_ptr<opt::Pass> && pass)56 Optimizer::PassToken::PassToken(std::unique_ptr<opt::Pass>&& pass)
57 : impl_(MakeUnique<Optimizer::PassToken::Impl>(std::move(pass))) {}
58
PassToken(PassToken && that)59 Optimizer::PassToken::PassToken(PassToken&& that)
60 : impl_(std::move(that.impl_)) {}
61
operator =(PassToken && that)62 Optimizer::PassToken& Optimizer::PassToken::operator=(PassToken&& that) {
63 impl_ = std::move(that.impl_);
64 return *this;
65 }
66
~PassToken()67 Optimizer::PassToken::~PassToken() {}
68
69 struct Optimizer::Impl {
Implspvtools::Optimizer::Impl70 explicit Impl(spv_target_env env) : target_env(env), pass_manager() {}
71
72 spv_target_env target_env; // Target environment.
73 opt::PassManager pass_manager; // Internal implementation pass manager.
74 std::unordered_set<uint32_t> live_locs; // Arg to debug dead output passes
75 };
76
Optimizer(spv_target_env env)77 Optimizer::Optimizer(spv_target_env env) : impl_(new Impl(env)) {
78 assert(env != SPV_ENV_WEBGPU_0);
79 }
80
~Optimizer()81 Optimizer::~Optimizer() {}
82
SetMessageConsumer(MessageConsumer c)83 void Optimizer::SetMessageConsumer(MessageConsumer c) {
84 // All passes' message consumer needs to be updated.
85 for (uint32_t i = 0; i < impl_->pass_manager.NumPasses(); ++i) {
86 impl_->pass_manager.GetPass(i)->SetMessageConsumer(c);
87 }
88 impl_->pass_manager.SetMessageConsumer(std::move(c));
89 }
90
consumer() const91 const MessageConsumer& Optimizer::consumer() const {
92 return impl_->pass_manager.consumer();
93 }
94
RegisterPass(PassToken && p)95 Optimizer& Optimizer::RegisterPass(PassToken&& p) {
96 // Change to use the pass manager's consumer.
97 p.impl_->pass->SetMessageConsumer(consumer());
98 impl_->pass_manager.AddPass(std::move(p.impl_->pass));
99 return *this;
100 }
101
102 // The legalization passes take a spir-v shader generated by an HLSL front-end
103 // and turn it into a valid vulkan spir-v shader. There are two ways in which
104 // the code will be invalid at the start:
105 //
106 // 1) There will be opaque objects, like images, which will be passed around
107 // in intermediate objects. Valid spir-v will have to replace the use of
108 // the opaque object with an intermediate object that is the result of the
109 // load of the global opaque object.
110 //
111 // 2) There will be variables that contain pointers to structured or uniform
112 // buffers. It be legal, the variables must be eliminated, and the
113 // references to the structured buffers must use the result of OpVariable
114 // in the Uniform storage class.
115 //
116 // Optimization in this list must accept shaders with these relaxation of the
117 // rules. There is not guarantee that this list of optimizations is able to
118 // legalize all inputs, but it is on a best effort basis.
119 //
120 // The legalization problem is essentially a very general copy propagation
121 // problem. The optimization we use are all used to either do copy propagation
122 // or enable more copy propagation.
RegisterLegalizationPasses(bool preserve_interface)123 Optimizer& Optimizer::RegisterLegalizationPasses(bool preserve_interface) {
124 return
125 // Wrap OpKill instructions so all other code can be inlined.
126 RegisterPass(CreateWrapOpKillPass())
127 // Remove unreachable block so that merge return works.
128 .RegisterPass(CreateDeadBranchElimPass())
129 // Merge the returns so we can inline.
130 .RegisterPass(CreateMergeReturnPass())
131 // Make sure uses and definitions are in the same function.
132 .RegisterPass(CreateInlineExhaustivePass())
133 // Make private variable function scope
134 .RegisterPass(CreateEliminateDeadFunctionsPass())
135 .RegisterPass(CreatePrivateToLocalPass())
136 // Fix up the storage classes that DXC may have purposely generated
137 // incorrectly. All functions are inlined, and a lot of dead code has
138 // been removed.
139 .RegisterPass(CreateFixStorageClassPass())
140 // Propagate the value stored to the loads in very simple cases.
141 .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
142 .RegisterPass(CreateLocalSingleStoreElimPass())
143 .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
144 // Split up aggregates so they are easier to deal with.
145 .RegisterPass(CreateScalarReplacementPass(0))
146 // Remove loads and stores so everything is in intermediate values.
147 // Takes care of copy propagation of non-members.
148 .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
149 .RegisterPass(CreateLocalSingleStoreElimPass())
150 .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
151 .RegisterPass(CreateLocalMultiStoreElimPass())
152 .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
153 // Propagate constants to get as many constant conditions on branches
154 // as possible.
155 .RegisterPass(CreateCCPPass())
156 .RegisterPass(CreateLoopUnrollPass(true))
157 .RegisterPass(CreateDeadBranchElimPass())
158 // Copy propagate members. Cleans up code sequences generated by
159 // scalar replacement. Also important for removing OpPhi nodes.
160 .RegisterPass(CreateSimplificationPass())
161 .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
162 .RegisterPass(CreateCopyPropagateArraysPass())
163 // May need loop unrolling here see
164 // https://github.com/Microsoft/DirectXShaderCompiler/pull/930
165 // Get rid of unused code that contain traces of illegal code
166 // or unused references to unbound external objects
167 .RegisterPass(CreateVectorDCEPass())
168 .RegisterPass(CreateDeadInsertElimPass())
169 .RegisterPass(CreateReduceLoadSizePass())
170 .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
171 .RegisterPass(CreateRemoveUnusedInterfaceVariablesPass())
172 .RegisterPass(CreateInterpolateFixupPass())
173 .RegisterPass(CreateInvocationInterlockPlacementPass());
174 }
175
RegisterLegalizationPasses()176 Optimizer& Optimizer::RegisterLegalizationPasses() {
177 return RegisterLegalizationPasses(false);
178 }
179
RegisterPerformancePasses(bool preserve_interface)180 Optimizer& Optimizer::RegisterPerformancePasses(bool preserve_interface) {
181 return RegisterPass(CreateWrapOpKillPass())
182 .RegisterPass(CreateDeadBranchElimPass())
183 .RegisterPass(CreateMergeReturnPass())
184 .RegisterPass(CreateInlineExhaustivePass())
185 .RegisterPass(CreateEliminateDeadFunctionsPass())
186 .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
187 .RegisterPass(CreatePrivateToLocalPass())
188 .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
189 .RegisterPass(CreateLocalSingleStoreElimPass())
190 .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
191 .RegisterPass(CreateScalarReplacementPass())
192 .RegisterPass(CreateLocalAccessChainConvertPass())
193 .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
194 .RegisterPass(CreateLocalSingleStoreElimPass())
195 .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
196 .RegisterPass(CreateLocalMultiStoreElimPass())
197 .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
198 .RegisterPass(CreateCCPPass())
199 .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
200 .RegisterPass(CreateLoopUnrollPass(true))
201 .RegisterPass(CreateDeadBranchElimPass())
202 .RegisterPass(CreateRedundancyEliminationPass())
203 .RegisterPass(CreateCombineAccessChainsPass())
204 .RegisterPass(CreateSimplificationPass())
205 .RegisterPass(CreateScalarReplacementPass())
206 .RegisterPass(CreateLocalAccessChainConvertPass())
207 .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
208 .RegisterPass(CreateLocalSingleStoreElimPass())
209 .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
210 .RegisterPass(CreateSSARewritePass())
211 .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
212 .RegisterPass(CreateVectorDCEPass())
213 .RegisterPass(CreateDeadInsertElimPass())
214 .RegisterPass(CreateDeadBranchElimPass())
215 .RegisterPass(CreateSimplificationPass())
216 .RegisterPass(CreateIfConversionPass())
217 .RegisterPass(CreateCopyPropagateArraysPass())
218 .RegisterPass(CreateReduceLoadSizePass())
219 .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
220 .RegisterPass(CreateBlockMergePass())
221 .RegisterPass(CreateRedundancyEliminationPass())
222 .RegisterPass(CreateDeadBranchElimPass())
223 .RegisterPass(CreateBlockMergePass())
224 .RegisterPass(CreateSimplificationPass());
225 }
226
RegisterPerformancePasses()227 Optimizer& Optimizer::RegisterPerformancePasses() {
228 return RegisterPerformancePasses(false);
229 }
230
RegisterSizePasses(bool preserve_interface)231 Optimizer& Optimizer::RegisterSizePasses(bool preserve_interface) {
232 return RegisterPass(CreateWrapOpKillPass())
233 .RegisterPass(CreateDeadBranchElimPass())
234 .RegisterPass(CreateMergeReturnPass())
235 .RegisterPass(CreateInlineExhaustivePass())
236 .RegisterPass(CreateEliminateDeadFunctionsPass())
237 .RegisterPass(CreatePrivateToLocalPass())
238 .RegisterPass(CreateScalarReplacementPass(0))
239 .RegisterPass(CreateLocalMultiStoreElimPass())
240 .RegisterPass(CreateCCPPass())
241 .RegisterPass(CreateLoopUnrollPass(true))
242 .RegisterPass(CreateDeadBranchElimPass())
243 .RegisterPass(CreateSimplificationPass())
244 .RegisterPass(CreateScalarReplacementPass(0))
245 .RegisterPass(CreateLocalSingleStoreElimPass())
246 .RegisterPass(CreateIfConversionPass())
247 .RegisterPass(CreateSimplificationPass())
248 .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
249 .RegisterPass(CreateDeadBranchElimPass())
250 .RegisterPass(CreateBlockMergePass())
251 .RegisterPass(CreateLocalAccessChainConvertPass())
252 .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
253 .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
254 .RegisterPass(CreateCopyPropagateArraysPass())
255 .RegisterPass(CreateVectorDCEPass())
256 .RegisterPass(CreateDeadInsertElimPass())
257 .RegisterPass(CreateEliminateDeadMembersPass())
258 .RegisterPass(CreateLocalSingleStoreElimPass())
259 .RegisterPass(CreateBlockMergePass())
260 .RegisterPass(CreateLocalMultiStoreElimPass())
261 .RegisterPass(CreateRedundancyEliminationPass())
262 .RegisterPass(CreateSimplificationPass())
263 .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
264 .RegisterPass(CreateCFGCleanupPass());
265 }
266
RegisterSizePasses()267 Optimizer& Optimizer::RegisterSizePasses() { return RegisterSizePasses(false); }
268
RegisterPassesFromFlags(const std::vector<std::string> & flags)269 bool Optimizer::RegisterPassesFromFlags(const std::vector<std::string>& flags) {
270 return RegisterPassesFromFlags(flags, false);
271 }
272
RegisterPassesFromFlags(const std::vector<std::string> & flags,bool preserve_interface)273 bool Optimizer::RegisterPassesFromFlags(const std::vector<std::string>& flags,
274 bool preserve_interface) {
275 for (const auto& flag : flags) {
276 if (!RegisterPassFromFlag(flag, preserve_interface)) {
277 return false;
278 }
279 }
280
281 return true;
282 }
283
FlagHasValidForm(const std::string & flag) const284 bool Optimizer::FlagHasValidForm(const std::string& flag) const {
285 if (flag == "-O" || flag == "-Os") {
286 return true;
287 } else if (flag.size() > 2 && flag.substr(0, 2) == "--") {
288 return true;
289 }
290
291 Errorf(consumer(), nullptr, {},
292 "%s is not a valid flag. Flag passes should have the form "
293 "'--pass_name[=pass_args]'. Special flag names also accepted: -O "
294 "and -Os.",
295 flag.c_str());
296 return false;
297 }
298
RegisterPassFromFlag(const std::string & flag)299 bool Optimizer::RegisterPassFromFlag(const std::string& flag) {
300 return RegisterPassFromFlag(flag, false);
301 }
302
RegisterPassFromFlag(const std::string & flag,bool preserve_interface)303 bool Optimizer::RegisterPassFromFlag(const std::string& flag,
304 bool preserve_interface) {
305 if (!FlagHasValidForm(flag)) {
306 return false;
307 }
308
309 // Split flags of the form --pass_name=pass_args.
310 auto p = utils::SplitFlagArgs(flag);
311 std::string pass_name = p.first;
312 std::string pass_args = p.second;
313
314 // FIXME(dnovillo): This should be re-factored so that pass names can be
315 // automatically checked against Pass::name() and PassToken instances created
316 // via a template function. Additionally, class Pass should have a desc()
317 // method that describes the pass (so it can be used in --help).
318 //
319 // Both Pass::name() and Pass::desc() should be static class members so they
320 // can be invoked without creating a pass instance.
321 if (pass_name == "strip-debug") {
322 RegisterPass(CreateStripDebugInfoPass());
323 } else if (pass_name == "strip-reflect") {
324 RegisterPass(CreateStripReflectInfoPass());
325 } else if (pass_name == "strip-nonsemantic") {
326 RegisterPass(CreateStripNonSemanticInfoPass());
327 } else if (pass_name == "set-spec-const-default-value") {
328 if (pass_args.size() > 0) {
329 auto spec_ids_vals =
330 opt::SetSpecConstantDefaultValuePass::ParseDefaultValuesString(
331 pass_args.c_str());
332 if (!spec_ids_vals) {
333 Errorf(consumer(), nullptr, {},
334 "Invalid argument for --set-spec-const-default-value: %s",
335 pass_args.c_str());
336 return false;
337 }
338 RegisterPass(
339 CreateSetSpecConstantDefaultValuePass(std::move(*spec_ids_vals)));
340 } else {
341 Errorf(consumer(), nullptr, {},
342 "Invalid spec constant value string '%s'. Expected a string of "
343 "<spec id>:<default value> pairs.",
344 pass_args.c_str());
345 return false;
346 }
347 } else if (pass_name == "if-conversion") {
348 RegisterPass(CreateIfConversionPass());
349 } else if (pass_name == "freeze-spec-const") {
350 RegisterPass(CreateFreezeSpecConstantValuePass());
351 } else if (pass_name == "inline-entry-points-exhaustive") {
352 RegisterPass(CreateInlineExhaustivePass());
353 } else if (pass_name == "inline-entry-points-opaque") {
354 RegisterPass(CreateInlineOpaquePass());
355 } else if (pass_name == "combine-access-chains") {
356 RegisterPass(CreateCombineAccessChainsPass());
357 } else if (pass_name == "convert-local-access-chains") {
358 RegisterPass(CreateLocalAccessChainConvertPass());
359 } else if (pass_name == "replace-desc-array-access-using-var-index") {
360 RegisterPass(CreateReplaceDescArrayAccessUsingVarIndexPass());
361 } else if (pass_name == "spread-volatile-semantics") {
362 RegisterPass(CreateSpreadVolatileSemanticsPass());
363 } else if (pass_name == "descriptor-scalar-replacement") {
364 RegisterPass(CreateDescriptorScalarReplacementPass());
365 } else if (pass_name == "eliminate-dead-code-aggressive") {
366 RegisterPass(CreateAggressiveDCEPass(preserve_interface));
367 } else if (pass_name == "eliminate-insert-extract") {
368 RegisterPass(CreateInsertExtractElimPass());
369 } else if (pass_name == "eliminate-local-single-block") {
370 RegisterPass(CreateLocalSingleBlockLoadStoreElimPass());
371 } else if (pass_name == "eliminate-local-single-store") {
372 RegisterPass(CreateLocalSingleStoreElimPass());
373 } else if (pass_name == "merge-blocks") {
374 RegisterPass(CreateBlockMergePass());
375 } else if (pass_name == "merge-return") {
376 RegisterPass(CreateMergeReturnPass());
377 } else if (pass_name == "eliminate-dead-branches") {
378 RegisterPass(CreateDeadBranchElimPass());
379 } else if (pass_name == "eliminate-dead-functions") {
380 RegisterPass(CreateEliminateDeadFunctionsPass());
381 } else if (pass_name == "eliminate-local-multi-store") {
382 RegisterPass(CreateLocalMultiStoreElimPass());
383 } else if (pass_name == "eliminate-dead-const") {
384 RegisterPass(CreateEliminateDeadConstantPass());
385 } else if (pass_name == "eliminate-dead-inserts") {
386 RegisterPass(CreateDeadInsertElimPass());
387 } else if (pass_name == "eliminate-dead-variables") {
388 RegisterPass(CreateDeadVariableEliminationPass());
389 } else if (pass_name == "eliminate-dead-members") {
390 RegisterPass(CreateEliminateDeadMembersPass());
391 } else if (pass_name == "fold-spec-const-op-composite") {
392 RegisterPass(CreateFoldSpecConstantOpAndCompositePass());
393 } else if (pass_name == "loop-unswitch") {
394 RegisterPass(CreateLoopUnswitchPass());
395 } else if (pass_name == "scalar-replacement") {
396 if (pass_args.size() == 0) {
397 RegisterPass(CreateScalarReplacementPass());
398 } else {
399 int limit = -1;
400 if (pass_args.find_first_not_of("0123456789") == std::string::npos) {
401 limit = atoi(pass_args.c_str());
402 }
403
404 if (limit >= 0) {
405 RegisterPass(CreateScalarReplacementPass(limit));
406 } else {
407 Error(consumer(), nullptr, {},
408 "--scalar-replacement must have no arguments or a non-negative "
409 "integer argument");
410 return false;
411 }
412 }
413 } else if (pass_name == "strength-reduction") {
414 RegisterPass(CreateStrengthReductionPass());
415 } else if (pass_name == "unify-const") {
416 RegisterPass(CreateUnifyConstantPass());
417 } else if (pass_name == "flatten-decorations") {
418 RegisterPass(CreateFlattenDecorationPass());
419 } else if (pass_name == "compact-ids") {
420 RegisterPass(CreateCompactIdsPass());
421 } else if (pass_name == "cfg-cleanup") {
422 RegisterPass(CreateCFGCleanupPass());
423 } else if (pass_name == "local-redundancy-elimination") {
424 RegisterPass(CreateLocalRedundancyEliminationPass());
425 } else if (pass_name == "loop-invariant-code-motion") {
426 RegisterPass(CreateLoopInvariantCodeMotionPass());
427 } else if (pass_name == "reduce-load-size") {
428 if (pass_args.size() == 0) {
429 RegisterPass(CreateReduceLoadSizePass());
430 } else {
431 double load_replacement_threshold = 0.9;
432 if (pass_args.find_first_not_of(".0123456789") == std::string::npos) {
433 load_replacement_threshold = atof(pass_args.c_str());
434 }
435
436 if (load_replacement_threshold >= 0) {
437 RegisterPass(CreateReduceLoadSizePass(load_replacement_threshold));
438 } else {
439 Error(consumer(), nullptr, {},
440 "--reduce-load-size must have no arguments or a non-negative "
441 "double argument");
442 return false;
443 }
444 }
445 } else if (pass_name == "redundancy-elimination") {
446 RegisterPass(CreateRedundancyEliminationPass());
447 } else if (pass_name == "private-to-local") {
448 RegisterPass(CreatePrivateToLocalPass());
449 } else if (pass_name == "remove-duplicates") {
450 RegisterPass(CreateRemoveDuplicatesPass());
451 } else if (pass_name == "workaround-1209") {
452 RegisterPass(CreateWorkaround1209Pass());
453 } else if (pass_name == "replace-invalid-opcode") {
454 RegisterPass(CreateReplaceInvalidOpcodePass());
455 } else if (pass_name == "inst-bindless-check" ||
456 pass_name == "inst-desc-idx-check" ||
457 pass_name == "inst-buff-oob-check") {
458 // preserve legacy names
459 RegisterPass(CreateInstBindlessCheckPass(23));
460 RegisterPass(CreateSimplificationPass());
461 RegisterPass(CreateDeadBranchElimPass());
462 RegisterPass(CreateBlockMergePass());
463 } else if (pass_name == "inst-buff-addr-check") {
464 RegisterPass(CreateInstBuffAddrCheckPass(23));
465 } else if (pass_name == "convert-relaxed-to-half") {
466 RegisterPass(CreateConvertRelaxedToHalfPass());
467 } else if (pass_name == "relax-float-ops") {
468 RegisterPass(CreateRelaxFloatOpsPass());
469 } else if (pass_name == "inst-debug-printf") {
470 // This private option is not for user consumption.
471 // It is here to assist in debugging and fixing the debug printf
472 // instrumentation pass.
473 // For users who wish to utilize debug printf, see the white paper at
474 // https://www.lunarg.com/wp-content/uploads/2021/08/Using-Debug-Printf-02August2021.pdf
475 RegisterPass(CreateInstDebugPrintfPass(7, 23));
476 } else if (pass_name == "simplify-instructions") {
477 RegisterPass(CreateSimplificationPass());
478 } else if (pass_name == "ssa-rewrite") {
479 RegisterPass(CreateSSARewritePass());
480 } else if (pass_name == "copy-propagate-arrays") {
481 RegisterPass(CreateCopyPropagateArraysPass());
482 } else if (pass_name == "loop-fission") {
483 int register_threshold_to_split =
484 (pass_args.size() > 0) ? atoi(pass_args.c_str()) : -1;
485 if (register_threshold_to_split > 0) {
486 RegisterPass(CreateLoopFissionPass(
487 static_cast<size_t>(register_threshold_to_split)));
488 } else {
489 Error(consumer(), nullptr, {},
490 "--loop-fission must have a positive integer argument");
491 return false;
492 }
493 } else if (pass_name == "loop-fusion") {
494 int max_registers_per_loop =
495 (pass_args.size() > 0) ? atoi(pass_args.c_str()) : -1;
496 if (max_registers_per_loop > 0) {
497 RegisterPass(
498 CreateLoopFusionPass(static_cast<size_t>(max_registers_per_loop)));
499 } else {
500 Error(consumer(), nullptr, {},
501 "--loop-fusion must have a positive integer argument");
502 return false;
503 }
504 } else if (pass_name == "loop-unroll") {
505 RegisterPass(CreateLoopUnrollPass(true));
506 } else if (pass_name == "upgrade-memory-model") {
507 RegisterPass(CreateUpgradeMemoryModelPass());
508 } else if (pass_name == "vector-dce") {
509 RegisterPass(CreateVectorDCEPass());
510 } else if (pass_name == "loop-unroll-partial") {
511 int factor = (pass_args.size() > 0) ? atoi(pass_args.c_str()) : 0;
512 if (factor > 0) {
513 RegisterPass(CreateLoopUnrollPass(false, factor));
514 } else {
515 Error(consumer(), nullptr, {},
516 "--loop-unroll-partial must have a positive integer argument");
517 return false;
518 }
519 } else if (pass_name == "loop-peeling") {
520 RegisterPass(CreateLoopPeelingPass());
521 } else if (pass_name == "loop-peeling-threshold") {
522 int factor = (pass_args.size() > 0) ? atoi(pass_args.c_str()) : 0;
523 if (factor > 0) {
524 opt::LoopPeelingPass::SetLoopPeelingThreshold(factor);
525 } else {
526 Error(consumer(), nullptr, {},
527 "--loop-peeling-threshold must have a positive integer argument");
528 return false;
529 }
530 } else if (pass_name == "ccp") {
531 RegisterPass(CreateCCPPass());
532 } else if (pass_name == "code-sink") {
533 RegisterPass(CreateCodeSinkingPass());
534 } else if (pass_name == "fix-storage-class") {
535 RegisterPass(CreateFixStorageClassPass());
536 } else if (pass_name == "O") {
537 RegisterPerformancePasses(preserve_interface);
538 } else if (pass_name == "Os") {
539 RegisterSizePasses(preserve_interface);
540 } else if (pass_name == "legalize-hlsl") {
541 RegisterLegalizationPasses(preserve_interface);
542 } else if (pass_name == "remove-unused-interface-variables") {
543 RegisterPass(CreateRemoveUnusedInterfaceVariablesPass());
544 } else if (pass_name == "graphics-robust-access") {
545 RegisterPass(CreateGraphicsRobustAccessPass());
546 } else if (pass_name == "wrap-opkill") {
547 RegisterPass(CreateWrapOpKillPass());
548 } else if (pass_name == "amd-ext-to-khr") {
549 RegisterPass(CreateAmdExtToKhrPass());
550 } else if (pass_name == "interpolate-fixup") {
551 RegisterPass(CreateInterpolateFixupPass());
552 } else if (pass_name == "remove-dont-inline") {
553 RegisterPass(CreateRemoveDontInlinePass());
554 } else if (pass_name == "eliminate-dead-input-components") {
555 RegisterPass(CreateEliminateDeadInputComponentsSafePass());
556 } else if (pass_name == "fix-func-call-param") {
557 RegisterPass(CreateFixFuncCallArgumentsPass());
558 } else if (pass_name == "convert-to-sampled-image") {
559 if (pass_args.size() > 0) {
560 auto descriptor_set_binding_pairs =
561 opt::ConvertToSampledImagePass::ParseDescriptorSetBindingPairsString(
562 pass_args.c_str());
563 if (!descriptor_set_binding_pairs) {
564 Errorf(consumer(), nullptr, {},
565 "Invalid argument for --convert-to-sampled-image: %s",
566 pass_args.c_str());
567 return false;
568 }
569 RegisterPass(CreateConvertToSampledImagePass(
570 std::move(*descriptor_set_binding_pairs)));
571 } else {
572 Errorf(consumer(), nullptr, {},
573 "Invalid pairs of descriptor set and binding '%s'. Expected a "
574 "string of <descriptor set>:<binding> pairs.",
575 pass_args.c_str());
576 return false;
577 }
578 } else if (pass_name == "switch-descriptorset") {
579 if (pass_args.size() == 0) {
580 Error(consumer(), nullptr, {},
581 "--switch-descriptorset requires a from:to argument.");
582 return false;
583 }
584 uint32_t from_set = 0, to_set = 0;
585 const char* start = pass_args.data();
586 const char* end = pass_args.data() + pass_args.size();
587
588 auto result = std::from_chars(start, end, from_set);
589 if (result.ec != std::errc()) {
590 Errorf(consumer(), nullptr, {},
591 "Invalid argument for --switch-descriptorset: %s",
592 pass_args.c_str());
593 return false;
594 }
595 start = result.ptr;
596 if (start[0] != ':') {
597 Errorf(consumer(), nullptr, {},
598 "Invalid argument for --switch-descriptorset: %s",
599 pass_args.c_str());
600 return false;
601 }
602 start++;
603 result = std::from_chars(start, end, to_set);
604 if (result.ec != std::errc() || result.ptr != end) {
605 Errorf(consumer(), nullptr, {},
606 "Invalid argument for --switch-descriptorset: %s",
607 pass_args.c_str());
608 return false;
609 }
610 RegisterPass(CreateSwitchDescriptorSetPass(from_set, to_set));
611 } else if (pass_name == "modify-maximal-reconvergence") {
612 if (pass_args.size() == 0) {
613 Error(consumer(), nullptr, {},
614 "--modify-maximal-reconvergence requires an argument");
615 return false;
616 }
617 if (pass_args == "add") {
618 RegisterPass(CreateModifyMaximalReconvergencePass(true));
619 } else if (pass_args == "remove") {
620 RegisterPass(CreateModifyMaximalReconvergencePass(false));
621 } else {
622 Errorf(consumer(), nullptr, {},
623 "Invalid argument for --modify-maximal-reconvergence: %s (must be "
624 "'add' or 'remove')",
625 pass_args.c_str());
626 return false;
627 }
628 } else if (pass_name == "trim-capabilities") {
629 RegisterPass(CreateTrimCapabilitiesPass());
630 } else {
631 Errorf(consumer(), nullptr, {},
632 "Unknown flag '--%s'. Use --help for a list of valid flags",
633 pass_name.c_str());
634 return false;
635 }
636
637 return true;
638 }
639
SetTargetEnv(const spv_target_env env)640 void Optimizer::SetTargetEnv(const spv_target_env env) {
641 impl_->target_env = env;
642 }
643
Run(const uint32_t * original_binary,const size_t original_binary_size,std::vector<uint32_t> * optimized_binary) const644 bool Optimizer::Run(const uint32_t* original_binary,
645 const size_t original_binary_size,
646 std::vector<uint32_t>* optimized_binary) const {
647 return Run(original_binary, original_binary_size, optimized_binary,
648 OptimizerOptions());
649 }
650
Run(const uint32_t * original_binary,const size_t original_binary_size,std::vector<uint32_t> * optimized_binary,const ValidatorOptions & validator_options,bool skip_validation) const651 bool Optimizer::Run(const uint32_t* original_binary,
652 const size_t original_binary_size,
653 std::vector<uint32_t>* optimized_binary,
654 const ValidatorOptions& validator_options,
655 bool skip_validation) const {
656 OptimizerOptions opt_options;
657 opt_options.set_run_validator(!skip_validation);
658 opt_options.set_validator_options(validator_options);
659 return Run(original_binary, original_binary_size, optimized_binary,
660 opt_options);
661 }
662
Run(const uint32_t * original_binary,const size_t original_binary_size,std::vector<uint32_t> * optimized_binary,const spv_optimizer_options opt_options) const663 bool Optimizer::Run(const uint32_t* original_binary,
664 const size_t original_binary_size,
665 std::vector<uint32_t>* optimized_binary,
666 const spv_optimizer_options opt_options) const {
667 spvtools::SpirvTools tools(impl_->target_env);
668 tools.SetMessageConsumer(impl_->pass_manager.consumer());
669 if (opt_options->run_validator_ &&
670 !tools.Validate(original_binary, original_binary_size,
671 &opt_options->val_options_)) {
672 return false;
673 }
674
675 std::unique_ptr<opt::IRContext> context = BuildModule(
676 impl_->target_env, consumer(), original_binary, original_binary_size);
677 if (context == nullptr) return false;
678
679 context->set_max_id_bound(opt_options->max_id_bound_);
680 context->set_preserve_bindings(opt_options->preserve_bindings_);
681 context->set_preserve_spec_constants(opt_options->preserve_spec_constants_);
682
683 impl_->pass_manager.SetValidatorOptions(&opt_options->val_options_);
684 impl_->pass_manager.SetTargetEnv(impl_->target_env);
685 auto status = impl_->pass_manager.Run(context.get());
686
687 if (status == opt::Pass::Status::Failure) {
688 return false;
689 }
690
691 #ifndef NDEBUG
692 // We do not keep the result id of DebugScope in struct DebugScope.
693 // Instead, we assign random ids for them, which results in integrity
694 // check failures. In addition, propagating the OpLine/OpNoLine to preserve
695 // the debug information through transformations results in integrity
696 // check failures. We want to skip the integrity check when the module
697 // contains DebugScope or OpLine/OpNoLine instructions.
698 if (status == opt::Pass::Status::SuccessWithoutChange &&
699 !context->module()->ContainsDebugInfo()) {
700 std::vector<uint32_t> optimized_binary_with_nop;
701 context->module()->ToBinary(&optimized_binary_with_nop,
702 /* skip_nop = */ false);
703 assert(optimized_binary_with_nop.size() == original_binary_size &&
704 "Binary size unexpectedly changed despite the optimizer saying "
705 "there was no change");
706
707 // Compare the magic number to make sure the binaries were encoded in the
708 // endianness. If not, the contents of the binaries will be different, so
709 // do not check the contents.
710 if (optimized_binary_with_nop[0] == original_binary[0]) {
711 assert(memcmp(optimized_binary_with_nop.data(), original_binary,
712 original_binary_size) == 0 &&
713 "Binary content unexpectedly changed despite the optimizer saying "
714 "there was no change");
715 }
716 }
717 #endif // !NDEBUG
718
719 // Note that |original_binary| and |optimized_binary| may share the same
720 // buffer and the below will invalidate |original_binary|.
721 optimized_binary->clear();
722 context->module()->ToBinary(optimized_binary, /* skip_nop = */ true);
723
724 return true;
725 }
726
SetPrintAll(std::ostream * out)727 Optimizer& Optimizer::SetPrintAll(std::ostream* out) {
728 impl_->pass_manager.SetPrintAll(out);
729 return *this;
730 }
731
SetTimeReport(std::ostream * out)732 Optimizer& Optimizer::SetTimeReport(std::ostream* out) {
733 impl_->pass_manager.SetTimeReport(out);
734 return *this;
735 }
736
SetValidateAfterAll(bool validate)737 Optimizer& Optimizer::SetValidateAfterAll(bool validate) {
738 impl_->pass_manager.SetValidateAfterAll(validate);
739 return *this;
740 }
741
CreateNullPass()742 Optimizer::PassToken CreateNullPass() {
743 return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::NullPass>());
744 }
745
CreateStripDebugInfoPass()746 Optimizer::PassToken CreateStripDebugInfoPass() {
747 return MakeUnique<Optimizer::PassToken::Impl>(
748 MakeUnique<opt::StripDebugInfoPass>());
749 }
750
CreateStripReflectInfoPass()751 Optimizer::PassToken CreateStripReflectInfoPass() {
752 return CreateStripNonSemanticInfoPass();
753 }
754
CreateStripNonSemanticInfoPass()755 Optimizer::PassToken CreateStripNonSemanticInfoPass() {
756 return MakeUnique<Optimizer::PassToken::Impl>(
757 MakeUnique<opt::StripNonSemanticInfoPass>());
758 }
759
CreateEliminateDeadFunctionsPass()760 Optimizer::PassToken CreateEliminateDeadFunctionsPass() {
761 return MakeUnique<Optimizer::PassToken::Impl>(
762 MakeUnique<opt::EliminateDeadFunctionsPass>());
763 }
764
CreateEliminateDeadMembersPass()765 Optimizer::PassToken CreateEliminateDeadMembersPass() {
766 return MakeUnique<Optimizer::PassToken::Impl>(
767 MakeUnique<opt::EliminateDeadMembersPass>());
768 }
769
CreateSetSpecConstantDefaultValuePass(const std::unordered_map<uint32_t,std::string> & id_value_map)770 Optimizer::PassToken CreateSetSpecConstantDefaultValuePass(
771 const std::unordered_map<uint32_t, std::string>& id_value_map) {
772 return MakeUnique<Optimizer::PassToken::Impl>(
773 MakeUnique<opt::SetSpecConstantDefaultValuePass>(id_value_map));
774 }
775
CreateSetSpecConstantDefaultValuePass(const std::unordered_map<uint32_t,std::vector<uint32_t>> & id_value_map)776 Optimizer::PassToken CreateSetSpecConstantDefaultValuePass(
777 const std::unordered_map<uint32_t, std::vector<uint32_t>>& id_value_map) {
778 return MakeUnique<Optimizer::PassToken::Impl>(
779 MakeUnique<opt::SetSpecConstantDefaultValuePass>(id_value_map));
780 }
781
CreateFlattenDecorationPass()782 Optimizer::PassToken CreateFlattenDecorationPass() {
783 return MakeUnique<Optimizer::PassToken::Impl>(
784 MakeUnique<opt::FlattenDecorationPass>());
785 }
786
CreateFreezeSpecConstantValuePass()787 Optimizer::PassToken CreateFreezeSpecConstantValuePass() {
788 return MakeUnique<Optimizer::PassToken::Impl>(
789 MakeUnique<opt::FreezeSpecConstantValuePass>());
790 }
791
CreateFoldSpecConstantOpAndCompositePass()792 Optimizer::PassToken CreateFoldSpecConstantOpAndCompositePass() {
793 return MakeUnique<Optimizer::PassToken::Impl>(
794 MakeUnique<opt::FoldSpecConstantOpAndCompositePass>());
795 }
796
CreateUnifyConstantPass()797 Optimizer::PassToken CreateUnifyConstantPass() {
798 return MakeUnique<Optimizer::PassToken::Impl>(
799 MakeUnique<opt::UnifyConstantPass>());
800 }
801
CreateEliminateDeadConstantPass()802 Optimizer::PassToken CreateEliminateDeadConstantPass() {
803 return MakeUnique<Optimizer::PassToken::Impl>(
804 MakeUnique<opt::EliminateDeadConstantPass>());
805 }
806
CreateDeadVariableEliminationPass()807 Optimizer::PassToken CreateDeadVariableEliminationPass() {
808 return MakeUnique<Optimizer::PassToken::Impl>(
809 MakeUnique<opt::DeadVariableElimination>());
810 }
811
CreateStrengthReductionPass()812 Optimizer::PassToken CreateStrengthReductionPass() {
813 return MakeUnique<Optimizer::PassToken::Impl>(
814 MakeUnique<opt::StrengthReductionPass>());
815 }
816
CreateBlockMergePass()817 Optimizer::PassToken CreateBlockMergePass() {
818 return MakeUnique<Optimizer::PassToken::Impl>(
819 MakeUnique<opt::BlockMergePass>());
820 }
821
CreateInlineExhaustivePass()822 Optimizer::PassToken CreateInlineExhaustivePass() {
823 return MakeUnique<Optimizer::PassToken::Impl>(
824 MakeUnique<opt::InlineExhaustivePass>());
825 }
826
CreateInlineOpaquePass()827 Optimizer::PassToken CreateInlineOpaquePass() {
828 return MakeUnique<Optimizer::PassToken::Impl>(
829 MakeUnique<opt::InlineOpaquePass>());
830 }
831
CreateLocalAccessChainConvertPass()832 Optimizer::PassToken CreateLocalAccessChainConvertPass() {
833 return MakeUnique<Optimizer::PassToken::Impl>(
834 MakeUnique<opt::LocalAccessChainConvertPass>());
835 }
836
CreateLocalSingleBlockLoadStoreElimPass()837 Optimizer::PassToken CreateLocalSingleBlockLoadStoreElimPass() {
838 return MakeUnique<Optimizer::PassToken::Impl>(
839 MakeUnique<opt::LocalSingleBlockLoadStoreElimPass>());
840 }
841
CreateLocalSingleStoreElimPass()842 Optimizer::PassToken CreateLocalSingleStoreElimPass() {
843 return MakeUnique<Optimizer::PassToken::Impl>(
844 MakeUnique<opt::LocalSingleStoreElimPass>());
845 }
846
CreateInsertExtractElimPass()847 Optimizer::PassToken CreateInsertExtractElimPass() {
848 return MakeUnique<Optimizer::PassToken::Impl>(
849 MakeUnique<opt::SimplificationPass>());
850 }
851
CreateDeadInsertElimPass()852 Optimizer::PassToken CreateDeadInsertElimPass() {
853 return MakeUnique<Optimizer::PassToken::Impl>(
854 MakeUnique<opt::DeadInsertElimPass>());
855 }
856
CreateDeadBranchElimPass()857 Optimizer::PassToken CreateDeadBranchElimPass() {
858 return MakeUnique<Optimizer::PassToken::Impl>(
859 MakeUnique<opt::DeadBranchElimPass>());
860 }
861
CreateLocalMultiStoreElimPass()862 Optimizer::PassToken CreateLocalMultiStoreElimPass() {
863 return MakeUnique<Optimizer::PassToken::Impl>(
864 MakeUnique<opt::SSARewritePass>());
865 }
866
CreateAggressiveDCEPass()867 Optimizer::PassToken CreateAggressiveDCEPass() {
868 return MakeUnique<Optimizer::PassToken::Impl>(
869 MakeUnique<opt::AggressiveDCEPass>(false, false));
870 }
871
CreateAggressiveDCEPass(bool preserve_interface)872 Optimizer::PassToken CreateAggressiveDCEPass(bool preserve_interface) {
873 return MakeUnique<Optimizer::PassToken::Impl>(
874 MakeUnique<opt::AggressiveDCEPass>(preserve_interface, false));
875 }
876
CreateAggressiveDCEPass(bool preserve_interface,bool remove_outputs)877 Optimizer::PassToken CreateAggressiveDCEPass(bool preserve_interface,
878 bool remove_outputs) {
879 return MakeUnique<Optimizer::PassToken::Impl>(
880 MakeUnique<opt::AggressiveDCEPass>(preserve_interface, remove_outputs));
881 }
882
CreateRemoveUnusedInterfaceVariablesPass()883 Optimizer::PassToken CreateRemoveUnusedInterfaceVariablesPass() {
884 return MakeUnique<Optimizer::PassToken::Impl>(
885 MakeUnique<opt::RemoveUnusedInterfaceVariablesPass>());
886 }
887
CreatePropagateLineInfoPass()888 Optimizer::PassToken CreatePropagateLineInfoPass() {
889 return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::EmptyPass>());
890 }
891
CreateRedundantLineInfoElimPass()892 Optimizer::PassToken CreateRedundantLineInfoElimPass() {
893 return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::EmptyPass>());
894 }
895
CreateCompactIdsPass()896 Optimizer::PassToken CreateCompactIdsPass() {
897 return MakeUnique<Optimizer::PassToken::Impl>(
898 MakeUnique<opt::CompactIdsPass>());
899 }
900
CreateMergeReturnPass()901 Optimizer::PassToken CreateMergeReturnPass() {
902 return MakeUnique<Optimizer::PassToken::Impl>(
903 MakeUnique<opt::MergeReturnPass>());
904 }
905
GetPassNames() const906 std::vector<const char*> Optimizer::GetPassNames() const {
907 std::vector<const char*> v;
908 for (uint32_t i = 0; i < impl_->pass_manager.NumPasses(); i++) {
909 v.push_back(impl_->pass_manager.GetPass(i)->name());
910 }
911 return v;
912 }
913
CreateCFGCleanupPass()914 Optimizer::PassToken CreateCFGCleanupPass() {
915 return MakeUnique<Optimizer::PassToken::Impl>(
916 MakeUnique<opt::CFGCleanupPass>());
917 }
918
CreateLocalRedundancyEliminationPass()919 Optimizer::PassToken CreateLocalRedundancyEliminationPass() {
920 return MakeUnique<Optimizer::PassToken::Impl>(
921 MakeUnique<opt::LocalRedundancyEliminationPass>());
922 }
923
CreateLoopFissionPass(size_t threshold)924 Optimizer::PassToken CreateLoopFissionPass(size_t threshold) {
925 return MakeUnique<Optimizer::PassToken::Impl>(
926 MakeUnique<opt::LoopFissionPass>(threshold));
927 }
928
CreateLoopFusionPass(size_t max_registers_per_loop)929 Optimizer::PassToken CreateLoopFusionPass(size_t max_registers_per_loop) {
930 return MakeUnique<Optimizer::PassToken::Impl>(
931 MakeUnique<opt::LoopFusionPass>(max_registers_per_loop));
932 }
933
CreateLoopInvariantCodeMotionPass()934 Optimizer::PassToken CreateLoopInvariantCodeMotionPass() {
935 return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::LICMPass>());
936 }
937
CreateLoopPeelingPass()938 Optimizer::PassToken CreateLoopPeelingPass() {
939 return MakeUnique<Optimizer::PassToken::Impl>(
940 MakeUnique<opt::LoopPeelingPass>());
941 }
942
CreateLoopUnswitchPass()943 Optimizer::PassToken CreateLoopUnswitchPass() {
944 return MakeUnique<Optimizer::PassToken::Impl>(
945 MakeUnique<opt::LoopUnswitchPass>());
946 }
947
CreateRedundancyEliminationPass()948 Optimizer::PassToken CreateRedundancyEliminationPass() {
949 return MakeUnique<Optimizer::PassToken::Impl>(
950 MakeUnique<opt::RedundancyEliminationPass>());
951 }
952
CreateRemoveDuplicatesPass()953 Optimizer::PassToken CreateRemoveDuplicatesPass() {
954 return MakeUnique<Optimizer::PassToken::Impl>(
955 MakeUnique<opt::RemoveDuplicatesPass>());
956 }
957
CreateScalarReplacementPass(uint32_t size_limit)958 Optimizer::PassToken CreateScalarReplacementPass(uint32_t size_limit) {
959 return MakeUnique<Optimizer::PassToken::Impl>(
960 MakeUnique<opt::ScalarReplacementPass>(size_limit));
961 }
962
CreatePrivateToLocalPass()963 Optimizer::PassToken CreatePrivateToLocalPass() {
964 return MakeUnique<Optimizer::PassToken::Impl>(
965 MakeUnique<opt::PrivateToLocalPass>());
966 }
967
CreateCCPPass()968 Optimizer::PassToken CreateCCPPass() {
969 return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::CCPPass>());
970 }
971
CreateWorkaround1209Pass()972 Optimizer::PassToken CreateWorkaround1209Pass() {
973 return MakeUnique<Optimizer::PassToken::Impl>(
974 MakeUnique<opt::Workaround1209>());
975 }
976
CreateIfConversionPass()977 Optimizer::PassToken CreateIfConversionPass() {
978 return MakeUnique<Optimizer::PassToken::Impl>(
979 MakeUnique<opt::IfConversion>());
980 }
981
CreateReplaceInvalidOpcodePass()982 Optimizer::PassToken CreateReplaceInvalidOpcodePass() {
983 return MakeUnique<Optimizer::PassToken::Impl>(
984 MakeUnique<opt::ReplaceInvalidOpcodePass>());
985 }
986
CreateSimplificationPass()987 Optimizer::PassToken CreateSimplificationPass() {
988 return MakeUnique<Optimizer::PassToken::Impl>(
989 MakeUnique<opt::SimplificationPass>());
990 }
991
CreateLoopUnrollPass(bool fully_unroll,int factor)992 Optimizer::PassToken CreateLoopUnrollPass(bool fully_unroll, int factor) {
993 return MakeUnique<Optimizer::PassToken::Impl>(
994 MakeUnique<opt::LoopUnroller>(fully_unroll, factor));
995 }
996
CreateSSARewritePass()997 Optimizer::PassToken CreateSSARewritePass() {
998 return MakeUnique<Optimizer::PassToken::Impl>(
999 MakeUnique<opt::SSARewritePass>());
1000 }
1001
CreateCopyPropagateArraysPass()1002 Optimizer::PassToken CreateCopyPropagateArraysPass() {
1003 return MakeUnique<Optimizer::PassToken::Impl>(
1004 MakeUnique<opt::CopyPropagateArrays>());
1005 }
1006
CreateVectorDCEPass()1007 Optimizer::PassToken CreateVectorDCEPass() {
1008 return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::VectorDCE>());
1009 }
1010
CreateReduceLoadSizePass(double load_replacement_threshold)1011 Optimizer::PassToken CreateReduceLoadSizePass(
1012 double load_replacement_threshold) {
1013 return MakeUnique<Optimizer::PassToken::Impl>(
1014 MakeUnique<opt::ReduceLoadSize>(load_replacement_threshold));
1015 }
1016
CreateCombineAccessChainsPass()1017 Optimizer::PassToken CreateCombineAccessChainsPass() {
1018 return MakeUnique<Optimizer::PassToken::Impl>(
1019 MakeUnique<opt::CombineAccessChains>());
1020 }
1021
CreateUpgradeMemoryModelPass()1022 Optimizer::PassToken CreateUpgradeMemoryModelPass() {
1023 return MakeUnique<Optimizer::PassToken::Impl>(
1024 MakeUnique<opt::UpgradeMemoryModel>());
1025 }
1026
CreateInstBindlessCheckPass(uint32_t shader_id)1027 Optimizer::PassToken CreateInstBindlessCheckPass(uint32_t shader_id) {
1028 return MakeUnique<Optimizer::PassToken::Impl>(
1029 MakeUnique<opt::InstBindlessCheckPass>(shader_id));
1030 }
1031
CreateInstDebugPrintfPass(uint32_t desc_set,uint32_t shader_id)1032 Optimizer::PassToken CreateInstDebugPrintfPass(uint32_t desc_set,
1033 uint32_t shader_id) {
1034 return MakeUnique<Optimizer::PassToken::Impl>(
1035 MakeUnique<opt::InstDebugPrintfPass>(desc_set, shader_id));
1036 }
1037
CreateInstBuffAddrCheckPass(uint32_t shader_id)1038 Optimizer::PassToken CreateInstBuffAddrCheckPass(uint32_t shader_id) {
1039 return MakeUnique<Optimizer::PassToken::Impl>(
1040 MakeUnique<opt::InstBuffAddrCheckPass>(shader_id));
1041 }
1042
CreateConvertRelaxedToHalfPass()1043 Optimizer::PassToken CreateConvertRelaxedToHalfPass() {
1044 return MakeUnique<Optimizer::PassToken::Impl>(
1045 MakeUnique<opt::ConvertToHalfPass>());
1046 }
1047
CreateRelaxFloatOpsPass()1048 Optimizer::PassToken CreateRelaxFloatOpsPass() {
1049 return MakeUnique<Optimizer::PassToken::Impl>(
1050 MakeUnique<opt::RelaxFloatOpsPass>());
1051 }
1052
CreateCodeSinkingPass()1053 Optimizer::PassToken CreateCodeSinkingPass() {
1054 return MakeUnique<Optimizer::PassToken::Impl>(
1055 MakeUnique<opt::CodeSinkingPass>());
1056 }
1057
CreateFixStorageClassPass()1058 Optimizer::PassToken CreateFixStorageClassPass() {
1059 return MakeUnique<Optimizer::PassToken::Impl>(
1060 MakeUnique<opt::FixStorageClass>());
1061 }
1062
CreateGraphicsRobustAccessPass()1063 Optimizer::PassToken CreateGraphicsRobustAccessPass() {
1064 return MakeUnique<Optimizer::PassToken::Impl>(
1065 MakeUnique<opt::GraphicsRobustAccessPass>());
1066 }
1067
CreateReplaceDescArrayAccessUsingVarIndexPass()1068 Optimizer::PassToken CreateReplaceDescArrayAccessUsingVarIndexPass() {
1069 return MakeUnique<Optimizer::PassToken::Impl>(
1070 MakeUnique<opt::ReplaceDescArrayAccessUsingVarIndex>());
1071 }
1072
CreateSpreadVolatileSemanticsPass()1073 Optimizer::PassToken CreateSpreadVolatileSemanticsPass() {
1074 return MakeUnique<Optimizer::PassToken::Impl>(
1075 MakeUnique<opt::SpreadVolatileSemantics>());
1076 }
1077
CreateDescriptorScalarReplacementPass()1078 Optimizer::PassToken CreateDescriptorScalarReplacementPass() {
1079 return MakeUnique<Optimizer::PassToken::Impl>(
1080 MakeUnique<opt::DescriptorScalarReplacement>());
1081 }
1082
CreateWrapOpKillPass()1083 Optimizer::PassToken CreateWrapOpKillPass() {
1084 return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::WrapOpKill>());
1085 }
1086
CreateAmdExtToKhrPass()1087 Optimizer::PassToken CreateAmdExtToKhrPass() {
1088 return MakeUnique<Optimizer::PassToken::Impl>(
1089 MakeUnique<opt::AmdExtensionToKhrPass>());
1090 }
1091
CreateInterpolateFixupPass()1092 Optimizer::PassToken CreateInterpolateFixupPass() {
1093 return MakeUnique<Optimizer::PassToken::Impl>(
1094 MakeUnique<opt::InterpFixupPass>());
1095 }
1096
CreateEliminateDeadInputComponentsPass()1097 Optimizer::PassToken CreateEliminateDeadInputComponentsPass() {
1098 return MakeUnique<Optimizer::PassToken::Impl>(
1099 MakeUnique<opt::EliminateDeadIOComponentsPass>(spv::StorageClass::Input,
1100 /* safe_mode */ false));
1101 }
1102
CreateEliminateDeadOutputComponentsPass()1103 Optimizer::PassToken CreateEliminateDeadOutputComponentsPass() {
1104 return MakeUnique<Optimizer::PassToken::Impl>(
1105 MakeUnique<opt::EliminateDeadIOComponentsPass>(spv::StorageClass::Output,
1106 /* safe_mode */ false));
1107 }
1108
CreateEliminateDeadInputComponentsSafePass()1109 Optimizer::PassToken CreateEliminateDeadInputComponentsSafePass() {
1110 return MakeUnique<Optimizer::PassToken::Impl>(
1111 MakeUnique<opt::EliminateDeadIOComponentsPass>(spv::StorageClass::Input,
1112 /* safe_mode */ true));
1113 }
1114
CreateAnalyzeLiveInputPass(std::unordered_set<uint32_t> * live_locs,std::unordered_set<uint32_t> * live_builtins)1115 Optimizer::PassToken CreateAnalyzeLiveInputPass(
1116 std::unordered_set<uint32_t>* live_locs,
1117 std::unordered_set<uint32_t>* live_builtins) {
1118 return MakeUnique<Optimizer::PassToken::Impl>(
1119 MakeUnique<opt::AnalyzeLiveInputPass>(live_locs, live_builtins));
1120 }
1121
CreateEliminateDeadOutputStoresPass(std::unordered_set<uint32_t> * live_locs,std::unordered_set<uint32_t> * live_builtins)1122 Optimizer::PassToken CreateEliminateDeadOutputStoresPass(
1123 std::unordered_set<uint32_t>* live_locs,
1124 std::unordered_set<uint32_t>* live_builtins) {
1125 return MakeUnique<Optimizer::PassToken::Impl>(
1126 MakeUnique<opt::EliminateDeadOutputStoresPass>(live_locs, live_builtins));
1127 }
1128
CreateConvertToSampledImagePass(const std::vector<opt::DescriptorSetAndBinding> & descriptor_set_binding_pairs)1129 Optimizer::PassToken CreateConvertToSampledImagePass(
1130 const std::vector<opt::DescriptorSetAndBinding>&
1131 descriptor_set_binding_pairs) {
1132 return MakeUnique<Optimizer::PassToken::Impl>(
1133 MakeUnique<opt::ConvertToSampledImagePass>(descriptor_set_binding_pairs));
1134 }
1135
CreateInterfaceVariableScalarReplacementPass()1136 Optimizer::PassToken CreateInterfaceVariableScalarReplacementPass() {
1137 return MakeUnique<Optimizer::PassToken::Impl>(
1138 MakeUnique<opt::InterfaceVariableScalarReplacement>());
1139 }
1140
CreateRemoveDontInlinePass()1141 Optimizer::PassToken CreateRemoveDontInlinePass() {
1142 return MakeUnique<Optimizer::PassToken::Impl>(
1143 MakeUnique<opt::RemoveDontInline>());
1144 }
1145
CreateFixFuncCallArgumentsPass()1146 Optimizer::PassToken CreateFixFuncCallArgumentsPass() {
1147 return MakeUnique<Optimizer::PassToken::Impl>(
1148 MakeUnique<opt::FixFuncCallArgumentsPass>());
1149 }
1150
CreateTrimCapabilitiesPass()1151 Optimizer::PassToken CreateTrimCapabilitiesPass() {
1152 return MakeUnique<Optimizer::PassToken::Impl>(
1153 MakeUnique<opt::TrimCapabilitiesPass>());
1154 }
1155
CreateSwitchDescriptorSetPass(uint32_t from,uint32_t to)1156 Optimizer::PassToken CreateSwitchDescriptorSetPass(uint32_t from, uint32_t to) {
1157 return MakeUnique<Optimizer::PassToken::Impl>(
1158 MakeUnique<opt::SwitchDescriptorSetPass>(from, to));
1159 }
1160
CreateInvocationInterlockPlacementPass()1161 Optimizer::PassToken CreateInvocationInterlockPlacementPass() {
1162 return MakeUnique<Optimizer::PassToken::Impl>(
1163 MakeUnique<opt::InvocationInterlockPlacementPass>());
1164 }
1165
CreateModifyMaximalReconvergencePass(bool add)1166 Optimizer::PassToken CreateModifyMaximalReconvergencePass(bool add) {
1167 return MakeUnique<Optimizer::PassToken::Impl>(
1168 MakeUnique<opt::ModifyMaximalReconvergence>(add));
1169 }
1170 } // namespace spvtools
1171
1172 extern "C" {
1173
spvOptimizerCreate(spv_target_env env)1174 SPIRV_TOOLS_EXPORT spv_optimizer_t* spvOptimizerCreate(spv_target_env env) {
1175 return reinterpret_cast<spv_optimizer_t*>(new spvtools::Optimizer(env));
1176 }
1177
spvOptimizerDestroy(spv_optimizer_t * optimizer)1178 SPIRV_TOOLS_EXPORT void spvOptimizerDestroy(spv_optimizer_t* optimizer) {
1179 delete reinterpret_cast<spvtools::Optimizer*>(optimizer);
1180 }
1181
spvOptimizerSetMessageConsumer(spv_optimizer_t * optimizer,spv_message_consumer consumer)1182 SPIRV_TOOLS_EXPORT void spvOptimizerSetMessageConsumer(
1183 spv_optimizer_t* optimizer, spv_message_consumer consumer) {
1184 reinterpret_cast<spvtools::Optimizer*>(optimizer)->
1185 SetMessageConsumer(
1186 [consumer](spv_message_level_t level, const char* source,
1187 const spv_position_t& position, const char* message) {
1188 return consumer(level, source, &position, message);
1189 });
1190 }
1191
spvOptimizerRegisterLegalizationPasses(spv_optimizer_t * optimizer)1192 SPIRV_TOOLS_EXPORT void spvOptimizerRegisterLegalizationPasses(
1193 spv_optimizer_t* optimizer) {
1194 reinterpret_cast<spvtools::Optimizer*>(optimizer)->
1195 RegisterLegalizationPasses();
1196 }
1197
spvOptimizerRegisterPerformancePasses(spv_optimizer_t * optimizer)1198 SPIRV_TOOLS_EXPORT void spvOptimizerRegisterPerformancePasses(
1199 spv_optimizer_t* optimizer) {
1200 reinterpret_cast<spvtools::Optimizer*>(optimizer)->
1201 RegisterPerformancePasses();
1202 }
1203
spvOptimizerRegisterSizePasses(spv_optimizer_t * optimizer)1204 SPIRV_TOOLS_EXPORT void spvOptimizerRegisterSizePasses(
1205 spv_optimizer_t* optimizer) {
1206 reinterpret_cast<spvtools::Optimizer*>(optimizer)->RegisterSizePasses();
1207 }
1208
spvOptimizerRegisterPassFromFlag(spv_optimizer_t * optimizer,const char * flag)1209 SPIRV_TOOLS_EXPORT bool spvOptimizerRegisterPassFromFlag(
1210 spv_optimizer_t* optimizer, const char* flag)
1211 {
1212 return reinterpret_cast<spvtools::Optimizer*>(optimizer)->
1213 RegisterPassFromFlag(flag);
1214 }
1215
spvOptimizerRegisterPassesFromFlags(spv_optimizer_t * optimizer,const char ** flags,const size_t flag_count)1216 SPIRV_TOOLS_EXPORT bool spvOptimizerRegisterPassesFromFlags(
1217 spv_optimizer_t* optimizer, const char** flags, const size_t flag_count) {
1218 std::vector<std::string> opt_flags =
1219 spvtools::GetVectorOfStrings(flags, flag_count);
1220 return reinterpret_cast<spvtools::Optimizer*>(optimizer)
1221 ->RegisterPassesFromFlags(opt_flags, false);
1222 }
1223
1224 SPIRV_TOOLS_EXPORT bool
spvOptimizerRegisterPassesFromFlagsWhilePreservingTheInterface(spv_optimizer_t * optimizer,const char ** flags,const size_t flag_count)1225 spvOptimizerRegisterPassesFromFlagsWhilePreservingTheInterface(
1226 spv_optimizer_t* optimizer, const char** flags, const size_t flag_count) {
1227 std::vector<std::string> opt_flags =
1228 spvtools::GetVectorOfStrings(flags, flag_count);
1229 return reinterpret_cast<spvtools::Optimizer*>(optimizer)
1230 ->RegisterPassesFromFlags(opt_flags, true);
1231 }
1232
1233 SPIRV_TOOLS_EXPORT
spvOptimizerRun(spv_optimizer_t * optimizer,const uint32_t * binary,const size_t word_count,spv_binary * optimized_binary,const spv_optimizer_options options)1234 spv_result_t spvOptimizerRun(spv_optimizer_t* optimizer,
1235 const uint32_t* binary,
1236 const size_t word_count,
1237 spv_binary* optimized_binary,
1238 const spv_optimizer_options options) {
1239 std::vector<uint32_t> optimized;
1240
1241 if (!reinterpret_cast<spvtools::Optimizer*>(optimizer)->
1242 Run(binary, word_count, &optimized, options)) {
1243 return SPV_ERROR_INTERNAL;
1244 }
1245
1246 auto result_binary = new spv_binary_t();
1247 if (!result_binary) {
1248 *optimized_binary = nullptr;
1249 return SPV_ERROR_OUT_OF_MEMORY;
1250 }
1251
1252 result_binary->code = new uint32_t[optimized.size()];
1253 if (!result_binary->code) {
1254 delete result_binary;
1255 *optimized_binary = nullptr;
1256 return SPV_ERROR_OUT_OF_MEMORY;
1257 }
1258 result_binary->wordCount = optimized.size();
1259
1260 memcpy(result_binary->code, optimized.data(),
1261 optimized.size() * sizeof(uint32_t));
1262
1263 *optimized_binary = result_binary;
1264
1265 return SPV_SUCCESS;
1266 }
1267
1268 } // extern "C"
1269