blob: 703a10402d098a9058f3dc16b1f8b00ab7823a9d [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 Bik281c6812016-08-26 11:31:48 -0700224 SimplifyInduction(node);
Aart Bik482095d2016-10-10 15:39:10 -0700225 SimplifyBlocks(node);
Aart Bik9abf8942016-10-14 09:49:42 -0700226 if (node->inner == nullptr) {
227 RemoveIfEmptyInnerLoop(node);
228 }
Aart Bik281c6812016-08-26 11:31:48 -0700229 }
230}
231
232void HLoopOptimization::SimplifyInduction(LoopNode* node) {
233 HBasicBlock* header = node->loop_info->GetHeader();
234 HBasicBlock* preheader = node->loop_info->GetPreHeader();
Aart Bik8c4a8542016-10-06 11:36:57 -0700235 // Scan the phis in the header to find opportunities to simplify an induction
236 // cycle that is only used outside the loop. Replace these uses, if any, with
237 // the last value and remove the induction cycle.
238 // Examples: for (int i = 0; x != null; i++) { .... no i .... }
239 // for (int i = 0; i < 10; i++, k++) { .... no k .... } return k;
Aart Bik281c6812016-08-26 11:31:48 -0700240 for (HInstructionIterator it(header->GetPhis()); !it.Done(); it.Advance()) {
241 HPhi* phi = it.Current()->AsPhi();
Aart Bik8c4a8542016-10-06 11:36:57 -0700242 iset_->clear();
243 int32_t use_count = 0;
244 if (IsPhiInduction(phi, iset_) &&
Aart Bik482095d2016-10-10 15:39:10 -0700245 IsOnlyUsedAfterLoop(node->loop_info, phi, &use_count) &&
Aart Bik8c4a8542016-10-06 11:36:57 -0700246 TryReplaceWithLastValue(phi, use_count, preheader)) {
247 for (HInstruction* i : *iset_) {
248 RemoveFromCycle(i);
Aart Bik281c6812016-08-26 11:31:48 -0700249 }
Aart Bik482095d2016-10-10 15:39:10 -0700250 induction_simplication_count_++;
251 }
252 }
253}
254
255void HLoopOptimization::SimplifyBlocks(LoopNode* node) {
256 for (HBlocksInLoopIterator it(*node->loop_info); !it.Done(); it.Advance()) {
257 HBasicBlock* block = it.Current();
258 // Remove instructions that are dead, usually resulting from eliminating induction cycles.
259 for (HBackwardInstructionIterator i(block->GetInstructions()); !i.Done(); i.Advance()) {
260 HInstruction* instruction = i.Current();
261 if (instruction->IsDeadAndRemovable()) {
262 block->RemoveInstruction(instruction);
263 }
264 }
Aart Bik9abf8942016-10-14 09:49:42 -0700265 // Remove trivial control flow blocks from the loop-body, again usually resulting
Aart Bik482095d2016-10-10 15:39:10 -0700266 // from eliminating induction cycles.
267 if (block->GetPredecessors().size() == 1 &&
268 block->GetSuccessors().size() == 1 &&
269 block->GetFirstInstruction()->IsGoto()) {
270 HBasicBlock* pred = block->GetSinglePredecessor();
271 HBasicBlock* succ = block->GetSingleSuccessor();
272 if (succ->GetPredecessors().size() == 1) {
273 pred->ReplaceSuccessor(block, succ);
274 block->ClearDominanceInformation();
275 block->SetDominator(pred); // needed by next disconnect.
276 block->DisconnectAndDelete();
277 pred->AddDominatedBlock(succ);
278 succ->SetDominator(pred);
279 }
Aart Bik281c6812016-08-26 11:31:48 -0700280 }
281 }
282}
283
Aart Bik9abf8942016-10-14 09:49:42 -0700284void HLoopOptimization::RemoveIfEmptyInnerLoop(LoopNode* node) {
Aart Bik281c6812016-08-26 11:31:48 -0700285 HBasicBlock* header = node->loop_info->GetHeader();
286 HBasicBlock* preheader = node->loop_info->GetPreHeader();
Aart Bik9abf8942016-10-14 09:49:42 -0700287 // Ensure loop header logic is finite.
288 if (!induction_range_.IsFinite(node->loop_info)) {
289 return;
290 }
Aart Bik281c6812016-08-26 11:31:48 -0700291 // Ensure there is only a single loop-body (besides the header).
292 HBasicBlock* body = nullptr;
293 for (HBlocksInLoopIterator it(*node->loop_info); !it.Done(); it.Advance()) {
294 if (it.Current() != header) {
295 if (body != nullptr) {
296 return;
297 }
298 body = it.Current();
299 }
300 }
301 // Ensure there is only a single exit point.
302 if (header->GetSuccessors().size() != 2) {
303 return;
304 }
305 HBasicBlock* exit = (header->GetSuccessors()[0] == body)
306 ? header->GetSuccessors()[1]
307 : header->GetSuccessors()[0];
Aart Bik8c4a8542016-10-06 11:36:57 -0700308 // Ensure exit can only be reached by exiting loop.
Aart Bik281c6812016-08-26 11:31:48 -0700309 if (exit->GetPredecessors().size() != 1) {
310 return;
311 }
Aart Bik8c4a8542016-10-06 11:36:57 -0700312 // Detect an empty loop: no side effects other than plain iteration. Replace
313 // subsequent index uses, if any, with the last value and remove the loop.
314 iset_->clear();
315 int32_t use_count = 0;
316 if (IsEmptyHeader(header, iset_) &&
317 IsEmptyBody(body, iset_) &&
Aart Bik482095d2016-10-10 15:39:10 -0700318 IsOnlyUsedAfterLoop(node->loop_info, header->GetFirstPhi(), &use_count) &&
Aart Bik8c4a8542016-10-06 11:36:57 -0700319 TryReplaceWithLastValue(header->GetFirstPhi(), use_count, preheader)) {
Aart Bik281c6812016-08-26 11:31:48 -0700320 body->DisconnectAndDelete();
321 exit->RemovePredecessor(header);
322 header->RemoveSuccessor(exit);
323 header->ClearDominanceInformation();
324 header->SetDominator(preheader); // needed by next disconnect.
325 header->DisconnectAndDelete();
Aart Bik482095d2016-10-10 15:39:10 -0700326 preheader->AddSuccessor(exit);
327 preheader->AddInstruction(new (graph_->GetArena()) HGoto()); // global allocator
328 preheader->AddDominatedBlock(exit);
329 exit->SetDominator(preheader);
Aart Bik281c6812016-08-26 11:31:48 -0700330 // Update hierarchy.
331 RemoveLoop(node);
332 }
333}
334
Aart Bik482095d2016-10-10 15:39:10 -0700335bool HLoopOptimization::IsOnlyUsedAfterLoop(HLoopInformation* loop_info,
Aart Bik8c4a8542016-10-06 11:36:57 -0700336 HInstruction* instruction,
337 /*out*/ int32_t* use_count) {
338 for (const HUseListNode<HInstruction*>& use : instruction->GetUses()) {
339 HInstruction* user = use.GetUser();
340 if (iset_->find(user) == iset_->end()) { // not excluded?
341 HLoopInformation* other_loop_info = user->GetBlock()->GetLoopInformation();
Aart Bik482095d2016-10-10 15:39:10 -0700342 if (other_loop_info != nullptr && other_loop_info->IsIn(*loop_info)) {
Aart Bik8c4a8542016-10-06 11:36:57 -0700343 return false;
344 }
345 ++*use_count;
346 }
347 }
348 return true;
349}
350
351void HLoopOptimization::ReplaceAllUses(HInstruction* instruction, HInstruction* replacement) {
Aart Bik281c6812016-08-26 11:31:48 -0700352 const HUseList<HInstruction*>& uses = instruction->GetUses();
353 for (auto it = uses.begin(), end = uses.end(); it != end;) {
354 HInstruction* user = it->GetUser();
355 size_t index = it->GetIndex();
356 ++it; // increment before replacing
Aart Bik8c4a8542016-10-06 11:36:57 -0700357 if (iset_->find(user) == iset_->end()) { // not excluded?
Aart Bik281c6812016-08-26 11:31:48 -0700358 user->ReplaceInput(replacement, index);
359 induction_range_.Replace(user, instruction, replacement); // update induction
360 }
361 }
362 const HUseList<HEnvironment*>& env_uses = instruction->GetEnvUses();
363 for (auto it = env_uses.begin(), end = env_uses.end(); it != end;) {
364 HEnvironment* user = it->GetUser();
365 size_t index = it->GetIndex();
366 ++it; // increment before replacing
Aart Bik8c4a8542016-10-06 11:36:57 -0700367 if (iset_->find(user->GetHolder()) == iset_->end()) { // not excluded?
Aart Bik281c6812016-08-26 11:31:48 -0700368 user->RemoveAsUserOfInput(index);
369 user->SetRawEnvAt(index, replacement);
370 replacement->AddEnvUseAt(user, index);
371 }
372 }
373}
374
Aart Bik8c4a8542016-10-06 11:36:57 -0700375bool HLoopOptimization::TryReplaceWithLastValue(HInstruction* instruction,
376 int32_t use_count,
377 HBasicBlock* block) {
378 // If true uses appear after the loop, replace these uses with the last value. Environment
379 // uses can consume this value too, since any first true use is outside the loop (although
380 // this may imply that de-opting may look "ahead" a bit on the phi value). If there are only
381 // environment uses, the value is dropped altogether, since the computations have no effect.
382 if (use_count > 0) {
383 if (!induction_range_.CanGenerateLastValue(instruction)) {
384 return false;
385 }
386 ReplaceAllUses(instruction, induction_range_.GenerateLastValue(instruction, graph_, block));
387 }
388 return true;
389}
390
Aart Bik281c6812016-08-26 11:31:48 -0700391} // namespace art