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