blob: 7dfa4f160b97f6ca063e933157e0d3c63007e857 [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),
131 loop_allocator_(graph_->GetArena()->GetArenaPool()), // phase-local allocator on global pool
132 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
143 // Build the linear order. This step enables building a loop hierarchy that
144 // properly reflects the outer-inner and previous-next relation.
145 graph_->Linearize();
146 // Build the loop hierarchy.
147 for (HLinearOrderIterator it_graph(*graph_); !it_graph.Done(); it_graph.Advance()) {
148 HBasicBlock* block = it_graph.Current();
149 if (block->IsLoopHeader()) {
150 AddLoop(block->GetLoopInformation());
151 }
152 }
153 if (top_loop_ == nullptr) {
154 return; // no loops
155 }
156 // Traverse the loop hierarchy inner-to-outer and optimize.
157 TraverseLoopsInnerToOuter(top_loop_);
158}
159
160void HLoopOptimization::AddLoop(HLoopInformation* loop_info) {
161 DCHECK(loop_info != nullptr);
162 LoopNode* node = new (&loop_allocator_) LoopNode(loop_info); // phase-local allocator
163 if (last_loop_ == nullptr) {
164 // First loop.
165 DCHECK(top_loop_ == nullptr);
166 last_loop_ = top_loop_ = node;
167 } else if (loop_info->IsIn(*last_loop_->loop_info)) {
168 // Inner loop.
169 node->outer = last_loop_;
170 DCHECK(last_loop_->inner == nullptr);
171 last_loop_ = last_loop_->inner = node;
172 } else {
173 // Subsequent loop.
174 while (last_loop_->outer != nullptr && !loop_info->IsIn(*last_loop_->outer->loop_info)) {
175 last_loop_ = last_loop_->outer;
176 }
177 node->outer = last_loop_->outer;
178 node->previous = last_loop_;
179 DCHECK(last_loop_->next == nullptr);
180 last_loop_ = last_loop_->next = node;
181 }
182}
183
184void HLoopOptimization::RemoveLoop(LoopNode* node) {
185 DCHECK(node != nullptr);
186 // TODO: implement when needed (for current set of optimizations, we don't
187 // need to keep recorded loop hierarchy up to date, but as we get different
188 // traversal, we may want to remove the node from the hierarchy here.
189}
190
191void HLoopOptimization::TraverseLoopsInnerToOuter(LoopNode* node) {
192 for ( ; node != nullptr; node = node->next) {
193 if (node->inner != nullptr) {
194 TraverseLoopsInnerToOuter(node->inner);
195 }
196 // Visit loop after its inner loops have been visited.
197 SimplifyInduction(node);
198 RemoveIfEmptyLoop(node);
199 }
200}
201
202void HLoopOptimization::SimplifyInduction(LoopNode* node) {
203 HBasicBlock* header = node->loop_info->GetHeader();
204 HBasicBlock* preheader = node->loop_info->GetPreHeader();
205 // Scan the phis in the header to find opportunities to optimize induction.
206 for (HInstructionIterator it(header->GetPhis()); !it.Done(); it.Advance()) {
207 HPhi* phi = it.Current()->AsPhi();
208 HInstruction* addsub = nullptr;
209 // Find phi-add/sub cycle.
210 if (IsPhiAddSub(phi, &addsub)) {
211 // Simple case, the induction is only used by itself. Although redundant,
212 // later phases do not easily detect this property. Thus, eliminate here.
213 // Example: for (int i = 0; x != null; i++) { .... no i .... }
214 if (phi->GetUses().HasExactlyOneElement()) {
215 // Remove the cycle, including all uses. Even environment uses can be removed,
216 // since these computations have no effect at all.
217 RemoveFromCycle(phi); // removes environment uses too
218 RemoveFromCycle(addsub);
219 continue;
220 }
221 // Closed form case. Only the last value of the induction is needed. Remove all
222 // overhead from the loop, and replace subsequent uses with the last value.
223 // Example: for (int i = 0; i < 10; i++, k++) { .... no k .... } return k;
224 if (IsOnlyUsedAfterLoop(*node->loop_info, phi, addsub) &&
225 induction_range_.CanGenerateLastValue(phi)) {
226 HInstruction* last = induction_range_.GenerateLastValue(phi, graph_, preheader);
227 // Remove the cycle, replacing all uses. Even environment uses can consume the final
228 // value, since any first real use is outside the loop (although this may imply
229 // that deopting may look "ahead" a bit on the phi value).
230 ReplaceAllUses(phi, last, addsub);
231 RemoveFromCycle(phi); // removes environment uses too
232 RemoveFromCycle(addsub);
233 }
234 }
235 }
236}
237
238void HLoopOptimization::RemoveIfEmptyLoop(LoopNode* node) {
239 HBasicBlock* header = node->loop_info->GetHeader();
240 HBasicBlock* preheader = node->loop_info->GetPreHeader();
241 // Ensure there is only a single loop-body (besides the header).
242 HBasicBlock* body = nullptr;
243 for (HBlocksInLoopIterator it(*node->loop_info); !it.Done(); it.Advance()) {
244 if (it.Current() != header) {
245 if (body != nullptr) {
246 return;
247 }
248 body = it.Current();
249 }
250 }
251 // Ensure there is only a single exit point.
252 if (header->GetSuccessors().size() != 2) {
253 return;
254 }
255 HBasicBlock* exit = (header->GetSuccessors()[0] == body)
256 ? header->GetSuccessors()[1]
257 : header->GetSuccessors()[0];
258 // Ensure exit can only be reached by exiting loop (this seems typically the
259 // case anyway, and simplifies code generation below; TODO: perhaps relax?).
260 if (exit->GetPredecessors().size() != 1) {
261 return;
262 }
263 // Detect an empty loop: no side effects other than plain iteration.
264 HInstruction* addsub = nullptr;
265 if (IsEmptyHeader(header, &addsub) && IsEmptyBody(body, addsub)) {
266 HBasicBlock* entry = TryRemovePreHeader(preheader, graph_->GetEntryBlock());
267 body->DisconnectAndDelete();
268 exit->RemovePredecessor(header);
269 header->RemoveSuccessor(exit);
270 header->ClearDominanceInformation();
271 header->SetDominator(preheader); // needed by next disconnect.
272 header->DisconnectAndDelete();
273 // If allowed, remove preheader too, which may expose next outer empty loop
274 // Otherwise, link preheader directly to exit to restore the flow graph.
275 if (entry != nullptr) {
276 entry->ReplaceSuccessor(preheader, exit);
277 entry->AddDominatedBlock(exit);
278 exit->SetDominator(entry);
279 preheader->DisconnectAndDelete();
280 } else {
281 preheader->AddSuccessor(exit);
282 preheader->AddInstruction(new (graph_->GetArena()) HGoto()); // global allocator
283 preheader->AddDominatedBlock(exit);
284 exit->SetDominator(preheader);
285 }
286 // Update hierarchy.
287 RemoveLoop(node);
288 }
289}
290
291void HLoopOptimization::ReplaceAllUses(HInstruction* instruction,
292 HInstruction* replacement,
293 HInstruction* exclusion) {
294 const HUseList<HInstruction*>& uses = instruction->GetUses();
295 for (auto it = uses.begin(), end = uses.end(); it != end;) {
296 HInstruction* user = it->GetUser();
297 size_t index = it->GetIndex();
298 ++it; // increment before replacing
299 if (user != exclusion) {
300 user->ReplaceInput(replacement, index);
301 induction_range_.Replace(user, instruction, replacement); // update induction
302 }
303 }
304 const HUseList<HEnvironment*>& env_uses = instruction->GetEnvUses();
305 for (auto it = env_uses.begin(), end = env_uses.end(); it != end;) {
306 HEnvironment* user = it->GetUser();
307 size_t index = it->GetIndex();
308 ++it; // increment before replacing
309 if (user->GetHolder() != exclusion) {
310 user->RemoveAsUserOfInput(index);
311 user->SetRawEnvAt(index, replacement);
312 replacement->AddEnvUseAt(user, index);
313 }
314 }
315}
316
317} // namespace art