blob: 93c6c20d7c5fa7d03df5ce5d58d3572042320694 [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
72static HBasicBlock* TryRemovePreHeader(HBasicBlock* preheader, HBasicBlock* entry_block) {
73 if (preheader->GetPredecessors().size() == 1) {
74 HBasicBlock* entry = preheader->GetSinglePredecessor();
75 HInstruction* anchor = entry->GetLastInstruction();
76 // If the pre-header has a single predecessor we can remove it too if
77 // either the pre-header just contains a goto, or if the predecessor
78 // is not the entry block so we can push instructions backward
79 // (moving computation into the entry block is too dangerous!).
80 if (preheader->GetFirstInstruction() == nullptr ||
81 preheader->GetFirstInstruction()->IsGoto() ||
82 (entry != entry_block && anchor->IsGoto())) {
83 // Push non-goto statements backward to empty the pre-header.
84 for (HInstructionIterator it(preheader->GetInstructions()); !it.Done(); it.Advance()) {
85 HInstruction* instruction = it.Current();
86 if (!instruction->IsGoto()) {
87 if (!instruction->CanBeMoved()) {
88 return nullptr; // pushing failed to move all
89 }
90 it.Current()->MoveBefore(anchor);
91 }
92 }
93 return entry;
94 }
95 }
96 return nullptr;
97}
98
99static void RemoveFromCycle(HInstruction* instruction) {
100 // A bit more elaborate than the usual instruction removal,
101 // since there may be a cycle in the use structure.
102 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),
118 iset_(nullptr) {
Aart Bik281c6812016-08-26 11:31:48 -0700119}
120
121void HLoopOptimization::Run() {
122 // Well-behaved loops only.
123 // TODO: make this less of a sledgehammer.
Aart Bik96202302016-10-04 17:33:56 -0700124 if (graph_->HasTryCatch() || graph_->HasIrreducibleLoops()) {
Aart Bik281c6812016-08-26 11:31:48 -0700125 return;
126 }
127
Aart Bik96202302016-10-04 17:33:56 -0700128 // Phase-local allocator that draws from the global pool. Since the allocator
129 // itself resides on the stack, it is destructed on exiting Run(), which
130 // implies its underlying memory is released immediately.
Nicolas Geoffrayebe16742016-10-05 09:55:42 +0100131 ArenaAllocator allocator(graph_->GetArena()->GetArenaPool());
Aart Bik96202302016-10-04 17:33:56 -0700132 loop_allocator_ = &allocator;
Nicolas Geoffrayebe16742016-10-05 09:55:42 +0100133
Aart Bik96202302016-10-04 17:33:56 -0700134 // Perform loop optimizations.
135 LocalRun();
136
137 // Detach.
138 loop_allocator_ = nullptr;
139 last_loop_ = top_loop_ = nullptr;
140}
141
142void HLoopOptimization::LocalRun() {
143 // Build the linear order using the phase-local allocator. This step enables building
144 // a loop hierarchy that properly reflects the outer-inner and previous-next relation.
145 ArenaVector<HBasicBlock*> linear_order(loop_allocator_->Adapter(kArenaAllocLinearOrder));
146 LinearizeGraph(graph_, loop_allocator_, &linear_order);
147
Aart Bik281c6812016-08-26 11:31:48 -0700148 // Build the loop hierarchy.
Aart Bik96202302016-10-04 17:33:56 -0700149 for (HBasicBlock* block : linear_order) {
Aart Bik281c6812016-08-26 11:31:48 -0700150 if (block->IsLoopHeader()) {
151 AddLoop(block->GetLoopInformation());
152 }
153 }
Aart Bik96202302016-10-04 17:33:56 -0700154
Aart Bik8c4a8542016-10-06 11:36:57 -0700155 // Traverse the loop hierarchy inner-to-outer and optimize. Traversal can use
156 // a temporary set that stores instructions using the phase-local allocator.
157 if (top_loop_ != nullptr) {
158 ArenaSet<HInstruction*> iset(loop_allocator_->Adapter(kArenaAllocLoopOptimization));
159 iset_ = &iset;
160 TraverseLoopsInnerToOuter(top_loop_);
161 iset_ = nullptr; // detach
162 }
Aart Bik281c6812016-08-26 11:31:48 -0700163}
164
165void HLoopOptimization::AddLoop(HLoopInformation* loop_info) {
166 DCHECK(loop_info != nullptr);
Nicolas Geoffrayebe16742016-10-05 09:55:42 +0100167 LoopNode* node = new (loop_allocator_) LoopNode(loop_info); // phase-local allocator
Aart Bik281c6812016-08-26 11:31:48 -0700168 if (last_loop_ == nullptr) {
169 // First loop.
170 DCHECK(top_loop_ == nullptr);
171 last_loop_ = top_loop_ = node;
172 } else if (loop_info->IsIn(*last_loop_->loop_info)) {
173 // Inner loop.
174 node->outer = last_loop_;
175 DCHECK(last_loop_->inner == nullptr);
176 last_loop_ = last_loop_->inner = node;
177 } else {
178 // Subsequent loop.
179 while (last_loop_->outer != nullptr && !loop_info->IsIn(*last_loop_->outer->loop_info)) {
180 last_loop_ = last_loop_->outer;
181 }
182 node->outer = last_loop_->outer;
183 node->previous = last_loop_;
184 DCHECK(last_loop_->next == nullptr);
185 last_loop_ = last_loop_->next = node;
186 }
187}
188
189void HLoopOptimization::RemoveLoop(LoopNode* node) {
190 DCHECK(node != nullptr);
Aart Bik8c4a8542016-10-06 11:36:57 -0700191 DCHECK(node->inner == nullptr);
192 if (node->previous != nullptr) {
193 // Within sequence.
194 node->previous->next = node->next;
195 if (node->next != nullptr) {
196 node->next->previous = node->previous;
197 }
198 } else {
199 // First of sequence.
200 if (node->outer != nullptr) {
201 node->outer->inner = node->next;
202 } else {
203 top_loop_ = node->next;
204 }
205 if (node->next != nullptr) {
206 node->next->outer = node->outer;
207 node->next->previous = nullptr;
208 }
209 }
Aart Bik281c6812016-08-26 11:31:48 -0700210}
211
212void HLoopOptimization::TraverseLoopsInnerToOuter(LoopNode* node) {
213 for ( ; node != nullptr; node = node->next) {
214 if (node->inner != nullptr) {
215 TraverseLoopsInnerToOuter(node->inner);
216 }
217 // Visit loop after its inner loops have been visited.
218 SimplifyInduction(node);
219 RemoveIfEmptyLoop(node);
220 }
221}
222
223void HLoopOptimization::SimplifyInduction(LoopNode* node) {
224 HBasicBlock* header = node->loop_info->GetHeader();
225 HBasicBlock* preheader = node->loop_info->GetPreHeader();
Aart Bik8c4a8542016-10-06 11:36:57 -0700226 // Scan the phis in the header to find opportunities to simplify an induction
227 // cycle that is only used outside the loop. Replace these uses, if any, with
228 // the last value and remove the induction cycle.
229 // Examples: for (int i = 0; x != null; i++) { .... no i .... }
230 // for (int i = 0; i < 10; i++, k++) { .... no k .... } return k;
Aart Bik281c6812016-08-26 11:31:48 -0700231 for (HInstructionIterator it(header->GetPhis()); !it.Done(); it.Advance()) {
232 HPhi* phi = it.Current()->AsPhi();
Aart Bik8c4a8542016-10-06 11:36:57 -0700233 iset_->clear();
234 int32_t use_count = 0;
235 if (IsPhiInduction(phi, iset_) &&
236 IsOnlyUsedAfterLoop(*node->loop_info, phi, &use_count) &&
237 TryReplaceWithLastValue(phi, use_count, preheader)) {
238 for (HInstruction* i : *iset_) {
239 RemoveFromCycle(i);
Aart Bik281c6812016-08-26 11:31:48 -0700240 }
241 }
242 }
243}
244
245void HLoopOptimization::RemoveIfEmptyLoop(LoopNode* node) {
246 HBasicBlock* header = node->loop_info->GetHeader();
247 HBasicBlock* preheader = node->loop_info->GetPreHeader();
248 // Ensure there is only a single loop-body (besides the header).
249 HBasicBlock* body = nullptr;
250 for (HBlocksInLoopIterator it(*node->loop_info); !it.Done(); it.Advance()) {
251 if (it.Current() != header) {
252 if (body != nullptr) {
253 return;
254 }
255 body = it.Current();
256 }
257 }
258 // Ensure there is only a single exit point.
259 if (header->GetSuccessors().size() != 2) {
260 return;
261 }
262 HBasicBlock* exit = (header->GetSuccessors()[0] == body)
263 ? header->GetSuccessors()[1]
264 : header->GetSuccessors()[0];
Aart Bik8c4a8542016-10-06 11:36:57 -0700265 // Ensure exit can only be reached by exiting loop.
Aart Bik281c6812016-08-26 11:31:48 -0700266 if (exit->GetPredecessors().size() != 1) {
267 return;
268 }
Aart Bik8c4a8542016-10-06 11:36:57 -0700269 // Detect an empty loop: no side effects other than plain iteration. Replace
270 // subsequent index uses, if any, with the last value and remove the loop.
271 iset_->clear();
272 int32_t use_count = 0;
273 if (IsEmptyHeader(header, iset_) &&
274 IsEmptyBody(body, iset_) &&
275 IsOnlyUsedAfterLoop(*node->loop_info, header->GetFirstPhi(), &use_count) &&
276 TryReplaceWithLastValue(header->GetFirstPhi(), use_count, preheader)) {
Aart Bik281c6812016-08-26 11:31:48 -0700277 HBasicBlock* entry = TryRemovePreHeader(preheader, graph_->GetEntryBlock());
278 body->DisconnectAndDelete();
279 exit->RemovePredecessor(header);
280 header->RemoveSuccessor(exit);
281 header->ClearDominanceInformation();
282 header->SetDominator(preheader); // needed by next disconnect.
283 header->DisconnectAndDelete();
284 // If allowed, remove preheader too, which may expose next outer empty loop
285 // Otherwise, link preheader directly to exit to restore the flow graph.
286 if (entry != nullptr) {
287 entry->ReplaceSuccessor(preheader, exit);
288 entry->AddDominatedBlock(exit);
289 exit->SetDominator(entry);
290 preheader->DisconnectAndDelete();
291 } else {
292 preheader->AddSuccessor(exit);
293 preheader->AddInstruction(new (graph_->GetArena()) HGoto()); // global allocator
294 preheader->AddDominatedBlock(exit);
295 exit->SetDominator(preheader);
296 }
297 // Update hierarchy.
298 RemoveLoop(node);
299 }
300}
301
Aart Bik8c4a8542016-10-06 11:36:57 -0700302bool HLoopOptimization::IsOnlyUsedAfterLoop(const HLoopInformation& loop_info,
303 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();
309 if (other_loop_info != nullptr && other_loop_info->IsIn(loop_info)) {
310 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