OSDN Git Service

Generalize induction and range analysis across type conversions.
authorAart Bik <ajcbik@google.com>
Wed, 16 Mar 2016 17:49:38 +0000 (10:49 -0700)
committerAart Bik <ajcbik@google.com>
Mon, 21 Mar 2016 19:53:33 +0000 (12:53 -0700)
Rationale:
This changelist implements allowing narrowing conversions within
inductions and loop control. More induction and loops recognized,
more bounds eliminated. We all win. The basic idea is pretty simple
(record type with detected induction) but one has to get all the
details right, as illustrated by the many new unit tests.

BUG=27151098

Change-Id: I254020bfa5fa623799b31bbbb5ccc97d4d5a0100

12 files changed:
compiler/optimizing/induction_var_analysis.cc
compiler/optimizing/induction_var_analysis.h
compiler/optimizing/induction_var_analysis_test.cc
compiler/optimizing/induction_var_range.cc
compiler/optimizing/induction_var_range_test.cc
runtime/primitive.h
test/530-checker-loops/expected.txt [deleted file]
test/530-checker-loops1/expected.txt [new file with mode: 0644]
test/530-checker-loops1/info.txt [moved from test/530-checker-loops/info.txt with 100% similarity]
test/530-checker-loops1/src/Main.java [moved from test/530-checker-loops/src/Main.java with 87% similarity]
test/530-checker-loops2/expected.txt
test/530-checker-loops2/src/Main.java

index 82a898a..8d24e26 100644 (file)
@@ -53,6 +53,32 @@ static void RotateEntryPhiFirst(HLoopInformation* loop,
   }
 }
 
+/**
+ * Returns true if the from/to types denote a narrowing, integral conversion (precision loss).
+ */
+static bool IsNarrowingIntegralConversion(Primitive::Type from, Primitive::Type to) {
+  switch (from) {
+    case Primitive::kPrimLong:
+      return to == Primitive::kPrimByte || to == Primitive::kPrimShort
+          || to == Primitive::kPrimChar || to == Primitive::kPrimInt;
+    case Primitive::kPrimInt:
+      return to == Primitive::kPrimByte || to == Primitive::kPrimShort
+          || to == Primitive::kPrimChar;
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+      return to == Primitive::kPrimByte;
+    default:
+      return false;
+  }
+}
+
+/**
+ * Returns narrowest data type.
+ */
+static Primitive::Type Narrowest(Primitive::Type type1, Primitive::Type type2) {
+  return Primitive::ComponentSize(type1) <= Primitive::ComponentSize(type2) ? type1 : type2;
+}
+
 //
 // Class methods.
 //
@@ -148,6 +174,9 @@ void HInductionVarAnalysis::VisitNode(HLoopInformation* loop, HInstruction* inst
       }
     }
 
+    // Type of induction.
+    type_ = scc_[0]->GetType();
+
     // Classify the SCC.
     if (scc_.size() == 1 && !scc_[0]->IsLoopHeaderPhi()) {
       ClassifyTrivial(loop, scc_[0]);
@@ -197,14 +226,13 @@ void HInductionVarAnalysis::ClassifyTrivial(HLoopInformation* loop, HInstruction
                        instruction->InputAt(0)->GetType());
   } else if (instruction->IsNeg()) {
     info = TransferNeg(LookupInfo(loop, instruction->InputAt(0)));
+  } else if (instruction->IsTypeConversion()) {
+    info = TransferCnv(LookupInfo(loop, instruction->InputAt(0)),
+                       instruction->AsTypeConversion()->GetInputType(),
+                       instruction->AsTypeConversion()->GetResultType());
+
   } else if (instruction->IsBoundsCheck()) {
     info = LookupInfo(loop, instruction->InputAt(0));  // Pass-through.
-  } else if (instruction->IsTypeConversion()) {
-    HTypeConversion* conversion = instruction->AsTypeConversion();
-    // TODO: accept different conversion scenarios.
-    if (conversion->GetResultType() == conversion->GetInputType()) {
-      info = LookupInfo(loop, conversion->GetInput());
-    }
   }
 
   // Successfully classified?
@@ -239,7 +267,7 @@ void HInductionVarAnalysis::ClassifyNonTrivial(HLoopInformation* loop) {
   if (size == 1) {
     InductionInfo* update = TransferPhi(loop, phi, /* input_index */ 1);
     if (update != nullptr) {
-      AssignInfo(loop, phi, CreateInduction(kWrapAround, initial, update));
+      AssignInfo(loop, phi, CreateInduction(kWrapAround, initial, update, type_));
     }
     return;
   }
@@ -257,6 +285,8 @@ void HInductionVarAnalysis::ClassifyNonTrivial(HLoopInformation* loop) {
     } else if (instruction->IsSub()) {
       update = SolveAddSub(
           loop, phi, instruction, instruction->InputAt(0), instruction->InputAt(1), kSub, true);
+    } else if (instruction->IsTypeConversion()) {
+      update = SolveCnv(instruction->AsTypeConversion());
     }
     if (update == nullptr) {
       return;
@@ -271,7 +301,7 @@ void HInductionVarAnalysis::ClassifyNonTrivial(HLoopInformation* loop) {
       case kInvariant:
         // Classify first phi and then the rest of the cycle "on-demand".
         // Statements are scanned in order.
-        AssignInfo(loop, phi, CreateInduction(kLinear, induction, initial));
+        AssignInfo(loop, phi, CreateInduction(kLinear, induction, initial, type_));
         for (size_t i = 1; i < size; i++) {
           ClassifyTrivial(loop, scc_[i]);
         }
@@ -301,9 +331,10 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::RotatePeriodicInduc
   //   (b, c, d, e, a)
   // in preparation of assigning this to the previous variable in the sequence.
   if (induction->induction_class == kInvariant) {
-    return CreateInduction(kPeriodic, induction, last);
+    return CreateInduction(kPeriodic, induction, last, type_);
   }
-  return CreateInduction(kPeriodic, induction->op_a, RotatePeriodicInduction(induction->op_b, last));
+  return CreateInduction(
+      kPeriodic, induction->op_a, RotatePeriodicInduction(induction->op_b, last), type_);
 }
 
 HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferPhi(HLoopInformation* loop,
@@ -332,8 +363,10 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferAddSub(Indu
     if (a->induction_class == kInvariant && b->induction_class == kInvariant) {
       return CreateInvariantOp(op, a, b);
     } else if (a->induction_class == kLinear && b->induction_class == kLinear) {
-      return CreateInduction(
-          kLinear, TransferAddSub(a->op_a, b->op_a, op), TransferAddSub(a->op_b, b->op_b, op));
+      return CreateInduction(kLinear,
+                             TransferAddSub(a->op_a, b->op_a, op),
+                             TransferAddSub(a->op_b, b->op_b, op),
+                             type_);
     } else if (a->induction_class == kInvariant) {
       InductionInfo* new_a = b->op_a;
       InductionInfo* new_b = TransferAddSub(a, b->op_b, op);
@@ -343,7 +376,7 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferAddSub(Indu
       } else if (op == kSub) {  // Negation required.
         new_a = TransferNeg(new_a);
       }
-      return CreateInduction(b->induction_class, new_a, new_b);
+      return CreateInduction(b->induction_class, new_a, new_b, type_);
     } else if (b->induction_class == kInvariant) {
       InductionInfo* new_a = a->op_a;
       InductionInfo* new_b = TransferAddSub(a->op_b, b, op);
@@ -351,7 +384,7 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferAddSub(Indu
         DCHECK(a->induction_class == kWrapAround || a->induction_class == kPeriodic);
         new_a = TransferAddSub(new_a, b, op);
       }
-      return CreateInduction(a->induction_class, new_a, new_b);
+      return CreateInduction(a->induction_class, new_a, new_b, type_);
     }
   }
   return nullptr;
@@ -366,9 +399,15 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferMul(Inducti
     if (a->induction_class == kInvariant && b->induction_class == kInvariant) {
       return CreateInvariantOp(kMul, a, b);
     } else if (a->induction_class == kInvariant) {
-      return CreateInduction(b->induction_class, TransferMul(a, b->op_a), TransferMul(a, b->op_b));
+      return CreateInduction(b->induction_class,
+                             TransferMul(a, b->op_a),
+                             TransferMul(a, b->op_b),
+                             type_);
     } else if (b->induction_class == kInvariant) {
-      return CreateInduction(a->induction_class, TransferMul(a->op_a, b), TransferMul(a->op_b, b));
+      return CreateInduction(a->induction_class,
+                             TransferMul(a->op_a, b),
+                             TransferMul(a->op_b, b),
+                             type_);
     }
   }
   return nullptr;
@@ -400,7 +439,24 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferNeg(Inducti
     if (a->induction_class == kInvariant) {
       return CreateInvariantOp(kNeg, nullptr, a);
     }
-    return CreateInduction(a->induction_class, TransferNeg(a->op_a), TransferNeg(a->op_b));
+    return CreateInduction(a->induction_class, TransferNeg(a->op_a), TransferNeg(a->op_b), type_);
+  }
+  return nullptr;
+}
+
+HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferCnv(InductionInfo* a,
+                                                                         Primitive::Type from,
+                                                                         Primitive::Type to) {
+  if (a != nullptr) {
+    // Allow narrowing conversion in certain cases.
+    if (IsNarrowingIntegralConversion(from, to)) {
+      if (a->induction_class == kLinear) {
+        if (a->type == to || (a->type == from && IsNarrowingIntegralConversion(from, to))) {
+          return CreateInduction(kLinear, a->op_a, a->op_b, to);
+        }
+      }
+      // TODO: other cases useful too?
+    }
   }
   return nullptr;
 }
@@ -442,11 +498,11 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolvePhiAllInputs(
     if (a != nullptr && a->induction_class == kInvariant) {
       if (phi->InputAt(1) == entry_phi) {
         InductionInfo* initial = LookupInfo(loop, entry_phi->InputAt(0));
-        return CreateInduction(kPeriodic, a, initial);
+        return CreateInduction(kPeriodic, a, initial, type_);
       }
       InductionInfo* b = SolvePhi(phi, /* input_index */ 1);
       if (b != nullptr && b->induction_class == kPeriodic) {
-        return CreateInduction(kPeriodic, a, b);
+        return CreateInduction(kPeriodic, a, b, type_);
       }
     }
   }
@@ -489,7 +545,7 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolveAddSub(HLoopIn
       InductionInfo* a = LookupInfo(loop, x);
       if (a != nullptr && a->induction_class == kInvariant) {
         InductionInfo* initial = LookupInfo(loop, entry_phi->InputAt(0));
-        return CreateInduction(kPeriodic, CreateInvariantOp(kSub, a, initial), initial);
+        return CreateInduction(kPeriodic, CreateInvariantOp(kSub, a, initial), initial, type_);
       }
     }
   }
@@ -497,6 +553,21 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolveAddSub(HLoopIn
   return nullptr;
 }
 
+HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolveCnv(HTypeConversion* conversion) {
+  Primitive::Type from = conversion->GetInputType();
+  Primitive::Type to = conversion->GetResultType();
+  // A narrowing conversion is allowed within the cycle of a linear induction, provided that the
+  // narrowest encountered type is recorded with the induction to account for the precision loss.
+  if (IsNarrowingIntegralConversion(from, to)) {
+    auto it = cycle_.find(conversion->GetInput());
+    if (it != cycle_.end() && it->second->induction_class == kInvariant) {
+      type_ = Narrowest(type_, to);
+      return it->second;
+    }
+  }
+  return nullptr;
+}
+
 void HInductionVarAnalysis::VisitControl(HLoopInformation* loop) {
   HInstruction* control = loop->GetHeader()->GetLastInstruction();
   if (control->IsIf()) {
@@ -512,12 +583,10 @@ void HInductionVarAnalysis::VisitControl(HLoopInformation* loop) {
       InductionInfo* a = LookupInfo(loop, condition->InputAt(0));
       InductionInfo* b = LookupInfo(loop, condition->InputAt(1));
       Primitive::Type type = condition->InputAt(0)->GetType();
-      // Determine if the loop control uses integral arithmetic and an if-exit (X outside) or an
-      // if-iterate (X inside), always expressed as if-iterate when passing into VisitCondition().
-      if (type != Primitive::kPrimInt && type != Primitive::kPrimLong) {
-        // Loop control is not 32/64-bit integral.
-      } else if (a == nullptr || b == nullptr) {
-        // Loop control is not a sequence.
+      // Determine if the loop control uses a known sequence on an if-exit (X outside) or on
+      // an if-iterate (X inside), expressed as if-iterate when passed into VisitCondition().
+      if (a == nullptr || b == nullptr) {
+        return;  // Loop control is not a sequence.
       } else if (if_true->GetLoopInformation() != loop && if_false->GetLoopInformation() == loop) {
         VisitCondition(loop, a, b, type, condition->GetOppositeCondition());
       } else if (if_true->GetLoopInformation() == loop && if_false->GetLoopInformation() != loop) {
@@ -559,6 +628,14 @@ void HInductionVarAnalysis::VisitCondition(HLoopInformation* loop,
                            (stride_value == -1 && IsTaken(lower_expr, upper_expr, kCondGE)))) {
       cmp = stride_value > 0 ? kCondLT : kCondGT;
     }
+    // Only accept integral condition. A mismatch between the type of condition and the induction
+    // is only allowed if the, necessarily narrower, induction range fits the narrower control.
+    if (type != Primitive::kPrimInt && type != Primitive::kPrimLong) {
+      return;  // not integral
+    } else if (type != a->type &&
+               !FitsNarrowerControl(lower_expr, upper_expr, stride_value, a->type, cmp)) {
+      return;  // mismatched type
+    }
     // Normalize a linear loop control with a nonzero stride:
     //   stride > 0, either i < U or i <= U
     //   stride < 0, either i > U or i >= U
@@ -640,7 +717,7 @@ void HInductionVarAnalysis::VisitTripCount(HLoopInformation* loop,
   InductionInfo* taken_test = CreateInvariantOp(op, lower_expr, upper_expr);
   AssignInfo(loop,
              loop->GetHeader()->GetLastInstruction(),
-             CreateTripCount(tcKind, trip_count, taken_test));
+             CreateTripCount(tcKind, trip_count, taken_test, type));
 }
 
 bool HInductionVarAnalysis::IsTaken(InductionInfo* lower_expr,
@@ -675,10 +752,8 @@ bool HInductionVarAnalysis::IsFinite(InductionInfo* upper_expr,
                                      int64_t stride_value,
                                      Primitive::Type type,
                                      IfCondition cmp) {
-  const int64_t min = type == Primitive::kPrimInt ? std::numeric_limits<int32_t>::min()
-                                                  : std::numeric_limits<int64_t>::min();
-  const int64_t max = type == Primitive::kPrimInt ? std::numeric_limits<int32_t>::max()
-                                                  : std::numeric_limits<int64_t>::max();
+  const int64_t min = Primitive::MinValueOfIntegralType(type);
+  const int64_t max = Primitive::MaxValueOfIntegralType(type);
   // Some rules under which it is certain at compile-time that the loop is finite.
   int64_t value;
   switch (cmp) {
@@ -698,6 +773,29 @@ bool HInductionVarAnalysis::IsFinite(InductionInfo* upper_expr,
   return false;  // not certain, may be infinite
 }
 
+bool HInductionVarAnalysis::FitsNarrowerControl(InductionInfo* lower_expr,
+                                                InductionInfo* upper_expr,
+                                                int64_t stride_value,
+                                                Primitive::Type type,
+                                                IfCondition cmp) {
+  int64_t min = Primitive::MinValueOfIntegralType(type);
+  int64_t max = Primitive::MaxValueOfIntegralType(type);
+  // Inclusive test need one extra.
+  if (stride_value != 1 && stride_value != -1) {
+    return false;  // non-unit stride
+  } else if (cmp == kCondLE) {
+    max--;
+  } else if (cmp == kCondGE) {
+    min++;
+  }
+  // Do both bounds fit the range?
+  int64_t value;
+  return IsAtLeast(lower_expr, &value) && value >= min &&
+         IsAtMost(lower_expr, &value)  && value <= max &&
+         IsAtLeast(upper_expr, &value) && value >= min &&
+         IsAtMost(upper_expr, &value)  && value <= max;
+}
+
 void HInductionVarAnalysis::AssignInfo(HLoopInformation* loop,
                                        HInstruction* instruction,
                                        InductionInfo* info) {
@@ -794,7 +892,7 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::CreateSimplifiedInv
       return CreateSimplifiedInvariant(kSub, b->op_b, b->op_a);
     }
   }
-  return new (graph_->GetArena()) InductionInfo(kInvariant, op, a, b, nullptr);
+  return new (graph_->GetArena()) InductionInfo(kInvariant, op, a, b, nullptr, b->type);
 }
 
 bool HInductionVarAnalysis::IsExact(InductionInfo* info, int64_t* value) {
@@ -856,18 +954,22 @@ std::string HInductionVarAnalysis::InductionToString(InductionInfo* info) {
         case kTripCountInBodyUnsafe: inv += " (TC-body-unsafe) "; break;
       }
       inv += InductionToString(info->op_b);
-      return inv + ")";
+      inv += ")";
+      return inv;
     } else {
       DCHECK(info->operation == kNop);
       if (info->induction_class == kLinear) {
         return "(" + InductionToString(info->op_a) + " * i + " +
-                     InductionToString(info->op_b) + ")";
+                     InductionToString(info->op_b) + "):" +
+                     Primitive::PrettyDescriptor(info->type);
       } else if (info->induction_class == kWrapAround) {
         return "wrap(" + InductionToString(info->op_a) + ", " +
-                         InductionToString(info->op_b) + ")";
+                         InductionToString(info->op_b) + "):" +
+                         Primitive::PrettyDescriptor(info->type);
       } else if (info->induction_class == kPeriodic) {
         return "periodic(" + InductionToString(info->op_a) + ", " +
-                             InductionToString(info->op_b) + ")";
+                             InductionToString(info->op_b) + "):" +
+                             Primitive::PrettyDescriptor(info->type);
       }
     }
   }
index 94d2646..f1965f0 100644 (file)
@@ -97,17 +97,20 @@ class HInductionVarAnalysis : public HOptimization {
                   InductionOp op,
                   InductionInfo* a,
                   InductionInfo* b,
-                  HInstruction* f)
+                  HInstruction* f,
+                  Primitive::Type t)
         : induction_class(ic),
           operation(op),
           op_a(a),
           op_b(b),
-          fetch(f) {}
+          fetch(f),
+          type(t) {}
     InductionClass induction_class;
     InductionOp operation;
     InductionInfo* op_a;
     InductionInfo* op_b;
     HInstruction* fetch;
+    Primitive::Type type;  // precision of induction
   };
 
   bool IsVisitedNode(HInstruction* instruction) const {
@@ -121,17 +124,24 @@ class HInductionVarAnalysis : public HOptimization {
 
   InductionInfo* CreateInvariantFetch(HInstruction* f) {
     DCHECK(f != nullptr);
-    return new (graph_->GetArena()) InductionInfo(kInvariant, kFetch, nullptr, nullptr, f);
+    return new (graph_->GetArena())
+        InductionInfo(kInvariant, kFetch, nullptr, nullptr, f, f->GetType());
   }
 
-  InductionInfo* CreateTripCount(InductionOp op, InductionInfo* a, InductionInfo* b) {
+  InductionInfo* CreateTripCount(InductionOp op,
+                                 InductionInfo* a,
+                                 InductionInfo* b,
+                                 Primitive::Type type) {
     DCHECK(a != nullptr);
-    return new (graph_->GetArena()) InductionInfo(kInvariant, op, a, b, nullptr);
+    return new (graph_->GetArena()) InductionInfo(kInvariant, op, a, b, nullptr, type);
   }
 
-  InductionInfo* CreateInduction(InductionClass ic, InductionInfo* a, InductionInfo* b) {
+  InductionInfo* CreateInduction(InductionClass ic,
+                                 InductionInfo* a,
+                                 InductionInfo* b,
+                                 Primitive::Type type) {
     DCHECK(a != nullptr && b != nullptr);
-    return new (graph_->GetArena()) InductionInfo(ic, kNop, a, b, nullptr);
+    return new (graph_->GetArena()) InductionInfo(ic, kNop, a, b, nullptr, type);
   }
 
   // Methods for analysis.
@@ -148,6 +158,7 @@ class HInductionVarAnalysis : public HOptimization {
   InductionInfo* TransferMul(InductionInfo* a, InductionInfo* b);
   InductionInfo* TransferShl(InductionInfo* a, InductionInfo* b, Primitive::Type type);
   InductionInfo* TransferNeg(InductionInfo* a);
+  InductionInfo* TransferCnv(InductionInfo* a, Primitive::Type from, Primitive::Type to);
 
   // Solvers.
   InductionInfo* SolvePhi(HInstruction* phi, size_t input_index);
@@ -161,6 +172,7 @@ class HInductionVarAnalysis : public HOptimization {
                              HInstruction* y,
                              InductionOp op,
                              bool is_first_call);
+  InductionInfo* SolveCnv(HTypeConversion* conversion);
 
   // Trip count information.
   void VisitControl(HLoopInformation* loop);
@@ -181,6 +193,11 @@ class HInductionVarAnalysis : public HOptimization {
                 int64_t stride_value,
                 Primitive::Type type,
                 IfCondition cmp);
+  bool FitsNarrowerControl(InductionInfo* lower_expr,
+                           InductionInfo* upper_expr,
+                           int64_t stride_value,
+                           Primitive::Type type,
+                           IfCondition cmp);
 
   // Assign and lookup.
   void AssignInfo(HLoopInformation* loop, HInstruction* instruction, InductionInfo* info);
@@ -205,6 +222,7 @@ class HInductionVarAnalysis : public HOptimization {
   ArenaVector<HInstruction*> scc_;
   ArenaSafeMap<HInstruction*, NodeInfo> map_;
   ArenaSafeMap<HInstruction*, InductionInfo*> cycle_;
+  Primitive::Type type_;
 
   /**
    * Maintains the results of the analysis as a mapping from loops to a mapping from instructions
index 89e4690..0fbb67d 100644 (file)
@@ -202,6 +202,7 @@ TEST_F(InductionVarAnalysisTest, ProperLoopSetup) {
   // }
   BuildLoopNest(10);
   graph_->BuildDominatorTree();
+
   ASSERT_EQ(entry_->GetLoopInformation(), nullptr);
   for (int d = 0; d < 1; d++) {
     ASSERT_EQ(loop_preheader_[d]->GetLoopInformation(),
@@ -224,8 +225,8 @@ TEST_F(InductionVarAnalysisTest, FindBasicInduction) {
   HInstruction* store = InsertArrayStore(basic_[0], 0);
   PerformInductionVarAnalysis();
 
-  EXPECT_STREQ("((1) * i + (0))", GetInductionInfo(store->InputAt(1), 0).c_str());
-  EXPECT_STREQ("((1) * i + (1))", GetInductionInfo(increment_[0], 0).c_str());
+  EXPECT_STREQ("((1) * i + (0)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str());
+  EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(increment_[0], 0).c_str());
 
   // Trip-count.
   EXPECT_STREQ("((100) (TC-loop) ((0) < (100)))",
@@ -254,11 +255,11 @@ TEST_F(InductionVarAnalysisTest, FindDerivedInduction) {
       new (&allocator_) HNeg(Primitive::kPrimInt, basic_[0]), 0);
   PerformInductionVarAnalysis();
 
-  EXPECT_STREQ("((1) * i + (100))", GetInductionInfo(add, 0).c_str());
-  EXPECT_STREQ("(( - (1)) * i + (100))", GetInductionInfo(sub, 0).c_str());
-  EXPECT_STREQ("((100) * i + (0))", GetInductionInfo(mul, 0).c_str());
-  EXPECT_STREQ("((2) * i + (0))", GetInductionInfo(shl, 0).c_str());
-  EXPECT_STREQ("(( - (1)) * i + (0))", GetInductionInfo(neg, 0).c_str());
+  EXPECT_STREQ("((1) * i + (100)):PrimInt", GetInductionInfo(add, 0).c_str());
+  EXPECT_STREQ("(( - (1)) * i + (100)):PrimInt", GetInductionInfo(sub, 0).c_str());
+  EXPECT_STREQ("((100) * i + (0)):PrimInt", GetInductionInfo(mul, 0).c_str());
+  EXPECT_STREQ("((2) * i + (0)):PrimInt", GetInductionInfo(shl, 0).c_str());
+  EXPECT_STREQ("(( - (1)) * i + (0)):PrimInt", GetInductionInfo(neg, 0).c_str());
 }
 
 TEST_F(InductionVarAnalysisTest, FindChainInduction) {
@@ -283,9 +284,9 @@ TEST_F(InductionVarAnalysisTest, FindChainInduction) {
   k->AddInput(sub);
   PerformInductionVarAnalysis();
 
-  EXPECT_STREQ("(((100) - (1)) * i + (100))",
+  EXPECT_STREQ("(((100) - (1)) * i + (100)):PrimInt",
                GetInductionInfo(store1->InputAt(1), 0).c_str());
-  EXPECT_STREQ("(((100) - (1)) * i + ((100) - (1)))",
+  EXPECT_STREQ("(((100) - (1)) * i + ((100) - (1))):PrimInt",
                GetInductionInfo(store2->InputAt(1), 0).c_str());
 }
 
@@ -318,7 +319,7 @@ TEST_F(InductionVarAnalysisTest, FindTwoWayBasicInduction) {
   k_header->AddInput(k_body);
   PerformInductionVarAnalysis();
 
-  EXPECT_STREQ("((1) * i + (1))", GetInductionInfo(store->InputAt(1), 0).c_str());
+  EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str());
 }
 
 TEST_F(InductionVarAnalysisTest, FindTwoWayDerivedInduction) {
@@ -345,7 +346,7 @@ TEST_F(InductionVarAnalysisTest, FindTwoWayDerivedInduction) {
   HInstruction* store = InsertArrayStore(k, 0);
   PerformInductionVarAnalysis();
 
-  EXPECT_STREQ("((1) * i + (1))", GetInductionInfo(store->InputAt(1), 0).c_str());
+  EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str());
 }
 
 TEST_F(InductionVarAnalysisTest, FindFirstOrderWrapAroundInduction) {
@@ -365,7 +366,7 @@ TEST_F(InductionVarAnalysisTest, FindFirstOrderWrapAroundInduction) {
   k->AddInput(sub);
   PerformInductionVarAnalysis();
 
-  EXPECT_STREQ("wrap((0), (( - (1)) * i + (100)))",
+  EXPECT_STREQ("wrap((0), (( - (1)) * i + (100)):PrimInt):PrimInt",
                GetInductionInfo(store->InputAt(1), 0).c_str());
 }
 
@@ -391,7 +392,7 @@ TEST_F(InductionVarAnalysisTest, FindSecondOrderWrapAroundInduction) {
   t->AddInput(sub);
   PerformInductionVarAnalysis();
 
-  EXPECT_STREQ("wrap((0), wrap((100), (( - (1)) * i + (100))))",
+  EXPECT_STREQ("wrap((0), wrap((100), (( - (1)) * i + (100)):PrimInt):PrimInt):PrimInt",
                GetInductionInfo(store->InputAt(1), 0).c_str());
 }
 
@@ -424,11 +425,16 @@ TEST_F(InductionVarAnalysisTest, FindWrapAroundDerivedInduction) {
       InsertInstruction(new (&allocator_) HShl(Primitive::kPrimInt, basic_[0], constant1_), 0));
   PerformInductionVarAnalysis();
 
-  EXPECT_STREQ("wrap((100), ((2) * i + (100)))", GetInductionInfo(add, 0).c_str());
-  EXPECT_STREQ("wrap(((0) - (100)), ((2) * i + ((0) - (100))))", GetInductionInfo(sub, 0).c_str());
-  EXPECT_STREQ("wrap((0), (((2) * (100)) * i + (0)))", GetInductionInfo(mul, 0).c_str());
-  EXPECT_STREQ("wrap((0), (((2) * (2)) * i + (0)))", GetInductionInfo(shl, 0).c_str());
-  EXPECT_STREQ("wrap((0), (( - (2)) * i + (0)))", GetInductionInfo(neg, 0).c_str());
+  EXPECT_STREQ("wrap((100), ((2) * i + (100)):PrimInt):PrimInt",
+               GetInductionInfo(add, 0).c_str());
+  EXPECT_STREQ("wrap(((0) - (100)), ((2) * i + ((0) - (100))):PrimInt):PrimInt",
+               GetInductionInfo(sub, 0).c_str());
+  EXPECT_STREQ("wrap((0), (((2) * (100)) * i + (0)):PrimInt):PrimInt",
+               GetInductionInfo(mul, 0).c_str());
+  EXPECT_STREQ("wrap((0), (((2) * (2)) * i + (0)):PrimInt):PrimInt",
+               GetInductionInfo(shl, 0).c_str());
+  EXPECT_STREQ("wrap((0), (( - (2)) * i + (0)):PrimInt):PrimInt",
+               GetInductionInfo(neg, 0).c_str());
 }
 
 TEST_F(InductionVarAnalysisTest, FindPeriodicInduction) {
@@ -455,8 +461,8 @@ TEST_F(InductionVarAnalysisTest, FindPeriodicInduction) {
   t->AddInput(k);
   PerformInductionVarAnalysis();
 
-  EXPECT_STREQ("periodic((0), (100))", GetInductionInfo(store1->InputAt(1), 0).c_str());
-  EXPECT_STREQ("periodic((100), (0))", GetInductionInfo(store2->InputAt(1), 0).c_str());
+  EXPECT_STREQ("periodic((0), (100)):PrimInt", GetInductionInfo(store1->InputAt(1), 0).c_str());
+  EXPECT_STREQ("periodic((100), (0)):PrimInt", GetInductionInfo(store2->InputAt(1), 0).c_str());
 }
 
 TEST_F(InductionVarAnalysisTest, FindIdiomaticPeriodicInduction) {
@@ -476,8 +482,8 @@ TEST_F(InductionVarAnalysisTest, FindIdiomaticPeriodicInduction) {
   k->AddInput(sub);
   PerformInductionVarAnalysis();
 
-  EXPECT_STREQ("periodic((0), (1))", GetInductionInfo(store->InputAt(1), 0).c_str());
-  EXPECT_STREQ("periodic((1), (0))", GetInductionInfo(sub, 0).c_str());
+  EXPECT_STREQ("periodic((0), (1)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str());
+  EXPECT_STREQ("periodic((1), (0)):PrimInt", GetInductionInfo(sub, 0).c_str());
 }
 
 TEST_F(InductionVarAnalysisTest, FindDerivedPeriodicInduction) {
@@ -512,11 +518,11 @@ TEST_F(InductionVarAnalysisTest, FindDerivedPeriodicInduction) {
       new (&allocator_) HNeg(Primitive::kPrimInt, k_body), 0);
   PerformInductionVarAnalysis();
 
-  EXPECT_STREQ("periodic(((1) + (100)), (100))", GetInductionInfo(add, 0).c_str());
-  EXPECT_STREQ("periodic(((1) - (100)), ((0) - (100)))", GetInductionInfo(sub, 0).c_str());
-  EXPECT_STREQ("periodic((100), (0))", GetInductionInfo(mul, 0).c_str());
-  EXPECT_STREQ("periodic((2), (0))", GetInductionInfo(shl, 0).c_str());
-  EXPECT_STREQ("periodic(( - (1)), (0))", GetInductionInfo(neg, 0).c_str());
+  EXPECT_STREQ("periodic(((1) + (100)), (100)):PrimInt", GetInductionInfo(add, 0).c_str());
+  EXPECT_STREQ("periodic(((1) - (100)), ((0) - (100))):PrimInt", GetInductionInfo(sub, 0).c_str());
+  EXPECT_STREQ("periodic((100), (0)):PrimInt", GetInductionInfo(mul, 0).c_str());
+  EXPECT_STREQ("periodic((2), (0)):PrimInt", GetInductionInfo(shl, 0).c_str());
+  EXPECT_STREQ("periodic(( - (1)), (0)):PrimInt", GetInductionInfo(neg, 0).c_str());
 }
 
 TEST_F(InductionVarAnalysisTest, FindDeepLoopInduction) {
@@ -549,7 +555,7 @@ TEST_F(InductionVarAnalysisTest, FindDeepLoopInduction) {
 
   // Avoid exact phi number, since that depends on the SSA building phase.
   std::regex r("\\(\\(1\\) \\* i \\+ "
-               "\\(\\(1\\) \\+ \\(\\d+:Phi\\)\\)\\)");
+               "\\(\\(1\\) \\+ \\(\\d+:Phi\\)\\)\\):PrimInt");
 
   for (int d = 0; d < 10; d++) {
     if (d == 9) {
@@ -557,11 +563,122 @@ TEST_F(InductionVarAnalysisTest, FindDeepLoopInduction) {
     } else {
       EXPECT_STREQ("", GetInductionInfo(store->InputAt(1), d).c_str());
     }
-    EXPECT_STREQ("((1) * i + (1))", GetInductionInfo(increment_[d], d).c_str());
+    EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(increment_[d], d).c_str());
     // Trip-count.
     EXPECT_STREQ("((100) (TC-loop) ((0) < (100)))",
                  GetInductionInfo(loop_header_[d]->GetLastInstruction(), d).c_str());
   }
 }
 
+TEST_F(InductionVarAnalysisTest, ByteLoopControl1) {
+  // Setup:
+  // for (byte i = -128; i < 127; i++) {  // just fits!
+  // }
+  BuildLoopNest(1);
+  basic_[0]->ReplaceInput(graph_->GetIntConstant(-128), 0);
+  HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
+  ifs->ReplaceInput(graph_->GetIntConstant(127), 1);
+  HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimByte, increment_[0], -1);
+  loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
+  basic_[0]->ReplaceInput(conv, 1);
+  PerformInductionVarAnalysis();
+
+  EXPECT_STREQ("((1) * i + ((-128) + (1))):PrimByte", GetInductionInfo(increment_[0], 0).c_str());
+  // Trip-count.
+  EXPECT_STREQ("(((127) - (-128)) (TC-loop) ((-128) < (127)))",
+               GetInductionInfo(loop_header_[0]->GetLastInstruction(), 0).c_str());
+}
+
+TEST_F(InductionVarAnalysisTest, ByteLoopControl2) {
+  // Setup:
+  // for (byte i = -128; i < 128; i++) {  // infinite loop!
+  // }
+  BuildLoopNest(1);
+  basic_[0]->ReplaceInput(graph_->GetIntConstant(-128), 0);
+  HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
+  ifs->ReplaceInput(graph_->GetIntConstant(128), 1);
+  HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimByte, increment_[0], -1);
+  loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
+  basic_[0]->ReplaceInput(conv, 1);
+  PerformInductionVarAnalysis();
+
+  EXPECT_STREQ("((1) * i + ((-128) + (1))):PrimByte", GetInductionInfo(increment_[0], 0).c_str());
+  // Trip-count undefined.
+  EXPECT_STREQ("", GetInductionInfo(loop_header_[0]->GetLastInstruction(), 0).c_str());
+}
+
+TEST_F(InductionVarAnalysisTest, ShortLoopControl1) {
+  // Setup:
+  // for (short i = -32768; i < 32767; i++) {  // just fits!
+  // }
+  BuildLoopNest(1);
+  basic_[0]->ReplaceInput(graph_->GetIntConstant(-32768), 0);
+  HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
+  ifs->ReplaceInput(graph_->GetIntConstant(32767), 1);
+  HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimShort, increment_[0], -1);
+  loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
+  basic_[0]->ReplaceInput(conv, 1);
+  PerformInductionVarAnalysis();
+
+  EXPECT_STREQ("((1) * i + ((-32768) + (1))):PrimShort",
+               GetInductionInfo(increment_[0], 0).c_str());
+  // Trip-count.
+  EXPECT_STREQ("(((32767) - (-32768)) (TC-loop) ((-32768) < (32767)))",
+               GetInductionInfo(loop_header_[0]->GetLastInstruction(), 0).c_str());
+}
+
+TEST_F(InductionVarAnalysisTest, ShortLoopControl2) {
+  // Setup:
+  // for (short i = -32768; i < 32768; i++) {  // infinite loop!
+  // }
+  BuildLoopNest(1);
+  basic_[0]->ReplaceInput(graph_->GetIntConstant(-32768), 0);
+  HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
+  ifs->ReplaceInput(graph_->GetIntConstant(32768), 1);
+  HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimShort, increment_[0], -1);
+  loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
+  basic_[0]->ReplaceInput(conv, 1);
+  PerformInductionVarAnalysis();
+
+  EXPECT_STREQ("((1) * i + ((-32768) + (1))):PrimShort",
+               GetInductionInfo(increment_[0], 0).c_str());
+  // Trip-count undefined.
+  EXPECT_STREQ("", GetInductionInfo(loop_header_[0]->GetLastInstruction(), 0).c_str());
+}
+
+TEST_F(InductionVarAnalysisTest, CharLoopControl1) {
+  // Setup:
+  // for (char i = 0; i < 65535; i++) {  // just fits!
+  // }
+  BuildLoopNest(1);
+  HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
+  ifs->ReplaceInput(graph_->GetIntConstant(65535), 1);
+  HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimChar, increment_[0], -1);
+  loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
+  basic_[0]->ReplaceInput(conv, 1);
+  PerformInductionVarAnalysis();
+
+  EXPECT_STREQ("((1) * i + (1)):PrimChar", GetInductionInfo(increment_[0], 0).c_str());
+  // Trip-count.
+  EXPECT_STREQ("((65535) (TC-loop) ((0) < (65535)))",
+               GetInductionInfo(loop_header_[0]->GetLastInstruction(), 0).c_str());
+}
+
+TEST_F(InductionVarAnalysisTest, CharLoopControl2) {
+  // Setup:
+  // for (char i = 0; i < 65536; i++) {  // infinite loop!
+  // }
+  BuildLoopNest(1);
+  HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
+  ifs->ReplaceInput(graph_->GetIntConstant(65536), 1);
+  HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimChar, increment_[0], -1);
+  loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
+  basic_[0]->ReplaceInput(conv, 1);
+  PerformInductionVarAnalysis();
+
+  EXPECT_STREQ("((1) * i + (1)):PrimChar", GetInductionInfo(increment_[0], 0).c_str());
+  // Trip-count undefined.
+  EXPECT_STREQ("", GetInductionInfo(loop_header_[0]->GetLastInstruction(), 0).c_str());
+}
+
 }  // namespace art
index f9b6910..bc920d9 100644 (file)
@@ -58,13 +58,13 @@ static bool IsIntAndGet(HInstruction* instruction, int64_t* value) {
 }
 
 /**
- * An upper bound a * (length / a) + b, where a > 0, can be conservatively rewritten as length + b
+ * An upper bound a * (length / a) + b, where a >= 1, can be conservatively rewritten as length + b
  * because length >= 0 is true. This makes it more likely the bound is useful to clients.
  */
 static InductionVarRange::Value SimplifyMax(InductionVarRange::Value v) {
   int64_t value;
   if (v.is_known &&
-      v.a_constant > 1 &&
+      v.a_constant >= 1 &&
       v.instruction->IsDiv() &&
       v.instruction->InputAt(0)->IsArrayLength() &&
       IsIntAndGet(v.instruction->InputAt(1), &value) && v.a_constant == value) {
@@ -73,6 +73,28 @@ static InductionVarRange::Value SimplifyMax(InductionVarRange::Value v) {
   return v;
 }
 
+/**
+ * Corrects a value for type to account for arithmetic wrap-around in lower precision.
+ */
+static InductionVarRange::Value CorrectForType(InductionVarRange::Value v, Primitive::Type type) {
+  switch (type) {
+    case Primitive::kPrimShort:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimByte: {
+      // Constants within range only.
+      // TODO: maybe some room for improvement, like allowing widening conversions
+      const int32_t min = Primitive::MinValueOfIntegralType(type);
+      const int32_t max = Primitive::MaxValueOfIntegralType(type);
+      return (v.is_known && v.a_constant == 0 && min <= v.b_constant && v.b_constant <= max)
+          ? v
+          : InductionVarRange::Value();
+    }
+    default:
+      // At int or higher.
+      return v;
+  }
+}
+
 /** Helper method to test for a constant value. */
 static bool IsConstantValue(InductionVarRange::Value v) {
   return v.is_known && v.a_constant == 0;
@@ -114,6 +136,18 @@ bool InductionVarRange::GetInductionRange(HInstruction* context,
   if (info == nullptr) {
     return false;  // no induction information
   }
+  // Type int or lower (this is not too restrictive since intended clients, like
+  // bounds check elimination, will have truncated higher precision induction
+  // at their use point already).
+  switch (info->type) {
+    case Primitive::kPrimInt:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimByte:
+      break;
+    default:
+      return false;
+  }
   // Set up loop information.
   HBasicBlock* header = loop->GetHeader();
   bool in_body = context->GetBlock() != header;
@@ -128,25 +162,27 @@ bool InductionVarRange::GetInductionRange(HInstruction* context,
 
 bool InductionVarRange::RefineOuter(/*in-out*/ Value* min_val,
                                     /*in-out*/ Value* max_val) const {
-  Value v1_min = RefineOuter(*min_val, /* is_min */ true);
-  Value v2_max = RefineOuter(*max_val, /* is_min */ false);
-  // The refined range is safe if both sides refine the same instruction. Otherwise, since two
-  // different ranges are combined, the new refined range is safe to pass back to the client if
-  // the extremes of the computed ranges ensure no arithmetic wrap-around anomalies occur.
-  if (min_val->instruction != max_val->instruction) {
-    Value v1_max = RefineOuter(*min_val, /* is_min */ false);
-    Value v2_min = RefineOuter(*max_val, /* is_min */ true);
-    if (!IsConstantValue(v1_max) ||
-        !IsConstantValue(v2_min) ||
-        v1_max.b_constant > v2_min.b_constant) {
-      return false;
+  if (min_val->instruction != nullptr || max_val->instruction != nullptr) {
+    Value v1_min = RefineOuter(*min_val, /* is_min */ true);
+    Value v2_max = RefineOuter(*max_val, /* is_min */ false);
+    // The refined range is safe if both sides refine the same instruction. Otherwise, since two
+    // different ranges are combined, the new refined range is safe to pass back to the client if
+    // the extremes of the computed ranges ensure no arithmetic wrap-around anomalies occur.
+    if (min_val->instruction != max_val->instruction) {
+      Value v1_max = RefineOuter(*min_val, /* is_min */ false);
+      Value v2_min = RefineOuter(*max_val, /* is_min */ true);
+      if (!IsConstantValue(v1_max) ||
+          !IsConstantValue(v2_min) ||
+          v1_max.b_constant > v2_min.b_constant) {
+        return false;
+      }
+    }
+    // Did something change?
+    if (v1_min.instruction != min_val->instruction || v2_max.instruction != max_val->instruction) {
+      *min_val = v1_min;
+      *max_val = v2_max;
+      return true;
     }
-  }
-  // Did something change?
-  if (v1_min.instruction != min_val->instruction || v2_max.instruction != max_val->instruction) {
-    *min_val = v1_min;
-    *max_val = v2_max;
-    return true;
   }
   return false;
 }
@@ -277,7 +313,12 @@ InductionVarRange::Value InductionVarRange::GetLinear(HInductionVarAnalysis::Ind
           if (HInductionVarAnalysis::InductionEqual(trip_expr->op_b, info->op_b)) {
             // Analyze cancelled trip with just the positive operand (trip_expr->op_a).
             HInductionVarAnalysis::InductionInfo cancelled_trip(
-                trip->induction_class, trip->operation, trip_expr->op_a, trip->op_b, nullptr);
+                trip->induction_class,
+                trip->operation,
+                trip_expr->op_a,
+                trip->op_b,
+                nullptr,
+                trip->type);
             return GetVal(&cancelled_trip, trip, in_body, is_min);
           }
         } else if (is_min && stride_value == -1) {
@@ -289,9 +330,10 @@ InductionVarRange::Value InductionVarRange::GetLinear(HInductionVarAnalysis::Ind
                 HInductionVarAnalysis::kNeg,
                 nullptr,
                 trip_expr->op_b,
-                nullptr);
+                nullptr,
+                trip->type);
             HInductionVarAnalysis::InductionInfo cancelled_trip(
-                trip->induction_class, trip->operation, &neg, trip->op_b, nullptr);
+                trip->induction_class, trip->operation, &neg, trip->op_b, nullptr, trip->type);
             return SubValue(Value(0), GetVal(&cancelled_trip, trip, in_body, !is_min));
           }
         }
@@ -322,6 +364,12 @@ InductionVarRange::Value InductionVarRange::GetFetch(HInstruction* instruction,
     }
   } else if (instruction->IsArrayLength() && instruction->InputAt(0)->IsNewArray()) {
     return GetFetch(instruction->InputAt(0)->InputAt(0), trip, in_body, is_min);
+  } else if (instruction->IsTypeConversion()) {
+    // Since analysis is 32-bit (or narrower) we allow a widening along the path.
+    if (instruction->AsTypeConversion()->GetInputType() == Primitive::kPrimInt &&
+        instruction->AsTypeConversion()->GetResultType() == Primitive::kPrimLong) {
+      return GetFetch(instruction->InputAt(0), trip, in_body, is_min);
+    }
   } else if (is_min) {
     // Special case for finding minimum: minimum of trip-count in loop-body is 1.
     if (trip != nullptr && in_body && instruction == trip->op_a->fetch) {
@@ -374,7 +422,7 @@ InductionVarRange::Value InductionVarRange::GetVal(HInductionVarAnalysis::Induct
         }
         break;
       case HInductionVarAnalysis::kLinear: {
-        return GetLinear(info, trip, in_body, is_min);
+        return CorrectForType(GetLinear(info, trip, in_body, is_min), info->type);
       }
       case HInductionVarAnalysis::kWrapAround:
       case HInductionVarAnalysis::kPeriodic:
@@ -613,8 +661,12 @@ bool InductionVarRange::GenerateCode(HInductionVarAnalysis::InductionInfo* info,
                                      bool in_body,
                                      bool is_min) const {
   if (info != nullptr) {
-    // Handle current operation.
+    // Verify type safety.
     Primitive::Type type = Primitive::kPrimInt;
+    if (info->type != type) {
+      return false;
+    }
+    // Handle current operation.
     HInstruction* opa = nullptr;
     HInstruction* opb = nullptr;
     switch (info->induction_class) {
@@ -667,13 +719,10 @@ bool InductionVarRange::GenerateCode(HInductionVarAnalysis::InductionInfo* info,
             }
             break;
           case HInductionVarAnalysis::kFetch:
-            if (info->fetch->GetType() == type) {
-              if (graph != nullptr) {
-                *result = info->fetch;  // already in HIR
-              }
-              return true;
+            if (graph != nullptr) {
+              *result = info->fetch;  // already in HIR
             }
-            break;
+            return true;
           case HInductionVarAnalysis::kTripCountInLoop:
           case HInductionVarAnalysis::kTripCountInLoopUnsafe:
             if (!in_body && !is_min) {  // one extra!
index c5c33bd..dc04dc2 100644 (file)
@@ -139,37 +139,40 @@ class InductionVarRangeTest : public CommonCompilerTest {
 
   /** Constructs a trip-count. */
   HInductionVarAnalysis::InductionInfo* CreateTripCount(int32_t tc, bool in_loop, bool safe) {
+    Primitive::Type type = Primitive::kPrimInt;
     if (in_loop && safe) {
       return iva_->CreateTripCount(
-          HInductionVarAnalysis::kTripCountInLoop, CreateConst(tc), nullptr);
+          HInductionVarAnalysis::kTripCountInLoop, CreateConst(tc), nullptr, type);
     } else if (in_loop) {
       return iva_->CreateTripCount(
-          HInductionVarAnalysis::kTripCountInLoopUnsafe, CreateConst(tc), nullptr);
+          HInductionVarAnalysis::kTripCountInLoopUnsafe, CreateConst(tc), nullptr, type);
     } else if (safe) {
       return iva_->CreateTripCount(
-          HInductionVarAnalysis::kTripCountInBody, CreateConst(tc), nullptr);
+          HInductionVarAnalysis::kTripCountInBody, CreateConst(tc), nullptr, type);
     } else {
       return iva_->CreateTripCount(
-          HInductionVarAnalysis::kTripCountInBodyUnsafe, CreateConst(tc), nullptr);
+          HInductionVarAnalysis::kTripCountInBodyUnsafe, CreateConst(tc), nullptr, type);
     }
   }
 
   /** Constructs a linear a * i + b induction. */
   HInductionVarAnalysis::InductionInfo* CreateLinear(int32_t a, int32_t b) {
-    return iva_->CreateInduction(HInductionVarAnalysis::kLinear, CreateConst(a), CreateConst(b));
+    return iva_->CreateInduction(
+        HInductionVarAnalysis::kLinear, CreateConst(a), CreateConst(b), Primitive::kPrimInt);
   }
 
   /** Constructs a range [lo, hi] using a periodic induction. */
   HInductionVarAnalysis::InductionInfo* CreateRange(int32_t lo, int32_t hi) {
     return iva_->CreateInduction(
-        HInductionVarAnalysis::kPeriodic, CreateConst(lo), CreateConst(hi));
+        HInductionVarAnalysis::kPeriodic, CreateConst(lo), CreateConst(hi), Primitive::kPrimInt);
   }
 
   /** Constructs a wrap-around induction consisting of a constant, followed info */
   HInductionVarAnalysis::InductionInfo* CreateWrapAround(
       int32_t initial,
       HInductionVarAnalysis::InductionInfo* info) {
-    return iva_->CreateInduction(HInductionVarAnalysis::kWrapAround, CreateConst(initial), info);
+    return iva_->CreateInduction(
+        HInductionVarAnalysis::kWrapAround, CreateConst(initial), info, Primitive::kPrimInt);
   }
 
   /** Constructs a wrap-around induction consisting of a constant, followed by a range. */
index 9c19ad5..18f45ff 100644 (file)
@@ -180,6 +180,46 @@ class Primitive {
     }
   }
 
+  static int64_t MinValueOfIntegralType(Type type) {
+    switch (type) {
+      case kPrimBoolean:
+        return std::numeric_limits<bool>::min();
+      case kPrimByte:
+        return std::numeric_limits<int8_t>::min();
+      case kPrimChar:
+        return std::numeric_limits<uint16_t>::min();
+      case kPrimShort:
+        return std::numeric_limits<int16_t>::min();
+      case kPrimInt:
+        return std::numeric_limits<int32_t>::min();
+      case kPrimLong:
+        return std::numeric_limits<int64_t>::min();
+      default:
+        LOG(FATAL) << "non integral type";
+    }
+    return 0;
+  }
+
+  static int64_t MaxValueOfIntegralType(Type type) {
+    switch (type) {
+      case kPrimBoolean:
+        return std::numeric_limits<bool>::max();
+      case kPrimByte:
+        return std::numeric_limits<int8_t>::max();
+      case kPrimChar:
+        return std::numeric_limits<uint16_t>::max();
+      case kPrimShort:
+        return std::numeric_limits<int16_t>::max();
+      case kPrimInt:
+        return std::numeric_limits<int32_t>::max();
+      case kPrimLong:
+        return std::numeric_limits<int64_t>::max();
+      default:
+        LOG(FATAL) << "non integral type";
+    }
+    return 0;
+  }
+
  private:
   DISALLOW_IMPLICIT_CONSTRUCTORS(Primitive);
 };
diff --git a/test/530-checker-loops/expected.txt b/test/530-checker-loops/expected.txt
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/test/530-checker-loops1/expected.txt b/test/530-checker-loops1/expected.txt
new file mode 100644 (file)
index 0000000..b0aad4d
--- /dev/null
@@ -0,0 +1 @@
+passed
similarity index 87%
rename from test/530-checker-loops/src/Main.java
rename to test/530-checker-loops1/src/Main.java
index 2e5fd25..948a7b7 100644 (file)
@@ -454,24 +454,87 @@ public class Main {
     return result;
   }
 
-  /// CHECK-START: int Main.linearShort() BCE (before)
+  /// CHECK-START: int Main.linearLong() BCE (before)
   /// CHECK-DAG: BoundsCheck
   //
-  /// CHECK-START: int Main.linearShort() BCE (after)
+  /// CHECK-START: int Main.linearLong() BCE (after)
+  /// CHECK-NOT: BoundsCheck
+  /// CHECK-NOT: Deoptimize
+  private static int linearLong() {
+    int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+    int result = 0;
+    // Induction on constant interval is done in higher precision than necessary,
+    // but truncated at the use as subscript.
+    for (long i = 0; i < 10; i++) {
+      result += x[(int)i];
+    }
+    return result;
+  }
+
+  /// CHECK-START: int Main.linearLongAlt(int[]) BCE (before)
+  /// CHECK-DAG: BoundsCheck
+  //
+  /// CHECK-START: int Main.linearLongAlt(int[]) BCE (after)
+  /// CHECK-NOT: BoundsCheck
+  /// CHECK-NOT: Deoptimize
+  private static int linearLongAlt(int[] x) {
+    int result = 0;
+    // Induction on array length is done in higher precision than necessary,
+    // but truncated at the use as subscript.
+    for (long i = 0; i < x.length; i++) {
+      result += x[(int)i];
+    }
+    return result;
+  }
+
+  /// CHECK-START: int Main.linearShort() BCE (before)
   /// CHECK-DAG: BoundsCheck
   //
   /// CHECK-START: int Main.linearShort() BCE (after)
+  /// CHECK-NOT: BoundsCheck
   /// CHECK-NOT: Deoptimize
   private static int linearShort() {
     int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
     int result = 0;
-    // TODO: make this work
+    // Induction is done in short precision, but fits.
     for (short i = 0; i < 10; i++) {
       result += x[i];
     }
     return result;
   }
 
+  /// CHECK-START: int Main.linearChar() BCE (before)
+  /// CHECK-DAG: BoundsCheck
+  //
+  /// CHECK-START: int Main.linearChar() BCE (after)
+  /// CHECK-NOT: BoundsCheck
+  /// CHECK-NOT: Deoptimize
+  private static int linearChar() {
+    int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+    int result = 0;
+    // Induction is done in char precision, but fits.
+    for (char i = 0; i < 10; i++) {
+      result += x[i];
+    }
+    return result;
+  }
+
+  /// CHECK-START: int Main.linearByte() BCE (before)
+  /// CHECK-DAG: BoundsCheck
+  //
+  /// CHECK-START: int Main.linearByte() BCE (after)
+  /// CHECK-NOT: BoundsCheck
+  /// CHECK-NOT: Deoptimize
+  private static int linearByte() {
+    int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+    int result = 0;
+    // Induction is done in byte precision, but fits.
+    for (byte i = 0; i < 10; i++) {
+      result += x[i];
+    }
+    return result;
+  }
+
   /// CHECK-START: int Main.invariantFromPreLoop(int[], int) BCE (before)
   /// CHECK-DAG: BoundsCheck
   //
@@ -633,6 +696,30 @@ public class Main {
     }
   }
 
+  /// CHECK-START: int[] Main.linearTriangularOOB() BCE (before)
+  /// CHECK-DAG: BoundsCheck
+  //
+  /// CHECK-START: int[] Main.linearTriangularOOB() BCE (after)
+  /// CHECK-DAG: BoundsCheck
+  //
+  /// CHECK-START: int[] Main.linearTriangularOOB() BCE (after)
+  /// CHECK-NOT: Deoptimize
+  private static int[] linearTriangularOOB() {
+    int[] a = new int[200];
+    try {
+      for (int i = 0; i < 200; i++) {
+        // Lower bound must be recognized as lower precision induction with arithmetic
+        // wrap-around to -128 when i exceeds 127.
+        for (int j = (byte) i; j < 200; j++) {
+          a[j] += 1;
+        }
+      }
+    } catch (ArrayIndexOutOfBoundsException e) {
+      return a;
+    }
+    return null;  // failure if this is reached
+  }
+
   //
   // Verifier.
   //
@@ -706,13 +793,25 @@ public class Main {
     expectEquals(55, linearForNEArrayLengthDown(x));
     expectEquals(55, linearDoWhileUp());
     expectEquals(55, linearDoWhileDown());
+    expectEquals(55, linearLong());
+    expectEquals(55, linearLongAlt(x));
     expectEquals(55, linearShort());
+    expectEquals(55, linearChar());
+    expectEquals(55, linearByte());
     expectEquals(55, invariantFromPreLoop(x, 1));
     linearTriangularOnTwoArrayLengths(10);
     linearTriangularOnOneArrayLength(10);
     linearTriangularOnParameter(10);
     linearTriangularVariationsInnerStrict(10);
     linearTriangularVariationsInnerNonStrict(10);
+    {
+      int[] t = linearTriangularOOB();
+      for (int i = 0; i < 200; i++) {
+        expectEquals(i <= 127 ? i + 1 : 128, t[i]);
+      }
+    }
+
+    System.out.println("passed");
   }
 
   private static void expectEquals(int expected, int result) {
index 64be1a2..c644692 100644 (file)
@@ -383,6 +383,55 @@ public class Main {
     }
   }
 
+  /// CHECK-START: void Main.inductionOOB(int[]) BCE (before)
+  /// CHECK-DAG: BoundsCheck
+  //
+  /// CHECK-START: void Main.inductionOOB(int[]) BCE (after)
+  /// CHECK-DAG: BoundsCheck
+  //
+  /// CHECK-START: void Main.inductionOOB(int[]) BCE (after)
+  /// CHECK-NOT: Deoptimize
+  private static void inductionOOB(int[] a) {
+    // Careless range analysis would remove the bounds check.
+    // However, the narrower induction b wraps around arithmetically
+    // before it reaches the end of arrays longer than 127.
+    byte b = 0;
+    for (int i = 0; i < a.length; i++) {
+      a[b++] = i;
+    }
+  }
+
+  /// CHECK-START: void Main.controlOOB(int[]) BCE (before)
+  /// CHECK-DAG: BoundsCheck
+  //
+  /// CHECK-START: void Main.controlOOB(int[]) BCE (after)
+  /// CHECK-DAG: BoundsCheck
+  //
+  /// CHECK-START: void Main.controlOOB(int[]) BCE (after)
+  /// CHECK-NOT: Deoptimize
+  private static void controlOOB(int[] a) {
+    // As above, but now the loop control also wraps around.
+    for (byte i = 0; i < a.length; i++) {
+      a[i] = -i;
+    }
+  }
+
+  /// CHECK-START: void Main.conversionOOB(int[]) BCE (before)
+  /// CHECK-DAG: BoundsCheck
+  //
+  /// CHECK-START: void Main.conversionOOB(int[]) BCE (after)
+  /// CHECK-DAG: BoundsCheck
+  //
+  /// CHECK-START: void Main.conversionOOB(int[]) BCE (after)
+  /// CHECK-NOT: Deoptimize
+  private static void conversionOOB(int[] a) {
+    // As above, but with wrap around caused by an explicit conversion.
+    for (int i = 0; i < a.length; ) {
+      a[i] = i;
+      i = (byte) (i + 1);
+    }
+  }
+
   /// CHECK-START: int[] Main.add() BCE (before)
   /// CHECK-DAG: BoundsCheck
   //
@@ -750,6 +799,8 @@ public class Main {
 
     int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
 
+    int[] a200 = new int[200];
+
     // Sorting.
     int[] sort = { 5, 4, 1, 9, 10, 2, 7, 6, 3, 8 };
     bubble(sort);
@@ -884,6 +935,36 @@ public class Main {
       sResult += 1000;
     }
     expectEquals(1111, sResult);
+    sResult = 0;
+    try {
+      inductionOOB(a200);
+    } catch (ArrayIndexOutOfBoundsException e) {
+      sResult += 1000;
+    }
+    expectEquals(1000, sResult);
+    for (int i = 0; i < 200; i++) {
+      expectEquals(i < 128 ? i : 0, a200[i]);
+    }
+    sResult = 0;
+    try {
+      controlOOB(a200);
+    } catch (ArrayIndexOutOfBoundsException e) {
+      sResult += 1000;
+    }
+    expectEquals(1000, sResult);
+    for (int i = 0; i < 200; i++) {
+      expectEquals(i < 128 ? -i : 0, a200[i]);
+    }
+    sResult = 0;
+    try {
+      conversionOOB(a200);
+    } catch (ArrayIndexOutOfBoundsException e) {
+      sResult += 1000;
+    }
+    expectEquals(1000, sResult);
+    for (int i = 0; i < 200; i++) {
+      expectEquals(i < 128 ? i : 0, a200[i]);
+    }
 
     // Addition.
     {
@@ -989,6 +1070,8 @@ public class Main {
         dynamicBCEAndConstantIndicesAllPrimTypes(x, x1, x2, x3, x4, x5, x6, x7, x8, 0, 10));
     Integer[] x9 = { 9 };
     expectEquals(145, dynamicBCEAndConstantIndexRefType(x, x9, 0, 10));
+
+    System.out.println("passed");
   }
 
   private static void expectEquals(int expected, int result) {