blob: b12a7f76c6104c080d185c43fa1dbb342d0af7ab [file] [log] [blame]
Aart Bik281c6812016-08-26 11:31:48 -07001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "loop_optimization.h"
18
19#include "base/arena_containers.h"
20#include "induction_var_range.h"
21#include "ssa_liveness_analysis.h"
22#include "nodes.h"
23
24namespace art {
25
26// TODO: Generalize to cycles, as found by induction analysis?
27static bool IsPhiAddSub(HPhi* phi, /*out*/ HInstruction** addsub_out) {
28 HInputsRef inputs = phi->GetInputs();
29 if (inputs.size() == 2 && (inputs[1]->IsAdd() || inputs[1]->IsSub())) {
30 HInstruction* addsub = inputs[1];
31 if (addsub->InputAt(0) == phi || addsub->InputAt(1) == phi) {
32 if (addsub->GetUses().HasExactlyOneElement()) {
33 *addsub_out = addsub;
34 return true;
35 }
36 }
37 }
38 return false;
39}
40
41static bool IsOnlyUsedAfterLoop(const HLoopInformation& loop_info,
42 HPhi* phi, HInstruction* addsub) {
43 for (const HUseListNode<HInstruction*>& use : phi->GetUses()) {
44 if (use.GetUser() != addsub) {
45 HLoopInformation* other_loop_info = use.GetUser()->GetBlock()->GetLoopInformation();
46 if (other_loop_info != nullptr && other_loop_info->IsIn(loop_info)) {
47 return false;
48 }
49 }
50 }
51 return true;
52}
53
54// Find: phi: Phi(init, addsub)
55// s: SuspendCheck
56// c: Condition(phi, bound)
57// i: If(c)
58// TODO: Find a less pattern matching approach?
59static bool IsEmptyHeader(HBasicBlock* block, /*out*/ HInstruction** addsub) {
60 HInstruction* phi = block->GetFirstPhi();
61 if (phi != nullptr && phi->GetNext() == nullptr && IsPhiAddSub(phi->AsPhi(), addsub)) {
62 HInstruction* s = block->GetFirstInstruction();
63 if (s != nullptr && s->IsSuspendCheck()) {
64 HInstruction* c = s->GetNext();
65 if (c != nullptr && c->IsCondition() && c->GetUses().HasExactlyOneElement()) {
66 HInstruction* i = c->GetNext();
67 if (i != nullptr && i->IsIf() && i->InputAt(0) == c) {
68 // Check that phi is only used inside loop as expected.
69 for (const HUseListNode<HInstruction*>& use : phi->GetUses()) {
70 if (use.GetUser() != *addsub && use.GetUser() != c) {
71 return false;
72 }
73 }
74 return true;
75 }
76 }
77 }
78 }
79 return false;
80}
81
82static bool IsEmptyBody(HBasicBlock* block, HInstruction* addsub) {
83 HInstruction* phi = block->GetFirstPhi();
84 HInstruction* i = block->GetFirstInstruction();
85 return phi == nullptr && i == addsub && i->GetNext() != nullptr && i->GetNext()->IsGoto();
86}
87
88static HBasicBlock* TryRemovePreHeader(HBasicBlock* preheader, HBasicBlock* entry_block) {
89 if (preheader->GetPredecessors().size() == 1) {
90 HBasicBlock* entry = preheader->GetSinglePredecessor();
91 HInstruction* anchor = entry->GetLastInstruction();
92 // If the pre-header has a single predecessor we can remove it too if
93 // either the pre-header just contains a goto, or if the predecessor
94 // is not the entry block so we can push instructions backward
95 // (moving computation into the entry block is too dangerous!).
96 if (preheader->GetFirstInstruction() == nullptr ||
97 preheader->GetFirstInstruction()->IsGoto() ||
98 (entry != entry_block && anchor->IsGoto())) {
99 // Push non-goto statements backward to empty the pre-header.
100 for (HInstructionIterator it(preheader->GetInstructions()); !it.Done(); it.Advance()) {
101 HInstruction* instruction = it.Current();
102 if (!instruction->IsGoto()) {
103 if (!instruction->CanBeMoved()) {
104 return nullptr; // pushing failed to move all
105 }
106 it.Current()->MoveBefore(anchor);
107 }
108 }
109 return entry;
110 }
111 }
112 return nullptr;
113}
114
115static void RemoveFromCycle(HInstruction* instruction) {
116 // A bit more elaborate than the usual instruction removal,
117 // since there may be a cycle in the use structure.
118 instruction->RemoveAsUserOfAllInputs();
119 instruction->RemoveEnvironmentUsers();
120 instruction->GetBlock()->RemoveInstructionOrPhi(instruction, /*ensure_safety=*/ false);
121}
122
123//
124// Class methods.
125//
126
127HLoopOptimization::HLoopOptimization(HGraph* graph,
128 HInductionVarAnalysis* induction_analysis)
Nicolas Geoffray5ed20f92016-10-05 13:49:44 +0100129 : HLoopOptimization(graph, induction_analysis, nullptr) {}
130
131HLoopOptimization::HLoopOptimization(HGraph* graph,
132 HInductionVarAnalysis* induction_analysis,
133 ArenaAllocator* allocator)
Aart Bik281c6812016-08-26 11:31:48 -0700134 : HOptimization(graph, kLoopOptimizationPassName),
135 induction_range_(induction_analysis),
Nicolas Geoffray5ed20f92016-10-05 13:49:44 +0100136 loop_allocator_(allocator),
Aart Bik281c6812016-08-26 11:31:48 -0700137 top_loop_(nullptr),
138 last_loop_(nullptr) {
139}
140
141void HLoopOptimization::Run() {
142 // Well-behaved loops only.
143 // TODO: make this less of a sledgehammer.
144 if (graph_-> HasTryCatch() || graph_->HasIrreducibleLoops()) {
145 return;
146 }
147
Nicolas Geoffrayebe16742016-10-05 09:55:42 +0100148 ArenaAllocator allocator(graph_->GetArena()->GetArenaPool());
Nicolas Geoffray5ed20f92016-10-05 13:49:44 +0100149 if (loop_allocator_ == nullptr) {
150 loop_allocator_ = &allocator;
151 }
Nicolas Geoffrayebe16742016-10-05 09:55:42 +0100152
Aart Bik281c6812016-08-26 11:31:48 -0700153 // Build the linear order. This step enables building a loop hierarchy that
154 // properly reflects the outer-inner and previous-next relation.
155 graph_->Linearize();
156 // Build the loop hierarchy.
157 for (HLinearOrderIterator it_graph(*graph_); !it_graph.Done(); it_graph.Advance()) {
158 HBasicBlock* block = it_graph.Current();
159 if (block->IsLoopHeader()) {
160 AddLoop(block->GetLoopInformation());
161 }
162 }
Nicolas Geoffrayebe16742016-10-05 09:55:42 +0100163 if (top_loop_ != nullptr) {
164 // Traverse the loop hierarchy inner-to-outer and optimize.
165 TraverseLoopsInnerToOuter(top_loop_);
Aart Bik281c6812016-08-26 11:31:48 -0700166 }
Nicolas Geoffray5ed20f92016-10-05 13:49:44 +0100167 if (loop_allocator_ == &allocator) {
168 loop_allocator_ = nullptr;
169 }
Aart Bik281c6812016-08-26 11:31:48 -0700170}
171
172void HLoopOptimization::AddLoop(HLoopInformation* loop_info) {
173 DCHECK(loop_info != nullptr);
Nicolas Geoffrayebe16742016-10-05 09:55:42 +0100174 LoopNode* node = new (loop_allocator_) LoopNode(loop_info); // phase-local allocator
Aart Bik281c6812016-08-26 11:31:48 -0700175 if (last_loop_ == nullptr) {
176 // First loop.
177 DCHECK(top_loop_ == nullptr);
178 last_loop_ = top_loop_ = node;
179 } else if (loop_info->IsIn(*last_loop_->loop_info)) {
180 // Inner loop.
181 node->outer = last_loop_;
182 DCHECK(last_loop_->inner == nullptr);
183 last_loop_ = last_loop_->inner = node;
184 } else {
185 // Subsequent loop.
186 while (last_loop_->outer != nullptr && !loop_info->IsIn(*last_loop_->outer->loop_info)) {
187 last_loop_ = last_loop_->outer;
188 }
189 node->outer = last_loop_->outer;
190 node->previous = last_loop_;
191 DCHECK(last_loop_->next == nullptr);
192 last_loop_ = last_loop_->next = node;
193 }
194}
195
196void HLoopOptimization::RemoveLoop(LoopNode* node) {
197 DCHECK(node != nullptr);
198 // TODO: implement when needed (for current set of optimizations, we don't
199 // need to keep recorded loop hierarchy up to date, but as we get different
200 // traversal, we may want to remove the node from the hierarchy here.
201}
202
203void HLoopOptimization::TraverseLoopsInnerToOuter(LoopNode* node) {
204 for ( ; node != nullptr; node = node->next) {
205 if (node->inner != nullptr) {
206 TraverseLoopsInnerToOuter(node->inner);
207 }
208 // Visit loop after its inner loops have been visited.
209 SimplifyInduction(node);
210 RemoveIfEmptyLoop(node);
211 }
212}
213
214void HLoopOptimization::SimplifyInduction(LoopNode* node) {
215 HBasicBlock* header = node->loop_info->GetHeader();
216 HBasicBlock* preheader = node->loop_info->GetPreHeader();
217 // Scan the phis in the header to find opportunities to optimize induction.
218 for (HInstructionIterator it(header->GetPhis()); !it.Done(); it.Advance()) {
219 HPhi* phi = it.Current()->AsPhi();
220 HInstruction* addsub = nullptr;
221 // Find phi-add/sub cycle.
222 if (IsPhiAddSub(phi, &addsub)) {
223 // Simple case, the induction is only used by itself. Although redundant,
224 // later phases do not easily detect this property. Thus, eliminate here.
225 // Example: for (int i = 0; x != null; i++) { .... no i .... }
226 if (phi->GetUses().HasExactlyOneElement()) {
227 // Remove the cycle, including all uses. Even environment uses can be removed,
228 // since these computations have no effect at all.
229 RemoveFromCycle(phi); // removes environment uses too
230 RemoveFromCycle(addsub);
231 continue;
232 }
233 // Closed form case. Only the last value of the induction is needed. Remove all
234 // overhead from the loop, and replace subsequent uses with the last value.
235 // Example: for (int i = 0; i < 10; i++, k++) { .... no k .... } return k;
236 if (IsOnlyUsedAfterLoop(*node->loop_info, phi, addsub) &&
237 induction_range_.CanGenerateLastValue(phi)) {
238 HInstruction* last = induction_range_.GenerateLastValue(phi, graph_, preheader);
239 // Remove the cycle, replacing all uses. Even environment uses can consume the final
240 // value, since any first real use is outside the loop (although this may imply
241 // that deopting may look "ahead" a bit on the phi value).
242 ReplaceAllUses(phi, last, addsub);
243 RemoveFromCycle(phi); // removes environment uses too
244 RemoveFromCycle(addsub);
245 }
246 }
247 }
248}
249
250void HLoopOptimization::RemoveIfEmptyLoop(LoopNode* node) {
251 HBasicBlock* header = node->loop_info->GetHeader();
252 HBasicBlock* preheader = node->loop_info->GetPreHeader();
253 // Ensure there is only a single loop-body (besides the header).
254 HBasicBlock* body = nullptr;
255 for (HBlocksInLoopIterator it(*node->loop_info); !it.Done(); it.Advance()) {
256 if (it.Current() != header) {
257 if (body != nullptr) {
258 return;
259 }
260 body = it.Current();
261 }
262 }
263 // Ensure there is only a single exit point.
264 if (header->GetSuccessors().size() != 2) {
265 return;
266 }
267 HBasicBlock* exit = (header->GetSuccessors()[0] == body)
268 ? header->GetSuccessors()[1]
269 : header->GetSuccessors()[0];
270 // Ensure exit can only be reached by exiting loop (this seems typically the
271 // case anyway, and simplifies code generation below; TODO: perhaps relax?).
272 if (exit->GetPredecessors().size() != 1) {
273 return;
274 }
275 // Detect an empty loop: no side effects other than plain iteration.
276 HInstruction* addsub = nullptr;
277 if (IsEmptyHeader(header, &addsub) && IsEmptyBody(body, addsub)) {
278 HBasicBlock* entry = TryRemovePreHeader(preheader, graph_->GetEntryBlock());
279 body->DisconnectAndDelete();
280 exit->RemovePredecessor(header);
281 header->RemoveSuccessor(exit);
282 header->ClearDominanceInformation();
283 header->SetDominator(preheader); // needed by next disconnect.
284 header->DisconnectAndDelete();
285 // If allowed, remove preheader too, which may expose next outer empty loop
286 // Otherwise, link preheader directly to exit to restore the flow graph.
287 if (entry != nullptr) {
288 entry->ReplaceSuccessor(preheader, exit);
289 entry->AddDominatedBlock(exit);
290 exit->SetDominator(entry);
291 preheader->DisconnectAndDelete();
292 } else {
293 preheader->AddSuccessor(exit);
294 preheader->AddInstruction(new (graph_->GetArena()) HGoto()); // global allocator
295 preheader->AddDominatedBlock(exit);
296 exit->SetDominator(preheader);
297 }
298 // Update hierarchy.
299 RemoveLoop(node);
300 }
301}
302
303void HLoopOptimization::ReplaceAllUses(HInstruction* instruction,
304 HInstruction* replacement,
305 HInstruction* exclusion) {
306 const HUseList<HInstruction*>& uses = instruction->GetUses();
307 for (auto it = uses.begin(), end = uses.end(); it != end;) {
308 HInstruction* user = it->GetUser();
309 size_t index = it->GetIndex();
310 ++it; // increment before replacing
311 if (user != exclusion) {
312 user->ReplaceInput(replacement, index);
313 induction_range_.Replace(user, instruction, replacement); // update induction
314 }
315 }
316 const HUseList<HEnvironment*>& env_uses = instruction->GetEnvUses();
317 for (auto it = env_uses.begin(), end = env_uses.end(); it != end;) {
318 HEnvironment* user = it->GetUser();
319 size_t index = it->GetIndex();
320 ++it; // increment before replacing
321 if (user->GetHolder() != exclusion) {
322 user->RemoveAsUserOfInput(index);
323 user->SetRawEnvAt(index, replacement);
324 replacement->AddEnvUseAt(user, index);
325 }
326 }
327}
328
329} // namespace art