1 // Copyright (c) 2016 Google Inc.
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
7 // http://www.apache.org/licenses/LICENSE-2.0
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.
15 #include "spirv-tools/optimizer.hpp"
17 #include "build_module.h"
18 #include "make_unique.h"
19 #include "pass_manager.h"
21 #include "simplification_pass.h"
25 struct Optimizer::PassToken::Impl {
26 Impl(std::unique_ptr<opt::Pass> p) : pass(std::move(p)) {}
28 std::unique_ptr<opt::Pass> pass; // Internal implementation pass.
31 Optimizer::PassToken::PassToken(
32 std::unique_ptr<Optimizer::PassToken::Impl> impl)
33 : impl_(std::move(impl)) {}
34 Optimizer::PassToken::PassToken(PassToken&& that)
35 : impl_(std::move(that.impl_)) {}
37 Optimizer::PassToken& Optimizer::PassToken::operator=(PassToken&& that) {
38 impl_ = std::move(that.impl_);
42 Optimizer::PassToken::~PassToken() {}
44 struct Optimizer::Impl {
45 explicit Impl(spv_target_env env) : target_env(env), pass_manager() {}
47 const spv_target_env target_env; // Target environment.
48 opt::PassManager pass_manager; // Internal implementation pass manager.
51 Optimizer::Optimizer(spv_target_env env) : impl_(new Impl(env)) {}
53 Optimizer::~Optimizer() {}
55 void Optimizer::SetMessageConsumer(MessageConsumer c) {
56 // All passes' message consumer needs to be updated.
57 for (uint32_t i = 0; i < impl_->pass_manager.NumPasses(); ++i) {
58 impl_->pass_manager.GetPass(i)->SetMessageConsumer(c);
60 impl_->pass_manager.SetMessageConsumer(std::move(c));
63 Optimizer& Optimizer::RegisterPass(PassToken&& p) {
64 // Change to use the pass manager's consumer.
65 p.impl_->pass->SetMessageConsumer(impl_->pass_manager.consumer());
66 impl_->pass_manager.AddPass(std::move(p.impl_->pass));
70 // The legalization passes take a spir-v shader generated by an HLSL front-end
71 // and turn it into a valid vulkan spir-v shader. There are two ways in which
72 // the code will be invalid at the start:
74 // 1) There will be opaque objects, like images, which will be passed around
75 // in intermediate objects. Valid spir-v will have to replace the use of
76 // the opaque object with an intermediate object that is the result of the
77 // load of the global opaque object.
79 // 2) There will be variables that contain pointers to structured or uniform
80 // buffers. It be legal, the variables must be eliminated, and the
81 // references to the structured buffers must use the result of OpVariable
82 // in the Uniform storage class.
84 // Optimization in this list must accept shaders with these relaxation of the
85 // rules. There is not guarantee that this list of optimizations is able to
86 // legalize all inputs, but it is on a best effort basis.
88 // The legalization problem is essentially a very general copy propagation
89 // problem. The optimization we use are all used to either do copy propagation
90 // or enable more copy propagation.
91 Optimizer& Optimizer::RegisterLegalizationPasses() {
93 // Make sure uses and definitions are in the same function.
94 RegisterPass(CreateInlineExhaustivePass())
95 // Make private variable function scope
96 .RegisterPass(CreateEliminateDeadFunctionsPass())
97 .RegisterPass(CreatePrivateToLocalPass())
98 // Split up aggragates so they are easier to deal with.
99 .RegisterPass(CreateScalarReplacementPass())
100 // Remove loads and stores so everything is in intermediate values.
101 // Takes care of copy propagation of non-members.
102 .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
103 .RegisterPass(CreateLocalSingleStoreElimPass())
104 .RegisterPass(CreateLocalMultiStoreElimPass())
105 // Copy propagate members. Cleans up code sequences generated by
106 // scalar replacement.
107 .RegisterPass(CreateSimplificationPass())
108 // May need loop unrolling here see
109 // https://github.com/Microsoft/DirectXShaderCompiler/pull/930
110 .RegisterPass(CreateDeadBranchElimPass())
111 // Get rid of unused code that contain traces of illegal code
112 // or unused references to unbound external objects
113 .RegisterPass(CreateDeadInsertElimPass())
114 .RegisterPass(CreateAggressiveDCEPass());
117 Optimizer& Optimizer::RegisterPerformancePasses() {
118 return RegisterPass(CreateRemoveDuplicatesPass())
119 .RegisterPass(CreateMergeReturnPass())
120 .RegisterPass(CreateInlineExhaustivePass())
121 .RegisterPass(CreateAggressiveDCEPass())
122 .RegisterPass(CreateScalarReplacementPass())
123 .RegisterPass(CreateLocalAccessChainConvertPass())
124 .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
125 .RegisterPass(CreateLocalSingleStoreElimPass())
126 .RegisterPass(CreateLocalMultiStoreElimPass())
127 .RegisterPass(CreateCCPPass())
128 .RegisterPass(CreateAggressiveDCEPass())
129 .RegisterPass(CreateRedundancyEliminationPass())
130 .RegisterPass(CreateInsertExtractElimPass())
131 .RegisterPass(CreateDeadInsertElimPass())
132 .RegisterPass(CreateDeadBranchElimPass())
133 .RegisterPass(CreateIfConversionPass())
134 .RegisterPass(CreateAggressiveDCEPass())
135 .RegisterPass(CreateBlockMergePass())
136 .RegisterPass(CreateRedundancyEliminationPass())
137 .RegisterPass(CreateDeadBranchElimPass())
138 .RegisterPass(CreateBlockMergePass())
139 .RegisterPass(CreateInsertExtractElimPass());
140 // Currently exposing driver bugs resulting in crashes (#946)
141 // .RegisterPass(CreateCommonUniformElimPass())
144 Optimizer& Optimizer::RegisterSizePasses() {
145 return RegisterPass(CreateRemoveDuplicatesPass())
146 .RegisterPass(CreateMergeReturnPass())
147 .RegisterPass(CreateInlineExhaustivePass())
148 .RegisterPass(CreateAggressiveDCEPass())
149 .RegisterPass(CreateScalarReplacementPass())
150 .RegisterPass(CreateLocalAccessChainConvertPass())
151 .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
152 .RegisterPass(CreateLocalSingleStoreElimPass())
153 .RegisterPass(CreateInsertExtractElimPass())
154 .RegisterPass(CreateDeadInsertElimPass())
155 .RegisterPass(CreateLocalMultiStoreElimPass())
156 .RegisterPass(CreateCCPPass())
157 .RegisterPass(CreateAggressiveDCEPass())
158 .RegisterPass(CreateDeadBranchElimPass())
159 .RegisterPass(CreateIfConversionPass())
160 .RegisterPass(CreateAggressiveDCEPass())
161 .RegisterPass(CreateBlockMergePass())
162 .RegisterPass(CreateInsertExtractElimPass())
163 .RegisterPass(CreateDeadInsertElimPass())
164 .RegisterPass(CreateRedundancyEliminationPass())
165 .RegisterPass(CreateCFGCleanupPass())
166 // Currently exposing driver bugs resulting in crashes (#946)
167 // .RegisterPass(CreateCommonUniformElimPass())
168 .RegisterPass(CreateAggressiveDCEPass());
171 bool Optimizer::Run(const uint32_t* original_binary,
172 const size_t original_binary_size,
173 std::vector<uint32_t>* optimized_binary) const {
174 std::unique_ptr<ir::IRContext> context =
175 BuildModule(impl_->target_env, impl_->pass_manager.consumer(),
176 original_binary, original_binary_size);
177 if (context == nullptr) return false;
179 auto status = impl_->pass_manager.Run(context.get());
180 if (status == opt::Pass::Status::SuccessWithChange ||
181 (status == opt::Pass::Status::SuccessWithoutChange &&
182 (optimized_binary->data() != original_binary ||
183 optimized_binary->size() != original_binary_size))) {
184 optimized_binary->clear();
185 context->module()->ToBinary(optimized_binary, /* skip_nop = */ true);
188 return status != opt::Pass::Status::Failure;
191 Optimizer& Optimizer::SetPrintAll(std::ostream* out) {
192 impl_->pass_manager.SetPrintAll(out);
196 Optimizer::PassToken CreateNullPass() {
197 return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::NullPass>());
200 Optimizer::PassToken CreateStripDebugInfoPass() {
201 return MakeUnique<Optimizer::PassToken::Impl>(
202 MakeUnique<opt::StripDebugInfoPass>());
205 Optimizer::PassToken CreateEliminateDeadFunctionsPass() {
206 return MakeUnique<Optimizer::PassToken::Impl>(
207 MakeUnique<opt::EliminateDeadFunctionsPass>());
210 Optimizer::PassToken CreateSetSpecConstantDefaultValuePass(
211 const std::unordered_map<uint32_t, std::string>& id_value_map) {
212 return MakeUnique<Optimizer::PassToken::Impl>(
213 MakeUnique<opt::SetSpecConstantDefaultValuePass>(id_value_map));
216 Optimizer::PassToken CreateSetSpecConstantDefaultValuePass(
217 const std::unordered_map<uint32_t, std::vector<uint32_t>>& id_value_map) {
218 return MakeUnique<Optimizer::PassToken::Impl>(
219 MakeUnique<opt::SetSpecConstantDefaultValuePass>(id_value_map));
222 Optimizer::PassToken CreateFlattenDecorationPass() {
223 return MakeUnique<Optimizer::PassToken::Impl>(
224 MakeUnique<opt::FlattenDecorationPass>());
227 Optimizer::PassToken CreateFreezeSpecConstantValuePass() {
228 return MakeUnique<Optimizer::PassToken::Impl>(
229 MakeUnique<opt::FreezeSpecConstantValuePass>());
232 Optimizer::PassToken CreateFoldSpecConstantOpAndCompositePass() {
233 return MakeUnique<Optimizer::PassToken::Impl>(
234 MakeUnique<opt::FoldSpecConstantOpAndCompositePass>());
237 Optimizer::PassToken CreateUnifyConstantPass() {
238 return MakeUnique<Optimizer::PassToken::Impl>(
239 MakeUnique<opt::UnifyConstantPass>());
242 Optimizer::PassToken CreateEliminateDeadConstantPass() {
243 return MakeUnique<Optimizer::PassToken::Impl>(
244 MakeUnique<opt::EliminateDeadConstantPass>());
247 Optimizer::PassToken CreateDeadVariableEliminationPass() {
248 return MakeUnique<Optimizer::PassToken::Impl>(
249 MakeUnique<opt::DeadVariableElimination>());
252 Optimizer::PassToken CreateStrengthReductionPass() {
253 return MakeUnique<Optimizer::PassToken::Impl>(
254 MakeUnique<opt::StrengthReductionPass>());
257 Optimizer::PassToken CreateBlockMergePass() {
258 return MakeUnique<Optimizer::PassToken::Impl>(
259 MakeUnique<opt::BlockMergePass>());
262 Optimizer::PassToken CreateInlineExhaustivePass() {
263 return MakeUnique<Optimizer::PassToken::Impl>(
264 MakeUnique<opt::InlineExhaustivePass>());
267 Optimizer::PassToken CreateInlineOpaquePass() {
268 return MakeUnique<Optimizer::PassToken::Impl>(
269 MakeUnique<opt::InlineOpaquePass>());
272 Optimizer::PassToken CreateLocalAccessChainConvertPass() {
273 return MakeUnique<Optimizer::PassToken::Impl>(
274 MakeUnique<opt::LocalAccessChainConvertPass>());
277 Optimizer::PassToken CreateLocalSingleBlockLoadStoreElimPass() {
278 return MakeUnique<Optimizer::PassToken::Impl>(
279 MakeUnique<opt::LocalSingleBlockLoadStoreElimPass>());
282 Optimizer::PassToken CreateLocalSingleStoreElimPass() {
283 return MakeUnique<Optimizer::PassToken::Impl>(
284 MakeUnique<opt::LocalSingleStoreElimPass>());
287 Optimizer::PassToken CreateInsertExtractElimPass() {
288 return MakeUnique<Optimizer::PassToken::Impl>(
289 MakeUnique<opt::InsertExtractElimPass>());
292 Optimizer::PassToken CreateDeadInsertElimPass() {
293 return MakeUnique<Optimizer::PassToken::Impl>(
294 MakeUnique<opt::DeadInsertElimPass>());
297 Optimizer::PassToken CreateDeadBranchElimPass() {
298 return MakeUnique<Optimizer::PassToken::Impl>(
299 MakeUnique<opt::DeadBranchElimPass>());
302 Optimizer::PassToken CreateLocalMultiStoreElimPass() {
303 return MakeUnique<Optimizer::PassToken::Impl>(
304 MakeUnique<opt::LocalMultiStoreElimPass>());
307 Optimizer::PassToken CreateAggressiveDCEPass() {
308 return MakeUnique<Optimizer::PassToken::Impl>(
309 MakeUnique<opt::AggressiveDCEPass>());
312 Optimizer::PassToken CreateCommonUniformElimPass() {
313 return MakeUnique<Optimizer::PassToken::Impl>(
314 MakeUnique<opt::CommonUniformElimPass>());
317 Optimizer::PassToken CreateCompactIdsPass() {
318 return MakeUnique<Optimizer::PassToken::Impl>(
319 MakeUnique<opt::CompactIdsPass>());
322 Optimizer::PassToken CreateMergeReturnPass() {
323 return MakeUnique<Optimizer::PassToken::Impl>(
324 MakeUnique<opt::MergeReturnPass>());
327 std::vector<const char*> Optimizer::GetPassNames() const {
328 std::vector<const char*> v;
329 for (uint32_t i = 0; i < impl_->pass_manager.NumPasses(); i++) {
330 v.push_back(impl_->pass_manager.GetPass(i)->name());
335 Optimizer::PassToken CreateCFGCleanupPass() {
336 return MakeUnique<Optimizer::PassToken::Impl>(
337 MakeUnique<opt::CFGCleanupPass>());
340 Optimizer::PassToken CreateLocalRedundancyEliminationPass() {
341 return MakeUnique<Optimizer::PassToken::Impl>(
342 MakeUnique<opt::LocalRedundancyEliminationPass>());
345 Optimizer::PassToken CreateRedundancyEliminationPass() {
346 return MakeUnique<Optimizer::PassToken::Impl>(
347 MakeUnique<opt::RedundancyEliminationPass>());
350 Optimizer::PassToken CreateRemoveDuplicatesPass() {
351 return MakeUnique<Optimizer::PassToken::Impl>(
352 MakeUnique<opt::RemoveDuplicatesPass>());
355 Optimizer::PassToken CreateScalarReplacementPass() {
356 return MakeUnique<Optimizer::PassToken::Impl>(
357 MakeUnique<opt::ScalarReplacementPass>());
360 Optimizer::PassToken CreatePrivateToLocalPass() {
361 return MakeUnique<Optimizer::PassToken::Impl>(
362 MakeUnique<opt::PrivateToLocalPass>());
365 Optimizer::PassToken CreateCCPPass() {
366 return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::CCPPass>());
369 Optimizer::PassToken CreateWorkaround1209Pass() {
370 return MakeUnique<Optimizer::PassToken::Impl>(
371 MakeUnique<opt::Workaround1209>());
374 Optimizer::PassToken CreateIfConversionPass() {
375 return MakeUnique<Optimizer::PassToken::Impl>(
376 MakeUnique<opt::IfConversion>());
379 Optimizer::PassToken CreateReplaceInvalidOpcodePass() {
380 return MakeUnique<Optimizer::PassToken::Impl>(
381 MakeUnique<opt::ReplaceInvalidOpcodePass>());
384 Optimizer::PassToken CreateSimplificationPass() {
385 return MakeUnique<Optimizer::PassToken::Impl>(
386 MakeUnique<opt::SimplificationPass>());
388 } // namespace spvtools