Merge "MIPS64: Add intrinsic support for bit rotation"
diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h
index a82d09e..d6cb65b 100644
--- a/compiler/oat_writer.h
+++ b/compiler/oat_writer.h
@@ -281,7 +281,7 @@
 
   // Offsets of the dex cache arrays for each app dex file. For the
   // boot image, this information is provided by the ImageWriter.
-  SafeMap<const DexFile*, size_t> dex_cache_arrays_offsets_;
+  SafeMap<const DexFile*, size_t> dex_cache_arrays_offsets_;  // DexFiles not owned.
 
   // Offset of the oat data from the start of the mmapped region of the elf file.
   size_t oat_data_offset_;
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 3e6cad8..92a5878 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -1376,8 +1376,7 @@
   LocationSummary* locations = new (GetGraph()->GetArena())
       LocationSummary(deoptimize, LocationSummary::kCallOnSlowPath);
   HInstruction* cond = deoptimize->InputAt(0);
-  DCHECK(cond->IsCondition());
-  if (cond->AsCondition()->NeedsMaterialization()) {
+  if (!cond->IsCondition() || cond->AsCondition()->NeedsMaterialization()) {
     locations->SetInAt(0, Location::RequiresRegister());
   }
 }
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index ffb9b79..f68b11b 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -2353,8 +2353,7 @@
   LocationSummary* locations = new (GetGraph()->GetArena())
       LocationSummary(deoptimize, LocationSummary::kCallOnSlowPath);
   HInstruction* cond = deoptimize->InputAt(0);
-  DCHECK(cond->IsCondition());
-  if (cond->AsCondition()->NeedsMaterialization()) {
+  if (!cond->IsCondition() || cond->AsCondition()->NeedsMaterialization()) {
     locations->SetInAt(0, Location::RequiresRegister());
   }
 }
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index eb20291..f561c97 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -2223,8 +2223,7 @@
   LocationSummary* locations = new (GetGraph()->GetArena())
       LocationSummary(deoptimize, LocationSummary::kCallOnSlowPath);
   HInstruction* cond = deoptimize->InputAt(0);
-  DCHECK(cond->IsCondition());
-  if (cond->AsCondition()->NeedsMaterialization()) {
+  if (!cond->IsCondition() || cond->AsCondition()->NeedsMaterialization()) {
     locations->SetInAt(0, Location::RequiresRegister());
   }
 }
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 2aea859..963eec2 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -1306,8 +1306,7 @@
   LocationSummary* locations = new (GetGraph()->GetArena())
       LocationSummary(deoptimize, LocationSummary::kCallOnSlowPath);
   HInstruction* cond = deoptimize->InputAt(0);
-  DCHECK(cond->IsCondition());
-  if (cond->AsCondition()->NeedsMaterialization()) {
+  if (!cond->IsCondition() || cond->AsCondition()->NeedsMaterialization()) {
     locations->SetInAt(0, Location::Any());
   }
 }
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index bf570f5..ed2e4ca 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -1252,8 +1252,7 @@
   LocationSummary* locations = new (GetGraph()->GetArena())
       LocationSummary(deoptimize, LocationSummary::kCallOnSlowPath);
   HInstruction* cond = deoptimize->InputAt(0);
-  DCHECK(cond->IsCondition());
-  if (cond->AsCondition()->NeedsMaterialization()) {
+  if (!cond->IsCondition() || cond->AsCondition()->NeedsMaterialization()) {
     locations->SetInAt(0, Location::Any());
   }
 }
diff --git a/compiler/optimizing/induction_var_analysis_test.cc b/compiler/optimizing/induction_var_analysis_test.cc
index 20492e7..fd1e334 100644
--- a/compiler/optimizing/induction_var_analysis_test.cc
+++ b/compiler/optimizing/induction_var_analysis_test.cc
@@ -20,7 +20,6 @@
 #include "builder.h"
 #include "gtest/gtest.h"
 #include "induction_var_analysis.h"
-#include "induction_var_range.h"
 #include "nodes.h"
 #include "optimizing_unit_test.h"
 
@@ -522,36 +521,6 @@
   EXPECT_STREQ("periodic(( - (1)), (0))", GetInductionInfo(neg, 0).c_str());
 }
 
-TEST_F(InductionVarAnalysisTest, FindRange) {
-  // Setup:
-  // for (int i = 0; i < 100; i++) {
-  //   k = i << 1;
-  //   k = k + 1;
-  //   a[k] = 0;
-  // }
-  BuildLoopNest(1);
-  HInstruction *shl = InsertInstruction(
-      new (&allocator_) HShl(Primitive::kPrimInt, InsertLocalLoad(basic_[0], 0), constant1_), 0);
-  InsertLocalStore(induc_, shl, 0);
-  HInstruction *add = InsertInstruction(
-      new (&allocator_) HAdd(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant1_), 0);
-  InsertLocalStore(induc_, add, 0);
-  HInstruction* store = InsertArrayStore(induc_, 0);
-  PerformInductionVarAnalysis();
-
-  EXPECT_STREQ("((2) * i + (1))", GetInductionInfo(store->InputAt(1), 0).c_str());
-
-  InductionVarRange range(iva_);
-  InductionVarRange::Value v_min = range.GetMinInduction(store, store->InputAt(1));
-  InductionVarRange::Value v_max = range.GetMaxInduction(store, store->InputAt(1));
-  ASSERT_TRUE(v_min.is_known);
-  EXPECT_EQ(0, v_min.a_constant);
-  EXPECT_EQ(1, v_min.b_constant);
-  ASSERT_TRUE(v_max.is_known);
-  EXPECT_EQ(0, v_max.a_constant);
-  EXPECT_EQ(199, v_max.b_constant);
-}
-
 TEST_F(InductionVarAnalysisTest, FindDeepLoopInduction) {
   // Setup:
   // k = 0;
diff --git a/compiler/optimizing/induction_var_range.cc b/compiler/optimizing/induction_var_range.cc
index db12819..f4842f9 100644
--- a/compiler/optimizing/induction_var_range.cc
+++ b/compiler/optimizing/induction_var_range.cc
@@ -75,6 +75,13 @@
   return v;
 }
 
+static HInstruction* Insert(HBasicBlock* preheader, HInstruction* instruction) {
+  DCHECK(preheader != nullptr);
+  DCHECK(instruction != nullptr);
+  preheader->InsertInstructionBefore(instruction, preheader->GetLastInstruction());
+  return instruction;
+}
+
 //
 // Public class methods.
 //
@@ -94,6 +101,21 @@
   return SimplifyMax(GetInduction(context, instruction, /* is_min */ false));
 }
 
+bool InductionVarRange::CanGenerateCode(HInstruction* context,
+                                        HInstruction* instruction,
+                                        /*out*/bool* top_test) {
+  return GenerateCode(context, instruction, nullptr, nullptr, nullptr, nullptr, top_test);
+}
+
+bool InductionVarRange::GenerateCode(HInstruction* context,
+                                     HInstruction* instruction,
+                                     HGraph* graph,
+                                     HBasicBlock* block,
+                                     /*out*/HInstruction** lower,
+                                     /*out*/HInstruction** upper) {
+  return GenerateCode(context, instruction, graph, block, lower, upper, nullptr);
+}
+
 //
 // Private class methods.
 //
@@ -162,15 +184,15 @@
           case HInductionVarAnalysis::kFetch:
             return GetFetch(info->fetch, trip, in_body, is_min);
           case HInductionVarAnalysis::kTripCountInLoop:
-            if (!in_body) {
-              return is_min ? Value(0)
-                            : GetVal(info->op_b, trip, in_body, is_min);   // one extra!
+            if (!in_body && !is_min) {  // one extra!
+              return GetVal(info->op_b, trip, in_body, is_min);
             }
             FALLTHROUGH_INTENDED;
           case HInductionVarAnalysis::kTripCountInBody:
-            if (in_body) {
-              return is_min ? Value(0)
-                            : SubValue(GetVal(info->op_b, trip, in_body, is_min), Value(1));
+            if (is_min) {
+              return Value(0);
+            } else if (in_body) {
+              return SubValue(GetVal(info->op_b, trip, in_body, is_min), Value(1));
             }
             break;
           default:
@@ -256,9 +278,11 @@
 bool InductionVarRange::GetConstant(HInductionVarAnalysis::InductionInfo* info, int32_t *value) {
   Value v_min = GetVal(info, nullptr, false, /* is_min */ true);
   Value v_max = GetVal(info, nullptr, false, /* is_min */ false);
-  if (v_min.a_constant == 0 && v_max.a_constant == 0 && v_min.b_constant == v_max.b_constant) {
-    *value = v_min.b_constant;
-    return true;
+  if (v_min.is_known && v_max.is_known) {
+    if (v_min.a_constant == 0 && v_max.a_constant == 0 && v_min.b_constant == v_max.b_constant) {
+      *value = v_min.b_constant;
+      return true;
+    }
   }
   return false;
 }
@@ -326,4 +350,129 @@
   return Value();
 }
 
+bool InductionVarRange::GenerateCode(HInstruction* context,
+                                     HInstruction* instruction,
+                                     HGraph* graph,
+                                     HBasicBlock* block,
+                                     /*out*/HInstruction** lower,
+                                     /*out*/HInstruction** upper,
+                                     /*out*/bool* top_test) {
+  HLoopInformation* loop = context->GetBlock()->GetLoopInformation();  // closest enveloping loop
+  if (loop != nullptr) {
+    HBasicBlock* header = loop->GetHeader();
+    bool in_body = context->GetBlock() != header;
+    HInductionVarAnalysis::InductionInfo* info = induction_analysis_->LookupInfo(loop, instruction);
+    HInductionVarAnalysis::InductionInfo* trip =
+        induction_analysis_->LookupInfo(loop, header->GetLastInstruction());
+    if (info != nullptr && trip != nullptr) {
+      if (top_test != nullptr) {
+        *top_test = trip->operation != HInductionVarAnalysis::kTripCountInLoop;
+      }
+      return
+        // Success on lower if invariant (not set), or code can be generated.
+        ((info->induction_class == HInductionVarAnalysis::kInvariant) ||
+            GenerateCode(info, trip, graph, block, lower, in_body, /* is_min */ true)) &&
+        // And success on upper.
+        GenerateCode(info, trip, graph, block, upper, in_body, /* is_min */ false);
+    }
+  }
+  return false;
+}
+
+bool InductionVarRange::GenerateCode(HInductionVarAnalysis::InductionInfo* info,
+                                     HInductionVarAnalysis::InductionInfo* trip,
+                                     HGraph* graph,  // when set, code is generated
+                                     HBasicBlock* block,
+                                     /*out*/HInstruction** result,
+                                     bool in_body,
+                                     bool is_min) {
+  if (info != nullptr) {
+    Primitive::Type type = Primitive::kPrimInt;
+    HInstruction* opa = nullptr;
+    HInstruction* opb = nullptr;
+    int32_t value = 0;
+    switch (info->induction_class) {
+      case HInductionVarAnalysis::kInvariant:
+        // Invariants.
+        switch (info->operation) {
+          case HInductionVarAnalysis::kAdd:
+            if (GenerateCode(info->op_a, trip, graph, block, &opa, in_body, is_min) &&
+                GenerateCode(info->op_b, trip, graph, block, &opb, in_body, is_min)) {
+              if (graph != nullptr) {
+                *result = Insert(block, new (graph->GetArena()) HAdd(type, opa, opb));
+              }
+              return true;
+            }
+            break;
+          case HInductionVarAnalysis::kSub:  // second reversed!
+            if (GenerateCode(info->op_a, trip, graph, block, &opa, in_body, is_min) &&
+                GenerateCode(info->op_b, trip, graph, block, &opb, in_body, !is_min)) {
+              if (graph != nullptr) {
+                *result = Insert(block, new (graph->GetArena()) HSub(type, opa, opb));
+              }
+              return true;
+            }
+            break;
+          case HInductionVarAnalysis::kNeg:  // reversed!
+            if (GenerateCode(info->op_b, trip, graph, block, &opb, in_body, !is_min)) {
+              if (graph != nullptr) {
+                *result = Insert(block, new (graph->GetArena()) HNeg(type, opb));
+              }
+              return true;
+            }
+            break;
+          case HInductionVarAnalysis::kFetch:
+            if (graph != nullptr) {
+              *result = info->fetch;  // already in HIR
+            }
+            return true;
+          case HInductionVarAnalysis::kTripCountInLoop:
+            if (!in_body && !is_min) {  // one extra!
+              return GenerateCode(info->op_b, trip, graph, block, result, in_body, is_min);
+            }
+            FALLTHROUGH_INTENDED;
+          case HInductionVarAnalysis::kTripCountInBody:
+            if (is_min) {
+              if (graph != nullptr) {
+                *result = graph->GetIntConstant(0);
+              }
+              return true;
+            } else if (in_body) {
+              if (GenerateCode(info->op_b, trip, graph, block, &opb, in_body, is_min)) {
+                if (graph != nullptr) {
+                  *result = Insert(block,
+                                   new (graph->GetArena())
+                                       HSub(type, opb, graph->GetIntConstant(1)));
+                }
+                return true;
+              }
+            }
+            break;
+          default:
+            break;
+        }
+        break;
+      case HInductionVarAnalysis::kLinear:
+        // Linear induction a * i + b, for normalized 0 <= i < TC. Restrict to unit stride only
+        // to avoid arithmetic wrap-around situations that are hard to guard against.
+        if (GetConstant(info->op_a, &value)) {
+          if (value == 1 || value == -1) {
+            const bool is_min_a = value == 1 ? is_min : !is_min;
+            if (GenerateCode(trip,       trip, graph, block, &opa, in_body, is_min_a) &&
+                GenerateCode(info->op_b, trip, graph, block, &opb, in_body, is_min)) {
+              if (graph != nullptr) {
+                *result = Insert(block, new (graph->GetArena()) HAdd(type, opa, opb));
+              }
+              return true;
+            }
+          }
+        }
+        break;
+      default:  // TODO(ajcbik): add more cases
+        break;
+    }
+  }
+  return false;
+}
+
 }  // namespace art
diff --git a/compiler/optimizing/induction_var_range.h b/compiler/optimizing/induction_var_range.h
index dbdd2ee..7fa5a26 100644
--- a/compiler/optimizing/induction_var_range.h
+++ b/compiler/optimizing/induction_var_range.h
@@ -68,6 +68,33 @@
    */
   Value GetMaxInduction(HInstruction* context, HInstruction* instruction);
 
+  /**
+   * Returns true if range analysis is able to generate code for the lower and upper bound
+   * expressions on the instruction in the given context. Output parameter top_test denotes
+   * whether a top test is needed to protect the trip-count expression evaluation.
+   */
+  bool CanGenerateCode(HInstruction* context, HInstruction* instruction, /*out*/bool* top_test);
+
+  /**
+   * Generates the actual code in the HIR for the lower and upper bound expressions on the
+   * instruction in the given context. Code for the lower and upper bound expression are
+   * generated in given block and graph and are returned in lower and upper, respectively.
+   * For a loop invariant, lower is not set.
+   *
+   * For example, given expression x+i with range [0, 5] for i, calling this method
+   * will generate the following sequence:
+   *
+   * block:
+   *   lower: add x, 0
+   *   upper: add x, 5
+   */
+  bool GenerateCode(HInstruction* context,
+                    HInstruction* instruction,
+                    HGraph* graph,
+                    HBasicBlock* block,
+                    /*out*/HInstruction** lower,
+                    /*out*/HInstruction** upper);
+
  private:
   //
   // Private helper methods.
@@ -102,6 +129,27 @@
   static Value DivValue(Value v1, Value v2);
   static Value MergeVal(Value v1, Value v2, bool is_min);
 
+  /**
+   * Generates code for lower/upper expression in the HIR. Returns true on success.
+   * With graph == nullptr, the method can be used to determine if code generation
+   * would be successful without generating actual code yet.
+   */
+  bool GenerateCode(HInstruction* context,
+                    HInstruction* instruction,
+                    HGraph* graph,
+                    HBasicBlock* block,
+                    /*out*/HInstruction** lower,
+                    /*out*/HInstruction** upper,
+                    bool* top_test);
+
+  static bool GenerateCode(HInductionVarAnalysis::InductionInfo* info,
+                           HInductionVarAnalysis::InductionInfo* trip,
+                           HGraph* graph,
+                           HBasicBlock* block,
+                           /*out*/HInstruction** result,
+                           bool in_body,
+                           bool is_min);
+
   /** Results of prior induction variable analysis. */
   HInductionVarAnalysis *induction_analysis_;
 
diff --git a/compiler/optimizing/induction_var_range_test.cc b/compiler/optimizing/induction_var_range_test.cc
index 4497a88..56f661e 100644
--- a/compiler/optimizing/induction_var_range_test.cc
+++ b/compiler/optimizing/induction_var_range_test.cc
@@ -49,12 +49,52 @@
   /** Constructs bare minimum graph. */
   void BuildGraph() {
     graph_->SetNumberOfVRegs(1);
-    HBasicBlock* entry_block = new (&allocator_) HBasicBlock(graph_);
-    HBasicBlock* exit_block = new (&allocator_) HBasicBlock(graph_);
-    graph_->AddBlock(entry_block);
-    graph_->AddBlock(exit_block);
-    graph_->SetEntryBlock(entry_block);
-    graph_->SetExitBlock(exit_block);
+    entry_block_ = new (&allocator_) HBasicBlock(graph_);
+    exit_block_ = new (&allocator_) HBasicBlock(graph_);
+    graph_->AddBlock(entry_block_);
+    graph_->AddBlock(exit_block_);
+    graph_->SetEntryBlock(entry_block_);
+    graph_->SetExitBlock(exit_block_);
+  }
+
+  /** Constructs loop with given upper bound. */
+  void BuildLoop(HInstruction* upper) {
+    // Control flow.
+    loop_preheader_ = new (&allocator_) HBasicBlock(graph_);
+    graph_->AddBlock(loop_preheader_);
+    HBasicBlock* loop_header = new (&allocator_) HBasicBlock(graph_);
+    graph_->AddBlock(loop_header);
+    HBasicBlock* loop_body = new (&allocator_) HBasicBlock(graph_);
+    graph_->AddBlock(loop_body);
+    entry_block_->AddSuccessor(loop_preheader_);
+    loop_preheader_->AddSuccessor(loop_header);
+    loop_header->AddSuccessor(loop_body);
+    loop_header->AddSuccessor(exit_block_);
+    loop_body->AddSuccessor(loop_header);
+    // Instructions.
+    HLocal* induc = new (&allocator_) HLocal(0);
+    entry_block_->AddInstruction(induc);
+    loop_preheader_->AddInstruction(
+        new (&allocator_) HStoreLocal(induc, graph_->GetIntConstant(0)));  // i = 0
+    loop_preheader_->AddInstruction(new (&allocator_) HGoto());
+    HInstruction* load = new (&allocator_) HLoadLocal(induc, Primitive::kPrimInt);
+          loop_header->AddInstruction(load);
+    condition_ = new (&allocator_) HLessThan(load, upper);
+    loop_header->AddInstruction(condition_);
+    loop_header->AddInstruction(new (&allocator_) HIf(condition_));  // i < u
+    load = new (&allocator_) HLoadLocal(induc, Primitive::kPrimInt);
+    loop_body->AddInstruction(load);
+    increment_ = new (&allocator_) HAdd(Primitive::kPrimInt, load, graph_->GetIntConstant(1));
+    loop_body->AddInstruction(increment_);
+    loop_body->AddInstruction(new (&allocator_) HStoreLocal(induc, increment_));  // i++
+    loop_body->AddInstruction(new (&allocator_) HGoto());
+    exit_block_->AddInstruction(new (&allocator_) HReturnVoid());
+  }
+
+  /** Performs induction variable analysis. */
+  void PerformInductionVarAnalysis() {
+    ASSERT_TRUE(graph_->TryBuildingSsa());
+    iva_->Run();
   }
 
   /** Constructs an invariant. */
@@ -146,15 +186,20 @@
   ArenaPool pool_;
   ArenaAllocator allocator_;
   HGraph* graph_;
+  HBasicBlock* entry_block_;
+  HBasicBlock* exit_block_;
+  HBasicBlock* loop_preheader_;
   HInductionVarAnalysis* iva_;
 
-  // Two dummy instructions.
+  // Instructions.
+  HInstruction* condition_;
+  HInstruction* increment_;
   HReturnVoid x_;
   HReturnVoid y_;
 };
 
 //
-// The actual InductionVarRange tests.
+// Tests on static methods.
 //
 
 TEST_F(InductionVarRangeTest, GetMinMaxNull) {
@@ -349,4 +394,81 @@
   ExpectEqual(Value(), MaxValue(Value(55), Value(&y_, 1, -50)));
 }
 
+//
+// Tests on instance methods.
+//
+
+TEST_F(InductionVarRangeTest, FindRangeConstantTripCount) {
+  BuildLoop(graph_->GetIntConstant(1000));
+  PerformInductionVarAnalysis();
+  InductionVarRange range(iva_);
+
+  // In context of header: known.
+  ExpectEqual(Value(0), range.GetMinInduction(condition_, condition_->InputAt(0)));
+  ExpectEqual(Value(1000), range.GetMaxInduction(condition_, condition_->InputAt(0)));
+
+  // In context of loop-body: known.
+  ExpectEqual(Value(0), range.GetMinInduction(increment_, condition_->InputAt(0)));
+  ExpectEqual(Value(999), range.GetMaxInduction(increment_, condition_->InputAt(0)));
+  ExpectEqual(Value(1), range.GetMinInduction(increment_, increment_));
+  ExpectEqual(Value(1000), range.GetMaxInduction(increment_, increment_));
+}
+
+TEST_F(InductionVarRangeTest, FindRangeSymbolicTripCount) {
+  HInstruction* parameter = new (&allocator_) HParameterValue(0, Primitive::kPrimInt);
+  entry_block_->AddInstruction(parameter);
+  BuildLoop(parameter);
+  PerformInductionVarAnalysis();
+  InductionVarRange range(iva_);
+
+  // In context of header: full range unknown.
+  ExpectEqual(Value(0), range.GetMinInduction(condition_, condition_->InputAt(0)));
+  ExpectEqual(Value(), range.GetMaxInduction(condition_, condition_->InputAt(0)));
+
+  // In context of loop-body: known.
+  ExpectEqual(Value(0), range.GetMinInduction(increment_, condition_->InputAt(0)));
+  ExpectEqual(Value(parameter, 1, -1), range.GetMaxInduction(increment_, condition_->InputAt(0)));
+  ExpectEqual(Value(1), range.GetMinInduction(increment_, increment_));
+  ExpectEqual(Value(parameter, 1, 0), range.GetMaxInduction(increment_, increment_));
+}
+
+TEST_F(InductionVarRangeTest, CodeGeneration) {
+  HInstruction* parameter = new (&allocator_) HParameterValue(0, Primitive::kPrimInt);
+  entry_block_->AddInstruction(parameter);
+  BuildLoop(parameter);
+  PerformInductionVarAnalysis();
+  InductionVarRange range(iva_);
+
+  HInstruction* lower = nullptr;
+  HInstruction* upper = nullptr;
+  bool top_test = false;
+
+  // Can generate code in context of loop-body only.
+  EXPECT_FALSE(range.CanGenerateCode(condition_, condition_->InputAt(0), &top_test));
+  ASSERT_TRUE(range.CanGenerateCode(increment_, condition_->InputAt(0), &top_test));
+  EXPECT_TRUE(top_test);
+
+  // Generates code.
+  EXPECT_TRUE(range.GenerateCode(
+      increment_, condition_->InputAt(0), graph_, loop_preheader_, &lower, &upper));
+
+  // Verify lower is 0+0.
+  ASSERT_TRUE(lower != nullptr);
+  ASSERT_TRUE(lower->IsAdd());
+  ASSERT_TRUE(lower->InputAt(0)->IsIntConstant());
+  EXPECT_EQ(0, lower->InputAt(0)->AsIntConstant()->GetValue());
+  ASSERT_TRUE(lower->InputAt(1)->IsIntConstant());
+  EXPECT_EQ(0, lower->InputAt(1)->AsIntConstant()->GetValue());
+
+  // Verify upper is (V-1)+0
+  ASSERT_TRUE(upper != nullptr);
+  ASSERT_TRUE(upper->IsAdd());
+  ASSERT_TRUE(upper->InputAt(0)->IsSub());
+  EXPECT_TRUE(upper->InputAt(0)->InputAt(0)->IsParameterValue());
+  ASSERT_TRUE(upper->InputAt(0)->InputAt(1)->IsIntConstant());
+  EXPECT_EQ(1, upper->InputAt(0)->InputAt(1)->AsIntConstant()->GetValue());
+  ASSERT_TRUE(upper->InputAt(1)->IsIntConstant());
+  EXPECT_EQ(0, upper->InputAt(1)->AsIntConstant()->GetValue());
+}
+
 }  // namespace art
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index d468540..7814eb9 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -73,6 +73,7 @@
   void VisitInstanceOf(HInstanceOf* instruction) OVERRIDE;
   void VisitFakeString(HFakeString* fake_string) OVERRIDE;
   void VisitInvoke(HInvoke* invoke) OVERRIDE;
+  void VisitDeoptimize(HDeoptimize* deoptimize) OVERRIDE;
 
   bool CanEnsureNotNullAt(HInstruction* instr, HInstruction* at) const;
 
@@ -1151,4 +1152,16 @@
   }
 }
 
+void InstructionSimplifierVisitor::VisitDeoptimize(HDeoptimize* deoptimize) {
+  HInstruction* cond = deoptimize->InputAt(0);
+  if (cond->IsConstant()) {
+    if (cond->AsIntConstant()->IsZero()) {
+      // Never deopt: instruction can be removed.
+      deoptimize->GetBlock()->RemoveInstruction(deoptimize);
+    } else {
+      // Always deopt.
+    }
+  }
+}
+
 }  // namespace art
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 395649e..81622e1 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -1208,7 +1208,7 @@
   uint8_t* raw_arrays = nullptr;
   if (dex_file.GetOatDexFile() != nullptr &&
       dex_file.GetOatDexFile()->GetDexCacheArrays() != nullptr) {
-    raw_arrays = const_cast<uint8_t*>(dex_file.GetOatDexFile()->GetDexCacheArrays());
+    raw_arrays = dex_file.GetOatDexFile()->GetDexCacheArrays();
   } else if (dex_file.NumStringIds() != 0u || dex_file.NumTypeIds() != 0u ||
       dex_file.NumMethodIds() != 0u || dex_file.NumFieldIds() != 0u) {
     // NOTE: We "leak" the raw_arrays because we never destroy the dex cache.
@@ -6369,6 +6369,21 @@
   }
 }
 
+void ClassLinker::InsertDexFileInToClassLoader(mirror::Object* dex_file,
+                                               mirror::ClassLoader* class_loader) {
+  DCHECK(dex_file != nullptr);
+  DCHECK(class_loader != nullptr);
+  Thread* const self = Thread::Current();
+  WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
+  ClassTable* const table = class_loader->GetClassTable();
+  DCHECK(table != nullptr);
+  if (table->InsertDexFile(dex_file)) {
+    // It was not already inserted, perform the write barrier to let the GC know the class loader's
+    // class table was modified.
+    Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(class_loader);
+  }
+}
+
 void ClassLinker::CleanupClassLoaders() {
   Thread* const self = Thread::Current();
   WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index fd30a46..a2d38ac 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -526,8 +526,8 @@
 
   // Clean up class loaders, this needs to happen after JNI weak globals are cleared.
   void CleanupClassLoaders()
-      SHARED_REQUIRES(Locks::mutator_lock_)
-      REQUIRES(!Locks::classlinker_classes_lock_);
+      REQUIRES(!Locks::classlinker_classes_lock_)
+      SHARED_REQUIRES(Locks::mutator_lock_);
 
   // Unlike GetOrCreateAllocatorForClassLoader, GetAllocatorForClassLoader asserts that the
   // allocator for this class loader is already created.
@@ -537,8 +537,12 @@
   // Return the linear alloc for a class loader if it is already allocated, otherwise allocate and
   // set it. TODO: Consider using a lock other than classlinker_classes_lock_.
   static LinearAlloc* GetOrCreateAllocatorForClassLoader(mirror::ClassLoader* class_loader)
-      SHARED_REQUIRES(Locks::mutator_lock_)
-      REQUIRES(!Locks::classlinker_classes_lock_);
+      REQUIRES(!Locks::classlinker_classes_lock_)
+      SHARED_REQUIRES(Locks::mutator_lock_);
+
+  void InsertDexFileInToClassLoader(mirror::Object* dex_file, mirror::ClassLoader* class_loader)
+      REQUIRES(!Locks::classlinker_classes_lock_)
+      SHARED_REQUIRES(Locks::mutator_lock_);
 
  private:
   struct ClassLoaderData {
diff --git a/runtime/class_table-inl.h b/runtime/class_table-inl.h
index dc60a2c..aef02b6 100644
--- a/runtime/class_table-inl.h
+++ b/runtime/class_table-inl.h
@@ -37,6 +37,9 @@
       visitor.VisitRoot(root.AddressWithoutBarrier());
     }
   }
+  for (GcRoot<mirror::Object>& root : dex_files_) {
+    visitor.VisitRoot(root.AddressWithoutBarrier());
+  }
 }
 
 }  // namespace art
diff --git a/runtime/class_table.cc b/runtime/class_table.cc
index 4b0cbc8..3ed1c95 100644
--- a/runtime/class_table.cc
+++ b/runtime/class_table.cc
@@ -137,4 +137,15 @@
   return ComputeModifiedUtf8Hash(descriptor);
 }
 
+bool ClassTable::InsertDexFile(mirror::Object* dex_file) {
+  DCHECK(dex_file != nullptr);
+  for (GcRoot<mirror::Object>& root : dex_files_) {
+    if (root.Read() == dex_file) {
+      return false;
+    }
+  }
+  dex_files_.push_back(GcRoot<mirror::Object>(dex_file));
+  return true;
+}
+
 }  // namespace art
diff --git a/runtime/class_table.h b/runtime/class_table.h
index 727392e..002bb56 100644
--- a/runtime/class_table.h
+++ b/runtime/class_table.h
@@ -50,12 +50,14 @@
 
   // Used by image writer for checking.
   bool Contains(mirror::Class* klass)
-      REQUIRES(Locks::classlinker_classes_lock_) SHARED_REQUIRES(Locks::mutator_lock_);
+      REQUIRES(Locks::classlinker_classes_lock_)
+      SHARED_REQUIRES(Locks::mutator_lock_);
 
   // Freeze the current class tables by allocating a new table and never updating or modifying the
   // existing table. This helps prevents dirty pages after caused by inserting after zygote fork.
   void FreezeSnapshot()
-      REQUIRES(Locks::classlinker_classes_lock_) SHARED_REQUIRES(Locks::mutator_lock_);
+      REQUIRES(Locks::classlinker_classes_lock_)
+      SHARED_REQUIRES(Locks::mutator_lock_);
 
   // Returns the number of classes in previous snapshots.
   size_t NumZygoteClasses() const SHARED_REQUIRES(Locks::classlinker_classes_lock_);
@@ -65,17 +67,18 @@
 
   // Update a class in the table with the new class. Returns the existing class which was replaced.
   mirror::Class* UpdateClass(const char* descriptor, mirror::Class* new_klass, size_t hash)
-      REQUIRES(Locks::classlinker_classes_lock_) SHARED_REQUIRES(Locks::mutator_lock_);
+      REQUIRES(Locks::classlinker_classes_lock_)
+      SHARED_REQUIRES(Locks::mutator_lock_);
 
   // NO_THREAD_SAFETY_ANALYSIS for object marking requiring heap bitmap lock.
   template<class Visitor>
   void VisitRoots(Visitor& visitor)
-      SHARED_REQUIRES(Locks::classlinker_classes_lock_, Locks::mutator_lock_)
-      NO_THREAD_SAFETY_ANALYSIS;
+      NO_THREAD_SAFETY_ANALYSIS
+      SHARED_REQUIRES(Locks::classlinker_classes_lock_, Locks::mutator_lock_);
   template<class Visitor>
   void VisitRoots(const Visitor& visitor)
-      SHARED_REQUIRES(Locks::classlinker_classes_lock_, Locks::mutator_lock_)
-      NO_THREAD_SAFETY_ANALYSIS;
+      NO_THREAD_SAFETY_ANALYSIS
+      SHARED_REQUIRES(Locks::classlinker_classes_lock_, Locks::mutator_lock_);
 
   // Return false if the callback told us to exit.
   bool Visit(ClassVisitor* visitor)
@@ -85,13 +88,21 @@
       SHARED_REQUIRES(Locks::classlinker_classes_lock_, Locks::mutator_lock_);
 
   void Insert(mirror::Class* klass)
-      REQUIRES(Locks::classlinker_classes_lock_) SHARED_REQUIRES(Locks::mutator_lock_);
+      REQUIRES(Locks::classlinker_classes_lock_)
+      SHARED_REQUIRES(Locks::mutator_lock_);
   void InsertWithHash(mirror::Class* klass, size_t hash)
-      REQUIRES(Locks::classlinker_classes_lock_) SHARED_REQUIRES(Locks::mutator_lock_);
+      REQUIRES(Locks::classlinker_classes_lock_)
+      SHARED_REQUIRES(Locks::mutator_lock_);
 
   // Returns true if the class was found and removed, false otherwise.
   bool Remove(const char* descriptor)
-      REQUIRES(Locks::classlinker_classes_lock_) SHARED_REQUIRES(Locks::mutator_lock_);
+      REQUIRES(Locks::classlinker_classes_lock_)
+      SHARED_REQUIRES(Locks::mutator_lock_);
+
+  // Return true if we inserted the dex file, false if it already exists.
+  bool InsertDexFile(mirror::Object* dex_file)
+      REQUIRES(Locks::classlinker_classes_lock_)
+      SHARED_REQUIRES(Locks::mutator_lock_);
 
  private:
   class ClassDescriptorHashEquals {
@@ -123,6 +134,9 @@
   // TODO: shard lock to have one per class loader.
   // We have a vector to help prevent dirty pages after the zygote forks by calling FreezeSnapshot.
   std::vector<ClassSet> classes_ GUARDED_BY(Locks::classlinker_classes_lock_);
+  // Dex files used by the class loader which may not be owned by the class loader. We keep these
+  // live so that we do not have issues closing any of the dex files.
+  std::vector<GcRoot<mirror::Object>> dex_files_ GUARDED_BY(Locks::classlinker_classes_lock_);
 };
 
 }  // namespace art
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index 4eea3f3..8b2f4d8 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -243,7 +243,8 @@
                                         jclass,
                                         jstring javaName,
                                         jobject javaLoader,
-                                        jobject cookie) {
+                                        jobject cookie,
+                                        jobject dexFile) {
   std::vector<const DexFile*> dex_files;
   const OatFile* oat_file;
   if (!ConvertJavaArrayToDexFiles(env, cookie, /*out*/ dex_files, /*out*/ oat_file)) {
@@ -276,6 +277,10 @@
                                                         class_loader,
                                                         *dex_file,
                                                         *dex_class_def);
+      // Add the used dex file. This only required for the DexFile.loadClass API since normal
+      // class loaders already keep their dex files live.
+      class_linker->InsertDexFileInToClassLoader(soa.Decode<mirror::Object*>(dexFile),
+                                                 class_loader.Get());
       if (result != nullptr) {
         VLOG(class_linker) << "DexFile_defineClassNative returning " << result
                            << " for " << class_name.c_str();
@@ -424,8 +429,13 @@
 
 static JNINativeMethod gMethods[] = {
   NATIVE_METHOD(DexFile, closeDexFile, "(Ljava/lang/Object;)Z"),
-  NATIVE_METHOD(DexFile, defineClassNative,
-                "(Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/lang/Object;)Ljava/lang/Class;"),
+  NATIVE_METHOD(DexFile,
+                defineClassNative,
+                "(Ljava/lang/String;"
+                "Ljava/lang/ClassLoader;"
+                "Ljava/lang/Object;"
+                "Ldalvik/system/DexFile;"
+                ")Ljava/lang/Class;"),
   NATIVE_METHOD(DexFile, getClassNameList, "(Ljava/lang/Object;)[Ljava/lang/String;"),
   NATIVE_METHOD(DexFile, isDexOptNeeded, "(Ljava/lang/String;)Z"),
   NATIVE_METHOD(DexFile, getDexOptNeeded,
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index e861921..a162a4e 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -18,6 +18,7 @@
 
 #include <dlfcn.h>
 #include <string.h>
+#include <type_traits>
 #include <unistd.h>
 
 #include <cstdlib>
@@ -388,13 +389,13 @@
   // Readjust to be non-inclusive upper bound.
   end_ += sizeof(uint32_t);
 
-  bss_begin_ = elf_file_->FindDynamicSymbolAddress("oatbss");
+  bss_begin_ = const_cast<uint8_t*>(elf_file_->FindDynamicSymbolAddress("oatbss"));
   if (bss_begin_ == nullptr) {
     // No .bss section. Clear dlerror().
     bss_end_ = nullptr;
     dlerror();
   } else {
-    bss_end_ = elf_file_->FindDynamicSymbolAddress("oatbsslastword");
+    bss_end_ = const_cast<uint8_t*>(elf_file_->FindDynamicSymbolAddress("oatbsslastword"));
     if (bss_end_ == nullptr) {
       *error_msg = StringPrintf("Failed to find oatbasslastword symbol in '%s'",
                                 file->GetPath().c_str());
@@ -407,10 +408,31 @@
   return Setup(abs_dex_location, error_msg);
 }
 
+// Read an unaligned entry from the OatDexFile data in OatFile and advance the read
+// position by the number of bytes read, i.e. sizeof(T).
+// Return true on success, false if the read would go beyond the end of the OatFile.
+template <typename T>
+inline static bool ReadOatDexFileData(const OatFile& oat_file,
+                                      /*inout*/const uint8_t** oat,
+                                      /*out*/T* value) {
+  DCHECK(oat != nullptr);
+  DCHECK(value != nullptr);
+  DCHECK_LE(*oat, oat_file.End());
+  if (UNLIKELY(static_cast<size_t>(oat_file.End() - *oat) < sizeof(T))) {
+    return false;
+  }
+  static_assert(std::is_trivial<T>::value, "T must be a trivial type");
+  typedef __attribute__((__aligned__(1))) T unaligned_type;
+  *value = *reinterpret_cast<const unaligned_type*>(*oat);
+  *oat += sizeof(T);
+  return true;
+}
+
 bool OatFile::Setup(const char* abs_dex_location, std::string* error_msg) {
   if (!GetOatHeader().IsValid()) {
     std::string cause = GetOatHeader().GetValidationErrorMessage();
-    *error_msg = StringPrintf("Invalid oat header for '%s': %s", GetLocation().c_str(),
+    *error_msg = StringPrintf("Invalid oat header for '%s': %s",
+                              GetLocation().c_str(),
                               cause.c_str());
     return false;
   }
@@ -424,35 +446,42 @@
   oat += GetOatHeader().GetKeyValueStoreSize();
   if (oat > End()) {
     *error_msg = StringPrintf("In oat file '%s' found truncated variable-size data: "
-                              "%p + %zd + %ud <= %p", GetLocation().c_str(),
-                              Begin(), sizeof(OatHeader), GetOatHeader().GetKeyValueStoreSize(),
+                                  "%p + %zu + %u <= %p",
+                              GetLocation().c_str(),
+                              Begin(),
+                              sizeof(OatHeader),
+                              GetOatHeader().GetKeyValueStoreSize(),
                               End());
     return false;
   }
 
   size_t pointer_size = GetInstructionSetPointerSize(GetOatHeader().GetInstructionSet());
-  const uint8_t* dex_cache_arrays = bss_begin_;
+  uint8_t* dex_cache_arrays = bss_begin_;
   uint32_t dex_file_count = GetOatHeader().GetDexFileCount();
   oat_dex_files_storage_.reserve(dex_file_count);
   for (size_t i = 0; i < dex_file_count; i++) {
-    uint32_t dex_file_location_size = *reinterpret_cast<const uint32_t*>(oat);
-    if (UNLIKELY(dex_file_location_size == 0U)) {
-      *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd with empty location name",
-                                GetLocation().c_str(), i);
+    uint32_t dex_file_location_size;
+    if (UNLIKELY(!ReadOatDexFileData(*this, &oat, &dex_file_location_size))) {
+      *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu truncated after dex file "
+                                    "location size",
+                                GetLocation().c_str(),
+                                i);
       return false;
     }
-    oat += sizeof(dex_file_location_size);
-    if (UNLIKELY(oat > End())) {
-      *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd truncated after dex file "
-                                "location size", GetLocation().c_str(), i);
+    if (UNLIKELY(dex_file_location_size == 0U)) {
+      *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu with empty location name",
+                                GetLocation().c_str(),
+                                i);
       return false;
     }
 
     const char* dex_file_location_data = reinterpret_cast<const char*>(oat);
     oat += dex_file_location_size;
     if (UNLIKELY(oat > End())) {
-      *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd with truncated dex file "
-                                "location", GetLocation().c_str(), i);
+      *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu with truncated dex file "
+                                    "location",
+                                GetLocation().c_str(),
+                                i);
       return false;
     }
 
@@ -460,46 +489,61 @@
         abs_dex_location,
         std::string(dex_file_location_data, dex_file_location_size));
 
-    uint32_t dex_file_checksum = *reinterpret_cast<const uint32_t*>(oat);
-    oat += sizeof(dex_file_checksum);
-    if (UNLIKELY(oat > End())) {
-      *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd for '%s' truncated after "
-                                "dex file checksum", GetLocation().c_str(), i,
+    uint32_t dex_file_checksum;
+    if (UNLIKELY(!ReadOatDexFileData(*this, &oat, &dex_file_checksum))) {
+      *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' truncated after "
+                                    "dex file checksum",
+                                GetLocation().c_str(),
+                                i,
                                 dex_file_location.c_str());
       return false;
     }
 
-    uint32_t dex_file_offset = *reinterpret_cast<const uint32_t*>(oat);
+    uint32_t dex_file_offset;
+    if (UNLIKELY(!ReadOatDexFileData(*this, &oat, &dex_file_offset))) {
+      *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' truncated "
+                                    "after dex file offsets",
+                                GetLocation().c_str(),
+                                i,
+                                dex_file_location.c_str());
+      return false;
+    }
     if (UNLIKELY(dex_file_offset == 0U)) {
-      *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd for '%s' with zero dex "
-                                "file offset", GetLocation().c_str(), i, dex_file_location.c_str());
+      *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with zero dex "
+                                    "file offset",
+                                GetLocation().c_str(),
+                                i,
+                                dex_file_location.c_str());
       return false;
     }
     if (UNLIKELY(dex_file_offset > Size())) {
-      *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd for '%s' with dex file "
-                                "offset %ud > %zd", GetLocation().c_str(), i,
-                                dex_file_location.c_str(), dex_file_offset, Size());
-      return false;
-    }
-    oat += sizeof(dex_file_offset);
-    if (UNLIKELY(oat > End())) {
-      *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd for '%s' truncated "
-                                "after dex file offsets", GetLocation().c_str(), i,
-                                dex_file_location.c_str());
+      *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with dex file "
+                                    "offset %u > %zu",
+                                GetLocation().c_str(),
+                                i,
+                                dex_file_location.c_str(),
+                                dex_file_offset,
+                                Size());
       return false;
     }
 
     const uint8_t* dex_file_pointer = Begin() + dex_file_offset;
     if (UNLIKELY(!DexFile::IsMagicValid(dex_file_pointer))) {
-      *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd for '%s' with invalid "
-                                "dex file magic '%s'", GetLocation().c_str(), i,
-                                dex_file_location.c_str(), dex_file_pointer);
+      *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with invalid "
+                                    "dex file magic '%s'",
+                                GetLocation().c_str(),
+                                i,
+                                dex_file_location.c_str(),
+                                dex_file_pointer);
       return false;
     }
     if (UNLIKELY(!DexFile::IsVersionValid(dex_file_pointer))) {
-      *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd for '%s' with invalid "
-                                "dex file version '%s'", GetLocation().c_str(), i,
-                                dex_file_location.c_str(), dex_file_pointer);
+      *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with invalid "
+                                    "dex file version '%s'",
+                                GetLocation().c_str(),
+                                i,
+                                dex_file_location.c_str(),
+                                dex_file_pointer);
       return false;
     }
     const DexFile::Header* header = reinterpret_cast<const DexFile::Header*>(dex_file_pointer);
@@ -507,21 +551,26 @@
 
     oat += (sizeof(*methods_offsets_pointer) * header->class_defs_size_);
     if (UNLIKELY(oat > End())) {
-      *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd for '%s' with truncated "
-                                "method offsets", GetLocation().c_str(), i,
+      *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with truncated "
+                                    "method offsets",
+                                GetLocation().c_str(),
+                                i,
                                 dex_file_location.c_str());
       return false;
     }
 
-    const uint8_t* current_dex_cache_arrays = nullptr;
+    uint8_t* current_dex_cache_arrays = nullptr;
     if (dex_cache_arrays != nullptr) {
       DexCacheArraysLayout layout(pointer_size, *header);
       if (layout.Size() != 0u) {
         if (static_cast<size_t>(bss_end_ - dex_cache_arrays) < layout.Size()) {
-          *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd for '%s' with "
-                                    "truncated dex cache arrays, %zd < %zd.",
-                                    GetLocation().c_str(), i, dex_file_location.c_str(),
-                                    static_cast<size_t>(bss_end_ - dex_cache_arrays), layout.Size());
+          *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with "
+                                        "truncated dex cache arrays, %zu < %zu.",
+                                    GetLocation().c_str(),
+                                    i,
+                                    dex_file_location.c_str(),
+                                    static_cast<size_t>(bss_end_ - dex_cache_arrays),
+                                    layout.Size());
           return false;
         }
         current_dex_cache_arrays = dex_cache_arrays;
@@ -553,7 +602,7 @@
   if (dex_cache_arrays != bss_end_) {
     // We expect the bss section to be either empty (dex_cache_arrays and bss_end_
     // both null) or contain just the dex cache arrays and nothing else.
-    *error_msg = StringPrintf("In oat file '%s' found unexpected bss size bigger by %zd bytes.",
+    *error_msg = StringPrintf("In oat file '%s' found unexpected bss size bigger by %zu bytes.",
                               GetLocation().c_str(),
                               static_cast<size_t>(bss_end_ - dex_cache_arrays));
     return false;
@@ -661,7 +710,7 @@
                                 uint32_t dex_file_location_checksum,
                                 const uint8_t* dex_file_pointer,
                                 const uint32_t* oat_class_offsets_pointer,
-                                const uint8_t* dex_cache_arrays)
+                                uint8_t* dex_cache_arrays)
     : oat_file_(oat_file),
       dex_file_location_(dex_file_location),
       canonical_dex_file_location_(canonical_dex_file_location),
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 34f0141..6acdf86 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -301,10 +301,10 @@
   const uint8_t* end_;
 
   // Pointer to the .bss section, if present, otherwise null.
-  const uint8_t* bss_begin_;
+  uint8_t* bss_begin_;
 
   // Pointer to the end of the .bss section, if present, otherwise null.
-  const uint8_t* bss_end_;
+  uint8_t* bss_end_;
 
   // Was this oat_file loaded executable?
   const bool is_executable_;
@@ -396,7 +396,7 @@
   // Returns the offset to the OatClass information. Most callers should use GetOatClass.
   uint32_t GetOatClassOffset(uint16_t class_def_index) const;
 
-  const uint8_t* GetDexCacheArrays() const {
+  uint8_t* GetDexCacheArrays() const {
     return dex_cache_arrays_;
   }
 
@@ -409,7 +409,7 @@
              uint32_t dex_file_checksum,
              const uint8_t* dex_file_pointer,
              const uint32_t* oat_class_offsets_pointer,
-             const uint8_t* dex_cache_arrays);
+             uint8_t* dex_cache_arrays);
 
   const OatFile* const oat_file_;
   const std::string dex_file_location_;
@@ -417,7 +417,7 @@
   const uint32_t dex_file_location_checksum_;
   const uint8_t* const dex_file_pointer_;
   const uint32_t* const oat_class_offsets_pointer_;
-  const uint8_t* const dex_cache_arrays_;
+  uint8_t* const dex_cache_arrays_;
 
   friend class OatFile;
   DISALLOW_COPY_AND_ASSIGN(OatDexFile);
diff --git a/test/087-gc-after-link/src/Main.java b/test/087-gc-after-link/src/Main.java
index 2f6d496..7c47e99 100644
--- a/test/087-gc-after-link/src/Main.java
+++ b/test/087-gc-after-link/src/Main.java
@@ -91,6 +91,7 @@
                      * is an error we can't recover from.
                      */
                     meth.invoke(dexFile, name, this);
+                    System.out.println("Unreachable");
                 } finally {
                     if (dexFile != null) {
                         /* close the DexFile to make CloseGuard happy */