Merge "Remove Quick from tree."
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 0b69810..07bd0e3 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -229,6 +229,7 @@
 
     CHECK_EQ(image_header->storage_mode_, image_storage_mode_);
     switch (image_storage_mode_) {
+      case ImageHeader::kStorageModeLZ4HC:  // Fall-through.
       case ImageHeader::kStorageModeLZ4: {
         const size_t compressed_max_size = LZ4_compressBound(image_data_size);
         compressed_data.reset(new char[compressed_max_size]);
@@ -239,6 +240,8 @@
 
         break;
       }
+      /*
+       * Disabled due to image_test64 flakyness. Both use same decompression. b/27560444
       case ImageHeader::kStorageModeLZ4HC: {
         // Bound is same as non HC.
         const size_t compressed_max_size = LZ4_compressBound(image_data_size);
@@ -249,6 +252,7 @@
             image_data_size);
         break;
       }
+      */
       case ImageHeader::kStorageModeUncompressed: {
         data_size = image_data_size;
         image_data_to_write = image_data;
@@ -264,6 +268,16 @@
       image_data_to_write = &compressed_data[0];
       VLOG(compiler) << "Compressed from " << image_data_size << " to " << data_size << " in "
                      << PrettyDuration(NanoTime() - compress_start_time);
+      if (kIsDebugBuild) {
+        std::unique_ptr<uint8_t[]> temp(new uint8_t[image_data_size]);
+        const size_t decompressed_size = LZ4_decompress_safe(
+            reinterpret_cast<char*>(&compressed_data[0]),
+            reinterpret_cast<char*>(&temp[0]),
+            data_size,
+            image_data_size);
+        CHECK_EQ(decompressed_size, image_data_size);
+        CHECK_EQ(memcmp(image_data, &temp[0], image_data_size), 0) << image_storage_mode_;
+      }
     }
 
     // Write out the image + fields + methods.
@@ -2024,7 +2038,6 @@
   memcpy(copy, orig, ArtMethod::Size(target_ptr_size_));
 
   copy->SetDeclaringClass(GetImageAddress(orig->GetDeclaringClassUnchecked()));
-
   ArtMethod** orig_resolved_methods = orig->GetDexCacheResolvedMethods(target_ptr_size_);
   copy->SetDexCacheResolvedMethods(NativeLocationInImage(orig_resolved_methods), target_ptr_size_);
   GcRoot<mirror::Class>* orig_resolved_types = orig->GetDexCacheResolvedTypes(target_ptr_size_);
diff --git a/compiler/optimizing/induction_var_analysis.cc b/compiler/optimizing/induction_var_analysis.cc
index 82a898a..8d24e26 100644
--- a/compiler/optimizing/induction_var_analysis.cc
+++ b/compiler/optimizing/induction_var_analysis.cc
@@ -53,6 +53,32 @@
   }
 }
 
+/**
+ * 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 @@
       }
     }
 
+    // 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 @@
                        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 @@
   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 @@
     } 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 @@
       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 @@
   //   (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 @@
     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 @@
       } 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 @@
         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 @@
     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 @@
     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 @@
     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 @@
       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 @@
   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 @@
       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 @@
                            (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 @@
   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 @@
                                      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 @@
   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 @@
       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 @@
         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);
       }
     }
   }
diff --git a/compiler/optimizing/induction_var_analysis.h b/compiler/optimizing/induction_var_analysis.h
index 94d2646..f1965f0 100644
--- a/compiler/optimizing/induction_var_analysis.h
+++ b/compiler/optimizing/induction_var_analysis.h
@@ -97,17 +97,20 @@
                   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 @@
 
   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 @@
   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 @@
                              HInstruction* y,
                              InductionOp op,
                              bool is_first_call);
+  InductionInfo* SolveCnv(HTypeConversion* conversion);
 
   // Trip count information.
   void VisitControl(HLoopInformation* loop);
@@ -181,6 +193,11 @@
                 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 @@
   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
diff --git a/compiler/optimizing/induction_var_analysis_test.cc b/compiler/optimizing/induction_var_analysis_test.cc
index 89e4690..0fbb67d 100644
--- a/compiler/optimizing/induction_var_analysis_test.cc
+++ b/compiler/optimizing/induction_var_analysis_test.cc
@@ -202,6 +202,7 @@
   // }
   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 @@
   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 @@
       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 @@
   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 @@
   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 @@
   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 @@
   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 @@
   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 @@
       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 @@
   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 @@
   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 @@
       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 @@
 
   // 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 @@
     } 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
diff --git a/compiler/optimizing/induction_var_range.cc b/compiler/optimizing/induction_var_range.cc
index f9b6910..bc920d9 100644
--- a/compiler/optimizing/induction_var_range.cc
+++ b/compiler/optimizing/induction_var_range.cc
@@ -58,13 +58,13 @@
 }
 
 /**
- * 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 @@
   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 @@
   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::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 @@
           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 @@
                 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 @@
     }
   } 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 @@
         }
         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 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 @@
             }
             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!
diff --git a/compiler/optimizing/induction_var_range_test.cc b/compiler/optimizing/induction_var_range_test.cc
index c5c33bd..dc04dc2 100644
--- a/compiler/optimizing/induction_var_range_test.cc
+++ b/compiler/optimizing/induction_var_range_test.cc
@@ -139,37 +139,40 @@
 
   /** 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. */
diff --git a/runtime/Android.mk b/runtime/Android.mk
index f70e696..fc96acf 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -368,6 +368,7 @@
   mirror/class.h \
   oat.h \
   object_callbacks.h \
+  process_state.h \
   profiler_options.h \
   quick/inline_method_analyser.h \
   runtime.h \
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 2b43dfb..c1115da 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -760,7 +760,8 @@
                                  const std::vector<gc::space::ImageSpace*>& spaces)
     SHARED_REQUIRES(Locks::mutator_lock_) {
   if (m->IsRuntimeMethod()) {
-    CHECK(m->GetDeclaringClass() == nullptr) << PrettyMethod(m);
+    mirror::Class* declaring_class = m->GetDeclaringClassUnchecked();
+    CHECK(declaring_class == nullptr) << declaring_class << " " << PrettyMethod(m);
   } else if (m->IsCopied()) {
     CHECK(m->GetDeclaringClass() != nullptr) << PrettyMethod(m);
   } else if (expected_class != nullptr) {
diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc
index 6073fc8..894ceba 100644
--- a/runtime/gc/collector/mark_sweep.cc
+++ b/runtime/gc/collector/mark_sweep.cc
@@ -845,7 +845,9 @@
 };
 
 size_t MarkSweep::GetThreadCount(bool paused) const {
-  if (heap_->GetThreadPool() == nullptr || !heap_->CareAboutPauseTimes()) {
+  // Use less threads if we are in a background state (non jank perceptible) since we want to leave
+  // more CPU time for the foreground apps.
+  if (heap_->GetThreadPool() == nullptr || !Runtime::Current()->InJankPerceptibleProcessState()) {
     return 1;
   }
   return (paused ? heap_->GetParallelGCThreadCount() : heap_->GetConcGCThreadCount()) + 1;
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 4ff0c6b..a96847f 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -117,6 +117,10 @@
 // For deterministic compilation, we need the heap to be at a well-known address.
 static constexpr uint32_t kAllocSpaceBeginForDeterministicAoT = 0x40000000;
 
+static inline bool CareAboutPauseTimes() {
+  return Runtime::Current()->InJankPerceptibleProcessState();
+}
+
 Heap::Heap(size_t initial_size,
            size_t growth_limit,
            size_t min_free,
@@ -175,8 +179,6 @@
       max_allowed_footprint_(initial_size),
       native_footprint_gc_watermark_(initial_size),
       native_need_to_run_finalization_(false),
-      // Initially assume we perceive jank in case the process state is never updated.
-      process_state_(kProcessStateJankPerceptible),
       concurrent_start_bytes_(std::numeric_limits<size_t>::max()),
       total_bytes_freed_ever_(0),
       total_objects_freed_ever_(0),
@@ -924,17 +926,18 @@
   thread_flip_cond_->Broadcast(self);
 }
 
-void Heap::UpdateProcessState(ProcessState process_state) {
-  if (process_state_ != process_state) {
-    process_state_ = process_state;
+void Heap::UpdateProcessState(ProcessState old_process_state, ProcessState new_process_state) {
+  if (old_process_state != new_process_state) {
+    const bool jank_perceptible = new_process_state == kProcessStateJankPerceptible;
     for (size_t i = 1; i <= kCollectorTransitionStressIterations; ++i) {
       // Start at index 1 to avoid "is always false" warning.
       // Have iteration 1 always transition the collector.
-      TransitionCollector((((i & 1) == 1) == (process_state_ == kProcessStateJankPerceptible))
-                          ? foreground_collector_type_ : background_collector_type_);
+      TransitionCollector((((i & 1) == 1) == jank_perceptible)
+          ? foreground_collector_type_
+          : background_collector_type_);
       usleep(kCollectorTransitionStressWait);
     }
-    if (process_state_ == kProcessStateJankPerceptible) {
+    if (jank_perceptible) {
       // Transition back to foreground right away to prevent jank.
       RequestCollectorTransition(foreground_collector_type_, 0);
     } else {
@@ -2204,8 +2207,8 @@
   } else {
     saved_str = " expanded " + PrettySize(-delta_allocated);
   }
-  VLOG(heap) << "Heap transition to " << process_state_ << " took "
-      << PrettyDuration(duration) << saved_str;
+  VLOG(heap) << "Collector transition to " << collector_type << " took "
+             << PrettyDuration(duration) << saved_str;
 }
 
 void Heap::ChangeCollector(CollectorType collector_type) {
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 9eda422..2925591 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -36,6 +36,7 @@
 #include "globals.h"
 #include "object_callbacks.h"
 #include "offsets.h"
+#include "process_state.h"
 #include "safe_map.h"
 #include "verify_object.h"
 
@@ -116,14 +117,6 @@
 // If true, use thread-local allocation stack.
 static constexpr bool kUseThreadLocalAllocationStack = true;
 
-// The process state passed in from the activity manager, used to determine when to do trimming
-// and compaction.
-enum ProcessState {
-  kProcessStateJankPerceptible = 0,
-  kProcessStateJankImperceptible = 1,
-};
-std::ostream& operator<<(std::ostream& os, const ProcessState& process_state);
-
 class Heap {
  public:
   // If true, measure the total allocation time.
@@ -382,7 +375,7 @@
   collector::GcType WaitForGcToComplete(GcCause cause, Thread* self) REQUIRES(!*gc_complete_lock_);
 
   // Update the heap's process state to a new value, may cause compaction to occur.
-  void UpdateProcessState(ProcessState process_state)
+  void UpdateProcessState(ProcessState old_process_state, ProcessState new_process_state)
       REQUIRES(!*pending_task_lock_, !*gc_complete_lock_);
 
   bool HaveContinuousSpaces() const NO_THREAD_SAFETY_ANALYSIS {
@@ -664,11 +657,6 @@
   void DumpGcPerformanceInfo(std::ostream& os) REQUIRES(!*gc_complete_lock_);
   void ResetGcPerformanceInfo() REQUIRES(!*gc_complete_lock_);
 
-  // Returns true if we currently care about pause times.
-  bool CareAboutPauseTimes() const {
-    return process_state_ == kProcessStateJankPerceptible;
-  }
-
   // Thread pool.
   void CreateThreadPool();
   void DeleteThreadPool();
@@ -1152,9 +1140,6 @@
   // Whether or not we need to run finalizers in the next native allocation.
   bool native_need_to_run_finalization_;
 
-  // Whether or not we currently care about pause times.
-  ProcessState process_state_;
-
   // When num_bytes_allocated_ exceeds this amount then a concurrent GC should be requested so that
   // it completes ahead of an allocation failing.
   size_t concurrent_start_bytes_;
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 5ff1cb7..22bf5f9 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -1520,6 +1520,17 @@
                                      /*out*/error_msg);
 }
 
+void ImageSpace::DumpSections(std::ostream& os) const {
+  const uint8_t* base = Begin();
+  const ImageHeader& header = GetImageHeader();
+  for (size_t i = 0; i < ImageHeader::kSectionCount; ++i) {
+    auto section_type = static_cast<ImageHeader::ImageSections>(i);
+    const ImageSection& section = header.GetImageSection(section_type);
+    os << section_type << " " << reinterpret_cast<const void*>(base + section.Offset())
+       << "-" << reinterpret_cast<const void*>(base + section.End()) << "\n";
+  }
+}
+
 }  // namespace space
 }  // namespace gc
 }  // namespace art
diff --git a/runtime/gc/space/image_space.h b/runtime/gc/space/image_space.h
index f2f4163..c9741d0 100644
--- a/runtime/gc/space/image_space.h
+++ b/runtime/gc/space/image_space.h
@@ -149,6 +149,8 @@
     return GetImageHeader().GetOatFileEnd();
   }
 
+  void DumpSections(std::ostream& os) const;
+
  protected:
   // Tries to initialize an ImageSpace from the given image path, returning null on error.
   //
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index 19584ed..dfb728f 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -388,8 +388,6 @@
       }
       return false;
     }
-    DCHECK_EQ(this->CanAccessMember(access_to, field->GetAccessFlags()),
-              this->CanAccessMember(dex_access_to, field->GetAccessFlags()));
   }
   if (LIKELY(this->CanAccessMember(access_to, field->GetAccessFlags()))) {
     return true;
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index 4ac28ae..d22c0c7 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -223,7 +223,7 @@
 
 static void VMRuntime_updateProcessState(JNIEnv*, jobject, jint process_state) {
   Runtime* runtime = Runtime::Current();
-  runtime->GetHeap()->UpdateProcessState(static_cast<gc::ProcessState>(process_state));
+  runtime->UpdateProcessState(static_cast<ProcessState>(process_state));
 }
 
 static void VMRuntime_trimHeap(JNIEnv* env, jobject) {
diff --git a/runtime/primitive.h b/runtime/primitive.h
index 9c19ad5..18f45ff 100644
--- a/runtime/primitive.h
+++ b/runtime/primitive.h
@@ -180,6 +180,46 @@
     }
   }
 
+  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/runtime/process_state.h b/runtime/process_state.h
new file mode 100644
index 0000000..e8797d6
--- /dev/null
+++ b/runtime/process_state.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_PROCESS_STATE_H_
+#define ART_RUNTIME_PROCESS_STATE_H_
+
+namespace art {
+
+// The process state passed in from the activity manager, used to determine when to do trimming
+// and compaction.
+enum ProcessState {
+  kProcessStateJankPerceptible = 0,
+  kProcessStateJankImperceptible = 1,
+};
+
+std::ostream& operator<<(std::ostream& os, const ProcessState& process_state);
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_PROCESS_STATE_H_
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 5284c93..daeb447 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -209,7 +209,9 @@
       oat_file_manager_(nullptr),
       is_low_memory_mode_(false),
       safe_mode_(false),
-      pruned_dalvik_cache_(false) {
+      pruned_dalvik_cache_(false),
+      // Initially assume we perceive jank in case the process state is never updated.
+      process_state_(kProcessStateJankPerceptible) {
   CheckAsmSupportOffsetsAndSizes();
   std::fill(callee_save_methods_, callee_save_methods_ + arraysize(callee_save_methods_), 0u);
   interpreter::CheckInterpreterAsmConstants();
@@ -1955,4 +1957,10 @@
   return is_low_memory_mode_ ? kLowMemoryMaxLoadFactor : kNormalMaxLoadFactor;
 }
 
+void Runtime::UpdateProcessState(ProcessState process_state) {
+  ProcessState old_process_state = process_state_;
+  process_state_ = process_state;
+  GetHeap()->UpdateProcessState(old_process_state, process_state);
+}
+
 }  // namespace art
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 36ad7f1..ac6e689 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -35,6 +35,7 @@
 #include "method_reference.h"
 #include "object_callbacks.h"
 #include "offsets.h"
+#include "process_state.h"
 #include "profiler_options.h"
 #include "quick/quick_method_frame_info.h"
 #include "runtime_stats.h"
@@ -626,6 +627,13 @@
     pruned_dalvik_cache_ = pruned;
   }
 
+  void UpdateProcessState(ProcessState process_state);
+
+  // Returns true if we currently care about long mutator pause.
+  bool InJankPerceptibleProcessState() const {
+    return process_state_ == kProcessStateJankPerceptible;
+  }
+
  private:
   static void InitPlatformSignalHandlers();
 
@@ -844,6 +852,9 @@
   // Whether the dalvik cache was pruned when initializing the runtime.
   bool pruned_dalvik_cache_;
 
+  // Whether or not we currently care about pause times.
+  ProcessState process_state_;
+
   DISALLOW_COPY_AND_ASSIGN(Runtime);
 };
 std::ostream& operator<<(std::ostream& os, const Runtime::CalleeSaveType& rhs);
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 2a7cd07..4dc6d57 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -46,7 +46,7 @@
 #include "gc/accounting/card_table-inl.h"
 #include "gc/allocator/rosalloc.h"
 #include "gc/heap.h"
-#include "gc/space/space.h"
+#include "gc/space/space-inl.h"
 #include "handle_scope-inl.h"
 #include "indirect_reference_table-inl.h"
 #include "jni_internal.h"
@@ -90,6 +90,8 @@
 pthread_key_t Thread::pthread_key_self_;
 ConditionVariable* Thread::resume_cond_ = nullptr;
 const size_t Thread::kStackOverflowImplicitCheckSize = GetStackOverflowReservedBytes(kRuntimeISA);
+// Enabled for b/27493510. TODO: disable when fixed.
+static constexpr bool kVerifyImageObjectsMarked = true;
 
 // For implicit overflow checks we reserve an extra piece of memory at the bottom
 // of the stack (lowest memory).  The higher portion of the memory
@@ -2716,11 +2718,37 @@
 
  private:
   // Visiting the declaring class is necessary so that we don't unload the class of a method that
-  // is executing. We need to ensure that the code stays mapped.
-  void VisitDeclaringClass(ArtMethod* method) SHARED_REQUIRES(Locks::mutator_lock_) {
+  // is executing. We need to ensure that the code stays mapped. NO_THREAD_SAFETY_ANALYSIS since
+  // the threads do not all hold the heap bitmap lock for parallel GC.
+  void VisitDeclaringClass(ArtMethod* method)
+      SHARED_REQUIRES(Locks::mutator_lock_)
+      NO_THREAD_SAFETY_ANALYSIS {
     mirror::Class* klass = method->GetDeclaringClassUnchecked<kWithoutReadBarrier>();
     // klass can be null for runtime methods.
     if (klass != nullptr) {
+      if (kVerifyImageObjectsMarked) {
+        gc::Heap* const heap = Runtime::Current()->GetHeap();
+        gc::space::ContinuousSpace* space = heap->FindContinuousSpaceFromObject(klass,
+                                                                                /*fail_ok*/true);
+        if (space != nullptr && space->IsImageSpace()) {
+          bool failed = false;
+          if (!space->GetLiveBitmap()->Test(klass)) {
+            failed = true;
+            LOG(INTERNAL_FATAL) << "Unmarked object in image " << *space;
+          } else if (!heap->GetLiveBitmap()->Test(klass)) {
+            failed = true;
+            LOG(INTERNAL_FATAL) << "Unmarked object in image through live bitmap " << *space;
+          }
+          if (failed) {
+            GetThread()->Dump(LOG(INTERNAL_FATAL));
+            space->AsImageSpace()->DumpSections(LOG(INTERNAL_FATAL));
+            LOG(INTERNAL_FATAL) << "Method@" << method->GetDexMethodIndex() << ":" << method
+                                << " klass@" << klass;
+            // Pretty info last in case it crashes.
+            LOG(FATAL) << "Method " << PrettyMethod(method) << " klass " << PrettyClass(klass);
+          }
+        }
+      }
       mirror::Object* new_ref = klass;
       visitor_(&new_ref, -1, this);
       if (new_ref != klass) {
diff --git a/test/064-field-access/expected.txt b/test/064-field-access/expected.txt
index 0af56ba..69a586c 100644
--- a/test/064-field-access/expected.txt
+++ b/test/064-field-access/expected.txt
@@ -1,2 +1,3 @@
 good
 Got expected failure
+Got expected failure
diff --git a/test/064-field-access/smali/SubClassUsingInaccessibleField.smali b/test/064-field-access/smali/SubClassUsingInaccessibleField.smali
new file mode 100644
index 0000000..224b431
--- /dev/null
+++ b/test/064-field-access/smali/SubClassUsingInaccessibleField.smali
@@ -0,0 +1,32 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public LSubClassUsingInaccessibleField;
+
+.super Lother/PublicClass;
+
+.method public constructor <init>()V
+    .registers 1
+    invoke-direct {p0}, Lother/PublicClass;-><init>()V
+    return-void
+.end method
+
+# Regression test for compiler DCHECK() failure (bogus check) when referencing
+# a package-private field from an indirectly inherited package-private class,
+# using this very class as the declaring class in the FieldId, bug: 27684368 .
+.method public test()I
+    .registers 2
+    iget v0, p0, LSubClassUsingInaccessibleField;->otherProtectedClassPackageIntInstanceField:I
+    return v0
+.end method
diff --git a/test/064-field-access/src/Main.java b/test/064-field-access/src/Main.java
index 8dd22ba..5d90129 100644
--- a/test/064-field-access/src/Main.java
+++ b/test/064-field-access/src/Main.java
@@ -16,6 +16,7 @@
 
 import other.PublicClass;
 import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 
 /*
@@ -35,6 +36,20 @@
       // reference
       System.out.println("Got expected failure");
     }
+
+    try {
+      Class c = Class.forName("SubClassUsingInaccessibleField");
+      Object o = c.newInstance();
+      c.getMethod("test").invoke(o, null);
+    } catch (InvocationTargetException ite) {
+      if (ite.getCause() instanceof IllegalAccessError) {
+        System.out.println("Got expected failure");
+      } else {
+        System.out.println("Got unexpected failure " + ite.getCause());
+      }
+    } catch (Exception e) {
+      System.out.println("Got unexpected failure " + e);
+    }
   }
 
   /*
diff --git a/test/530-checker-loops/expected.txt b/test/530-checker-loops/expected.txt
deleted file mode 100644
index e69de29..0000000
--- a/test/530-checker-loops/expected.txt
+++ /dev/null
diff --git a/test/530-checker-loops1/expected.txt b/test/530-checker-loops1/expected.txt
new file mode 100644
index 0000000..b0aad4d
--- /dev/null
+++ b/test/530-checker-loops1/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/530-checker-loops/info.txt b/test/530-checker-loops1/info.txt
similarity index 100%
rename from test/530-checker-loops/info.txt
rename to test/530-checker-loops1/info.txt
diff --git a/test/530-checker-loops/src/Main.java b/test/530-checker-loops1/src/Main.java
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
--- a/test/530-checker-loops/src/Main.java
+++ b/test/530-checker-loops1/src/Main.java
@@ -454,24 +454,87 @@
     return result;
   }
 
+  /// CHECK-START: int Main.linearLong() BCE (before)
+  /// CHECK-DAG: BoundsCheck
+  //
+  /// 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-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 @@
     }
   }
 
+  /// 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 @@
     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) {
diff --git a/test/530-checker-loops2/expected.txt b/test/530-checker-loops2/expected.txt
index e69de29..b0aad4d 100644
--- a/test/530-checker-loops2/expected.txt
+++ b/test/530-checker-loops2/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/530-checker-loops2/src/Main.java b/test/530-checker-loops2/src/Main.java
index 64be1a2..c644692 100644
--- a/test/530-checker-loops2/src/Main.java
+++ b/test/530-checker-loops2/src/Main.java
@@ -383,6 +383,55 @@
     }
   }
 
+  /// 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 @@
 
     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 @@
       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 @@
         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) {