xref: /aosp_15_r20/art/compiler/optimizing/induction_var_range.cc (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
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       (stride_value != -1 &&
285        stride_value != 0 &&
286        stride_value != 1)) {
287     LOG(FATAL) << "Failed precondition: CanGenerateRange()";
288   }
289 }
290 
GenerateTakenTest(HInstruction * loop_control,HGraph * graph,HBasicBlock * block)291 HInstruction* InductionVarRange::GenerateTakenTest(HInstruction* loop_control,
292                                                    HGraph* graph,
293                                                    HBasicBlock* block) {
294   const HBasicBlock* context = loop_control->GetBlock();
295   HInstruction* taken_test = nullptr;
296   bool is_last_value = false;
297   int64_t stride_value = 0;
298   bool b1, b2;  // unused
299   if (!GenerateRangeOrLastValue(context,
300                                 loop_control,
301                                 is_last_value,
302                                 graph,
303                                 block,
304                                 nullptr,
305                                 nullptr,
306                                 &taken_test,
307                                 &stride_value,
308                                 &b1,
309                                 &b2) ||
310       (stride_value != -1 &&
311        stride_value != 0 &&
312        stride_value != 1)) {
313     LOG(FATAL) << "Failed precondition: CanGenerateRange()";
314   }
315   return taken_test;
316 }
317 
CanGenerateLastValue(HInstruction * instruction)318 bool InductionVarRange::CanGenerateLastValue(HInstruction* instruction) {
319   const HBasicBlock* context = instruction->GetBlock();
320   bool is_last_value = true;
321   int64_t stride_value = 0;
322   bool needs_finite_test = false;
323   bool needs_taken_test = false;
324   return GenerateRangeOrLastValue(context,
325                                   instruction,
326                                   is_last_value,
327                                   nullptr,
328                                   nullptr,
329                                   nullptr,
330                                   nullptr,
331                                   nullptr,  // nothing generated yet
332                                   &stride_value,
333                                   &needs_finite_test,
334                                   &needs_taken_test)
335       && !needs_finite_test && !needs_taken_test;
336 }
337 
GenerateLastValue(HInstruction * instruction,HGraph * graph,HBasicBlock * block)338 HInstruction* InductionVarRange::GenerateLastValue(HInstruction* instruction,
339                                                    HGraph* graph,
340                                                    HBasicBlock* block) {
341   const HBasicBlock* context = instruction->GetBlock();
342   HInstruction* last_value = nullptr;
343   bool is_last_value = true;
344   int64_t stride_value = 0;
345   bool needs_finite_test = false;
346   bool needs_taken_test = false;
347   if (!GenerateRangeOrLastValue(context,
348                                 instruction,
349                                 is_last_value,
350                                 graph,
351                                 block,
352                                 &last_value,
353                                 &last_value,
354                                 nullptr,
355                                 &stride_value,
356                                 &needs_finite_test,
357                                 &needs_taken_test) ||
358       needs_finite_test ||
359       needs_taken_test) {
360     LOG(FATAL) << "Failed precondition: CanGenerateLastValue()";
361   }
362   return last_value;
363 }
364 
Replace(HInstruction * instruction,HInstruction * fetch,HInstruction * replacement)365 void InductionVarRange::Replace(HInstruction* instruction,
366                                 HInstruction* fetch,
367                                 HInstruction* replacement) {
368   for (HLoopInformation* lp = instruction->GetBlock()->GetLoopInformation();  // closest enveloping loop
369        lp != nullptr;
370        lp = lp->GetPreHeader()->GetLoopInformation()) {
371     // Update loop's InductionInfo about fetch.
372     ReplaceInduction(induction_analysis_->LookupInfo(lp, fetch), fetch, replacement);
373     // Update loop's trip-count information.
374     ReplaceInduction(induction_analysis_->LookupInfo(lp, GetLoopControl(lp)), fetch, replacement);
375   }
376 }
377 
IsFinite(const HLoopInformation * loop,int64_t * trip_count) const378 bool InductionVarRange::IsFinite(const HLoopInformation* loop, /*out*/ int64_t* trip_count) const {
379   bool is_constant_unused = false;
380   return CheckForFiniteAndConstantProps(loop, &is_constant_unused, trip_count);
381 }
382 
HasKnownTripCount(const HLoopInformation * loop,int64_t * trip_count) const383 bool InductionVarRange::HasKnownTripCount(const HLoopInformation* loop,
384                                           /*out*/ int64_t* trip_count) const {
385   bool is_constant = false;
386   CheckForFiniteAndConstantProps(loop, &is_constant, trip_count);
387   // Set negative trip counts as 0, since it means that no trips would happen. Note that if the
388   // `is_constant` value is false, `trip_count` would be disregareded.
389   if (*trip_count < 0) {
390     *trip_count = 0;
391   }
392   return is_constant;
393 }
394 
IsUnitStride(const HBasicBlock * context,HInstruction * instruction,HGraph * graph,HInstruction ** offset) const395 bool InductionVarRange::IsUnitStride(const HBasicBlock* context,
396                                      HInstruction* instruction,
397                                      HGraph* graph,
398                                      /*out*/ HInstruction** offset) const {
399   const HLoopInformation* loop = nullptr;
400   HInductionVarAnalysis::InductionInfo* info = nullptr;
401   HInductionVarAnalysis::InductionInfo* trip = nullptr;
402   if (HasInductionInfo(context, instruction, &loop, &info, &trip)) {
403     if (info->induction_class == HInductionVarAnalysis::kLinear &&
404         !HInductionVarAnalysis::IsNarrowingLinear(info)) {
405       int64_t stride_value = 0;
406       if (IsConstant(context, loop, info->op_a, kExact, &stride_value) && stride_value == 1) {
407         int64_t off_value = 0;
408         if (IsConstant(context, loop, info->op_b, kExact, &off_value)) {
409           *offset = graph->GetConstant(info->op_b->type, off_value);
410         } else if (info->op_b->operation == HInductionVarAnalysis::kFetch) {
411           *offset = info->op_b->fetch;
412         } else {
413           return false;
414         }
415         return true;
416       }
417     }
418   }
419   return false;
420 }
421 
GenerateTripCount(const HLoopInformation * loop,HGraph * graph,HBasicBlock * block)422 HInstruction* InductionVarRange::GenerateTripCount(const HLoopInformation* loop,
423                                                    HGraph* graph,
424                                                    HBasicBlock* block) {
425   HInstruction* loop_control = GetLoopControl(loop);
426   HInductionVarAnalysis::InductionInfo *trip = induction_analysis_->LookupInfo(loop, loop_control);
427   if (trip != nullptr && !IsUnsafeTripCount(trip)) {
428     const HBasicBlock* context = loop_control->GetBlock();
429     HInstruction* taken_test = nullptr;
430     HInstruction* trip_expr = nullptr;
431     if (IsBodyTripCount(trip)) {
432       if (!GenerateCode(context,
433                         loop,
434                         trip->op_b,
435                         /*trip=*/ nullptr,
436                         graph,
437                         block,
438                         /*is_min=*/ false,
439                         &taken_test)) {
440         return nullptr;
441       }
442     }
443     if (GenerateCode(context,
444                      loop,
445                      trip->op_a,
446                      /*trip=*/ nullptr,
447                      graph,
448                      block,
449                      /*is_min=*/ false,
450                      &trip_expr)) {
451       if (taken_test != nullptr) {
452         HInstruction* zero = graph->GetConstant(trip->type, 0);
453         ArenaAllocator* allocator = graph->GetAllocator();
454         trip_expr = Insert(block, new (allocator) HSelect(taken_test, trip_expr, zero, kNoDexPc));
455       }
456       return trip_expr;
457     }
458   }
459   return nullptr;
460 }
461 
462 //
463 // Private class methods.
464 //
465 
CheckForFiniteAndConstantProps(const HLoopInformation * loop,bool * is_constant,int64_t * trip_count) const466 bool InductionVarRange::CheckForFiniteAndConstantProps(const HLoopInformation* loop,
467                                                        /*out*/ bool* is_constant,
468                                                        /*out*/ int64_t* trip_count) const {
469   HInstruction* loop_control = GetLoopControl(loop);
470   HInductionVarAnalysis::InductionInfo *trip = induction_analysis_->LookupInfo(loop, loop_control);
471   if (trip != nullptr && !IsUnsafeTripCount(trip)) {
472     const HBasicBlock* context = loop_control->GetBlock();
473     *is_constant = IsConstant(context, loop, trip->op_a, kExact, trip_count);
474     return true;
475   }
476   return false;
477 }
478 
IsConstant(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * info,ConstantRequest request,int64_t * value) const479 bool InductionVarRange::IsConstant(const HBasicBlock* context,
480                                    const HLoopInformation* loop,
481                                    HInductionVarAnalysis::InductionInfo* info,
482                                    ConstantRequest request,
483                                    /*out*/ int64_t* value) const {
484   if (info != nullptr) {
485     // A direct 32-bit or 64-bit constant fetch. This immediately satisfies
486     // any of the three requests (kExact, kAtMost, and KAtLeast).
487     if (info->induction_class == HInductionVarAnalysis::kInvariant &&
488         info->operation == HInductionVarAnalysis::kFetch) {
489       if (IsInt64AndGet(info->fetch, value)) {
490         return true;
491       }
492     }
493     // Try range analysis on the invariant, only accept a proper range
494     // to avoid arithmetic wrap-around anomalies.
495     Value min_val = GetVal(context, loop, info, /*trip=*/ nullptr, /*is_min=*/ true);
496     Value max_val = GetVal(context, loop, info, /*trip=*/ nullptr, /*is_min=*/ false);
497     if (IsConstantValue(min_val) &&
498         IsConstantValue(max_val) && min_val.b_constant <= max_val.b_constant) {
499       if ((request == kExact && min_val.b_constant == max_val.b_constant) || request == kAtMost) {
500         *value = max_val.b_constant;
501         return true;
502       } else if (request == kAtLeast) {
503         *value = min_val.b_constant;
504         return true;
505       }
506     }
507   }
508   return false;
509 }
510 
HasInductionInfo(const HBasicBlock * context,HInstruction * instruction,const HLoopInformation ** loop,HInductionVarAnalysis::InductionInfo ** info,HInductionVarAnalysis::InductionInfo ** trip) const511 bool InductionVarRange::HasInductionInfo(
512     const HBasicBlock* context,
513     HInstruction* instruction,
514     /*out*/ const HLoopInformation** loop,
515     /*out*/ HInductionVarAnalysis::InductionInfo** info,
516     /*out*/ HInductionVarAnalysis::InductionInfo** trip) const {
517   DCHECK(context != nullptr);
518   HLoopInformation* lp = context->GetLoopInformation();  // closest enveloping loop
519   if (lp != nullptr) {
520     HInductionVarAnalysis::InductionInfo* i = induction_analysis_->LookupInfo(lp, instruction);
521     if (i != nullptr) {
522       *loop = lp;
523       *info = i;
524       *trip = induction_analysis_->LookupInfo(lp, GetLoopControl(lp));
525       return true;
526     }
527   }
528   return false;
529 }
530 
IsWellBehavedTripCount(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * trip) const531 bool InductionVarRange::IsWellBehavedTripCount(const HBasicBlock* context,
532                                                const HLoopInformation* loop,
533                                                HInductionVarAnalysis::InductionInfo* trip) const {
534   if (trip != nullptr) {
535     // Both bounds that define a trip-count are well-behaved if they either are not defined
536     // in any loop, or are contained in a proper interval. This allows finding the min/max
537     // of an expression by chasing outward.
538     InductionVarRange range(induction_analysis_);
539     HInductionVarAnalysis::InductionInfo* lower = trip->op_b->op_a;
540     HInductionVarAnalysis::InductionInfo* upper = trip->op_b->op_b;
541     int64_t not_used = 0;
542     return
543         (!HasFetchInLoop(lower) || range.IsConstant(context, loop, lower, kAtLeast, &not_used)) &&
544         (!HasFetchInLoop(upper) || range.IsConstant(context, loop, upper, kAtLeast, &not_used));
545   }
546   return true;
547 }
548 
HasFetchInLoop(HInductionVarAnalysis::InductionInfo * info) const549 bool InductionVarRange::HasFetchInLoop(HInductionVarAnalysis::InductionInfo* info) const {
550   if (info != nullptr) {
551     if (info->induction_class == HInductionVarAnalysis::kInvariant &&
552         info->operation == HInductionVarAnalysis::kFetch) {
553       return info->fetch->GetBlock()->GetLoopInformation() != nullptr;
554     }
555     return HasFetchInLoop(info->op_a) || HasFetchInLoop(info->op_b);
556   }
557   return false;
558 }
559 
NeedsTripCount(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * info,int64_t * stride_value) const560 bool InductionVarRange::NeedsTripCount(const HBasicBlock* context,
561                                        const HLoopInformation* loop,
562                                        HInductionVarAnalysis::InductionInfo* info,
563                                        int64_t* stride_value) const {
564   if (info != nullptr) {
565     if (info->induction_class == HInductionVarAnalysis::kLinear) {
566       return IsConstant(context, loop, info->op_a, kExact, stride_value);
567     } else if (info->induction_class == HInductionVarAnalysis::kPolynomial) {
568       return NeedsTripCount(context, loop, info->op_a, stride_value);
569     } else if (info->induction_class == HInductionVarAnalysis::kWrapAround) {
570       return NeedsTripCount(context, loop, info->op_b, stride_value);
571     }
572   }
573   return false;
574 }
575 
IsBodyTripCount(HInductionVarAnalysis::InductionInfo * trip) const576 bool InductionVarRange::IsBodyTripCount(HInductionVarAnalysis::InductionInfo* trip) const {
577   if (trip != nullptr) {
578     if (trip->induction_class == HInductionVarAnalysis::kInvariant) {
579       return trip->operation == HInductionVarAnalysis::kTripCountInBody ||
580              trip->operation == HInductionVarAnalysis::kTripCountInBodyUnsafe;
581     }
582   }
583   return false;
584 }
585 
IsUnsafeTripCount(HInductionVarAnalysis::InductionInfo * trip) const586 bool InductionVarRange::IsUnsafeTripCount(HInductionVarAnalysis::InductionInfo* trip) const {
587   if (trip != nullptr) {
588     if (trip->induction_class == HInductionVarAnalysis::kInvariant) {
589       return trip->operation == HInductionVarAnalysis::kTripCountInBodyUnsafe ||
590              trip->operation == HInductionVarAnalysis::kTripCountInLoopUnsafe;
591     }
592   }
593   return false;
594 }
595 
GetLinear(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * info,HInductionVarAnalysis::InductionInfo * trip,bool is_min) const596 InductionVarRange::Value InductionVarRange::GetLinear(const HBasicBlock* context,
597                                                       const HLoopInformation* loop,
598                                                       HInductionVarAnalysis::InductionInfo* info,
599                                                       HInductionVarAnalysis::InductionInfo* trip,
600                                                       bool is_min) const {
601   DCHECK(info != nullptr);
602   DCHECK_EQ(info->induction_class, HInductionVarAnalysis::kLinear);
603   // Detect common situation where an offset inside the trip-count cancels out during range
604   // analysis (finding max a * (TC - 1) + OFFSET for a == 1 and TC = UPPER - OFFSET or finding
605   // min a * (TC - 1) + OFFSET for a == -1 and TC = OFFSET - UPPER) to avoid losing information
606   // with intermediate results that only incorporate single instructions.
607   if (trip != nullptr) {
608     HInductionVarAnalysis::InductionInfo* trip_expr = trip->op_a;
609     if (trip_expr->type == info->type && trip_expr->operation == HInductionVarAnalysis::kSub) {
610       int64_t stride_value = 0;
611       if (IsConstant(context, loop, info->op_a, kExact, &stride_value)) {
612         if (!is_min && stride_value == 1) {
613           // Test original trip's negative operand (trip_expr->op_b) against offset of induction.
614           if (HInductionVarAnalysis::InductionEqual(trip_expr->op_b, info->op_b)) {
615             // Analyze cancelled trip with just the positive operand (trip_expr->op_a).
616             HInductionVarAnalysis::InductionInfo cancelled_trip(
617                 trip->induction_class,
618                 trip->operation,
619                 trip_expr->op_a,
620                 trip->op_b,
621                 nullptr,
622                 trip->type);
623             return GetVal(context, loop, &cancelled_trip, trip, is_min);
624           }
625         } else if (is_min && stride_value == -1) {
626           // Test original trip's positive operand (trip_expr->op_a) against offset of induction.
627           if (HInductionVarAnalysis::InductionEqual(trip_expr->op_a, info->op_b)) {
628             // Analyze cancelled trip with just the negative operand (trip_expr->op_b).
629             HInductionVarAnalysis::InductionInfo neg(
630                 HInductionVarAnalysis::kInvariant,
631                 HInductionVarAnalysis::kNeg,
632                 nullptr,
633                 trip_expr->op_b,
634                 nullptr,
635                 trip->type);
636             HInductionVarAnalysis::InductionInfo cancelled_trip(
637                 trip->induction_class, trip->operation, &neg, trip->op_b, nullptr, trip->type);
638             return SubValue(Value(0), GetVal(context, loop, &cancelled_trip, trip, !is_min));
639           }
640         }
641       }
642     }
643   }
644   // General rule of linear induction a * i + b, for normalized 0 <= i < TC.
645   return AddValue(GetMul(context, loop, info->op_a, trip, trip, is_min),
646                   GetVal(context, loop, info->op_b, trip, is_min));
647 }
648 
GetPolynomial(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * info,HInductionVarAnalysis::InductionInfo * trip,bool is_min) const649 InductionVarRange::Value InductionVarRange::GetPolynomial(
650     const HBasicBlock* context,
651     const HLoopInformation* loop,
652     HInductionVarAnalysis::InductionInfo* info,
653     HInductionVarAnalysis::InductionInfo* trip,
654     bool is_min) const {
655   DCHECK(info != nullptr);
656   DCHECK_EQ(info->induction_class, HInductionVarAnalysis::kPolynomial);
657   int64_t a = 0;
658   int64_t b = 0;
659   if (IsConstant(context, loop, info->op_a->op_a, kExact, &a) &&
660       CanLongValueFitIntoInt(a) &&
661       a >= 0 &&
662       IsConstant(context, loop, info->op_a->op_b, kExact, &b) &&
663       CanLongValueFitIntoInt(b) &&
664       b >= 0) {
665     // Evaluate bounds on sum_i=0^m-1(a * i + b) + c with a,b >= 0 for
666     // maximum index value m as a * (m * (m-1)) / 2 + b * m + c.
667     // Do not simply return `c` as minimum because the trip count may be non-zero
668     // if the `context` is after the `loop` (and therefore ignoring `is_min`).
669     Value c = GetVal(context, loop, info->op_b, trip, is_min);
670     Value m = GetVal(context, loop, trip, trip, is_min);
671     Value t = DivValue(MulValue(m, SubValue(m, Value(1))), Value(2));
672     Value x = MulValue(Value(a), t);
673     Value y = MulValue(Value(b), m);
674     return AddValue(AddValue(x, y), c);
675   }
676   return Value();
677 }
678 
GetGeometric(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * info,HInductionVarAnalysis::InductionInfo * trip,bool is_min) const679 InductionVarRange::Value InductionVarRange::GetGeometric(const HBasicBlock* context,
680                                                          const HLoopInformation* loop,
681                                                          HInductionVarAnalysis::InductionInfo* info,
682                                                          HInductionVarAnalysis::InductionInfo* trip,
683                                                          bool is_min) const {
684   DCHECK(info != nullptr);
685   DCHECK_EQ(info->induction_class, HInductionVarAnalysis::kGeometric);
686   int64_t a = 0;
687   int64_t f = 0;
688   if (IsConstant(context, loop, info->op_a, kExact, &a) &&
689       CanLongValueFitIntoInt(a) &&
690       IsInt64AndGet(info->fetch, &f) && f >= 1) {
691     // Conservative bounds on a * f^-i + b with f >= 1 can be computed without
692     // trip count. Other forms would require a much more elaborate evaluation.
693     const bool is_min_a = a >= 0 ? is_min : !is_min;
694     if (info->operation == HInductionVarAnalysis::kDiv) {
695       Value b = GetVal(context, loop, info->op_b, trip, is_min);
696       return is_min_a ? b : AddValue(Value(a), b);
697     }
698   }
699   return Value();
700 }
701 
GetFetch(const HBasicBlock * context,const HLoopInformation * loop,HInstruction * instruction,HInductionVarAnalysis::InductionInfo * trip,bool is_min) const702 InductionVarRange::Value InductionVarRange::GetFetch(const HBasicBlock* context,
703                                                      const HLoopInformation* loop,
704                                                      HInstruction* instruction,
705                                                      HInductionVarAnalysis::InductionInfo* trip,
706                                                      bool is_min) const {
707   // Special case when chasing constants: single instruction that denotes trip count in the
708   // loop-body is minimal 1 and maximal, with safe trip-count, max int,
709   if (chase_hint_ == nullptr &&
710       IsContextInBody(context, loop) &&
711       trip != nullptr &&
712       instruction == trip->op_a->fetch) {
713     if (is_min) {
714       return Value(1);
715     } else if (!instruction->IsConstant() && !IsUnsafeTripCount(trip)) {
716       return Value(std::numeric_limits<int32_t>::max());
717     }
718   }
719   // Unless at a constant or hint, chase the instruction a bit deeper into the HIR tree, so that
720   // it becomes more likely range analysis will compare the same instructions as terminal nodes.
721   int64_t value;
722   if (IsInt64AndGet(instruction, &value) && CanLongValueFitIntoInt(value)) {
723     // Proper constant reveals best information.
724     return Value(static_cast<int32_t>(value));
725   } else if (instruction == chase_hint_) {
726     // At hint, fetch is represented by itself.
727     return Value(instruction, 1, 0);
728   } else if (instruction->IsAdd()) {
729     // Incorporate suitable constants in the chased value.
730     if (IsInt64AndGet(instruction->InputAt(0), &value) && CanLongValueFitIntoInt(value)) {
731       return AddValue(Value(static_cast<int32_t>(value)),
732                       GetFetch(context, loop, instruction->InputAt(1), trip, is_min));
733     } else if (IsInt64AndGet(instruction->InputAt(1), &value) && CanLongValueFitIntoInt(value)) {
734       return AddValue(GetFetch(context, loop, instruction->InputAt(0), trip, is_min),
735                       Value(static_cast<int32_t>(value)));
736     }
737   } else if (instruction->IsSub()) {
738     // Incorporate suitable constants in the chased value.
739     if (IsInt64AndGet(instruction->InputAt(0), &value) && CanLongValueFitIntoInt(value)) {
740       return SubValue(Value(static_cast<int32_t>(value)),
741                       GetFetch(context, loop, instruction->InputAt(1), trip, !is_min));
742     } else if (IsInt64AndGet(instruction->InputAt(1), &value) && CanLongValueFitIntoInt(value)) {
743       return SubValue(GetFetch(context, loop, instruction->InputAt(0), trip, is_min),
744                       Value(static_cast<int32_t>(value)));
745     }
746   } else if (instruction->IsArrayLength()) {
747     // Exploit length properties when chasing constants or chase into a new array declaration.
748     if (chase_hint_ == nullptr) {
749       return is_min ? Value(0) : Value(std::numeric_limits<int32_t>::max());
750     } else if (instruction->InputAt(0)->IsNewArray()) {
751       return GetFetch(
752           context, loop, instruction->InputAt(0)->AsNewArray()->GetLength(), trip, is_min);
753     }
754   } else if (instruction->IsTypeConversion()) {
755     // Since analysis is 32-bit (or narrower), chase beyond widening along the path.
756     // For example, this discovers the length in: for (long i = 0; i < a.length; i++);
757     if (instruction->AsTypeConversion()->GetInputType() == DataType::Type::kInt32 &&
758         instruction->AsTypeConversion()->GetResultType() == DataType::Type::kInt64) {
759       return GetFetch(context, loop, instruction->InputAt(0), trip, is_min);
760     }
761   }
762   // Chase an invariant fetch that is defined by another loop if the trip-count used
763   // so far is well-behaved in both bounds and the next trip-count is safe.
764   // Example:
765   //   for (int i = 0; i <= 100; i++)  // safe
766   //     for (int j = 0; j <= i; j++)  // well-behaved
767   //       j is in range [0, i  ] (if i is chase hint)
768   //         or in range [0, 100] (otherwise)
769   // Example:
770   //   for (i = 0; i < 100; ++i)
771   //     <some-code>
772   //   for (j = 0; j < 10; ++j)
773   //     sum += i;  // The `i` is a "fetch" of a loop Phi from the previous loop.
774   const HLoopInformation* next_loop = nullptr;
775   HInductionVarAnalysis::InductionInfo* next_info = nullptr;
776   HInductionVarAnalysis::InductionInfo* next_trip = nullptr;
777   if (HasInductionInfo(instruction->GetBlock(), instruction, &next_loop, &next_info, &next_trip) &&
778       IsWellBehavedTripCount(context, next_loop, trip) &&
779       !IsUnsafeTripCount(next_trip)) {
780     return GetVal(context, next_loop, next_info, next_trip, is_min);
781   }
782   // Fetch is represented by itself.
783   return Value(instruction, 1, 0);
784 }
785 
GetVal(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * info,HInductionVarAnalysis::InductionInfo * trip,bool is_min) const786 InductionVarRange::Value InductionVarRange::GetVal(const HBasicBlock* context,
787                                                    const HLoopInformation* loop,
788                                                    HInductionVarAnalysis::InductionInfo* info,
789                                                    HInductionVarAnalysis::InductionInfo* trip,
790                                                    bool is_min) const {
791   if (info != nullptr) {
792     switch (info->induction_class) {
793       case HInductionVarAnalysis::kInvariant:
794         // Invariants.
795         switch (info->operation) {
796           case HInductionVarAnalysis::kAdd:
797             return AddValue(GetVal(context, loop, info->op_a, trip, is_min),
798                             GetVal(context, loop, info->op_b, trip, is_min));
799           case HInductionVarAnalysis::kSub:  // second reversed!
800             return SubValue(GetVal(context, loop, info->op_a, trip, is_min),
801                             GetVal(context, loop, info->op_b, trip, !is_min));
802           case HInductionVarAnalysis::kNeg:  // second reversed!
803             return SubValue(Value(0),
804                             GetVal(context, loop, info->op_b, trip, !is_min));
805           case HInductionVarAnalysis::kMul:
806             return GetMul(context, loop, info->op_a, info->op_b, trip, is_min);
807           case HInductionVarAnalysis::kDiv:
808             return GetDiv(context, loop, info->op_a, info->op_b, trip, is_min);
809           case HInductionVarAnalysis::kRem:
810             return GetRem(context, loop, info->op_a, info->op_b);
811           case HInductionVarAnalysis::kXor:
812             return GetXor(context, loop, info->op_a, info->op_b);
813           case HInductionVarAnalysis::kFetch:
814             return GetFetch(context, loop, info->fetch, trip, is_min);
815           case HInductionVarAnalysis::kTripCountInLoop:
816           case HInductionVarAnalysis::kTripCountInLoopUnsafe:
817             if (UseFullTripCount(context, loop, is_min)) {
818               // Return the full trip count (do not subtract 1 as we do in loop body).
819               return GetVal(context, loop, info->op_a, trip, is_min);
820             }
821             FALLTHROUGH_INTENDED;
822           case HInductionVarAnalysis::kTripCountInBody:
823           case HInductionVarAnalysis::kTripCountInBodyUnsafe:
824             if (is_min) {
825               return Value(0);
826             } else if (IsContextInBody(context, loop)) {
827               return SubValue(GetVal(context, loop, info->op_a, trip, is_min), Value(1));
828             }
829             break;
830           default:
831             break;
832         }
833         break;
834       case HInductionVarAnalysis::kLinear:
835         return CorrectForType(GetLinear(context, loop, info, trip, is_min), info->type);
836       case HInductionVarAnalysis::kPolynomial:
837         return GetPolynomial(context, loop, info, trip, is_min);
838       case HInductionVarAnalysis::kGeometric:
839         return GetGeometric(context, loop, info, trip, is_min);
840       case HInductionVarAnalysis::kWrapAround:
841       case HInductionVarAnalysis::kPeriodic:
842         return MergeVal(GetVal(context, loop, info->op_a, trip, is_min),
843                         GetVal(context, loop, info->op_b, trip, is_min),
844                         is_min);
845     }
846   }
847   return Value();
848 }
849 
GetMul(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * info1,HInductionVarAnalysis::InductionInfo * info2,HInductionVarAnalysis::InductionInfo * trip,bool is_min) const850 InductionVarRange::Value InductionVarRange::GetMul(const HBasicBlock* context,
851                                                    const HLoopInformation* loop,
852                                                    HInductionVarAnalysis::InductionInfo* info1,
853                                                    HInductionVarAnalysis::InductionInfo* info2,
854                                                    HInductionVarAnalysis::InductionInfo* trip,
855                                                    bool is_min) const {
856   // Constant times range.
857   int64_t value = 0;
858   if (IsConstant(context, loop, info1, kExact, &value)) {
859     return MulRangeAndConstant(context, loop, value, info2, trip, is_min);
860   } else if (IsConstant(context, loop, info2, kExact, &value)) {
861     return MulRangeAndConstant(context, loop, value, info1, trip, is_min);
862   }
863   // Interval ranges.
864   Value v1_min = GetVal(context, loop, info1, trip, /*is_min=*/ true);
865   Value v1_max = GetVal(context, loop, info1, trip, /*is_min=*/ false);
866   Value v2_min = GetVal(context, loop, info2, trip, /*is_min=*/ true);
867   Value v2_max = GetVal(context, loop, info2, trip, /*is_min=*/ false);
868   // Positive range vs. positive or negative range.
869   if (IsConstantValue(v1_min) && v1_min.b_constant >= 0) {
870     if (IsConstantValue(v2_min) && v2_min.b_constant >= 0) {
871       return is_min ? MulValue(v1_min, v2_min) : MulValue(v1_max, v2_max);
872     } else if (IsConstantValue(v2_max) && v2_max.b_constant <= 0) {
873       return is_min ? MulValue(v1_max, v2_min) : MulValue(v1_min, v2_max);
874     }
875   }
876   // Negative range vs. positive or negative range.
877   if (IsConstantValue(v1_max) && v1_max.b_constant <= 0) {
878     if (IsConstantValue(v2_min) && v2_min.b_constant >= 0) {
879       return is_min ? MulValue(v1_min, v2_max) : MulValue(v1_max, v2_min);
880     } else if (IsConstantValue(v2_max) && v2_max.b_constant <= 0) {
881       return is_min ? MulValue(v1_max, v2_max) : MulValue(v1_min, v2_min);
882     }
883   }
884   return Value();
885 }
886 
GetDiv(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * info1,HInductionVarAnalysis::InductionInfo * info2,HInductionVarAnalysis::InductionInfo * trip,bool is_min) const887 InductionVarRange::Value InductionVarRange::GetDiv(const HBasicBlock* context,
888                                                    const HLoopInformation* loop,
889                                                    HInductionVarAnalysis::InductionInfo* info1,
890                                                    HInductionVarAnalysis::InductionInfo* info2,
891                                                    HInductionVarAnalysis::InductionInfo* trip,
892                                                    bool is_min) const {
893   // Range divided by constant.
894   int64_t value = 0;
895   if (IsConstant(context, loop, info2, kExact, &value)) {
896     return DivRangeAndConstant(context, loop, value, info1, trip, is_min);
897   }
898   // Interval ranges.
899   Value v1_min = GetVal(context, loop, info1, trip, /*is_min=*/ true);
900   Value v1_max = GetVal(context, loop, info1, trip, /*is_min=*/ false);
901   Value v2_min = GetVal(context, loop, info2, trip, /*is_min=*/ true);
902   Value v2_max = GetVal(context, loop, info2, trip, /*is_min=*/ false);
903   // Positive range vs. positive or negative range.
904   if (IsConstantValue(v1_min) && v1_min.b_constant >= 0) {
905     if (IsConstantValue(v2_min) && v2_min.b_constant >= 0) {
906       return is_min ? DivValue(v1_min, v2_max) : DivValue(v1_max, v2_min);
907     } else if (IsConstantValue(v2_max) && v2_max.b_constant <= 0) {
908       return is_min ? DivValue(v1_max, v2_max) : DivValue(v1_min, v2_min);
909     }
910   }
911   // Negative range vs. positive or negative range.
912   if (IsConstantValue(v1_max) && v1_max.b_constant <= 0) {
913     if (IsConstantValue(v2_min) && v2_min.b_constant >= 0) {
914       return is_min ? DivValue(v1_min, v2_min) : DivValue(v1_max, v2_max);
915     } else if (IsConstantValue(v2_max) && v2_max.b_constant <= 0) {
916       return is_min ? DivValue(v1_max, v2_min) : DivValue(v1_min, v2_max);
917     }
918   }
919   return Value();
920 }
921 
GetRem(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * info1,HInductionVarAnalysis::InductionInfo * info2) const922 InductionVarRange::Value InductionVarRange::GetRem(
923     const HBasicBlock* context,
924     const HLoopInformation* loop,
925     HInductionVarAnalysis::InductionInfo* info1,
926     HInductionVarAnalysis::InductionInfo* info2) const {
927   int64_t v1 = 0;
928   int64_t v2 = 0;
929   // Only accept exact values.
930   if (IsConstant(context, loop, info1, kExact, &v1) &&
931       IsConstant(context, loop, info2, kExact, &v2) &&
932       v2 != 0) {
933     int64_t value = v1 % v2;
934     if (CanLongValueFitIntoInt(value)) {
935       return Value(static_cast<int32_t>(value));
936     }
937   }
938   return Value();
939 }
940 
GetXor(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * info1,HInductionVarAnalysis::InductionInfo * info2) const941 InductionVarRange::Value InductionVarRange::GetXor(
942     const HBasicBlock* context,
943     const HLoopInformation* loop,
944     HInductionVarAnalysis::InductionInfo* info1,
945     HInductionVarAnalysis::InductionInfo* info2) const {
946   int64_t v1 = 0;
947   int64_t v2 = 0;
948   // Only accept exact values.
949   if (IsConstant(context, loop, info1, kExact, &v1) &&
950       IsConstant(context, loop, info2, kExact, &v2)) {
951     int64_t value = v1 ^ v2;
952     if (CanLongValueFitIntoInt(value)) {
953       return Value(static_cast<int32_t>(value));
954     }
955   }
956   return Value();
957 }
958 
MulRangeAndConstant(const HBasicBlock * context,const HLoopInformation * loop,int64_t value,HInductionVarAnalysis::InductionInfo * info,HInductionVarAnalysis::InductionInfo * trip,bool is_min) const959 InductionVarRange::Value InductionVarRange::MulRangeAndConstant(
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 MulValue(GetVal(context, loop, info, trip, is_min == value >= 0), c);
969   }
970   return Value();
971 }
972 
DivRangeAndConstant(const HBasicBlock * context,const HLoopInformation * loop,int64_t value,HInductionVarAnalysis::InductionInfo * info,HInductionVarAnalysis::InductionInfo * trip,bool is_min) const973 InductionVarRange::Value InductionVarRange::DivRangeAndConstant(
974     const HBasicBlock* context,
975     const HLoopInformation* loop,
976     int64_t value,
977     HInductionVarAnalysis::InductionInfo* info,
978     HInductionVarAnalysis::InductionInfo* trip,
979     bool is_min) const {
980   if (CanLongValueFitIntoInt(value)) {
981     Value c(static_cast<int32_t>(value));
982     return DivValue(GetVal(context, loop, info, trip, is_min == value >= 0), c);
983   }
984   return Value();
985 }
986 
AddValue(Value v1,Value v2) const987 InductionVarRange::Value InductionVarRange::AddValue(Value v1, Value v2) const {
988   if (v1.is_known && v2.is_known && IsSafeAdd(v1.b_constant, v2.b_constant)) {
989     int32_t b = v1.b_constant + v2.b_constant;
990     if (v1.a_constant == 0) {
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 && IsSafeAdd(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 
SubValue(Value v1,Value v2) const1001 InductionVarRange::Value InductionVarRange::SubValue(Value v1, Value v2) const {
1002   if (v1.is_known && v2.is_known && IsSafeSub(v1.b_constant, v2.b_constant)) {
1003     int32_t b = v1.b_constant - v2.b_constant;
1004     if (v1.a_constant == 0 && IsSafeSub(0, v2.a_constant)) {
1005       return Value(v2.instruction, -v2.a_constant, b);
1006     } else if (v2.a_constant == 0) {
1007       return Value(v1.instruction, v1.a_constant, b);
1008     } else if (v1.instruction == v2.instruction && IsSafeSub(v1.a_constant, v2.a_constant)) {
1009       return Value(v1.instruction, v1.a_constant - v2.a_constant, b);
1010     }
1011   }
1012   return Value();
1013 }
1014 
MulValue(Value v1,Value v2) const1015 InductionVarRange::Value InductionVarRange::MulValue(Value v1, Value v2) const {
1016   if (v1.is_known && v2.is_known) {
1017     if (v1.a_constant == 0) {
1018       if (IsSafeMul(v1.b_constant, v2.a_constant) && IsSafeMul(v1.b_constant, v2.b_constant)) {
1019         return Value(v2.instruction, v1.b_constant * v2.a_constant, v1.b_constant * v2.b_constant);
1020       }
1021     } else if (v2.a_constant == 0) {
1022       if (IsSafeMul(v1.a_constant, v2.b_constant) && IsSafeMul(v1.b_constant, v2.b_constant)) {
1023         return Value(v1.instruction, v1.a_constant * v2.b_constant, v1.b_constant * v2.b_constant);
1024       }
1025     }
1026   }
1027   return Value();
1028 }
1029 
DivValue(Value v1,Value v2) const1030 InductionVarRange::Value InductionVarRange::DivValue(Value v1, Value v2) const {
1031   if (v1.is_known && v2.is_known && v1.a_constant == 0 && v2.a_constant == 0) {
1032     if (IsSafeDiv(v1.b_constant, v2.b_constant)) {
1033       return Value(v1.b_constant / v2.b_constant);
1034     }
1035   }
1036   return Value();
1037 }
1038 
MergeVal(Value v1,Value v2,bool is_min) const1039 InductionVarRange::Value InductionVarRange::MergeVal(Value v1, Value v2, bool is_min) const {
1040   if (v1.is_known && v2.is_known) {
1041     if (v1.instruction == v2.instruction && v1.a_constant == v2.a_constant) {
1042       return Value(v1.instruction, v1.a_constant,
1043                    is_min ? std::min(v1.b_constant, v2.b_constant)
1044                           : std::max(v1.b_constant, v2.b_constant));
1045     }
1046   }
1047   return Value();
1048 }
1049 
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) const1050 bool InductionVarRange::GenerateRangeOrLastValue(const HBasicBlock* context,
1051                                                  HInstruction* instruction,
1052                                                  bool is_last_value,
1053                                                  HGraph* graph,
1054                                                  HBasicBlock* block,
1055                                                  /*out*/HInstruction** lower,
1056                                                  /*out*/HInstruction** upper,
1057                                                  /*out*/HInstruction** taken_test,
1058                                                  /*out*/int64_t* stride_value,
1059                                                  /*out*/bool* needs_finite_test,
1060                                                  /*out*/bool* needs_taken_test) const {
1061   const HLoopInformation* loop = nullptr;
1062   HInductionVarAnalysis::InductionInfo* info = nullptr;
1063   HInductionVarAnalysis::InductionInfo* trip = nullptr;
1064   if (!HasInductionInfo(context, instruction, &loop, &info, &trip) || trip == nullptr) {
1065     return false;  // codegen needs all information, including tripcount
1066   }
1067   // Determine what tests are needed. A finite test is needed if the evaluation code uses the
1068   // trip-count and the loop maybe unsafe (because in such cases, the index could "overshoot"
1069   // the computed range). A taken test is needed for any unknown trip-count, even if evaluation
1070   // code does not use the trip-count explicitly (since there could be an implicit relation
1071   // between e.g. an invariant subscript and a not-taken condition).
1072   *stride_value = 0;
1073   *needs_finite_test = NeedsTripCount(context, loop, info, stride_value) && IsUnsafeTripCount(trip);
1074   *needs_taken_test = IsBodyTripCount(trip);
1075   // Handle last value request.
1076   if (is_last_value) {
1077     DCHECK(!IsContextInBody(context, loop));
1078     switch (info->induction_class) {
1079       case HInductionVarAnalysis::kLinear:
1080         if (*stride_value > 0) {
1081           lower = nullptr;
1082           return GenerateLastValueLinear(
1083               context, loop, info, trip, graph, block, /*is_min=*/false, upper, needs_taken_test);
1084         } else {
1085           upper = nullptr;
1086           return GenerateLastValueLinear(
1087               context, loop, info, trip, graph, block, /*is_min=*/true, lower, needs_taken_test);
1088         }
1089       case HInductionVarAnalysis::kPolynomial:
1090         return GenerateLastValuePolynomial(context, loop, info, trip, graph, block, lower);
1091       case HInductionVarAnalysis::kGeometric:
1092         return GenerateLastValueGeometric(context, loop, info, trip, graph, block, lower);
1093       case HInductionVarAnalysis::kWrapAround:
1094         return GenerateLastValueWrapAround(context, loop, info, trip, graph, block, lower);
1095       case HInductionVarAnalysis::kPeriodic:
1096         return GenerateLastValuePeriodic(
1097             context, loop, info, trip, graph, block, lower, needs_taken_test);
1098       default:
1099         return false;
1100     }
1101   }
1102   // Code generation for taken test: generate the code when requested or otherwise analyze
1103   // if code generation is feasible when taken test is needed.
1104   if (taken_test != nullptr) {
1105     return GenerateCode(context,
1106                         loop,
1107                         trip->op_b,
1108                         /*trip=*/ nullptr,
1109                         graph,
1110                         block,
1111                         /*is_min=*/ false,
1112                         taken_test);
1113   } else if (*needs_taken_test) {
1114     if (!GenerateCode(context,
1115                       loop,
1116                       trip->op_b,
1117                       /*trip=*/ nullptr,
1118                       /*graph=*/ nullptr,
1119                       /*block=*/ nullptr,
1120                       /*is_min=*/ false,
1121                       /*result=*/ nullptr)) {
1122       return false;
1123     }
1124   }
1125   // Code generation for lower and upper.
1126   return
1127       // Success on lower if invariant (not set), or code can be generated.
1128       ((info->induction_class == HInductionVarAnalysis::kInvariant) ||
1129           GenerateCode(context, loop, info, trip, graph, block, /*is_min=*/ true, lower)) &&
1130       // And success on upper.
1131       GenerateCode(context, loop, info, trip, graph, block, /*is_min=*/ false, upper);
1132 }
1133 
GenerateLastValueLinear(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * info,HInductionVarAnalysis::InductionInfo * trip,HGraph * graph,HBasicBlock * block,bool is_min,HInstruction ** result,bool * needs_taken_test) const1134 bool InductionVarRange::GenerateLastValueLinear(const HBasicBlock* context,
1135                                                 const HLoopInformation* loop,
1136                                                 HInductionVarAnalysis::InductionInfo* info,
1137                                                 HInductionVarAnalysis::InductionInfo* trip,
1138                                                 HGraph* graph,
1139                                                 HBasicBlock* block,
1140                                                 bool is_min,
1141                                                 /*out*/ HInstruction** result,
1142                                                 /*inout*/ bool* needs_taken_test) const {
1143   DataType::Type type = info->type;
1144   // Avoid any narrowing linear induction or any type mismatch between the linear induction and the
1145   // trip count expression.
1146   if (HInductionVarAnalysis::IsNarrowingLinear(info) || trip->type != type) {
1147     return false;
1148   }
1149 
1150   // Stride value must be a known constant that fits into int32. The stride will be the `i` in `a *
1151   // i + b`.
1152   int64_t stride_value = 0;
1153   if (!IsConstant(context, loop, info->op_a, kExact, &stride_value) ||
1154       !CanLongValueFitIntoInt(stride_value)) {
1155     return false;
1156   }
1157 
1158   // We require the calculation of `a` to not overflow.
1159   const bool is_min_a = stride_value >= 0 ? is_min : !is_min;
1160   HInstruction* opa;
1161   HInstruction* opb;
1162   if (!GenerateCode(context,
1163                     loop,
1164                     trip,
1165                     trip,
1166                     graph,
1167                     block,
1168                     is_min_a,
1169                     &opa,
1170                     /*allow_potential_overflow=*/false) ||
1171       !GenerateCode(context, loop, info->op_b, trip, graph, block, is_min, &opb)) {
1172     return false;
1173   }
1174 
1175   if (graph != nullptr) {
1176     ArenaAllocator* allocator = graph->GetAllocator();
1177     HInstruction* oper;
1178     // Emit instructions for `a * i + b`. These are fine to overflow as they would have overflown
1179     // also if we had kept the loop.
1180     if (stride_value == 1) {
1181       oper = new (allocator) HAdd(type, opa, opb);
1182     } else if (stride_value == -1) {
1183       oper = new (graph->GetAllocator()) HSub(type, opb, opa);
1184     } else {
1185       HInstruction* mul = new (allocator) HMul(type, graph->GetConstant(type, stride_value), opa);
1186       oper = new (allocator) HAdd(type, Insert(block, mul), opb);
1187     }
1188     *result = Insert(block, oper);
1189   }
1190 
1191   if (*needs_taken_test) {
1192     if (TryGenerateTakenTest(context, loop, trip->op_b, graph, block, result, opb)) {
1193       *needs_taken_test = false;  // taken care of
1194     } else {
1195       return false;
1196     }
1197   }
1198 
1199   return true;
1200 }
1201 
GenerateLastValuePolynomial(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * info,HInductionVarAnalysis::InductionInfo * trip,HGraph * graph,HBasicBlock * block,HInstruction ** result) const1202 bool InductionVarRange::GenerateLastValuePolynomial(const HBasicBlock* context,
1203                                                     const HLoopInformation* loop,
1204                                                     HInductionVarAnalysis::InductionInfo* info,
1205                                                     HInductionVarAnalysis::InductionInfo* trip,
1206                                                     HGraph* graph,
1207                                                     HBasicBlock* block,
1208                                                     /*out*/HInstruction** result) const {
1209   DCHECK(info != nullptr);
1210   DCHECK_EQ(info->induction_class, HInductionVarAnalysis::kPolynomial);
1211   // Detect known coefficients and trip count (always taken).
1212   int64_t a = 0;
1213   int64_t b = 0;
1214   int64_t m = 0;
1215   if (IsConstant(context, loop, info->op_a->op_a, kExact, &a) &&
1216       IsConstant(context, loop, info->op_a->op_b, kExact, &b) &&
1217       IsConstant(context, loop, trip->op_a, kExact, &m) &&
1218       m >= 1) {
1219     // Evaluate bounds on sum_i=0^m-1(a * i + b) + c for known
1220     // maximum index value m as a * (m * (m-1)) / 2 + b * m + c.
1221     HInstruction* c = nullptr;
1222     if (GenerateCode(context,
1223                      loop,
1224                      info->op_b,
1225                      /*trip=*/ nullptr,
1226                      graph,
1227                      block,
1228                      /*is_min=*/ false,
1229                      graph ? &c : nullptr)) {
1230       if (graph != nullptr) {
1231         DataType::Type type = info->type;
1232         int64_t sum = a * ((m * (m - 1)) / 2) + b * m;
1233         if (type != DataType::Type::kInt64) {
1234           sum = static_cast<int32_t>(sum);  // okay to truncate
1235         }
1236         *result =
1237             Insert(block, new (graph->GetAllocator()) HAdd(type, graph->GetConstant(type, sum), c));
1238       }
1239       return true;
1240     }
1241   }
1242   return false;
1243 }
1244 
GenerateLastValueGeometric(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * info,HInductionVarAnalysis::InductionInfo * trip,HGraph * graph,HBasicBlock * block,HInstruction ** result) const1245 bool InductionVarRange::GenerateLastValueGeometric(const HBasicBlock* context,
1246                                                    const HLoopInformation* loop,
1247                                                    HInductionVarAnalysis::InductionInfo* info,
1248                                                    HInductionVarAnalysis::InductionInfo* trip,
1249                                                    HGraph* graph,
1250                                                    HBasicBlock* block,
1251                                                    /*out*/HInstruction** result) const {
1252   DCHECK(info != nullptr);
1253   DCHECK_EQ(info->induction_class, HInductionVarAnalysis::kGeometric);
1254   // Detect known base and trip count (always taken).
1255   int64_t f = 0;
1256   int64_t m = 0;
1257   if (IsInt64AndGet(info->fetch, &f) &&
1258       f >= 1 &&
1259       IsConstant(context, loop, trip->op_a, kExact, &m) &&
1260       m >= 1) {
1261     HInstruction* opa = nullptr;
1262     HInstruction* opb = nullptr;
1263     if (GenerateCode(
1264             context, loop, info->op_a, /*trip=*/ nullptr, graph, block, /*is_min=*/ false, &opa) &&
1265         GenerateCode(
1266             context, loop, info->op_b, /*trip=*/ nullptr, graph, block, /*is_min=*/ false, &opb)) {
1267       if (graph != nullptr) {
1268         DataType::Type type = info->type;
1269         // Compute f ^ m for known maximum index value m.
1270         bool overflow = false;
1271         int64_t fpow = IntPow(f, m, &overflow);
1272         if (info->operation == HInductionVarAnalysis::kDiv) {
1273           // For division, any overflow truncates to zero.
1274           if (overflow || (type != DataType::Type::kInt64 && !CanLongValueFitIntoInt(fpow))) {
1275             fpow = 0;
1276           }
1277         } else if (type != DataType::Type::kInt64) {
1278           // For multiplication, okay to truncate to required precision.
1279           DCHECK(info->operation == HInductionVarAnalysis::kMul);
1280           fpow = static_cast<int32_t>(fpow);
1281         }
1282         // Generate code.
1283         if (fpow == 0) {
1284           // Special case: repeated mul/div always yields zero.
1285           *result = graph->GetConstant(type, 0);
1286         } else {
1287           // Last value: a * f ^ m + b or a * f ^ -m + b.
1288           HInstruction* e = nullptr;
1289           ArenaAllocator* allocator = graph->GetAllocator();
1290           if (info->operation == HInductionVarAnalysis::kMul) {
1291             e = new (allocator) HMul(type, opa, graph->GetConstant(type, fpow));
1292           } else {
1293             e = new (allocator) HDiv(type, opa, graph->GetConstant(type, fpow), kNoDexPc);
1294           }
1295           *result = Insert(block, new (allocator) HAdd(type, Insert(block, e), opb));
1296         }
1297       }
1298       return true;
1299     }
1300   }
1301   return false;
1302 }
1303 
GenerateLastValueWrapAround(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * info,HInductionVarAnalysis::InductionInfo * trip,HGraph * graph,HBasicBlock * block,HInstruction ** result) const1304 bool InductionVarRange::GenerateLastValueWrapAround(const HBasicBlock* context,
1305                                                     const HLoopInformation* loop,
1306                                                     HInductionVarAnalysis::InductionInfo* info,
1307                                                     HInductionVarAnalysis::InductionInfo* trip,
1308                                                     HGraph* graph,
1309                                                     HBasicBlock* block,
1310                                                     /*out*/HInstruction** result) const {
1311   DCHECK(info != nullptr);
1312   DCHECK_EQ(info->induction_class, HInductionVarAnalysis::kWrapAround);
1313   // Count depth.
1314   int32_t depth = 0;
1315   for (; info->induction_class == HInductionVarAnalysis::kWrapAround;
1316        info = info->op_b, ++depth) {}
1317   // Handle wrap(x, wrap(.., y)) if trip count reaches an invariant at end.
1318   // TODO: generalize, but be careful to adjust the terminal.
1319   int64_t m = 0;
1320   if (info->induction_class == HInductionVarAnalysis::kInvariant &&
1321       IsConstant(context, loop, trip->op_a, kExact, &m) &&
1322       m >= depth) {
1323     return GenerateCode(
1324         context, loop, info, /*trip=*/ nullptr, graph, block, /*is_min=*/ false, result);
1325   }
1326   return false;
1327 }
1328 
GenerateLastValuePeriodic(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * info,HInductionVarAnalysis::InductionInfo * trip,HGraph * graph,HBasicBlock * block,HInstruction ** result,bool * needs_taken_test) const1329 bool InductionVarRange::GenerateLastValuePeriodic(const HBasicBlock* context,
1330                                                   const HLoopInformation* loop,
1331                                                   HInductionVarAnalysis::InductionInfo* info,
1332                                                   HInductionVarAnalysis::InductionInfo* trip,
1333                                                   HGraph* graph,
1334                                                   HBasicBlock* block,
1335                                                   /*out*/ HInstruction** result,
1336                                                   /*inout*/ bool* needs_taken_test) const {
1337   DCHECK(info != nullptr);
1338   DCHECK_EQ(info->induction_class, HInductionVarAnalysis::kPeriodic);
1339   // Count period and detect all-invariants.
1340   int64_t period = 1;
1341   bool all_invariants = true;
1342   HInductionVarAnalysis::InductionInfo* p = info;
1343   for (; p->induction_class == HInductionVarAnalysis::kPeriodic; p = p->op_b, ++period) {
1344     DCHECK_EQ(p->op_a->induction_class, HInductionVarAnalysis::kInvariant);
1345     if (p->op_a->operation != HInductionVarAnalysis::kFetch) {
1346       all_invariants = false;
1347     }
1348   }
1349   DCHECK_EQ(p->induction_class, HInductionVarAnalysis::kInvariant);
1350   if (p->operation != HInductionVarAnalysis::kFetch) {
1351     all_invariants = false;
1352   }
1353   // Don't rely on FP arithmetic to be precise, unless the full period
1354   // consist of pre-computed expressions only.
1355   if (info->type == DataType::Type::kFloat32 || info->type == DataType::Type::kFloat64) {
1356     if (!all_invariants) {
1357       return false;
1358     }
1359   }
1360   // Handle any periodic(x, periodic(.., y)) for known maximum index value m.
1361   int64_t m = 0;
1362   if (IsConstant(context, loop, trip->op_a, kExact, &m) && m >= 1) {
1363     int64_t li = m % period;
1364     for (int64_t i = 0; i < li; info = info->op_b, i++) {}
1365     if (info->induction_class == HInductionVarAnalysis::kPeriodic) {
1366       info = info->op_a;
1367     }
1368     return GenerateCode(
1369         context, loop, info, /*trip=*/ nullptr, graph, block, /*is_min=*/ false, result);
1370   }
1371   // Handle periodic(x, y) using even/odd-select on trip count. Enter trip count expression
1372   // directly to obtain the maximum index value t even if taken test is needed.
1373   HInstruction* x = nullptr;
1374   HInstruction* y = nullptr;
1375   HInstruction* t = nullptr;
1376 
1377   // Overflows when the stride is equal to `1` are fine since the periodicity is
1378   // `2` and the lowest bit is the same. Similar with `-1`.
1379   auto allow_potential_overflow = [&]() {
1380     int64_t stride_value = 0;
1381     return IsConstant(context, loop, trip->op_a->op_b, kExact, &stride_value) &&
1382            (stride_value == 1 || stride_value == -1);
1383   };
1384 
1385   if (period == 2 &&
1386       GenerateCode(context,
1387                    loop,
1388                    info->op_a,
1389                    /*trip=*/ nullptr,
1390                    graph,
1391                    block,
1392                    /*is_min=*/ false,
1393                    graph ? &x : nullptr) &&
1394       GenerateCode(context,
1395                    loop,
1396                    info->op_b,
1397                    /*trip=*/ nullptr,
1398                    graph,
1399                    block,
1400                    /*is_min=*/ false,
1401                    graph ? &y : nullptr) &&
1402       GenerateCode(context,
1403                    loop,
1404                    trip->op_a,
1405                    /*trip=*/ nullptr,
1406                    graph,
1407                    block,
1408                    /*is_min=*/ false,
1409                    graph ? &t : nullptr,
1410                    allow_potential_overflow())) {
1411     // During actual code generation (graph != nullptr), generate is_even ? x : y.
1412     if (graph != nullptr) {
1413       DataType::Type type = trip->type;
1414       ArenaAllocator* allocator = graph->GetAllocator();
1415       HInstruction* msk =
1416           Insert(block, new (allocator) HAnd(type, t, graph->GetConstant(type, 1)));
1417       HInstruction* is_even =
1418           Insert(block, new (allocator) HEqual(msk, graph->GetConstant(type, 0), kNoDexPc));
1419       *result = Insert(block, new (graph->GetAllocator()) HSelect(is_even, x, y, kNoDexPc));
1420     }
1421 
1422     if (*needs_taken_test) {
1423       if (TryGenerateTakenTest(context, loop, trip->op_b, graph, block, result, x)) {
1424         *needs_taken_test = false;  // taken care of
1425       } else {
1426         return false;
1427       }
1428     }
1429     return true;
1430   }
1431   return false;
1432 }
1433 
GenerateCode(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * info,HInductionVarAnalysis::InductionInfo * trip,HGraph * graph,HBasicBlock * block,bool is_min,HInstruction ** result,bool allow_potential_overflow) const1434 bool InductionVarRange::GenerateCode(const HBasicBlock* context,
1435                                      const HLoopInformation* loop,
1436                                      HInductionVarAnalysis::InductionInfo* info,
1437                                      HInductionVarAnalysis::InductionInfo* trip,
1438                                      HGraph* graph,  // when set, code is generated
1439                                      HBasicBlock* block,
1440                                      bool is_min,
1441                                      /*out*/ HInstruction** result,
1442                                      bool allow_potential_overflow) const {
1443   if (info != nullptr) {
1444     // If during codegen, the result is not needed (nullptr), simply return success.
1445     if (graph != nullptr && result == nullptr) {
1446       return true;
1447     }
1448     // Handle current operation.
1449     DataType::Type type = info->type;
1450     HInstruction* opa = nullptr;
1451     HInstruction* opb = nullptr;
1452     switch (info->induction_class) {
1453       case HInductionVarAnalysis::kInvariant:
1454         // Invariants (note that since invariants only have other invariants as
1455         // sub expressions, viz. no induction, there is no need to adjust is_min).
1456         switch (info->operation) {
1457           case HInductionVarAnalysis::kAdd:
1458           case HInductionVarAnalysis::kSub:
1459           case HInductionVarAnalysis::kMul:
1460           case HInductionVarAnalysis::kDiv:
1461           case HInductionVarAnalysis::kRem:
1462           case HInductionVarAnalysis::kXor:
1463           case HInductionVarAnalysis::kLT:
1464           case HInductionVarAnalysis::kLE:
1465           case HInductionVarAnalysis::kGT:
1466           case HInductionVarAnalysis::kGE:
1467             if (GenerateCode(context,
1468                              loop,
1469                              info->op_a,
1470                              trip,
1471                              graph,
1472                              block,
1473                              is_min,
1474                              &opa,
1475                              allow_potential_overflow) &&
1476                 GenerateCode(context,
1477                              loop,
1478                              info->op_b,
1479                              trip,
1480                              graph,
1481                              block,
1482                              is_min,
1483                              &opb,
1484                              allow_potential_overflow)) {
1485               // Check for potentially invalid operations.
1486               if (!allow_potential_overflow) {
1487                 switch (info->operation) {
1488                   case HInductionVarAnalysis::kAdd:
1489                     return TryGenerateAddWithoutOverflow(
1490                         context, loop, info, graph, opa, opb, result);
1491                   case HInductionVarAnalysis::kSub:
1492                     return TryGenerateSubWithoutOverflow(context, loop, info, graph, opa, result);
1493                   default:
1494                     // The rest of the operations are not relevant in the cases where
1495                     // `allow_potential_overflow` is false. Fall through to the allowed overflow
1496                     // case.
1497                     break;
1498                 }
1499               }
1500 
1501               // Overflows here are accepted.
1502               if (graph != nullptr) {
1503                 HInstruction* operation = nullptr;
1504                 switch (info->operation) {
1505                   case HInductionVarAnalysis::kAdd:
1506                     operation = new (graph->GetAllocator()) HAdd(type, opa, opb); break;
1507                   case HInductionVarAnalysis::kSub:
1508                     operation = new (graph->GetAllocator()) HSub(type, opa, opb); break;
1509                   case HInductionVarAnalysis::kMul:
1510                     operation = new (graph->GetAllocator()) HMul(type, opa, opb, kNoDexPc); break;
1511                   case HInductionVarAnalysis::kDiv:
1512                     operation = new (graph->GetAllocator()) HDiv(type, opa, opb, kNoDexPc); break;
1513                   case HInductionVarAnalysis::kRem:
1514                     operation = new (graph->GetAllocator()) HRem(type, opa, opb, kNoDexPc); break;
1515                   case HInductionVarAnalysis::kXor:
1516                     operation = new (graph->GetAllocator()) HXor(type, opa, opb); break;
1517                   case HInductionVarAnalysis::kLT:
1518                     operation = new (graph->GetAllocator()) HLessThan(opa, opb); break;
1519                   case HInductionVarAnalysis::kLE:
1520                     operation = new (graph->GetAllocator()) HLessThanOrEqual(opa, opb); break;
1521                   case HInductionVarAnalysis::kGT:
1522                     operation = new (graph->GetAllocator()) HGreaterThan(opa, opb); break;
1523                   case HInductionVarAnalysis::kGE:
1524                     operation = new (graph->GetAllocator()) HGreaterThanOrEqual(opa, opb); break;
1525                   default:
1526                     LOG(FATAL) << "unknown operation";
1527                 }
1528                 *result = Insert(block, operation);
1529               }
1530               return true;
1531             }
1532             break;
1533           case HInductionVarAnalysis::kNeg:
1534             if (GenerateCode(context,
1535                              loop,
1536                              info->op_b,
1537                              trip,
1538                              graph,
1539                              block,
1540                              !is_min,
1541                              &opb,
1542                              allow_potential_overflow)) {
1543               if (graph != nullptr) {
1544                 *result = Insert(block, new (graph->GetAllocator()) HNeg(type, opb));
1545               }
1546               return true;
1547             }
1548             break;
1549           case HInductionVarAnalysis::kFetch:
1550             if (graph != nullptr) {
1551               *result = info->fetch;  // already in HIR
1552             }
1553             return true;
1554           case HInductionVarAnalysis::kTripCountInLoop:
1555           case HInductionVarAnalysis::kTripCountInLoopUnsafe:
1556             if (UseFullTripCount(context, loop, is_min)) {
1557               // Generate the full trip count (do not subtract 1 as we do in loop body).
1558               return GenerateCode(context,
1559                                   loop,
1560                                   info->op_a,
1561                                   trip,
1562                                   graph,
1563                                   block,
1564                                   is_min,
1565                                   result,
1566                                   allow_potential_overflow);
1567             }
1568             FALLTHROUGH_INTENDED;
1569           case HInductionVarAnalysis::kTripCountInBody:
1570           case HInductionVarAnalysis::kTripCountInBodyUnsafe:
1571             if (is_min) {
1572               if (graph != nullptr) {
1573                 *result = graph->GetConstant(type, 0);
1574               }
1575               return true;
1576             } else if (IsContextInBody(context, loop) ||
1577                        (context == loop->GetHeader() && !allow_potential_overflow)) {
1578               if (GenerateCode(context,
1579                                loop,
1580                                info->op_a,
1581                                trip,
1582                                graph,
1583                                block,
1584                                is_min,
1585                                &opb,
1586                                allow_potential_overflow)) {
1587                 if (graph != nullptr) {
1588                   if (IsContextInBody(context, loop)) {
1589                     ArenaAllocator* allocator = graph->GetAllocator();
1590                     *result =
1591                         Insert(block, new (allocator) HSub(type, opb, graph->GetConstant(type, 1)));
1592                   } else {
1593                     // We want to generate the full trip count since we want the last value. This
1594                     // will be combined with an `is_taken` test so we don't want to subtract one.
1595                     DCHECK(context == loop->GetHeader());
1596                     // TODO(solanes): Remove the !allow_potential_overflow restriction and allow
1597                     // other parts e.g. BCE to take advantage of this.
1598                     DCHECK(!allow_potential_overflow);
1599                     *result = opb;
1600                   }
1601                 }
1602                 return true;
1603               }
1604             }
1605             break;
1606           case HInductionVarAnalysis::kNop:
1607             LOG(FATAL) << "unexpected invariant nop";
1608         }  // switch invariant operation
1609         break;
1610       case HInductionVarAnalysis::kLinear: {
1611         // Linear induction a * i + b, for normalized 0 <= i < TC. For ranges, this should
1612         // be restricted to a unit stride to avoid arithmetic wrap-around situations that
1613         // are harder to guard against. For a last value, requesting min/max based on any
1614         // known stride yields right value. Always avoid any narrowing linear induction or
1615         // any type mismatch between the linear induction and the trip count expression.
1616         // TODO: careful runtime type conversions could generalize this latter restriction.
1617         if (!HInductionVarAnalysis::IsNarrowingLinear(info) && trip->type == type) {
1618           int64_t stride_value = 0;
1619           if (IsConstant(context, loop, info->op_a, kExact, &stride_value) &&
1620               CanLongValueFitIntoInt(stride_value)) {
1621             const bool is_min_a = stride_value >= 0 ? is_min : !is_min;
1622             if (GenerateCode(context,
1623                              loop,
1624                              trip,
1625                              trip,
1626                              graph,
1627                              block,
1628                              is_min_a,
1629                              &opa,
1630                              allow_potential_overflow) &&
1631                 GenerateCode(context,
1632                              loop,
1633                              info->op_b,
1634                              trip,
1635                              graph,
1636                              block,
1637                              is_min,
1638                              &opb,
1639                              allow_potential_overflow)) {
1640               if (graph != nullptr) {
1641                 ArenaAllocator* allocator = graph->GetAllocator();
1642                 HInstruction* oper;
1643                 if (stride_value == 1) {
1644                   oper = new (allocator) HAdd(type, opa, opb);
1645                 } else if (stride_value == -1) {
1646                   oper = new (graph->GetAllocator()) HSub(type, opb, opa);
1647                 } else {
1648                   HInstruction* mul =
1649                       new (allocator) HMul(type, graph->GetConstant(type, stride_value), opa);
1650                   oper = new (allocator) HAdd(type, Insert(block, mul), opb);
1651                 }
1652                 *result = Insert(block, oper);
1653               }
1654               return true;
1655             }
1656           }
1657         }
1658         break;
1659       }
1660       case HInductionVarAnalysis::kPolynomial:
1661       case HInductionVarAnalysis::kGeometric:
1662         break;
1663       case HInductionVarAnalysis::kWrapAround:
1664       case HInductionVarAnalysis::kPeriodic: {
1665         // Wrap-around and periodic inductions are restricted to constants only, so that extreme
1666         // values are easy to test at runtime without complications of arithmetic wrap-around.
1667         Value extreme = GetVal(context, loop, info, trip, is_min);
1668         if (IsConstantValue(extreme)) {
1669           if (graph != nullptr) {
1670             *result = graph->GetConstant(type, extreme.b_constant);
1671           }
1672           return true;
1673         }
1674         break;
1675       }
1676     }  // switch induction class
1677   }
1678   return false;
1679 }
1680 
TryGenerateAddWithoutOverflow(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * info,HGraph * graph,HInstruction * opa,HInstruction * opb,HInstruction ** result) const1681 bool InductionVarRange::TryGenerateAddWithoutOverflow(const HBasicBlock* context,
1682                                                       const HLoopInformation* loop,
1683                                                       HInductionVarAnalysis::InductionInfo* info,
1684                                                       HGraph* graph,
1685                                                       /*in*/ HInstruction* opa,
1686                                                       /*in*/ HInstruction* opb,
1687                                                       /*out*/ HInstruction** result) const {
1688   // Calculate `a + b` making sure we can't overflow.
1689   int64_t val_a;
1690   const bool a_is_const = IsConstant(context, loop, info->op_a, kExact, &val_a);
1691   int64_t val_b;
1692   const bool b_is_const = IsConstant(context, loop, info->op_b, kExact, &val_b);
1693   if (a_is_const && b_is_const) {
1694     // Calculate `a + b` and use that. Note that even when the values are known,
1695     // their addition can still overflow.
1696     Value add_val = AddValue(Value(val_a), Value(val_b));
1697     if (add_val.is_known) {
1698       DCHECK(IsConstantValue(add_val));
1699       // Known value not overflowing.
1700       if (graph != nullptr) {
1701         *result = graph->GetConstant(info->type, add_val.b_constant);
1702       }
1703       return true;
1704     }
1705   }
1706 
1707   // When `a` is `0`, we can just use `b`.
1708   if (a_is_const && val_a == 0) {
1709     if (graph != nullptr) {
1710       *result = opb;
1711     }
1712     return true;
1713   }
1714 
1715   if (b_is_const && val_b == 0) {
1716     if (graph != nullptr) {
1717       *result = opa;
1718     }
1719     return true;
1720   }
1721 
1722   // Couldn't safely calculate the addition.
1723   return false;
1724 }
1725 
TryGenerateSubWithoutOverflow(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * info,HGraph * graph,HInstruction * opa,HInstruction ** result) const1726 bool InductionVarRange::TryGenerateSubWithoutOverflow(const HBasicBlock* context,
1727                                                       const HLoopInformation* loop,
1728                                                       HInductionVarAnalysis::InductionInfo* info,
1729                                                       HGraph* graph,
1730                                                       /*in*/ HInstruction* opa,
1731                                                       /*out*/ HInstruction** result) const {
1732   // Calculate `a - b` making sure we can't overflow.
1733   int64_t val_b;
1734   if (!IsConstant(context, loop, info->op_b, kExact, &val_b)) {
1735     // If b is unknown, a - b can potentially overflow for any value of a since b
1736     // can be Integer.MIN_VALUE.
1737     return false;
1738   }
1739 
1740   int64_t val_a;
1741   if (IsConstant(context, loop, info->op_a, kExact, &val_a)) {
1742     // Calculate `a - b` and use that. Note that even when the values are known,
1743     // their subtraction can still overflow.
1744     Value sub_val = SubValue(Value(val_a), Value(val_b));
1745     if (sub_val.is_known) {
1746       DCHECK(IsConstantValue(sub_val));
1747       // Known value not overflowing.
1748       if (graph != nullptr) {
1749         *result = graph->GetConstant(info->type, sub_val.b_constant);
1750       }
1751       return true;
1752     }
1753   }
1754 
1755   // When `b` is `0`, we can just use `a`.
1756   if (val_b == 0) {
1757     if (graph != nullptr) {
1758       *result = opa;
1759     }
1760     return true;
1761   }
1762 
1763   // Couldn't safely calculate the subtraction.
1764   return false;
1765 }
1766 
TryGenerateTakenTest(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * info,HGraph * graph,HBasicBlock * block,HInstruction ** result,HInstruction * not_taken_result) const1767 bool InductionVarRange::TryGenerateTakenTest(const HBasicBlock* context,
1768                                              const HLoopInformation* loop,
1769                                              HInductionVarAnalysis::InductionInfo* info,
1770                                              HGraph* graph,
1771                                              HBasicBlock* block,
1772                                              /*inout*/ HInstruction** result,
1773                                              /*inout*/ HInstruction* not_taken_result) const {
1774   HInstruction* is_taken = nullptr;
1775   if (GenerateCode(context,
1776                    loop,
1777                    info,
1778                    /*trip=*/nullptr,
1779                    graph,
1780                    block,
1781                    /*is_min=*/false,
1782                    graph != nullptr ? &is_taken : nullptr)) {
1783     if (graph != nullptr) {
1784       ArenaAllocator* allocator = graph->GetAllocator();
1785       *result =
1786           Insert(block, new (allocator) HSelect(is_taken, *result, not_taken_result, kNoDexPc));
1787     }
1788     return true;
1789   } else {
1790     return false;
1791   }
1792 }
1793 
ReplaceInduction(HInductionVarAnalysis::InductionInfo * info,HInstruction * fetch,HInstruction * replacement)1794 void InductionVarRange::ReplaceInduction(HInductionVarAnalysis::InductionInfo* info,
1795                                          HInstruction* fetch,
1796                                          HInstruction* replacement) {
1797   if (info != nullptr) {
1798     if (info->induction_class == HInductionVarAnalysis::kInvariant &&
1799         info->operation == HInductionVarAnalysis::kFetch &&
1800         info->fetch == fetch) {
1801       info->fetch = replacement;
1802     }
1803     ReplaceInduction(info->op_a, fetch, replacement);
1804     ReplaceInduction(info->op_b, fetch, replacement);
1805   }
1806 }
1807 
1808 }  // namespace art
1809