Loop invariant code motion initial implementation
[platform/upstream/SPIRV-Tools.git] / source / opt / optimizer.cpp
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 "build_module.h"
18 #include "make_unique.h"
19 #include "pass_manager.h"
20 #include "passes.h"
21 #include "simplification_pass.h"
22
23 namespace spvtools {
24
25 struct Optimizer::PassToken::Impl {
26   Impl(std::unique_ptr<opt::Pass> p) : pass(std::move(p)) {}
27
28   std::unique_ptr<opt::Pass> pass;  // Internal implementation pass.
29 };
30
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_)) {}
36
37 Optimizer::PassToken& Optimizer::PassToken::operator=(PassToken&& that) {
38   impl_ = std::move(that.impl_);
39   return *this;
40 }
41
42 Optimizer::PassToken::~PassToken() {}
43
44 struct Optimizer::Impl {
45   explicit Impl(spv_target_env env) : target_env(env), pass_manager() {}
46
47   const spv_target_env target_env;  // Target environment.
48   opt::PassManager pass_manager;    // Internal implementation pass manager.
49 };
50
51 Optimizer::Optimizer(spv_target_env env) : impl_(new Impl(env)) {}
52
53 Optimizer::~Optimizer() {}
54
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);
59   }
60   impl_->pass_manager.SetMessageConsumer(std::move(c));
61 }
62
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));
67   return *this;
68 }
69
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:
73 //
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.
78 //
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.
83 //
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.
87 //
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() {
92   return
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());
115 }
116
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())
142 }
143
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());
169 }
170
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;
178
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);
186   }
187
188   return status != opt::Pass::Status::Failure;
189 }
190
191 Optimizer& Optimizer::SetPrintAll(std::ostream* out) {
192   impl_->pass_manager.SetPrintAll(out);
193   return *this;
194 }
195
196 Optimizer::PassToken CreateNullPass() {
197   return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::NullPass>());
198 }
199
200 Optimizer::PassToken CreateStripDebugInfoPass() {
201   return MakeUnique<Optimizer::PassToken::Impl>(
202       MakeUnique<opt::StripDebugInfoPass>());
203 }
204
205 Optimizer::PassToken CreateEliminateDeadFunctionsPass() {
206   return MakeUnique<Optimizer::PassToken::Impl>(
207       MakeUnique<opt::EliminateDeadFunctionsPass>());
208 }
209
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));
214 }
215
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));
220 }
221
222 Optimizer::PassToken CreateFlattenDecorationPass() {
223   return MakeUnique<Optimizer::PassToken::Impl>(
224       MakeUnique<opt::FlattenDecorationPass>());
225 }
226
227 Optimizer::PassToken CreateFreezeSpecConstantValuePass() {
228   return MakeUnique<Optimizer::PassToken::Impl>(
229       MakeUnique<opt::FreezeSpecConstantValuePass>());
230 }
231
232 Optimizer::PassToken CreateFoldSpecConstantOpAndCompositePass() {
233   return MakeUnique<Optimizer::PassToken::Impl>(
234       MakeUnique<opt::FoldSpecConstantOpAndCompositePass>());
235 }
236
237 Optimizer::PassToken CreateUnifyConstantPass() {
238   return MakeUnique<Optimizer::PassToken::Impl>(
239       MakeUnique<opt::UnifyConstantPass>());
240 }
241
242 Optimizer::PassToken CreateEliminateDeadConstantPass() {
243   return MakeUnique<Optimizer::PassToken::Impl>(
244       MakeUnique<opt::EliminateDeadConstantPass>());
245 }
246
247 Optimizer::PassToken CreateDeadVariableEliminationPass() {
248   return MakeUnique<Optimizer::PassToken::Impl>(
249       MakeUnique<opt::DeadVariableElimination>());
250 }
251
252 Optimizer::PassToken CreateStrengthReductionPass() {
253   return MakeUnique<Optimizer::PassToken::Impl>(
254       MakeUnique<opt::StrengthReductionPass>());
255 }
256
257 Optimizer::PassToken CreateBlockMergePass() {
258   return MakeUnique<Optimizer::PassToken::Impl>(
259       MakeUnique<opt::BlockMergePass>());
260 }
261
262 Optimizer::PassToken CreateInlineExhaustivePass() {
263   return MakeUnique<Optimizer::PassToken::Impl>(
264       MakeUnique<opt::InlineExhaustivePass>());
265 }
266
267 Optimizer::PassToken CreateInlineOpaquePass() {
268   return MakeUnique<Optimizer::PassToken::Impl>(
269       MakeUnique<opt::InlineOpaquePass>());
270 }
271
272 Optimizer::PassToken CreateLocalAccessChainConvertPass() {
273   return MakeUnique<Optimizer::PassToken::Impl>(
274       MakeUnique<opt::LocalAccessChainConvertPass>());
275 }
276
277 Optimizer::PassToken CreateLocalSingleBlockLoadStoreElimPass() {
278   return MakeUnique<Optimizer::PassToken::Impl>(
279       MakeUnique<opt::LocalSingleBlockLoadStoreElimPass>());
280 }
281
282 Optimizer::PassToken CreateLocalSingleStoreElimPass() {
283   return MakeUnique<Optimizer::PassToken::Impl>(
284       MakeUnique<opt::LocalSingleStoreElimPass>());
285 }
286
287 Optimizer::PassToken CreateInsertExtractElimPass() {
288   return MakeUnique<Optimizer::PassToken::Impl>(
289       MakeUnique<opt::InsertExtractElimPass>());
290 }
291
292 Optimizer::PassToken CreateDeadInsertElimPass() {
293   return MakeUnique<Optimizer::PassToken::Impl>(
294       MakeUnique<opt::DeadInsertElimPass>());
295 }
296
297 Optimizer::PassToken CreateDeadBranchElimPass() {
298   return MakeUnique<Optimizer::PassToken::Impl>(
299       MakeUnique<opt::DeadBranchElimPass>());
300 }
301
302 Optimizer::PassToken CreateLocalMultiStoreElimPass() {
303   return MakeUnique<Optimizer::PassToken::Impl>(
304       MakeUnique<opt::LocalMultiStoreElimPass>());
305 }
306
307 Optimizer::PassToken CreateAggressiveDCEPass() {
308   return MakeUnique<Optimizer::PassToken::Impl>(
309       MakeUnique<opt::AggressiveDCEPass>());
310 }
311
312 Optimizer::PassToken CreateCommonUniformElimPass() {
313   return MakeUnique<Optimizer::PassToken::Impl>(
314       MakeUnique<opt::CommonUniformElimPass>());
315 }
316
317 Optimizer::PassToken CreateCompactIdsPass() {
318   return MakeUnique<Optimizer::PassToken::Impl>(
319       MakeUnique<opt::CompactIdsPass>());
320 }
321
322 Optimizer::PassToken CreateMergeReturnPass() {
323   return MakeUnique<Optimizer::PassToken::Impl>(
324       MakeUnique<opt::MergeReturnPass>());
325 }
326
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());
331   }
332   return v;
333 }
334
335 Optimizer::PassToken CreateCFGCleanupPass() {
336   return MakeUnique<Optimizer::PassToken::Impl>(
337       MakeUnique<opt::CFGCleanupPass>());
338 }
339
340 Optimizer::PassToken CreateLocalRedundancyEliminationPass() {
341   return MakeUnique<Optimizer::PassToken::Impl>(
342       MakeUnique<opt::LocalRedundancyEliminationPass>());
343 }
344
345 Optimizer::PassToken CreateLoopInvariantCodeMotionPass() {
346   return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::LICMPass>());
347 }
348
349 Optimizer::PassToken CreateRedundancyEliminationPass() {
350   return MakeUnique<Optimizer::PassToken::Impl>(
351       MakeUnique<opt::RedundancyEliminationPass>());
352 }
353
354 Optimizer::PassToken CreateRemoveDuplicatesPass() {
355   return MakeUnique<Optimizer::PassToken::Impl>(
356       MakeUnique<opt::RemoveDuplicatesPass>());
357 }
358
359 Optimizer::PassToken CreateScalarReplacementPass() {
360   return MakeUnique<Optimizer::PassToken::Impl>(
361       MakeUnique<opt::ScalarReplacementPass>());
362 }
363
364 Optimizer::PassToken CreatePrivateToLocalPass() {
365   return MakeUnique<Optimizer::PassToken::Impl>(
366       MakeUnique<opt::PrivateToLocalPass>());
367 }
368
369 Optimizer::PassToken CreateCCPPass() {
370   return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::CCPPass>());
371 }
372
373 Optimizer::PassToken CreateWorkaround1209Pass() {
374   return MakeUnique<Optimizer::PassToken::Impl>(
375       MakeUnique<opt::Workaround1209>());
376 }
377
378 Optimizer::PassToken CreateIfConversionPass() {
379   return MakeUnique<Optimizer::PassToken::Impl>(
380       MakeUnique<opt::IfConversion>());
381 }
382
383 Optimizer::PassToken CreateReplaceInvalidOpcodePass() {
384   return MakeUnique<Optimizer::PassToken::Impl>(
385       MakeUnique<opt::ReplaceInvalidOpcodePass>());
386 }
387
388 Optimizer::PassToken CreateSimplificationPass() {
389   return MakeUnique<Optimizer::PassToken::Impl>(
390       MakeUnique<opt::SimplificationPass>());
391 }
392 }  // namespace spvtools