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