blob: b88e73b9793c6f76309d295e149941b3485d434c [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
Aart Bik96202302016-10-04 17:33:56 -070019#include "linear_order.h"
Aart Bik281c6812016-08-26 11:31:48 -070020
21namespace art {
22
Aart Bik9abf8942016-10-14 09:49:42 -070023// Detects a potential induction cycle. Note that the actual induction
24// information is queried later if its last value is really needed.
Aart Bik8c4a8542016-10-06 11:36:57 -070025static bool IsPhiInduction(HPhi* phi, ArenaSet<HInstruction*>* iset) {
26 DCHECK(iset->empty());
Aart Bik281c6812016-08-26 11:31:48 -070027 HInputsRef inputs = phi->GetInputs();
Aart Bik9abf8942016-10-14 09:49:42 -070028 if (inputs.size() == 2) {
29 HLoopInformation* loop_info = phi->GetBlock()->GetLoopInformation();
30 HInstruction* op = inputs[1];
31 if (op->GetBlock()->GetLoopInformation() == loop_info) {
32 // Chase a simple chain back to phi.
33 while (!op->IsPhi()) {
34 // Binary operation with single use in same loop.
35 if (!op->IsBinaryOperation() || !op->GetUses().HasExactlyOneElement()) {
36 return false;
37 }
38 // Chase back either through left or right operand.
39 iset->insert(op);
40 HInstruction* a = op->InputAt(0);
41 HInstruction* b = op->InputAt(1);
42 if (a->GetBlock()->GetLoopInformation() == loop_info && b != phi) {
43 op = a;
44 } else if (b->GetBlock()->GetLoopInformation() == loop_info) {
45 op = b;
46 } else {
47 return false;
48 }
49 }
50 // Closed the cycle?
51 if (op == phi) {
52 iset->insert(phi);
53 return true;
Aart Bik281c6812016-08-26 11:31:48 -070054 }
55 }
56 }
57 return false;
58}
59
Aart Bik281c6812016-08-26 11:31:48 -070060// Find: phi: Phi(init, addsub)
61// s: SuspendCheck
62// c: Condition(phi, bound)
63// i: If(c)
64// TODO: Find a less pattern matching approach?
Aart Bik8c4a8542016-10-06 11:36:57 -070065static bool IsEmptyHeader(HBasicBlock* block, ArenaSet<HInstruction*>* iset) {
66 DCHECK(iset->empty());
Aart Bik281c6812016-08-26 11:31:48 -070067 HInstruction* phi = block->GetFirstPhi();
Aart Bik8c4a8542016-10-06 11:36:57 -070068 if (phi != nullptr && phi->GetNext() == nullptr && IsPhiInduction(phi->AsPhi(), iset)) {
Aart Bik281c6812016-08-26 11:31:48 -070069 HInstruction* s = block->GetFirstInstruction();
70 if (s != nullptr && s->IsSuspendCheck()) {
71 HInstruction* c = s->GetNext();
72 if (c != nullptr && c->IsCondition() && c->GetUses().HasExactlyOneElement()) {
73 HInstruction* i = c->GetNext();
74 if (i != nullptr && i->IsIf() && i->InputAt(0) == c) {
Aart Bik8c4a8542016-10-06 11:36:57 -070075 iset->insert(c);
76 iset->insert(s);
Aart Bik281c6812016-08-26 11:31:48 -070077 return true;
78 }
79 }
80 }
81 }
82 return false;
83}
84
Aart Bik9abf8942016-10-14 09:49:42 -070085// Does the loop-body consist of induction cycle and direct control flow only?
Aart Bik8c4a8542016-10-06 11:36:57 -070086static bool IsEmptyBody(HBasicBlock* block, ArenaSet<HInstruction*>* iset) {
Aart Bik9abf8942016-10-14 09:49:42 -070087 if (block->GetFirstPhi() == nullptr) {
88 for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
89 HInstruction* instruction = it.Current();
90 if (!instruction->IsGoto() && iset->find(instruction) == iset->end()) {
91 return false;
92 }
93 }
94 return true;
95 }
96 return false;
Aart Bik281c6812016-08-26 11:31:48 -070097}
98
Aart Bik9abf8942016-10-14 09:49:42 -070099// Remove the instruction from the graph. A bit more elaborate than the usual
100// instruction removal, since there may be a cycle in the use structure.
Aart Bik281c6812016-08-26 11:31:48 -0700101static void RemoveFromCycle(HInstruction* instruction) {
Aart Bik281c6812016-08-26 11:31:48 -0700102 instruction->RemoveAsUserOfAllInputs();
103 instruction->RemoveEnvironmentUsers();
104 instruction->GetBlock()->RemoveInstructionOrPhi(instruction, /*ensure_safety=*/ false);
105}
106
107//
108// Class methods.
109//
110
111HLoopOptimization::HLoopOptimization(HGraph* graph,
112 HInductionVarAnalysis* induction_analysis)
113 : HOptimization(graph, kLoopOptimizationPassName),
114 induction_range_(induction_analysis),
Aart Bik96202302016-10-04 17:33:56 -0700115 loop_allocator_(nullptr),
Aart Bik281c6812016-08-26 11:31:48 -0700116 top_loop_(nullptr),
Aart Bik8c4a8542016-10-06 11:36:57 -0700117 last_loop_(nullptr),
Aart Bik482095d2016-10-10 15:39:10 -0700118 iset_(nullptr),
119 induction_simplication_count_(0) {
Aart Bik281c6812016-08-26 11:31:48 -0700120}
121
122void HLoopOptimization::Run() {
123 // Well-behaved loops only.
124 // TODO: make this less of a sledgehammer.
Aart Bik96202302016-10-04 17:33:56 -0700125 if (graph_->HasTryCatch() || graph_->HasIrreducibleLoops()) {
Aart Bik281c6812016-08-26 11:31:48 -0700126 return;
127 }
128
Aart Bik96202302016-10-04 17:33:56 -0700129 // Phase-local allocator that draws from the global pool. Since the allocator
130 // itself resides on the stack, it is destructed on exiting Run(), which
131 // implies its underlying memory is released immediately.
Nicolas Geoffrayebe16742016-10-05 09:55:42 +0100132 ArenaAllocator allocator(graph_->GetArena()->GetArenaPool());
Aart Bik96202302016-10-04 17:33:56 -0700133 loop_allocator_ = &allocator;
Nicolas Geoffrayebe16742016-10-05 09:55:42 +0100134
Aart Bik96202302016-10-04 17:33:56 -0700135 // Perform loop optimizations.
136 LocalRun();
137
138 // Detach.
139 loop_allocator_ = nullptr;
140 last_loop_ = top_loop_ = nullptr;
141}
142
143void HLoopOptimization::LocalRun() {
144 // Build the linear order using the phase-local allocator. This step enables building
145 // a loop hierarchy that properly reflects the outer-inner and previous-next relation.
146 ArenaVector<HBasicBlock*> linear_order(loop_allocator_->Adapter(kArenaAllocLinearOrder));
147 LinearizeGraph(graph_, loop_allocator_, &linear_order);
148
Aart Bik281c6812016-08-26 11:31:48 -0700149 // Build the loop hierarchy.
Aart Bik96202302016-10-04 17:33:56 -0700150 for (HBasicBlock* block : linear_order) {
Aart Bik281c6812016-08-26 11:31:48 -0700151 if (block->IsLoopHeader()) {
152 AddLoop(block->GetLoopInformation());
153 }
154 }
Aart Bik96202302016-10-04 17:33:56 -0700155
Aart Bik8c4a8542016-10-06 11:36:57 -0700156 // Traverse the loop hierarchy inner-to-outer and optimize. Traversal can use
157 // a temporary set that stores instructions using the phase-local allocator.
158 if (top_loop_ != nullptr) {
159 ArenaSet<HInstruction*> iset(loop_allocator_->Adapter(kArenaAllocLoopOptimization));
160 iset_ = &iset;
161 TraverseLoopsInnerToOuter(top_loop_);
162 iset_ = nullptr; // detach
163 }
Aart Bik281c6812016-08-26 11:31:48 -0700164}
165
166void HLoopOptimization::AddLoop(HLoopInformation* loop_info) {
167 DCHECK(loop_info != nullptr);
Nicolas Geoffrayebe16742016-10-05 09:55:42 +0100168 LoopNode* node = new (loop_allocator_) LoopNode(loop_info); // phase-local allocator
Aart Bik281c6812016-08-26 11:31:48 -0700169 if (last_loop_ == nullptr) {
170 // First loop.
171 DCHECK(top_loop_ == nullptr);
172 last_loop_ = top_loop_ = node;
173 } else if (loop_info->IsIn(*last_loop_->loop_info)) {
174 // Inner loop.
175 node->outer = last_loop_;
176 DCHECK(last_loop_->inner == nullptr);
177 last_loop_ = last_loop_->inner = node;
178 } else {
179 // Subsequent loop.
180 while (last_loop_->outer != nullptr && !loop_info->IsIn(*last_loop_->outer->loop_info)) {
181 last_loop_ = last_loop_->outer;
182 }
183 node->outer = last_loop_->outer;
184 node->previous = last_loop_;
185 DCHECK(last_loop_->next == nullptr);
186 last_loop_ = last_loop_->next = node;
187 }
188}
189
190void HLoopOptimization::RemoveLoop(LoopNode* node) {
191 DCHECK(node != nullptr);
Aart Bik8c4a8542016-10-06 11:36:57 -0700192 DCHECK(node->inner == nullptr);
193 if (node->previous != nullptr) {
194 // Within sequence.
195 node->previous->next = node->next;
196 if (node->next != nullptr) {
197 node->next->previous = node->previous;
198 }
199 } else {
200 // First of sequence.
201 if (node->outer != nullptr) {
202 node->outer->inner = node->next;
203 } else {
204 top_loop_ = node->next;
205 }
206 if (node->next != nullptr) {
207 node->next->outer = node->outer;
208 node->next->previous = nullptr;
209 }
210 }
Aart Bik281c6812016-08-26 11:31:48 -0700211}
212
213void HLoopOptimization::TraverseLoopsInnerToOuter(LoopNode* node) {
214 for ( ; node != nullptr; node = node->next) {
Aart Bik482095d2016-10-10 15:39:10 -0700215 int current_induction_simplification_count = induction_simplication_count_;
Aart Bik281c6812016-08-26 11:31:48 -0700216 if (node->inner != nullptr) {
217 TraverseLoopsInnerToOuter(node->inner);
218 }
Aart Bik482095d2016-10-10 15:39:10 -0700219 // Visit loop after its inner loops have been visited. If the induction of any inner
220 // loop has been simplified, recompute the induction information of this loop first.
221 if (current_induction_simplification_count != induction_simplication_count_) {
222 induction_range_.ReVisit(node->loop_info);
223 }
Aart Bik639cc8c2016-10-18 13:03:31 -0700224 SimplifyBlocks(node);
Aart Bik281c6812016-08-26 11:31:48 -0700225 SimplifyInduction(node);
Aart Bik482095d2016-10-10 15:39:10 -0700226 SimplifyBlocks(node);
Aart Bik9abf8942016-10-14 09:49:42 -0700227 if (node->inner == nullptr) {
228 RemoveIfEmptyInnerLoop(node);
229 }
Aart Bik281c6812016-08-26 11:31:48 -0700230 }
231}
232
233void HLoopOptimization::SimplifyInduction(LoopNode* node) {
234 HBasicBlock* header = node->loop_info->GetHeader();
235 HBasicBlock* preheader = node->loop_info->GetPreHeader();
Aart Bik8c4a8542016-10-06 11:36:57 -0700236 // Scan the phis in the header to find opportunities to simplify an induction
237 // cycle that is only used outside the loop. Replace these uses, if any, with
238 // the last value and remove the induction cycle.
239 // Examples: for (int i = 0; x != null; i++) { .... no i .... }
240 // for (int i = 0; i < 10; i++, k++) { .... no k .... } return k;
Aart Bik281c6812016-08-26 11:31:48 -0700241 for (HInstructionIterator it(header->GetPhis()); !it.Done(); it.Advance()) {
242 HPhi* phi = it.Current()->AsPhi();
Aart Bik8c4a8542016-10-06 11:36:57 -0700243 iset_->clear();
244 int32_t use_count = 0;
245 if (IsPhiInduction(phi, iset_) &&
Aart Bik482095d2016-10-10 15:39:10 -0700246 IsOnlyUsedAfterLoop(node->loop_info, phi, &use_count) &&
Aart Bik8c4a8542016-10-06 11:36:57 -0700247 TryReplaceWithLastValue(phi, use_count, preheader)) {
248 for (HInstruction* i : *iset_) {
249 RemoveFromCycle(i);
Aart Bik281c6812016-08-26 11:31:48 -0700250 }
Aart Bik482095d2016-10-10 15:39:10 -0700251 induction_simplication_count_++;
252 }
253 }
254}
255
256void HLoopOptimization::SimplifyBlocks(LoopNode* node) {
257 for (HBlocksInLoopIterator it(*node->loop_info); !it.Done(); it.Advance()) {
258 HBasicBlock* block = it.Current();
259 // Remove instructions that are dead, usually resulting from eliminating induction cycles.
260 for (HBackwardInstructionIterator i(block->GetInstructions()); !i.Done(); i.Advance()) {
261 HInstruction* instruction = i.Current();
262 if (instruction->IsDeadAndRemovable()) {
263 block->RemoveInstruction(instruction);
264 }
265 }
Aart Bik9abf8942016-10-14 09:49:42 -0700266 // Remove trivial control flow blocks from the loop-body, again usually resulting
Aart Bik482095d2016-10-10 15:39:10 -0700267 // from eliminating induction cycles.
268 if (block->GetPredecessors().size() == 1 &&
269 block->GetSuccessors().size() == 1 &&
270 block->GetFirstInstruction()->IsGoto()) {
271 HBasicBlock* pred = block->GetSinglePredecessor();
272 HBasicBlock* succ = block->GetSingleSuccessor();
273 if (succ->GetPredecessors().size() == 1) {
274 pred->ReplaceSuccessor(block, succ);
275 block->ClearDominanceInformation();
276 block->SetDominator(pred); // needed by next disconnect.
277 block->DisconnectAndDelete();
278 pred->AddDominatedBlock(succ);
279 succ->SetDominator(pred);
280 }
Aart Bik281c6812016-08-26 11:31:48 -0700281 }
282 }
283}
284
Aart Bik9abf8942016-10-14 09:49:42 -0700285void HLoopOptimization::RemoveIfEmptyInnerLoop(LoopNode* node) {
Aart Bik281c6812016-08-26 11:31:48 -0700286 HBasicBlock* header = node->loop_info->GetHeader();
287 HBasicBlock* preheader = node->loop_info->GetPreHeader();
Aart Bik9abf8942016-10-14 09:49:42 -0700288 // Ensure loop header logic is finite.
289 if (!induction_range_.IsFinite(node->loop_info)) {
290 return;
291 }
Aart Bik281c6812016-08-26 11:31:48 -0700292 // Ensure there is only a single loop-body (besides the header).
293 HBasicBlock* body = nullptr;
294 for (HBlocksInLoopIterator it(*node->loop_info); !it.Done(); it.Advance()) {
295 if (it.Current() != header) {
296 if (body != nullptr) {
297 return;
298 }
299 body = it.Current();
300 }
301 }
302 // Ensure there is only a single exit point.
303 if (header->GetSuccessors().size() != 2) {
304 return;
305 }
306 HBasicBlock* exit = (header->GetSuccessors()[0] == body)
307 ? header->GetSuccessors()[1]
308 : header->GetSuccessors()[0];
Aart Bik8c4a8542016-10-06 11:36:57 -0700309 // Ensure exit can only be reached by exiting loop.
Aart Bik281c6812016-08-26 11:31:48 -0700310 if (exit->GetPredecessors().size() != 1) {
311 return;
312 }
Aart Bik8c4a8542016-10-06 11:36:57 -0700313 // Detect an empty loop: no side effects other than plain iteration. Replace
314 // subsequent index uses, if any, with the last value and remove the loop.
315 iset_->clear();
316 int32_t use_count = 0;
317 if (IsEmptyHeader(header, iset_) &&
318 IsEmptyBody(body, iset_) &&
Aart Bik482095d2016-10-10 15:39:10 -0700319 IsOnlyUsedAfterLoop(node->loop_info, header->GetFirstPhi(), &use_count) &&
Aart Bik8c4a8542016-10-06 11:36:57 -0700320 TryReplaceWithLastValue(header->GetFirstPhi(), use_count, preheader)) {
Aart Bik281c6812016-08-26 11:31:48 -0700321 body->DisconnectAndDelete();
322 exit->RemovePredecessor(header);
323 header->RemoveSuccessor(exit);
324 header->ClearDominanceInformation();
325 header->SetDominator(preheader); // needed by next disconnect.
326 header->DisconnectAndDelete();
Aart Bik482095d2016-10-10 15:39:10 -0700327 preheader->AddSuccessor(exit);
328 preheader->AddInstruction(new (graph_->GetArena()) HGoto()); // global allocator
329 preheader->AddDominatedBlock(exit);
330 exit->SetDominator(preheader);
Aart Bik281c6812016-08-26 11:31:48 -0700331 // Update hierarchy.
332 RemoveLoop(node);
333 }
334}
335
Aart Bik482095d2016-10-10 15:39:10 -0700336bool HLoopOptimization::IsOnlyUsedAfterLoop(HLoopInformation* loop_info,
Aart Bik8c4a8542016-10-06 11:36:57 -0700337 HInstruction* instruction,
338 /*out*/ int32_t* use_count) {
339 for (const HUseListNode<HInstruction*>& use : instruction->GetUses()) {
340 HInstruction* user = use.GetUser();
341 if (iset_->find(user) == iset_->end()) { // not excluded?
342 HLoopInformation* other_loop_info = user->GetBlock()->GetLoopInformation();
Aart Bik482095d2016-10-10 15:39:10 -0700343 if (other_loop_info != nullptr && other_loop_info->IsIn(*loop_info)) {
Aart Bik8c4a8542016-10-06 11:36:57 -0700344 return false;
345 }
346 ++*use_count;
347 }
348 }
349 return true;
350}
351
352void HLoopOptimization::ReplaceAllUses(HInstruction* instruction, HInstruction* replacement) {
Aart Bik281c6812016-08-26 11:31:48 -0700353 const HUseList<HInstruction*>& uses = instruction->GetUses();
354 for (auto it = uses.begin(), end = uses.end(); it != end;) {
355 HInstruction* user = it->GetUser();
356 size_t index = it->GetIndex();
357 ++it; // increment before replacing
Aart Bik8c4a8542016-10-06 11:36:57 -0700358 if (iset_->find(user) == iset_->end()) { // not excluded?
Aart Bik281c6812016-08-26 11:31:48 -0700359 user->ReplaceInput(replacement, index);
360 induction_range_.Replace(user, instruction, replacement); // update induction
361 }
362 }
363 const HUseList<HEnvironment*>& env_uses = instruction->GetEnvUses();
364 for (auto it = env_uses.begin(), end = env_uses.end(); it != end;) {
365 HEnvironment* user = it->GetUser();
366 size_t index = it->GetIndex();
367 ++it; // increment before replacing
Aart Bik8c4a8542016-10-06 11:36:57 -0700368 if (iset_->find(user->GetHolder()) == iset_->end()) { // not excluded?
Aart Bik281c6812016-08-26 11:31:48 -0700369 user->RemoveAsUserOfInput(index);
370 user->SetRawEnvAt(index, replacement);
371 replacement->AddEnvUseAt(user, index);
372 }
373 }
374}
375
Aart Bik8c4a8542016-10-06 11:36:57 -0700376bool HLoopOptimization::TryReplaceWithLastValue(HInstruction* instruction,
377 int32_t use_count,
378 HBasicBlock* block) {
379 // If true uses appear after the loop, replace these uses with the last value. Environment
380 // uses can consume this value too, since any first true use is outside the loop (although
381 // this may imply that de-opting may look "ahead" a bit on the phi value). If there are only
382 // environment uses, the value is dropped altogether, since the computations have no effect.
383 if (use_count > 0) {
384 if (!induction_range_.CanGenerateLastValue(instruction)) {
385 return false;
386 }
387 ReplaceAllUses(instruction, induction_range_.GenerateLastValue(instruction, graph_, block));
388 }
389 return true;
390}
391
Aart Bik281c6812016-08-26 11:31:48 -0700392} // namespace art