blob: 8aaec6804d747b5914b97aa1db984c6e54ac1aef [file] [log] [blame]
Aart Bik30efb4e2015-07-30 12:14:31 -07001/*
2 * Copyright (C) 2015 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 "induction_var_analysis.h"
18
19namespace art {
20
21/**
22 * Returns true if instruction is invariant within the given loop.
23 */
24static bool IsLoopInvariant(HLoopInformation* loop, HInstruction* instruction) {
25 HLoopInformation* other_loop = instruction->GetBlock()->GetLoopInformation();
26 if (other_loop != loop) {
27 // If instruction does not occur in same loop, it is invariant
28 // if it appears in an outer loop (including no loop at all).
29 return other_loop == nullptr || loop->IsIn(*other_loop);
30 }
31 return false;
32}
33
34/**
35 * Returns true if instruction is proper entry-phi-operation for given loop
36 * (referred to as mu-operation in Gerlek's paper).
37 */
38static bool IsEntryPhi(HLoopInformation* loop, HInstruction* instruction) {
39 return
40 instruction->IsPhi() &&
41 instruction->InputCount() == 2 &&
42 instruction->GetBlock() == loop->GetHeader();
43}
44
45//
46// Class methods.
47//
48
49HInductionVarAnalysis::HInductionVarAnalysis(HGraph* graph)
50 : HOptimization(graph, kInductionPassName),
51 global_depth_(0),
52 stack_(graph->GetArena()->Adapter()),
53 scc_(graph->GetArena()->Adapter()),
54 map_(std::less<int>(), graph->GetArena()->Adapter()),
55 cycle_(std::less<int>(), graph->GetArena()->Adapter()),
56 induction_(std::less<int>(), graph->GetArena()->Adapter()) {
57}
58
59void HInductionVarAnalysis::Run() {
60 // Detects sequence variables (generalized induction variables) during an
61 // inner-loop-first traversal of all loops using Gerlek's algorithm.
62 for (HPostOrderIterator it_graph(*graph_); !it_graph.Done(); it_graph.Advance()) {
63 HBasicBlock* graph_block = it_graph.Current();
64 if (graph_block->IsLoopHeader()) {
65 VisitLoop(graph_block->GetLoopInformation());
66 }
67 }
68}
69
70void HInductionVarAnalysis::VisitLoop(HLoopInformation* loop) {
71 // Find strongly connected components (SSCs) in the SSA graph of this loop using Tarjan's
72 // algorithm. Due to the descendant-first nature, classification happens "on-demand".
73 global_depth_ = 0;
74 CHECK(stack_.empty());
75 map_.clear();
76
77 for (HBlocksInLoopIterator it_loop(*loop); !it_loop.Done(); it_loop.Advance()) {
78 HBasicBlock* loop_block = it_loop.Current();
79 CHECK(loop_block->IsInLoop());
80 if (loop_block->GetLoopInformation() != loop) {
81 continue; // Inner loops already visited.
82 }
83 // Visit phi-operations and instructions.
84 for (HInstructionIterator it(loop_block->GetPhis()); !it.Done(); it.Advance()) {
85 HInstruction* instruction = it.Current();
86 if (!IsVisitedNode(instruction->GetId())) {
87 VisitNode(loop, instruction);
88 }
89 }
90 for (HInstructionIterator it(loop_block->GetInstructions()); !it.Done(); it.Advance()) {
91 HInstruction* instruction = it.Current();
92 if (!IsVisitedNode(instruction->GetId())) {
93 VisitNode(loop, instruction);
94 }
95 }
96 }
97
98 CHECK(stack_.empty());
99 map_.clear();
100}
101
102void HInductionVarAnalysis::VisitNode(HLoopInformation* loop, HInstruction* instruction) {
103 const int id = instruction->GetId();
104 const uint32_t d1 = ++global_depth_;
105 map_.Put(id, NodeInfo(d1));
106 stack_.push_back(instruction);
107
108 // Visit all descendants.
109 uint32_t low = d1;
110 for (size_t i = 0, count = instruction->InputCount(); i < count; ++i) {
111 low = std::min(low, VisitDescendant(loop, instruction->InputAt(i)));
112 }
113
114 // Lower or found SCC?
115 if (low < d1) {
116 map_.find(id)->second.depth = low;
117 } else {
118 scc_.clear();
119 cycle_.clear();
120
121 // Pop the stack to build the SCC for classification.
122 while (!stack_.empty()) {
123 HInstruction* x = stack_.back();
124 scc_.push_back(x);
125 stack_.pop_back();
126 map_.find(x->GetId())->second.done = true;
127 if (x == instruction) {
128 break;
129 }
130 }
131
132 // Classify the SCC.
133 if (scc_.size() == 1 && !IsEntryPhi(loop, scc_[0])) {
134 ClassifyTrivial(loop, scc_[0]);
135 } else {
136 ClassifyNonTrivial(loop);
137 }
138
139 scc_.clear();
140 cycle_.clear();
141 }
142}
143
144uint32_t HInductionVarAnalysis::VisitDescendant(HLoopInformation* loop, HInstruction* instruction) {
145 // If the definition is either outside the loop (loop invariant entry value)
146 // or assigned in inner loop (inner exit value), the traversal stops.
147 HLoopInformation* otherLoop = instruction->GetBlock()->GetLoopInformation();
148 if (otherLoop != loop) {
149 return global_depth_;
150 }
151
152 // Inspect descendant node.
153 const int id = instruction->GetId();
154 if (!IsVisitedNode(id)) {
155 VisitNode(loop, instruction);
156 return map_.find(id)->second.depth;
157 } else {
158 auto it = map_.find(id);
159 return it->second.done ? global_depth_ : it->second.depth;
160 }
161}
162
163void HInductionVarAnalysis::ClassifyTrivial(HLoopInformation* loop, HInstruction* instruction) {
164 InductionInfo* info = nullptr;
165 if (instruction->IsPhi()) {
166 for (size_t i = 1, count = instruction->InputCount(); i < count; i++) {
167 info = TransferPhi(LookupInfo(loop, instruction->InputAt(0)),
168 LookupInfo(loop, instruction->InputAt(i)));
169 }
170 } else if (instruction->IsAdd()) {
171 info = TransferAddSub(LookupInfo(loop, instruction->InputAt(0)),
172 LookupInfo(loop, instruction->InputAt(1)), kAdd);
173 } else if (instruction->IsSub()) {
174 info = TransferAddSub(LookupInfo(loop, instruction->InputAt(0)),
175 LookupInfo(loop, instruction->InputAt(1)), kSub);
176 } else if (instruction->IsMul()) {
177 info = TransferMul(LookupInfo(loop, instruction->InputAt(0)),
178 LookupInfo(loop, instruction->InputAt(1)));
179 } else if (instruction->IsNeg()) {
180 info = TransferNeg(LookupInfo(loop, instruction->InputAt(0)));
181 }
182
183 // Successfully classified?
184 if (info != nullptr) {
185 AssignInfo(loop, instruction, info);
186 }
187}
188
189void HInductionVarAnalysis::ClassifyNonTrivial(HLoopInformation* loop) {
190 const size_t size = scc_.size();
191 CHECK_GE(size, 1u);
192 HInstruction* phi = scc_[size - 1];
193 if (!IsEntryPhi(loop, phi)) {
194 return;
195 }
196 HInstruction* external = phi->InputAt(0);
197 HInstruction* internal = phi->InputAt(1);
198 InductionInfo* initial = LookupInfo(loop, external);
199 if (initial == nullptr || initial->induction_class != kInvariant) {
200 return;
201 }
202
203 // Singleton entry-phi-operation may be a wrap-around induction.
204 if (size == 1) {
205 InductionInfo* update = LookupInfo(loop, internal);
206 if (update != nullptr) {
207 AssignInfo(loop, phi, NewInductionInfo(kWrapAround, kNop, initial, update, nullptr));
208 }
209 return;
210 }
211
212 // Inspect remainder of the cycle that resides in scc_. The cycle_ mapping assigns
213 // temporary meaning to its nodes.
214 cycle_.Overwrite(phi->GetId(), nullptr);
215 for (size_t i = 0; i < size - 1; i++) {
216 HInstruction* operation = scc_[i];
217 InductionInfo* update = nullptr;
218 if (operation->IsPhi()) {
219 update = TransferCycleOverPhi(operation);
220 } else if (operation->IsAdd()) {
221 update = TransferCycleOverAddSub(loop, operation->InputAt(0), operation->InputAt(1), kAdd, true);
222 } else if (operation->IsSub()) {
223 update = TransferCycleOverAddSub(loop, operation->InputAt(0), operation->InputAt(1), kSub, true);
224 }
225 if (update == nullptr) {
226 return;
227 }
228 cycle_.Overwrite(operation->GetId(), update);
229 }
230
231 // Success if the internal link received accumulated nonzero update.
232 auto it = cycle_.find(internal->GetId());
233 if (it != cycle_.end() && it->second != nullptr) {
234 // Classify header phi and feed the cycle "on-demand".
235 AssignInfo(loop, phi, NewInductionInfo(kLinear, kNop, it->second, initial, nullptr));
236 for (size_t i = 0; i < size - 1; i++) {
237 ClassifyTrivial(loop, scc_[i]);
238 }
239 }
240}
241
242HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferPhi(InductionInfo* a,
243 InductionInfo* b) {
244 // Transfer over a phi: if both inputs are identical, result is input.
245 if (InductionEqual(a, b)) {
246 return a;
247 }
248 return nullptr;
249}
250
251HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferAddSub(InductionInfo* a,
252 InductionInfo* b,
253 InductionOp op) {
254 // Transfer over an addition or subtraction: invariant or linear
255 // inputs combine into new invariant or linear result.
256 if (a != nullptr && b != nullptr) {
257 if (a->induction_class == kInvariant && b->induction_class == kInvariant) {
258 return NewInductionInfo(kInvariant, op, a, b, nullptr);
259 } else if (a->induction_class == kLinear && b->induction_class == kInvariant) {
260 return NewInductionInfo(
261 kLinear,
262 kNop,
263 a->op_a,
264 NewInductionInfo(kInvariant, op, a->op_b, b, nullptr),
265 nullptr);
266 } else if (a->induction_class == kInvariant && b->induction_class == kLinear) {
267 InductionInfo* ba = b->op_a;
268 if (op == kSub) { // negation required
269 ba = NewInductionInfo(kInvariant, kNeg, nullptr, ba, nullptr);
270 }
271 return NewInductionInfo(
272 kLinear,
273 kNop,
274 ba,
275 NewInductionInfo(kInvariant, op, a, b->op_b, nullptr),
276 nullptr);
277 } else if (a->induction_class == kLinear && b->induction_class == kLinear) {
278 return NewInductionInfo(
279 kLinear,
280 kNop,
281 NewInductionInfo(kInvariant, op, a->op_a, b->op_a, nullptr),
282 NewInductionInfo(kInvariant, op, a->op_b, b->op_b, nullptr),
283 nullptr);
284 }
285 }
286 return nullptr;
287}
288
289HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferMul(InductionInfo* a,
290 InductionInfo* b) {
291 // Transfer over a multiplication: invariant or linear
292 // inputs combine into new invariant or linear result.
293 // Two linear inputs would become quadratic.
294 if (a != nullptr && b != nullptr) {
295 if (a->induction_class == kInvariant && b->induction_class == kInvariant) {
296 return NewInductionInfo(kInvariant, kMul, a, b, nullptr);
297 } else if (a->induction_class == kLinear && b->induction_class == kInvariant) {
298 return NewInductionInfo(
299 kLinear,
300 kNop,
301 NewInductionInfo(kInvariant, kMul, a->op_a, b, nullptr),
302 NewInductionInfo(kInvariant, kMul, a->op_b, b, nullptr),
303 nullptr);
304 } else if (a->induction_class == kInvariant && b->induction_class == kLinear) {
305 return NewInductionInfo(
306 kLinear,
307 kNop,
308 NewInductionInfo(kInvariant, kMul, a, b->op_a, nullptr),
309 NewInductionInfo(kInvariant, kMul, a, b->op_b, nullptr),
310 nullptr);
311 }
312 }
313 return nullptr;
314}
315
316HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferNeg(InductionInfo* a) {
317 // Transfer over a unary negation: invariant or linear input
318 // yields a similar, but negated result.
319 if (a != nullptr) {
320 if (a->induction_class == kInvariant) {
321 return NewInductionInfo(kInvariant, kNeg, nullptr, a, nullptr);
322 } else if (a->induction_class == kLinear) {
323 return NewInductionInfo(
324 kLinear,
325 kNop,
326 NewInductionInfo(kInvariant, kNeg, nullptr, a->op_a, nullptr),
327 NewInductionInfo(kInvariant, kNeg, nullptr, a->op_b, nullptr),
328 nullptr);
329 }
330 }
331 return nullptr;
332}
333
334HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferCycleOverPhi(HInstruction* phi) {
335 // Transfer within a cycle over a phi: only identical inputs
336 // can be combined into that input as result.
337 const size_t count = phi->InputCount();
338 CHECK_GT(count, 0u);
339 auto ita = cycle_.find(phi->InputAt(0)->GetId());
340 if (ita != cycle_.end()) {
341 InductionInfo* a = ita->second;
342 for (size_t i = 1; i < count; i++) {
343 auto itb = cycle_.find(phi->InputAt(i)->GetId());
344 if (itb == cycle_.end() ||!HInductionVarAnalysis::InductionEqual(a, itb->second)) {
345 return nullptr;
346 }
347 }
348 return a;
349 }
350 return nullptr;
351}
352
353HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferCycleOverAddSub(
354 HLoopInformation* loop,
355 HInstruction* x,
356 HInstruction* y,
357 InductionOp op,
358 bool first) {
359 // Transfer within a cycle over an addition or subtraction: adding or
360 // subtracting an invariant value adds to the stride of the induction,
361 // starting with the phi value denoted by the unusual nullptr value.
362 auto it = cycle_.find(x->GetId());
363 if (it != cycle_.end()) {
364 InductionInfo* a = it->second;
365 InductionInfo* b = LookupInfo(loop, y);
366 if (b != nullptr && b->induction_class == kInvariant) {
367 if (a == nullptr) {
368 if (op == kSub) { // negation required
369 return NewInductionInfo(kInvariant, kNeg, nullptr, b, nullptr);
370 }
371 return b;
372 } else if (a->induction_class == kInvariant) {
373 return NewInductionInfo(kInvariant, op, a, b, nullptr);
374 }
375 }
376 }
377 // On failure, try alternatives.
378 if (op == kAdd) {
379 // Try the other way around for an addition.
380 if (first) {
381 return TransferCycleOverAddSub(loop, y, x, op, false);
382 }
383 }
384 return nullptr;
385}
386
387void HInductionVarAnalysis::PutInfo(int loop_id, int id, InductionInfo* info) {
388 auto it = induction_.find(loop_id);
389 if (it == induction_.end()) {
390 it = induction_.Put(
391 loop_id, ArenaSafeMap<int, InductionInfo*>(std::less<int>(), graph_->GetArena()->Adapter()));
392 }
393 it->second.Overwrite(id, info);
394}
395
396HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::GetInfo(int loop_id, int id) {
397 auto it = induction_.find(loop_id);
398 if (it != induction_.end()) {
399 auto loop_it = it->second.find(id);
400 if (loop_it != it->second.end()) {
401 return loop_it->second;
402 }
403 }
404 return nullptr;
405}
406
407void HInductionVarAnalysis::AssignInfo(HLoopInformation* loop,
408 HInstruction* instruction,
409 InductionInfo* info) {
410 const int loopId = loop->GetHeader()->GetBlockId();
411 const int id = instruction->GetId();
412 PutInfo(loopId, id, info);
413}
414
415HInductionVarAnalysis::InductionInfo*
416HInductionVarAnalysis::LookupInfo(HLoopInformation* loop,
417 HInstruction* instruction) {
418 const int loop_id = loop->GetHeader()->GetBlockId();
419 const int id = instruction->GetId();
420 InductionInfo* info = GetInfo(loop_id, id);
421 if (info == nullptr && IsLoopInvariant(loop, instruction)) {
422 info = NewInductionInfo(kInvariant, kFetch, nullptr, nullptr, instruction);
423 PutInfo(loop_id, id, info);
424 }
425 return info;
426}
427
428bool HInductionVarAnalysis::InductionEqual(InductionInfo* info1,
429 InductionInfo* info2) {
430 // Test structural equality only, without accounting for simplifications.
431 if (info1 != nullptr && info2 != nullptr) {
432 return
433 info1->induction_class == info2->induction_class &&
434 info1->operation == info2->operation &&
435 info1->fetch == info2->fetch &&
436 InductionEqual(info1->op_a, info2->op_a) &&
437 InductionEqual(info1->op_b, info2->op_b);
438 }
439 // Otherwise only two nullptrs are considered equal.
440 return info1 == info2;
441}
442
443std::string HInductionVarAnalysis::InductionToString(InductionInfo* info) {
444 if (info != nullptr) {
445 if (info->induction_class == kInvariant) {
446 std::string inv = "(";
447 inv += InductionToString(info->op_a);
448 switch (info->operation) {
449 case kNop: inv += " ? "; break;
450 case kAdd: inv += " + "; break;
451 case kSub:
452 case kNeg: inv += " - "; break;
453 case kMul: inv += " * "; break;
454 case kDiv: inv += " / "; break;
455 case kFetch:
456 CHECK(info->fetch != nullptr);
457 inv += std::to_string(info->fetch->GetId()) + ":" + info->fetch->DebugName();
458 break;
459 }
460 inv += InductionToString(info->op_b);
461 return inv + ")";
462 } else {
463 CHECK(info->operation == kNop);
464 if (info->induction_class == kLinear) {
465 return "(" + InductionToString(info->op_a) + " * i + " +
466 InductionToString(info->op_b) + ")";
467 } else if (info->induction_class == kWrapAround) {
468 return "wrap(" + InductionToString(info->op_a) + ", " +
469 InductionToString(info->op_b) + ")";
470 } else if (info->induction_class == kPeriodic) {
471 return "periodic(" + InductionToString(info->op_a) + ", " +
472 InductionToString(info->op_b) + ")";
473 }
474 }
475 }
476 return "";
477}
478
479} // namespace art