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