blob: 33fa87d568355b3ae83965a73ed30725f4e1023c [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
23// TODO: Generalize to cycles, as found by induction analysis?
Aart Bik8c4a8542016-10-06 11:36:57 -070024static bool IsPhiInduction(HPhi* phi, ArenaSet<HInstruction*>* iset) {
25 DCHECK(iset->empty());
Aart Bik281c6812016-08-26 11:31:48 -070026 HInputsRef inputs = phi->GetInputs();
27 if (inputs.size() == 2 && (inputs[1]->IsAdd() || inputs[1]->IsSub())) {
28 HInstruction* addsub = inputs[1];
29 if (addsub->InputAt(0) == phi || addsub->InputAt(1) == phi) {
30 if (addsub->GetUses().HasExactlyOneElement()) {
Aart Bik8c4a8542016-10-06 11:36:57 -070031 iset->insert(phi);
32 iset->insert(addsub);
Aart Bik281c6812016-08-26 11:31:48 -070033 return true;
34 }
35 }
36 }
37 return false;
38}
39
Aart Bik281c6812016-08-26 11:31:48 -070040// Find: phi: Phi(init, addsub)
41// s: SuspendCheck
42// c: Condition(phi, bound)
43// i: If(c)
44// TODO: Find a less pattern matching approach?
Aart Bik8c4a8542016-10-06 11:36:57 -070045static bool IsEmptyHeader(HBasicBlock* block, ArenaSet<HInstruction*>* iset) {
46 DCHECK(iset->empty());
Aart Bik281c6812016-08-26 11:31:48 -070047 HInstruction* phi = block->GetFirstPhi();
Aart Bik8c4a8542016-10-06 11:36:57 -070048 if (phi != nullptr && phi->GetNext() == nullptr && IsPhiInduction(phi->AsPhi(), iset)) {
Aart Bik281c6812016-08-26 11:31:48 -070049 HInstruction* s = block->GetFirstInstruction();
50 if (s != nullptr && s->IsSuspendCheck()) {
51 HInstruction* c = s->GetNext();
52 if (c != nullptr && c->IsCondition() && c->GetUses().HasExactlyOneElement()) {
53 HInstruction* i = c->GetNext();
54 if (i != nullptr && i->IsIf() && i->InputAt(0) == c) {
Aart Bik8c4a8542016-10-06 11:36:57 -070055 iset->insert(c);
56 iset->insert(s);
Aart Bik281c6812016-08-26 11:31:48 -070057 return true;
58 }
59 }
60 }
61 }
62 return false;
63}
64
Aart Bik8c4a8542016-10-06 11:36:57 -070065static bool IsEmptyBody(HBasicBlock* block, ArenaSet<HInstruction*>* iset) {
Aart Bik281c6812016-08-26 11:31:48 -070066 HInstruction* phi = block->GetFirstPhi();
67 HInstruction* i = block->GetFirstInstruction();
Aart Bik8c4a8542016-10-06 11:36:57 -070068 return phi == nullptr && iset->find(i) != iset->end() &&
69 i->GetNext() != nullptr && i->GetNext()->IsGoto();
Aart Bik281c6812016-08-26 11:31:48 -070070}
71
Aart Bik281c6812016-08-26 11:31:48 -070072static void RemoveFromCycle(HInstruction* instruction) {
73 // A bit more elaborate than the usual instruction removal,
74 // since there may be a cycle in the use structure.
75 instruction->RemoveAsUserOfAllInputs();
76 instruction->RemoveEnvironmentUsers();
77 instruction->GetBlock()->RemoveInstructionOrPhi(instruction, /*ensure_safety=*/ false);
78}
79
80//
81// Class methods.
82//
83
84HLoopOptimization::HLoopOptimization(HGraph* graph,
85 HInductionVarAnalysis* induction_analysis)
86 : HOptimization(graph, kLoopOptimizationPassName),
87 induction_range_(induction_analysis),
Aart Bik96202302016-10-04 17:33:56 -070088 loop_allocator_(nullptr),
Aart Bik281c6812016-08-26 11:31:48 -070089 top_loop_(nullptr),
Aart Bik8c4a8542016-10-06 11:36:57 -070090 last_loop_(nullptr),
Aart Bik482095d2016-10-10 15:39:10 -070091 iset_(nullptr),
92 induction_simplication_count_(0) {
Aart Bik281c6812016-08-26 11:31:48 -070093}
94
95void HLoopOptimization::Run() {
96 // Well-behaved loops only.
97 // TODO: make this less of a sledgehammer.
Aart Bik96202302016-10-04 17:33:56 -070098 if (graph_->HasTryCatch() || graph_->HasIrreducibleLoops()) {
Aart Bik281c6812016-08-26 11:31:48 -070099 return;
100 }
101
Aart Bik96202302016-10-04 17:33:56 -0700102 // Phase-local allocator that draws from the global pool. Since the allocator
103 // itself resides on the stack, it is destructed on exiting Run(), which
104 // implies its underlying memory is released immediately.
Nicolas Geoffrayebe16742016-10-05 09:55:42 +0100105 ArenaAllocator allocator(graph_->GetArena()->GetArenaPool());
Aart Bik96202302016-10-04 17:33:56 -0700106 loop_allocator_ = &allocator;
Nicolas Geoffrayebe16742016-10-05 09:55:42 +0100107
Aart Bik96202302016-10-04 17:33:56 -0700108 // Perform loop optimizations.
109 LocalRun();
110
111 // Detach.
112 loop_allocator_ = nullptr;
113 last_loop_ = top_loop_ = nullptr;
114}
115
116void HLoopOptimization::LocalRun() {
117 // Build the linear order using the phase-local allocator. This step enables building
118 // a loop hierarchy that properly reflects the outer-inner and previous-next relation.
119 ArenaVector<HBasicBlock*> linear_order(loop_allocator_->Adapter(kArenaAllocLinearOrder));
120 LinearizeGraph(graph_, loop_allocator_, &linear_order);
121
Aart Bik281c6812016-08-26 11:31:48 -0700122 // Build the loop hierarchy.
Aart Bik96202302016-10-04 17:33:56 -0700123 for (HBasicBlock* block : linear_order) {
Aart Bik281c6812016-08-26 11:31:48 -0700124 if (block->IsLoopHeader()) {
125 AddLoop(block->GetLoopInformation());
126 }
127 }
Aart Bik96202302016-10-04 17:33:56 -0700128
Aart Bik8c4a8542016-10-06 11:36:57 -0700129 // Traverse the loop hierarchy inner-to-outer and optimize. Traversal can use
130 // a temporary set that stores instructions using the phase-local allocator.
131 if (top_loop_ != nullptr) {
132 ArenaSet<HInstruction*> iset(loop_allocator_->Adapter(kArenaAllocLoopOptimization));
133 iset_ = &iset;
134 TraverseLoopsInnerToOuter(top_loop_);
135 iset_ = nullptr; // detach
136 }
Aart Bik281c6812016-08-26 11:31:48 -0700137}
138
139void HLoopOptimization::AddLoop(HLoopInformation* loop_info) {
140 DCHECK(loop_info != nullptr);
Nicolas Geoffrayebe16742016-10-05 09:55:42 +0100141 LoopNode* node = new (loop_allocator_) LoopNode(loop_info); // phase-local allocator
Aart Bik281c6812016-08-26 11:31:48 -0700142 if (last_loop_ == nullptr) {
143 // First loop.
144 DCHECK(top_loop_ == nullptr);
145 last_loop_ = top_loop_ = node;
146 } else if (loop_info->IsIn(*last_loop_->loop_info)) {
147 // Inner loop.
148 node->outer = last_loop_;
149 DCHECK(last_loop_->inner == nullptr);
150 last_loop_ = last_loop_->inner = node;
151 } else {
152 // Subsequent loop.
153 while (last_loop_->outer != nullptr && !loop_info->IsIn(*last_loop_->outer->loop_info)) {
154 last_loop_ = last_loop_->outer;
155 }
156 node->outer = last_loop_->outer;
157 node->previous = last_loop_;
158 DCHECK(last_loop_->next == nullptr);
159 last_loop_ = last_loop_->next = node;
160 }
161}
162
163void HLoopOptimization::RemoveLoop(LoopNode* node) {
164 DCHECK(node != nullptr);
Aart Bik8c4a8542016-10-06 11:36:57 -0700165 DCHECK(node->inner == nullptr);
166 if (node->previous != nullptr) {
167 // Within sequence.
168 node->previous->next = node->next;
169 if (node->next != nullptr) {
170 node->next->previous = node->previous;
171 }
172 } else {
173 // First of sequence.
174 if (node->outer != nullptr) {
175 node->outer->inner = node->next;
176 } else {
177 top_loop_ = node->next;
178 }
179 if (node->next != nullptr) {
180 node->next->outer = node->outer;
181 node->next->previous = nullptr;
182 }
183 }
Aart Bik281c6812016-08-26 11:31:48 -0700184}
185
186void HLoopOptimization::TraverseLoopsInnerToOuter(LoopNode* node) {
187 for ( ; node != nullptr; node = node->next) {
Aart Bik482095d2016-10-10 15:39:10 -0700188 int current_induction_simplification_count = induction_simplication_count_;
Aart Bik281c6812016-08-26 11:31:48 -0700189 if (node->inner != nullptr) {
190 TraverseLoopsInnerToOuter(node->inner);
191 }
Aart Bik482095d2016-10-10 15:39:10 -0700192 // Visit loop after its inner loops have been visited. If the induction of any inner
193 // loop has been simplified, recompute the induction information of this loop first.
194 if (current_induction_simplification_count != induction_simplication_count_) {
195 induction_range_.ReVisit(node->loop_info);
196 }
Aart Bik281c6812016-08-26 11:31:48 -0700197 SimplifyInduction(node);
Aart Bik482095d2016-10-10 15:39:10 -0700198 SimplifyBlocks(node);
Aart Bik281c6812016-08-26 11:31:48 -0700199 RemoveIfEmptyLoop(node);
200 }
201}
202
203void HLoopOptimization::SimplifyInduction(LoopNode* node) {
204 HBasicBlock* header = node->loop_info->GetHeader();
205 HBasicBlock* preheader = node->loop_info->GetPreHeader();
Aart Bik8c4a8542016-10-06 11:36:57 -0700206 // Scan the phis in the header to find opportunities to simplify an induction
207 // cycle that is only used outside the loop. Replace these uses, if any, with
208 // the last value and remove the induction cycle.
209 // Examples: for (int i = 0; x != null; i++) { .... no i .... }
210 // for (int i = 0; i < 10; i++, k++) { .... no k .... } return k;
Aart Bik281c6812016-08-26 11:31:48 -0700211 for (HInstructionIterator it(header->GetPhis()); !it.Done(); it.Advance()) {
212 HPhi* phi = it.Current()->AsPhi();
Aart Bik8c4a8542016-10-06 11:36:57 -0700213 iset_->clear();
214 int32_t use_count = 0;
215 if (IsPhiInduction(phi, iset_) &&
Aart Bik482095d2016-10-10 15:39:10 -0700216 IsOnlyUsedAfterLoop(node->loop_info, phi, &use_count) &&
Aart Bik8c4a8542016-10-06 11:36:57 -0700217 TryReplaceWithLastValue(phi, use_count, preheader)) {
218 for (HInstruction* i : *iset_) {
219 RemoveFromCycle(i);
Aart Bik281c6812016-08-26 11:31:48 -0700220 }
Aart Bik482095d2016-10-10 15:39:10 -0700221 induction_simplication_count_++;
222 }
223 }
224}
225
226void HLoopOptimization::SimplifyBlocks(LoopNode* node) {
227 for (HBlocksInLoopIterator it(*node->loop_info); !it.Done(); it.Advance()) {
228 HBasicBlock* block = it.Current();
229 // Remove instructions that are dead, usually resulting from eliminating induction cycles.
230 for (HBackwardInstructionIterator i(block->GetInstructions()); !i.Done(); i.Advance()) {
231 HInstruction* instruction = i.Current();
232 if (instruction->IsDeadAndRemovable()) {
233 block->RemoveInstruction(instruction);
234 }
235 }
236 // Remove trivial control flow blocks from the loop body, again usually resulting
237 // from eliminating induction cycles.
238 if (block->GetPredecessors().size() == 1 &&
239 block->GetSuccessors().size() == 1 &&
240 block->GetFirstInstruction()->IsGoto()) {
241 HBasicBlock* pred = block->GetSinglePredecessor();
242 HBasicBlock* succ = block->GetSingleSuccessor();
243 if (succ->GetPredecessors().size() == 1) {
244 pred->ReplaceSuccessor(block, succ);
245 block->ClearDominanceInformation();
246 block->SetDominator(pred); // needed by next disconnect.
247 block->DisconnectAndDelete();
248 pred->AddDominatedBlock(succ);
249 succ->SetDominator(pred);
250 }
Aart Bik281c6812016-08-26 11:31:48 -0700251 }
252 }
253}
254
255void HLoopOptimization::RemoveIfEmptyLoop(LoopNode* node) {
256 HBasicBlock* header = node->loop_info->GetHeader();
257 HBasicBlock* preheader = node->loop_info->GetPreHeader();
258 // Ensure there is only a single loop-body (besides the header).
259 HBasicBlock* body = nullptr;
260 for (HBlocksInLoopIterator it(*node->loop_info); !it.Done(); it.Advance()) {
261 if (it.Current() != header) {
262 if (body != nullptr) {
263 return;
264 }
265 body = it.Current();
266 }
267 }
268 // Ensure there is only a single exit point.
269 if (header->GetSuccessors().size() != 2) {
270 return;
271 }
272 HBasicBlock* exit = (header->GetSuccessors()[0] == body)
273 ? header->GetSuccessors()[1]
274 : header->GetSuccessors()[0];
Aart Bik8c4a8542016-10-06 11:36:57 -0700275 // Ensure exit can only be reached by exiting loop.
Aart Bik281c6812016-08-26 11:31:48 -0700276 if (exit->GetPredecessors().size() != 1) {
277 return;
278 }
Aart Bik8c4a8542016-10-06 11:36:57 -0700279 // Detect an empty loop: no side effects other than plain iteration. Replace
280 // subsequent index uses, if any, with the last value and remove the loop.
281 iset_->clear();
282 int32_t use_count = 0;
283 if (IsEmptyHeader(header, iset_) &&
284 IsEmptyBody(body, iset_) &&
Aart Bik482095d2016-10-10 15:39:10 -0700285 IsOnlyUsedAfterLoop(node->loop_info, header->GetFirstPhi(), &use_count) &&
Aart Bik8c4a8542016-10-06 11:36:57 -0700286 TryReplaceWithLastValue(header->GetFirstPhi(), use_count, preheader)) {
Aart Bik281c6812016-08-26 11:31:48 -0700287 body->DisconnectAndDelete();
288 exit->RemovePredecessor(header);
289 header->RemoveSuccessor(exit);
290 header->ClearDominanceInformation();
291 header->SetDominator(preheader); // needed by next disconnect.
292 header->DisconnectAndDelete();
Aart Bik482095d2016-10-10 15:39:10 -0700293 preheader->AddSuccessor(exit);
294 preheader->AddInstruction(new (graph_->GetArena()) HGoto()); // global allocator
295 preheader->AddDominatedBlock(exit);
296 exit->SetDominator(preheader);
Aart Bik281c6812016-08-26 11:31:48 -0700297 // Update hierarchy.
298 RemoveLoop(node);
299 }
300}
301
Aart Bik482095d2016-10-10 15:39:10 -0700302bool HLoopOptimization::IsOnlyUsedAfterLoop(HLoopInformation* loop_info,
Aart Bik8c4a8542016-10-06 11:36:57 -0700303 HInstruction* instruction,
304 /*out*/ int32_t* use_count) {
305 for (const HUseListNode<HInstruction*>& use : instruction->GetUses()) {
306 HInstruction* user = use.GetUser();
307 if (iset_->find(user) == iset_->end()) { // not excluded?
308 HLoopInformation* other_loop_info = user->GetBlock()->GetLoopInformation();
Aart Bik482095d2016-10-10 15:39:10 -0700309 if (other_loop_info != nullptr && other_loop_info->IsIn(*loop_info)) {
Aart Bik8c4a8542016-10-06 11:36:57 -0700310 return false;
311 }
312 ++*use_count;
313 }
314 }
315 return true;
316}
317
318void HLoopOptimization::ReplaceAllUses(HInstruction* instruction, HInstruction* replacement) {
Aart Bik281c6812016-08-26 11:31:48 -0700319 const HUseList<HInstruction*>& uses = instruction->GetUses();
320 for (auto it = uses.begin(), end = uses.end(); it != end;) {
321 HInstruction* user = it->GetUser();
322 size_t index = it->GetIndex();
323 ++it; // increment before replacing
Aart Bik8c4a8542016-10-06 11:36:57 -0700324 if (iset_->find(user) == iset_->end()) { // not excluded?
Aart Bik281c6812016-08-26 11:31:48 -0700325 user->ReplaceInput(replacement, index);
326 induction_range_.Replace(user, instruction, replacement); // update induction
327 }
328 }
329 const HUseList<HEnvironment*>& env_uses = instruction->GetEnvUses();
330 for (auto it = env_uses.begin(), end = env_uses.end(); it != end;) {
331 HEnvironment* user = it->GetUser();
332 size_t index = it->GetIndex();
333 ++it; // increment before replacing
Aart Bik8c4a8542016-10-06 11:36:57 -0700334 if (iset_->find(user->GetHolder()) == iset_->end()) { // not excluded?
Aart Bik281c6812016-08-26 11:31:48 -0700335 user->RemoveAsUserOfInput(index);
336 user->SetRawEnvAt(index, replacement);
337 replacement->AddEnvUseAt(user, index);
338 }
339 }
340}
341
Aart Bik8c4a8542016-10-06 11:36:57 -0700342bool HLoopOptimization::TryReplaceWithLastValue(HInstruction* instruction,
343 int32_t use_count,
344 HBasicBlock* block) {
345 // If true uses appear after the loop, replace these uses with the last value. Environment
346 // uses can consume this value too, since any first true use is outside the loop (although
347 // this may imply that de-opting may look "ahead" a bit on the phi value). If there are only
348 // environment uses, the value is dropped altogether, since the computations have no effect.
349 if (use_count > 0) {
350 if (!induction_range_.CanGenerateLastValue(instruction)) {
351 return false;
352 }
353 ReplaceAllUses(instruction, induction_range_.GenerateLastValue(instruction, graph_, block));
354 }
355 return true;
356}
357
Aart Bik281c6812016-08-26 11:31:48 -0700358} // namespace art