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