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 <memory> |
19 | #include <string> |
20 | #include <unordered_map> |
21 | #include <utility> |
22 | #include <vector> |
23 | |
24 | #include "source/opt/build_module.h" |
25 | #include "source/opt/graphics_robust_access_pass.h" |
26 | #include "source/opt/log.h" |
27 | #include "source/opt/pass_manager.h" |
28 | #include "source/opt/passes.h" |
29 | #include "source/spirv_optimizer_options.h" |
30 | #include "source/util/make_unique.h" |
31 | #include "source/util/string_utils.h" |
32 | |
33 | namespace spvtools { |
34 | |
35 | struct Optimizer::PassToken::Impl { |
36 | Impl(std::unique_ptr<opt::Pass> p) : pass(std::move(p)) {} |
37 | |
38 | std::unique_ptr<opt::Pass> pass; // Internal implementation pass. |
39 | }; |
40 | |
41 | Optimizer::PassToken::PassToken( |
42 | std::unique_ptr<Optimizer::PassToken::Impl> impl) |
43 | : impl_(std::move(impl)) {} |
44 | |
45 | Optimizer::PassToken::PassToken(std::unique_ptr<opt::Pass>&& pass) |
46 | : impl_(MakeUnique<Optimizer::PassToken::Impl>(std::move(pass))) {} |
47 | |
48 | Optimizer::PassToken::PassToken(PassToken&& that) |
49 | : impl_(std::move(that.impl_)) {} |
50 | |
51 | Optimizer::PassToken& Optimizer::PassToken::operator=(PassToken&& that) { |
52 | impl_ = std::move(that.impl_); |
53 | return *this; |
54 | } |
55 | |
56 | Optimizer::PassToken::~PassToken() {} |
57 | |
58 | struct Optimizer::Impl { |
59 | explicit Impl(spv_target_env env) : target_env(env), pass_manager() {} |
60 | |
61 | spv_target_env target_env; // Target environment. |
62 | opt::PassManager pass_manager; // Internal implementation pass manager. |
63 | }; |
64 | |
65 | Optimizer::Optimizer(spv_target_env env) : impl_(new Impl(env)) {} |
66 | |
67 | Optimizer::~Optimizer() {} |
68 | |
69 | void Optimizer::SetMessageConsumer(MessageConsumer c) { |
70 | // All passes' message consumer needs to be updated. |
71 | for (uint32_t i = 0; i < impl_->pass_manager.NumPasses(); ++i) { |
72 | impl_->pass_manager.GetPass(i)->SetMessageConsumer(c); |
73 | } |
74 | impl_->pass_manager.SetMessageConsumer(std::move(c)); |
75 | } |
76 | |
77 | const MessageConsumer& Optimizer::consumer() const { |
78 | return impl_->pass_manager.consumer(); |
79 | } |
80 | |
81 | Optimizer& Optimizer::RegisterPass(PassToken&& p) { |
82 | // Change to use the pass manager's consumer. |
83 | p.impl_->pass->SetMessageConsumer(consumer()); |
84 | impl_->pass_manager.AddPass(std::move(p.impl_->pass)); |
85 | return *this; |
86 | } |
87 | |
88 | // The legalization passes take a spir-v shader generated by an HLSL front-end |
89 | // and turn it into a valid vulkan spir-v shader. There are two ways in which |
90 | // the code will be invalid at the start: |
91 | // |
92 | // 1) There will be opaque objects, like images, which will be passed around |
93 | // in intermediate objects. Valid spir-v will have to replace the use of |
94 | // the opaque object with an intermediate object that is the result of the |
95 | // load of the global opaque object. |
96 | // |
97 | // 2) There will be variables that contain pointers to structured or uniform |
98 | // buffers. It be legal, the variables must be eliminated, and the |
99 | // references to the structured buffers must use the result of OpVariable |
100 | // in the Uniform storage class. |
101 | // |
102 | // Optimization in this list must accept shaders with these relaxation of the |
103 | // rules. There is not guarantee that this list of optimizations is able to |
104 | // legalize all inputs, but it is on a best effort basis. |
105 | // |
106 | // The legalization problem is essentially a very general copy propagation |
107 | // problem. The optimization we use are all used to either do copy propagation |
108 | // or enable more copy propagation. |
109 | Optimizer& Optimizer::RegisterLegalizationPasses() { |
110 | return |
111 | // Wrap OpKill instructions so all other code can be inlined. |
112 | RegisterPass(CreateWrapOpKillPass()) |
113 | // Remove unreachable block so that merge return works. |
114 | .RegisterPass(CreateDeadBranchElimPass()) |
115 | // Merge the returns so we can inline. |
116 | .RegisterPass(CreateMergeReturnPass()) |
117 | // Make sure uses and definitions are in the same function. |
118 | .RegisterPass(CreateInlineExhaustivePass()) |
119 | // Make private variable function scope |
120 | .RegisterPass(CreateEliminateDeadFunctionsPass()) |
121 | .RegisterPass(CreatePrivateToLocalPass()) |
122 | // Fix up the storage classes that DXC may have purposely generated |
123 | // incorrectly. All functions are inlined, and a lot of dead code has |
124 | // been removed. |
125 | .RegisterPass(CreateFixStorageClassPass()) |
126 | // Propagate the value stored to the loads in very simple cases. |
127 | .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass()) |
128 | .RegisterPass(CreateLocalSingleStoreElimPass()) |
129 | .RegisterPass(CreateAggressiveDCEPass()) |
130 | // Split up aggregates so they are easier to deal with. |
131 | .RegisterPass(CreateScalarReplacementPass(0)) |
132 | // Remove loads and stores so everything is in intermediate values. |
133 | // Takes care of copy propagation of non-members. |
134 | .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass()) |
135 | .RegisterPass(CreateLocalSingleStoreElimPass()) |
136 | .RegisterPass(CreateAggressiveDCEPass()) |
137 | .RegisterPass(CreateLocalMultiStoreElimPass()) |
138 | .RegisterPass(CreateAggressiveDCEPass()) |
139 | // Propagate constants to get as many constant conditions on branches |
140 | // as possible. |
141 | .RegisterPass(CreateCCPPass()) |
142 | .RegisterPass(CreateLoopUnrollPass(true)) |
143 | .RegisterPass(CreateDeadBranchElimPass()) |
144 | // Copy propagate members. Cleans up code sequences generated by |
145 | // scalar replacement. Also important for removing OpPhi nodes. |
146 | .RegisterPass(CreateSimplificationPass()) |
147 | .RegisterPass(CreateAggressiveDCEPass()) |
148 | .RegisterPass(CreateCopyPropagateArraysPass()) |
149 | // May need loop unrolling here see |
150 | // https://github.com/Microsoft/DirectXShaderCompiler/pull/930 |
151 | // Get rid of unused code that contain traces of illegal code |
152 | // or unused references to unbound external objects |
153 | .RegisterPass(CreateVectorDCEPass()) |
154 | .RegisterPass(CreateDeadInsertElimPass()) |
155 | .RegisterPass(CreateReduceLoadSizePass()) |
156 | .RegisterPass(CreateAggressiveDCEPass()); |
157 | } |
158 | |
159 | Optimizer& Optimizer::RegisterPerformancePasses() { |
160 | return RegisterPass(CreateWrapOpKillPass()) |
161 | .RegisterPass(CreateDeadBranchElimPass()) |
162 | .RegisterPass(CreateMergeReturnPass()) |
163 | .RegisterPass(CreateInlineExhaustivePass()) |
164 | .RegisterPass(CreateAggressiveDCEPass()) |
165 | .RegisterPass(CreatePrivateToLocalPass()) |
166 | .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass()) |
167 | .RegisterPass(CreateLocalSingleStoreElimPass()) |
168 | .RegisterPass(CreateAggressiveDCEPass()) |
169 | .RegisterPass(CreateScalarReplacementPass()) |
170 | .RegisterPass(CreateLocalAccessChainConvertPass()) |
171 | .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass()) |
172 | .RegisterPass(CreateLocalSingleStoreElimPass()) |
173 | .RegisterPass(CreateAggressiveDCEPass()) |
174 | .RegisterPass(CreateLocalMultiStoreElimPass()) |
175 | .RegisterPass(CreateAggressiveDCEPass()) |
176 | .RegisterPass(CreateCCPPass()) |
177 | .RegisterPass(CreateAggressiveDCEPass()) |
178 | .RegisterPass(CreateRedundancyEliminationPass()) |
179 | .RegisterPass(CreateCombineAccessChainsPass()) |
180 | .RegisterPass(CreateSimplificationPass()) |
181 | .RegisterPass(CreateVectorDCEPass()) |
182 | .RegisterPass(CreateDeadInsertElimPass()) |
183 | .RegisterPass(CreateDeadBranchElimPass()) |
184 | .RegisterPass(CreateSimplificationPass()) |
185 | .RegisterPass(CreateIfConversionPass()) |
186 | .RegisterPass(CreateCopyPropagateArraysPass()) |
187 | .RegisterPass(CreateReduceLoadSizePass()) |
188 | .RegisterPass(CreateAggressiveDCEPass()) |
189 | .RegisterPass(CreateBlockMergePass()) |
190 | .RegisterPass(CreateRedundancyEliminationPass()) |
191 | .RegisterPass(CreateDeadBranchElimPass()) |
192 | .RegisterPass(CreateBlockMergePass()) |
193 | .RegisterPass(CreateSimplificationPass()); |
194 | } |
195 | |
196 | Optimizer& Optimizer::RegisterSizePasses() { |
197 | return RegisterPass(CreateWrapOpKillPass()) |
198 | .RegisterPass(CreateDeadBranchElimPass()) |
199 | .RegisterPass(CreateMergeReturnPass()) |
200 | .RegisterPass(CreateInlineExhaustivePass()) |
201 | .RegisterPass(CreateEliminateDeadFunctionsPass()) |
202 | .RegisterPass(CreatePrivateToLocalPass()) |
203 | .RegisterPass(CreateScalarReplacementPass(0)) |
204 | .RegisterPass(CreateLocalMultiStoreElimPass()) |
205 | .RegisterPass(CreateCCPPass()) |
206 | .RegisterPass(CreateLoopUnrollPass(true)) |
207 | .RegisterPass(CreateDeadBranchElimPass()) |
208 | .RegisterPass(CreateSimplificationPass()) |
209 | .RegisterPass(CreateScalarReplacementPass(0)) |
210 | .RegisterPass(CreateLocalSingleStoreElimPass()) |
211 | .RegisterPass(CreateIfConversionPass()) |
212 | .RegisterPass(CreateSimplificationPass()) |
213 | .RegisterPass(CreateAggressiveDCEPass()) |
214 | .RegisterPass(CreateDeadBranchElimPass()) |
215 | .RegisterPass(CreateBlockMergePass()) |
216 | .RegisterPass(CreateLocalAccessChainConvertPass()) |
217 | .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass()) |
218 | .RegisterPass(CreateAggressiveDCEPass()) |
219 | .RegisterPass(CreateCopyPropagateArraysPass()) |
220 | .RegisterPass(CreateVectorDCEPass()) |
221 | .RegisterPass(CreateDeadInsertElimPass()) |
222 | .RegisterPass(CreateEliminateDeadMembersPass()) |
223 | .RegisterPass(CreateLocalSingleStoreElimPass()) |
224 | .RegisterPass(CreateBlockMergePass()) |
225 | .RegisterPass(CreateLocalMultiStoreElimPass()) |
226 | .RegisterPass(CreateRedundancyEliminationPass()) |
227 | .RegisterPass(CreateSimplificationPass()) |
228 | .RegisterPass(CreateAggressiveDCEPass()) |
229 | .RegisterPass(CreateCFGCleanupPass()); |
230 | } |
231 | |
232 | Optimizer& Optimizer::RegisterVulkanToWebGPUPasses() { |
233 | return RegisterPass(CreateStripAtomicCounterMemoryPass()) |
234 | .RegisterPass(CreateGenerateWebGPUInitializersPass()) |
235 | .RegisterPass(CreateLegalizeVectorShufflePass()) |
236 | .RegisterPass(CreateSplitInvalidUnreachablePass()) |
237 | .RegisterPass(CreateEliminateDeadConstantPass()) |
238 | .RegisterPass(CreateFlattenDecorationPass()) |
239 | .RegisterPass(CreateAggressiveDCEPass()) |
240 | .RegisterPass(CreateDeadBranchElimPass()) |
241 | .RegisterPass(CreateCompactIdsPass()); |
242 | } |
243 | |
244 | Optimizer& Optimizer::RegisterWebGPUToVulkanPasses() { |
245 | return RegisterPass(CreateDecomposeInitializedVariablesPass()) |
246 | .RegisterPass(CreateCompactIdsPass()); |
247 | } |
248 | |
249 | bool Optimizer::RegisterPassesFromFlags(const std::vector<std::string>& flags) { |
250 | for (const auto& flag : flags) { |
251 | if (!RegisterPassFromFlag(flag)) { |
252 | return false; |
253 | } |
254 | } |
255 | |
256 | return true; |
257 | } |
258 | |
259 | bool Optimizer::FlagHasValidForm(const std::string& flag) const { |
260 | if (flag == "-O" || flag == "-Os" ) { |
261 | return true; |
262 | } else if (flag.size() > 2 && flag.substr(0, 2) == "--" ) { |
263 | return true; |
264 | } |
265 | |
266 | Errorf(consumer(), nullptr, {}, |
267 | "%s is not a valid flag. Flag passes should have the form " |
268 | "'--pass_name[=pass_args]'. Special flag names also accepted: -O " |
269 | "and -Os." , |
270 | flag.c_str()); |
271 | return false; |
272 | } |
273 | |
274 | bool Optimizer::RegisterPassFromFlag(const std::string& flag) { |
275 | if (!FlagHasValidForm(flag)) { |
276 | return false; |
277 | } |
278 | |
279 | // Split flags of the form --pass_name=pass_args. |
280 | auto p = utils::SplitFlagArgs(flag); |
281 | std::string pass_name = p.first; |
282 | std::string pass_args = p.second; |
283 | |
284 | // FIXME(dnovillo): This should be re-factored so that pass names can be |
285 | // automatically checked against Pass::name() and PassToken instances created |
286 | // via a template function. Additionally, class Pass should have a desc() |
287 | // method that describes the pass (so it can be used in --help). |
288 | // |
289 | // Both Pass::name() and Pass::desc() should be static class members so they |
290 | // can be invoked without creating a pass instance. |
291 | if (pass_name == "strip-atomic-counter-memory" ) { |
292 | RegisterPass(CreateStripAtomicCounterMemoryPass()); |
293 | } else if (pass_name == "strip-debug" ) { |
294 | RegisterPass(CreateStripDebugInfoPass()); |
295 | } else if (pass_name == "strip-reflect" ) { |
296 | RegisterPass(CreateStripReflectInfoPass()); |
297 | } else if (pass_name == "set-spec-const-default-value" ) { |
298 | if (pass_args.size() > 0) { |
299 | auto spec_ids_vals = |
300 | opt::SetSpecConstantDefaultValuePass::ParseDefaultValuesString( |
301 | pass_args.c_str()); |
302 | if (!spec_ids_vals) { |
303 | Errorf(consumer(), nullptr, {}, |
304 | "Invalid argument for --set-spec-const-default-value: %s" , |
305 | pass_args.c_str()); |
306 | return false; |
307 | } |
308 | RegisterPass( |
309 | CreateSetSpecConstantDefaultValuePass(std::move(*spec_ids_vals))); |
310 | } else { |
311 | Errorf(consumer(), nullptr, {}, |
312 | "Invalid spec constant value string '%s'. Expected a string of " |
313 | "<spec id>:<default value> pairs." , |
314 | pass_args.c_str()); |
315 | return false; |
316 | } |
317 | } else if (pass_name == "if-conversion" ) { |
318 | RegisterPass(CreateIfConversionPass()); |
319 | } else if (pass_name == "freeze-spec-const" ) { |
320 | RegisterPass(CreateFreezeSpecConstantValuePass()); |
321 | } else if (pass_name == "inline-entry-points-exhaustive" ) { |
322 | RegisterPass(CreateInlineExhaustivePass()); |
323 | } else if (pass_name == "inline-entry-points-opaque" ) { |
324 | RegisterPass(CreateInlineOpaquePass()); |
325 | } else if (pass_name == "combine-access-chains" ) { |
326 | RegisterPass(CreateCombineAccessChainsPass()); |
327 | } else if (pass_name == "convert-local-access-chains" ) { |
328 | RegisterPass(CreateLocalAccessChainConvertPass()); |
329 | } else if (pass_name == "descriptor-scalar-replacement" ) { |
330 | RegisterPass(CreateDescriptorScalarReplacementPass()); |
331 | } else if (pass_name == "eliminate-dead-code-aggressive" ) { |
332 | RegisterPass(CreateAggressiveDCEPass()); |
333 | } else if (pass_name == "propagate-line-info" ) { |
334 | RegisterPass(CreatePropagateLineInfoPass()); |
335 | } else if (pass_name == "eliminate-redundant-line-info" ) { |
336 | RegisterPass(CreateRedundantLineInfoElimPass()); |
337 | } else if (pass_name == "eliminate-insert-extract" ) { |
338 | RegisterPass(CreateInsertExtractElimPass()); |
339 | } else if (pass_name == "eliminate-local-single-block" ) { |
340 | RegisterPass(CreateLocalSingleBlockLoadStoreElimPass()); |
341 | } else if (pass_name == "eliminate-local-single-store" ) { |
342 | RegisterPass(CreateLocalSingleStoreElimPass()); |
343 | } else if (pass_name == "merge-blocks" ) { |
344 | RegisterPass(CreateBlockMergePass()); |
345 | } else if (pass_name == "merge-return" ) { |
346 | RegisterPass(CreateMergeReturnPass()); |
347 | } else if (pass_name == "eliminate-dead-branches" ) { |
348 | RegisterPass(CreateDeadBranchElimPass()); |
349 | } else if (pass_name == "eliminate-dead-functions" ) { |
350 | RegisterPass(CreateEliminateDeadFunctionsPass()); |
351 | } else if (pass_name == "eliminate-local-multi-store" ) { |
352 | RegisterPass(CreateLocalMultiStoreElimPass()); |
353 | } else if (pass_name == "eliminate-dead-const" ) { |
354 | RegisterPass(CreateEliminateDeadConstantPass()); |
355 | } else if (pass_name == "eliminate-dead-inserts" ) { |
356 | RegisterPass(CreateDeadInsertElimPass()); |
357 | } else if (pass_name == "eliminate-dead-variables" ) { |
358 | RegisterPass(CreateDeadVariableEliminationPass()); |
359 | } else if (pass_name == "eliminate-dead-members" ) { |
360 | RegisterPass(CreateEliminateDeadMembersPass()); |
361 | } else if (pass_name == "fold-spec-const-op-composite" ) { |
362 | RegisterPass(CreateFoldSpecConstantOpAndCompositePass()); |
363 | } else if (pass_name == "loop-unswitch" ) { |
364 | RegisterPass(CreateLoopUnswitchPass()); |
365 | } else if (pass_name == "scalar-replacement" ) { |
366 | if (pass_args.size() == 0) { |
367 | RegisterPass(CreateScalarReplacementPass()); |
368 | } else { |
369 | int limit = -1; |
370 | if (pass_args.find_first_not_of("0123456789" ) == std::string::npos) { |
371 | limit = atoi(pass_args.c_str()); |
372 | } |
373 | |
374 | if (limit >= 0) { |
375 | RegisterPass(CreateScalarReplacementPass(limit)); |
376 | } else { |
377 | Error(consumer(), nullptr, {}, |
378 | "--scalar-replacement must have no arguments or a non-negative " |
379 | "integer argument" ); |
380 | return false; |
381 | } |
382 | } |
383 | } else if (pass_name == "strength-reduction" ) { |
384 | RegisterPass(CreateStrengthReductionPass()); |
385 | } else if (pass_name == "unify-const" ) { |
386 | RegisterPass(CreateUnifyConstantPass()); |
387 | } else if (pass_name == "flatten-decorations" ) { |
388 | RegisterPass(CreateFlattenDecorationPass()); |
389 | } else if (pass_name == "compact-ids" ) { |
390 | RegisterPass(CreateCompactIdsPass()); |
391 | } else if (pass_name == "cfg-cleanup" ) { |
392 | RegisterPass(CreateCFGCleanupPass()); |
393 | } else if (pass_name == "local-redundancy-elimination" ) { |
394 | RegisterPass(CreateLocalRedundancyEliminationPass()); |
395 | } else if (pass_name == "loop-invariant-code-motion" ) { |
396 | RegisterPass(CreateLoopInvariantCodeMotionPass()); |
397 | } else if (pass_name == "reduce-load-size" ) { |
398 | RegisterPass(CreateReduceLoadSizePass()); |
399 | } else if (pass_name == "redundancy-elimination" ) { |
400 | RegisterPass(CreateRedundancyEliminationPass()); |
401 | } else if (pass_name == "private-to-local" ) { |
402 | RegisterPass(CreatePrivateToLocalPass()); |
403 | } else if (pass_name == "remove-duplicates" ) { |
404 | RegisterPass(CreateRemoveDuplicatesPass()); |
405 | } else if (pass_name == "workaround-1209" ) { |
406 | RegisterPass(CreateWorkaround1209Pass()); |
407 | } else if (pass_name == "replace-invalid-opcode" ) { |
408 | RegisterPass(CreateReplaceInvalidOpcodePass()); |
409 | } else if (pass_name == "inst-bindless-check" ) { |
410 | RegisterPass(CreateInstBindlessCheckPass(7, 23, false, false, 2)); |
411 | RegisterPass(CreateSimplificationPass()); |
412 | RegisterPass(CreateDeadBranchElimPass()); |
413 | RegisterPass(CreateBlockMergePass()); |
414 | RegisterPass(CreateAggressiveDCEPass()); |
415 | } else if (pass_name == "inst-desc-idx-check" ) { |
416 | RegisterPass(CreateInstBindlessCheckPass(7, 23, true, true, 2)); |
417 | RegisterPass(CreateSimplificationPass()); |
418 | RegisterPass(CreateDeadBranchElimPass()); |
419 | RegisterPass(CreateBlockMergePass()); |
420 | RegisterPass(CreateAggressiveDCEPass()); |
421 | } else if (pass_name == "inst-buff-addr-check" ) { |
422 | RegisterPass(CreateInstBuffAddrCheckPass(7, 23, 2)); |
423 | RegisterPass(CreateAggressiveDCEPass()); |
424 | } else if (pass_name == "convert-relaxed-to-half" ) { |
425 | RegisterPass(CreateConvertRelaxedToHalfPass()); |
426 | } else if (pass_name == "relax-float-ops" ) { |
427 | RegisterPass(CreateRelaxFloatOpsPass()); |
428 | } else if (pass_name == "inst-debug-printf" ) { |
429 | RegisterPass(CreateInstDebugPrintfPass(7, 23)); |
430 | } else if (pass_name == "simplify-instructions" ) { |
431 | RegisterPass(CreateSimplificationPass()); |
432 | } else if (pass_name == "ssa-rewrite" ) { |
433 | RegisterPass(CreateSSARewritePass()); |
434 | } else if (pass_name == "copy-propagate-arrays" ) { |
435 | RegisterPass(CreateCopyPropagateArraysPass()); |
436 | } else if (pass_name == "loop-fission" ) { |
437 | int register_threshold_to_split = |
438 | (pass_args.size() > 0) ? atoi(pass_args.c_str()) : -1; |
439 | if (register_threshold_to_split > 0) { |
440 | RegisterPass(CreateLoopFissionPass( |
441 | static_cast<size_t>(register_threshold_to_split))); |
442 | } else { |
443 | Error(consumer(), nullptr, {}, |
444 | "--loop-fission must have a positive integer argument" ); |
445 | return false; |
446 | } |
447 | } else if (pass_name == "loop-fusion" ) { |
448 | int max_registers_per_loop = |
449 | (pass_args.size() > 0) ? atoi(pass_args.c_str()) : -1; |
450 | if (max_registers_per_loop > 0) { |
451 | RegisterPass( |
452 | CreateLoopFusionPass(static_cast<size_t>(max_registers_per_loop))); |
453 | } else { |
454 | Error(consumer(), nullptr, {}, |
455 | "--loop-fusion must have a positive integer argument" ); |
456 | return false; |
457 | } |
458 | } else if (pass_name == "loop-unroll" ) { |
459 | RegisterPass(CreateLoopUnrollPass(true)); |
460 | } else if (pass_name == "upgrade-memory-model" ) { |
461 | RegisterPass(CreateUpgradeMemoryModelPass()); |
462 | } else if (pass_name == "vector-dce" ) { |
463 | RegisterPass(CreateVectorDCEPass()); |
464 | } else if (pass_name == "loop-unroll-partial" ) { |
465 | int factor = (pass_args.size() > 0) ? atoi(pass_args.c_str()) : 0; |
466 | if (factor > 0) { |
467 | RegisterPass(CreateLoopUnrollPass(false, factor)); |
468 | } else { |
469 | Error(consumer(), nullptr, {}, |
470 | "--loop-unroll-partial must have a positive integer argument" ); |
471 | return false; |
472 | } |
473 | } else if (pass_name == "loop-peeling" ) { |
474 | RegisterPass(CreateLoopPeelingPass()); |
475 | } else if (pass_name == "loop-peeling-threshold" ) { |
476 | int factor = (pass_args.size() > 0) ? atoi(pass_args.c_str()) : 0; |
477 | if (factor > 0) { |
478 | opt::LoopPeelingPass::SetLoopPeelingThreshold(factor); |
479 | } else { |
480 | Error(consumer(), nullptr, {}, |
481 | "--loop-peeling-threshold must have a positive integer argument" ); |
482 | return false; |
483 | } |
484 | } else if (pass_name == "ccp" ) { |
485 | RegisterPass(CreateCCPPass()); |
486 | } else if (pass_name == "code-sink" ) { |
487 | RegisterPass(CreateCodeSinkingPass()); |
488 | } else if (pass_name == "fix-storage-class" ) { |
489 | RegisterPass(CreateFixStorageClassPass()); |
490 | } else if (pass_name == "O" ) { |
491 | RegisterPerformancePasses(); |
492 | } else if (pass_name == "Os" ) { |
493 | RegisterSizePasses(); |
494 | } else if (pass_name == "legalize-hlsl" ) { |
495 | RegisterLegalizationPasses(); |
496 | } else if (pass_name == "generate-webgpu-initializers" ) { |
497 | RegisterPass(CreateGenerateWebGPUInitializersPass()); |
498 | } else if (pass_name == "legalize-vector-shuffle" ) { |
499 | RegisterPass(CreateLegalizeVectorShufflePass()); |
500 | } else if (pass_name == "split-invalid-unreachable" ) { |
501 | RegisterPass(CreateLegalizeVectorShufflePass()); |
502 | } else if (pass_name == "decompose-initialized-variables" ) { |
503 | RegisterPass(CreateDecomposeInitializedVariablesPass()); |
504 | } else if (pass_name == "graphics-robust-access" ) { |
505 | RegisterPass(CreateGraphicsRobustAccessPass()); |
506 | } else if (pass_name == "wrap-opkill" ) { |
507 | RegisterPass(CreateWrapOpKillPass()); |
508 | } else if (pass_name == "amd-ext-to-khr" ) { |
509 | RegisterPass(CreateAmdExtToKhrPass()); |
510 | } else { |
511 | Errorf(consumer(), nullptr, {}, |
512 | "Unknown flag '--%s'. Use --help for a list of valid flags" , |
513 | pass_name.c_str()); |
514 | return false; |
515 | } |
516 | |
517 | return true; |
518 | } |
519 | |
520 | void Optimizer::SetTargetEnv(const spv_target_env env) { |
521 | impl_->target_env = env; |
522 | } |
523 | |
524 | bool Optimizer::Run(const uint32_t* original_binary, |
525 | const size_t original_binary_size, |
526 | std::vector<uint32_t>* optimized_binary) const { |
527 | return Run(original_binary, original_binary_size, optimized_binary, |
528 | OptimizerOptions()); |
529 | } |
530 | |
531 | bool Optimizer::Run(const uint32_t* original_binary, |
532 | const size_t original_binary_size, |
533 | std::vector<uint32_t>* optimized_binary, |
534 | const ValidatorOptions& validator_options, |
535 | bool skip_validation) const { |
536 | OptimizerOptions opt_options; |
537 | opt_options.set_run_validator(!skip_validation); |
538 | opt_options.set_validator_options(validator_options); |
539 | return Run(original_binary, original_binary_size, optimized_binary, |
540 | opt_options); |
541 | } |
542 | |
543 | bool Optimizer::Run(const uint32_t* original_binary, |
544 | const size_t original_binary_size, |
545 | std::vector<uint32_t>* optimized_binary, |
546 | const spv_optimizer_options opt_options) const { |
547 | spvtools::SpirvTools tools(impl_->target_env); |
548 | tools.SetMessageConsumer(impl_->pass_manager.consumer()); |
549 | if (opt_options->run_validator_ && |
550 | !tools.Validate(original_binary, original_binary_size, |
551 | &opt_options->val_options_)) { |
552 | return false; |
553 | } |
554 | |
555 | std::unique_ptr<opt::IRContext> context = BuildModule( |
556 | impl_->target_env, consumer(), original_binary, original_binary_size); |
557 | if (context == nullptr) return false; |
558 | |
559 | context->set_max_id_bound(opt_options->max_id_bound_); |
560 | context->set_preserve_bindings(opt_options->preserve_bindings_); |
561 | context->set_preserve_spec_constants(opt_options->preserve_spec_constants_); |
562 | |
563 | impl_->pass_manager.SetValidatorOptions(&opt_options->val_options_); |
564 | impl_->pass_manager.SetTargetEnv(impl_->target_env); |
565 | auto status = impl_->pass_manager.Run(context.get()); |
566 | |
567 | if (status == opt::Pass::Status::Failure) { |
568 | return false; |
569 | } |
570 | |
571 | #ifndef NDEBUG |
572 | // We do not keep the result id of DebugScope in struct DebugScope. |
573 | // Instead, we assign random ids for them, which results in sanity |
574 | // check failures. We want to skip the sanity check when the module |
575 | // contains DebugScope instructions. |
576 | if (status == opt::Pass::Status::SuccessWithoutChange && |
577 | !context->module()->ContainsDebugScope()) { |
578 | std::vector<uint32_t> optimized_binary_with_nop; |
579 | context->module()->ToBinary(&optimized_binary_with_nop, |
580 | /* skip_nop = */ false); |
581 | assert(optimized_binary_with_nop.size() == original_binary_size && |
582 | "Binary size unexpectedly changed despite the optimizer saying " |
583 | "there was no change" ); |
584 | assert(memcmp(optimized_binary_with_nop.data(), original_binary, |
585 | original_binary_size) == 0 && |
586 | "Binary content unexpectedly changed despite the optimizer saying " |
587 | "there was no change" ); |
588 | } |
589 | #endif // !NDEBUG |
590 | |
591 | // Note that |original_binary| and |optimized_binary| may share the same |
592 | // buffer and the below will invalidate |original_binary|. |
593 | optimized_binary->clear(); |
594 | context->module()->ToBinary(optimized_binary, /* skip_nop = */ true); |
595 | |
596 | return true; |
597 | } |
598 | |
599 | Optimizer& Optimizer::SetPrintAll(std::ostream* out) { |
600 | impl_->pass_manager.SetPrintAll(out); |
601 | return *this; |
602 | } |
603 | |
604 | Optimizer& Optimizer::SetTimeReport(std::ostream* out) { |
605 | impl_->pass_manager.SetTimeReport(out); |
606 | return *this; |
607 | } |
608 | |
609 | Optimizer& Optimizer::SetValidateAfterAll(bool validate) { |
610 | impl_->pass_manager.SetValidateAfterAll(validate); |
611 | return *this; |
612 | } |
613 | |
614 | Optimizer::PassToken CreateNullPass() { |
615 | return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::NullPass>()); |
616 | } |
617 | |
618 | Optimizer::PassToken CreateStripAtomicCounterMemoryPass() { |
619 | return MakeUnique<Optimizer::PassToken::Impl>( |
620 | MakeUnique<opt::StripAtomicCounterMemoryPass>()); |
621 | } |
622 | |
623 | Optimizer::PassToken CreateStripDebugInfoPass() { |
624 | return MakeUnique<Optimizer::PassToken::Impl>( |
625 | MakeUnique<opt::StripDebugInfoPass>()); |
626 | } |
627 | |
628 | Optimizer::PassToken CreateStripReflectInfoPass() { |
629 | return MakeUnique<Optimizer::PassToken::Impl>( |
630 | MakeUnique<opt::StripReflectInfoPass>()); |
631 | } |
632 | |
633 | Optimizer::PassToken CreateEliminateDeadFunctionsPass() { |
634 | return MakeUnique<Optimizer::PassToken::Impl>( |
635 | MakeUnique<opt::EliminateDeadFunctionsPass>()); |
636 | } |
637 | |
638 | Optimizer::PassToken CreateEliminateDeadMembersPass() { |
639 | return MakeUnique<Optimizer::PassToken::Impl>( |
640 | MakeUnique<opt::EliminateDeadMembersPass>()); |
641 | } |
642 | |
643 | Optimizer::PassToken CreateSetSpecConstantDefaultValuePass( |
644 | const std::unordered_map<uint32_t, std::string>& id_value_map) { |
645 | return MakeUnique<Optimizer::PassToken::Impl>( |
646 | MakeUnique<opt::SetSpecConstantDefaultValuePass>(id_value_map)); |
647 | } |
648 | |
649 | Optimizer::PassToken CreateSetSpecConstantDefaultValuePass( |
650 | const std::unordered_map<uint32_t, std::vector<uint32_t>>& id_value_map) { |
651 | return MakeUnique<Optimizer::PassToken::Impl>( |
652 | MakeUnique<opt::SetSpecConstantDefaultValuePass>(id_value_map)); |
653 | } |
654 | |
655 | Optimizer::PassToken CreateFlattenDecorationPass() { |
656 | return MakeUnique<Optimizer::PassToken::Impl>( |
657 | MakeUnique<opt::FlattenDecorationPass>()); |
658 | } |
659 | |
660 | Optimizer::PassToken CreateFreezeSpecConstantValuePass() { |
661 | return MakeUnique<Optimizer::PassToken::Impl>( |
662 | MakeUnique<opt::FreezeSpecConstantValuePass>()); |
663 | } |
664 | |
665 | Optimizer::PassToken CreateFoldSpecConstantOpAndCompositePass() { |
666 | return MakeUnique<Optimizer::PassToken::Impl>( |
667 | MakeUnique<opt::FoldSpecConstantOpAndCompositePass>()); |
668 | } |
669 | |
670 | Optimizer::PassToken CreateUnifyConstantPass() { |
671 | return MakeUnique<Optimizer::PassToken::Impl>( |
672 | MakeUnique<opt::UnifyConstantPass>()); |
673 | } |
674 | |
675 | Optimizer::PassToken CreateEliminateDeadConstantPass() { |
676 | return MakeUnique<Optimizer::PassToken::Impl>( |
677 | MakeUnique<opt::EliminateDeadConstantPass>()); |
678 | } |
679 | |
680 | Optimizer::PassToken CreateDeadVariableEliminationPass() { |
681 | return MakeUnique<Optimizer::PassToken::Impl>( |
682 | MakeUnique<opt::DeadVariableElimination>()); |
683 | } |
684 | |
685 | Optimizer::PassToken CreateStrengthReductionPass() { |
686 | return MakeUnique<Optimizer::PassToken::Impl>( |
687 | MakeUnique<opt::StrengthReductionPass>()); |
688 | } |
689 | |
690 | Optimizer::PassToken CreateBlockMergePass() { |
691 | return MakeUnique<Optimizer::PassToken::Impl>( |
692 | MakeUnique<opt::BlockMergePass>()); |
693 | } |
694 | |
695 | Optimizer::PassToken CreateInlineExhaustivePass() { |
696 | return MakeUnique<Optimizer::PassToken::Impl>( |
697 | MakeUnique<opt::InlineExhaustivePass>()); |
698 | } |
699 | |
700 | Optimizer::PassToken CreateInlineOpaquePass() { |
701 | return MakeUnique<Optimizer::PassToken::Impl>( |
702 | MakeUnique<opt::InlineOpaquePass>()); |
703 | } |
704 | |
705 | Optimizer::PassToken CreateLocalAccessChainConvertPass() { |
706 | return MakeUnique<Optimizer::PassToken::Impl>( |
707 | MakeUnique<opt::LocalAccessChainConvertPass>()); |
708 | } |
709 | |
710 | Optimizer::PassToken CreateLocalSingleBlockLoadStoreElimPass() { |
711 | return MakeUnique<Optimizer::PassToken::Impl>( |
712 | MakeUnique<opt::LocalSingleBlockLoadStoreElimPass>()); |
713 | } |
714 | |
715 | Optimizer::PassToken CreateLocalSingleStoreElimPass() { |
716 | return MakeUnique<Optimizer::PassToken::Impl>( |
717 | MakeUnique<opt::LocalSingleStoreElimPass>()); |
718 | } |
719 | |
720 | Optimizer::PassToken () { |
721 | return MakeUnique<Optimizer::PassToken::Impl>( |
722 | MakeUnique<opt::SimplificationPass>()); |
723 | } |
724 | |
725 | Optimizer::PassToken CreateDeadInsertElimPass() { |
726 | return MakeUnique<Optimizer::PassToken::Impl>( |
727 | MakeUnique<opt::DeadInsertElimPass>()); |
728 | } |
729 | |
730 | Optimizer::PassToken CreateDeadBranchElimPass() { |
731 | return MakeUnique<Optimizer::PassToken::Impl>( |
732 | MakeUnique<opt::DeadBranchElimPass>()); |
733 | } |
734 | |
735 | Optimizer::PassToken CreateLocalMultiStoreElimPass() { |
736 | return MakeUnique<Optimizer::PassToken::Impl>( |
737 | MakeUnique<opt::SSARewritePass>()); |
738 | } |
739 | |
740 | Optimizer::PassToken CreateAggressiveDCEPass() { |
741 | return MakeUnique<Optimizer::PassToken::Impl>( |
742 | MakeUnique<opt::AggressiveDCEPass>()); |
743 | } |
744 | |
745 | Optimizer::PassToken CreatePropagateLineInfoPass() { |
746 | return MakeUnique<Optimizer::PassToken::Impl>( |
747 | MakeUnique<opt::ProcessLinesPass>(opt::kLinesPropagateLines)); |
748 | } |
749 | |
750 | Optimizer::PassToken CreateRedundantLineInfoElimPass() { |
751 | return MakeUnique<Optimizer::PassToken::Impl>( |
752 | MakeUnique<opt::ProcessLinesPass>(opt::kLinesEliminateDeadLines)); |
753 | } |
754 | |
755 | Optimizer::PassToken CreateCompactIdsPass() { |
756 | return MakeUnique<Optimizer::PassToken::Impl>( |
757 | MakeUnique<opt::CompactIdsPass>()); |
758 | } |
759 | |
760 | Optimizer::PassToken CreateMergeReturnPass() { |
761 | return MakeUnique<Optimizer::PassToken::Impl>( |
762 | MakeUnique<opt::MergeReturnPass>()); |
763 | } |
764 | |
765 | std::vector<const char*> Optimizer::GetPassNames() const { |
766 | std::vector<const char*> v; |
767 | for (uint32_t i = 0; i < impl_->pass_manager.NumPasses(); i++) { |
768 | v.push_back(impl_->pass_manager.GetPass(i)->name()); |
769 | } |
770 | return v; |
771 | } |
772 | |
773 | Optimizer::PassToken CreateCFGCleanupPass() { |
774 | return MakeUnique<Optimizer::PassToken::Impl>( |
775 | MakeUnique<opt::CFGCleanupPass>()); |
776 | } |
777 | |
778 | Optimizer::PassToken CreateLocalRedundancyEliminationPass() { |
779 | return MakeUnique<Optimizer::PassToken::Impl>( |
780 | MakeUnique<opt::LocalRedundancyEliminationPass>()); |
781 | } |
782 | |
783 | Optimizer::PassToken CreateLoopFissionPass(size_t threshold) { |
784 | return MakeUnique<Optimizer::PassToken::Impl>( |
785 | MakeUnique<opt::LoopFissionPass>(threshold)); |
786 | } |
787 | |
788 | Optimizer::PassToken CreateLoopFusionPass(size_t max_registers_per_loop) { |
789 | return MakeUnique<Optimizer::PassToken::Impl>( |
790 | MakeUnique<opt::LoopFusionPass>(max_registers_per_loop)); |
791 | } |
792 | |
793 | Optimizer::PassToken CreateLoopInvariantCodeMotionPass() { |
794 | return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::LICMPass>()); |
795 | } |
796 | |
797 | Optimizer::PassToken CreateLoopPeelingPass() { |
798 | return MakeUnique<Optimizer::PassToken::Impl>( |
799 | MakeUnique<opt::LoopPeelingPass>()); |
800 | } |
801 | |
802 | Optimizer::PassToken CreateLoopUnswitchPass() { |
803 | return MakeUnique<Optimizer::PassToken::Impl>( |
804 | MakeUnique<opt::LoopUnswitchPass>()); |
805 | } |
806 | |
807 | Optimizer::PassToken CreateRedundancyEliminationPass() { |
808 | return MakeUnique<Optimizer::PassToken::Impl>( |
809 | MakeUnique<opt::RedundancyEliminationPass>()); |
810 | } |
811 | |
812 | Optimizer::PassToken CreateRemoveDuplicatesPass() { |
813 | return MakeUnique<Optimizer::PassToken::Impl>( |
814 | MakeUnique<opt::RemoveDuplicatesPass>()); |
815 | } |
816 | |
817 | Optimizer::PassToken CreateScalarReplacementPass(uint32_t size_limit) { |
818 | return MakeUnique<Optimizer::PassToken::Impl>( |
819 | MakeUnique<opt::ScalarReplacementPass>(size_limit)); |
820 | } |
821 | |
822 | Optimizer::PassToken CreatePrivateToLocalPass() { |
823 | return MakeUnique<Optimizer::PassToken::Impl>( |
824 | MakeUnique<opt::PrivateToLocalPass>()); |
825 | } |
826 | |
827 | Optimizer::PassToken CreateCCPPass() { |
828 | return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::CCPPass>()); |
829 | } |
830 | |
831 | Optimizer::PassToken CreateWorkaround1209Pass() { |
832 | return MakeUnique<Optimizer::PassToken::Impl>( |
833 | MakeUnique<opt::Workaround1209>()); |
834 | } |
835 | |
836 | Optimizer::PassToken CreateIfConversionPass() { |
837 | return MakeUnique<Optimizer::PassToken::Impl>( |
838 | MakeUnique<opt::IfConversion>()); |
839 | } |
840 | |
841 | Optimizer::PassToken CreateReplaceInvalidOpcodePass() { |
842 | return MakeUnique<Optimizer::PassToken::Impl>( |
843 | MakeUnique<opt::ReplaceInvalidOpcodePass>()); |
844 | } |
845 | |
846 | Optimizer::PassToken CreateSimplificationPass() { |
847 | return MakeUnique<Optimizer::PassToken::Impl>( |
848 | MakeUnique<opt::SimplificationPass>()); |
849 | } |
850 | |
851 | Optimizer::PassToken CreateLoopUnrollPass(bool fully_unroll, int factor) { |
852 | return MakeUnique<Optimizer::PassToken::Impl>( |
853 | MakeUnique<opt::LoopUnroller>(fully_unroll, factor)); |
854 | } |
855 | |
856 | Optimizer::PassToken CreateSSARewritePass() { |
857 | return MakeUnique<Optimizer::PassToken::Impl>( |
858 | MakeUnique<opt::SSARewritePass>()); |
859 | } |
860 | |
861 | Optimizer::PassToken CreateCopyPropagateArraysPass() { |
862 | return MakeUnique<Optimizer::PassToken::Impl>( |
863 | MakeUnique<opt::CopyPropagateArrays>()); |
864 | } |
865 | |
866 | Optimizer::PassToken CreateVectorDCEPass() { |
867 | return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::VectorDCE>()); |
868 | } |
869 | |
870 | Optimizer::PassToken CreateReduceLoadSizePass() { |
871 | return MakeUnique<Optimizer::PassToken::Impl>( |
872 | MakeUnique<opt::ReduceLoadSize>()); |
873 | } |
874 | |
875 | Optimizer::PassToken CreateCombineAccessChainsPass() { |
876 | return MakeUnique<Optimizer::PassToken::Impl>( |
877 | MakeUnique<opt::CombineAccessChains>()); |
878 | } |
879 | |
880 | Optimizer::PassToken CreateUpgradeMemoryModelPass() { |
881 | return MakeUnique<Optimizer::PassToken::Impl>( |
882 | MakeUnique<opt::UpgradeMemoryModel>()); |
883 | } |
884 | |
885 | Optimizer::PassToken CreateInstBindlessCheckPass(uint32_t desc_set, |
886 | uint32_t shader_id, |
887 | bool input_length_enable, |
888 | bool input_init_enable, |
889 | uint32_t version) { |
890 | return MakeUnique<Optimizer::PassToken::Impl>( |
891 | MakeUnique<opt::InstBindlessCheckPass>(desc_set, shader_id, |
892 | input_length_enable, |
893 | input_init_enable, version)); |
894 | } |
895 | |
896 | Optimizer::PassToken CreateInstDebugPrintfPass(uint32_t desc_set, |
897 | uint32_t shader_id) { |
898 | return MakeUnique<Optimizer::PassToken::Impl>( |
899 | MakeUnique<opt::InstDebugPrintfPass>(desc_set, shader_id)); |
900 | } |
901 | |
902 | Optimizer::PassToken CreateInstBuffAddrCheckPass(uint32_t desc_set, |
903 | uint32_t shader_id, |
904 | uint32_t version) { |
905 | return MakeUnique<Optimizer::PassToken::Impl>( |
906 | MakeUnique<opt::InstBuffAddrCheckPass>(desc_set, shader_id, version)); |
907 | } |
908 | |
909 | Optimizer::PassToken CreateConvertRelaxedToHalfPass() { |
910 | return MakeUnique<Optimizer::PassToken::Impl>( |
911 | MakeUnique<opt::ConvertToHalfPass>()); |
912 | } |
913 | |
914 | Optimizer::PassToken CreateRelaxFloatOpsPass() { |
915 | return MakeUnique<Optimizer::PassToken::Impl>( |
916 | MakeUnique<opt::RelaxFloatOpsPass>()); |
917 | } |
918 | |
919 | Optimizer::PassToken CreateCodeSinkingPass() { |
920 | return MakeUnique<Optimizer::PassToken::Impl>( |
921 | MakeUnique<opt::CodeSinkingPass>()); |
922 | } |
923 | |
924 | Optimizer::PassToken CreateGenerateWebGPUInitializersPass() { |
925 | return MakeUnique<Optimizer::PassToken::Impl>( |
926 | MakeUnique<opt::GenerateWebGPUInitializersPass>()); |
927 | } |
928 | |
929 | Optimizer::PassToken CreateFixStorageClassPass() { |
930 | return MakeUnique<Optimizer::PassToken::Impl>( |
931 | MakeUnique<opt::FixStorageClass>()); |
932 | } |
933 | |
934 | Optimizer::PassToken CreateLegalizeVectorShufflePass() { |
935 | return MakeUnique<Optimizer::PassToken::Impl>( |
936 | MakeUnique<opt::LegalizeVectorShufflePass>()); |
937 | } |
938 | |
939 | Optimizer::PassToken CreateDecomposeInitializedVariablesPass() { |
940 | return MakeUnique<Optimizer::PassToken::Impl>( |
941 | MakeUnique<opt::DecomposeInitializedVariablesPass>()); |
942 | } |
943 | |
944 | Optimizer::PassToken CreateSplitInvalidUnreachablePass() { |
945 | return MakeUnique<Optimizer::PassToken::Impl>( |
946 | MakeUnique<opt::SplitInvalidUnreachablePass>()); |
947 | } |
948 | |
949 | Optimizer::PassToken CreateGraphicsRobustAccessPass() { |
950 | return MakeUnique<Optimizer::PassToken::Impl>( |
951 | MakeUnique<opt::GraphicsRobustAccessPass>()); |
952 | } |
953 | |
954 | Optimizer::PassToken CreateDescriptorScalarReplacementPass() { |
955 | return MakeUnique<Optimizer::PassToken::Impl>( |
956 | MakeUnique<opt::DescriptorScalarReplacement>()); |
957 | } |
958 | |
959 | Optimizer::PassToken CreateWrapOpKillPass() { |
960 | return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::WrapOpKill>()); |
961 | } |
962 | |
963 | Optimizer::PassToken CreateAmdExtToKhrPass() { |
964 | return MakeUnique<Optimizer::PassToken::Impl>( |
965 | MakeUnique<opt::AmdExtensionToKhrPass>()); |
966 | } |
967 | |
968 | } // namespace spvtools |
969 | |