• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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