blob: 4acf3ac682cff5268a983e1d9cb0acade11f9181 [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?
24static bool IsPhiAddSub(HPhi* phi, /*out*/ HInstruction** addsub_out) {
25 HInputsRef inputs = phi->GetInputs();
26 if (inputs.size() == 2 && (inputs[1]->IsAdd() || inputs[1]->IsSub())) {
27 HInstruction* addsub = inputs[1];
28 if (addsub->InputAt(0) == phi || addsub->InputAt(1) == phi) {
29 if (addsub->GetUses().HasExactlyOneElement()) {
30 *addsub_out = addsub;
31 return true;
32 }
33 }
34 }
35 return false;
36}
37
38static bool IsOnlyUsedAfterLoop(const HLoopInformation& loop_info,
39 HPhi* phi, HInstruction* addsub) {
40 for (const HUseListNode<HInstruction*>& use : phi->GetUses()) {
41 if (use.GetUser() != addsub) {
42 HLoopInformation* other_loop_info = use.GetUser()->GetBlock()->GetLoopInformation();
43 if (other_loop_info != nullptr && other_loop_info->IsIn(loop_info)) {
44 return false;
45 }
46 }
47 }
48 return true;
49}
50
51// Find: phi: Phi(init, addsub)
52// s: SuspendCheck
53// c: Condition(phi, bound)
54// i: If(c)
55// TODO: Find a less pattern matching approach?
56static bool IsEmptyHeader(HBasicBlock* block, /*out*/ HInstruction** addsub) {
57 HInstruction* phi = block->GetFirstPhi();
58 if (phi != nullptr && phi->GetNext() == nullptr && IsPhiAddSub(phi->AsPhi(), addsub)) {
59 HInstruction* s = block->GetFirstInstruction();
60 if (s != nullptr && s->IsSuspendCheck()) {
61 HInstruction* c = s->GetNext();
62 if (c != nullptr && c->IsCondition() && c->GetUses().HasExactlyOneElement()) {
63 HInstruction* i = c->GetNext();
64 if (i != nullptr && i->IsIf() && i->InputAt(0) == c) {
65 // Check that phi is only used inside loop as expected.
66 for (const HUseListNode<HInstruction*>& use : phi->GetUses()) {
67 if (use.GetUser() != *addsub && use.GetUser() != c) {
68 return false;
69 }
70 }
71 return true;
72 }
73 }
74 }
75 }
76 return false;
77}
78
79static bool IsEmptyBody(HBasicBlock* block, HInstruction* addsub) {
80 HInstruction* phi = block->GetFirstPhi();
81 HInstruction* i = block->GetFirstInstruction();
82 return phi == nullptr && i == addsub && i->GetNext() != nullptr && i->GetNext()->IsGoto();
83}
84
85static HBasicBlock* TryRemovePreHeader(HBasicBlock* preheader, HBasicBlock* entry_block) {
86 if (preheader->GetPredecessors().size() == 1) {
87 HBasicBlock* entry = preheader->GetSinglePredecessor();
88 HInstruction* anchor = entry->GetLastInstruction();
89 // If the pre-header has a single predecessor we can remove it too if
90 // either the pre-header just contains a goto, or if the predecessor
91 // is not the entry block so we can push instructions backward
92 // (moving computation into the entry block is too dangerous!).
93 if (preheader->GetFirstInstruction() == nullptr ||
94 preheader->GetFirstInstruction()->IsGoto() ||
95 (entry != entry_block && anchor->IsGoto())) {
96 // Push non-goto statements backward to empty the pre-header.
97 for (HInstructionIterator it(preheader->GetInstructions()); !it.Done(); it.Advance()) {
98 HInstruction* instruction = it.Current();
99 if (!instruction->IsGoto()) {
100 if (!instruction->CanBeMoved()) {
101 return nullptr; // pushing failed to move all
102 }
103 it.Current()->MoveBefore(anchor);
104 }
105 }
106 return entry;
107 }
108 }
109 return nullptr;
110}
111
112static void RemoveFromCycle(HInstruction* instruction) {
113 // A bit more elaborate than the usual instruction removal,
114 // since there may be a cycle in the use structure.
115 instruction->RemoveAsUserOfAllInputs();
116 instruction->RemoveEnvironmentUsers();
117 instruction->GetBlock()->RemoveInstructionOrPhi(instruction, /*ensure_safety=*/ false);
118}
119
120//
121// Class methods.
122//
123
124HLoopOptimization::HLoopOptimization(HGraph* graph,
125 HInductionVarAnalysis* induction_analysis)
126 : HOptimization(graph, kLoopOptimizationPassName),
127 induction_range_(induction_analysis),
Aart Bik96202302016-10-04 17:33:56 -0700128 loop_allocator_(nullptr),
Aart Bik281c6812016-08-26 11:31:48 -0700129 top_loop_(nullptr),
130 last_loop_(nullptr) {
131}
132
133void HLoopOptimization::Run() {
134 // Well-behaved loops only.
135 // TODO: make this less of a sledgehammer.
Aart Bik96202302016-10-04 17:33:56 -0700136 if (graph_->HasTryCatch() || graph_->HasIrreducibleLoops()) {
Aart Bik281c6812016-08-26 11:31:48 -0700137 return;
138 }
139
Aart Bik96202302016-10-04 17:33:56 -0700140 // Phase-local allocator that draws from the global pool. Since the allocator
141 // itself resides on the stack, it is destructed on exiting Run(), which
142 // implies its underlying memory is released immediately.
Nicolas Geoffrayebe16742016-10-05 09:55:42 +0100143 ArenaAllocator allocator(graph_->GetArena()->GetArenaPool());
Aart Bik96202302016-10-04 17:33:56 -0700144 loop_allocator_ = &allocator;
Nicolas Geoffrayebe16742016-10-05 09:55:42 +0100145
Aart Bik96202302016-10-04 17:33:56 -0700146 // Perform loop optimizations.
147 LocalRun();
148
149 // Detach.
150 loop_allocator_ = nullptr;
151 last_loop_ = top_loop_ = nullptr;
152}
153
154void HLoopOptimization::LocalRun() {
155 // Build the linear order using the phase-local allocator. This step enables building
156 // a loop hierarchy that properly reflects the outer-inner and previous-next relation.
157 ArenaVector<HBasicBlock*> linear_order(loop_allocator_->Adapter(kArenaAllocLinearOrder));
158 LinearizeGraph(graph_, loop_allocator_, &linear_order);
159
Aart Bik281c6812016-08-26 11:31:48 -0700160 // Build the loop hierarchy.
Aart Bik96202302016-10-04 17:33:56 -0700161 for (HBasicBlock* block : linear_order) {
Aart Bik281c6812016-08-26 11:31:48 -0700162 if (block->IsLoopHeader()) {
163 AddLoop(block->GetLoopInformation());
164 }
165 }
Aart Bik96202302016-10-04 17:33:56 -0700166
167 // Traverse the loop hierarchy inner-to-outer and optimize.
168 TraverseLoopsInnerToOuter(top_loop_);
Aart Bik281c6812016-08-26 11:31:48 -0700169}
170
171void HLoopOptimization::AddLoop(HLoopInformation* loop_info) {
172 DCHECK(loop_info != nullptr);
Nicolas Geoffrayebe16742016-10-05 09:55:42 +0100173 LoopNode* node = new (loop_allocator_) LoopNode(loop_info); // phase-local allocator
Aart Bik281c6812016-08-26 11:31:48 -0700174 if (last_loop_ == nullptr) {
175 // First loop.
176 DCHECK(top_loop_ == nullptr);
177 last_loop_ = top_loop_ = node;
178 } else if (loop_info->IsIn(*last_loop_->loop_info)) {
179 // Inner loop.
180 node->outer = last_loop_;
181 DCHECK(last_loop_->inner == nullptr);
182 last_loop_ = last_loop_->inner = node;
183 } else {
184 // Subsequent loop.
185 while (last_loop_->outer != nullptr && !loop_info->IsIn(*last_loop_->outer->loop_info)) {
186 last_loop_ = last_loop_->outer;
187 }
188 node->outer = last_loop_->outer;
189 node->previous = last_loop_;
190 DCHECK(last_loop_->next == nullptr);
191 last_loop_ = last_loop_->next = node;
192 }
193}
194
195void HLoopOptimization::RemoveLoop(LoopNode* node) {
196 DCHECK(node != nullptr);
197 // TODO: implement when needed (for current set of optimizations, we don't
198 // need to keep recorded loop hierarchy up to date, but as we get different
199 // traversal, we may want to remove the node from the hierarchy here.
200}
201
202void HLoopOptimization::TraverseLoopsInnerToOuter(LoopNode* node) {
203 for ( ; node != nullptr; node = node->next) {
204 if (node->inner != nullptr) {
205 TraverseLoopsInnerToOuter(node->inner);
206 }
207 // Visit loop after its inner loops have been visited.
208 SimplifyInduction(node);
209 RemoveIfEmptyLoop(node);
210 }
211}
212
213void HLoopOptimization::SimplifyInduction(LoopNode* node) {
214 HBasicBlock* header = node->loop_info->GetHeader();
215 HBasicBlock* preheader = node->loop_info->GetPreHeader();
216 // Scan the phis in the header to find opportunities to optimize induction.
217 for (HInstructionIterator it(header->GetPhis()); !it.Done(); it.Advance()) {
218 HPhi* phi = it.Current()->AsPhi();
219 HInstruction* addsub = nullptr;
220 // Find phi-add/sub cycle.
221 if (IsPhiAddSub(phi, &addsub)) {
222 // Simple case, the induction is only used by itself. Although redundant,
223 // later phases do not easily detect this property. Thus, eliminate here.
224 // Example: for (int i = 0; x != null; i++) { .... no i .... }
225 if (phi->GetUses().HasExactlyOneElement()) {
226 // Remove the cycle, including all uses. Even environment uses can be removed,
227 // since these computations have no effect at all.
228 RemoveFromCycle(phi); // removes environment uses too
229 RemoveFromCycle(addsub);
230 continue;
231 }
232 // Closed form case. Only the last value of the induction is needed. Remove all
233 // overhead from the loop, and replace subsequent uses with the last value.
234 // Example: for (int i = 0; i < 10; i++, k++) { .... no k .... } return k;
235 if (IsOnlyUsedAfterLoop(*node->loop_info, phi, addsub) &&
236 induction_range_.CanGenerateLastValue(phi)) {
237 HInstruction* last = induction_range_.GenerateLastValue(phi, graph_, preheader);
238 // Remove the cycle, replacing all uses. Even environment uses can consume the final
239 // value, since any first real use is outside the loop (although this may imply
240 // that deopting may look "ahead" a bit on the phi value).
241 ReplaceAllUses(phi, last, addsub);
242 RemoveFromCycle(phi); // removes environment uses too
243 RemoveFromCycle(addsub);
244 }
245 }
246 }
247}
248
249void HLoopOptimization::RemoveIfEmptyLoop(LoopNode* node) {
250 HBasicBlock* header = node->loop_info->GetHeader();
251 HBasicBlock* preheader = node->loop_info->GetPreHeader();
252 // Ensure there is only a single loop-body (besides the header).
253 HBasicBlock* body = nullptr;
254 for (HBlocksInLoopIterator it(*node->loop_info); !it.Done(); it.Advance()) {
255 if (it.Current() != header) {
256 if (body != nullptr) {
257 return;
258 }
259 body = it.Current();
260 }
261 }
262 // Ensure there is only a single exit point.
263 if (header->GetSuccessors().size() != 2) {
264 return;
265 }
266 HBasicBlock* exit = (header->GetSuccessors()[0] == body)
267 ? header->GetSuccessors()[1]
268 : header->GetSuccessors()[0];
269 // Ensure exit can only be reached by exiting loop (this seems typically the
270 // case anyway, and simplifies code generation below; TODO: perhaps relax?).
271 if (exit->GetPredecessors().size() != 1) {
272 return;
273 }
274 // Detect an empty loop: no side effects other than plain iteration.
275 HInstruction* addsub = nullptr;
276 if (IsEmptyHeader(header, &addsub) && IsEmptyBody(body, addsub)) {
277 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
302void HLoopOptimization::ReplaceAllUses(HInstruction* instruction,
303 HInstruction* replacement,
304 HInstruction* exclusion) {
305 const HUseList<HInstruction*>& uses = instruction->GetUses();
306 for (auto it = uses.begin(), end = uses.end(); it != end;) {
307 HInstruction* user = it->GetUser();
308 size_t index = it->GetIndex();
309 ++it; // increment before replacing
310 if (user != exclusion) {
311 user->ReplaceInput(replacement, index);
312 induction_range_.Replace(user, instruction, replacement); // update induction
313 }
314 }
315 const HUseList<HEnvironment*>& env_uses = instruction->GetEnvUses();
316 for (auto it = env_uses.begin(), end = env_uses.end(); it != end;) {
317 HEnvironment* user = it->GetUser();
318 size_t index = it->GetIndex();
319 ++it; // increment before replacing
320 if (user->GetHolder() != exclusion) {
321 user->RemoveAsUserOfInput(index);
322 user->SetRawEnvAt(index, replacement);
323 replacement->AddEnvUseAt(user, index);
324 }
325 }
326}
327
328} // namespace art