Home | History | Annotate | Download | only in optimizing
      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 
     21 namespace art {
     22 
     23 /** Returns true if 64-bit constant fits in 32-bit constant. */
     24 static bool CanLongValueFitIntoInt(int64_t c) {
     25   return std::numeric_limits<int32_t>::min() <= c && c <= std::numeric_limits<int32_t>::max();
     26 }
     27 
     28 /** Returns true if 32-bit addition can be done safely. */
     29 static bool IsSafeAdd(int32_t c1, int32_t c2) {
     30   return CanLongValueFitIntoInt(static_cast<int64_t>(c1) + static_cast<int64_t>(c2));
     31 }
     32 
     33 /** Returns true if 32-bit subtraction can be done safely. */
     34 static bool IsSafeSub(int32_t c1, int32_t c2) {
     35   return CanLongValueFitIntoInt(static_cast<int64_t>(c1) - static_cast<int64_t>(c2));
     36 }
     37 
     38 /** Returns true if 32-bit multiplication can be done safely. */
     39 static bool IsSafeMul(int32_t c1, int32_t c2) {
     40   return CanLongValueFitIntoInt(static_cast<int64_t>(c1) * static_cast<int64_t>(c2));
     41 }
     42 
     43 /** Returns true if 32-bit division can be done safely. */
     44 static bool IsSafeDiv(int32_t c1, int32_t c2) {
     45   return c2 != 0 && CanLongValueFitIntoInt(static_cast<int64_t>(c1) / static_cast<int64_t>(c2));
     46 }
     47 
     48 /** Returns true for 32/64-bit constant instruction. */
     49 static bool IsIntAndGet(HInstruction* instruction, int64_t* value) {
     50   if (instruction->IsIntConstant()) {
     51     *value = instruction->AsIntConstant()->GetValue();
     52     return true;
     53   } else if (instruction->IsLongConstant()) {
     54     *value = instruction->AsLongConstant()->GetValue();
     55     return true;
     56   }
     57   return false;
     58 }
     59 
     60 /**
     61  * An upper bound a * (length / a) + b, where a >= 1, can be conservatively rewritten as length + b
     62  * because length >= 0 is true. This makes it more likely the bound is useful to clients.
     63  */
     64 static InductionVarRange::Value SimplifyMax(InductionVarRange::Value v) {
     65   int64_t value;
     66   if (v.is_known &&
     67       v.a_constant >= 1 &&
     68       v.instruction->IsDiv() &&
     69       v.instruction->InputAt(0)->IsArrayLength() &&
     70       IsIntAndGet(v.instruction->InputAt(1), &value) && v.a_constant == value) {
     71     return InductionVarRange::Value(v.instruction->InputAt(0), 1, v.b_constant);
     72   }
     73   return v;
     74 }
     75 
     76 /**
     77  * Corrects a value for type to account for arithmetic wrap-around in lower precision.
     78  */
     79 static InductionVarRange::Value CorrectForType(InductionVarRange::Value v, Primitive::Type type) {
     80   switch (type) {
     81     case Primitive::kPrimShort:
     82     case Primitive::kPrimChar:
     83     case Primitive::kPrimByte: {
     84       // Constants within range only.
     85       // TODO: maybe some room for improvement, like allowing widening conversions
     86       const int32_t min = Primitive::MinValueOfIntegralType(type);
     87       const int32_t max = Primitive::MaxValueOfIntegralType(type);
     88       return (v.is_known && v.a_constant == 0 && min <= v.b_constant && v.b_constant <= max)
     89           ? v
     90           : InductionVarRange::Value();
     91     }
     92     default:
     93       // At int or higher.
     94       return v;
     95   }
     96 }
     97 
     98 /** Helper method to test for a constant value. */
     99 static bool IsConstantValue(InductionVarRange::Value v) {
    100   return v.is_known && v.a_constant == 0;
    101 }
    102 
    103 /** Helper method to test for same constant value. */
    104 static bool IsSameConstantValue(InductionVarRange::Value v1, InductionVarRange::Value v2) {
    105   return IsConstantValue(v1) && IsConstantValue(v2) && v1.b_constant == v2.b_constant;
    106 }
    107 
    108 /** Helper method to insert an instruction. */
    109 static HInstruction* Insert(HBasicBlock* block, HInstruction* instruction) {
    110   DCHECK(block != nullptr);
    111   DCHECK(block->GetLastInstruction() != nullptr) << block->GetBlockId();
    112   DCHECK(instruction != nullptr);
    113   block->InsertInstructionBefore(instruction, block->GetLastInstruction());
    114   return instruction;
    115 }
    116 
    117 //
    118 // Public class methods.
    119 //
    120 
    121 InductionVarRange::InductionVarRange(HInductionVarAnalysis* induction_analysis)
    122     : induction_analysis_(induction_analysis) {
    123   DCHECK(induction_analysis != nullptr);
    124 }
    125 
    126 bool InductionVarRange::GetInductionRange(HInstruction* context,
    127                                           HInstruction* instruction,
    128                                           /*out*/Value* min_val,
    129                                           /*out*/Value* max_val,
    130                                           /*out*/bool* needs_finite_test) {
    131   HLoopInformation* loop = context->GetBlock()->GetLoopInformation();  // closest enveloping loop
    132   if (loop == nullptr) {
    133     return false;  // no loop
    134   }
    135   HInductionVarAnalysis::InductionInfo* info = induction_analysis_->LookupInfo(loop, instruction);
    136   if (info == nullptr) {
    137     return false;  // no induction information
    138   }
    139   // Type int or lower (this is not too restrictive since intended clients, like
    140   // bounds check elimination, will have truncated higher precision induction
    141   // at their use point already).
    142   switch (info->type) {
    143     case Primitive::kPrimInt:
    144     case Primitive::kPrimShort:
    145     case Primitive::kPrimChar:
    146     case Primitive::kPrimByte:
    147       break;
    148     default:
    149       return false;
    150   }
    151   // Set up loop information.
    152   HBasicBlock* header = loop->GetHeader();
    153   bool in_body = context->GetBlock() != header;
    154   HInductionVarAnalysis::InductionInfo* trip =
    155       induction_analysis_->LookupInfo(loop, header->GetLastInstruction());
    156   // Find range.
    157   *min_val = GetVal(info, trip, in_body, /* is_min */ true);
    158   *max_val = SimplifyMax(GetVal(info, trip, in_body, /* is_min */ false));
    159   *needs_finite_test = NeedsTripCount(info) && IsUnsafeTripCount(trip);
    160   return true;
    161 }
    162 
    163 bool InductionVarRange::RefineOuter(/*in-out*/ Value* min_val,
    164                                     /*in-out*/ Value* max_val) const {
    165   if (min_val->instruction != nullptr || max_val->instruction != nullptr) {
    166     Value v1_min = RefineOuter(*min_val, /* is_min */ true);
    167     Value v2_max = RefineOuter(*max_val, /* is_min */ false);
    168     // The refined range is safe if both sides refine the same instruction. Otherwise, since two
    169     // different ranges are combined, the new refined range is safe to pass back to the client if
    170     // the extremes of the computed ranges ensure no arithmetic wrap-around anomalies occur.
    171     if (min_val->instruction != max_val->instruction) {
    172       Value v1_max = RefineOuter(*min_val, /* is_min */ false);
    173       Value v2_min = RefineOuter(*max_val, /* is_min */ true);
    174       if (!IsConstantValue(v1_max) ||
    175           !IsConstantValue(v2_min) ||
    176           v1_max.b_constant > v2_min.b_constant) {
    177         return false;
    178       }
    179     }
    180     // Did something change?
    181     if (v1_min.instruction != min_val->instruction || v2_max.instruction != max_val->instruction) {
    182       *min_val = v1_min;
    183       *max_val = v2_max;
    184       return true;
    185     }
    186   }
    187   return false;
    188 }
    189 
    190 bool InductionVarRange::CanGenerateCode(HInstruction* context,
    191                                         HInstruction* instruction,
    192                                         /*out*/bool* needs_finite_test,
    193                                         /*out*/bool* needs_taken_test) {
    194   return GenerateCode(context,
    195                       instruction,
    196                       nullptr, nullptr, nullptr, nullptr, nullptr,  // nothing generated yet
    197                       needs_finite_test,
    198                       needs_taken_test);
    199 }
    200 
    201 void InductionVarRange::GenerateRangeCode(HInstruction* context,
    202                                           HInstruction* instruction,
    203                                           HGraph* graph,
    204                                           HBasicBlock* block,
    205                                           /*out*/HInstruction** lower,
    206                                           /*out*/HInstruction** upper) {
    207   bool b1, b2;  // unused
    208   if (!GenerateCode(context, instruction, graph, block, lower, upper, nullptr, &b1, &b2)) {
    209     LOG(FATAL) << "Failed precondition: GenerateCode()";
    210   }
    211 }
    212 
    213 void InductionVarRange::GenerateTakenTest(HInstruction* context,
    214                                           HGraph* graph,
    215                                           HBasicBlock* block,
    216                                           /*out*/HInstruction** taken_test) {
    217   bool b1, b2;  // unused
    218   if (!GenerateCode(context, context, graph, block, nullptr, nullptr, taken_test, &b1, &b2)) {
    219     LOG(FATAL) << "Failed precondition: GenerateCode()";
    220   }
    221 }
    222 
    223 //
    224 // Private class methods.
    225 //
    226 
    227 bool InductionVarRange::IsConstant(HInductionVarAnalysis::InductionInfo* info,
    228                                    ConstantRequest request,
    229                                    /*out*/ int64_t *value) const {
    230   if (info != nullptr) {
    231     // A direct 32-bit or 64-bit constant fetch. This immediately satisfies
    232     // any of the three requests (kExact, kAtMost, and KAtLeast).
    233     if (info->induction_class == HInductionVarAnalysis::kInvariant &&
    234         info->operation == HInductionVarAnalysis::kFetch) {
    235       if (IsIntAndGet(info->fetch, value)) {
    236         return true;
    237       }
    238     }
    239     // Try range analysis while traversing outward on loops.
    240     bool in_body = true;  // no known trip count
    241     Value v_min = GetVal(info, nullptr, in_body, /* is_min */ true);
    242     Value v_max = GetVal(info, nullptr, in_body, /* is_min */ false);
    243     do {
    244       // Make sure *both* extremes are known to avoid arithmetic wrap-around anomalies.
    245       if (IsConstantValue(v_min) && IsConstantValue(v_max) && v_min.b_constant <= v_max.b_constant) {
    246         if ((request == kExact && v_min.b_constant == v_max.b_constant) || request == kAtMost) {
    247           *value = v_max.b_constant;
    248           return true;
    249         } else if (request == kAtLeast) {
    250           *value = v_min.b_constant;
    251           return true;
    252         }
    253       }
    254     } while (RefineOuter(&v_min, &v_max));
    255     // Exploit array length + c >= c, with c <= 0 to avoid arithmetic wrap-around anomalies
    256     // (e.g. array length == maxint and c == 1 would yield minint).
    257     if (request == kAtLeast) {
    258       if (v_min.a_constant == 1 && v_min.b_constant <= 0 && v_min.instruction->IsArrayLength()) {
    259         *value = v_min.b_constant;
    260         return true;
    261       }
    262     }
    263   }
    264   return false;
    265 }
    266 
    267 bool InductionVarRange::NeedsTripCount(HInductionVarAnalysis::InductionInfo* info) const {
    268   if (info != nullptr) {
    269     if (info->induction_class == HInductionVarAnalysis::kLinear) {
    270       return true;
    271     } else if (info->induction_class == HInductionVarAnalysis::kWrapAround) {
    272       return NeedsTripCount(info->op_b);
    273     }
    274   }
    275   return false;
    276 }
    277 
    278 bool InductionVarRange::IsBodyTripCount(HInductionVarAnalysis::InductionInfo* trip) const {
    279   if (trip != nullptr) {
    280     if (trip->induction_class == HInductionVarAnalysis::kInvariant) {
    281       return trip->operation == HInductionVarAnalysis::kTripCountInBody ||
    282              trip->operation == HInductionVarAnalysis::kTripCountInBodyUnsafe;
    283     }
    284   }
    285   return false;
    286 }
    287 
    288 bool InductionVarRange::IsUnsafeTripCount(HInductionVarAnalysis::InductionInfo* trip) const {
    289   if (trip != nullptr) {
    290     if (trip->induction_class == HInductionVarAnalysis::kInvariant) {
    291       return trip->operation == HInductionVarAnalysis::kTripCountInBodyUnsafe ||
    292              trip->operation == HInductionVarAnalysis::kTripCountInLoopUnsafe;
    293     }
    294   }
    295   return false;
    296 }
    297 
    298 InductionVarRange::Value InductionVarRange::GetLinear(HInductionVarAnalysis::InductionInfo* info,
    299                                                       HInductionVarAnalysis::InductionInfo* trip,
    300                                                       bool in_body,
    301                                                       bool is_min) const {
    302   // Detect common situation where an offset inside the trip count cancels out during range
    303   // analysis (finding max a * (TC - 1) + OFFSET for a == 1 and TC = UPPER - OFFSET or finding
    304   // min a * (TC - 1) + OFFSET for a == -1 and TC = OFFSET - UPPER) to avoid losing information
    305   // with intermediate results that only incorporate single instructions.
    306   if (trip != nullptr) {
    307     HInductionVarAnalysis::InductionInfo* trip_expr = trip->op_a;
    308     if (trip_expr->operation == HInductionVarAnalysis::kSub) {
    309       int64_t stride_value = 0;
    310       if (IsConstant(info->op_a, kExact, &stride_value)) {
    311         if (!is_min && stride_value == 1) {
    312           // Test original trip's negative operand (trip_expr->op_b) against offset of induction.
    313           if (HInductionVarAnalysis::InductionEqual(trip_expr->op_b, info->op_b)) {
    314             // Analyze cancelled trip with just the positive operand (trip_expr->op_a).
    315             HInductionVarAnalysis::InductionInfo cancelled_trip(
    316                 trip->induction_class,
    317                 trip->operation,
    318                 trip_expr->op_a,
    319                 trip->op_b,
    320                 nullptr,
    321                 trip->type);
    322             return GetVal(&cancelled_trip, trip, in_body, is_min);
    323           }
    324         } else if (is_min && stride_value == -1) {
    325           // Test original trip's positive operand (trip_expr->op_a) against offset of induction.
    326           if (HInductionVarAnalysis::InductionEqual(trip_expr->op_a, info->op_b)) {
    327             // Analyze cancelled trip with just the negative operand (trip_expr->op_b).
    328             HInductionVarAnalysis::InductionInfo neg(
    329                 HInductionVarAnalysis::kInvariant,
    330                 HInductionVarAnalysis::kNeg,
    331                 nullptr,
    332                 trip_expr->op_b,
    333                 nullptr,
    334                 trip->type);
    335             HInductionVarAnalysis::InductionInfo cancelled_trip(
    336                 trip->induction_class, trip->operation, &neg, trip->op_b, nullptr, trip->type);
    337             return SubValue(Value(0), GetVal(&cancelled_trip, trip, in_body, !is_min));
    338           }
    339         }
    340       }
    341     }
    342   }
    343   // General rule of linear induction a * i + b, for normalized 0 <= i < TC.
    344   return AddValue(GetMul(info->op_a, trip, trip, in_body, is_min),
    345                   GetVal(info->op_b, trip, in_body, is_min));
    346 }
    347 
    348 InductionVarRange::Value InductionVarRange::GetFetch(HInstruction* instruction,
    349                                                      HInductionVarAnalysis::InductionInfo* trip,
    350                                                      bool in_body,
    351                                                      bool is_min) const {
    352   // Detect constants and chase the fetch a bit deeper into the HIR tree, so that it becomes
    353   // more likely range analysis will compare the same instructions as terminal nodes.
    354   int64_t value;
    355   if (IsIntAndGet(instruction, &value) && CanLongValueFitIntoInt(value))  {
    356     return Value(static_cast<int32_t>(value));
    357   } else if (instruction->IsAdd()) {
    358     if (IsIntAndGet(instruction->InputAt(0), &value) && CanLongValueFitIntoInt(value)) {
    359       return AddValue(Value(static_cast<int32_t>(value)),
    360                       GetFetch(instruction->InputAt(1), trip, in_body, is_min));
    361     } else if (IsIntAndGet(instruction->InputAt(1), &value) && CanLongValueFitIntoInt(value)) {
    362       return AddValue(GetFetch(instruction->InputAt(0), trip, in_body, is_min),
    363                       Value(static_cast<int32_t>(value)));
    364     }
    365   } else if (instruction->IsArrayLength() && instruction->InputAt(0)->IsNewArray()) {
    366     return GetFetch(instruction->InputAt(0)->InputAt(0), trip, in_body, is_min);
    367   } else if (instruction->IsTypeConversion()) {
    368     // Since analysis is 32-bit (or narrower) we allow a widening along the path.
    369     if (instruction->AsTypeConversion()->GetInputType() == Primitive::kPrimInt &&
    370         instruction->AsTypeConversion()->GetResultType() == Primitive::kPrimLong) {
    371       return GetFetch(instruction->InputAt(0), trip, in_body, is_min);
    372     }
    373   } else if (is_min) {
    374     // Special case for finding minimum: minimum of trip-count in loop-body is 1.
    375     if (trip != nullptr && in_body && instruction == trip->op_a->fetch) {
    376       return Value(1);
    377     }
    378   }
    379   return Value(instruction, 1, 0);
    380 }
    381 
    382 InductionVarRange::Value InductionVarRange::GetVal(HInductionVarAnalysis::InductionInfo* info,
    383                                                    HInductionVarAnalysis::InductionInfo* trip,
    384                                                    bool in_body,
    385                                                    bool is_min) const {
    386   if (info != nullptr) {
    387     switch (info->induction_class) {
    388       case HInductionVarAnalysis::kInvariant:
    389         // Invariants.
    390         switch (info->operation) {
    391           case HInductionVarAnalysis::kAdd:
    392             return AddValue(GetVal(info->op_a, trip, in_body, is_min),
    393                             GetVal(info->op_b, trip, in_body, is_min));
    394           case HInductionVarAnalysis::kSub:  // second reversed!
    395             return SubValue(GetVal(info->op_a, trip, in_body, is_min),
    396                             GetVal(info->op_b, trip, in_body, !is_min));
    397           case HInductionVarAnalysis::kNeg:  // second reversed!
    398             return SubValue(Value(0),
    399                             GetVal(info->op_b, trip, in_body, !is_min));
    400           case HInductionVarAnalysis::kMul:
    401             return GetMul(info->op_a, info->op_b, trip, in_body, is_min);
    402           case HInductionVarAnalysis::kDiv:
    403             return GetDiv(info->op_a, info->op_b, trip, in_body, is_min);
    404           case HInductionVarAnalysis::kFetch:
    405             return GetFetch(info->fetch, trip, in_body, is_min);
    406           case HInductionVarAnalysis::kTripCountInLoop:
    407           case HInductionVarAnalysis::kTripCountInLoopUnsafe:
    408             if (!in_body && !is_min) {  // one extra!
    409               return GetVal(info->op_a, trip, in_body, is_min);
    410             }
    411             FALLTHROUGH_INTENDED;
    412           case HInductionVarAnalysis::kTripCountInBody:
    413           case HInductionVarAnalysis::kTripCountInBodyUnsafe:
    414             if (is_min) {
    415               return Value(0);
    416             } else if (in_body) {
    417               return SubValue(GetVal(info->op_a, trip, in_body, is_min), Value(1));
    418             }
    419             break;
    420           default:
    421             break;
    422         }
    423         break;
    424       case HInductionVarAnalysis::kLinear: {
    425         return CorrectForType(GetLinear(info, trip, in_body, is_min), info->type);
    426       }
    427       case HInductionVarAnalysis::kWrapAround:
    428       case HInductionVarAnalysis::kPeriodic:
    429         return MergeVal(GetVal(info->op_a, trip, in_body, is_min),
    430                         GetVal(info->op_b, trip, in_body, is_min), is_min);
    431     }
    432   }
    433   return Value();
    434 }
    435 
    436 InductionVarRange::Value InductionVarRange::GetMul(HInductionVarAnalysis::InductionInfo* info1,
    437                                                    HInductionVarAnalysis::InductionInfo* info2,
    438                                                    HInductionVarAnalysis::InductionInfo* trip,
    439                                                    bool in_body,
    440                                                    bool is_min) const {
    441   Value v1_min = GetVal(info1, trip, in_body, /* is_min */ true);
    442   Value v1_max = GetVal(info1, trip, in_body, /* is_min */ false);
    443   Value v2_min = GetVal(info2, trip, in_body, /* is_min */ true);
    444   Value v2_max = GetVal(info2, trip, in_body, /* is_min */ false);
    445   // Try to refine first operand.
    446   if (!IsConstantValue(v1_min) && !IsConstantValue(v1_max)) {
    447     RefineOuter(&v1_min, &v1_max);
    448   }
    449   // Constant times range.
    450   if (IsSameConstantValue(v1_min, v1_max)) {
    451     return MulRangeAndConstant(v2_min, v2_max, v1_min, is_min);
    452   } else if (IsSameConstantValue(v2_min, v2_max)) {
    453     return MulRangeAndConstant(v1_min, v1_max, v2_min, is_min);
    454   }
    455   // Positive range vs. positive or negative range.
    456   if (IsConstantValue(v1_min) && v1_min.b_constant >= 0) {
    457     if (IsConstantValue(v2_min) && v2_min.b_constant >= 0) {
    458       return is_min ? MulValue(v1_min, v2_min) : MulValue(v1_max, v2_max);
    459     } else if (IsConstantValue(v2_max) && v2_max.b_constant <= 0) {
    460       return is_min ? MulValue(v1_max, v2_min) : MulValue(v1_min, v2_max);
    461     }
    462   }
    463   // Negative range vs. positive or negative range.
    464   if (IsConstantValue(v1_max) && v1_max.b_constant <= 0) {
    465     if (IsConstantValue(v2_min) && v2_min.b_constant >= 0) {
    466       return is_min ? MulValue(v1_min, v2_max) : MulValue(v1_max, v2_min);
    467     } else if (IsConstantValue(v2_max) && v2_max.b_constant <= 0) {
    468       return is_min ? MulValue(v1_max, v2_max) : MulValue(v1_min, v2_min);
    469     }
    470   }
    471   return Value();
    472 }
    473 
    474 InductionVarRange::Value InductionVarRange::GetDiv(HInductionVarAnalysis::InductionInfo* info1,
    475                                                    HInductionVarAnalysis::InductionInfo* info2,
    476                                                    HInductionVarAnalysis::InductionInfo* trip,
    477                                                    bool in_body,
    478                                                    bool is_min) const {
    479   Value v1_min = GetVal(info1, trip, in_body, /* is_min */ true);
    480   Value v1_max = GetVal(info1, trip, in_body, /* is_min */ false);
    481   Value v2_min = GetVal(info2, trip, in_body, /* is_min */ true);
    482   Value v2_max = GetVal(info2, trip, in_body, /* is_min */ false);
    483   // Range divided by constant.
    484   if (IsSameConstantValue(v2_min, v2_max)) {
    485     return DivRangeAndConstant(v1_min, v1_max, v2_min, is_min);
    486   }
    487   // Positive range vs. positive or negative range.
    488   if (IsConstantValue(v1_min) && v1_min.b_constant >= 0) {
    489     if (IsConstantValue(v2_min) && v2_min.b_constant >= 0) {
    490       return is_min ? DivValue(v1_min, v2_max) : DivValue(v1_max, v2_min);
    491     } else if (IsConstantValue(v2_max) && v2_max.b_constant <= 0) {
    492       return is_min ? DivValue(v1_max, v2_max) : DivValue(v1_min, v2_min);
    493     }
    494   }
    495   // Negative range vs. positive or negative range.
    496   if (IsConstantValue(v1_max) && v1_max.b_constant <= 0) {
    497     if (IsConstantValue(v2_min) && v2_min.b_constant >= 0) {
    498       return is_min ? DivValue(v1_min, v2_min) : DivValue(v1_max, v2_max);
    499     } else if (IsConstantValue(v2_max) && v2_max.b_constant <= 0) {
    500       return is_min ? DivValue(v1_max, v2_min) : DivValue(v1_min, v2_max);
    501     }
    502   }
    503   return Value();
    504 }
    505 
    506 InductionVarRange::Value InductionVarRange::MulRangeAndConstant(Value v_min,
    507                                                                 Value v_max,
    508                                                                 Value c,
    509                                                                 bool is_min) const {
    510   return is_min == (c.b_constant >= 0) ? MulValue(v_min, c) : MulValue(v_max, c);
    511 }
    512 
    513 InductionVarRange::Value InductionVarRange::DivRangeAndConstant(Value v_min,
    514                                                                 Value v_max,
    515                                                                 Value c,
    516                                                                 bool is_min) const {
    517   return is_min == (c.b_constant >= 0) ? DivValue(v_min, c) : DivValue(v_max, c);
    518 }
    519 
    520 InductionVarRange::Value InductionVarRange::AddValue(Value v1, Value v2) const {
    521   if (v1.is_known && v2.is_known && IsSafeAdd(v1.b_constant, v2.b_constant)) {
    522     const int32_t b = v1.b_constant + v2.b_constant;
    523     if (v1.a_constant == 0) {
    524       return Value(v2.instruction, v2.a_constant, b);
    525     } else if (v2.a_constant == 0) {
    526       return Value(v1.instruction, v1.a_constant, b);
    527     } else if (v1.instruction == v2.instruction && IsSafeAdd(v1.a_constant, v2.a_constant)) {
    528       return Value(v1.instruction, v1.a_constant + v2.a_constant, b);
    529     }
    530   }
    531   return Value();
    532 }
    533 
    534 InductionVarRange::Value InductionVarRange::SubValue(Value v1, Value v2) const {
    535   if (v1.is_known && v2.is_known && IsSafeSub(v1.b_constant, v2.b_constant)) {
    536     const int32_t b = v1.b_constant - v2.b_constant;
    537     if (v1.a_constant == 0 && IsSafeSub(0, v2.a_constant)) {
    538       return Value(v2.instruction, -v2.a_constant, b);
    539     } else if (v2.a_constant == 0) {
    540       return Value(v1.instruction, v1.a_constant, b);
    541     } else if (v1.instruction == v2.instruction && IsSafeSub(v1.a_constant, v2.a_constant)) {
    542       return Value(v1.instruction, v1.a_constant - v2.a_constant, b);
    543     }
    544   }
    545   return Value();
    546 }
    547 
    548 InductionVarRange::Value InductionVarRange::MulValue(Value v1, Value v2) const {
    549   if (v1.is_known && v2.is_known) {
    550     if (v1.a_constant == 0) {
    551       if (IsSafeMul(v1.b_constant, v2.a_constant) && IsSafeMul(v1.b_constant, v2.b_constant)) {
    552         return Value(v2.instruction, v1.b_constant * v2.a_constant, v1.b_constant * v2.b_constant);
    553       }
    554     } else if (v2.a_constant == 0) {
    555       if (IsSafeMul(v1.a_constant, v2.b_constant) && IsSafeMul(v1.b_constant, v2.b_constant)) {
    556         return Value(v1.instruction, v1.a_constant * v2.b_constant, v1.b_constant * v2.b_constant);
    557       }
    558     }
    559   }
    560   return Value();
    561 }
    562 
    563 InductionVarRange::Value InductionVarRange::DivValue(Value v1, Value v2) const {
    564   if (v1.is_known && v2.is_known && v1.a_constant == 0 && v2.a_constant == 0) {
    565     if (IsSafeDiv(v1.b_constant, v2.b_constant)) {
    566       return Value(v1.b_constant / v2.b_constant);
    567     }
    568   }
    569   return Value();
    570 }
    571 
    572 InductionVarRange::Value InductionVarRange::MergeVal(Value v1, Value v2, bool is_min) const {
    573   if (v1.is_known && v2.is_known) {
    574     if (v1.instruction == v2.instruction && v1.a_constant == v2.a_constant) {
    575       return Value(v1.instruction, v1.a_constant,
    576                    is_min ? std::min(v1.b_constant, v2.b_constant)
    577                           : std::max(v1.b_constant, v2.b_constant));
    578     }
    579   }
    580   return Value();
    581 }
    582 
    583 InductionVarRange::Value InductionVarRange::RefineOuter(Value v, bool is_min) const {
    584   if (v.instruction == nullptr) {
    585     return v;  // nothing to refine
    586   }
    587   HLoopInformation* loop =
    588       v.instruction->GetBlock()->GetLoopInformation();  // closest enveloping loop
    589   if (loop == nullptr) {
    590     return v;  // no loop
    591   }
    592   HInductionVarAnalysis::InductionInfo* info = induction_analysis_->LookupInfo(loop, v.instruction);
    593   if (info == nullptr) {
    594     return v;  // no induction information
    595   }
    596   // Set up loop information.
    597   HBasicBlock* header = loop->GetHeader();
    598   bool in_body = true;  // inner always in more outer
    599   HInductionVarAnalysis::InductionInfo* trip =
    600       induction_analysis_->LookupInfo(loop, header->GetLastInstruction());
    601   // Try to refine "a x instruction + b" with outer loop range information on instruction.
    602   return AddValue(MulValue(Value(v.a_constant), GetVal(info, trip, in_body, is_min)), Value(v.b_constant));
    603 }
    604 
    605 bool InductionVarRange::GenerateCode(HInstruction* context,
    606                                      HInstruction* instruction,
    607                                      HGraph* graph,
    608                                      HBasicBlock* block,
    609                                      /*out*/HInstruction** lower,
    610                                      /*out*/HInstruction** upper,
    611                                      /*out*/HInstruction** taken_test,
    612                                      /*out*/bool* needs_finite_test,
    613                                      /*out*/bool* needs_taken_test) const {
    614   HLoopInformation* loop = context->GetBlock()->GetLoopInformation();  // closest enveloping loop
    615   if (loop == nullptr) {
    616     return false;  // no loop
    617   }
    618   HInductionVarAnalysis::InductionInfo* info = induction_analysis_->LookupInfo(loop, instruction);
    619   if (info == nullptr) {
    620     return false;  // no induction information
    621   }
    622   // Set up loop information.
    623   HBasicBlock* header = loop->GetHeader();
    624   bool in_body = context->GetBlock() != header;
    625   HInductionVarAnalysis::InductionInfo* trip =
    626       induction_analysis_->LookupInfo(loop, header->GetLastInstruction());
    627   if (trip == nullptr) {
    628     return false;  // codegen relies on trip count
    629   }
    630   // Determine what tests are needed. A finite test is needed if the evaluation code uses the
    631   // trip-count and the loop maybe unsafe (because in such cases, the index could "overshoot"
    632   // the computed range). A taken test is needed for any unknown trip-count, even if evaluation
    633   // code does not use the trip-count explicitly (since there could be an implicit relation
    634   // between e.g. an invariant subscript and a not-taken condition).
    635   *needs_finite_test = NeedsTripCount(info) && IsUnsafeTripCount(trip);
    636   *needs_taken_test = IsBodyTripCount(trip);
    637   // Code generation for taken test: generate the code when requested or otherwise analyze
    638   // if code generation is feasible when taken test is needed.
    639   if (taken_test != nullptr) {
    640     return GenerateCode(trip->op_b, nullptr, graph, block, taken_test, in_body, /* is_min */ false);
    641   } else if (*needs_taken_test) {
    642     if (!GenerateCode(
    643         trip->op_b, nullptr, nullptr, nullptr, nullptr, in_body, /* is_min */ false)) {
    644       return false;
    645     }
    646   }
    647   // Code generation for lower and upper.
    648   return
    649       // Success on lower if invariant (not set), or code can be generated.
    650       ((info->induction_class == HInductionVarAnalysis::kInvariant) ||
    651           GenerateCode(info, trip, graph, block, lower, in_body, /* is_min */ true)) &&
    652       // And success on upper.
    653       GenerateCode(info, trip, graph, block, upper, in_body, /* is_min */ false);
    654 }
    655 
    656 bool InductionVarRange::GenerateCode(HInductionVarAnalysis::InductionInfo* info,
    657                                      HInductionVarAnalysis::InductionInfo* trip,
    658                                      HGraph* graph,  // when set, code is generated
    659                                      HBasicBlock* block,
    660                                      /*out*/HInstruction** result,
    661                                      bool in_body,
    662                                      bool is_min) const {
    663   if (info != nullptr) {
    664     // Verify type safety.
    665     Primitive::Type type = Primitive::kPrimInt;
    666     if (info->type != type) {
    667       return false;
    668     }
    669     // Handle current operation.
    670     HInstruction* opa = nullptr;
    671     HInstruction* opb = nullptr;
    672     switch (info->induction_class) {
    673       case HInductionVarAnalysis::kInvariant:
    674         // Invariants.
    675         switch (info->operation) {
    676           case HInductionVarAnalysis::kAdd:
    677           case HInductionVarAnalysis::kLT:
    678           case HInductionVarAnalysis::kLE:
    679           case HInductionVarAnalysis::kGT:
    680           case HInductionVarAnalysis::kGE:
    681             if (GenerateCode(info->op_a, trip, graph, block, &opa, in_body, is_min) &&
    682                 GenerateCode(info->op_b, trip, graph, block, &opb, in_body, is_min)) {
    683               if (graph != nullptr) {
    684                 HInstruction* operation = nullptr;
    685                 switch (info->operation) {
    686                   case HInductionVarAnalysis::kAdd:
    687                     operation = new (graph->GetArena()) HAdd(type, opa, opb); break;
    688                   case HInductionVarAnalysis::kLT:
    689                     operation = new (graph->GetArena()) HLessThan(opa, opb); break;
    690                   case HInductionVarAnalysis::kLE:
    691                     operation = new (graph->GetArena()) HLessThanOrEqual(opa, opb); break;
    692                   case HInductionVarAnalysis::kGT:
    693                     operation = new (graph->GetArena()) HGreaterThan(opa, opb); break;
    694                   case HInductionVarAnalysis::kGE:
    695                     operation = new (graph->GetArena()) HGreaterThanOrEqual(opa, opb); break;
    696                   default:
    697                     LOG(FATAL) << "unknown operation";
    698                 }
    699                 *result = Insert(block, operation);
    700               }
    701               return true;
    702             }
    703             break;
    704           case HInductionVarAnalysis::kSub:  // second reversed!
    705             if (GenerateCode(info->op_a, trip, graph, block, &opa, in_body, is_min) &&
    706                 GenerateCode(info->op_b, trip, graph, block, &opb, in_body, !is_min)) {
    707               if (graph != nullptr) {
    708                 *result = Insert(block, new (graph->GetArena()) HSub(type, opa, opb));
    709               }
    710               return true;
    711             }
    712             break;
    713           case HInductionVarAnalysis::kNeg:  // reversed!
    714             if (GenerateCode(info->op_b, trip, graph, block, &opb, in_body, !is_min)) {
    715               if (graph != nullptr) {
    716                 *result = Insert(block, new (graph->GetArena()) HNeg(type, opb));
    717               }
    718               return true;
    719             }
    720             break;
    721           case HInductionVarAnalysis::kFetch:
    722             if (graph != nullptr) {
    723               *result = info->fetch;  // already in HIR
    724             }
    725             return true;
    726           case HInductionVarAnalysis::kTripCountInLoop:
    727           case HInductionVarAnalysis::kTripCountInLoopUnsafe:
    728             if (!in_body && !is_min) {  // one extra!
    729               return GenerateCode(info->op_a, trip, graph, block, result, in_body, is_min);
    730             }
    731             FALLTHROUGH_INTENDED;
    732           case HInductionVarAnalysis::kTripCountInBody:
    733           case HInductionVarAnalysis::kTripCountInBodyUnsafe:
    734             if (is_min) {
    735               if (graph != nullptr) {
    736                 *result = graph->GetIntConstant(0);
    737               }
    738               return true;
    739             } else if (in_body) {
    740               if (GenerateCode(info->op_a, trip, graph, block, &opb, in_body, is_min)) {
    741                 if (graph != nullptr) {
    742                   *result = Insert(block,
    743                                    new (graph->GetArena())
    744                                        HSub(type, opb, graph->GetIntConstant(1)));
    745                 }
    746                 return true;
    747               }
    748             }
    749             break;
    750           default:
    751             break;
    752         }
    753         break;
    754       case HInductionVarAnalysis::kLinear: {
    755         // Linear induction a * i + b, for normalized 0 <= i < TC. Restrict to unit stride only
    756         // to avoid arithmetic wrap-around situations that are hard to guard against.
    757         int64_t stride_value = 0;
    758         if (IsConstant(info->op_a, kExact, &stride_value)) {
    759           if (stride_value == 1 || stride_value == -1) {
    760             const bool is_min_a = stride_value == 1 ? is_min : !is_min;
    761             if (GenerateCode(trip,       trip, graph, block, &opa, in_body, is_min_a) &&
    762                 GenerateCode(info->op_b, trip, graph, block, &opb, in_body, is_min)) {
    763               if (graph != nullptr) {
    764                 HInstruction* oper;
    765                 if (stride_value == 1) {
    766                   oper = new (graph->GetArena()) HAdd(type, opa, opb);
    767                 } else {
    768                   oper = new (graph->GetArena()) HSub(type, opb, opa);
    769                 }
    770                 *result = Insert(block, oper);
    771               }
    772               return true;
    773             }
    774           }
    775         }
    776         break;
    777       }
    778       case HInductionVarAnalysis::kWrapAround:
    779       case HInductionVarAnalysis::kPeriodic: {
    780         // Wrap-around and periodic inductions are restricted to constants only, so that extreme
    781         // values are easy to test at runtime without complications of arithmetic wrap-around.
    782         Value extreme = GetVal(info, trip, in_body, is_min);
    783         if (IsConstantValue(extreme)) {
    784           if (graph != nullptr) {
    785             *result = graph->GetIntConstant(extreme.b_constant);
    786           }
    787           return true;
    788         }
    789         break;
    790       }
    791       default:
    792         break;
    793     }
    794   }
    795   return false;
    796 }
    797 
    798 }  // namespace art
    799