• 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_range.h"
18 
19 #include <limits>
20 #include "optimizing/nodes.h"
21 
22 namespace art HIDDEN {
23 
24 /** Returns true if 64-bit constant fits in 32-bit constant. */
CanLongValueFitIntoInt(int64_t c)25 static bool CanLongValueFitIntoInt(int64_t c) {
26   return std::numeric_limits<int32_t>::min() <= c && c <= std::numeric_limits<int32_t>::max();
27 }
28 
29 /** Returns true if 32-bit addition can be done safely. */
IsSafeAdd(int32_t c1,int32_t c2)30 static bool IsSafeAdd(int32_t c1, int32_t c2) {
31   return CanLongValueFitIntoInt(static_cast<int64_t>(c1) + static_cast<int64_t>(c2));
32 }
33 
34 /** Returns true if 32-bit subtraction can be done safely. */
IsSafeSub(int32_t c1,int32_t c2)35 static bool IsSafeSub(int32_t c1, int32_t c2) {
36   return CanLongValueFitIntoInt(static_cast<int64_t>(c1) - static_cast<int64_t>(c2));
37 }
38 
39 /** Returns true if 32-bit multiplication can be done safely. */
IsSafeMul(int32_t c1,int32_t c2)40 static bool IsSafeMul(int32_t c1, int32_t c2) {
41   return CanLongValueFitIntoInt(static_cast<int64_t>(c1) * static_cast<int64_t>(c2));
42 }
43 
44 /** Returns true if 32-bit division can be done safely. */
IsSafeDiv(int32_t c1,int32_t c2)45 static bool IsSafeDiv(int32_t c1, int32_t c2) {
46   return c2 != 0 && CanLongValueFitIntoInt(static_cast<int64_t>(c1) / static_cast<int64_t>(c2));
47 }
48 
49 /** Computes a * b for a,b > 0 (at least until first overflow happens). */
SafeMul(int64_t a,int64_t b,bool * overflow)50 static int64_t SafeMul(int64_t a, int64_t b, /*out*/ bool* overflow) {
51   if (a > 0 && b > 0 && a > (std::numeric_limits<int64_t>::max() / b)) {
52     *overflow = true;
53   }
54   return a * b;
55 }
56 
57 /** Returns b^e for b,e > 0. Sets overflow if arithmetic wrap-around occurred. */
IntPow(int64_t b,int64_t e,bool * overflow)58 static int64_t IntPow(int64_t b, int64_t e, /*out*/ bool* overflow) {
59   DCHECK_LT(0, b);
60   DCHECK_LT(0, e);
61   int64_t pow = 1;
62   while (e) {
63     if (e & 1) {
64       pow = SafeMul(pow, b, overflow);
65     }
66     e >>= 1;
67     if (e) {
68       b = SafeMul(b, b, overflow);
69     }
70   }
71   return pow;
72 }
73 
74 /** Hunts "under the hood" for a suitable instruction at the hint. */
IsMaxAtHint(HInstruction * instruction,HInstruction * hint,HInstruction ** suitable)75 static bool IsMaxAtHint(
76     HInstruction* instruction, HInstruction* hint, /*out*/HInstruction** suitable) {
77   if (instruction->IsMin()) {
78     // For MIN(x, y), return most suitable x or y as maximum.
79     return IsMaxAtHint(instruction->InputAt(0), hint, suitable) ||
80            IsMaxAtHint(instruction->InputAt(1), hint, suitable);
81   } else {
82     *suitable = instruction;
83     return HuntForDeclaration(instruction) == hint;
84   }
85 }
86 
87 /** Post-analysis simplification of a minimum value that makes the bound more useful to clients. */
SimplifyMin(InductionVarRange::Value v)88 static InductionVarRange::Value SimplifyMin(InductionVarRange::Value v) {
89   if (v.is_known && v.a_constant == 1 && v.b_constant <= 0) {
90     // If a == 1,  instruction >= 0 and b <= 0, just return the constant b.
91     // No arithmetic wrap-around can occur.
92     if (IsGEZero(v.instruction)) {
93       return InductionVarRange::Value(v.b_constant);
94     }
95   }
96   return v;
97 }
98 
99 /** Post-analysis simplification of a maximum value that makes the bound more useful to clients. */
SimplifyMax(InductionVarRange::Value v,HInstruction * hint)100 static InductionVarRange::Value SimplifyMax(InductionVarRange::Value v, HInstruction* hint) {
101   if (v.is_known && v.a_constant >= 1) {
102     // An upper bound a * (length / a) + b, where a >= 1, can be conservatively rewritten as
103     // length + b because length >= 0 is true.
104     int64_t value;
105     if (v.instruction->IsDiv() &&
106         v.instruction->InputAt(0)->IsArrayLength() &&
107         IsInt64AndGet(v.instruction->InputAt(1), &value) && v.a_constant == value) {
108       return InductionVarRange::Value(v.instruction->InputAt(0), 1, v.b_constant);
109     }
110     // If a == 1, the most suitable one suffices as maximum value.
111     HInstruction* suitable = nullptr;
112     if (v.a_constant == 1 && IsMaxAtHint(v.instruction, hint, &suitable)) {
113       return InductionVarRange::Value(suitable, 1, v.b_constant);
114     }
115   }
116   return v;
117 }
118 
119 /** Tests for a constant value. */
IsConstantValue(InductionVarRange::Value v)120 static bool IsConstantValue(InductionVarRange::Value v) {
121   return v.is_known && v.a_constant == 0;
122 }
123 
124 /** Corrects a value for type to account for arithmetic wrap-around in lower precision. */
CorrectForType(InductionVarRange::Value v,DataType::Type type)125 static InductionVarRange::Value CorrectForType(InductionVarRange::Value v, DataType::Type type) {
126   switch (type) {
127     case DataType::Type::kUint8:
128     case DataType::Type::kInt8:
129     case DataType::Type::kUint16:
130     case DataType::Type::kInt16: {
131       // Constants within range only.
132       // TODO: maybe some room for improvement, like allowing widening conversions
133       int32_t min = DataType::MinValueOfIntegralType(type);
134       int32_t max = DataType::MaxValueOfIntegralType(type);
135       return (IsConstantValue(v) && min <= v.b_constant && v.b_constant <= max)
136           ? v
137           : InductionVarRange::Value();
138     }
139     default:
140       return v;
141   }
142 }
143 
144 /** Inserts an instruction. */
Insert(HBasicBlock * block,HInstruction * instruction)145 static HInstruction* Insert(HBasicBlock* block, HInstruction* instruction) {
146   DCHECK(block != nullptr);
147   DCHECK(block->GetLastInstruction() != nullptr) << block->GetBlockId();
148   DCHECK(instruction != nullptr);
149   block->InsertInstructionBefore(instruction, block->GetLastInstruction());
150   return instruction;
151 }
152 
153 /** Obtains loop's control instruction. */
GetLoopControl(const HLoopInformation * loop)154 static HInstruction* GetLoopControl(const HLoopInformation* loop) {
155   DCHECK(loop != nullptr);
156   return loop->GetHeader()->GetLastInstruction();
157 }
158 
159 /** Determines whether the `context` is in the body of the `loop`. */
IsContextInBody(const HBasicBlock * context,const HLoopInformation * loop)160 static bool IsContextInBody(const HBasicBlock* context, const HLoopInformation* loop) {
161   DCHECK(loop != nullptr);
162   // We're currently classifying trip count only for the exit condition from loop header.
163   // All other blocks in the loop are considered loop body.
164   return context != loop->GetHeader() && loop->Contains(*context);
165 }
166 
167 /** Determines whether to use the full trip count for given `context`, `loop` and `is_min`. */
UseFullTripCount(const HBasicBlock * context,const HLoopInformation * loop,bool is_min)168 bool UseFullTripCount(const HBasicBlock* context, const HLoopInformation* loop, bool is_min) {
169   // We're currently classifying trip count only for the exit condition from loop header.
170   // So, we should call this helper function only if the loop control is an `HIf` with
171   // one edge leaving the loop. The loop header is the only block that's both inside
172   // the loop and not in the loop body.
173   DCHECK(GetLoopControl(loop)->IsIf());
174   DCHECK_NE(loop->Contains(*GetLoopControl(loop)->AsIf()->IfTrueSuccessor()),
175             loop->Contains(*GetLoopControl(loop)->AsIf()->IfFalseSuccessor()));
176   if (loop->Contains(*context)) {
177     // Use the full trip count if determining the maximum and context is not in the loop body.
178     DCHECK_NE(context == loop->GetHeader(), IsContextInBody(context, loop));
179     return !is_min && context == loop->GetHeader();
180   } else {
181     // Trip count after the loop is always the maximum (ignoring `is_min`),
182     // as long as the `context` is dominated by the loop control exit block.
183     // If there are additional exit edges, the value is unknown on those paths.
184     HInstruction* loop_control = GetLoopControl(loop);
185     HBasicBlock* then_block = loop_control->AsIf()->IfTrueSuccessor();
186     HBasicBlock* else_block = loop_control->AsIf()->IfFalseSuccessor();
187     HBasicBlock* loop_exit_block = loop->Contains(*then_block) ? else_block : then_block;
188     return loop_exit_block->Dominates(context);
189   }
190 }
191 
192 //
193 // Public class methods.
194 //
195 
InductionVarRange(HInductionVarAnalysis * induction_analysis)196 InductionVarRange::InductionVarRange(HInductionVarAnalysis* induction_analysis)
197     : induction_analysis_(induction_analysis),
198       chase_hint_(nullptr) {
199   DCHECK(induction_analysis != nullptr);
200 }
201 
GetInductionRange(const HBasicBlock * context,HInstruction * instruction,HInstruction * chase_hint,Value * min_val,Value * max_val,bool * needs_finite_test)202 bool InductionVarRange::GetInductionRange(const HBasicBlock* context,
203                                           HInstruction* instruction,
204                                           HInstruction* chase_hint,
205                                           /*out*/Value* min_val,
206                                           /*out*/Value* max_val,
207                                           /*out*/bool* needs_finite_test) {
208   const HLoopInformation* loop = nullptr;
209   HInductionVarAnalysis::InductionInfo* info = nullptr;
210   HInductionVarAnalysis::InductionInfo* trip = nullptr;
211   if (!HasInductionInfo(context, instruction, &loop, &info, &trip)) {
212     return false;
213   }
214   // Type int or lower (this is not too restrictive since intended clients, like
215   // bounds check elimination, will have truncated higher precision induction
216   // at their use point already).
217   switch (info->type) {
218     case DataType::Type::kUint8:
219     case DataType::Type::kInt8:
220     case DataType::Type::kUint16:
221     case DataType::Type::kInt16:
222     case DataType::Type::kInt32:
223       break;
224     default:
225       return false;
226   }
227   // Find range.
228   chase_hint_ = chase_hint;
229   int64_t stride_value = 0;
230   *min_val = SimplifyMin(GetVal(context, loop, info, trip, /*is_min=*/ true));
231   *max_val = SimplifyMax(GetVal(context, loop, info, trip, /*is_min=*/ false), chase_hint);
232   *needs_finite_test =
233       NeedsTripCount(context, loop, info, &stride_value) && IsUnsafeTripCount(trip);
234   chase_hint_ = nullptr;
235   // Retry chasing constants for wrap-around (merge sensitive).
236   if (!min_val->is_known && info->induction_class == HInductionVarAnalysis::kWrapAround) {
237     *min_val = SimplifyMin(GetVal(context, loop, info, trip, /*is_min=*/ true));
238   }
239   return true;
240 }
241 
CanGenerateRange(const HBasicBlock * context,HInstruction * instruction,bool * needs_finite_test,bool * needs_taken_test)242 bool InductionVarRange::CanGenerateRange(const HBasicBlock* context,
243                                          HInstruction* instruction,
244                                          /*out*/bool* needs_finite_test,
245                                          /*out*/bool* needs_taken_test) {
246   bool is_last_value = false;
247   int64_t stride_value = 0;
248   return GenerateRangeOrLastValue(context,
249                                   instruction,
250                                   is_last_value,
251                                   nullptr,
252                                   nullptr,
253                                   nullptr,
254                                   nullptr,
255                                   nullptr,  // nothing generated yet
256                                   &stride_value,
257                                   needs_finite_test,
258                                   needs_taken_test)
259       && (stride_value == -1 ||
260           stride_value == 0 ||
261           stride_value == 1);  // avoid arithmetic wrap-around anomalies.
262 }
263 
GenerateRange(const HBasicBlock * context,HInstruction * instruction,HGraph * graph,HBasicBlock * block,HInstruction ** lower,HInstruction ** upper)264 void InductionVarRange::GenerateRange(const HBasicBlock* context,
265                                       HInstruction* instruction,
266                                       HGraph* graph,
267                                       HBasicBlock* block,
268                                       /*out*/HInstruction** lower,
269                                       /*out*/HInstruction** upper) {
270   bool is_last_value = false;
271   int64_t stride_value = 0;
272   bool b1, b2;  // unused
273   if (!GenerateRangeOrLastValue(context,
274                                 instruction,
275                                 is_last_value,
276                                 graph,
277                                 block,
278                                 lower,
279                                 upper,
280                                 nullptr,
281                                 &stride_value,
282                                 &b1,
283                                 &b2)) {
284     LOG(FATAL) << "Failed precondition: CanGenerateRange()";
285   }
286 }
287 
GenerateTakenTest(HInstruction * loop_control,HGraph * graph,HBasicBlock * block)288 HInstruction* InductionVarRange::GenerateTakenTest(HInstruction* loop_control,
289                                                    HGraph* graph,
290                                                    HBasicBlock* block) {
291   const HBasicBlock* context = loop_control->GetBlock();
292   HInstruction* taken_test = nullptr;
293   bool is_last_value = false;
294   int64_t stride_value = 0;
295   bool b1, b2;  // unused
296   if (!GenerateRangeOrLastValue(context,
297                                 loop_control,
298                                 is_last_value,
299                                 graph,
300                                 block,
301                                 nullptr,
302                                 nullptr,
303                                 &taken_test,
304                                 &stride_value,
305                                 &b1,
306                                 &b2)) {
307     LOG(FATAL) << "Failed precondition: CanGenerateRange()";
308   }
309   return taken_test;
310 }
311 
CanGenerateLastValue(HInstruction * instruction)312 bool InductionVarRange::CanGenerateLastValue(HInstruction* instruction) {
313   const HBasicBlock* context = instruction->GetBlock();
314   bool is_last_value = true;
315   int64_t stride_value = 0;
316   bool needs_finite_test = false;
317   bool needs_taken_test = false;
318   return GenerateRangeOrLastValue(context,
319                                   instruction,
320                                   is_last_value,
321                                   nullptr,
322                                   nullptr,
323                                   nullptr,
324                                   nullptr,
325                                   nullptr,  // nothing generated yet
326                                   &stride_value,
327                                   &needs_finite_test,
328                                   &needs_taken_test)
329       && !needs_finite_test && !needs_taken_test;
330 }
331 
GenerateLastValue(HInstruction * instruction,HGraph * graph,HBasicBlock * block)332 HInstruction* InductionVarRange::GenerateLastValue(HInstruction* instruction,
333                                                    HGraph* graph,
334                                                    HBasicBlock* block) {
335   const HBasicBlock* context = instruction->GetBlock();
336   HInstruction* last_value = nullptr;
337   bool is_last_value = true;
338   int64_t stride_value = 0;
339   bool b1, b2;  // unused
340   if (!GenerateRangeOrLastValue(context,
341                                 instruction,
342                                 is_last_value,
343                                 graph,
344                                 block,
345                                 &last_value,
346                                 &last_value,
347                                 nullptr,
348                                 &stride_value,
349                                 &b1,
350                                 &b2)) {
351     LOG(FATAL) << "Failed precondition: CanGenerateLastValue()";
352   }
353   return last_value;
354 }
355 
Replace(HInstruction * instruction,HInstruction * fetch,HInstruction * replacement)356 void InductionVarRange::Replace(HInstruction* instruction,
357                                 HInstruction* fetch,
358                                 HInstruction* replacement) {
359   for (HLoopInformation* lp = instruction->GetBlock()->GetLoopInformation();  // closest enveloping loop
360        lp != nullptr;
361        lp = lp->GetPreHeader()->GetLoopInformation()) {
362     // Update instruction's information.
363     ReplaceInduction(induction_analysis_->LookupInfo(lp, instruction), fetch, replacement);
364     // Update loop's trip-count information.
365     ReplaceInduction(induction_analysis_->LookupInfo(lp, GetLoopControl(lp)), fetch, replacement);
366   }
367 }
368 
IsFinite(const HLoopInformation * loop,int64_t * trip_count) const369 bool InductionVarRange::IsFinite(const HLoopInformation* loop, /*out*/ int64_t* trip_count) const {
370   bool is_constant_unused = false;
371   return CheckForFiniteAndConstantProps(loop, &is_constant_unused, trip_count);
372 }
373 
HasKnownTripCount(const HLoopInformation * loop,int64_t * trip_count) const374 bool InductionVarRange::HasKnownTripCount(const HLoopInformation* loop,
375                                           /*out*/ int64_t* trip_count) const {
376   bool is_constant = false;
377   CheckForFiniteAndConstantProps(loop, &is_constant, trip_count);
378   return is_constant;
379 }
380 
IsUnitStride(const HBasicBlock * context,HInstruction * instruction,HGraph * graph,HInstruction ** offset) const381 bool InductionVarRange::IsUnitStride(const HBasicBlock* context,
382                                      HInstruction* instruction,
383                                      HGraph* graph,
384                                      /*out*/ HInstruction** offset) const {
385   const HLoopInformation* loop = nullptr;
386   HInductionVarAnalysis::InductionInfo* info = nullptr;
387   HInductionVarAnalysis::InductionInfo* trip = nullptr;
388   if (HasInductionInfo(context, instruction, &loop, &info, &trip)) {
389     if (info->induction_class == HInductionVarAnalysis::kLinear &&
390         !HInductionVarAnalysis::IsNarrowingLinear(info)) {
391       int64_t stride_value = 0;
392       if (IsConstant(context, loop, info->op_a, kExact, &stride_value) && stride_value == 1) {
393         int64_t off_value = 0;
394         if (IsConstant(context, loop, info->op_b, kExact, &off_value)) {
395           *offset = graph->GetConstant(info->op_b->type, off_value);
396         } else if (info->op_b->operation == HInductionVarAnalysis::kFetch) {
397           *offset = info->op_b->fetch;
398         } else {
399           return false;
400         }
401         return true;
402       }
403     }
404   }
405   return false;
406 }
407 
GenerateTripCount(const HLoopInformation * loop,HGraph * graph,HBasicBlock * block)408 HInstruction* InductionVarRange::GenerateTripCount(const HLoopInformation* loop,
409                                                    HGraph* graph,
410                                                    HBasicBlock* block) {
411   HInstruction* loop_control = GetLoopControl(loop);
412   HInductionVarAnalysis::InductionInfo *trip = induction_analysis_->LookupInfo(loop, loop_control);
413   if (trip != nullptr && !IsUnsafeTripCount(trip)) {
414     const HBasicBlock* context = loop_control->GetBlock();
415     HInstruction* taken_test = nullptr;
416     HInstruction* trip_expr = nullptr;
417     if (IsBodyTripCount(trip)) {
418       if (!GenerateCode(context,
419                         loop,
420                         trip->op_b,
421                         /*trip=*/ nullptr,
422                         graph,
423                         block,
424                         /*is_min=*/ false,
425                         &taken_test)) {
426         return nullptr;
427       }
428     }
429     if (GenerateCode(context,
430                      loop,
431                      trip->op_a,
432                      /*trip=*/ nullptr,
433                      graph,
434                      block,
435                      /*is_min=*/ false,
436                      &trip_expr)) {
437       if (taken_test != nullptr) {
438         HInstruction* zero = graph->GetConstant(trip->type, 0);
439         ArenaAllocator* allocator = graph->GetAllocator();
440         trip_expr = Insert(block, new (allocator) HSelect(taken_test, trip_expr, zero, kNoDexPc));
441       }
442       return trip_expr;
443     }
444   }
445   return nullptr;
446 }
447 
448 //
449 // Private class methods.
450 //
451 
CheckForFiniteAndConstantProps(const HLoopInformation * loop,bool * is_constant,int64_t * trip_count) const452 bool InductionVarRange::CheckForFiniteAndConstantProps(const HLoopInformation* loop,
453                                                        /*out*/ bool* is_constant,
454                                                        /*out*/ int64_t* trip_count) const {
455   HInstruction* loop_control = GetLoopControl(loop);
456   HInductionVarAnalysis::InductionInfo *trip = induction_analysis_->LookupInfo(loop, loop_control);
457   if (trip != nullptr && !IsUnsafeTripCount(trip)) {
458     const HBasicBlock* context = loop_control->GetBlock();
459     *is_constant = IsConstant(context, loop, trip->op_a, kExact, trip_count);
460     return true;
461   }
462   return false;
463 }
464 
IsConstant(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * info,ConstantRequest request,int64_t * value) const465 bool InductionVarRange::IsConstant(const HBasicBlock* context,
466                                    const HLoopInformation* loop,
467                                    HInductionVarAnalysis::InductionInfo* info,
468                                    ConstantRequest request,
469                                    /*out*/ int64_t* value) const {
470   if (info != nullptr) {
471     // A direct 32-bit or 64-bit constant fetch. This immediately satisfies
472     // any of the three requests (kExact, kAtMost, and KAtLeast).
473     if (info->induction_class == HInductionVarAnalysis::kInvariant &&
474         info->operation == HInductionVarAnalysis::kFetch) {
475       if (IsInt64AndGet(info->fetch, value)) {
476         return true;
477       }
478     }
479     // Try range analysis on the invariant, only accept a proper range
480     // to avoid arithmetic wrap-around anomalies.
481     Value min_val = GetVal(context, loop, info, /*trip=*/ nullptr, /*is_min=*/ true);
482     Value max_val = GetVal(context, loop, info, /*trip=*/ nullptr, /*is_min=*/ false);
483     if (IsConstantValue(min_val) &&
484         IsConstantValue(max_val) && min_val.b_constant <= max_val.b_constant) {
485       if ((request == kExact && min_val.b_constant == max_val.b_constant) || request == kAtMost) {
486         *value = max_val.b_constant;
487         return true;
488       } else if (request == kAtLeast) {
489         *value = min_val.b_constant;
490         return true;
491       }
492     }
493   }
494   return false;
495 }
496 
HasInductionInfo(const HBasicBlock * context,HInstruction * instruction,const HLoopInformation ** loop,HInductionVarAnalysis::InductionInfo ** info,HInductionVarAnalysis::InductionInfo ** trip) const497 bool InductionVarRange::HasInductionInfo(
498     const HBasicBlock* context,
499     HInstruction* instruction,
500     /*out*/ const HLoopInformation** loop,
501     /*out*/ HInductionVarAnalysis::InductionInfo** info,
502     /*out*/ HInductionVarAnalysis::InductionInfo** trip) const {
503   DCHECK(context != nullptr);
504   HLoopInformation* lp = context->GetLoopInformation();  // closest enveloping loop
505   if (lp != nullptr) {
506     HInductionVarAnalysis::InductionInfo* i = induction_analysis_->LookupInfo(lp, instruction);
507     if (i != nullptr) {
508       *loop = lp;
509       *info = i;
510       *trip = induction_analysis_->LookupInfo(lp, GetLoopControl(lp));
511       return true;
512     }
513   }
514   return false;
515 }
516 
IsWellBehavedTripCount(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * trip) const517 bool InductionVarRange::IsWellBehavedTripCount(const HBasicBlock* context,
518                                                const HLoopInformation* loop,
519                                                HInductionVarAnalysis::InductionInfo* trip) const {
520   if (trip != nullptr) {
521     // Both bounds that define a trip-count are well-behaved if they either are not defined
522     // in any loop, or are contained in a proper interval. This allows finding the min/max
523     // of an expression by chasing outward.
524     InductionVarRange range(induction_analysis_);
525     HInductionVarAnalysis::InductionInfo* lower = trip->op_b->op_a;
526     HInductionVarAnalysis::InductionInfo* upper = trip->op_b->op_b;
527     int64_t not_used = 0;
528     return
529         (!HasFetchInLoop(lower) || range.IsConstant(context, loop, lower, kAtLeast, &not_used)) &&
530         (!HasFetchInLoop(upper) || range.IsConstant(context, loop, upper, kAtLeast, &not_used));
531   }
532   return true;
533 }
534 
HasFetchInLoop(HInductionVarAnalysis::InductionInfo * info) const535 bool InductionVarRange::HasFetchInLoop(HInductionVarAnalysis::InductionInfo* info) const {
536   if (info != nullptr) {
537     if (info->induction_class == HInductionVarAnalysis::kInvariant &&
538         info->operation == HInductionVarAnalysis::kFetch) {
539       return info->fetch->GetBlock()->GetLoopInformation() != nullptr;
540     }
541     return HasFetchInLoop(info->op_a) || HasFetchInLoop(info->op_b);
542   }
543   return false;
544 }
545 
NeedsTripCount(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * info,int64_t * stride_value) const546 bool InductionVarRange::NeedsTripCount(const HBasicBlock* context,
547                                        const HLoopInformation* loop,
548                                        HInductionVarAnalysis::InductionInfo* info,
549                                        int64_t* stride_value) const {
550   if (info != nullptr) {
551     if (info->induction_class == HInductionVarAnalysis::kLinear) {
552       return IsConstant(context, loop, info->op_a, kExact, stride_value);
553     } else if (info->induction_class == HInductionVarAnalysis::kPolynomial) {
554       return NeedsTripCount(context, loop, info->op_a, stride_value);
555     } else if (info->induction_class == HInductionVarAnalysis::kWrapAround) {
556       return NeedsTripCount(context, loop, info->op_b, stride_value);
557     }
558   }
559   return false;
560 }
561 
IsBodyTripCount(HInductionVarAnalysis::InductionInfo * trip) const562 bool InductionVarRange::IsBodyTripCount(HInductionVarAnalysis::InductionInfo* trip) const {
563   if (trip != nullptr) {
564     if (trip->induction_class == HInductionVarAnalysis::kInvariant) {
565       return trip->operation == HInductionVarAnalysis::kTripCountInBody ||
566              trip->operation == HInductionVarAnalysis::kTripCountInBodyUnsafe;
567     }
568   }
569   return false;
570 }
571 
IsUnsafeTripCount(HInductionVarAnalysis::InductionInfo * trip) const572 bool InductionVarRange::IsUnsafeTripCount(HInductionVarAnalysis::InductionInfo* trip) const {
573   if (trip != nullptr) {
574     if (trip->induction_class == HInductionVarAnalysis::kInvariant) {
575       return trip->operation == HInductionVarAnalysis::kTripCountInBodyUnsafe ||
576              trip->operation == HInductionVarAnalysis::kTripCountInLoopUnsafe;
577     }
578   }
579   return false;
580 }
581 
GetLinear(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * info,HInductionVarAnalysis::InductionInfo * trip,bool is_min) const582 InductionVarRange::Value InductionVarRange::GetLinear(const HBasicBlock* context,
583                                                       const HLoopInformation* loop,
584                                                       HInductionVarAnalysis::InductionInfo* info,
585                                                       HInductionVarAnalysis::InductionInfo* trip,
586                                                       bool is_min) const {
587   DCHECK(info != nullptr);
588   DCHECK_EQ(info->induction_class, HInductionVarAnalysis::kLinear);
589   // Detect common situation where an offset inside the trip-count cancels out during range
590   // analysis (finding max a * (TC - 1) + OFFSET for a == 1 and TC = UPPER - OFFSET or finding
591   // min a * (TC - 1) + OFFSET for a == -1 and TC = OFFSET - UPPER) to avoid losing information
592   // with intermediate results that only incorporate single instructions.
593   if (trip != nullptr) {
594     HInductionVarAnalysis::InductionInfo* trip_expr = trip->op_a;
595     if (trip_expr->type == info->type && trip_expr->operation == HInductionVarAnalysis::kSub) {
596       int64_t stride_value = 0;
597       if (IsConstant(context, loop, info->op_a, kExact, &stride_value)) {
598         if (!is_min && stride_value == 1) {
599           // Test original trip's negative operand (trip_expr->op_b) against offset of induction.
600           if (HInductionVarAnalysis::InductionEqual(trip_expr->op_b, info->op_b)) {
601             // Analyze cancelled trip with just the positive operand (trip_expr->op_a).
602             HInductionVarAnalysis::InductionInfo cancelled_trip(
603                 trip->induction_class,
604                 trip->operation,
605                 trip_expr->op_a,
606                 trip->op_b,
607                 nullptr,
608                 trip->type);
609             return GetVal(context, loop, &cancelled_trip, trip, is_min);
610           }
611         } else if (is_min && stride_value == -1) {
612           // Test original trip's positive operand (trip_expr->op_a) against offset of induction.
613           if (HInductionVarAnalysis::InductionEqual(trip_expr->op_a, info->op_b)) {
614             // Analyze cancelled trip with just the negative operand (trip_expr->op_b).
615             HInductionVarAnalysis::InductionInfo neg(
616                 HInductionVarAnalysis::kInvariant,
617                 HInductionVarAnalysis::kNeg,
618                 nullptr,
619                 trip_expr->op_b,
620                 nullptr,
621                 trip->type);
622             HInductionVarAnalysis::InductionInfo cancelled_trip(
623                 trip->induction_class, trip->operation, &neg, trip->op_b, nullptr, trip->type);
624             return SubValue(Value(0), GetVal(context, loop, &cancelled_trip, trip, !is_min));
625           }
626         }
627       }
628     }
629   }
630   // General rule of linear induction a * i + b, for normalized 0 <= i < TC.
631   return AddValue(GetMul(context, loop, info->op_a, trip, trip, is_min),
632                   GetVal(context, loop, info->op_b, trip, is_min));
633 }
634 
GetPolynomial(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * info,HInductionVarAnalysis::InductionInfo * trip,bool is_min) const635 InductionVarRange::Value InductionVarRange::GetPolynomial(
636     const HBasicBlock* context,
637     const HLoopInformation* loop,
638     HInductionVarAnalysis::InductionInfo* info,
639     HInductionVarAnalysis::InductionInfo* trip,
640     bool is_min) const {
641   DCHECK(info != nullptr);
642   DCHECK_EQ(info->induction_class, HInductionVarAnalysis::kPolynomial);
643   int64_t a = 0;
644   int64_t b = 0;
645   if (IsConstant(context, loop, info->op_a->op_a, kExact, &a) &&
646       CanLongValueFitIntoInt(a) &&
647       a >= 0 &&
648       IsConstant(context, loop, info->op_a->op_b, kExact, &b) &&
649       CanLongValueFitIntoInt(b) &&
650       b >= 0) {
651     // Evaluate bounds on sum_i=0^m-1(a * i + b) + c with a,b >= 0 for
652     // maximum index value m as a * (m * (m-1)) / 2 + b * m + c.
653     // Do not simply return `c` as minimum because the trip count may be non-zero
654     // if the `context` is after the `loop` (and therefore ignoring `is_min`).
655     Value c = GetVal(context, loop, info->op_b, trip, is_min);
656     Value m = GetVal(context, loop, trip, trip, is_min);
657     Value t = DivValue(MulValue(m, SubValue(m, Value(1))), Value(2));
658     Value x = MulValue(Value(a), t);
659     Value y = MulValue(Value(b), m);
660     return AddValue(AddValue(x, y), c);
661   }
662   return Value();
663 }
664 
GetGeometric(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * info,HInductionVarAnalysis::InductionInfo * trip,bool is_min) const665 InductionVarRange::Value InductionVarRange::GetGeometric(const HBasicBlock* context,
666                                                          const HLoopInformation* loop,
667                                                          HInductionVarAnalysis::InductionInfo* info,
668                                                          HInductionVarAnalysis::InductionInfo* trip,
669                                                          bool is_min) const {
670   DCHECK(info != nullptr);
671   DCHECK_EQ(info->induction_class, HInductionVarAnalysis::kGeometric);
672   int64_t a = 0;
673   int64_t f = 0;
674   if (IsConstant(context, loop, info->op_a, kExact, &a) &&
675       CanLongValueFitIntoInt(a) &&
676       IsInt64AndGet(info->fetch, &f) && f >= 1) {
677     // Conservative bounds on a * f^-i + b with f >= 1 can be computed without
678     // trip count. Other forms would require a much more elaborate evaluation.
679     const bool is_min_a = a >= 0 ? is_min : !is_min;
680     if (info->operation == HInductionVarAnalysis::kDiv) {
681       Value b = GetVal(context, loop, info->op_b, trip, is_min);
682       return is_min_a ? b : AddValue(Value(a), b);
683     }
684   }
685   return Value();
686 }
687 
GetFetch(const HBasicBlock * context,const HLoopInformation * loop,HInstruction * instruction,HInductionVarAnalysis::InductionInfo * trip,bool is_min) const688 InductionVarRange::Value InductionVarRange::GetFetch(const HBasicBlock* context,
689                                                      const HLoopInformation* loop,
690                                                      HInstruction* instruction,
691                                                      HInductionVarAnalysis::InductionInfo* trip,
692                                                      bool is_min) const {
693   // Special case when chasing constants: single instruction that denotes trip count in the
694   // loop-body is minimal 1 and maximal, with safe trip-count, max int,
695   if (chase_hint_ == nullptr &&
696       IsContextInBody(context, loop) &&
697       trip != nullptr &&
698       instruction == trip->op_a->fetch) {
699     if (is_min) {
700       return Value(1);
701     } else if (!instruction->IsConstant() && !IsUnsafeTripCount(trip)) {
702       return Value(std::numeric_limits<int32_t>::max());
703     }
704   }
705   // Unless at a constant or hint, chase the instruction a bit deeper into the HIR tree, so that
706   // it becomes more likely range analysis will compare the same instructions as terminal nodes.
707   int64_t value;
708   if (IsInt64AndGet(instruction, &value) && CanLongValueFitIntoInt(value)) {
709     // Proper constant reveals best information.
710     return Value(static_cast<int32_t>(value));
711   } else if (instruction == chase_hint_) {
712     // At hint, fetch is represented by itself.
713     return Value(instruction, 1, 0);
714   } else if (instruction->IsAdd()) {
715     // Incorporate suitable constants in the chased value.
716     if (IsInt64AndGet(instruction->InputAt(0), &value) && CanLongValueFitIntoInt(value)) {
717       return AddValue(Value(static_cast<int32_t>(value)),
718                       GetFetch(context, loop, instruction->InputAt(1), trip, is_min));
719     } else if (IsInt64AndGet(instruction->InputAt(1), &value) && CanLongValueFitIntoInt(value)) {
720       return AddValue(GetFetch(context, loop, instruction->InputAt(0), trip, is_min),
721                       Value(static_cast<int32_t>(value)));
722     }
723   } else if (instruction->IsSub()) {
724     // Incorporate suitable constants in the chased value.
725     if (IsInt64AndGet(instruction->InputAt(0), &value) && CanLongValueFitIntoInt(value)) {
726       return SubValue(Value(static_cast<int32_t>(value)),
727                       GetFetch(context, loop, instruction->InputAt(1), trip, !is_min));
728     } else if (IsInt64AndGet(instruction->InputAt(1), &value) && CanLongValueFitIntoInt(value)) {
729       return SubValue(GetFetch(context, loop, instruction->InputAt(0), trip, is_min),
730                       Value(static_cast<int32_t>(value)));
731     }
732   } else if (instruction->IsArrayLength()) {
733     // Exploit length properties when chasing constants or chase into a new array declaration.
734     if (chase_hint_ == nullptr) {
735       return is_min ? Value(0) : Value(std::numeric_limits<int32_t>::max());
736     } else if (instruction->InputAt(0)->IsNewArray()) {
737       return GetFetch(
738           context, loop, instruction->InputAt(0)->AsNewArray()->GetLength(), trip, is_min);
739     }
740   } else if (instruction->IsTypeConversion()) {
741     // Since analysis is 32-bit (or narrower), chase beyond widening along the path.
742     // For example, this discovers the length in: for (long i = 0; i < a.length; i++);
743     if (instruction->AsTypeConversion()->GetInputType() == DataType::Type::kInt32 &&
744         instruction->AsTypeConversion()->GetResultType() == DataType::Type::kInt64) {
745       return GetFetch(context, loop, instruction->InputAt(0), trip, is_min);
746     }
747   }
748   // Chase an invariant fetch that is defined by another loop if the trip-count used
749   // so far is well-behaved in both bounds and the next trip-count is safe.
750   // Example:
751   //   for (int i = 0; i <= 100; i++)  // safe
752   //     for (int j = 0; j <= i; j++)  // well-behaved
753   //       j is in range [0, i  ] (if i is chase hint)
754   //         or in range [0, 100] (otherwise)
755   // Example:
756   //   for (i = 0; i < 100; ++i)
757   //     <some-code>
758   //   for (j = 0; j < 10; ++j)
759   //     sum += i;  // The `i` is a "fetch" of a loop Phi from the previous loop.
760   const HLoopInformation* next_loop = nullptr;
761   HInductionVarAnalysis::InductionInfo* next_info = nullptr;
762   HInductionVarAnalysis::InductionInfo* next_trip = nullptr;
763   if (HasInductionInfo(instruction->GetBlock(), instruction, &next_loop, &next_info, &next_trip) &&
764       IsWellBehavedTripCount(context, next_loop, trip) &&
765       !IsUnsafeTripCount(next_trip)) {
766     return GetVal(context, next_loop, next_info, next_trip, is_min);
767   }
768   // Fetch is represented by itself.
769   return Value(instruction, 1, 0);
770 }
771 
GetVal(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * info,HInductionVarAnalysis::InductionInfo * trip,bool is_min) const772 InductionVarRange::Value InductionVarRange::GetVal(const HBasicBlock* context,
773                                                    const HLoopInformation* loop,
774                                                    HInductionVarAnalysis::InductionInfo* info,
775                                                    HInductionVarAnalysis::InductionInfo* trip,
776                                                    bool is_min) const {
777   if (info != nullptr) {
778     switch (info->induction_class) {
779       case HInductionVarAnalysis::kInvariant:
780         // Invariants.
781         switch (info->operation) {
782           case HInductionVarAnalysis::kAdd:
783             return AddValue(GetVal(context, loop, info->op_a, trip, is_min),
784                             GetVal(context, loop, info->op_b, trip, is_min));
785           case HInductionVarAnalysis::kSub:  // second reversed!
786             return SubValue(GetVal(context, loop, info->op_a, trip, is_min),
787                             GetVal(context, loop, info->op_b, trip, !is_min));
788           case HInductionVarAnalysis::kNeg:  // second reversed!
789             return SubValue(Value(0),
790                             GetVal(context, loop, info->op_b, trip, !is_min));
791           case HInductionVarAnalysis::kMul:
792             return GetMul(context, loop, info->op_a, info->op_b, trip, is_min);
793           case HInductionVarAnalysis::kDiv:
794             return GetDiv(context, loop, info->op_a, info->op_b, trip, is_min);
795           case HInductionVarAnalysis::kRem:
796             return GetRem(context, loop, info->op_a, info->op_b);
797           case HInductionVarAnalysis::kXor:
798             return GetXor(context, loop, info->op_a, info->op_b);
799           case HInductionVarAnalysis::kFetch:
800             return GetFetch(context, loop, info->fetch, trip, is_min);
801           case HInductionVarAnalysis::kTripCountInLoop:
802           case HInductionVarAnalysis::kTripCountInLoopUnsafe:
803             if (UseFullTripCount(context, loop, is_min)) {
804               // Return the full trip count (do not subtract 1 as we do in loop body).
805               return GetVal(context, loop, info->op_a, trip, /*is_min=*/ false);
806             }
807             FALLTHROUGH_INTENDED;
808           case HInductionVarAnalysis::kTripCountInBody:
809           case HInductionVarAnalysis::kTripCountInBodyUnsafe:
810             if (is_min) {
811               return Value(0);
812             } else if (IsContextInBody(context, loop)) {
813               return SubValue(GetVal(context, loop, info->op_a, trip, is_min), Value(1));
814             }
815             break;
816           default:
817             break;
818         }
819         break;
820       case HInductionVarAnalysis::kLinear:
821         return CorrectForType(GetLinear(context, loop, info, trip, is_min), info->type);
822       case HInductionVarAnalysis::kPolynomial:
823         return GetPolynomial(context, loop, info, trip, is_min);
824       case HInductionVarAnalysis::kGeometric:
825         return GetGeometric(context, loop, info, trip, is_min);
826       case HInductionVarAnalysis::kWrapAround:
827       case HInductionVarAnalysis::kPeriodic:
828         return MergeVal(GetVal(context, loop, info->op_a, trip, is_min),
829                         GetVal(context, loop, info->op_b, trip, is_min),
830                         is_min);
831     }
832   }
833   return Value();
834 }
835 
GetMul(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * info1,HInductionVarAnalysis::InductionInfo * info2,HInductionVarAnalysis::InductionInfo * trip,bool is_min) const836 InductionVarRange::Value InductionVarRange::GetMul(const HBasicBlock* context,
837                                                    const HLoopInformation* loop,
838                                                    HInductionVarAnalysis::InductionInfo* info1,
839                                                    HInductionVarAnalysis::InductionInfo* info2,
840                                                    HInductionVarAnalysis::InductionInfo* trip,
841                                                    bool is_min) const {
842   // Constant times range.
843   int64_t value = 0;
844   if (IsConstant(context, loop, info1, kExact, &value)) {
845     return MulRangeAndConstant(context, loop, value, info2, trip, is_min);
846   } else if (IsConstant(context, loop, info2, kExact, &value)) {
847     return MulRangeAndConstant(context, loop, value, info1, trip, is_min);
848   }
849   // Interval ranges.
850   Value v1_min = GetVal(context, loop, info1, trip, /*is_min=*/ true);
851   Value v1_max = GetVal(context, loop, info1, trip, /*is_min=*/ false);
852   Value v2_min = GetVal(context, loop, info2, trip, /*is_min=*/ true);
853   Value v2_max = GetVal(context, loop, info2, trip, /*is_min=*/ false);
854   // Positive range vs. positive or negative range.
855   if (IsConstantValue(v1_min) && v1_min.b_constant >= 0) {
856     if (IsConstantValue(v2_min) && v2_min.b_constant >= 0) {
857       return is_min ? MulValue(v1_min, v2_min) : MulValue(v1_max, v2_max);
858     } else if (IsConstantValue(v2_max) && v2_max.b_constant <= 0) {
859       return is_min ? MulValue(v1_max, v2_min) : MulValue(v1_min, v2_max);
860     }
861   }
862   // Negative range vs. positive or negative range.
863   if (IsConstantValue(v1_max) && v1_max.b_constant <= 0) {
864     if (IsConstantValue(v2_min) && v2_min.b_constant >= 0) {
865       return is_min ? MulValue(v1_min, v2_max) : MulValue(v1_max, v2_min);
866     } else if (IsConstantValue(v2_max) && v2_max.b_constant <= 0) {
867       return is_min ? MulValue(v1_max, v2_max) : MulValue(v1_min, v2_min);
868     }
869   }
870   return Value();
871 }
872 
GetDiv(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * info1,HInductionVarAnalysis::InductionInfo * info2,HInductionVarAnalysis::InductionInfo * trip,bool is_min) const873 InductionVarRange::Value InductionVarRange::GetDiv(const HBasicBlock* context,
874                                                    const HLoopInformation* loop,
875                                                    HInductionVarAnalysis::InductionInfo* info1,
876                                                    HInductionVarAnalysis::InductionInfo* info2,
877                                                    HInductionVarAnalysis::InductionInfo* trip,
878                                                    bool is_min) const {
879   // Range divided by constant.
880   int64_t value = 0;
881   if (IsConstant(context, loop, info2, kExact, &value)) {
882     return DivRangeAndConstant(context, loop, value, info1, trip, is_min);
883   }
884   // Interval ranges.
885   Value v1_min = GetVal(context, loop, info1, trip, /*is_min=*/ true);
886   Value v1_max = GetVal(context, loop, info1, trip, /*is_min=*/ false);
887   Value v2_min = GetVal(context, loop, info2, trip, /*is_min=*/ true);
888   Value v2_max = GetVal(context, loop, info2, trip, /*is_min=*/ false);
889   // Positive range vs. positive or negative range.
890   if (IsConstantValue(v1_min) && v1_min.b_constant >= 0) {
891     if (IsConstantValue(v2_min) && v2_min.b_constant >= 0) {
892       return is_min ? DivValue(v1_min, v2_max) : DivValue(v1_max, v2_min);
893     } else if (IsConstantValue(v2_max) && v2_max.b_constant <= 0) {
894       return is_min ? DivValue(v1_max, v2_max) : DivValue(v1_min, v2_min);
895     }
896   }
897   // Negative range vs. positive or negative range.
898   if (IsConstantValue(v1_max) && v1_max.b_constant <= 0) {
899     if (IsConstantValue(v2_min) && v2_min.b_constant >= 0) {
900       return is_min ? DivValue(v1_min, v2_min) : DivValue(v1_max, v2_max);
901     } else if (IsConstantValue(v2_max) && v2_max.b_constant <= 0) {
902       return is_min ? DivValue(v1_max, v2_min) : DivValue(v1_min, v2_max);
903     }
904   }
905   return Value();
906 }
907 
GetRem(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * info1,HInductionVarAnalysis::InductionInfo * info2) const908 InductionVarRange::Value InductionVarRange::GetRem(
909     const HBasicBlock* context,
910     const HLoopInformation* loop,
911     HInductionVarAnalysis::InductionInfo* info1,
912     HInductionVarAnalysis::InductionInfo* info2) const {
913   int64_t v1 = 0;
914   int64_t v2 = 0;
915   // Only accept exact values.
916   if (IsConstant(context, loop, info1, kExact, &v1) &&
917       IsConstant(context, loop, info2, kExact, &v2) &&
918       v2 != 0) {
919     int64_t value = v1 % v2;
920     if (CanLongValueFitIntoInt(value)) {
921       return Value(static_cast<int32_t>(value));
922     }
923   }
924   return Value();
925 }
926 
GetXor(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * info1,HInductionVarAnalysis::InductionInfo * info2) const927 InductionVarRange::Value InductionVarRange::GetXor(
928     const HBasicBlock* context,
929     const HLoopInformation* loop,
930     HInductionVarAnalysis::InductionInfo* info1,
931     HInductionVarAnalysis::InductionInfo* info2) const {
932   int64_t v1 = 0;
933   int64_t v2 = 0;
934   // Only accept exact values.
935   if (IsConstant(context, loop, info1, kExact, &v1) &&
936       IsConstant(context, loop, info2, kExact, &v2)) {
937     int64_t value = v1 ^ v2;
938     if (CanLongValueFitIntoInt(value)) {
939       return Value(static_cast<int32_t>(value));
940     }
941   }
942   return Value();
943 }
944 
MulRangeAndConstant(const HBasicBlock * context,const HLoopInformation * loop,int64_t value,HInductionVarAnalysis::InductionInfo * info,HInductionVarAnalysis::InductionInfo * trip,bool is_min) const945 InductionVarRange::Value InductionVarRange::MulRangeAndConstant(
946     const HBasicBlock* context,
947     const HLoopInformation* loop,
948     int64_t value,
949     HInductionVarAnalysis::InductionInfo* info,
950     HInductionVarAnalysis::InductionInfo* trip,
951     bool is_min) const {
952   if (CanLongValueFitIntoInt(value)) {
953     Value c(static_cast<int32_t>(value));
954     return MulValue(GetVal(context, loop, info, trip, is_min == value >= 0), c);
955   }
956   return Value();
957 }
958 
DivRangeAndConstant(const HBasicBlock * context,const HLoopInformation * loop,int64_t value,HInductionVarAnalysis::InductionInfo * info,HInductionVarAnalysis::InductionInfo * trip,bool is_min) const959 InductionVarRange::Value InductionVarRange::DivRangeAndConstant(
960     const HBasicBlock* context,
961     const HLoopInformation* loop,
962     int64_t value,
963     HInductionVarAnalysis::InductionInfo* info,
964     HInductionVarAnalysis::InductionInfo* trip,
965     bool is_min) const {
966   if (CanLongValueFitIntoInt(value)) {
967     Value c(static_cast<int32_t>(value));
968     return DivValue(GetVal(context, loop, info, trip, is_min == value >= 0), c);
969   }
970   return Value();
971 }
972 
AddValue(Value v1,Value v2) const973 InductionVarRange::Value InductionVarRange::AddValue(Value v1, Value v2) const {
974   if (v1.is_known && v2.is_known && IsSafeAdd(v1.b_constant, v2.b_constant)) {
975     int32_t b = v1.b_constant + v2.b_constant;
976     if (v1.a_constant == 0) {
977       return Value(v2.instruction, v2.a_constant, b);
978     } else if (v2.a_constant == 0) {
979       return Value(v1.instruction, v1.a_constant, b);
980     } else if (v1.instruction == v2.instruction && IsSafeAdd(v1.a_constant, v2.a_constant)) {
981       return Value(v1.instruction, v1.a_constant + v2.a_constant, b);
982     }
983   }
984   return Value();
985 }
986 
SubValue(Value v1,Value v2) const987 InductionVarRange::Value InductionVarRange::SubValue(Value v1, Value v2) const {
988   if (v1.is_known && v2.is_known && IsSafeSub(v1.b_constant, v2.b_constant)) {
989     int32_t b = v1.b_constant - v2.b_constant;
990     if (v1.a_constant == 0 && IsSafeSub(0, v2.a_constant)) {
991       return Value(v2.instruction, -v2.a_constant, b);
992     } else if (v2.a_constant == 0) {
993       return Value(v1.instruction, v1.a_constant, b);
994     } else if (v1.instruction == v2.instruction && IsSafeSub(v1.a_constant, v2.a_constant)) {
995       return Value(v1.instruction, v1.a_constant - v2.a_constant, b);
996     }
997   }
998   return Value();
999 }
1000 
MulValue(Value v1,Value v2) const1001 InductionVarRange::Value InductionVarRange::MulValue(Value v1, Value v2) const {
1002   if (v1.is_known && v2.is_known) {
1003     if (v1.a_constant == 0) {
1004       if (IsSafeMul(v1.b_constant, v2.a_constant) && IsSafeMul(v1.b_constant, v2.b_constant)) {
1005         return Value(v2.instruction, v1.b_constant * v2.a_constant, v1.b_constant * v2.b_constant);
1006       }
1007     } else if (v2.a_constant == 0) {
1008       if (IsSafeMul(v1.a_constant, v2.b_constant) && IsSafeMul(v1.b_constant, v2.b_constant)) {
1009         return Value(v1.instruction, v1.a_constant * v2.b_constant, v1.b_constant * v2.b_constant);
1010       }
1011     }
1012   }
1013   return Value();
1014 }
1015 
DivValue(Value v1,Value v2) const1016 InductionVarRange::Value InductionVarRange::DivValue(Value v1, Value v2) const {
1017   if (v1.is_known && v2.is_known && v1.a_constant == 0 && v2.a_constant == 0) {
1018     if (IsSafeDiv(v1.b_constant, v2.b_constant)) {
1019       return Value(v1.b_constant / v2.b_constant);
1020     }
1021   }
1022   return Value();
1023 }
1024 
MergeVal(Value v1,Value v2,bool is_min) const1025 InductionVarRange::Value InductionVarRange::MergeVal(Value v1, Value v2, bool is_min) const {
1026   if (v1.is_known && v2.is_known) {
1027     if (v1.instruction == v2.instruction && v1.a_constant == v2.a_constant) {
1028       return Value(v1.instruction, v1.a_constant,
1029                    is_min ? std::min(v1.b_constant, v2.b_constant)
1030                           : std::max(v1.b_constant, v2.b_constant));
1031     }
1032   }
1033   return Value();
1034 }
1035 
GenerateRangeOrLastValue(const HBasicBlock * context,HInstruction * instruction,bool is_last_value,HGraph * graph,HBasicBlock * block,HInstruction ** lower,HInstruction ** upper,HInstruction ** taken_test,int64_t * stride_value,bool * needs_finite_test,bool * needs_taken_test) const1036 bool InductionVarRange::GenerateRangeOrLastValue(const HBasicBlock* context,
1037                                                  HInstruction* instruction,
1038                                                  bool is_last_value,
1039                                                  HGraph* graph,
1040                                                  HBasicBlock* block,
1041                                                  /*out*/HInstruction** lower,
1042                                                  /*out*/HInstruction** upper,
1043                                                  /*out*/HInstruction** taken_test,
1044                                                  /*out*/int64_t* stride_value,
1045                                                  /*out*/bool* needs_finite_test,
1046                                                  /*out*/bool* needs_taken_test) const {
1047   const HLoopInformation* loop = nullptr;
1048   HInductionVarAnalysis::InductionInfo* info = nullptr;
1049   HInductionVarAnalysis::InductionInfo* trip = nullptr;
1050   if (!HasInductionInfo(context, instruction, &loop, &info, &trip) || trip == nullptr) {
1051     return false;  // codegen needs all information, including tripcount
1052   }
1053   // Determine what tests are needed. A finite test is needed if the evaluation code uses the
1054   // trip-count and the loop maybe unsafe (because in such cases, the index could "overshoot"
1055   // the computed range). A taken test is needed for any unknown trip-count, even if evaluation
1056   // code does not use the trip-count explicitly (since there could be an implicit relation
1057   // between e.g. an invariant subscript and a not-taken condition).
1058   *stride_value = 0;
1059   *needs_finite_test = NeedsTripCount(context, loop, info, stride_value) && IsUnsafeTripCount(trip);
1060   *needs_taken_test = IsBodyTripCount(trip);
1061   // Handle last value request.
1062   if (is_last_value) {
1063     DCHECK(!IsContextInBody(context, loop));
1064     switch (info->induction_class) {
1065       case HInductionVarAnalysis::kLinear:
1066         if (*stride_value > 0) {
1067           lower = nullptr;
1068           return GenerateLastValueLinear(
1069               context, loop, info, trip, graph, block, /*is_min=*/false, upper);
1070         } else {
1071           upper = nullptr;
1072           return GenerateLastValueLinear(
1073               context, loop, info, trip, graph, block, /*is_min=*/true, lower);
1074         }
1075       case HInductionVarAnalysis::kPolynomial:
1076         return GenerateLastValuePolynomial(context, loop, info, trip, graph, block, lower);
1077       case HInductionVarAnalysis::kGeometric:
1078         return GenerateLastValueGeometric(context, loop, info, trip, graph, block, lower);
1079       case HInductionVarAnalysis::kWrapAround:
1080         return GenerateLastValueWrapAround(context, loop, info, trip, graph, block, lower);
1081       case HInductionVarAnalysis::kPeriodic:
1082         return GenerateLastValuePeriodic(
1083             context, loop, info, trip, graph, block, lower, needs_taken_test);
1084       default:
1085         return false;
1086     }
1087   }
1088   // Code generation for taken test: generate the code when requested or otherwise analyze
1089   // if code generation is feasible when taken test is needed.
1090   if (taken_test != nullptr) {
1091     return GenerateCode(context,
1092                         loop,
1093                         trip->op_b,
1094                         /*trip=*/ nullptr,
1095                         graph,
1096                         block,
1097                         /*is_min=*/ false,
1098                         taken_test);
1099   } else if (*needs_taken_test) {
1100     if (!GenerateCode(context,
1101                       loop,
1102                       trip->op_b,
1103                       /*trip=*/ nullptr,
1104                       /*graph=*/ nullptr,
1105                       /*block=*/ nullptr,
1106                       /*is_min=*/ false,
1107                       /*result=*/ nullptr)) {
1108       return false;
1109     }
1110   }
1111   // Code generation for lower and upper.
1112   return
1113       // Success on lower if invariant (not set), or code can be generated.
1114       ((info->induction_class == HInductionVarAnalysis::kInvariant) ||
1115           GenerateCode(context, loop, info, trip, graph, block, /*is_min=*/ true, lower)) &&
1116       // And success on upper.
1117       GenerateCode(context, loop, info, trip, graph, block, /*is_min=*/ false, upper);
1118 }
1119 
GenerateLastValueLinear(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * info,HInductionVarAnalysis::InductionInfo * trip,HGraph * graph,HBasicBlock * block,bool is_min,HInstruction ** result) const1120 bool InductionVarRange::GenerateLastValueLinear(const HBasicBlock* context,
1121                                                 const HLoopInformation* loop,
1122                                                 HInductionVarAnalysis::InductionInfo* info,
1123                                                 HInductionVarAnalysis::InductionInfo* trip,
1124                                                 HGraph* graph,
1125                                                 HBasicBlock* block,
1126                                                 bool is_min,
1127                                                 /*out*/ HInstruction** result) const {
1128   DataType::Type type = info->type;
1129   // Avoid any narrowing linear induction or any type mismatch between the linear induction and the
1130   // trip count expression.
1131   if (HInductionVarAnalysis::IsNarrowingLinear(info) || trip->type != type) {
1132     return false;
1133   }
1134 
1135   // Stride value must be a known constant that fits into int32.
1136   int64_t stride_value = 0;
1137   if (!IsConstant(context, loop, info->op_a, kExact, &stride_value) ||
1138       !CanLongValueFitIntoInt(stride_value)) {
1139     return false;
1140   }
1141 
1142   // We require `a` to be a constant value that didn't overflow.
1143   const bool is_min_a = stride_value >= 0 ? is_min : !is_min;
1144   Value val_a = GetVal(context, loop, trip, trip, is_min_a);
1145   HInstruction* opb;
1146   if (!IsConstantValue(val_a) ||
1147       !GenerateCode(context, loop, info->op_b, trip, graph, block, is_min, &opb)) {
1148     return false;
1149   }
1150 
1151   if (graph != nullptr) {
1152     ArenaAllocator* allocator = graph->GetAllocator();
1153     HInstruction* oper;
1154     HInstruction* opa = graph->GetConstant(type, val_a.b_constant);
1155     if (stride_value == 1) {
1156       oper = new (allocator) HAdd(type, opa, opb);
1157     } else if (stride_value == -1) {
1158       oper = new (graph->GetAllocator()) HSub(type, opb, opa);
1159     } else {
1160       HInstruction* mul = new (allocator) HMul(type, graph->GetConstant(type, stride_value), opa);
1161       oper = new (allocator) HAdd(type, Insert(block, mul), opb);
1162     }
1163     *result = Insert(block, oper);
1164   }
1165   return true;
1166 }
1167 
GenerateLastValuePolynomial(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * info,HInductionVarAnalysis::InductionInfo * trip,HGraph * graph,HBasicBlock * block,HInstruction ** result) const1168 bool InductionVarRange::GenerateLastValuePolynomial(const HBasicBlock* context,
1169                                                     const HLoopInformation* loop,
1170                                                     HInductionVarAnalysis::InductionInfo* info,
1171                                                     HInductionVarAnalysis::InductionInfo* trip,
1172                                                     HGraph* graph,
1173                                                     HBasicBlock* block,
1174                                                     /*out*/HInstruction** result) const {
1175   DCHECK(info != nullptr);
1176   DCHECK_EQ(info->induction_class, HInductionVarAnalysis::kPolynomial);
1177   // Detect known coefficients and trip count (always taken).
1178   int64_t a = 0;
1179   int64_t b = 0;
1180   int64_t m = 0;
1181   if (IsConstant(context, loop, info->op_a->op_a, kExact, &a) &&
1182       IsConstant(context, loop, info->op_a->op_b, kExact, &b) &&
1183       IsConstant(context, loop, trip->op_a, kExact, &m) &&
1184       m >= 1) {
1185     // Evaluate bounds on sum_i=0^m-1(a * i + b) + c for known
1186     // maximum index value m as a * (m * (m-1)) / 2 + b * m + c.
1187     HInstruction* c = nullptr;
1188     if (GenerateCode(context,
1189                      loop,
1190                      info->op_b,
1191                      /*trip=*/ nullptr,
1192                      graph,
1193                      block,
1194                      /*is_min=*/ false,
1195                      graph ? &c : nullptr)) {
1196       if (graph != nullptr) {
1197         DataType::Type type = info->type;
1198         int64_t sum = a * ((m * (m - 1)) / 2) + b * m;
1199         if (type != DataType::Type::kInt64) {
1200           sum = static_cast<int32_t>(sum);  // okay to truncate
1201         }
1202         *result =
1203             Insert(block, new (graph->GetAllocator()) HAdd(type, graph->GetConstant(type, sum), c));
1204       }
1205       return true;
1206     }
1207   }
1208   return false;
1209 }
1210 
GenerateLastValueGeometric(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * info,HInductionVarAnalysis::InductionInfo * trip,HGraph * graph,HBasicBlock * block,HInstruction ** result) const1211 bool InductionVarRange::GenerateLastValueGeometric(const HBasicBlock* context,
1212                                                    const HLoopInformation* loop,
1213                                                    HInductionVarAnalysis::InductionInfo* info,
1214                                                    HInductionVarAnalysis::InductionInfo* trip,
1215                                                    HGraph* graph,
1216                                                    HBasicBlock* block,
1217                                                    /*out*/HInstruction** result) const {
1218   DCHECK(info != nullptr);
1219   DCHECK_EQ(info->induction_class, HInductionVarAnalysis::kGeometric);
1220   // Detect known base and trip count (always taken).
1221   int64_t f = 0;
1222   int64_t m = 0;
1223   if (IsInt64AndGet(info->fetch, &f) &&
1224       f >= 1 &&
1225       IsConstant(context, loop, trip->op_a, kExact, &m) &&
1226       m >= 1) {
1227     HInstruction* opa = nullptr;
1228     HInstruction* opb = nullptr;
1229     if (GenerateCode(
1230             context, loop, info->op_a, /*trip=*/ nullptr, graph, block, /*is_min=*/ false, &opa) &&
1231         GenerateCode(
1232             context, loop, info->op_b, /*trip=*/ nullptr, graph, block, /*is_min=*/ false, &opb)) {
1233       if (graph != nullptr) {
1234         DataType::Type type = info->type;
1235         // Compute f ^ m for known maximum index value m.
1236         bool overflow = false;
1237         int64_t fpow = IntPow(f, m, &overflow);
1238         if (info->operation == HInductionVarAnalysis::kDiv) {
1239           // For division, any overflow truncates to zero.
1240           if (overflow || (type != DataType::Type::kInt64 && !CanLongValueFitIntoInt(fpow))) {
1241             fpow = 0;
1242           }
1243         } else if (type != DataType::Type::kInt64) {
1244           // For multiplication, okay to truncate to required precision.
1245           DCHECK(info->operation == HInductionVarAnalysis::kMul);
1246           fpow = static_cast<int32_t>(fpow);
1247         }
1248         // Generate code.
1249         if (fpow == 0) {
1250           // Special case: repeated mul/div always yields zero.
1251           *result = graph->GetConstant(type, 0);
1252         } else {
1253           // Last value: a * f ^ m + b or a * f ^ -m + b.
1254           HInstruction* e = nullptr;
1255           ArenaAllocator* allocator = graph->GetAllocator();
1256           if (info->operation == HInductionVarAnalysis::kMul) {
1257             e = new (allocator) HMul(type, opa, graph->GetConstant(type, fpow));
1258           } else {
1259             e = new (allocator) HDiv(type, opa, graph->GetConstant(type, fpow), kNoDexPc);
1260           }
1261           *result = Insert(block, new (allocator) HAdd(type, Insert(block, e), opb));
1262         }
1263       }
1264       return true;
1265     }
1266   }
1267   return false;
1268 }
1269 
GenerateLastValueWrapAround(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * info,HInductionVarAnalysis::InductionInfo * trip,HGraph * graph,HBasicBlock * block,HInstruction ** result) const1270 bool InductionVarRange::GenerateLastValueWrapAround(const HBasicBlock* context,
1271                                                     const HLoopInformation* loop,
1272                                                     HInductionVarAnalysis::InductionInfo* info,
1273                                                     HInductionVarAnalysis::InductionInfo* trip,
1274                                                     HGraph* graph,
1275                                                     HBasicBlock* block,
1276                                                     /*out*/HInstruction** result) const {
1277   DCHECK(info != nullptr);
1278   DCHECK_EQ(info->induction_class, HInductionVarAnalysis::kWrapAround);
1279   // Count depth.
1280   int32_t depth = 0;
1281   for (; info->induction_class == HInductionVarAnalysis::kWrapAround;
1282        info = info->op_b, ++depth) {}
1283   // Handle wrap(x, wrap(.., y)) if trip count reaches an invariant at end.
1284   // TODO: generalize, but be careful to adjust the terminal.
1285   int64_t m = 0;
1286   if (info->induction_class == HInductionVarAnalysis::kInvariant &&
1287       IsConstant(context, loop, trip->op_a, kExact, &m) &&
1288       m >= depth) {
1289     return GenerateCode(
1290         context, loop, info, /*trip=*/ nullptr, graph, block, /*is_min=*/ false, result);
1291   }
1292   return false;
1293 }
1294 
GenerateLastValuePeriodic(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * info,HInductionVarAnalysis::InductionInfo * trip,HGraph * graph,HBasicBlock * block,HInstruction ** result,bool * needs_taken_test) const1295 bool InductionVarRange::GenerateLastValuePeriodic(const HBasicBlock* context,
1296                                                   const HLoopInformation* loop,
1297                                                   HInductionVarAnalysis::InductionInfo* info,
1298                                                   HInductionVarAnalysis::InductionInfo* trip,
1299                                                   HGraph* graph,
1300                                                   HBasicBlock* block,
1301                                                   /*out*/HInstruction** result,
1302                                                   /*out*/bool* needs_taken_test) const {
1303   DCHECK(info != nullptr);
1304   DCHECK_EQ(info->induction_class, HInductionVarAnalysis::kPeriodic);
1305   // Count period and detect all-invariants.
1306   int64_t period = 1;
1307   bool all_invariants = true;
1308   HInductionVarAnalysis::InductionInfo* p = info;
1309   for (; p->induction_class == HInductionVarAnalysis::kPeriodic; p = p->op_b, ++period) {
1310     DCHECK_EQ(p->op_a->induction_class, HInductionVarAnalysis::kInvariant);
1311     if (p->op_a->operation != HInductionVarAnalysis::kFetch) {
1312       all_invariants = false;
1313     }
1314   }
1315   DCHECK_EQ(p->induction_class, HInductionVarAnalysis::kInvariant);
1316   if (p->operation != HInductionVarAnalysis::kFetch) {
1317     all_invariants = false;
1318   }
1319   // Don't rely on FP arithmetic to be precise, unless the full period
1320   // consist of pre-computed expressions only.
1321   if (info->type == DataType::Type::kFloat32 || info->type == DataType::Type::kFloat64) {
1322     if (!all_invariants) {
1323       return false;
1324     }
1325   }
1326   // Handle any periodic(x, periodic(.., y)) for known maximum index value m.
1327   int64_t m = 0;
1328   if (IsConstant(context, loop, trip->op_a, kExact, &m) && m >= 1) {
1329     int64_t li = m % period;
1330     for (int64_t i = 0; i < li; info = info->op_b, i++) {}
1331     if (info->induction_class == HInductionVarAnalysis::kPeriodic) {
1332       info = info->op_a;
1333     }
1334     return GenerateCode(
1335         context, loop, info, /*trip=*/ nullptr, graph, block, /*is_min=*/ false, result);
1336   }
1337   // Handle periodic(x, y) using even/odd-select on trip count. Enter trip count expression
1338   // directly to obtain the maximum index value t even if taken test is needed.
1339   HInstruction* x = nullptr;
1340   HInstruction* y = nullptr;
1341   HInstruction* t = nullptr;
1342   if (period == 2 &&
1343       GenerateCode(context,
1344                    loop,
1345                    info->op_a,
1346                    /*trip=*/ nullptr,
1347                    graph,
1348                    block,
1349                    /*is_min=*/ false,
1350                    graph ? &x : nullptr) &&
1351       GenerateCode(context,
1352                    loop,
1353                    info->op_b,
1354                    /*trip=*/ nullptr,
1355                    graph,
1356                    block,
1357                    /*is_min=*/ false,
1358                    graph ? &y : nullptr) &&
1359       GenerateCode(context,
1360                    loop,
1361                    trip->op_a,
1362                    /*trip=*/ nullptr,
1363                    graph,
1364                    block,
1365                    /*is_min=*/ false,
1366                    graph ? &t : nullptr)) {
1367     // During actual code generation (graph != nullptr), generate is_even ? x : y.
1368     if (graph != nullptr) {
1369       DataType::Type type = trip->type;
1370       ArenaAllocator* allocator = graph->GetAllocator();
1371       HInstruction* msk =
1372           Insert(block, new (allocator) HAnd(type, t, graph->GetConstant(type, 1)));
1373       HInstruction* is_even =
1374           Insert(block, new (allocator) HEqual(msk, graph->GetConstant(type, 0), kNoDexPc));
1375       *result = Insert(block, new (graph->GetAllocator()) HSelect(is_even, x, y, kNoDexPc));
1376     }
1377     // Guard select with taken test if needed.
1378     if (*needs_taken_test) {
1379       HInstruction* is_taken = nullptr;
1380       if (GenerateCode(context,
1381                        loop,
1382                        trip->op_b,
1383                        /*trip=*/ nullptr,
1384                        graph,
1385                        block,
1386                        /*is_min=*/ false,
1387                        graph ? &is_taken : nullptr)) {
1388         if (graph != nullptr) {
1389           ArenaAllocator* allocator = graph->GetAllocator();
1390           *result = Insert(block, new (allocator) HSelect(is_taken, *result, x, kNoDexPc));
1391         }
1392         *needs_taken_test = false;  // taken care of
1393       } else {
1394         return false;
1395       }
1396     }
1397     return true;
1398   }
1399   return false;
1400 }
1401 
GenerateCode(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * info,HInductionVarAnalysis::InductionInfo * trip,HGraph * graph,HBasicBlock * block,bool is_min,HInstruction ** result) const1402 bool InductionVarRange::GenerateCode(const HBasicBlock* context,
1403                                      const HLoopInformation* loop,
1404                                      HInductionVarAnalysis::InductionInfo* info,
1405                                      HInductionVarAnalysis::InductionInfo* trip,
1406                                      HGraph* graph,  // when set, code is generated
1407                                      HBasicBlock* block,
1408                                      bool is_min,
1409                                      /*out*/HInstruction** result) const {
1410   if (info != nullptr) {
1411     // If during codegen, the result is not needed (nullptr), simply return success.
1412     if (graph != nullptr && result == nullptr) {
1413       return true;
1414     }
1415     // Handle current operation.
1416     DataType::Type type = info->type;
1417     HInstruction* opa = nullptr;
1418     HInstruction* opb = nullptr;
1419     switch (info->induction_class) {
1420       case HInductionVarAnalysis::kInvariant:
1421         // Invariants (note that since invariants only have other invariants as
1422         // sub expressions, viz. no induction, there is no need to adjust is_min).
1423         switch (info->operation) {
1424           case HInductionVarAnalysis::kAdd:
1425           case HInductionVarAnalysis::kSub:
1426           case HInductionVarAnalysis::kMul:
1427           case HInductionVarAnalysis::kDiv:
1428           case HInductionVarAnalysis::kRem:
1429           case HInductionVarAnalysis::kXor:
1430           case HInductionVarAnalysis::kLT:
1431           case HInductionVarAnalysis::kLE:
1432           case HInductionVarAnalysis::kGT:
1433           case HInductionVarAnalysis::kGE:
1434             if (GenerateCode(context, loop, info->op_a, trip, graph, block, is_min, &opa) &&
1435                 GenerateCode(context, loop, info->op_b, trip, graph, block, is_min, &opb)) {
1436               if (graph != nullptr) {
1437                 HInstruction* operation = nullptr;
1438                 switch (info->operation) {
1439                   case HInductionVarAnalysis::kAdd:
1440                     operation = new (graph->GetAllocator()) HAdd(type, opa, opb); break;
1441                   case HInductionVarAnalysis::kSub:
1442                     operation = new (graph->GetAllocator()) HSub(type, opa, opb); break;
1443                   case HInductionVarAnalysis::kMul:
1444                     operation = new (graph->GetAllocator()) HMul(type, opa, opb, kNoDexPc); break;
1445                   case HInductionVarAnalysis::kDiv:
1446                     operation = new (graph->GetAllocator()) HDiv(type, opa, opb, kNoDexPc); break;
1447                   case HInductionVarAnalysis::kRem:
1448                     operation = new (graph->GetAllocator()) HRem(type, opa, opb, kNoDexPc); break;
1449                   case HInductionVarAnalysis::kXor:
1450                     operation = new (graph->GetAllocator()) HXor(type, opa, opb); break;
1451                   case HInductionVarAnalysis::kLT:
1452                     operation = new (graph->GetAllocator()) HLessThan(opa, opb); break;
1453                   case HInductionVarAnalysis::kLE:
1454                     operation = new (graph->GetAllocator()) HLessThanOrEqual(opa, opb); break;
1455                   case HInductionVarAnalysis::kGT:
1456                     operation = new (graph->GetAllocator()) HGreaterThan(opa, opb); break;
1457                   case HInductionVarAnalysis::kGE:
1458                     operation = new (graph->GetAllocator()) HGreaterThanOrEqual(opa, opb); break;
1459                   default:
1460                     LOG(FATAL) << "unknown operation";
1461                 }
1462                 *result = Insert(block, operation);
1463               }
1464               return true;
1465             }
1466             break;
1467           case HInductionVarAnalysis::kNeg:
1468             if (GenerateCode(context, loop, info->op_b, trip, graph, block, !is_min, &opb)) {
1469               if (graph != nullptr) {
1470                 *result = Insert(block, new (graph->GetAllocator()) HNeg(type, opb));
1471               }
1472               return true;
1473             }
1474             break;
1475           case HInductionVarAnalysis::kFetch:
1476             if (graph != nullptr) {
1477               *result = info->fetch;  // already in HIR
1478             }
1479             return true;
1480           case HInductionVarAnalysis::kTripCountInLoop:
1481           case HInductionVarAnalysis::kTripCountInLoopUnsafe:
1482             if (UseFullTripCount(context, loop, is_min)) {
1483               // Generate the full trip count (do not subtract 1 as we do in loop body).
1484               return GenerateCode(
1485                   context, loop, info->op_a, trip, graph, block, /*is_min=*/ false, result);
1486             }
1487             FALLTHROUGH_INTENDED;
1488           case HInductionVarAnalysis::kTripCountInBody:
1489           case HInductionVarAnalysis::kTripCountInBodyUnsafe:
1490             if (is_min) {
1491               if (graph != nullptr) {
1492                 *result = graph->GetConstant(type, 0);
1493               }
1494               return true;
1495             } else if (IsContextInBody(context, loop)) {
1496               if (GenerateCode(context, loop, info->op_a, trip, graph, block, is_min, &opb)) {
1497                 if (graph != nullptr) {
1498                   ArenaAllocator* allocator = graph->GetAllocator();
1499                   *result =
1500                       Insert(block, new (allocator) HSub(type, opb, graph->GetConstant(type, 1)));
1501                 }
1502                 return true;
1503               }
1504             }
1505             break;
1506           case HInductionVarAnalysis::kNop:
1507             LOG(FATAL) << "unexpected invariant nop";
1508         }  // switch invariant operation
1509         break;
1510       case HInductionVarAnalysis::kLinear: {
1511         // Linear induction a * i + b, for normalized 0 <= i < TC. For ranges, this should
1512         // be restricted to a unit stride to avoid arithmetic wrap-around situations that
1513         // are harder to guard against. For a last value, requesting min/max based on any
1514         // known stride yields right value. Always avoid any narrowing linear induction or
1515         // any type mismatch between the linear induction and the trip count expression.
1516         // TODO: careful runtime type conversions could generalize this latter restriction.
1517         if (!HInductionVarAnalysis::IsNarrowingLinear(info) && trip->type == type) {
1518           int64_t stride_value = 0;
1519           if (IsConstant(context, loop, info->op_a, kExact, &stride_value) &&
1520               CanLongValueFitIntoInt(stride_value)) {
1521             const bool is_min_a = stride_value >= 0 ? is_min : !is_min;
1522             if (GenerateCode(context, loop, trip,       trip, graph, block, is_min_a, &opa) &&
1523                 GenerateCode(context, loop, info->op_b, trip, graph, block, is_min,   &opb)) {
1524               if (graph != nullptr) {
1525                 ArenaAllocator* allocator = graph->GetAllocator();
1526                 HInstruction* oper;
1527                 if (stride_value == 1) {
1528                   oper = new (allocator) HAdd(type, opa, opb);
1529                 } else if (stride_value == -1) {
1530                   oper = new (graph->GetAllocator()) HSub(type, opb, opa);
1531                 } else {
1532                   HInstruction* mul =
1533                       new (allocator) HMul(type, graph->GetConstant(type, stride_value), opa);
1534                   oper = new (allocator) HAdd(type, Insert(block, mul), opb);
1535                 }
1536                 *result = Insert(block, oper);
1537               }
1538               return true;
1539             }
1540           }
1541         }
1542         break;
1543       }
1544       case HInductionVarAnalysis::kPolynomial:
1545       case HInductionVarAnalysis::kGeometric:
1546         break;
1547       case HInductionVarAnalysis::kWrapAround:
1548       case HInductionVarAnalysis::kPeriodic: {
1549         // Wrap-around and periodic inductions are restricted to constants only, so that extreme
1550         // values are easy to test at runtime without complications of arithmetic wrap-around.
1551         Value extreme = GetVal(context, loop, info, trip, is_min);
1552         if (IsConstantValue(extreme)) {
1553           if (graph != nullptr) {
1554             *result = graph->GetConstant(type, extreme.b_constant);
1555           }
1556           return true;
1557         }
1558         break;
1559       }
1560     }  // switch induction class
1561   }
1562   return false;
1563 }
1564 
ReplaceInduction(HInductionVarAnalysis::InductionInfo * info,HInstruction * fetch,HInstruction * replacement)1565 void InductionVarRange::ReplaceInduction(HInductionVarAnalysis::InductionInfo* info,
1566                                          HInstruction* fetch,
1567                                          HInstruction* replacement) {
1568   if (info != nullptr) {
1569     if (info->induction_class == HInductionVarAnalysis::kInvariant &&
1570         info->operation == HInductionVarAnalysis::kFetch &&
1571         info->fetch == fetch) {
1572       info->fetch = replacement;
1573     }
1574     ReplaceInduction(info->op_a, fetch, replacement);
1575     ReplaceInduction(info->op_b, fetch, replacement);
1576   }
1577 }
1578 
1579 }  // namespace art
1580