1 /*
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
19 #include "induction_var_range.h"
20
21 namespace art {
22
23 /**
24 * Returns true if the from/to types denote a narrowing, integral conversion (precision loss).
25 */
IsNarrowingIntegralConversion(DataType::Type from,DataType::Type to)26 static bool IsNarrowingIntegralConversion(DataType::Type from, DataType::Type to) {
27 switch (from) {
28 case DataType::Type::kInt64:
29 return to == DataType::Type::kUint8 ||
30 to == DataType::Type::kInt8 ||
31 to == DataType::Type::kUint16 ||
32 to == DataType::Type::kInt16 ||
33 to == DataType::Type::kInt32;
34 case DataType::Type::kInt32:
35 return to == DataType::Type::kUint8 ||
36 to == DataType::Type::kInt8 ||
37 to == DataType::Type::kUint16 ||
38 to == DataType::Type::kInt16;
39 case DataType::Type::kUint16:
40 case DataType::Type::kInt16:
41 return to == DataType::Type::kUint8 || to == DataType::Type::kInt8;
42 default:
43 return false;
44 }
45 }
46
47 /**
48 * Returns result of implicit widening type conversion done in HIR.
49 */
ImplicitConversion(DataType::Type type)50 static DataType::Type ImplicitConversion(DataType::Type type) {
51 switch (type) {
52 case DataType::Type::kBool:
53 case DataType::Type::kUint8:
54 case DataType::Type::kInt8:
55 case DataType::Type::kUint16:
56 case DataType::Type::kInt16:
57 return DataType::Type::kInt32;
58 default:
59 return type;
60 }
61 }
62
63 /**
64 * Returns true if loop is guarded by "a cmp b" on entry.
65 */
IsGuardedBy(const HLoopInformation * loop,IfCondition cmp,HInstruction * a,HInstruction * b)66 static bool IsGuardedBy(const HLoopInformation* loop,
67 IfCondition cmp,
68 HInstruction* a,
69 HInstruction* b) {
70 // Chase back through straightline code to the first potential
71 // block that has a control dependence.
72 // guard: if (x) bypass
73 // |
74 // entry: straightline code
75 // |
76 // preheader
77 // |
78 // header
79 HBasicBlock* guard = loop->GetPreHeader();
80 HBasicBlock* entry = loop->GetHeader();
81 while (guard->GetPredecessors().size() == 1 &&
82 guard->GetSuccessors().size() == 1) {
83 entry = guard;
84 guard = guard->GetSinglePredecessor();
85 }
86 // Find guard.
87 HInstruction* control = guard->GetLastInstruction();
88 if (!control->IsIf()) {
89 return false;
90 }
91 HIf* ifs = control->AsIf();
92 HInstruction* if_expr = ifs->InputAt(0);
93 if (if_expr->IsCondition()) {
94 IfCondition other_cmp = ifs->IfTrueSuccessor() == entry
95 ? if_expr->AsCondition()->GetCondition()
96 : if_expr->AsCondition()->GetOppositeCondition();
97 if (if_expr->InputAt(0) == a && if_expr->InputAt(1) == b) {
98 return cmp == other_cmp;
99 } else if (if_expr->InputAt(1) == a && if_expr->InputAt(0) == b) {
100 switch (cmp) {
101 case kCondLT: return other_cmp == kCondGT;
102 case kCondLE: return other_cmp == kCondGE;
103 case kCondGT: return other_cmp == kCondLT;
104 case kCondGE: return other_cmp == kCondLE;
105 default: LOG(FATAL) << "unexpected cmp: " << cmp;
106 }
107 }
108 }
109 return false;
110 }
111
112 /* Finds first loop header phi use. */
FindFirstLoopHeaderPhiUse(const HLoopInformation * loop,HInstruction * instruction)113 HInstruction* FindFirstLoopHeaderPhiUse(const HLoopInformation* loop, HInstruction* instruction) {
114 for (const HUseListNode<HInstruction*>& use : instruction->GetUses()) {
115 if (use.GetUser()->GetBlock() == loop->GetHeader() &&
116 use.GetUser()->IsPhi() &&
117 use.GetUser()->InputAt(1) == instruction) {
118 return use.GetUser();
119 }
120 }
121 return nullptr;
122 }
123
124 /**
125 * Relinks the Phi structure after break-loop rewriting.
126 */
FixOutsideUse(const HLoopInformation * loop,HInstruction * instruction,HInstruction * replacement,bool rewrite)127 static bool FixOutsideUse(const HLoopInformation* loop,
128 HInstruction* instruction,
129 HInstruction* replacement,
130 bool rewrite) {
131 // Deal with regular uses.
132 const HUseList<HInstruction*>& uses = instruction->GetUses();
133 for (auto it = uses.begin(), end = uses.end(); it != end; ) {
134 HInstruction* user = it->GetUser();
135 size_t index = it->GetIndex();
136 ++it; // increment prior to potential removal
137 if (user->GetBlock()->GetLoopInformation() != loop) {
138 if (replacement == nullptr) {
139 return false;
140 } else if (rewrite) {
141 user->ReplaceInput(replacement, index);
142 }
143 }
144 }
145 // Deal with environment uses.
146 const HUseList<HEnvironment*>& env_uses = instruction->GetEnvUses();
147 for (auto it = env_uses.begin(), end = env_uses.end(); it != end;) {
148 HEnvironment* user = it->GetUser();
149 size_t index = it->GetIndex();
150 ++it; // increment prior to potential removal
151 if (user->GetHolder()->GetBlock()->GetLoopInformation() != loop) {
152 if (replacement == nullptr) {
153 return false;
154 } else if (rewrite) {
155 user->ReplaceInput(replacement, index);
156 }
157 }
158 }
159 return true;
160 }
161
162 /**
163 * Test and rewrite the loop body of a break-loop. Returns true on success.
164 */
RewriteBreakLoopBody(const HLoopInformation * loop,HBasicBlock * body,HInstruction * cond,HInstruction * index,HInstruction * upper,bool rewrite)165 static bool RewriteBreakLoopBody(const HLoopInformation* loop,
166 HBasicBlock* body,
167 HInstruction* cond,
168 HInstruction* index,
169 HInstruction* upper,
170 bool rewrite) {
171 // Deal with Phis. Outside use prohibited, except for index (which gets exit value).
172 for (HInstructionIterator it(loop->GetHeader()->GetPhis()); !it.Done(); it.Advance()) {
173 HInstruction* exit_value = it.Current() == index ? upper : nullptr;
174 if (!FixOutsideUse(loop, it.Current(), exit_value, rewrite)) {
175 return false;
176 }
177 }
178 // Deal with other statements in header.
179 for (HInstruction* m = cond->GetPrevious(); m && !m->IsSuspendCheck();) {
180 HInstruction* p = m->GetPrevious();
181 if (rewrite) {
182 m->MoveBefore(body->GetFirstInstruction(), false);
183 }
184 if (!FixOutsideUse(loop, m, FindFirstLoopHeaderPhiUse(loop, m), rewrite)) {
185 return false;
186 }
187 m = p;
188 }
189 return true;
190 }
191
192 //
193 // Class members.
194 //
195
196 struct HInductionVarAnalysis::NodeInfo {
NodeInfoart::HInductionVarAnalysis::NodeInfo197 explicit NodeInfo(uint32_t d) : depth(d), done(false) {}
198 uint32_t depth;
199 bool done;
200 };
201
202 struct HInductionVarAnalysis::StackEntry {
StackEntryart::HInductionVarAnalysis::StackEntry203 StackEntry(HInstruction* insn, NodeInfo* info, size_t link = std::numeric_limits<size_t>::max())
204 : instruction(insn),
205 node_info(info),
206 user_link(link),
207 num_visited_inputs(0u),
208 low_depth(info->depth) {}
209
210 HInstruction* instruction;
211 NodeInfo* node_info;
212 size_t user_link; // Stack index of the user that is visiting this input.
213 size_t num_visited_inputs;
214 size_t low_depth;
215 };
216
HInductionVarAnalysis(HGraph * graph,const char * name)217 HInductionVarAnalysis::HInductionVarAnalysis(HGraph* graph, const char* name)
218 : HOptimization(graph, name),
219 induction_(std::less<const HLoopInformation*>(),
220 graph->GetAllocator()->Adapter(kArenaAllocInductionVarAnalysis)),
221 cycles_(std::less<HPhi*>(),
222 graph->GetAllocator()->Adapter(kArenaAllocInductionVarAnalysis)) {
223 }
224
Run()225 bool HInductionVarAnalysis::Run() {
226 // Detects sequence variables (generalized induction variables) during an outer to inner
227 // traversal of all loops using Gerlek's algorithm. The order is important to enable
228 // range analysis on outer loop while visiting inner loops.
229 for (HBasicBlock* graph_block : graph_->GetReversePostOrder()) {
230 // Don't analyze irreducible loops.
231 if (graph_block->IsLoopHeader() && !graph_block->GetLoopInformation()->IsIrreducible()) {
232 VisitLoop(graph_block->GetLoopInformation());
233 }
234 }
235 return !induction_.empty();
236 }
237
VisitLoop(const HLoopInformation * loop)238 void HInductionVarAnalysis::VisitLoop(const HLoopInformation* loop) {
239 ScopedArenaAllocator local_allocator(graph_->GetArenaStack());
240 ScopedArenaSafeMap<HInstruction*, NodeInfo> visited_instructions(
241 std::less<HInstruction*>(), local_allocator.Adapter(kArenaAllocInductionVarAnalysis));
242
243 // Find strongly connected components (SSCs) in the SSA graph of this loop using Tarjan's
244 // algorithm. Due to the descendant-first nature, classification happens "on-demand".
245 size_t global_depth = 0;
246 for (HBlocksInLoopIterator it_loop(*loop); !it_loop.Done(); it_loop.Advance()) {
247 HBasicBlock* loop_block = it_loop.Current();
248 DCHECK(loop_block->IsInLoop());
249 if (loop_block->GetLoopInformation() != loop) {
250 continue; // Inner loops visited later.
251 }
252 // Visit phi-operations and instructions.
253 for (HInstructionIterator it(loop_block->GetPhis()); !it.Done(); it.Advance()) {
254 global_depth = TryVisitNodes(loop, it.Current(), global_depth, &visited_instructions);
255 }
256 for (HInstructionIterator it(loop_block->GetInstructions()); !it.Done(); it.Advance()) {
257 global_depth = TryVisitNodes(loop, it.Current(), global_depth, &visited_instructions);
258 }
259 }
260
261 // Determine the loop's trip-count.
262 VisitControl(loop);
263 }
264
TryVisitNodes(const HLoopInformation * loop,HInstruction * start_instruction,size_t global_depth,ScopedArenaSafeMap<HInstruction *,NodeInfo> * visited_instructions)265 size_t HInductionVarAnalysis::TryVisitNodes(
266 const HLoopInformation* loop,
267 HInstruction* start_instruction,
268 size_t global_depth,
269 /*inout*/ ScopedArenaSafeMap<HInstruction*, NodeInfo>* visited_instructions) {
270 // This is recursion-free version of the SCC search algorithm. We have limited stack space,
271 // so recursion with the depth dependent on the input is undesirable as such depth is unlimited.
272 auto [it, inserted] =
273 visited_instructions->insert(std::make_pair(start_instruction, NodeInfo(global_depth + 1u)));
274 if (!inserted) {
275 return global_depth;
276 }
277 NodeInfo* start_info = &it->second;
278 ++global_depth;
279 DCHECK_EQ(global_depth, start_info->depth);
280
281 ScopedArenaVector<StackEntry> stack(visited_instructions->get_allocator());
282 stack.push_back({start_instruction, start_info});
283
284 size_t current_entry = 0u;
285 while (!stack.empty()) {
286 StackEntry& entry = stack[current_entry];
287
288 // Look for unvisited inputs (also known as "descentants").
289 bool visit_input = false;
290 auto inputs = entry.instruction->GetInputs();
291 while (entry.num_visited_inputs != inputs.size()) {
292 HInstruction* input = inputs[entry.num_visited_inputs];
293 ++entry.num_visited_inputs;
294 // If the definition is either outside the loop (loop invariant entry value)
295 // or assigned in inner loop (inner exit value), the input is not visited.
296 if (input->GetBlock()->GetLoopInformation() != loop) {
297 continue;
298 }
299 // Try visiting the input. If already visited, update `entry.low_depth`.
300 auto [input_it, input_inserted] =
301 visited_instructions->insert(std::make_pair(input, NodeInfo(global_depth + 1u)));
302 NodeInfo* input_info = &input_it->second;
303 if (input_inserted) {
304 // Push the input on the `stack` and visit it now.
305 ++global_depth;
306 DCHECK_EQ(global_depth, input_info->depth);
307 stack.push_back({input, input_info, current_entry});
308 current_entry = stack.size() - 1u;
309 visit_input = true;
310 break;
311 } else if (!input_info->done && input_info->depth < entry.low_depth) {
312 entry.low_depth = input_it->second.depth;
313 }
314 continue;
315 }
316 if (visit_input) {
317 continue; // Process the new top of the stack.
318 }
319
320 // All inputs of the current node have been visited.
321 // Check if we have found an input below this entry on the stack.
322 DCHECK(!entry.node_info->done);
323 size_t previous_entry = entry.user_link;
324 if (entry.node_info->depth > entry.low_depth) {
325 DCHECK_LT(previous_entry, current_entry) << entry.node_info->depth << " " << entry.low_depth;
326 entry.node_info->depth = entry.low_depth;
327 if (stack[previous_entry].low_depth > entry.low_depth) {
328 stack[previous_entry].low_depth = entry.low_depth;
329 }
330 } else {
331 // Classify the SCC we have just found.
332 ArrayRef<StackEntry> stack_tail = ArrayRef<StackEntry>(stack).SubArray(current_entry);
333 for (StackEntry& tail_entry : stack_tail) {
334 tail_entry.node_info->done = true;
335 }
336 if (current_entry + 1u == stack.size() && !entry.instruction->IsLoopHeaderPhi()) {
337 ClassifyTrivial(loop, entry.instruction);
338 } else {
339 ClassifyNonTrivial(loop, ArrayRef<const StackEntry>(stack_tail));
340 }
341 stack.erase(stack.begin() + current_entry, stack.end());
342 }
343 current_entry = previous_entry;
344 }
345
346 return global_depth;
347 }
348
349 /**
350 * Since graph traversal may enter a SCC at any position, an initial representation may be rotated,
351 * along dependences, viz. any of (a, b, c, d), (d, a, b, c) (c, d, a, b), (b, c, d, a) assuming
352 * a chain of dependences (mutual independent items may occur in arbitrary order). For proper
353 * classification, the lexicographically first loop-phi is rotated to the front. We do that
354 * as we extract the SCC instructions.
355 */
ExtractScc(ArrayRef<const StackEntry> stack_tail,ScopedArenaVector<HInstruction * > * scc)356 void HInductionVarAnalysis::ExtractScc(ArrayRef<const StackEntry> stack_tail,
357 ScopedArenaVector<HInstruction*>* scc) {
358 // Find very first loop-phi.
359 HInstruction* phi = nullptr;
360 size_t split_pos = 0;
361 const size_t size = stack_tail.size();
362 for (size_t i = 0; i != size; ++i) {
363 const StackEntry& entry = stack_tail[i];
364 HInstruction* instruction = entry.instruction;
365 if (instruction->IsLoopHeaderPhi()) {
366 // All loop Phis in SCC come from the same loop header.
367 HBasicBlock* block = instruction->GetBlock();
368 DCHECK(block->GetLoopInformation()->GetHeader() == block);
369 DCHECK(phi == nullptr || phi->GetBlock() == block);
370 if (phi == nullptr || block->GetPhis().FoundBefore(instruction, phi)) {
371 phi = instruction;
372 split_pos = i + 1u;
373 }
374 }
375 }
376
377 // Extract SCC in two chunks.
378 DCHECK(scc->empty());
379 scc->reserve(size);
380 for (const StackEntry& entry : ReverseRange(stack_tail.SubArray(/*pos=*/ 0u, split_pos))) {
381 scc->push_back(entry.instruction);
382 }
383 for (const StackEntry& entry : ReverseRange(stack_tail.SubArray(/*pos=*/ split_pos))) {
384 scc->push_back(entry.instruction);
385 }
386 DCHECK_EQ(scc->size(), stack_tail.size());
387 }
388
ClassifyTrivial(const HLoopInformation * loop,HInstruction * instruction)389 void HInductionVarAnalysis::ClassifyTrivial(const HLoopInformation* loop,
390 HInstruction* instruction) {
391 const HBasicBlock* context = instruction->GetBlock();
392 DataType::Type type = instruction->GetType();
393 InductionInfo* info = nullptr;
394 if (instruction->IsPhi()) {
395 info = TransferPhi(loop, instruction, /*input_index*/ 0, /*adjust_input_size*/ 0);
396 } else if (instruction->IsAdd()) {
397 info = TransferAddSub(context,
398 loop,
399 LookupInfo(loop, instruction->InputAt(0)),
400 LookupInfo(loop, instruction->InputAt(1)),
401 kAdd,
402 type);
403 } else if (instruction->IsSub()) {
404 info = TransferAddSub(context,
405 loop,
406 LookupInfo(loop, instruction->InputAt(0)),
407 LookupInfo(loop, instruction->InputAt(1)),
408 kSub,
409 type);
410 } else if (instruction->IsNeg()) {
411 info = TransferNeg(context, loop, LookupInfo(loop, instruction->InputAt(0)), type);
412 } else if (instruction->IsMul()) {
413 info = TransferMul(context,
414 loop,
415 LookupInfo(loop, instruction->InputAt(0)),
416 LookupInfo(loop, instruction->InputAt(1)),
417 type);
418 } else if (instruction->IsShl()) {
419 HInstruction* mulc = GetShiftConstant(loop, instruction, /*initial*/ nullptr);
420 if (mulc != nullptr) {
421 info = TransferMul(context,
422 loop,
423 LookupInfo(loop, instruction->InputAt(0)),
424 LookupInfo(loop, mulc),
425 type);
426 }
427 } else if (instruction->IsSelect()) {
428 info = TransferPhi(loop, instruction, /*input_index*/ 0, /*adjust_input_size*/ 1);
429 } else if (instruction->IsTypeConversion()) {
430 info = TransferConversion(LookupInfo(loop, instruction->InputAt(0)),
431 instruction->AsTypeConversion()->GetInputType(),
432 instruction->AsTypeConversion()->GetResultType());
433 } else if (instruction->IsBoundsCheck()) {
434 info = LookupInfo(loop, instruction->InputAt(0)); // Pass-through.
435 }
436
437 // Successfully classified?
438 if (info != nullptr) {
439 AssignInfo(loop, instruction, info);
440 }
441 }
442
ClassifyNonTrivial(const HLoopInformation * loop,ArrayRef<const StackEntry> stack_tail)443 void HInductionVarAnalysis::ClassifyNonTrivial(const HLoopInformation* loop,
444 ArrayRef<const StackEntry> stack_tail) {
445 const size_t size = stack_tail.size();
446 DCHECK_GE(size, 1u);
447 DataType::Type type = stack_tail.back().instruction->GetType();
448
449 ScopedArenaAllocator local_allocator(graph_->GetArenaStack());
450 ScopedArenaVector<HInstruction*> scc(local_allocator.Adapter(kArenaAllocInductionVarAnalysis));
451 ExtractScc(ArrayRef<const StackEntry>(stack_tail), &scc);
452
453 // Analyze from loop-phi onwards.
454 HInstruction* phi = scc[0];
455 if (!phi->IsLoopHeaderPhi()) {
456 return;
457 }
458
459 // External link should be loop invariant.
460 InductionInfo* initial = LookupInfo(loop, phi->InputAt(0));
461 if (initial == nullptr || initial->induction_class != kInvariant) {
462 return;
463 }
464
465 // Store interesting cycle in each loop phi.
466 for (size_t i = 0; i < size; i++) {
467 if (scc[i]->IsLoopHeaderPhi()) {
468 AssignCycle(scc[i]->AsPhi(), ArrayRef<HInstruction* const>(scc));
469 }
470 }
471
472 // Singleton is wrap-around induction if all internal links have the same meaning.
473 if (size == 1) {
474 InductionInfo* update = TransferPhi(loop, phi, /*input_index*/ 1, /*adjust_input_size*/ 0);
475 if (update != nullptr) {
476 AssignInfo(loop, phi, CreateInduction(kWrapAround,
477 kNop,
478 initial,
479 update,
480 /*fetch*/ nullptr,
481 type));
482 }
483 return;
484 }
485
486 // Inspect remainder of the cycle that resides in `scc`. The `cycle` mapping assigns
487 // temporary meaning to its nodes, seeded from the phi instruction and back.
488 ScopedArenaSafeMap<HInstruction*, InductionInfo*> cycle(
489 std::less<HInstruction*>(), local_allocator.Adapter(kArenaAllocInductionVarAnalysis));
490 for (size_t i = 1; i < size; i++) {
491 HInstruction* instruction = scc[i];
492 InductionInfo* update = nullptr;
493 if (instruction->IsPhi()) {
494 update = SolvePhiAllInputs(loop, phi, instruction, cycle, type);
495 } else if (instruction->IsAdd()) {
496 update = SolveAddSub(loop,
497 phi,
498 instruction,
499 instruction->InputAt(0),
500 instruction->InputAt(1),
501 kAdd,
502 cycle,
503 type);
504 } else if (instruction->IsSub()) {
505 update = SolveAddSub(loop,
506 phi,
507 instruction,
508 instruction->InputAt(0),
509 instruction->InputAt(1),
510 kSub,
511 cycle,
512 type);
513 } else if (instruction->IsMul()) {
514 update = SolveOp(
515 loop, phi, instruction, instruction->InputAt(0), instruction->InputAt(1), kMul, type);
516 } else if (instruction->IsDiv()) {
517 update = SolveOp(
518 loop, phi, instruction, instruction->InputAt(0), instruction->InputAt(1), kDiv, type);
519 } else if (instruction->IsRem()) {
520 update = SolveOp(
521 loop, phi, instruction, instruction->InputAt(0), instruction->InputAt(1), kRem, type);
522 } else if (instruction->IsShl()) {
523 HInstruction* mulc = GetShiftConstant(loop, instruction, /*initial*/ nullptr);
524 if (mulc != nullptr) {
525 update = SolveOp(loop, phi, instruction, instruction->InputAt(0), mulc, kMul, type);
526 }
527 } else if (instruction->IsShr() || instruction->IsUShr()) {
528 HInstruction* divc = GetShiftConstant(loop, instruction, initial);
529 if (divc != nullptr) {
530 update = SolveOp(loop, phi, instruction, instruction->InputAt(0), divc, kDiv, type);
531 }
532 } else if (instruction->IsXor()) {
533 update = SolveOp(
534 loop, phi, instruction, instruction->InputAt(0), instruction->InputAt(1), kXor, type);
535 } else if (instruction->IsEqual()) {
536 update = SolveTest(loop, phi, instruction, /*opposite_value=*/ 0, type);
537 } else if (instruction->IsNotEqual()) {
538 update = SolveTest(loop, phi, instruction, /*opposite_value=*/ 1, type);
539 } else if (instruction->IsSelect()) {
540 // Select acts like Phi.
541 update = SolvePhi(instruction, /*input_index=*/ 0, /*adjust_input_size=*/ 1, cycle);
542 } else if (instruction->IsTypeConversion()) {
543 update = SolveConversion(loop, phi, instruction->AsTypeConversion(), cycle, &type);
544 }
545 if (update == nullptr) {
546 return;
547 }
548 cycle.Put(instruction, update);
549 }
550
551 // Success if all internal links received the same temporary meaning.
552 InductionInfo* induction = SolvePhi(phi, /*input_index=*/ 1, /*adjust_input_size=*/ 0, cycle);
553 if (induction != nullptr) {
554 switch (induction->induction_class) {
555 case kInvariant:
556 // Construct combined stride of the linear induction.
557 induction = CreateInduction(kLinear, kNop, induction, initial, /*fetch*/ nullptr, type);
558 FALLTHROUGH_INTENDED;
559 case kPolynomial:
560 case kGeometric:
561 case kWrapAround:
562 // Classify first phi and then the rest of the cycle "on-demand".
563 // Statements are scanned in order.
564 AssignInfo(loop, phi, induction);
565 for (size_t i = 1; i < size; i++) {
566 ClassifyTrivial(loop, scc[i]);
567 }
568 break;
569 case kPeriodic:
570 // Classify all elements in the cycle with the found periodic induction while
571 // rotating each first element to the end. Lastly, phi is classified.
572 // Statements are scanned in reverse order.
573 for (size_t i = size - 1; i >= 1; i--) {
574 AssignInfo(loop, scc[i], induction);
575 induction = RotatePeriodicInduction(induction->op_b, induction->op_a, type);
576 }
577 AssignInfo(loop, phi, induction);
578 break;
579 default:
580 break;
581 }
582 }
583 }
584
RotatePeriodicInduction(InductionInfo * induction,InductionInfo * last,DataType::Type type)585 HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::RotatePeriodicInduction(
586 InductionInfo* induction,
587 InductionInfo* last,
588 DataType::Type type) {
589 // Rotates a periodic induction of the form
590 // (a, b, c, d, e)
591 // into
592 // (b, c, d, e, a)
593 // in preparation of assigning this to the previous variable in the sequence.
594 if (induction->induction_class == kInvariant) {
595 return CreateInduction(kPeriodic,
596 kNop,
597 induction,
598 last,
599 /*fetch*/ nullptr,
600 type);
601 }
602 return CreateInduction(kPeriodic,
603 kNop,
604 induction->op_a,
605 RotatePeriodicInduction(induction->op_b, last, type),
606 /*fetch*/ nullptr,
607 type);
608 }
609
TransferPhi(const HLoopInformation * loop,HInstruction * phi,size_t input_index,size_t adjust_input_size)610 HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferPhi(
611 const HLoopInformation* loop,
612 HInstruction* phi,
613 size_t input_index,
614 size_t adjust_input_size) {
615 // Match all phi inputs from input_index onwards exactly.
616 HInputsRef inputs = phi->GetInputs();
617 DCHECK_LT(input_index, inputs.size());
618 InductionInfo* a = LookupInfo(loop, inputs[input_index]);
619 for (size_t i = input_index + 1, n = inputs.size() - adjust_input_size; i < n; i++) {
620 InductionInfo* b = LookupInfo(loop, inputs[i]);
621 if (!InductionEqual(a, b)) {
622 return nullptr;
623 }
624 }
625 return a;
626 }
627
TransferAddSub(const HBasicBlock * context,const HLoopInformation * loop,InductionInfo * a,InductionInfo * b,InductionOp op,DataType::Type type)628 HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferAddSub(
629 const HBasicBlock* context,
630 const HLoopInformation* loop,
631 InductionInfo* a,
632 InductionInfo* b,
633 InductionOp op,
634 DataType::Type type) {
635 // Transfer over an addition or subtraction: any invariant, linear, polynomial, geometric,
636 // wrap-around, or periodic can be combined with an invariant to yield a similar result.
637 // Two linear or two polynomial inputs can be combined too. Other combinations fail.
638 if (a != nullptr && b != nullptr) {
639 if (IsNarrowingLinear(a) || IsNarrowingLinear(b)) {
640 return nullptr; // no transfer
641 } else if (a->induction_class == kInvariant && b->induction_class == kInvariant) {
642 return CreateInvariantOp(context, loop, op, a, b); // direct invariant
643 } else if ((a->induction_class == kLinear && b->induction_class == kLinear) ||
644 (a->induction_class == kPolynomial && b->induction_class == kPolynomial)) {
645 // Rule induc(a, b) + induc(a', b') -> induc(a + a', b + b').
646 InductionInfo* new_a = TransferAddSub(context, loop, a->op_a, b->op_a, op, type);
647 InductionInfo* new_b = TransferAddSub(context, loop, a->op_b, b->op_b, op, type);
648 if (new_a != nullptr && new_b != nullptr) {
649 return CreateInduction(a->induction_class, a->operation, new_a, new_b, a->fetch, type);
650 }
651 } else if (a->induction_class == kInvariant) {
652 // Rule a + induc(a', b') -> induc(a', a + b') or induc(a + a', a + b').
653 InductionInfo* new_a = b->op_a;
654 InductionInfo* new_b = TransferAddSub(context, loop, a, b->op_b, op, type);
655 if (b->induction_class == kWrapAround || b->induction_class == kPeriodic) {
656 new_a = TransferAddSub(context, loop, a, new_a, op, type);
657 } else if (op == kSub) { // Negation required.
658 new_a = TransferNeg(context, loop, new_a, type);
659 }
660 if (new_a != nullptr && new_b != nullptr) {
661 return CreateInduction(b->induction_class, b->operation, new_a, new_b, b->fetch, type);
662 }
663 } else if (b->induction_class == kInvariant) {
664 // Rule induc(a, b) + b' -> induc(a, b + b') or induc(a + b', b + b').
665 InductionInfo* new_a = a->op_a;
666 InductionInfo* new_b = TransferAddSub(context, loop, a->op_b, b, op, type);
667 if (a->induction_class == kWrapAround || a->induction_class == kPeriodic) {
668 new_a = TransferAddSub(context, loop, new_a, b, op, type);
669 }
670 if (new_a != nullptr && new_b != nullptr) {
671 return CreateInduction(a->induction_class, a->operation, new_a, new_b, a->fetch, type);
672 }
673 }
674 }
675 return nullptr;
676 }
677
TransferNeg(const HBasicBlock * context,const HLoopInformation * loop,InductionInfo * a,DataType::Type type)678 HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferNeg(
679 const HBasicBlock* context,
680 const HLoopInformation* loop,
681 InductionInfo* a,
682 DataType::Type type) {
683 // Transfer over a unary negation: an invariant, linear, polynomial, geometric (mul),
684 // wrap-around, or periodic input yields a similar but negated induction as result.
685 if (a != nullptr) {
686 if (IsNarrowingLinear(a)) {
687 return nullptr; // no transfer
688 } else if (a->induction_class == kInvariant) {
689 return CreateInvariantOp(context, loop, kNeg, nullptr, a); // direct invariant
690 } else if (a->induction_class != kGeometric || a->operation == kMul) {
691 // Rule - induc(a, b) -> induc(-a, -b).
692 InductionInfo* new_a = TransferNeg(context, loop, a->op_a, type);
693 InductionInfo* new_b = TransferNeg(context, loop, a->op_b, type);
694 if (new_a != nullptr && new_b != nullptr) {
695 return CreateInduction(a->induction_class, a->operation, new_a, new_b, a->fetch, type);
696 }
697 }
698 }
699 return nullptr;
700 }
701
TransferMul(const HBasicBlock * context,const HLoopInformation * loop,InductionInfo * a,InductionInfo * b,DataType::Type type)702 HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferMul(
703 const HBasicBlock* context,
704 const HLoopInformation* loop,
705 InductionInfo* a,
706 InductionInfo* b,
707 DataType::Type type) {
708 // Transfer over a multiplication: any invariant, linear, polynomial, geometric (mul),
709 // wrap-around, or periodic can be multiplied with an invariant to yield a similar
710 // but multiplied result. Two non-invariant inputs cannot be multiplied, however.
711 if (a != nullptr && b != nullptr) {
712 if (IsNarrowingLinear(a) || IsNarrowingLinear(b)) {
713 return nullptr; // no transfer
714 } else if (a->induction_class == kInvariant && b->induction_class == kInvariant) {
715 return CreateInvariantOp(context, loop, kMul, a, b); // direct invariant
716 } else if (a->induction_class == kInvariant && (b->induction_class != kGeometric ||
717 b->operation == kMul)) {
718 // Rule a * induc(a', b') -> induc(a * a', b * b').
719 InductionInfo* new_a = TransferMul(context, loop, a, b->op_a, type);
720 InductionInfo* new_b = TransferMul(context, loop, a, b->op_b, type);
721 if (new_a != nullptr && new_b != nullptr) {
722 return CreateInduction(b->induction_class, b->operation, new_a, new_b, b->fetch, type);
723 }
724 } else if (b->induction_class == kInvariant && (a->induction_class != kGeometric ||
725 a->operation == kMul)) {
726 // Rule induc(a, b) * b' -> induc(a * b', b * b').
727 InductionInfo* new_a = TransferMul(context, loop, a->op_a, b, type);
728 InductionInfo* new_b = TransferMul(context, loop, a->op_b, b, type);
729 if (new_a != nullptr && new_b != nullptr) {
730 return CreateInduction(a->induction_class, a->operation, new_a, new_b, a->fetch, type);
731 }
732 }
733 }
734 return nullptr;
735 }
736
TransferConversion(InductionInfo * a,DataType::Type from,DataType::Type to)737 HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferConversion(
738 InductionInfo* a,
739 DataType::Type from,
740 DataType::Type to) {
741 if (a != nullptr) {
742 // Allow narrowing conversion on linear induction in certain cases:
743 // induction is already at narrow type, or can be made narrower.
744 if (IsNarrowingIntegralConversion(from, to) &&
745 a->induction_class == kLinear &&
746 (a->type == to || IsNarrowingIntegralConversion(a->type, to))) {
747 return CreateInduction(kLinear, kNop, a->op_a, a->op_b, a->fetch, to);
748 }
749 }
750 return nullptr;
751 }
752
SolvePhi(HInstruction * phi,size_t input_index,size_t adjust_input_size,const ScopedArenaSafeMap<HInstruction *,InductionInfo * > & cycle)753 HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolvePhi(
754 HInstruction* phi,
755 size_t input_index,
756 size_t adjust_input_size,
757 const ScopedArenaSafeMap<HInstruction*, InductionInfo*>& cycle) {
758 // Match all phi inputs from input_index onwards exactly.
759 HInputsRef inputs = phi->GetInputs();
760 DCHECK_LT(input_index, inputs.size());
761 auto ita = cycle.find(inputs[input_index]);
762 if (ita != cycle.end()) {
763 for (size_t i = input_index + 1, n = inputs.size() - adjust_input_size; i < n; i++) {
764 auto itb = cycle.find(inputs[i]);
765 if (itb == cycle.end() ||
766 !HInductionVarAnalysis::InductionEqual(ita->second, itb->second)) {
767 return nullptr;
768 }
769 }
770 return ita->second;
771 }
772 return nullptr;
773 }
774
SolvePhiAllInputs(const HLoopInformation * loop,HInstruction * entry_phi,HInstruction * phi,const ScopedArenaSafeMap<HInstruction *,InductionInfo * > & cycle,DataType::Type type)775 HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolvePhiAllInputs(
776 const HLoopInformation* loop,
777 HInstruction* entry_phi,
778 HInstruction* phi,
779 const ScopedArenaSafeMap<HInstruction*, InductionInfo*>& cycle,
780 DataType::Type type) {
781 // Match all phi inputs.
782 InductionInfo* match = SolvePhi(phi, /*input_index=*/ 0, /*adjust_input_size=*/ 0, cycle);
783 if (match != nullptr) {
784 return match;
785 }
786
787 // Otherwise, try to solve for a periodic seeded from phi onward.
788 // Only tight multi-statement cycles are considered in order to
789 // simplify rotating the periodic during the final classification.
790 if (phi->IsLoopHeaderPhi() && phi->InputCount() == 2) {
791 InductionInfo* a = LookupInfo(loop, phi->InputAt(0));
792 if (a != nullptr && a->induction_class == kInvariant) {
793 if (phi->InputAt(1) == entry_phi) {
794 InductionInfo* initial = LookupInfo(loop, entry_phi->InputAt(0));
795 return CreateInduction(kPeriodic, kNop, a, initial, /*fetch*/ nullptr, type);
796 }
797 InductionInfo* b = SolvePhi(phi, /*input_index=*/ 1, /*adjust_input_size=*/ 0, cycle);
798 if (b != nullptr && b->induction_class == kPeriodic) {
799 return CreateInduction(kPeriodic, kNop, a, b, /*fetch*/ nullptr, type);
800 }
801 }
802 }
803 return nullptr;
804 }
805
SolveAddSub(const HLoopInformation * loop,HInstruction * entry_phi,HInstruction * instruction,HInstruction * x,HInstruction * y,InductionOp op,const ScopedArenaSafeMap<HInstruction *,InductionInfo * > & cycle,DataType::Type type)806 HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolveAddSub(
807 const HLoopInformation* loop,
808 HInstruction* entry_phi,
809 HInstruction* instruction,
810 HInstruction* x,
811 HInstruction* y,
812 InductionOp op,
813 const ScopedArenaSafeMap<HInstruction*, InductionInfo*>& cycle,
814 DataType::Type type) {
815 const HBasicBlock* context = instruction->GetBlock();
816 auto main_solve_add_sub = [&]() -> HInductionVarAnalysis::InductionInfo* {
817 // Solve within a cycle over an addition or subtraction.
818 InductionInfo* b = LookupInfo(loop, y);
819 if (b != nullptr) {
820 if (b->induction_class == kInvariant) {
821 // Adding or subtracting an invariant value, seeded from phi,
822 // keeps adding to the stride of the linear induction.
823 if (x == entry_phi) {
824 return (op == kAdd) ? b : CreateInvariantOp(context, loop, kNeg, nullptr, b);
825 }
826 auto it = cycle.find(x);
827 if (it != cycle.end()) {
828 InductionInfo* a = it->second;
829 if (a->induction_class == kInvariant) {
830 return CreateInvariantOp(context, loop, op, a, b);
831 }
832 }
833 } else if (b->induction_class == kLinear && b->type == type) {
834 // Solve within a tight cycle that adds a term that is already classified as a linear
835 // induction for a polynomial induction k = k + i (represented as sum over linear terms).
836 if (x == entry_phi &&
837 entry_phi->InputCount() == 2 &&
838 instruction == entry_phi->InputAt(1)) {
839 InductionInfo* initial = LookupInfo(loop, entry_phi->InputAt(0));
840 InductionInfo* new_a = op == kAdd ? b : TransferNeg(context, loop, b, type);
841 if (new_a != nullptr) {
842 return CreateInduction(kPolynomial, kNop, new_a, initial, /*fetch*/ nullptr, type);
843 }
844 }
845 }
846 }
847 return nullptr;
848 };
849 HInductionVarAnalysis::InductionInfo* result = main_solve_add_sub();
850 if (result == nullptr) {
851 // Try some alternatives before failing.
852 if (op == kAdd) {
853 // Try the other way around for an addition.
854 std::swap(x, y);
855 result = main_solve_add_sub();
856 } else if (op == kSub) {
857 // Solve within a tight cycle that is formed by exactly two instructions,
858 // one phi and one update, for a periodic idiom of the form k = c - k.
859 if (y == entry_phi && entry_phi->InputCount() == 2 && instruction == entry_phi->InputAt(1)) {
860 InductionInfo* a = LookupInfo(loop, x);
861 if (a != nullptr && a->induction_class == kInvariant) {
862 InductionInfo* initial = LookupInfo(loop, entry_phi->InputAt(0));
863 result = CreateInduction(kPeriodic,
864 kNop,
865 CreateInvariantOp(context, loop, kSub, a, initial),
866 initial,
867 /*fetch*/ nullptr,
868 type);
869 }
870 }
871 }
872 }
873 return result;
874 }
875
SolveOp(const HLoopInformation * loop,HInstruction * entry_phi,HInstruction * instruction,HInstruction * x,HInstruction * y,InductionOp op,DataType::Type type)876 HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolveOp(const HLoopInformation* loop,
877 HInstruction* entry_phi,
878 HInstruction* instruction,
879 HInstruction* x,
880 HInstruction* y,
881 InductionOp op,
882 DataType::Type type) {
883 // Solve within a tight cycle for a binary operation k = k op c or, for some op, k = c op k.
884 if (entry_phi->InputCount() == 2 && instruction == entry_phi->InputAt(1)) {
885 InductionInfo* c = nullptr;
886 InductionInfo* b = LookupInfo(loop, y);
887 if (b != nullptr && b->induction_class == kInvariant && entry_phi == x) {
888 c = b;
889 } else if (op != kDiv && op != kRem) {
890 InductionInfo* a = LookupInfo(loop, x);
891 if (a != nullptr && a->induction_class == kInvariant && entry_phi == y) {
892 c = a;
893 }
894 }
895 // Found suitable operand left or right?
896 if (c != nullptr) {
897 const HBasicBlock* context = instruction->GetBlock();
898 InductionInfo* initial = LookupInfo(loop, entry_phi->InputAt(0));
899 switch (op) {
900 case kMul:
901 case kDiv:
902 // Restrict base of geometric induction to direct fetch.
903 if (c->operation == kFetch) {
904 return CreateInduction(kGeometric,
905 op,
906 initial,
907 CreateConstant(0, type),
908 c->fetch,
909 type);
910 }
911 break;
912 case kRem:
913 // Idiomatic MOD wrap-around induction.
914 return CreateInduction(kWrapAround,
915 kNop,
916 initial,
917 CreateInvariantOp(context, loop, kRem, initial, c),
918 /*fetch*/ nullptr,
919 type);
920 case kXor:
921 // Idiomatic XOR periodic induction.
922 return CreateInduction(kPeriodic,
923 kNop,
924 CreateInvariantOp(context, loop, kXor, initial, c),
925 initial,
926 /*fetch*/ nullptr,
927 type);
928 default:
929 LOG(FATAL) << op;
930 UNREACHABLE();
931 }
932 }
933 }
934 return nullptr;
935 }
936
SolveTest(const HLoopInformation * loop,HInstruction * entry_phi,HInstruction * instruction,int64_t opposite_value,DataType::Type type)937 HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolveTest(const HLoopInformation* loop,
938 HInstruction* entry_phi,
939 HInstruction* instruction,
940 int64_t opposite_value,
941 DataType::Type type) {
942 // Detect hidden XOR construction in x = (x == false) or x = (x != true).
943 const HBasicBlock* context = instruction->GetBlock();
944 HInstruction* x = instruction->InputAt(0);
945 HInstruction* y = instruction->InputAt(1);
946 int64_t value = -1;
947 if (IsExact(context, loop, LookupInfo(loop, x), &value) && value == opposite_value) {
948 return SolveOp(loop, entry_phi, instruction, graph_->GetIntConstant(1), y, kXor, type);
949 } else if (IsExact(context, loop, LookupInfo(loop, y), &value) && value == opposite_value) {
950 return SolveOp(loop, entry_phi, instruction, x, graph_->GetIntConstant(1), kXor, type);
951 }
952 return nullptr;
953 }
954
SolveConversion(const HLoopInformation * loop,HInstruction * entry_phi,HTypeConversion * conversion,const ScopedArenaSafeMap<HInstruction *,InductionInfo * > & cycle,DataType::Type * type)955 HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolveConversion(
956 const HLoopInformation* loop,
957 HInstruction* entry_phi,
958 HTypeConversion* conversion,
959 const ScopedArenaSafeMap<HInstruction*, InductionInfo*>& cycle,
960 /*inout*/ DataType::Type* type) {
961 DataType::Type from = conversion->GetInputType();
962 DataType::Type to = conversion->GetResultType();
963 // A narrowing conversion is allowed as *last* operation of the cycle of a linear induction
964 // with an initial value that fits the type, provided that the narrowest encountered type is
965 // recorded with the induction to account for the precision loss. The narrower induction does
966 // *not* transfer to any wider operations, however, since these may yield out-of-type values
967 if (entry_phi->InputCount() == 2 && conversion == entry_phi->InputAt(1)) {
968 int64_t min = DataType::MinValueOfIntegralType(to);
969 int64_t max = DataType::MaxValueOfIntegralType(to);
970 int64_t value = 0;
971 const HBasicBlock* context = conversion->GetBlock();
972 InductionInfo* initial = LookupInfo(loop, entry_phi->InputAt(0));
973 if (IsNarrowingIntegralConversion(from, to) &&
974 IsAtLeast(context, loop, initial, &value) && value >= min &&
975 IsAtMost(context, loop, initial, &value) && value <= max) {
976 auto it = cycle.find(conversion->GetInput());
977 if (it != cycle.end() && it->second->induction_class == kInvariant) {
978 *type = to;
979 return it->second;
980 }
981 }
982 }
983 return nullptr;
984 }
985
986 //
987 // Loop trip count analysis methods.
988 //
989
VisitControl(const HLoopInformation * loop)990 void HInductionVarAnalysis::VisitControl(const HLoopInformation* loop) {
991 HInstruction* control = loop->GetHeader()->GetLastInstruction();
992 if (control->IsIf()) {
993 HIf* ifs = control->AsIf();
994 HBasicBlock* if_true = ifs->IfTrueSuccessor();
995 HBasicBlock* if_false = ifs->IfFalseSuccessor();
996 HInstruction* if_expr = ifs->InputAt(0);
997 // Determine if loop has following structure in header.
998 // loop-header: ....
999 // if (condition) goto X
1000 if (if_expr->IsCondition()) {
1001 HCondition* condition = if_expr->AsCondition();
1002 const HBasicBlock* context = condition->GetBlock();
1003 InductionInfo* a = LookupInfo(loop, condition->InputAt(0));
1004 InductionInfo* b = LookupInfo(loop, condition->InputAt(1));
1005 DataType::Type type = ImplicitConversion(condition->InputAt(0)->GetType());
1006 // Determine if the loop control uses a known sequence on an if-exit (X outside) or on
1007 // an if-iterate (X inside), expressed as if-iterate when passed into VisitCondition().
1008 if (a == nullptr || b == nullptr) {
1009 return; // Loop control is not a sequence.
1010 } else if (if_true->GetLoopInformation() != loop && if_false->GetLoopInformation() == loop) {
1011 VisitCondition(context, loop, if_false, a, b, type, condition->GetOppositeCondition());
1012 } else if (if_true->GetLoopInformation() == loop && if_false->GetLoopInformation() != loop) {
1013 VisitCondition(context, loop, if_true, a, b, type, condition->GetCondition());
1014 }
1015 }
1016 }
1017 }
1018
VisitCondition(const HBasicBlock * context,const HLoopInformation * loop,HBasicBlock * body,InductionInfo * a,InductionInfo * b,DataType::Type type,IfCondition cmp)1019 void HInductionVarAnalysis::VisitCondition(const HBasicBlock* context,
1020 const HLoopInformation* loop,
1021 HBasicBlock* body,
1022 InductionInfo* a,
1023 InductionInfo* b,
1024 DataType::Type type,
1025 IfCondition cmp) {
1026 if (a->induction_class == kInvariant && b->induction_class == kLinear) {
1027 // Swap condition if induction is at right-hand-side (e.g. U > i is same as i < U).
1028 switch (cmp) {
1029 case kCondLT: VisitCondition(context, loop, body, b, a, type, kCondGT); break;
1030 case kCondLE: VisitCondition(context, loop, body, b, a, type, kCondGE); break;
1031 case kCondGT: VisitCondition(context, loop, body, b, a, type, kCondLT); break;
1032 case kCondGE: VisitCondition(context, loop, body, b, a, type, kCondLE); break;
1033 case kCondNE: VisitCondition(context, loop, body, b, a, type, kCondNE); break;
1034 default: break;
1035 }
1036 } else if (a->induction_class == kLinear && b->induction_class == kInvariant) {
1037 // Analyze condition with induction at left-hand-side (e.g. i < U).
1038 InductionInfo* lower_expr = a->op_b;
1039 InductionInfo* upper_expr = b;
1040 InductionInfo* stride_expr = a->op_a;
1041 // Test for constant stride and integral condition.
1042 int64_t stride_value = 0;
1043 if (!IsExact(context, loop, stride_expr, &stride_value)) {
1044 return; // unknown stride
1045 } else if (type != DataType::Type::kInt32 && type != DataType::Type::kInt64) {
1046 return; // not integral
1047 }
1048 // Since loops with a i != U condition will not be normalized by the method below, first
1049 // try to rewrite a break-loop with terminating condition i != U into an equivalent loop
1050 // with non-strict end condition i <= U or i >= U if such a rewriting is possible and safe.
1051 if (cmp == kCondNE && RewriteBreakLoop(context, loop, body, stride_value, type)) {
1052 cmp = stride_value > 0 ? kCondLE : kCondGE;
1053 }
1054 // If this rewriting failed, try to rewrite condition i != U into strict end condition i < U
1055 // or i > U if this end condition is reached exactly (tested by verifying if the loop has a
1056 // unit stride and the non-strict condition would be always taken).
1057 if (cmp == kCondNE &&
1058 ((stride_value == +1 && IsTaken(context, loop, lower_expr, upper_expr, kCondLE)) ||
1059 (stride_value == -1 && IsTaken(context, loop, lower_expr, upper_expr, kCondGE)))) {
1060 cmp = stride_value > 0 ? kCondLT : kCondGT;
1061 }
1062 // A mismatch between the type of condition and the induction is only allowed if the,
1063 // necessarily narrower, induction range fits the narrower control.
1064 if (type != a->type &&
1065 !FitsNarrowerControl(context, loop, lower_expr, upper_expr, stride_value, a->type, cmp)) {
1066 return; // mismatched type
1067 }
1068 // Normalize a linear loop control with a nonzero stride:
1069 // stride > 0, either i < U or i <= U
1070 // stride < 0, either i > U or i >= U
1071 if ((stride_value > 0 && (cmp == kCondLT || cmp == kCondLE)) ||
1072 (stride_value < 0 && (cmp == kCondGT || cmp == kCondGE))) {
1073 VisitTripCount(context, loop, lower_expr, upper_expr, stride_expr, stride_value, type, cmp);
1074 }
1075 }
1076 }
1077
VisitTripCount(const HBasicBlock * context,const HLoopInformation * loop,InductionInfo * lower_expr,InductionInfo * upper_expr,InductionInfo * stride_expr,int64_t stride_value,DataType::Type type,IfCondition cmp)1078 void HInductionVarAnalysis::VisitTripCount(const HBasicBlock* context,
1079 const HLoopInformation* loop,
1080 InductionInfo* lower_expr,
1081 InductionInfo* upper_expr,
1082 InductionInfo* stride_expr,
1083 int64_t stride_value,
1084 DataType::Type type,
1085 IfCondition cmp) {
1086 // Any loop of the general form:
1087 //
1088 // for (i = L; i <= U; i += S) // S > 0
1089 // or for (i = L; i >= U; i += S) // S < 0
1090 // .. i ..
1091 //
1092 // can be normalized into:
1093 //
1094 // for (n = 0; n < TC; n++) // where TC = (U + S - L) / S
1095 // .. L + S * n ..
1096 //
1097 // taking the following into consideration:
1098 //
1099 // (1) Using the same precision, the TC (trip-count) expression should be interpreted as
1100 // an unsigned entity, for example, as in the following loop that uses the full range:
1101 // for (int i = INT_MIN; i < INT_MAX; i++) // TC = UINT_MAX
1102 // (2) The TC is only valid if the loop is taken, otherwise TC = 0, as in:
1103 // for (int i = 12; i < U; i++) // TC = 0 when U <= 12
1104 // If this cannot be determined at compile-time, the TC is only valid within the
1105 // loop-body proper, not the loop-header unless enforced with an explicit taken-test.
1106 // (3) The TC is only valid if the loop is finite, otherwise TC has no value, as in:
1107 // for (int i = 0; i <= U; i++) // TC = Inf when U = INT_MAX
1108 // If this cannot be determined at compile-time, the TC is only valid when enforced
1109 // with an explicit finite-test.
1110 // (4) For loops which early-exits, the TC forms an upper bound, as in:
1111 // for (int i = 0; i < 10 && ....; i++) // TC <= 10
1112 InductionInfo* trip_count = upper_expr;
1113 const bool is_taken = IsTaken(context, loop, lower_expr, upper_expr, cmp);
1114 const bool is_finite = IsFinite(context, loop, upper_expr, stride_value, type, cmp);
1115 const bool cancels = (cmp == kCondLT || cmp == kCondGT) && std::abs(stride_value) == 1;
1116 if (!cancels) {
1117 // Convert exclusive integral inequality into inclusive integral inequality,
1118 // viz. condition i < U is i <= U - 1 and condition i > U is i >= U + 1.
1119 if (cmp == kCondLT) {
1120 trip_count = CreateInvariantOp(context, loop, kSub, trip_count, CreateConstant(1, type));
1121 } else if (cmp == kCondGT) {
1122 trip_count = CreateInvariantOp(context, loop, kAdd, trip_count, CreateConstant(1, type));
1123 }
1124 // Compensate for stride.
1125 trip_count = CreateInvariantOp(context, loop, kAdd, trip_count, stride_expr);
1126 }
1127 trip_count = CreateInvariantOp(context, loop, kSub, trip_count, lower_expr);
1128 trip_count = CreateInvariantOp(context, loop, kDiv, trip_count, stride_expr);
1129 // Assign the trip-count expression to the loop control. Clients that use the information
1130 // should be aware that the expression is only valid under the conditions listed above.
1131 InductionOp tcKind = kTripCountInBodyUnsafe; // needs both tests
1132 if (is_taken && is_finite) {
1133 tcKind = kTripCountInLoop; // needs neither test
1134 } else if (is_finite) {
1135 tcKind = kTripCountInBody; // needs taken-test
1136 } else if (is_taken) {
1137 tcKind = kTripCountInLoopUnsafe; // needs finite-test
1138 }
1139 InductionOp op = kNop;
1140 switch (cmp) {
1141 case kCondLT: op = kLT; break;
1142 case kCondLE: op = kLE; break;
1143 case kCondGT: op = kGT; break;
1144 case kCondGE: op = kGE; break;
1145 default: LOG(FATAL) << "CONDITION UNREACHABLE";
1146 }
1147 // Associate trip count with control instruction, rather than the condition (even
1148 // though it's its use) since former provides a convenient use-free placeholder.
1149 HInstruction* control = loop->GetHeader()->GetLastInstruction();
1150 InductionInfo* taken_test = CreateInvariantOp(context, loop, op, lower_expr, upper_expr);
1151 DCHECK(control->IsIf());
1152 AssignInfo(loop, control, CreateTripCount(tcKind, trip_count, taken_test, type));
1153 }
1154
IsTaken(const HBasicBlock * context,const HLoopInformation * loop,InductionInfo * lower_expr,InductionInfo * upper_expr,IfCondition cmp)1155 bool HInductionVarAnalysis::IsTaken(const HBasicBlock* context,
1156 const HLoopInformation* loop,
1157 InductionInfo* lower_expr,
1158 InductionInfo* upper_expr,
1159 IfCondition cmp) {
1160 int64_t lower_value;
1161 int64_t upper_value;
1162 switch (cmp) {
1163 case kCondLT:
1164 return IsAtMost(context, loop, lower_expr, &lower_value)
1165 && IsAtLeast(context, loop, upper_expr, &upper_value)
1166 && lower_value < upper_value;
1167 case kCondLE:
1168 return IsAtMost(context, loop, lower_expr, &lower_value)
1169 && IsAtLeast(context, loop, upper_expr, &upper_value)
1170 && lower_value <= upper_value;
1171 case kCondGT:
1172 return IsAtLeast(context, loop, lower_expr, &lower_value)
1173 && IsAtMost(context, loop, upper_expr, &upper_value)
1174 && lower_value > upper_value;
1175 case kCondGE:
1176 return IsAtLeast(context, loop, lower_expr, &lower_value)
1177 && IsAtMost(context, loop, upper_expr, &upper_value)
1178 && lower_value >= upper_value;
1179 default:
1180 LOG(FATAL) << "CONDITION UNREACHABLE";
1181 UNREACHABLE();
1182 }
1183 }
1184
IsFinite(const HBasicBlock * context,const HLoopInformation * loop,InductionInfo * upper_expr,int64_t stride_value,DataType::Type type,IfCondition cmp)1185 bool HInductionVarAnalysis::IsFinite(const HBasicBlock* context,
1186 const HLoopInformation* loop,
1187 InductionInfo* upper_expr,
1188 int64_t stride_value,
1189 DataType::Type type,
1190 IfCondition cmp) {
1191 int64_t min = DataType::MinValueOfIntegralType(type);
1192 int64_t max = DataType::MaxValueOfIntegralType(type);
1193 // Some rules under which it is certain at compile-time that the loop is finite.
1194 int64_t value;
1195 switch (cmp) {
1196 case kCondLT:
1197 return stride_value == 1 ||
1198 (IsAtMost(context, loop, upper_expr, &value) && value <= (max - stride_value + 1));
1199 case kCondLE:
1200 return (IsAtMost(context, loop, upper_expr, &value) && value <= (max - stride_value));
1201 case kCondGT:
1202 return stride_value == -1 ||
1203 (IsAtLeast(context, loop, upper_expr, &value) && value >= (min - stride_value - 1));
1204 case kCondGE:
1205 return (IsAtLeast(context, loop, upper_expr, &value) && value >= (min - stride_value));
1206 default:
1207 LOG(FATAL) << "CONDITION UNREACHABLE";
1208 UNREACHABLE();
1209 }
1210 }
1211
FitsNarrowerControl(const HBasicBlock * context,const HLoopInformation * loop,InductionInfo * lower_expr,InductionInfo * upper_expr,int64_t stride_value,DataType::Type type,IfCondition cmp)1212 bool HInductionVarAnalysis::FitsNarrowerControl(const HBasicBlock* context,
1213 const HLoopInformation* loop,
1214 InductionInfo* lower_expr,
1215 InductionInfo* upper_expr,
1216 int64_t stride_value,
1217 DataType::Type type,
1218 IfCondition cmp) {
1219 int64_t min = DataType::MinValueOfIntegralType(type);
1220 int64_t max = DataType::MaxValueOfIntegralType(type);
1221 // Inclusive test need one extra.
1222 if (stride_value != 1 && stride_value != -1) {
1223 return false; // non-unit stride
1224 } else if (cmp == kCondLE) {
1225 max--;
1226 } else if (cmp == kCondGE) {
1227 min++;
1228 }
1229 // Do both bounds fit the range?
1230 int64_t value = 0;
1231 return IsAtLeast(context, loop, lower_expr, &value) && value >= min &&
1232 IsAtMost(context, loop, lower_expr, &value) && value <= max &&
1233 IsAtLeast(context, loop, upper_expr, &value) && value >= min &&
1234 IsAtMost(context, loop, upper_expr, &value) && value <= max;
1235 }
1236
RewriteBreakLoop(const HBasicBlock * context,const HLoopInformation * loop,HBasicBlock * body,int64_t stride_value,DataType::Type type)1237 bool HInductionVarAnalysis::RewriteBreakLoop(const HBasicBlock* context,
1238 const HLoopInformation* loop,
1239 HBasicBlock* body,
1240 int64_t stride_value,
1241 DataType::Type type) {
1242 // Only accept unit stride.
1243 if (std::abs(stride_value) != 1) {
1244 return false;
1245 }
1246 // Simple terminating i != U condition, used nowhere else.
1247 HIf* ifs = loop->GetHeader()->GetLastInstruction()->AsIf();
1248 HInstruction* cond = ifs->InputAt(0);
1249 if (ifs->GetPrevious() != cond || !cond->HasOnlyOneNonEnvironmentUse()) {
1250 return false;
1251 }
1252 int c = LookupInfo(loop, cond->InputAt(0))->induction_class == kLinear ? 0 : 1;
1253 HInstruction* index = cond->InputAt(c);
1254 HInstruction* upper = cond->InputAt(1 - c);
1255 // Safe to rewrite into i <= U?
1256 IfCondition cmp = stride_value > 0 ? kCondLE : kCondGE;
1257 if (!index->IsPhi() ||
1258 !IsFinite(context, loop, LookupInfo(loop, upper), stride_value, type, cmp)) {
1259 return false;
1260 }
1261 // Body consists of update to index i only, used nowhere else.
1262 if (body->GetSuccessors().size() != 1 ||
1263 body->GetSingleSuccessor() != loop->GetHeader() ||
1264 !body->GetPhis().IsEmpty() ||
1265 body->GetInstructions().IsEmpty() ||
1266 body->GetFirstInstruction() != index->InputAt(1) ||
1267 !body->GetFirstInstruction()->HasOnlyOneNonEnvironmentUse() ||
1268 !body->GetFirstInstruction()->GetNext()->IsGoto()) {
1269 return false;
1270 }
1271 // Always taken or guarded by enclosing condition.
1272 if (!IsTaken(context, loop, LookupInfo(loop, index)->op_b, LookupInfo(loop, upper), cmp) &&
1273 !IsGuardedBy(loop, cmp, index->InputAt(0), upper)) {
1274 return false;
1275 }
1276 // Test if break-loop body can be written, and do so on success.
1277 if (RewriteBreakLoopBody(loop, body, cond, index, upper, /*rewrite*/ false)) {
1278 RewriteBreakLoopBody(loop, body, cond, index, upper, /*rewrite*/ true);
1279 } else {
1280 return false;
1281 }
1282 // Rewrite condition in HIR.
1283 if (ifs->IfTrueSuccessor() != body) {
1284 cmp = (cmp == kCondLE) ? kCondGT : kCondLT;
1285 }
1286 HInstruction* rep = nullptr;
1287 switch (cmp) {
1288 case kCondLT: rep = new (graph_->GetAllocator()) HLessThan(index, upper); break;
1289 case kCondGT: rep = new (graph_->GetAllocator()) HGreaterThan(index, upper); break;
1290 case kCondLE: rep = new (graph_->GetAllocator()) HLessThanOrEqual(index, upper); break;
1291 case kCondGE: rep = new (graph_->GetAllocator()) HGreaterThanOrEqual(index, upper); break;
1292 default: LOG(FATAL) << cmp; UNREACHABLE();
1293 }
1294 loop->GetHeader()->ReplaceAndRemoveInstructionWith(cond, rep);
1295 return true;
1296 }
1297
1298 //
1299 // Helper methods.
1300 //
1301
AssignInfo(const HLoopInformation * loop,HInstruction * instruction,InductionInfo * info)1302 void HInductionVarAnalysis::AssignInfo(const HLoopInformation* loop,
1303 HInstruction* instruction,
1304 InductionInfo* info) {
1305 auto it = induction_.find(loop);
1306 if (it == induction_.end()) {
1307 it = induction_.Put(loop,
1308 ArenaSafeMap<HInstruction*, InductionInfo*>(
1309 std::less<HInstruction*>(),
1310 graph_->GetAllocator()->Adapter(kArenaAllocInductionVarAnalysis)));
1311 }
1312 it->second.Put(instruction, info);
1313 }
1314
LookupInfo(const HLoopInformation * loop,HInstruction * instruction)1315 HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::LookupInfo(
1316 const HLoopInformation* loop,
1317 HInstruction* instruction) {
1318 auto it = induction_.find(loop);
1319 if (it != induction_.end()) {
1320 auto loop_it = it->second.find(instruction);
1321 if (loop_it != it->second.end()) {
1322 return loop_it->second;
1323 }
1324 }
1325 if (loop->IsDefinedOutOfTheLoop(instruction)) {
1326 InductionInfo* info = CreateInvariantFetch(instruction);
1327 AssignInfo(loop, instruction, info);
1328 return info;
1329 }
1330 return nullptr;
1331 }
1332
CreateConstant(int64_t value,DataType::Type type)1333 HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::CreateConstant(int64_t value,
1334 DataType::Type type) {
1335 HInstruction* constant;
1336 switch (type) {
1337 case DataType::Type::kFloat64: constant = graph_->GetDoubleConstant(value); break;
1338 case DataType::Type::kFloat32: constant = graph_->GetFloatConstant(value); break;
1339 case DataType::Type::kInt64: constant = graph_->GetLongConstant(value); break;
1340 default: constant = graph_->GetIntConstant(value); break;
1341 }
1342 return CreateInvariantFetch(constant);
1343 }
1344
CreateSimplifiedInvariant(const HBasicBlock * context,const HLoopInformation * loop,InductionOp op,InductionInfo * a,InductionInfo * b)1345 HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::CreateSimplifiedInvariant(
1346 const HBasicBlock* context,
1347 const HLoopInformation* loop,
1348 InductionOp op,
1349 InductionInfo* a,
1350 InductionInfo* b) {
1351 // Perform some light-weight simplifications during construction of a new invariant.
1352 // This often safes memory and yields a more concise representation of the induction.
1353 // More exhaustive simplifications are done by later phases once induction nodes are
1354 // translated back into HIR code (e.g. by loop optimizations or BCE).
1355 int64_t value = -1;
1356 if (IsExact(context, loop, a, &value)) {
1357 if (value == 0) {
1358 // Simplify 0 + b = b, 0 ^ b = b, 0 * b = 0.
1359 if (op == kAdd || op == kXor) {
1360 return b;
1361 } else if (op == kMul) {
1362 return a;
1363 }
1364 } else if (op == kMul) {
1365 // Simplify 1 * b = b, -1 * b = -b
1366 if (value == 1) {
1367 return b;
1368 } else if (value == -1) {
1369 return CreateSimplifiedInvariant(context, loop, kNeg, nullptr, b);
1370 }
1371 }
1372 }
1373 if (IsExact(context, loop, b, &value)) {
1374 if (value == 0) {
1375 // Simplify a + 0 = a, a - 0 = a, a ^ 0 = a, a * 0 = 0, -0 = 0.
1376 if (op == kAdd || op == kSub || op == kXor) {
1377 return a;
1378 } else if (op == kMul || op == kNeg) {
1379 return b;
1380 }
1381 } else if (op == kMul || op == kDiv) {
1382 // Simplify a * 1 = a, a / 1 = a, a * -1 = -a, a / -1 = -a
1383 if (value == 1) {
1384 return a;
1385 } else if (value == -1) {
1386 return CreateSimplifiedInvariant(context, loop, kNeg, nullptr, a);
1387 }
1388 }
1389 } else if (b->operation == kNeg) {
1390 // Simplify a + (-b) = a - b, a - (-b) = a + b, -(-b) = b.
1391 if (op == kAdd) {
1392 return CreateSimplifiedInvariant(context, loop, kSub, a, b->op_b);
1393 } else if (op == kSub) {
1394 return CreateSimplifiedInvariant(context, loop, kAdd, a, b->op_b);
1395 } else if (op == kNeg) {
1396 return b->op_b;
1397 }
1398 } else if (b->operation == kSub) {
1399 // Simplify - (a - b) = b - a.
1400 if (op == kNeg) {
1401 return CreateSimplifiedInvariant(context, loop, kSub, b->op_b, b->op_a);
1402 }
1403 }
1404 return new (graph_->GetAllocator()) InductionInfo(
1405 kInvariant, op, a, b, nullptr, ImplicitConversion(b->type));
1406 }
1407
GetShiftConstant(const HLoopInformation * loop,HInstruction * instruction,InductionInfo * initial)1408 HInstruction* HInductionVarAnalysis::GetShiftConstant(const HLoopInformation* loop,
1409 HInstruction* instruction,
1410 InductionInfo* initial) {
1411 DCHECK(instruction->IsShl() || instruction->IsShr() || instruction->IsUShr());
1412 const HBasicBlock* context = instruction->GetBlock();
1413 // Shift-rights are only the same as division for non-negative initial inputs.
1414 // Otherwise we would round incorrectly.
1415 if (initial != nullptr) {
1416 int64_t value = -1;
1417 if (!IsAtLeast(context, loop, initial, &value) || value < 0) {
1418 return nullptr;
1419 }
1420 }
1421 // Obtain the constant needed to treat shift as equivalent multiplication or division.
1422 // This yields an existing instruction if the constant is already there. Otherwise, this
1423 // has a side effect on the HIR. The restriction on the shift factor avoids generating a
1424 // negative constant (viz. 1 << 31 and 1L << 63 set the sign bit). The code assumes that
1425 // generalization for shift factors outside [0,32) and [0,64) ranges is done earlier.
1426 InductionInfo* b = LookupInfo(loop, instruction->InputAt(1));
1427 int64_t value = -1;
1428 if (IsExact(context, loop, b, &value)) {
1429 DataType::Type type = instruction->InputAt(0)->GetType();
1430 if (type == DataType::Type::kInt32 && 0 <= value && value < 31) {
1431 return graph_->GetIntConstant(1 << value);
1432 }
1433 if (type == DataType::Type::kInt64 && 0 <= value && value < 63) {
1434 return graph_->GetLongConstant(1L << value);
1435 }
1436 }
1437 return nullptr;
1438 }
1439
AssignCycle(HPhi * phi,ArrayRef<HInstruction * const> scc)1440 void HInductionVarAnalysis::AssignCycle(HPhi* phi, ArrayRef<HInstruction* const> scc) {
1441 ArenaSet<HInstruction*>* set = &cycles_.Put(phi, ArenaSet<HInstruction*>(
1442 graph_->GetAllocator()->Adapter(kArenaAllocInductionVarAnalysis)))->second;
1443 for (HInstruction* i : scc) {
1444 set->insert(i);
1445 }
1446 }
1447
LookupCycle(HPhi * phi)1448 ArenaSet<HInstruction*>* HInductionVarAnalysis::LookupCycle(HPhi* phi) {
1449 auto it = cycles_.find(phi);
1450 if (it != cycles_.end()) {
1451 return &it->second;
1452 }
1453 return nullptr;
1454 }
1455
IsExact(const HBasicBlock * context,const HLoopInformation * loop,InductionInfo * info,int64_t * value)1456 bool HInductionVarAnalysis::IsExact(const HBasicBlock* context,
1457 const HLoopInformation* loop,
1458 InductionInfo* info,
1459 /*out*/int64_t* value) {
1460 InductionVarRange range(this);
1461 return range.IsConstant(context, loop, info, InductionVarRange::kExact, value);
1462 }
1463
IsAtMost(const HBasicBlock * context,const HLoopInformation * loop,InductionInfo * info,int64_t * value)1464 bool HInductionVarAnalysis::IsAtMost(const HBasicBlock* context,
1465 const HLoopInformation* loop,
1466 InductionInfo* info,
1467 /*out*/int64_t* value) {
1468 InductionVarRange range(this);
1469 return range.IsConstant(context, loop, info, InductionVarRange::kAtMost, value);
1470 }
1471
IsAtLeast(const HBasicBlock * context,const HLoopInformation * loop,InductionInfo * info,int64_t * value)1472 bool HInductionVarAnalysis::IsAtLeast(const HBasicBlock* context,
1473 const HLoopInformation* loop,
1474 InductionInfo* info,
1475 /*out*/int64_t* value) {
1476 InductionVarRange range(this);
1477 return range.IsConstant(context, loop, info, InductionVarRange::kAtLeast, value);
1478 }
1479
IsNarrowingLinear(InductionInfo * info)1480 bool HInductionVarAnalysis::IsNarrowingLinear(InductionInfo* info) {
1481 return info != nullptr &&
1482 info->induction_class == kLinear &&
1483 (info->type == DataType::Type::kUint8 ||
1484 info->type == DataType::Type::kInt8 ||
1485 info->type == DataType::Type::kUint16 ||
1486 info->type == DataType::Type::kInt16 ||
1487 (info->type == DataType::Type::kInt32 && (info->op_a->type == DataType::Type::kInt64 ||
1488 info->op_b->type == DataType::Type::kInt64)));
1489 }
1490
InductionEqual(InductionInfo * info1,InductionInfo * info2)1491 bool HInductionVarAnalysis::InductionEqual(InductionInfo* info1,
1492 InductionInfo* info2) {
1493 // Test structural equality only, without accounting for simplifications.
1494 if (info1 != nullptr && info2 != nullptr) {
1495 return
1496 info1->induction_class == info2->induction_class &&
1497 info1->operation == info2->operation &&
1498 info1->fetch == info2->fetch &&
1499 info1->type == info2->type &&
1500 InductionEqual(info1->op_a, info2->op_a) &&
1501 InductionEqual(info1->op_b, info2->op_b);
1502 }
1503 // Otherwise only two nullptrs are considered equal.
1504 return info1 == info2;
1505 }
1506
FetchToString(HInstruction * fetch)1507 std::string HInductionVarAnalysis::FetchToString(HInstruction* fetch) {
1508 DCHECK(fetch != nullptr);
1509 if (fetch->IsIntConstant()) {
1510 return std::to_string(fetch->AsIntConstant()->GetValue());
1511 } else if (fetch->IsLongConstant()) {
1512 return std::to_string(fetch->AsLongConstant()->GetValue());
1513 }
1514 return std::to_string(fetch->GetId()) + ":" + fetch->DebugName();
1515 }
1516
InductionToString(InductionInfo * info)1517 std::string HInductionVarAnalysis::InductionToString(InductionInfo* info) {
1518 if (info != nullptr) {
1519 if (info->induction_class == kInvariant) {
1520 std::string inv = "(";
1521 inv += InductionToString(info->op_a);
1522 switch (info->operation) {
1523 case kNop: inv += " @ "; break;
1524 case kAdd: inv += " + "; break;
1525 case kSub:
1526 case kNeg: inv += " - "; break;
1527 case kMul: inv += " * "; break;
1528 case kDiv: inv += " / "; break;
1529 case kRem: inv += " % "; break;
1530 case kXor: inv += " ^ "; break;
1531 case kLT: inv += " < "; break;
1532 case kLE: inv += " <= "; break;
1533 case kGT: inv += " > "; break;
1534 case kGE: inv += " >= "; break;
1535 case kFetch: inv += FetchToString(info->fetch); break;
1536 case kTripCountInLoop: inv += " (TC-loop) "; break;
1537 case kTripCountInBody: inv += " (TC-body) "; break;
1538 case kTripCountInLoopUnsafe: inv += " (TC-loop-unsafe) "; break;
1539 case kTripCountInBodyUnsafe: inv += " (TC-body-unsafe) "; break;
1540 }
1541 inv += InductionToString(info->op_b);
1542 inv += ")";
1543 return inv;
1544 } else {
1545 if (info->induction_class == kLinear) {
1546 DCHECK(info->operation == kNop);
1547 return "(" + InductionToString(info->op_a) + " * i + " +
1548 InductionToString(info->op_b) + "):" +
1549 DataType::PrettyDescriptor(info->type);
1550 } else if (info->induction_class == kPolynomial) {
1551 DCHECK(info->operation == kNop);
1552 return "poly(sum_lt(" + InductionToString(info->op_a) + ") + " +
1553 InductionToString(info->op_b) + "):" +
1554 DataType::PrettyDescriptor(info->type);
1555 } else if (info->induction_class == kGeometric) {
1556 DCHECK(info->operation == kMul || info->operation == kDiv);
1557 DCHECK(info->fetch != nullptr);
1558 return "geo(" + InductionToString(info->op_a) + " * " +
1559 FetchToString(info->fetch) +
1560 (info->operation == kMul ? " ^ i + " : " ^ -i + ") +
1561 InductionToString(info->op_b) + "):" +
1562 DataType::PrettyDescriptor(info->type);
1563 } else if (info->induction_class == kWrapAround) {
1564 DCHECK(info->operation == kNop);
1565 return "wrap(" + InductionToString(info->op_a) + ", " +
1566 InductionToString(info->op_b) + "):" +
1567 DataType::PrettyDescriptor(info->type);
1568 } else if (info->induction_class == kPeriodic) {
1569 DCHECK(info->operation == kNop);
1570 return "periodic(" + InductionToString(info->op_a) + ", " +
1571 InductionToString(info->op_b) + "):" +
1572 DataType::PrettyDescriptor(info->type);
1573 }
1574 }
1575 }
1576 return "";
1577 }
1578
1579 } // namespace art
1580