Merge "Optimizing: Tag basic block allocations with their source."
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index c88d677..326a92b 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -204,6 +204,8 @@
   runtime/interpreter/unstarted_runtime_test.cc \
   runtime/java_vm_ext_test.cc \
   runtime/jit/jit_code_cache_test.cc \
+  runtime/lambda/closure_test.cc \
+  runtime/lambda/shorty_field_type_test.cc \
   runtime/leb128_test.cc \
   runtime/mem_map_test.cc \
   runtime/memory_region_test.cc \
diff --git a/compiler/dex/quick/arm/call_arm.cc b/compiler/dex/quick/arm/call_arm.cc
index 981ab2c..868d9a4 100644
--- a/compiler/dex/quick/arm/call_arm.cc
+++ b/compiler/dex/quick/arm/call_arm.cc
@@ -547,27 +547,28 @@
     cfi_.RestoreMany(DwarfFpReg(0), fp_spill_mask_);
   }
   bool unspill_LR_to_PC = (core_spill_mask_ & (1 << rs_rARM_LR.GetRegNum())) != 0;
+  uint32_t core_unspill_mask = core_spill_mask_;
   if (unspill_LR_to_PC) {
-    core_spill_mask_ &= ~(1 << rs_rARM_LR.GetRegNum());
-    core_spill_mask_ |= (1 << rs_rARM_PC.GetRegNum());
+    core_unspill_mask &= ~(1 << rs_rARM_LR.GetRegNum());
+    core_unspill_mask |= (1 << rs_rARM_PC.GetRegNum());
   }
-  if (core_spill_mask_ != 0u) {
-    if ((core_spill_mask_ & ~(0xffu | (1u << rs_rARM_PC.GetRegNum()))) == 0u) {
+  if (core_unspill_mask != 0u) {
+    if ((core_unspill_mask & ~(0xffu | (1u << rs_rARM_PC.GetRegNum()))) == 0u) {
       // Unspilling only low regs and/or PC, use 16-bit POP.
       constexpr int pc_bit_shift = rs_rARM_PC.GetRegNum() - 8;
       NewLIR1(kThumbPop,
-              (core_spill_mask_ & ~(1u << rs_rARM_PC.GetRegNum())) |
-              ((core_spill_mask_ & (1u << rs_rARM_PC.GetRegNum())) >> pc_bit_shift));
-    } else if (IsPowerOfTwo(core_spill_mask_)) {
+              (core_unspill_mask & ~(1u << rs_rARM_PC.GetRegNum())) |
+              ((core_unspill_mask & (1u << rs_rARM_PC.GetRegNum())) >> pc_bit_shift));
+    } else if (IsPowerOfTwo(core_unspill_mask)) {
       // kThumb2Pop cannot be used to unspill a single register.
-      NewLIR1(kThumb2Pop1, CTZ(core_spill_mask_));
+      NewLIR1(kThumb2Pop1, CTZ(core_unspill_mask));
     } else {
-      NewLIR1(kThumb2Pop, core_spill_mask_);
+      NewLIR1(kThumb2Pop, core_unspill_mask);
     }
     // If we pop to PC, there is no further epilogue code.
     if (!unspill_LR_to_PC) {
       cfi_.AdjustCFAOffset(-num_core_spills_ * kArmPointerSize);
-      cfi_.RestoreMany(DwarfCoreReg(0), core_spill_mask_);
+      cfi_.RestoreMany(DwarfCoreReg(0), core_unspill_mask);
       DCHECK_EQ(cfi_.GetCurrentCFAOffset(), 0);  // empty stack.
     }
   }
@@ -677,10 +678,11 @@
       FALLTHROUGH_INTENDED;
     case 1:  // Get method->dex_cache_resolved_methods_
       if (!use_pc_rel) {
-        cg->LoadRefDisp(arg0_ref,
-                        ArtMethod::DexCacheResolvedMethodsOffset().Int32Value(),
-                        arg0_ref,
-                        kNotVolatile);
+        cg->LoadBaseDisp(arg0_ref,
+                         ArtMethod::DexCacheResolvedMethodsOffset(kArmPointerSize).Int32Value(),
+                         arg0_ref,
+                         k32,
+                         kNotVolatile);
       }
       // Set up direct code if known.
       if (direct_code != 0) {
@@ -702,8 +704,8 @@
       CHECK_EQ(cu->dex_file, target_method.dex_file);
       if (!use_pc_rel) {
         cg->LoadRefDisp(arg0_ref,
-                        mirror::ObjectArray<mirror::Object>::OffsetOfElement(
-                            target_method.dex_method_index).Int32Value(),
+                        cg->GetCachePointerOffset(target_method.dex_method_index,
+                                                  kArmPointerSize),
                         arg0_ref,
                         kNotVolatile);
       } else {
diff --git a/compiler/dex/quick/arm64/call_arm64.cc b/compiler/dex/quick/arm64/call_arm64.cc
index 83a6aff..036da2e 100644
--- a/compiler/dex/quick/arm64/call_arm64.cc
+++ b/compiler/dex/quick/arm64/call_arm64.cc
@@ -511,10 +511,11 @@
       FALLTHROUGH_INTENDED;
     case 1:  // Get method->dex_cache_resolved_methods_
       if (!use_pc_rel) {
-        cg->LoadRefDisp(arg0_ref,
-                        ArtMethod::DexCacheResolvedMethodsOffset().Int32Value(),
-                        arg0_ref,
-                        kNotVolatile);
+        cg->LoadBaseDisp(arg0_ref,
+                         ArtMethod::DexCacheResolvedMethodsOffset(kArm64PointerSize).Int32Value(),
+                         arg0_ref,
+                         k64,
+                         kNotVolatile);
       }
       // Set up direct code if known.
       if (direct_code != 0) {
@@ -536,8 +537,9 @@
       CHECK_EQ(cu->dex_file, target_method.dex_file);
       if (!use_pc_rel) {
         cg->LoadWordDisp(arg0_ref,
-                         mirror::Array::DataOffset(kArm64PointerSize).Uint32Value() +
-                         target_method.dex_method_index * kArm64PointerSize, arg0_ref);
+                         cg->GetCachePointerOffset(target_method.dex_method_index,
+                                                   kArm64PointerSize),
+                         arg0_ref);
       } else {
         size_t offset = cg->dex_cache_arrays_layout_.MethodOffset(target_method.dex_method_index);
         cg->OpPcRelDexCacheArrayLoad(cu->dex_file, offset, arg0_ref, true);
diff --git a/compiler/dex/quick/dex_file_method_inliner.cc b/compiler/dex/quick/dex_file_method_inliner.cc
index 42b792c..af93aab 100644
--- a/compiler/dex/quick/dex_file_method_inliner.cc
+++ b/compiler/dex/quick/dex_file_method_inliner.cc
@@ -39,6 +39,9 @@
     true,   // kIntrinsicReverseBits
     true,   // kIntrinsicReverseBytes
     true,   // kIntrinsicNumberOfLeadingZeros
+    true,   // kIntrinsicNumberOfTrailingZeros
+    true,   // kIntrinsicRotateRight
+    true,   // kIntrinsicRotateLeft
     true,   // kIntrinsicAbsInt
     true,   // kIntrinsicAbsLong
     true,   // kIntrinsicAbsFloat
@@ -79,6 +82,10 @@
 static_assert(kIntrinsicIsStatic[kIntrinsicReverseBytes], "ReverseBytes must be static");
 static_assert(kIntrinsicIsStatic[kIntrinsicNumberOfLeadingZeros],
               "NumberOfLeadingZeros must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicNumberOfTrailingZeros],
+              "NumberOfTrailingZeros must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicRotateRight], "RotateRight must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicRotateLeft], "RotateLeft must be static");
 static_assert(kIntrinsicIsStatic[kIntrinsicAbsInt], "AbsInt must be static");
 static_assert(kIntrinsicIsStatic[kIntrinsicAbsLong], "AbsLong must be static");
 static_assert(kIntrinsicIsStatic[kIntrinsicAbsFloat], "AbsFloat must be static");
@@ -232,6 +239,9 @@
     "putOrderedObject",      // kNameCachePutOrderedObject
     "arraycopy",             // kNameCacheArrayCopy
     "numberOfLeadingZeros",  // kNameCacheNumberOfLeadingZeros
+    "numberOfTrailingZeros",  // kNameCacheNumberOfTrailingZeros
+    "rotateRight",           // kNameCacheRotateRight
+    "rotateLeft",            // kNameCacheRotateLeft
 };
 
 const DexFileMethodInliner::ProtoDef DexFileMethodInliner::kProtoCacheDefs[] = {
@@ -289,6 +299,8 @@
     { kClassCacheVoid, 2, { kClassCacheLong, kClassCacheShort } },
     // kProtoCacheObject_Z
     { kClassCacheBoolean, 1, { kClassCacheJavaLangObject } },
+    // kProtoCacheJI_J
+    { kClassCacheLong, 2, { kClassCacheLong, kClassCacheInt } },
     // kProtoCacheObjectJII_Z
     { kClassCacheBoolean, 4, { kClassCacheJavaLangObject, kClassCacheLong,
         kClassCacheInt, kClassCacheInt } },
@@ -379,6 +391,8 @@
 
     INTRINSIC(JavaLangInteger, NumberOfLeadingZeros, I_I, kIntrinsicNumberOfLeadingZeros, k32),
     INTRINSIC(JavaLangLong, NumberOfLeadingZeros, J_I, kIntrinsicNumberOfLeadingZeros, k64),
+    INTRINSIC(JavaLangInteger, NumberOfTrailingZeros, I_I, kIntrinsicNumberOfTrailingZeros, k32),
+    INTRINSIC(JavaLangLong, NumberOfTrailingZeros, J_I, kIntrinsicNumberOfTrailingZeros, k64),
 
     INTRINSIC(JavaLangMath,       Abs, I_I, kIntrinsicAbsInt, 0),
     INTRINSIC(JavaLangStrictMath, Abs, I_I, kIntrinsicAbsInt, 0),
@@ -468,6 +482,11 @@
     INTRINSIC(JavaLangSystem, ArrayCopy, CharArrayICharArrayII_V , kIntrinsicSystemArrayCopyCharArray,
               0),
 
+    INTRINSIC(JavaLangInteger, RotateRight, II_I, kIntrinsicRotateRight, k32),
+    INTRINSIC(JavaLangLong, RotateRight, JI_J, kIntrinsicRotateRight, k64),
+    INTRINSIC(JavaLangInteger, RotateLeft, II_I, kIntrinsicRotateLeft, k32),
+    INTRINSIC(JavaLangLong, RotateLeft, JI_J, kIntrinsicRotateLeft, k64),
+
 #undef INTRINSIC
 
 #define SPECIAL(c, n, p, o, d) \
@@ -631,7 +650,10 @@
     case kIntrinsicSystemArrayCopyCharArray:
       return backend->GenInlinedArrayCopyCharArray(info);
     case kIntrinsicNumberOfLeadingZeros:
-      return false;  // not implemented in quick
+    case kIntrinsicNumberOfTrailingZeros:
+    case kIntrinsicRotateRight:
+    case kIntrinsicRotateLeft:
+      return false;   // not implemented in quick.
     default:
       LOG(FATAL) << "Unexpected intrinsic opcode: " << intrinsic.opcode;
       return false;  // avoid warning "control reaches end of non-void function"
diff --git a/compiler/dex/quick/dex_file_method_inliner.h b/compiler/dex/quick/dex_file_method_inliner.h
index d6c8bfb..8458806 100644
--- a/compiler/dex/quick/dex_file_method_inliner.h
+++ b/compiler/dex/quick/dex_file_method_inliner.h
@@ -208,6 +208,9 @@
       kNameCachePutOrderedObject,
       kNameCacheArrayCopy,
       kNameCacheNumberOfLeadingZeros,
+      kNameCacheNumberOfTrailingZeros,
+      kNameCacheRotateRight,
+      kNameCacheRotateLeft,
       kNameCacheLast
     };
 
@@ -245,6 +248,7 @@
       kProtoCacheJJ_V,
       kProtoCacheJS_V,
       kProtoCacheObject_Z,
+      kProtoCacheJI_J,
       kProtoCacheObjectJII_Z,
       kProtoCacheObjectJJJ_Z,
       kProtoCacheObjectJObjectObject_Z,
diff --git a/compiler/dex/quick/gen_common.cc b/compiler/dex/quick/gen_common.cc
index af10817..2a1d644 100644
--- a/compiler/dex/quick/gen_common.cc
+++ b/compiler/dex/quick/gen_common.cc
@@ -88,24 +88,30 @@
                                                        r_result));
 }
 
+void Mir2Lir::LoadTypeFromCache(uint32_t type_index, RegStorage class_reg) {
+  if (CanUseOpPcRelDexCacheArrayLoad()) {
+    uint32_t offset = dex_cache_arrays_layout_.TypeOffset(type_index);
+    OpPcRelDexCacheArrayLoad(cu_->dex_file, offset, class_reg, false);
+  } else {
+    RegStorage r_method = LoadCurrMethodWithHint(class_reg);
+    MemberOffset resolved_types_offset = ArtMethod::DexCacheResolvedTypesOffset(
+        GetInstructionSetPointerSize(cu_->instruction_set));
+    LoadBaseDisp(r_method, resolved_types_offset.Int32Value(), class_reg,
+                 cu_->target64 ? k64 : k32, kNotVolatile);
+    int32_t offset_of_type = GetCacheOffset(type_index);
+    LoadRefDisp(class_reg, offset_of_type, class_reg, kNotVolatile);
+  }
+}
+
 RegStorage Mir2Lir::GenGetOtherTypeForSgetSput(const MirSFieldLoweringInfo& field_info,
                                                int opt_flags) {
   DCHECK_NE(field_info.StorageIndex(), DexFile::kDexNoIndex);
   // May do runtime call so everything to home locations.
   FlushAllRegs();
+  // Using fixed register to sync with possible call to runtime support.
   RegStorage r_base = TargetReg(kArg0, kRef);
   LockTemp(r_base);
-  if (CanUseOpPcRelDexCacheArrayLoad()) {
-    uint32_t offset = dex_cache_arrays_layout_.TypeOffset(field_info.StorageIndex());
-    OpPcRelDexCacheArrayLoad(cu_->dex_file, offset, r_base, false);
-  } else {
-    // Using fixed register to sync with possible call to runtime support.
-    RegStorage r_method = LoadCurrMethodWithHint(r_base);
-    LoadRefDisp(r_method, ArtMethod::DexCacheResolvedTypesOffset().Int32Value(), r_base,
-                kNotVolatile);
-    int32_t offset_of_field = ObjArray::OffsetOfElement(field_info.StorageIndex()).Int32Value();
-    LoadRefDisp(r_base, offset_of_field, r_base, kNotVolatile);
-  }
+  LoadTypeFromCache(field_info.StorageIndex(), r_base);
   // r_base now points at static storage (Class*) or null if the type is not yet resolved.
   LIR* unresolved_branch = nullptr;
   if (!field_info.IsClassInDexCache() && (opt_flags & MIR_CLASS_IS_IN_DEX_CACHE) == 0) {
@@ -1029,19 +1035,7 @@
   } else {
     rl_result = EvalLoc(rl_dest, kRefReg, true);
     // We don't need access checks, load type from dex cache
-    if (CanUseOpPcRelDexCacheArrayLoad()) {
-      size_t offset = dex_cache_arrays_layout_.TypeOffset(type_idx);
-      OpPcRelDexCacheArrayLoad(cu_->dex_file, offset, rl_result.reg, false);
-    } else {
-      int32_t dex_cache_offset =
-          ArtMethod::DexCacheResolvedTypesOffset().Int32Value();
-      RegStorage res_reg = AllocTempRef();
-      RegStorage r_method = LoadCurrMethodWithHint(res_reg);
-      LoadRefDisp(r_method, dex_cache_offset, res_reg, kNotVolatile);
-      int32_t offset_of_type = ClassArray::OffsetOfElement(type_idx).Int32Value();
-      LoadRefDisp(res_reg, offset_of_type, rl_result.reg, kNotVolatile);
-      FreeTemp(res_reg);
-    }
+    LoadTypeFromCache(type_idx, rl_result.reg);
     if (!cu_->compiler_driver->CanAssumeTypeIsPresentInDexCache(*cu_->dex_file,
         type_idx) || ForceSlowTypePath(cu_)) {
       // Slow path, at runtime test if type is null and if so initialize
@@ -1054,8 +1048,7 @@
 
 void Mir2Lir::GenConstString(uint32_t string_idx, RegLocation rl_dest) {
   /* NOTE: Most strings should be available at compile time */
-  int32_t offset_of_string = mirror::ObjectArray<mirror::String>::OffsetOfElement(string_idx).
-                                                                                      Int32Value();
+  int32_t offset_of_string = GetCacheOffset(string_idx);
   if (!cu_->compiler_driver->CanAssumeStringIsPresentInDexCache(
       *cu_->dex_file, string_idx) || ForceSlowStringPath(cu_)) {
     // slow path, resolve string if not in dex cache
@@ -1073,7 +1066,8 @@
       RegStorage r_method = LoadCurrMethodWithHint(arg0);
       LoadRefDisp(r_method, ArtMethod::DeclaringClassOffset().Int32Value(), arg0, kNotVolatile);
       // Declaring class to dex cache strings.
-      LoadRefDisp(arg0, mirror::Class::DexCacheStringsOffset().Int32Value(), arg0, kNotVolatile);
+      LoadBaseDisp(arg0, mirror::Class::DexCacheStringsOffset().Int32Value(), arg0,
+                   cu_->target64 ? k64 : k32, kNotVolatile);
 
       LoadRefDisp(arg0, offset_of_string, ret0, kNotVolatile);
     }
@@ -1091,8 +1085,8 @@
       RegStorage res_reg = AllocTempRef();
       LoadRefDisp(rl_method.reg, ArtMethod::DeclaringClassOffset().Int32Value(), res_reg,
                   kNotVolatile);
-      LoadRefDisp(res_reg, mirror::Class::DexCacheStringsOffset().Int32Value(), res_reg,
-                  kNotVolatile);
+      LoadBaseDisp(res_reg, mirror::Class::DexCacheStringsOffset().Int32Value(), res_reg,
+                   cu_->target64 ? k64 : k32, kNotVolatile);
       LoadRefDisp(res_reg, offset_of_string, rl_result.reg, kNotVolatile);
       FreeTemp(res_reg);
     }
@@ -1176,19 +1170,10 @@
                 kNotVolatile);
     LoadRefDisp(object.reg,  mirror::Object::ClassOffset().Int32Value(), object_class,
                 kNotVolatile);
-  } else if (CanUseOpPcRelDexCacheArrayLoad()) {
-    size_t offset = dex_cache_arrays_layout_.TypeOffset(type_idx);
-    OpPcRelDexCacheArrayLoad(cu_->dex_file, offset, check_class, false);
-    LoadRefDisp(object.reg,  mirror::Object::ClassOffset().Int32Value(), object_class,
-                kNotVolatile);
   } else {
-    RegStorage r_method = LoadCurrMethodWithHint(check_class);
-    LoadRefDisp(r_method, ArtMethod::DexCacheResolvedTypesOffset().Int32Value(),
-                check_class, kNotVolatile);
+    LoadTypeFromCache(type_idx, check_class);
     LoadRefDisp(object.reg,  mirror::Object::ClassOffset().Int32Value(), object_class,
                 kNotVolatile);
-    int32_t offset_of_type = ClassArray::OffsetOfElement(type_idx).Int32Value();
-    LoadRefDisp(check_class, offset_of_type, check_class, kNotVolatile);
   }
 
   // FIXME: what should we be comparing here? compressed or decompressed references?
@@ -1239,17 +1224,8 @@
       LoadValueDirectFixed(rl_src, ref_reg);  // kArg0 <= ref
     }
 
-    if (CanUseOpPcRelDexCacheArrayLoad()) {
-      size_t offset = dex_cache_arrays_layout_.TypeOffset(type_idx);
-      OpPcRelDexCacheArrayLoad(cu_->dex_file, offset, class_reg, false);
-    } else {
-      RegStorage r_method = LoadCurrMethodWithHint(class_reg);
-      // Load dex cache entry into class_reg (kArg2)
-      LoadRefDisp(r_method, ArtMethod::DexCacheResolvedTypesOffset().Int32Value(),
-                  class_reg, kNotVolatile);
-      int32_t offset_of_type = ClassArray::OffsetOfElement(type_idx).Int32Value();
-      LoadRefDisp(class_reg, offset_of_type, class_reg, kNotVolatile);
-    }
+    // Load dex cache entry into class_reg (kArg2)
+    LoadTypeFromCache(type_idx, class_reg);
     if (!can_assume_type_is_in_dex_cache) {
       GenIfNullUseHelperImm(class_reg, kQuickInitializeType, type_idx);
 
@@ -1370,17 +1346,7 @@
                 class_reg, kNotVolatile);
   } else {
     // Load dex cache entry into class_reg (kArg2)
-    if (CanUseOpPcRelDexCacheArrayLoad()) {
-      size_t offset = dex_cache_arrays_layout_.TypeOffset(type_idx);
-      OpPcRelDexCacheArrayLoad(cu_->dex_file, offset, class_reg, false);
-    } else {
-      RegStorage r_method = LoadCurrMethodWithHint(class_reg);
-
-      LoadRefDisp(r_method, ArtMethod::DexCacheResolvedTypesOffset().Int32Value(),
-                  class_reg, kNotVolatile);
-      int32_t offset_of_type = ClassArray::OffsetOfElement(type_idx).Int32Value();
-      LoadRefDisp(class_reg, offset_of_type, class_reg, kNotVolatile);
-    }
+    LoadTypeFromCache(type_idx, class_reg);
     if (!cu_->compiler_driver->CanAssumeTypeIsPresentInDexCache(*cu_->dex_file, type_idx)) {
       // Need to test presence of type in dex cache at runtime
       GenIfNullUseHelperImm(class_reg, kQuickInitializeType, type_idx);
diff --git a/compiler/dex/quick/mips/call_mips.cc b/compiler/dex/quick/mips/call_mips.cc
index 853980d..8863c05 100644
--- a/compiler/dex/quick/mips/call_mips.cc
+++ b/compiler/dex/quick/mips/call_mips.cc
@@ -415,10 +415,11 @@
  * Bit of a hack here - in the absence of a real scheduling pass,
  * emit the next instruction in static & direct invoke sequences.
  */
-static int NextSDCallInsn(CompilationUnit* cu, CallInfo* info, int state,
-                          const MethodReference& target_method, uint32_t, uintptr_t direct_code,
-                          uintptr_t direct_method, InvokeType type) {
-  Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get());
+int MipsMir2Lir::MipsNextSDCallInsn(CompilationUnit* cu, CallInfo* info, int state,
+                                    const MethodReference& target_method, uint32_t,
+                                    uintptr_t direct_code, uintptr_t direct_method,
+                                    InvokeType type) {
+  MipsMir2Lir* cg = static_cast<MipsMir2Lir*>(cu->cg.get());
   if (info->string_init_offset != 0) {
     RegStorage arg0_ref = cg->TargetReg(kArg0, kRef);
     switch (state) {
@@ -469,10 +470,12 @@
         cg->LoadCurrMethodDirect(arg0_ref);
         break;
       case 1:  // Get method->dex_cache_resolved_methods_
-        cg->LoadRefDisp(arg0_ref,
-                        ArtMethod::DexCacheResolvedMethodsOffset().Int32Value(),
-                        arg0_ref,
-                        kNotVolatile);
+        cg->LoadBaseDisp(arg0_ref,
+                         ArtMethod::DexCacheResolvedMethodsOffset(
+                             cu->target64 ? kMips64PointerSize : kMipsPointerSize).Int32Value(),
+                         arg0_ref,
+                         cu->target64 ? k64 : k32,
+                         kNotVolatile);
         // Set up direct code if known.
         if (direct_code != 0) {
           if (direct_code != static_cast<uintptr_t>(-1)) {
@@ -492,8 +495,9 @@
         CHECK_EQ(cu->dex_file, target_method.dex_file);
         const size_t pointer_size = GetInstructionSetPointerSize(cu->instruction_set);
         cg->LoadWordDisp(arg0_ref,
-                         mirror::Array::DataOffset(pointer_size).Uint32Value() +
-                         target_method.dex_method_index * pointer_size, arg0_ref);
+                         cg->GetCachePointerOffset(target_method.dex_method_index,
+                                                   pointer_size),
+                         arg0_ref);
         break;
       }
       case 3:  // Grab the code from the method*
@@ -512,7 +516,7 @@
 }
 
 NextCallInsn MipsMir2Lir::GetNextSDCallInsn() {
-  return NextSDCallInsn;
+  return MipsNextSDCallInsn;
 }
 
 LIR* MipsMir2Lir::GenCallInsn(const MirMethodLoweringInfo& method_info ATTRIBUTE_UNUSED) {
diff --git a/compiler/dex/quick/mips/codegen_mips.h b/compiler/dex/quick/mips/codegen_mips.h
index 2173253..378b9a0 100644
--- a/compiler/dex/quick/mips/codegen_mips.h
+++ b/compiler/dex/quick/mips/codegen_mips.h
@@ -269,6 +269,11 @@
   const bool fpuIs32Bit_;
 
  private:
+  static int MipsNextSDCallInsn(CompilationUnit* cu, CallInfo* info, int state,
+                                const MethodReference& target_method, uint32_t,
+                                uintptr_t direct_code, uintptr_t direct_method,
+                                InvokeType type);
+
   void GenNegLong(RegLocation rl_dest, RegLocation rl_src);
   void GenAddLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
   void GenSubLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
diff --git a/compiler/dex/quick/mir_to_lir-inl.h b/compiler/dex/quick/mir_to_lir-inl.h
index 767fe25..f96816c 100644
--- a/compiler/dex/quick/mir_to_lir-inl.h
+++ b/compiler/dex/quick/mir_to_lir-inl.h
@@ -21,6 +21,7 @@
 
 #include "base/logging.h"
 #include "dex/compiler_ir.h"
+#include "gc_root.h"
 #include "utils.h"
 
 namespace art {
@@ -278,6 +279,14 @@
   }
 }
 
+inline size_t Mir2Lir::GetCacheOffset(uint32_t index) {
+  return sizeof(GcRoot<mirror::Object>) * index;
+}
+
+inline size_t Mir2Lir::GetCachePointerOffset(uint32_t index, size_t pointer_size) {
+  return pointer_size * index;
+}
+
 inline Mir2Lir::ShortyIterator::ShortyIterator(const char* shorty, bool is_static)
     : cur_(shorty + 1), pending_this_(!is_static), initialized_(false) {
   DCHECK(shorty != nullptr);
diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h
index 73787e9..4e3aab2 100644
--- a/compiler/dex/quick/mir_to_lir.h
+++ b/compiler/dex/quick/mir_to_lir.h
@@ -1771,6 +1771,11 @@
       return (core_spill_mask_ & (1u << reg)) != 0;
     }
 
+    size_t GetCacheOffset(uint32_t index);
+    size_t GetCachePointerOffset(uint32_t index, size_t pointer_size);
+
+    void LoadTypeFromCache(uint32_t type_index, RegStorage class_reg);
+
   public:
     // TODO: add accessors for these.
     LIR* literal_list_;                        // Constants.
diff --git a/compiler/dex/quick/x86/call_x86.cc b/compiler/dex/quick/x86/call_x86.cc
index 43167a1..9cb45a4 100644
--- a/compiler/dex/quick/x86/call_x86.cc
+++ b/compiler/dex/quick/x86/call_x86.cc
@@ -394,18 +394,19 @@
       cg->LoadCurrMethodDirect(arg0_ref);
       break;
     case 1:  // Get method->dex_cache_resolved_methods_
-      cg->LoadRefDisp(arg0_ref,
-                      ArtMethod::DexCacheResolvedMethodsOffset().Int32Value(),
-                      arg0_ref,
-                      kNotVolatile);
+      cg->LoadBaseDisp(arg0_ref,
+                       ArtMethod::DexCacheResolvedMethodsOffset(
+                           cu->target64 ? kX86_64PointerSize : kX86PointerSize).Int32Value(),
+                       arg0_ref,
+                       cu->target64 ? k64 : k32,
+                       kNotVolatile);
       break;
     case 2: {
       // Grab target method*
       CHECK_EQ(cu->dex_file, target_method.dex_file);
       const size_t pointer_size = GetInstructionSetPointerSize(cu->instruction_set);
       cg->LoadWordDisp(arg0_ref,
-                       mirror::Array::DataOffset(pointer_size).Uint32Value() +
-                       target_method.dex_method_index * pointer_size,
+                       cg->GetCachePointerOffset(target_method.dex_method_index, pointer_size),
                        arg0_ref);
       break;
     }
diff --git a/compiler/dex/quick/x86/int_x86.cc b/compiler/dex/quick/x86/int_x86.cc
index d1fe167..ecd23e9 100755
--- a/compiler/dex/quick/x86/int_x86.cc
+++ b/compiler/dex/quick/x86/int_x86.cc
@@ -3031,31 +3031,12 @@
   // The LoadRefDisp(s) below will work normally, even in 64 bit mode.
   RegStorage check_class = AllocTemp();
 
-  // If Method* is already in a register, we can save a copy.
-  RegLocation rl_method = mir_graph_->GetMethodLoc();
-  int32_t offset_of_type = mirror::Array::DataOffset(
-      sizeof(mirror::HeapReference<mirror::Class*>)).Int32Value() +
-      (sizeof(mirror::HeapReference<mirror::Class*>) * type_idx);
-
-  if (rl_method.location == kLocPhysReg) {
-    if (use_declaring_class) {
-      LoadRefDisp(rl_method.reg, ArtMethod::DeclaringClassOffset().Int32Value(),
-                  check_class, kNotVolatile);
-    } else {
-      LoadRefDisp(rl_method.reg, ArtMethod::DexCacheResolvedTypesOffset().Int32Value(),
-                  check_class, kNotVolatile);
-      LoadRefDisp(check_class, offset_of_type, check_class, kNotVolatile);
-    }
+  if (use_declaring_class) {
+    RegStorage r_method = LoadCurrMethodWithHint(check_class);
+    LoadRefDisp(r_method, ArtMethod::DeclaringClassOffset().Int32Value(),
+                check_class, kNotVolatile);
   } else {
-    LoadCurrMethodDirect(check_class);
-    if (use_declaring_class) {
-      LoadRefDisp(check_class, ArtMethod::DeclaringClassOffset().Int32Value(),
-                  check_class, kNotVolatile);
-    } else {
-      LoadRefDisp(check_class, ArtMethod::DexCacheResolvedTypesOffset().Int32Value(),
-                  check_class, kNotVolatile);
-      LoadRefDisp(check_class, offset_of_type, check_class, kNotVolatile);
-    }
+    LoadTypeFromCache(type_idx, check_class);
   }
 
   // Compare the computed class to the class in the object.
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 9ee0a36..4c1408a 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -843,16 +843,17 @@
   virtual bool Visit(mirror::Class* c) OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
     const auto pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
     for (auto& m : c->GetVirtualMethods(pointer_size)) {
-      ResolveExceptionsForMethod(&m);
+      ResolveExceptionsForMethod(&m, pointer_size);
     }
     for (auto& m : c->GetDirectMethods(pointer_size)) {
-      ResolveExceptionsForMethod(&m);
+      ResolveExceptionsForMethod(&m, pointer_size);
     }
     return true;
   }
 
  private:
-  void ResolveExceptionsForMethod(ArtMethod* method_handle) SHARED_REQUIRES(Locks::mutator_lock_) {
+  void ResolveExceptionsForMethod(ArtMethod* method_handle, size_t pointer_size)
+      SHARED_REQUIRES(Locks::mutator_lock_) {
     const DexFile::CodeItem* code_item = method_handle->GetCodeItem();
     if (code_item == nullptr) {
       return;  // native or abstract method
@@ -873,7 +874,8 @@
         uint16_t encoded_catch_handler_handlers_type_idx =
             DecodeUnsignedLeb128(&encoded_catch_handler_list);
         // Add to set of types to resolve if not already in the dex cache resolved types
-        if (!method_handle->IsResolvedTypeIdx(encoded_catch_handler_handlers_type_idx)) {
+        if (!method_handle->IsResolvedTypeIdx(encoded_catch_handler_handlers_type_idx,
+                                              pointer_size)) {
           exceptions_to_resolve_.emplace(encoded_catch_handler_handlers_type_idx,
                                          method_handle->GetDexFile());
         }
@@ -1138,23 +1140,20 @@
   if (IsImage()) {
     TimingLogger::ScopedTiming t("UpdateImageClasses", timings);
 
-    Runtime* current = Runtime::Current();
+    Runtime* runtime = Runtime::Current();
 
     // Suspend all threads.
-    current->GetThreadList()->SuspendAll(__FUNCTION__);
+    ScopedSuspendAll ssa(__FUNCTION__);
 
     std::string error_msg;
     std::unique_ptr<ClinitImageUpdate> update(ClinitImageUpdate::Create(image_classes_.get(),
                                                                         Thread::Current(),
-                                                                        current->GetClassLinker(),
+                                                                        runtime->GetClassLinker(),
                                                                         &error_msg));
     CHECK(update.get() != nullptr) << error_msg;  // TODO: Soft failure?
 
     // Do the marking.
     update->Walk();
-
-    // Resume threads.
-    current->GetThreadList()->ResumeAll();
   }
 }
 
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index f318921..955c575 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -308,32 +308,29 @@
     dex_cache_array_starts_.Put(dex_file, size);
     DexCacheArraysLayout layout(target_ptr_size_, dex_file);
     DCHECK(layout.Valid());
-    auto types_size = layout.TypesSize(dex_file->NumTypeIds());
-    auto methods_size = layout.MethodsSize(dex_file->NumMethodIds());
-    auto fields_size = layout.FieldsSize(dex_file->NumFieldIds());
-    auto strings_size = layout.StringsSize(dex_file->NumStringIds());
-    dex_cache_array_indexes_.Put(
-        dex_cache->GetResolvedTypes(),
-        DexCacheArrayLocation {size + layout.TypesOffset(), types_size, kBinRegular});
-    dex_cache_array_indexes_.Put(
-        dex_cache->GetResolvedMethods(),
-        DexCacheArrayLocation {size + layout.MethodsOffset(), methods_size, kBinArtMethodClean});
-    AddMethodPointerArray(dex_cache->GetResolvedMethods());
-    dex_cache_array_indexes_.Put(
-        dex_cache->GetResolvedFields(),
-        DexCacheArrayLocation {size + layout.FieldsOffset(), fields_size, kBinArtField});
-    pointer_arrays_.emplace(dex_cache->GetResolvedFields(), kBinArtField);
-    dex_cache_array_indexes_.Put(
-        dex_cache->GetStrings(),
-        DexCacheArrayLocation {size + layout.StringsOffset(), strings_size, kBinRegular});
+    DCHECK_EQ(dex_file->NumTypeIds() != 0u, dex_cache->GetResolvedTypes() != nullptr);
+    AddDexCacheArrayRelocation(dex_cache->GetResolvedTypes(), size + layout.TypesOffset());
+    DCHECK_EQ(dex_file->NumMethodIds() != 0u, dex_cache->GetResolvedMethods() != nullptr);
+    AddDexCacheArrayRelocation(dex_cache->GetResolvedMethods(), size + layout.MethodsOffset());
+    DCHECK_EQ(dex_file->NumFieldIds() != 0u, dex_cache->GetResolvedFields() != nullptr);
+    AddDexCacheArrayRelocation(dex_cache->GetResolvedFields(), size + layout.FieldsOffset());
+    DCHECK_EQ(dex_file->NumStringIds() != 0u, dex_cache->GetStrings() != nullptr);
+    AddDexCacheArrayRelocation(dex_cache->GetStrings(), size + layout.StringsOffset());
     size += layout.Size();
-    CHECK_EQ(layout.Size(), types_size + methods_size + fields_size + strings_size);
   }
   // Set the slot size early to avoid DCHECK() failures in IsImageBinSlotAssigned()
   // when AssignImageBinSlot() assigns their indexes out or order.
   bin_slot_sizes_[kBinDexCacheArray] = size;
 }
 
+void ImageWriter::AddDexCacheArrayRelocation(void* array, size_t offset) {
+  if (array != nullptr) {
+    native_object_relocations_.emplace(
+        array,
+        NativeObjectRelocation { offset, kNativeObjectRelocationTypeDexCacheArray });
+  }
+}
+
 void ImageWriter::AddMethodPointerArray(mirror::PointerArray* arr) {
   DCHECK(arr != nullptr);
   if (kIsDebugBuild) {
@@ -380,7 +377,7 @@
     //   so we pre-calculate their offsets separately in PrepareDexCacheArraySlots().
     //   Since these arrays are huge, most pages do not overlap other objects and it's not
     //   really important where they are for the clean/dirty separation. Due to their
-    //   special PC-relative addressing, we arbitrarily keep them at the beginning.
+    //   special PC-relative addressing, we arbitrarily keep them at the end.
     // * Class'es which are verified [their clinit runs only at runtime]
     //   - classes in general [because their static fields get overwritten]
     //   - initialized classes with all-final statics are unlikely to be ever dirty,
@@ -442,28 +439,13 @@
       }
     } else if (object->GetClass<kVerifyNone>()->IsStringClass()) {
       bin = kBinString;  // Strings are almost always immutable (except for object header).
-    } else if (object->IsArrayInstance()) {
-      mirror::Class* klass = object->GetClass<kVerifyNone>();
-      if (klass->IsObjectArrayClass() || klass->IsIntArrayClass() || klass->IsLongArrayClass()) {
-        auto it = dex_cache_array_indexes_.find(object);
-        if (it != dex_cache_array_indexes_.end()) {
-          bin = kBinDexCacheArray;
-          // Use prepared offset defined by the DexCacheLayout.
-          current_offset = it->second.offset_;
-          // Override incase of cross compilation.
-          object_size = it->second.length_;
-        }  // else bin = kBinRegular
-      }
     }  // else bin = kBinRegular
   }
 
   size_t offset_delta = RoundUp(object_size, kObjectAlignment);  // 64-bit alignment
-  if (bin != kBinDexCacheArray) {
-    DCHECK(dex_cache_array_indexes_.find(object) == dex_cache_array_indexes_.end()) << object;
-    current_offset = bin_slot_sizes_[bin];  // How many bytes the current bin is at (aligned).
-    // Move the current bin size up to accomodate the object we just assigned a bin slot.
-    bin_slot_sizes_[bin] += offset_delta;
-  }
+  current_offset = bin_slot_sizes_[bin];  // How many bytes the current bin is at (aligned).
+  // Move the current bin size up to accomodate the object we just assigned a bin slot.
+  bin_slot_sizes_[bin] += offset_delta;
 
   BinSlot new_bin_slot(bin, current_offset);
   SetImageBinSlot(object, new_bin_slot);
@@ -594,7 +576,7 @@
   }
 
   // Clear references to removed classes from the DexCaches.
-  const ArtMethod* resolution_method = runtime->GetResolutionMethod();
+  ArtMethod* resolution_method = runtime->GetResolutionMethod();
 
   ScopedAssertNoThreadSuspension sa(self, __FUNCTION__);
   ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_);  // For ClassInClassTable
@@ -610,16 +592,20 @@
         dex_cache->SetResolvedType(i, nullptr);
       }
     }
-    auto* resolved_methods = down_cast<mirror::PointerArray*>(dex_cache->GetResolvedMethods());
-    for (size_t i = 0, len = resolved_methods->GetLength(); i < len; i++) {
-      auto* method = resolved_methods->GetElementPtrSize<ArtMethod*>(i, target_ptr_size_);
+    ArtMethod** resolved_methods = dex_cache->GetResolvedMethods();
+    for (size_t i = 0, num = dex_cache->NumResolvedMethods(); i != num; ++i) {
+      ArtMethod* method =
+          mirror::DexCache::GetElementPtrSize(resolved_methods, i, target_ptr_size_);
       if (method != nullptr) {
         auto* declaring_class = method->GetDeclaringClass();
         // Miranda methods may be held live by a class which was not an image class but have a
         // declaring class which is an image class. Set it to the resolution method to be safe and
         // prevent dangling pointers.
         if (method->IsMiranda() || !IsImageClass(declaring_class)) {
-          resolved_methods->SetElementPtrSize(i, resolution_method, target_ptr_size_);
+          mirror::DexCache::SetElementPtrSize(resolved_methods,
+                                              i,
+                                              resolution_method,
+                                              target_ptr_size_);
         } else {
           // Check that the class is still in the classes table.
           DCHECK(class_linker->ClassInClassTable(declaring_class)) << "Class "
@@ -921,8 +907,6 @@
   image_end_ += RoundUp(sizeof(ImageHeader), kObjectAlignment);  // 64-bit-alignment
 
   image_objects_offset_begin_ = image_end_;
-  // Prepare bin slots for dex cache arrays.
-  PrepareDexCacheArraySlots();
   // Clear any pre-existing monitors which may have been in the monitor words, assign bin slots.
   heap->VisitObjects(WalkFieldsCallback, this);
   // Write the image runtime methods.
@@ -952,6 +936,8 @@
     CHECK(m->IsRuntimeMethod());
     AssignMethodOffset(m, kNativeObjectRelocationTypeArtMethodClean);
   }
+  // Calculate size of the dex cache arrays slot and prepare offsets.
+  PrepareDexCacheArraySlots();
 
   // Calculate bin slot offsets.
   size_t bin_offset = image_objects_offset_begin_;
@@ -1018,6 +1004,11 @@
                                   bin_slot_sizes_[kBinArtMethodDirty]);
   CHECK_EQ(bin_slot_offsets_[kBinArtMethodClean], methods_section->Offset());
   cur_pos = methods_section->End();
+  // Add dex cache arrays section.
+  auto* dex_cache_arrays_section = &sections[ImageHeader::kSectionDexCacheArrays];
+  *dex_cache_arrays_section = ImageSection(cur_pos, bin_slot_sizes_[kBinDexCacheArray]);
+  CHECK_EQ(bin_slot_offsets_[kBinDexCacheArray], dex_cache_arrays_section->Offset());
+  cur_pos = dex_cache_arrays_section->End();
   // Round up to the alignment the string table expects. See HashSet::WriteToMemory.
   cur_pos = RoundUp(cur_pos, sizeof(uint64_t));
   // Calculate the size of the interned strings.
@@ -1119,6 +1110,9 @@
             ArtMethod::Size(target_ptr_size_),
             ArtMethod::Alignment(target_ptr_size_)));
         break;
+      case kNativeObjectRelocationTypeDexCacheArray:
+        // Nothing to copy here, everything is done in FixupDexCache().
+        break;
       }
     }
   }
@@ -1186,7 +1180,7 @@
     auto* elem = arr->GetElementPtrSize<void*>(i, target_ptr_size_);
     if (elem != nullptr) {
       auto it = native_object_relocations_.find(elem);
-      if (it == native_object_relocations_.end()) {
+      if (UNLIKELY(it == native_object_relocations_.end())) {
         if (it->second.IsArtMethodRelocation()) {
           auto* method = reinterpret_cast<ArtMethod*>(elem);
           LOG(FATAL) << "No relocation entry for ArtMethod " << PrettyMethod(method) << " @ "
@@ -1199,6 +1193,7 @@
               << field << " idx=" << i << "/" << num_elements << " with declaring class "
               << PrettyClass(field->GetDeclaringClass());
         }
+        UNREACHABLE();
       } else {
         elem = image_begin_ + it->second.offset;
       }
@@ -1279,27 +1274,31 @@
   }
 };
 
-void* ImageWriter::NativeLocationInImage(void* obj) {
-  if (obj == nullptr) {
-    return nullptr;
-  }
+uintptr_t ImageWriter::NativeOffsetInImage(void* obj) {
+  DCHECK(obj != nullptr);
   auto it = native_object_relocations_.find(obj);
   CHECK(it != native_object_relocations_.end()) << obj;
   const NativeObjectRelocation& relocation = it->second;
-  return reinterpret_cast<void*>(image_begin_ + relocation.offset);
+  return relocation.offset;
+}
+
+template <typename T>
+T* ImageWriter::NativeLocationInImage(T* obj) {
+  if (obj == nullptr) {
+    return nullptr;
+  }
+  return reinterpret_cast<T*>(image_begin_ + NativeOffsetInImage(obj));
 }
 
 void ImageWriter::FixupClass(mirror::Class* orig, mirror::Class* copy) {
   // Update the field arrays.
-  copy->SetSFieldsPtrUnchecked(reinterpret_cast<LengthPrefixedArray<ArtField>*>(
-      NativeLocationInImage(orig->GetSFieldsPtr())));
-  copy->SetIFieldsPtrUnchecked(reinterpret_cast<LengthPrefixedArray<ArtField>*>(
-      NativeLocationInImage(orig->GetIFieldsPtr())));
+  copy->SetSFieldsPtrUnchecked(NativeLocationInImage(orig->GetSFieldsPtr()));
+  copy->SetIFieldsPtrUnchecked(NativeLocationInImage(orig->GetIFieldsPtr()));
   // Update direct and virtual method arrays.
-  copy->SetDirectMethodsPtrUnchecked(reinterpret_cast<LengthPrefixedArray<ArtMethod>*>(
-      NativeLocationInImage(orig->GetDirectMethodsPtr())));
-  copy->SetVirtualMethodsPtr(reinterpret_cast<LengthPrefixedArray<ArtMethod>*>(
-      NativeLocationInImage(orig->GetVirtualMethodsPtr())));
+  copy->SetDirectMethodsPtrUnchecked(NativeLocationInImage(orig->GetDirectMethodsPtr()));
+  copy->SetVirtualMethodsPtr(NativeLocationInImage(orig->GetVirtualMethodsPtr()));
+  // Update dex cache strings.
+  copy->SetDexCacheStrings(NativeLocationInImage(orig->GetDexCacheStrings()));
   // Fix up embedded tables.
   if (orig->ShouldHaveEmbeddedImtAndVTable()) {
     for (int32_t i = 0; i < orig->GetEmbeddedVTableLength(); ++i) {
@@ -1332,7 +1331,7 @@
   }
   auto* klass = orig->GetClass();
   if (klass->IsIntArrayClass() || klass->IsLongArrayClass()) {
-    // Is this a native dex cache array?
+    // Is this a native pointer array?
     auto it = pointer_arrays_.find(down_cast<mirror::PointerArray*>(orig));
     if (it != pointer_arrays_.end()) {
       // Should only need to fixup every pointer array exactly once.
@@ -1340,8 +1339,6 @@
       pointer_arrays_.erase(it);
       return;
     }
-    CHECK(dex_cache_array_indexes_.find(orig) == dex_cache_array_indexes_.end())
-        << "Should have been pointer array.";
   }
   if (orig->IsClass()) {
     FixupClass(orig->AsClass<kVerifyNone>(), down_cast<mirror::Class*>(copy));
@@ -1356,17 +1353,81 @@
           << "Missing relocation for AbstractMethod.artMethod " << PrettyMethod(src_method);
       dest->SetArtMethod(
           reinterpret_cast<ArtMethod*>(image_begin_ + it->second.offset));
-    } else if (!klass->IsArrayClass() && klass->IsSubClass(down_cast<mirror::Class*>(
-        Thread::Current()->DecodeJObject(WellKnownClasses::java_lang_ClassLoader)))) {
-      // If src is a ClassLoader, set the class table to null so that it gets recreated by the
-      // ClassLoader.
-      down_cast<mirror::ClassLoader*>(copy)->SetClassTable(nullptr);
+    } else if (!klass->IsArrayClass()) {
+      ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+      if (klass == class_linker->GetClassRoot(ClassLinker::kJavaLangDexCache)) {
+        FixupDexCache(down_cast<mirror::DexCache*>(orig), down_cast<mirror::DexCache*>(copy));
+      } else if (klass->IsSubClass(down_cast<mirror::Class*>(
+          class_linker->GetClassRoot(ClassLinker::kJavaLangClassLoader)))) {
+        // If src is a ClassLoader, set the class table to null so that it gets recreated by the
+        // ClassLoader.
+        down_cast<mirror::ClassLoader*>(copy)->SetClassTable(nullptr);
+      }
     }
     FixupVisitor visitor(this, copy);
     orig->VisitReferences(visitor, visitor);
   }
 }
 
+void ImageWriter::FixupDexCache(mirror::DexCache* orig_dex_cache,
+                                mirror::DexCache* copy_dex_cache) {
+  // Though the DexCache array fields are usually treated as native pointers, we set the full
+  // 64-bit values here, clearing the top 32 bits for 32-bit targets. The zero-extension is
+  // done by casting to the unsigned type uintptr_t before casting to int64_t, i.e.
+  //     static_cast<int64_t>(reinterpret_cast<uintptr_t>(image_begin_ + offset))).
+  GcRoot<mirror::String>* orig_strings = orig_dex_cache->GetStrings();
+  if (orig_strings != nullptr) {
+    uintptr_t copy_strings_offset = NativeOffsetInImage(orig_strings);
+    copy_dex_cache->SetField64<false>(
+        mirror::DexCache::StringsOffset(),
+        static_cast<int64_t>(reinterpret_cast<uintptr_t>(image_begin_ + copy_strings_offset)));
+    GcRoot<mirror::String>* copy_strings =
+        reinterpret_cast<GcRoot<mirror::String>*>(image_->Begin() + copy_strings_offset);
+    for (size_t i = 0, num = orig_dex_cache->NumStrings(); i != num; ++i) {
+      copy_strings[i] = GcRoot<mirror::String>(GetImageAddress(orig_strings[i].Read()));
+    }
+  }
+  GcRoot<mirror::Class>* orig_types = orig_dex_cache->GetResolvedTypes();
+  if (orig_types != nullptr) {
+    uintptr_t copy_types_offset = NativeOffsetInImage(orig_types);
+    copy_dex_cache->SetField64<false>(
+        mirror::DexCache::ResolvedTypesOffset(),
+        static_cast<int64_t>(reinterpret_cast<uintptr_t>(image_begin_ + copy_types_offset)));
+    GcRoot<mirror::Class>* copy_types =
+        reinterpret_cast<GcRoot<mirror::Class>*>(image_->Begin() + copy_types_offset);
+    for (size_t i = 0, num = orig_dex_cache->NumResolvedTypes(); i != num; ++i) {
+      copy_types[i] = GcRoot<mirror::Class>(GetImageAddress(orig_types[i].Read()));
+    }
+  }
+  ArtMethod** orig_methods = orig_dex_cache->GetResolvedMethods();
+  if (orig_methods != nullptr) {
+    uintptr_t copy_methods_offset = NativeOffsetInImage(orig_methods);
+    copy_dex_cache->SetField64<false>(
+        mirror::DexCache::ResolvedMethodsOffset(),
+        static_cast<int64_t>(reinterpret_cast<uintptr_t>(image_begin_ + copy_methods_offset)));
+    ArtMethod** copy_methods =
+        reinterpret_cast<ArtMethod**>(image_->Begin() + copy_methods_offset);
+    for (size_t i = 0, num = orig_dex_cache->NumResolvedMethods(); i != num; ++i) {
+      ArtMethod* orig = mirror::DexCache::GetElementPtrSize(orig_methods, i, target_ptr_size_);
+      ArtMethod* copy = NativeLocationInImage(orig);
+      mirror::DexCache::SetElementPtrSize(copy_methods, i, copy, target_ptr_size_);
+    }
+  }
+  ArtField** orig_fields = orig_dex_cache->GetResolvedFields();
+  if (orig_fields != nullptr) {
+    uintptr_t copy_fields_offset = NativeOffsetInImage(orig_fields);
+    copy_dex_cache->SetField64<false>(
+        mirror::DexCache::ResolvedFieldsOffset(),
+        static_cast<int64_t>(reinterpret_cast<uintptr_t>(image_begin_ + copy_fields_offset)));
+    ArtField** copy_fields = reinterpret_cast<ArtField**>(image_->Begin() + copy_fields_offset);
+    for (size_t i = 0, num = orig_dex_cache->NumResolvedFields(); i != num; ++i) {
+      ArtField* orig = mirror::DexCache::GetElementPtrSize(orig_fields, i, target_ptr_size_);
+      ArtField* copy = NativeLocationInImage(orig);
+      mirror::DexCache::SetElementPtrSize(copy_fields, i, copy, target_ptr_size_);
+    }
+  }
+}
+
 const uint8_t* ImageWriter::GetQuickCode(ArtMethod* method, bool* quick_is_interpreted) {
   DCHECK(!method->IsResolutionMethod() && !method->IsImtConflictMethod() &&
          !method->IsImtUnimplementedMethod() && !method->IsAbstract()) << PrettyMethod(method);
@@ -1429,8 +1490,11 @@
   memcpy(copy, orig, ArtMethod::Size(target_ptr_size_));
 
   copy->SetDeclaringClass(GetImageAddress(orig->GetDeclaringClassUnchecked()));
-  copy->SetDexCacheResolvedMethods(GetImageAddress(orig->GetDexCacheResolvedMethods()));
-  copy->SetDexCacheResolvedTypes(GetImageAddress(orig->GetDexCacheResolvedTypes()));
+
+  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_);
+  copy->SetDexCacheResolvedTypes(NativeLocationInImage(orig_resolved_types), target_ptr_size_);
 
   // OatWriter replaces the code_ with an offset value. Here we re-adjust to a pointer relative to
   // oat_begin_
@@ -1533,9 +1597,11 @@
 
 uint8_t* ImageWriter::GetOatFileBegin() const {
   DCHECK_GT(intern_table_bytes_, 0u);
-  return image_begin_ + RoundUp(
-      image_end_ + bin_slot_sizes_[kBinArtField] + bin_slot_sizes_[kBinArtMethodDirty] +
-      bin_slot_sizes_[kBinArtMethodClean] + intern_table_bytes_, kPageSize);
+  size_t native_sections_size =
+      bin_slot_sizes_[kBinArtField] + bin_slot_sizes_[kBinArtMethodDirty] +
+      bin_slot_sizes_[kBinArtMethodClean] + bin_slot_sizes_[kBinDexCacheArray] +
+      intern_table_bytes_;
+  return image_begin_ + RoundUp(image_end_ + native_sections_size, kPageSize);
 }
 
 ImageWriter::Bin ImageWriter::BinTypeForNativeRelocationType(NativeObjectRelocationType type) {
@@ -1549,6 +1615,8 @@
     case kNativeObjectRelocationTypeArtMethodDirty:
     case kNativeObjectRelocationTypeArtMethodArrayDirty:
       return kBinArtMethodDirty;
+    case kNativeObjectRelocationTypeDexCacheArray:
+      return kBinDexCacheArray;
   }
   UNREACHABLE();
 }
diff --git a/compiler/image_writer.h b/compiler/image_writer.h
index 778521c..e235bc4 100644
--- a/compiler/image_writer.h
+++ b/compiler/image_writer.h
@@ -78,12 +78,13 @@
 
   ArtMethod* GetImageMethodAddress(ArtMethod* method) SHARED_REQUIRES(Locks::mutator_lock_);
 
-  mirror::HeapReference<mirror::Object>* GetDexCacheArrayElementImageAddress(
-      const DexFile* dex_file, uint32_t offset) const SHARED_REQUIRES(Locks::mutator_lock_) {
+  template <typename PtrType>
+  PtrType GetDexCacheArrayElementImageAddress(const DexFile* dex_file, uint32_t offset)
+      const SHARED_REQUIRES(Locks::mutator_lock_) {
     auto it = dex_cache_array_starts_.find(dex_file);
     DCHECK(it != dex_cache_array_starts_.end());
-    return reinterpret_cast<mirror::HeapReference<mirror::Object>*>(
-        image_begin_ + RoundUp(sizeof(ImageHeader), kObjectAlignment) + it->second + offset);
+    return reinterpret_cast<PtrType>(
+        image_begin_ + bin_slot_offsets_[kBinDexCacheArray] + it->second + offset);
   }
 
   uint8_t* GetOatFileBegin() const;
@@ -104,13 +105,8 @@
 
   // Classify different kinds of bins that objects end up getting packed into during image writing.
   enum Bin {
-    // Dex cache arrays have a special slot for PC-relative addressing. Since they are
-    // huge, and as such their dirtiness is not important for the clean/dirty separation,
-    // we arbitrarily keep them at the beginning.
-    kBinDexCacheArray,            // Object arrays belonging to dex cache.
     // Likely-clean:
     kBinString,                        // [String] Almost always immutable (except for obj header).
-    kBinArtMethodsManagedInitialized,  // [ArtMethod] Not-native, and initialized. Unlikely to dirty
     // Unknown mix of clean/dirty:
     kBinRegular,
     // Likely-dirty:
@@ -127,6 +123,10 @@
     // ArtMethods may be dirty if the class has native methods or a declaring class that isn't
     // initialized.
     kBinArtMethodDirty,
+    // Dex cache arrays have a special slot for PC-relative addressing. Since they are
+    // huge, and as such their dirtiness is not important for the clean/dirty separation,
+    // we arbitrarily keep them at the end of the native data.
+    kBinDexCacheArray,            // Arrays belonging to dex cache.
     kBinSize,
     // Number of bins which are for mirror objects.
     kBinMirrorCount = kBinArtField,
@@ -140,6 +140,7 @@
     kNativeObjectRelocationTypeArtMethodArrayClean,
     kNativeObjectRelocationTypeArtMethodDirty,
     kNativeObjectRelocationTypeArtMethodArrayDirty,
+    kNativeObjectRelocationTypeDexCacheArray,
   };
   friend std::ostream& operator<<(std::ostream& stream, const NativeObjectRelocationType& type);
 
@@ -193,6 +194,7 @@
       SHARED_REQUIRES(Locks::mutator_lock_);
   BinSlot GetImageBinSlot(mirror::Object* object) const SHARED_REQUIRES(Locks::mutator_lock_);
 
+  void AddDexCacheArrayRelocation(void* array, size_t offset) SHARED_REQUIRES(Locks::mutator_lock_);
   void AddMethodPointerArray(mirror::PointerArray* arr) SHARED_REQUIRES(Locks::mutator_lock_);
 
   static void* GetImageAddressCallback(void* writer, mirror::Object* obj)
@@ -266,6 +268,8 @@
       SHARED_REQUIRES(Locks::mutator_lock_);
   void FixupObject(mirror::Object* orig, mirror::Object* copy)
       SHARED_REQUIRES(Locks::mutator_lock_);
+  void FixupDexCache(mirror::DexCache* orig_dex_cache, mirror::DexCache* copy_dex_cache)
+      SHARED_REQUIRES(Locks::mutator_lock_);
   void FixupPointerArray(mirror::Object* dst, mirror::PointerArray* arr, mirror::Class* klass,
                          Bin array_type) SHARED_REQUIRES(Locks::mutator_lock_);
 
@@ -291,7 +295,10 @@
 
   static Bin BinTypeForNativeRelocationType(NativeObjectRelocationType type);
 
-  void* NativeLocationInImage(void* obj);
+  uintptr_t NativeOffsetInImage(void* obj);
+
+  template <typename T>
+  T* NativeLocationInImage(T* obj);
 
   const CompilerDriver& compiler_driver_;
 
@@ -313,15 +320,6 @@
   // Memory mapped for generating the image.
   std::unique_ptr<MemMap> image_;
 
-  // Indexes, lengths for dex cache arrays (objects are inside of the image so that they don't
-  // move).
-  struct DexCacheArrayLocation {
-    size_t offset_;
-    size_t length_;
-    Bin bin_type_;
-  };
-  SafeMap<mirror::Object*, DexCacheArrayLocation> dex_cache_array_indexes_;
-
   // Pointer arrays that need to be updated. Since these are only some int and long arrays, we need
   // to keep track. These include vtable arrays, iftable arrays, and dex caches.
   std::unordered_map<mirror::PointerArray*, Bin> pointer_arrays_;
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index fdf904d..4ddd457 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -842,10 +842,10 @@
 
   uint32_t GetDexCacheOffset(const LinkerPatch& patch) SHARED_REQUIRES(Locks::mutator_lock_) {
     if (writer_->image_writer_ != nullptr) {
-      auto* element = writer_->image_writer_->GetDexCacheArrayElementImageAddress(
+      auto* element = writer_->image_writer_->GetDexCacheArrayElementImageAddress<const uint8_t*>(
               patch.TargetDexCacheDexFile(), patch.TargetDexCacheElementOffset());
       const uint8_t* oat_data = writer_->image_writer_->GetOatFileBegin() + file_offset_;
-      return reinterpret_cast<const uint8_t*>(element) - oat_data;
+      return element - oat_data;
     } else {
       LOG(FATAL) << "Unimplemented.";
       UNREACHABLE();
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index 7fa16bb..9895953 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -46,7 +46,7 @@
   explicit Temporaries(HGraph* graph) : graph_(graph), index_(0) {}
 
   void Add(HInstruction* instruction) {
-    HInstruction* temp = new (graph_->GetArena()) HTemporary(index_);
+    HInstruction* temp = new (graph_->GetArena()) HTemporary(index_, instruction->GetDexPc());
     instruction->GetBlock()->AddInstruction(temp);
 
     DCHECK(temp->GetPrevious() == instruction);
@@ -161,23 +161,25 @@
 
   if (!dex_compilation_unit_->IsStatic()) {
     // Add the implicit 'this' argument, not expressed in the signature.
-    HParameterValue* parameter =
-        new (arena_) HParameterValue(parameter_index++, Primitive::kPrimNot, true);
+    HParameterValue* parameter = new (arena_) HParameterValue(parameter_index++,
+                                                              Primitive::kPrimNot,
+                                                              true);
     entry_block_->AddInstruction(parameter);
     HLocal* local = GetLocalAt(locals_index++);
-    entry_block_->AddInstruction(new (arena_) HStoreLocal(local, parameter));
+    entry_block_->AddInstruction(new (arena_) HStoreLocal(local, parameter, local->GetDexPc()));
     number_of_parameters--;
   }
 
   uint32_t pos = 1;
   for (int i = 0; i < number_of_parameters; i++) {
-    HParameterValue* parameter =
-        new (arena_) HParameterValue(parameter_index++, Primitive::GetType(shorty[pos++]));
+    HParameterValue* parameter = new (arena_) HParameterValue(parameter_index++,
+                                                              Primitive::GetType(shorty[pos++]),
+                                                              false);
     entry_block_->AddInstruction(parameter);
     HLocal* local = GetLocalAt(locals_index++);
     // Store the parameter value in the local that the dex code will use
     // to reference that parameter.
-    entry_block_->AddInstruction(new (arena_) HStoreLocal(local, parameter));
+    entry_block_->AddInstruction(new (arena_) HStoreLocal(local, parameter, local->GetDexPc()));
     bool is_wide = (parameter->GetType() == Primitive::kPrimLong)
         || (parameter->GetType() == Primitive::kPrimDouble);
     if (is_wide) {
@@ -196,11 +198,11 @@
   DCHECK(branch_target != nullptr);
   DCHECK(fallthrough_target != nullptr);
   PotentiallyAddSuspendCheck(branch_target, dex_pc);
-  HInstruction* first = LoadLocal(instruction.VRegA(), Primitive::kPrimInt);
-  HInstruction* second = LoadLocal(instruction.VRegB(), Primitive::kPrimInt);
-  T* comparison = new (arena_) T(first, second);
+  HInstruction* first = LoadLocal(instruction.VRegA(), Primitive::kPrimInt, dex_pc);
+  HInstruction* second = LoadLocal(instruction.VRegB(), Primitive::kPrimInt, dex_pc);
+  T* comparison = new (arena_) T(first, second, dex_pc);
   current_block_->AddInstruction(comparison);
-  HInstruction* ifinst = new (arena_) HIf(comparison);
+  HInstruction* ifinst = new (arena_) HIf(comparison, dex_pc);
   current_block_->AddInstruction(ifinst);
   current_block_->AddSuccessor(branch_target);
   current_block_->AddSuccessor(fallthrough_target);
@@ -215,10 +217,10 @@
   DCHECK(branch_target != nullptr);
   DCHECK(fallthrough_target != nullptr);
   PotentiallyAddSuspendCheck(branch_target, dex_pc);
-  HInstruction* value = LoadLocal(instruction.VRegA(), Primitive::kPrimInt);
-  T* comparison = new (arena_) T(value, graph_->GetIntConstant(0));
+  HInstruction* value = LoadLocal(instruction.VRegA(), Primitive::kPrimInt, dex_pc);
+  T* comparison = new (arena_) T(value, graph_->GetIntConstant(0, dex_pc), dex_pc);
   current_block_->AddInstruction(comparison);
-  HInstruction* ifinst = new (arena_) HIf(comparison);
+  HInstruction* ifinst = new (arena_) HIf(comparison, dex_pc);
   current_block_->AddInstruction(ifinst);
   current_block_->AddSuccessor(branch_target);
   current_block_->AddSuccessor(fallthrough_target);
@@ -320,7 +322,7 @@
                                          const DexFile::CodeItem& code_item,
                                          const DexFile::TryItem& try_item) {
   // Split the edge with a single TryBoundary instruction.
-  HTryBoundary* try_boundary = new (arena_) HTryBoundary(kind);
+  HTryBoundary* try_boundary = new (arena_) HTryBoundary(kind, successor->GetDexPc());
   HBasicBlock* try_entry_block = graph_->SplitEdge(predecessor, successor);
   try_entry_block->AddInstruction(try_boundary);
 
@@ -537,7 +539,7 @@
     // Branching instructions clear current_block, so we know
     // the last instruction of the current block is not a branching
     // instruction. We add an unconditional goto to the found block.
-    current_block_->AddInstruction(new (arena_) HGoto());
+    current_block_->AddInstruction(new (arena_) HGoto(dex_pc));
     current_block_->AddSuccessor(block);
   }
   graph_->AddBlock(block);
@@ -633,104 +635,92 @@
 }
 
 template<typename T>
-void HGraphBuilder::Unop_12x(const Instruction& instruction, Primitive::Type type) {
-  HInstruction* first = LoadLocal(instruction.VRegB(), type);
-  current_block_->AddInstruction(new (arena_) T(type, first));
-  UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
+void HGraphBuilder::Unop_12x(const Instruction& instruction,
+                             Primitive::Type type,
+                             uint32_t dex_pc) {
+  HInstruction* first = LoadLocal(instruction.VRegB(), type, dex_pc);
+  current_block_->AddInstruction(new (arena_) T(type, first, dex_pc));
+  UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction(), dex_pc);
 }
 
 void HGraphBuilder::Conversion_12x(const Instruction& instruction,
                                    Primitive::Type input_type,
                                    Primitive::Type result_type,
                                    uint32_t dex_pc) {
-  HInstruction* first = LoadLocal(instruction.VRegB(), input_type);
+  HInstruction* first = LoadLocal(instruction.VRegB(), input_type, dex_pc);
   current_block_->AddInstruction(new (arena_) HTypeConversion(result_type, first, dex_pc));
-  UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
-}
-
-template<typename T>
-void HGraphBuilder::Binop_23x(const Instruction& instruction, Primitive::Type type) {
-  HInstruction* first = LoadLocal(instruction.VRegB(), type);
-  HInstruction* second = LoadLocal(instruction.VRegC(), type);
-  current_block_->AddInstruction(new (arena_) T(type, first, second));
-  UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
+  UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction(), dex_pc);
 }
 
 template<typename T>
 void HGraphBuilder::Binop_23x(const Instruction& instruction,
                               Primitive::Type type,
                               uint32_t dex_pc) {
-  HInstruction* first = LoadLocal(instruction.VRegB(), type);
-  HInstruction* second = LoadLocal(instruction.VRegC(), type);
+  HInstruction* first = LoadLocal(instruction.VRegB(), type, dex_pc);
+  HInstruction* second = LoadLocal(instruction.VRegC(), type, dex_pc);
   current_block_->AddInstruction(new (arena_) T(type, first, second, dex_pc));
-  UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
+  UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction(), dex_pc);
 }
 
 template<typename T>
 void HGraphBuilder::Binop_23x_shift(const Instruction& instruction,
-                                    Primitive::Type type) {
-  HInstruction* first = LoadLocal(instruction.VRegB(), type);
-  HInstruction* second = LoadLocal(instruction.VRegC(), Primitive::kPrimInt);
-  current_block_->AddInstruction(new (arena_) T(type, first, second));
-  UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
+                                    Primitive::Type type,
+                                    uint32_t dex_pc) {
+  HInstruction* first = LoadLocal(instruction.VRegB(), type, dex_pc);
+  HInstruction* second = LoadLocal(instruction.VRegC(), Primitive::kPrimInt, dex_pc);
+  current_block_->AddInstruction(new (arena_) T(type, first, second, dex_pc));
+  UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction(), dex_pc);
 }
 
 void HGraphBuilder::Binop_23x_cmp(const Instruction& instruction,
                                   Primitive::Type type,
                                   ComparisonBias bias,
                                   uint32_t dex_pc) {
-  HInstruction* first = LoadLocal(instruction.VRegB(), type);
-  HInstruction* second = LoadLocal(instruction.VRegC(), type);
+  HInstruction* first = LoadLocal(instruction.VRegB(), type, dex_pc);
+  HInstruction* second = LoadLocal(instruction.VRegC(), type, dex_pc);
   current_block_->AddInstruction(new (arena_) HCompare(type, first, second, bias, dex_pc));
-  UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
+  UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction(), dex_pc);
 }
 
 template<typename T>
-void HGraphBuilder::Binop_12x(const Instruction& instruction, Primitive::Type type) {
-  HInstruction* first = LoadLocal(instruction.VRegA(), type);
-  HInstruction* second = LoadLocal(instruction.VRegB(), type);
-  current_block_->AddInstruction(new (arena_) T(type, first, second));
-  UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
-}
-
-template<typename T>
-void HGraphBuilder::Binop_12x_shift(const Instruction& instruction, Primitive::Type type) {
-  HInstruction* first = LoadLocal(instruction.VRegA(), type);
-  HInstruction* second = LoadLocal(instruction.VRegB(), Primitive::kPrimInt);
-  current_block_->AddInstruction(new (arena_) T(type, first, second));
-  UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
+void HGraphBuilder::Binop_12x_shift(const Instruction& instruction, Primitive::Type type,
+                                    uint32_t dex_pc) {
+  HInstruction* first = LoadLocal(instruction.VRegA(), type, dex_pc);
+  HInstruction* second = LoadLocal(instruction.VRegB(), Primitive::kPrimInt, dex_pc);
+  current_block_->AddInstruction(new (arena_) T(type, first, second, dex_pc));
+  UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction(), dex_pc);
 }
 
 template<typename T>
 void HGraphBuilder::Binop_12x(const Instruction& instruction,
                               Primitive::Type type,
                               uint32_t dex_pc) {
-  HInstruction* first = LoadLocal(instruction.VRegA(), type);
-  HInstruction* second = LoadLocal(instruction.VRegB(), type);
+  HInstruction* first = LoadLocal(instruction.VRegA(), type, dex_pc);
+  HInstruction* second = LoadLocal(instruction.VRegB(), type, dex_pc);
   current_block_->AddInstruction(new (arena_) T(type, first, second, dex_pc));
-  UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
+  UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction(), dex_pc);
 }
 
 template<typename T>
-void HGraphBuilder::Binop_22s(const Instruction& instruction, bool reverse) {
-  HInstruction* first = LoadLocal(instruction.VRegB(), Primitive::kPrimInt);
-  HInstruction* second = graph_->GetIntConstant(instruction.VRegC_22s());
+void HGraphBuilder::Binop_22s(const Instruction& instruction, bool reverse, uint32_t dex_pc) {
+  HInstruction* first = LoadLocal(instruction.VRegB(), Primitive::kPrimInt, dex_pc);
+  HInstruction* second = graph_->GetIntConstant(instruction.VRegC_22s(), dex_pc);
   if (reverse) {
     std::swap(first, second);
   }
-  current_block_->AddInstruction(new (arena_) T(Primitive::kPrimInt, first, second));
-  UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
+  current_block_->AddInstruction(new (arena_) T(Primitive::kPrimInt, first, second, dex_pc));
+  UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction(), dex_pc);
 }
 
 template<typename T>
-void HGraphBuilder::Binop_22b(const Instruction& instruction, bool reverse) {
-  HInstruction* first = LoadLocal(instruction.VRegB(), Primitive::kPrimInt);
-  HInstruction* second = graph_->GetIntConstant(instruction.VRegC_22b());
+void HGraphBuilder::Binop_22b(const Instruction& instruction, bool reverse, uint32_t dex_pc) {
+  HInstruction* first = LoadLocal(instruction.VRegB(), Primitive::kPrimInt, dex_pc);
+  HInstruction* second = graph_->GetIntConstant(instruction.VRegC_22b(), dex_pc);
   if (reverse) {
     std::swap(first, second);
   }
-  current_block_->AddInstruction(new (arena_) T(Primitive::kPrimInt, first, second));
-  UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
+  current_block_->AddInstruction(new (arena_) T(Primitive::kPrimInt, first, second, dex_pc));
+  UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction(), dex_pc);
 }
 
 static bool RequiresConstructorBarrier(const DexCompilationUnit* cu, const CompilerDriver& driver) {
@@ -739,7 +729,9 @@
       && driver.RequiresConstructorBarrier(self, cu->GetDexFile(), cu->GetClassDefIndex());
 }
 
-void HGraphBuilder::BuildReturn(const Instruction& instruction, Primitive::Type type) {
+void HGraphBuilder::BuildReturn(const Instruction& instruction,
+                                Primitive::Type type,
+                                uint32_t dex_pc) {
   if (type == Primitive::kPrimVoid) {
     if (graph_->ShouldGenerateConstructorBarrier()) {
       // The compilation unit is null during testing.
@@ -747,12 +739,12 @@
         DCHECK(RequiresConstructorBarrier(dex_compilation_unit_, *compiler_driver_))
           << "Inconsistent use of ShouldGenerateConstructorBarrier. Should not generate a barrier.";
       }
-      current_block_->AddInstruction(new (arena_) HMemoryBarrier(kStoreStore));
+      current_block_->AddInstruction(new (arena_) HMemoryBarrier(kStoreStore, dex_pc));
     }
-    current_block_->AddInstruction(new (arena_) HReturnVoid());
+    current_block_->AddInstruction(new (arena_) HReturnVoid(dex_pc));
   } else {
-    HInstruction* value = LoadLocal(instruction.VRegA(), type);
-    current_block_->AddInstruction(new (arena_) HReturn(value));
+    HInstruction* value = LoadLocal(instruction.VRegA(), type, dex_pc);
+    current_block_->AddInstruction(new (arena_) HReturn(value, dex_pc));
   }
   current_block_->AddSuccessor(exit_block_);
   current_block_ = nullptr;
@@ -1049,6 +1041,7 @@
   size_t start_index = 0;
   size_t argument_index = 0;
   uint32_t descriptor_index = 1;  // Skip the return type.
+  uint32_t dex_pc = invoke->GetDexPc();
 
   bool is_instance_call = invoke->GetOriginalInvokeType() != InvokeType::kStatic;
   bool is_string_init = invoke->IsInvokeStaticOrDirect()
@@ -1059,7 +1052,7 @@
     argument_index = 0;
   } else if (is_instance_call) {
     Temporaries temps(graph_);
-    HInstruction* arg = LoadLocal(is_range ? register_index : args[0], Primitive::kPrimNot);
+    HInstruction* arg = LoadLocal(is_range ? register_index : args[0], Primitive::kPrimNot, dex_pc);
     HNullCheck* null_check = new (arena_) HNullCheck(arg, invoke->GetDexPc());
     current_block_->AddInstruction(null_check);
     temps.Add(null_check);
@@ -1088,7 +1081,7 @@
       MaybeRecordStat(MethodCompilationStat::kNotCompiledMalformedOpcode);
       return false;
     }
-    HInstruction* arg = LoadLocal(is_range ? register_index + i : args[i], type);
+    HInstruction* arg = LoadLocal(is_range ? register_index + i : args[i], type, dex_pc);
     invoke->SetArgumentAt(argument_index, arg);
     if (is_wide) {
       i++;
@@ -1121,7 +1114,7 @@
   // Add move-result for StringFactory method.
   if (is_string_init) {
     uint32_t orig_this_reg = is_range ? register_index : args[0];
-    HInstruction* fake_string = LoadLocal(orig_this_reg, Primitive::kPrimNot);
+    HInstruction* fake_string = LoadLocal(orig_this_reg, Primitive::kPrimNot, dex_pc);
     invoke->SetArgumentAt(argument_index, fake_string);
     current_block_->AddInstruction(invoke);
     PotentiallySimplifyFakeString(orig_this_reg, invoke->GetDexPc(), invoke);
@@ -1147,15 +1140,15 @@
   const VerifiedMethod* verified_method =
       compiler_driver_->GetVerifiedMethod(dex_file_, dex_compilation_unit_->GetDexMethodIndex());
   if (verified_method != nullptr) {
-    UpdateLocal(original_dex_register, actual_string);
+    UpdateLocal(original_dex_register, actual_string, dex_pc);
     const SafeMap<uint32_t, std::set<uint32_t>>& string_init_map =
         verified_method->GetStringInitPcRegMap();
     auto map_it = string_init_map.find(dex_pc);
     if (map_it != string_init_map.end()) {
       std::set<uint32_t> reg_set = map_it->second;
       for (auto set_it = reg_set.begin(); set_it != reg_set.end(); ++set_it) {
-        HInstruction* load_local = LoadLocal(original_dex_register, Primitive::kPrimNot);
-        UpdateLocal(*set_it, load_local);
+        HInstruction* load_local = LoadLocal(original_dex_register, Primitive::kPrimNot, dex_pc);
+        UpdateLocal(*set_it, load_local, dex_pc);
       }
     }
   } else {
@@ -1189,14 +1182,14 @@
 
   Primitive::Type field_type = resolved_field->GetTypeAsPrimitiveType();
 
-  HInstruction* object = LoadLocal(obj_reg, Primitive::kPrimNot);
+  HInstruction* object = LoadLocal(obj_reg, Primitive::kPrimNot, dex_pc);
   current_block_->AddInstruction(new (arena_) HNullCheck(object, dex_pc));
   if (is_put) {
     Temporaries temps(graph_);
     HInstruction* null_check = current_block_->GetLastInstruction();
     // We need one temporary for the null check.
     temps.Add(null_check);
-    HInstruction* value = LoadLocal(source_or_dest_reg, field_type);
+    HInstruction* value = LoadLocal(source_or_dest_reg, field_type, dex_pc);
     current_block_->AddInstruction(new (arena_) HInstanceFieldSet(
         null_check,
         value,
@@ -1205,7 +1198,8 @@
         resolved_field->IsVolatile(),
         field_index,
         *dex_file_,
-        dex_compilation_unit_->GetDexCache()));
+        dex_compilation_unit_->GetDexCache(),
+        dex_pc));
   } else {
     current_block_->AddInstruction(new (arena_) HInstanceFieldGet(
         current_block_->GetLastInstruction(),
@@ -1214,9 +1208,10 @@
         resolved_field->IsVolatile(),
         field_index,
         *dex_file_,
-        dex_compilation_unit_->GetDexCache()));
+        dex_compilation_unit_->GetDexCache(),
+        dex_pc));
 
-    UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction());
+    UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction(), dex_pc);
   }
   return true;
 }
@@ -1327,7 +1322,7 @@
     // We need to keep the class alive before loading the value.
     Temporaries temps(graph_);
     temps.Add(cls);
-    HInstruction* value = LoadLocal(source_or_dest_reg, field_type);
+    HInstruction* value = LoadLocal(source_or_dest_reg, field_type, dex_pc);
     DCHECK_EQ(value->GetType(), field_type);
     current_block_->AddInstruction(new (arena_) HStaticFieldSet(cls,
                                                                 value,
@@ -1336,7 +1331,8 @@
                                                                 resolved_field->IsVolatile(),
                                                                 field_index,
                                                                 *dex_file_,
-                                                                dex_cache_));
+                                                                dex_cache_,
+                                                                dex_pc));
   } else {
     current_block_->AddInstruction(new (arena_) HStaticFieldGet(cls,
                                                                 field_type,
@@ -1344,8 +1340,9 @@
                                                                 resolved_field->IsVolatile(),
                                                                 field_index,
                                                                 *dex_file_,
-                                                                dex_cache_));
-    UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction());
+                                                                dex_cache_,
+                                                                dex_pc));
+    UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction(), dex_pc);
   }
   return true;
 }
@@ -1359,16 +1356,16 @@
                                        bool isDiv) {
   DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
 
-  HInstruction* first = LoadLocal(first_vreg, type);
+  HInstruction* first = LoadLocal(first_vreg, type, dex_pc);
   HInstruction* second = nullptr;
   if (second_is_constant) {
     if (type == Primitive::kPrimInt) {
-      second = graph_->GetIntConstant(second_vreg_or_constant);
+      second = graph_->GetIntConstant(second_vreg_or_constant, dex_pc);
     } else {
-      second = graph_->GetLongConstant(second_vreg_or_constant);
+      second = graph_->GetLongConstant(second_vreg_or_constant, dex_pc);
     }
   } else {
-    second = LoadLocal(second_vreg_or_constant, type);
+    second = LoadLocal(second_vreg_or_constant, type, dex_pc);
   }
 
   if (!second_is_constant
@@ -1385,7 +1382,7 @@
   } else {
     current_block_->AddInstruction(new (arena_) HRem(type, first, second, dex_pc));
   }
-  UpdateLocal(out_vreg, current_block_->GetLastInstruction());
+  UpdateLocal(out_vreg, current_block_->GetLastInstruction(), dex_pc);
 }
 
 void HGraphBuilder::BuildArrayAccess(const Instruction& instruction,
@@ -1399,26 +1396,26 @@
   // We need one temporary for the null check, one for the index, and one for the length.
   Temporaries temps(graph_);
 
-  HInstruction* object = LoadLocal(array_reg, Primitive::kPrimNot);
+  HInstruction* object = LoadLocal(array_reg, Primitive::kPrimNot, dex_pc);
   object = new (arena_) HNullCheck(object, dex_pc);
   current_block_->AddInstruction(object);
   temps.Add(object);
 
-  HInstruction* length = new (arena_) HArrayLength(object);
+  HInstruction* length = new (arena_) HArrayLength(object, dex_pc);
   current_block_->AddInstruction(length);
   temps.Add(length);
-  HInstruction* index = LoadLocal(index_reg, Primitive::kPrimInt);
+  HInstruction* index = LoadLocal(index_reg, Primitive::kPrimInt, dex_pc);
   index = new (arena_) HBoundsCheck(index, length, dex_pc);
   current_block_->AddInstruction(index);
   temps.Add(index);
   if (is_put) {
-    HInstruction* value = LoadLocal(source_or_dest_reg, anticipated_type);
+    HInstruction* value = LoadLocal(source_or_dest_reg, anticipated_type, dex_pc);
     // TODO: Insert a type check node if the type is Object.
     current_block_->AddInstruction(new (arena_) HArraySet(
         object, index, value, anticipated_type, dex_pc));
   } else {
-    current_block_->AddInstruction(new (arena_) HArrayGet(object, index, anticipated_type));
-    UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction());
+    current_block_->AddInstruction(new (arena_) HArrayGet(object, index, anticipated_type, dex_pc));
+    UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction(), dex_pc);
   }
   graph_->SetHasBoundsChecks(true);
 }
@@ -1429,7 +1426,7 @@
                                         bool is_range,
                                         uint32_t* args,
                                         uint32_t register_index) {
-  HInstruction* length = graph_->GetIntConstant(number_of_vreg_arguments);
+  HInstruction* length = graph_->GetIntConstant(number_of_vreg_arguments, dex_pc);
   QuickEntrypointEnum entrypoint = NeedsAccessCheck(type_index)
       ? kQuickAllocArrayWithAccessCheck
       : kQuickAllocArray;
@@ -1453,8 +1450,8 @@
   Temporaries temps(graph_);
   temps.Add(object);
   for (size_t i = 0; i < number_of_vreg_arguments; ++i) {
-    HInstruction* value = LoadLocal(is_range ? register_index + i : args[i], type);
-    HInstruction* index = graph_->GetIntConstant(i);
+    HInstruction* value = LoadLocal(is_range ? register_index + i : args[i], type, dex_pc);
+    HInstruction* index = graph_->GetIntConstant(i, dex_pc);
     current_block_->AddInstruction(
         new (arena_) HArraySet(object, index, value, type, dex_pc));
   }
@@ -1468,8 +1465,8 @@
                                        Primitive::Type anticipated_type,
                                        uint32_t dex_pc) {
   for (uint32_t i = 0; i < element_count; ++i) {
-    HInstruction* index = graph_->GetIntConstant(i);
-    HInstruction* value = graph_->GetIntConstant(data[i]);
+    HInstruction* index = graph_->GetIntConstant(i, dex_pc);
+    HInstruction* value = graph_->GetIntConstant(data[i], dex_pc);
     current_block_->AddInstruction(new (arena_) HArraySet(
       object, index, value, anticipated_type, dex_pc));
   }
@@ -1477,12 +1474,12 @@
 
 void HGraphBuilder::BuildFillArrayData(const Instruction& instruction, uint32_t dex_pc) {
   Temporaries temps(graph_);
-  HInstruction* array = LoadLocal(instruction.VRegA_31t(), Primitive::kPrimNot);
+  HInstruction* array = LoadLocal(instruction.VRegA_31t(), Primitive::kPrimNot, dex_pc);
   HNullCheck* null_check = new (arena_) HNullCheck(array, dex_pc);
   current_block_->AddInstruction(null_check);
   temps.Add(null_check);
 
-  HInstruction* length = new (arena_) HArrayLength(null_check);
+  HInstruction* length = new (arena_) HArrayLength(null_check, dex_pc);
   current_block_->AddInstruction(length);
 
   int32_t payload_offset = instruction.VRegB_31t() + dex_pc;
@@ -1493,7 +1490,7 @@
 
   // Implementation of this DEX instruction seems to be that the bounds check is
   // done before doing any stores.
-  HInstruction* last_index = graph_->GetIntConstant(payload->element_count - 1);
+  HInstruction* last_index = graph_->GetIntConstant(payload->element_count - 1, dex_pc);
   current_block_->AddInstruction(new (arena_) HBoundsCheck(last_index, length, dex_pc));
 
   switch (payload->element_width) {
@@ -1535,8 +1532,8 @@
                                            uint32_t element_count,
                                            uint32_t dex_pc) {
   for (uint32_t i = 0; i < element_count; ++i) {
-    HInstruction* index = graph_->GetIntConstant(i);
-    HInstruction* value = graph_->GetLongConstant(data[i]);
+    HInstruction* index = graph_->GetIntConstant(i, dex_pc);
+    HInstruction* value = graph_->GetLongConstant(data[i], dex_pc);
     current_block_->AddInstruction(new (arena_) HArraySet(
       object, index, value, Primitive::kPrimLong, dex_pc));
   }
@@ -1561,7 +1558,7 @@
     MaybeRecordStat(MethodCompilationStat::kNotCompiledCantAccesType);
     return false;
   }
-  HInstruction* object = LoadLocal(reference, Primitive::kPrimNot);
+  HInstruction* object = LoadLocal(reference, Primitive::kPrimNot, dex_pc);
   HLoadClass* cls = new (arena_) HLoadClass(
       graph_->GetCurrentMethod(),
       type_index,
@@ -1575,7 +1572,7 @@
   if (instruction.Opcode() == Instruction::INSTANCE_OF) {
     current_block_->AddInstruction(
         new (arena_) HInstanceOf(object, cls, type_known_final, dex_pc));
-    UpdateLocal(destination, current_block_->GetLastInstruction());
+    UpdateLocal(destination, current_block_->GetLastInstruction(), dex_pc);
   } else {
     DCHECK_EQ(instruction.Opcode(), Instruction::CHECK_CAST);
     current_block_->AddInstruction(
@@ -1597,7 +1594,7 @@
   SwitchTable table(instruction, dex_pc, false);
 
   // Value to test against.
-  HInstruction* value = LoadLocal(instruction.VRegA(), Primitive::kPrimInt);
+  HInstruction* value = LoadLocal(instruction.VRegA(), Primitive::kPrimInt, dex_pc);
 
   // Retrieve number of entries.
   uint16_t num_entries = table.GetNumEntries();
@@ -1622,7 +1619,7 @@
   SwitchTable table(instruction, dex_pc, true);
 
   // Value to test against.
-  HInstruction* value = LoadLocal(instruction.VRegA(), Primitive::kPrimInt);
+  HInstruction* value = LoadLocal(instruction.VRegA(), Primitive::kPrimInt, dex_pc);
 
   uint16_t num_entries = table.GetNumEntries();
 
@@ -1641,12 +1638,12 @@
   PotentiallyAddSuspendCheck(case_target, dex_pc);
 
   // The current case's value.
-  HInstruction* this_case_value = graph_->GetIntConstant(case_value_int);
+  HInstruction* this_case_value = graph_->GetIntConstant(case_value_int, dex_pc);
 
   // Compare value and this_case_value.
-  HEqual* comparison = new (arena_) HEqual(value, this_case_value);
+  HEqual* comparison = new (arena_) HEqual(value, this_case_value, dex_pc);
   current_block_->AddInstruction(comparison);
-  HInstruction* ifinst = new (arena_) HIf(comparison);
+  HInstruction* ifinst = new (arena_) HIf(comparison, dex_pc);
   current_block_->AddInstruction(ifinst);
 
   // Case hit: use the target offset to determine where to go.
@@ -1710,29 +1707,29 @@
   switch (instruction.Opcode()) {
     case Instruction::CONST_4: {
       int32_t register_index = instruction.VRegA();
-      HIntConstant* constant = graph_->GetIntConstant(instruction.VRegB_11n());
-      UpdateLocal(register_index, constant);
+      HIntConstant* constant = graph_->GetIntConstant(instruction.VRegB_11n(), dex_pc);
+      UpdateLocal(register_index, constant, dex_pc);
       break;
     }
 
     case Instruction::CONST_16: {
       int32_t register_index = instruction.VRegA();
-      HIntConstant* constant = graph_->GetIntConstant(instruction.VRegB_21s());
-      UpdateLocal(register_index, constant);
+      HIntConstant* constant = graph_->GetIntConstant(instruction.VRegB_21s(), dex_pc);
+      UpdateLocal(register_index, constant, dex_pc);
       break;
     }
 
     case Instruction::CONST: {
       int32_t register_index = instruction.VRegA();
-      HIntConstant* constant = graph_->GetIntConstant(instruction.VRegB_31i());
-      UpdateLocal(register_index, constant);
+      HIntConstant* constant = graph_->GetIntConstant(instruction.VRegB_31i(), dex_pc);
+      UpdateLocal(register_index, constant, dex_pc);
       break;
     }
 
     case Instruction::CONST_HIGH16: {
       int32_t register_index = instruction.VRegA();
-      HIntConstant* constant = graph_->GetIntConstant(instruction.VRegB_21h() << 16);
-      UpdateLocal(register_index, constant);
+      HIntConstant* constant = graph_->GetIntConstant(instruction.VRegB_21h() << 16, dex_pc);
+      UpdateLocal(register_index, constant, dex_pc);
       break;
     }
 
@@ -1742,8 +1739,8 @@
       int64_t value = instruction.VRegB_21s();
       value <<= 48;
       value >>= 48;
-      HLongConstant* constant = graph_->GetLongConstant(value);
-      UpdateLocal(register_index, constant);
+      HLongConstant* constant = graph_->GetLongConstant(value, dex_pc);
+      UpdateLocal(register_index, constant, dex_pc);
       break;
     }
 
@@ -1753,23 +1750,23 @@
       int64_t value = instruction.VRegB_31i();
       value <<= 32;
       value >>= 32;
-      HLongConstant* constant = graph_->GetLongConstant(value);
-      UpdateLocal(register_index, constant);
+      HLongConstant* constant = graph_->GetLongConstant(value, dex_pc);
+      UpdateLocal(register_index, constant, dex_pc);
       break;
     }
 
     case Instruction::CONST_WIDE: {
       int32_t register_index = instruction.VRegA();
-      HLongConstant* constant = graph_->GetLongConstant(instruction.VRegB_51l());
-      UpdateLocal(register_index, constant);
+      HLongConstant* constant = graph_->GetLongConstant(instruction.VRegB_51l(), dex_pc);
+      UpdateLocal(register_index, constant, dex_pc);
       break;
     }
 
     case Instruction::CONST_WIDE_HIGH16: {
       int32_t register_index = instruction.VRegA();
       int64_t value = static_cast<int64_t>(instruction.VRegB_21h()) << 48;
-      HLongConstant* constant = graph_->GetLongConstant(value);
-      UpdateLocal(register_index, constant);
+      HLongConstant* constant = graph_->GetLongConstant(value, dex_pc);
+      UpdateLocal(register_index, constant, dex_pc);
       break;
     }
 
@@ -1777,8 +1774,8 @@
     case Instruction::MOVE:
     case Instruction::MOVE_FROM16:
     case Instruction::MOVE_16: {
-      HInstruction* value = LoadLocal(instruction.VRegB(), Primitive::kPrimInt);
-      UpdateLocal(instruction.VRegA(), value);
+      HInstruction* value = LoadLocal(instruction.VRegB(), Primitive::kPrimInt, dex_pc);
+      UpdateLocal(instruction.VRegA(), value, dex_pc);
       break;
     }
 
@@ -1786,22 +1783,22 @@
     case Instruction::MOVE_WIDE:
     case Instruction::MOVE_WIDE_FROM16:
     case Instruction::MOVE_WIDE_16: {
-      HInstruction* value = LoadLocal(instruction.VRegB(), Primitive::kPrimLong);
-      UpdateLocal(instruction.VRegA(), value);
+      HInstruction* value = LoadLocal(instruction.VRegB(), Primitive::kPrimLong, dex_pc);
+      UpdateLocal(instruction.VRegA(), value, dex_pc);
       break;
     }
 
     case Instruction::MOVE_OBJECT:
     case Instruction::MOVE_OBJECT_16:
     case Instruction::MOVE_OBJECT_FROM16: {
-      HInstruction* value = LoadLocal(instruction.VRegB(), Primitive::kPrimNot);
-      UpdateLocal(instruction.VRegA(), value);
+      HInstruction* value = LoadLocal(instruction.VRegB(), Primitive::kPrimNot, dex_pc);
+      UpdateLocal(instruction.VRegA(), value, dex_pc);
       break;
     }
 
     case Instruction::RETURN_VOID_NO_BARRIER:
     case Instruction::RETURN_VOID: {
-      BuildReturn(instruction, Primitive::kPrimVoid);
+      BuildReturn(instruction, Primitive::kPrimVoid, dex_pc);
       break;
     }
 
@@ -1823,24 +1820,24 @@
       HBasicBlock* target = FindBlockStartingAt(offset + dex_pc);
       DCHECK(target != nullptr);
       PotentiallyAddSuspendCheck(target, dex_pc);
-      current_block_->AddInstruction(new (arena_) HGoto());
+      current_block_->AddInstruction(new (arena_) HGoto(dex_pc));
       current_block_->AddSuccessor(target);
       current_block_ = nullptr;
       break;
     }
 
     case Instruction::RETURN: {
-      BuildReturn(instruction, return_type_);
+      BuildReturn(instruction, return_type_, dex_pc);
       break;
     }
 
     case Instruction::RETURN_OBJECT: {
-      BuildReturn(instruction, return_type_);
+      BuildReturn(instruction, return_type_, dex_pc);
       break;
     }
 
     case Instruction::RETURN_WIDE: {
-      BuildReturn(instruction, return_type_);
+      BuildReturn(instruction, return_type_, dex_pc);
       break;
     }
 
@@ -1894,32 +1891,32 @@
     }
 
     case Instruction::NEG_INT: {
-      Unop_12x<HNeg>(instruction, Primitive::kPrimInt);
+      Unop_12x<HNeg>(instruction, Primitive::kPrimInt, dex_pc);
       break;
     }
 
     case Instruction::NEG_LONG: {
-      Unop_12x<HNeg>(instruction, Primitive::kPrimLong);
+      Unop_12x<HNeg>(instruction, Primitive::kPrimLong, dex_pc);
       break;
     }
 
     case Instruction::NEG_FLOAT: {
-      Unop_12x<HNeg>(instruction, Primitive::kPrimFloat);
+      Unop_12x<HNeg>(instruction, Primitive::kPrimFloat, dex_pc);
       break;
     }
 
     case Instruction::NEG_DOUBLE: {
-      Unop_12x<HNeg>(instruction, Primitive::kPrimDouble);
+      Unop_12x<HNeg>(instruction, Primitive::kPrimDouble, dex_pc);
       break;
     }
 
     case Instruction::NOT_INT: {
-      Unop_12x<HNot>(instruction, Primitive::kPrimInt);
+      Unop_12x<HNot>(instruction, Primitive::kPrimInt, dex_pc);
       break;
     }
 
     case Instruction::NOT_LONG: {
-      Unop_12x<HNot>(instruction, Primitive::kPrimLong);
+      Unop_12x<HNot>(instruction, Primitive::kPrimLong, dex_pc);
       break;
     }
 
@@ -1999,67 +1996,67 @@
     }
 
     case Instruction::ADD_INT: {
-      Binop_23x<HAdd>(instruction, Primitive::kPrimInt);
+      Binop_23x<HAdd>(instruction, Primitive::kPrimInt, dex_pc);
       break;
     }
 
     case Instruction::ADD_LONG: {
-      Binop_23x<HAdd>(instruction, Primitive::kPrimLong);
+      Binop_23x<HAdd>(instruction, Primitive::kPrimLong, dex_pc);
       break;
     }
 
     case Instruction::ADD_DOUBLE: {
-      Binop_23x<HAdd>(instruction, Primitive::kPrimDouble);
+      Binop_23x<HAdd>(instruction, Primitive::kPrimDouble, dex_pc);
       break;
     }
 
     case Instruction::ADD_FLOAT: {
-      Binop_23x<HAdd>(instruction, Primitive::kPrimFloat);
+      Binop_23x<HAdd>(instruction, Primitive::kPrimFloat, dex_pc);
       break;
     }
 
     case Instruction::SUB_INT: {
-      Binop_23x<HSub>(instruction, Primitive::kPrimInt);
+      Binop_23x<HSub>(instruction, Primitive::kPrimInt, dex_pc);
       break;
     }
 
     case Instruction::SUB_LONG: {
-      Binop_23x<HSub>(instruction, Primitive::kPrimLong);
+      Binop_23x<HSub>(instruction, Primitive::kPrimLong, dex_pc);
       break;
     }
 
     case Instruction::SUB_FLOAT: {
-      Binop_23x<HSub>(instruction, Primitive::kPrimFloat);
+      Binop_23x<HSub>(instruction, Primitive::kPrimFloat, dex_pc);
       break;
     }
 
     case Instruction::SUB_DOUBLE: {
-      Binop_23x<HSub>(instruction, Primitive::kPrimDouble);
+      Binop_23x<HSub>(instruction, Primitive::kPrimDouble, dex_pc);
       break;
     }
 
     case Instruction::ADD_INT_2ADDR: {
-      Binop_12x<HAdd>(instruction, Primitive::kPrimInt);
+      Binop_12x<HAdd>(instruction, Primitive::kPrimInt, dex_pc);
       break;
     }
 
     case Instruction::MUL_INT: {
-      Binop_23x<HMul>(instruction, Primitive::kPrimInt);
+      Binop_23x<HMul>(instruction, Primitive::kPrimInt, dex_pc);
       break;
     }
 
     case Instruction::MUL_LONG: {
-      Binop_23x<HMul>(instruction, Primitive::kPrimLong);
+      Binop_23x<HMul>(instruction, Primitive::kPrimLong, dex_pc);
       break;
     }
 
     case Instruction::MUL_FLOAT: {
-      Binop_23x<HMul>(instruction, Primitive::kPrimFloat);
+      Binop_23x<HMul>(instruction, Primitive::kPrimFloat, dex_pc);
       break;
     }
 
     case Instruction::MUL_DOUBLE: {
-      Binop_23x<HMul>(instruction, Primitive::kPrimDouble);
+      Binop_23x<HMul>(instruction, Primitive::kPrimDouble, dex_pc);
       break;
     }
 
@@ -2108,117 +2105,117 @@
     }
 
     case Instruction::AND_INT: {
-      Binop_23x<HAnd>(instruction, Primitive::kPrimInt);
+      Binop_23x<HAnd>(instruction, Primitive::kPrimInt, dex_pc);
       break;
     }
 
     case Instruction::AND_LONG: {
-      Binop_23x<HAnd>(instruction, Primitive::kPrimLong);
+      Binop_23x<HAnd>(instruction, Primitive::kPrimLong, dex_pc);
       break;
     }
 
     case Instruction::SHL_INT: {
-      Binop_23x_shift<HShl>(instruction, Primitive::kPrimInt);
+      Binop_23x_shift<HShl>(instruction, Primitive::kPrimInt, dex_pc);
       break;
     }
 
     case Instruction::SHL_LONG: {
-      Binop_23x_shift<HShl>(instruction, Primitive::kPrimLong);
+      Binop_23x_shift<HShl>(instruction, Primitive::kPrimLong, dex_pc);
       break;
     }
 
     case Instruction::SHR_INT: {
-      Binop_23x_shift<HShr>(instruction, Primitive::kPrimInt);
+      Binop_23x_shift<HShr>(instruction, Primitive::kPrimInt, dex_pc);
       break;
     }
 
     case Instruction::SHR_LONG: {
-      Binop_23x_shift<HShr>(instruction, Primitive::kPrimLong);
+      Binop_23x_shift<HShr>(instruction, Primitive::kPrimLong, dex_pc);
       break;
     }
 
     case Instruction::USHR_INT: {
-      Binop_23x_shift<HUShr>(instruction, Primitive::kPrimInt);
+      Binop_23x_shift<HUShr>(instruction, Primitive::kPrimInt, dex_pc);
       break;
     }
 
     case Instruction::USHR_LONG: {
-      Binop_23x_shift<HUShr>(instruction, Primitive::kPrimLong);
+      Binop_23x_shift<HUShr>(instruction, Primitive::kPrimLong, dex_pc);
       break;
     }
 
     case Instruction::OR_INT: {
-      Binop_23x<HOr>(instruction, Primitive::kPrimInt);
+      Binop_23x<HOr>(instruction, Primitive::kPrimInt, dex_pc);
       break;
     }
 
     case Instruction::OR_LONG: {
-      Binop_23x<HOr>(instruction, Primitive::kPrimLong);
+      Binop_23x<HOr>(instruction, Primitive::kPrimLong, dex_pc);
       break;
     }
 
     case Instruction::XOR_INT: {
-      Binop_23x<HXor>(instruction, Primitive::kPrimInt);
+      Binop_23x<HXor>(instruction, Primitive::kPrimInt, dex_pc);
       break;
     }
 
     case Instruction::XOR_LONG: {
-      Binop_23x<HXor>(instruction, Primitive::kPrimLong);
+      Binop_23x<HXor>(instruction, Primitive::kPrimLong, dex_pc);
       break;
     }
 
     case Instruction::ADD_LONG_2ADDR: {
-      Binop_12x<HAdd>(instruction, Primitive::kPrimLong);
+      Binop_12x<HAdd>(instruction, Primitive::kPrimLong, dex_pc);
       break;
     }
 
     case Instruction::ADD_DOUBLE_2ADDR: {
-      Binop_12x<HAdd>(instruction, Primitive::kPrimDouble);
+      Binop_12x<HAdd>(instruction, Primitive::kPrimDouble, dex_pc);
       break;
     }
 
     case Instruction::ADD_FLOAT_2ADDR: {
-      Binop_12x<HAdd>(instruction, Primitive::kPrimFloat);
+      Binop_12x<HAdd>(instruction, Primitive::kPrimFloat, dex_pc);
       break;
     }
 
     case Instruction::SUB_INT_2ADDR: {
-      Binop_12x<HSub>(instruction, Primitive::kPrimInt);
+      Binop_12x<HSub>(instruction, Primitive::kPrimInt, dex_pc);
       break;
     }
 
     case Instruction::SUB_LONG_2ADDR: {
-      Binop_12x<HSub>(instruction, Primitive::kPrimLong);
+      Binop_12x<HSub>(instruction, Primitive::kPrimLong, dex_pc);
       break;
     }
 
     case Instruction::SUB_FLOAT_2ADDR: {
-      Binop_12x<HSub>(instruction, Primitive::kPrimFloat);
+      Binop_12x<HSub>(instruction, Primitive::kPrimFloat, dex_pc);
       break;
     }
 
     case Instruction::SUB_DOUBLE_2ADDR: {
-      Binop_12x<HSub>(instruction, Primitive::kPrimDouble);
+      Binop_12x<HSub>(instruction, Primitive::kPrimDouble, dex_pc);
       break;
     }
 
     case Instruction::MUL_INT_2ADDR: {
-      Binop_12x<HMul>(instruction, Primitive::kPrimInt);
+      Binop_12x<HMul>(instruction, Primitive::kPrimInt, dex_pc);
       break;
     }
 
     case Instruction::MUL_LONG_2ADDR: {
-      Binop_12x<HMul>(instruction, Primitive::kPrimLong);
+      Binop_12x<HMul>(instruction, Primitive::kPrimLong, dex_pc);
       break;
     }
 
     case Instruction::MUL_FLOAT_2ADDR: {
-      Binop_12x<HMul>(instruction, Primitive::kPrimFloat);
+      Binop_12x<HMul>(instruction, Primitive::kPrimFloat, dex_pc);
       break;
     }
 
     case Instruction::MUL_DOUBLE_2ADDR: {
-      Binop_12x<HMul>(instruction, Primitive::kPrimDouble);
+      Binop_12x<HMul>(instruction, Primitive::kPrimDouble, dex_pc);
       break;
     }
 
@@ -2257,32 +2254,32 @@
     }
 
     case Instruction::SHL_INT_2ADDR: {
-      Binop_12x_shift<HShl>(instruction, Primitive::kPrimInt);
+      Binop_12x_shift<HShl>(instruction, Primitive::kPrimInt, dex_pc);
       break;
     }
 
     case Instruction::SHL_LONG_2ADDR: {
-      Binop_12x_shift<HShl>(instruction, Primitive::kPrimLong);
+      Binop_12x_shift<HShl>(instruction, Primitive::kPrimLong, dex_pc);
       break;
     }
 
     case Instruction::SHR_INT_2ADDR: {
-      Binop_12x_shift<HShr>(instruction, Primitive::kPrimInt);
+      Binop_12x_shift<HShr>(instruction, Primitive::kPrimInt, dex_pc);
       break;
     }
 
     case Instruction::SHR_LONG_2ADDR: {
-      Binop_12x_shift<HShr>(instruction, Primitive::kPrimLong);
+      Binop_12x_shift<HShr>(instruction, Primitive::kPrimLong, dex_pc);
       break;
     }
 
     case Instruction::USHR_INT_2ADDR: {
-      Binop_12x_shift<HUShr>(instruction, Primitive::kPrimInt);
+      Binop_12x_shift<HUShr>(instruction, Primitive::kPrimInt, dex_pc);
       break;
     }
 
     case Instruction::USHR_LONG_2ADDR: {
-      Binop_12x_shift<HUShr>(instruction, Primitive::kPrimLong);
+      Binop_12x_shift<HUShr>(instruction, Primitive::kPrimLong, dex_pc);
       break;
     }
 
@@ -2297,92 +2294,92 @@
     }
 
     case Instruction::AND_INT_2ADDR: {
-      Binop_12x<HAnd>(instruction, Primitive::kPrimInt);
+      Binop_12x<HAnd>(instruction, Primitive::kPrimInt, dex_pc);
       break;
     }
 
     case Instruction::AND_LONG_2ADDR: {
-      Binop_12x<HAnd>(instruction, Primitive::kPrimLong);
+      Binop_12x<HAnd>(instruction, Primitive::kPrimLong, dex_pc);
       break;
     }
 
     case Instruction::OR_INT_2ADDR: {
-      Binop_12x<HOr>(instruction, Primitive::kPrimInt);
+      Binop_12x<HOr>(instruction, Primitive::kPrimInt, dex_pc);
       break;
     }
 
     case Instruction::OR_LONG_2ADDR: {
-      Binop_12x<HOr>(instruction, Primitive::kPrimLong);
+      Binop_12x<HOr>(instruction, Primitive::kPrimLong, dex_pc);
       break;
     }
 
     case Instruction::XOR_INT_2ADDR: {
-      Binop_12x<HXor>(instruction, Primitive::kPrimInt);
+      Binop_12x<HXor>(instruction, Primitive::kPrimInt, dex_pc);
       break;
     }
 
     case Instruction::XOR_LONG_2ADDR: {
-      Binop_12x<HXor>(instruction, Primitive::kPrimLong);
+      Binop_12x<HXor>(instruction, Primitive::kPrimLong, dex_pc);
       break;
     }
 
     case Instruction::ADD_INT_LIT16: {
-      Binop_22s<HAdd>(instruction, false);
+      Binop_22s<HAdd>(instruction, false, dex_pc);
       break;
     }
 
     case Instruction::AND_INT_LIT16: {
-      Binop_22s<HAnd>(instruction, false);
+      Binop_22s<HAnd>(instruction, false, dex_pc);
       break;
     }
 
     case Instruction::OR_INT_LIT16: {
-      Binop_22s<HOr>(instruction, false);
+      Binop_22s<HOr>(instruction, false, dex_pc);
       break;
     }
 
     case Instruction::XOR_INT_LIT16: {
-      Binop_22s<HXor>(instruction, false);
+      Binop_22s<HXor>(instruction, false, dex_pc);
       break;
     }
 
     case Instruction::RSUB_INT: {
-      Binop_22s<HSub>(instruction, true);
+      Binop_22s<HSub>(instruction, true, dex_pc);
       break;
     }
 
     case Instruction::MUL_INT_LIT16: {
-      Binop_22s<HMul>(instruction, false);
+      Binop_22s<HMul>(instruction, false, dex_pc);
       break;
     }
 
     case Instruction::ADD_INT_LIT8: {
-      Binop_22b<HAdd>(instruction, false);
+      Binop_22b<HAdd>(instruction, false, dex_pc);
       break;
     }
 
     case Instruction::AND_INT_LIT8: {
-      Binop_22b<HAnd>(instruction, false);
+      Binop_22b<HAnd>(instruction, false, dex_pc);
       break;
     }
 
     case Instruction::OR_INT_LIT8: {
-      Binop_22b<HOr>(instruction, false);
+      Binop_22b<HOr>(instruction, false, dex_pc);
       break;
     }
 
     case Instruction::XOR_INT_LIT8: {
-      Binop_22b<HXor>(instruction, false);
+      Binop_22b<HXor>(instruction, false, dex_pc);
       break;
     }
 
     case Instruction::RSUB_INT_LIT8: {
-      Binop_22b<HSub>(instruction, true);
+      Binop_22b<HSub>(instruction, true, dex_pc);
       break;
     }
 
     case Instruction::MUL_INT_LIT8: {
-      Binop_22b<HMul>(instruction, false);
+      Binop_22b<HMul>(instruction, false, dex_pc);
       break;
     }
 
@@ -2401,17 +2398,17 @@
     }
 
     case Instruction::SHL_INT_LIT8: {
-      Binop_22b<HShl>(instruction, false);
+      Binop_22b<HShl>(instruction, false, dex_pc);
       break;
     }
 
     case Instruction::SHR_INT_LIT8: {
-      Binop_22b<HShr>(instruction, false);
+      Binop_22b<HShr>(instruction, false, dex_pc);
       break;
     }
 
     case Instruction::USHR_INT_LIT8: {
-      Binop_22b<HUShr>(instruction, false);
+      Binop_22b<HUShr>(instruction, false, dex_pc);
       break;
     }
 
@@ -2419,9 +2416,9 @@
       uint16_t type_index = instruction.VRegB_21c();
       if (compiler_driver_->IsStringTypeIndex(type_index, dex_file_)) {
         int32_t register_index = instruction.VRegA();
-        HFakeString* fake_string = new (arena_) HFakeString();
+        HFakeString* fake_string = new (arena_) HFakeString(dex_pc);
         current_block_->AddInstruction(fake_string);
-        UpdateLocal(register_index, fake_string);
+        UpdateLocal(register_index, fake_string, dex_pc);
       } else {
         QuickEntrypointEnum entrypoint = NeedsAccessCheck(type_index)
             ? kQuickAllocObjectWithAccessCheck
@@ -2433,14 +2430,14 @@
             type_index,
             *dex_compilation_unit_->GetDexFile(),
             entrypoint));
-        UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
+        UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction(), dex_pc);
       }
       break;
     }
 
     case Instruction::NEW_ARRAY: {
       uint16_t type_index = instruction.VRegC_22c();
-      HInstruction* length = LoadLocal(instruction.VRegB_22c(), Primitive::kPrimInt);
+      HInstruction* length = LoadLocal(instruction.VRegB_22c(), Primitive::kPrimInt, dex_pc);
       QuickEntrypointEnum entrypoint = NeedsAccessCheck(type_index)
           ? kQuickAllocArrayWithAccessCheck
           : kQuickAllocArray;
@@ -2450,7 +2447,7 @@
                                                             type_index,
                                                             *dex_compilation_unit_->GetDexFile(),
                                                             entrypoint));
-      UpdateLocal(instruction.VRegA_22c(), current_block_->GetLastInstruction());
+      UpdateLocal(instruction.VRegA_22c(), current_block_->GetLastInstruction(), dex_pc);
       break;
     }
 
@@ -2490,7 +2487,7 @@
         // FilledNewArray, the local needs to be updated after the array was
         // filled, otherwise we might overwrite an input vreg.
         HStoreLocal* update_local =
-            new (arena_) HStoreLocal(GetLocalAt(instruction.VRegA()), latest_result_);
+            new (arena_) HStoreLocal(GetLocalAt(instruction.VRegA()), latest_result_, dex_pc);
         HBasicBlock* block = latest_result_->GetBlock();
         if (block == current_block_) {
           // MoveResult and the previous instruction are in the same block.
@@ -2620,27 +2617,27 @@
     ARRAY_XX(_SHORT, Primitive::kPrimShort);
 
     case Instruction::ARRAY_LENGTH: {
-      HInstruction* object = LoadLocal(instruction.VRegB_12x(), Primitive::kPrimNot);
+      HInstruction* object = LoadLocal(instruction.VRegB_12x(), Primitive::kPrimNot, dex_pc);
       // No need for a temporary for the null check, it is the only input of the following
       // instruction.
       object = new (arena_) HNullCheck(object, dex_pc);
       current_block_->AddInstruction(object);
-      current_block_->AddInstruction(new (arena_) HArrayLength(object));
-      UpdateLocal(instruction.VRegA_12x(), current_block_->GetLastInstruction());
+      current_block_->AddInstruction(new (arena_) HArrayLength(object, dex_pc));
+      UpdateLocal(instruction.VRegA_12x(), current_block_->GetLastInstruction(), dex_pc);
       break;
     }
 
     case Instruction::CONST_STRING: {
       current_block_->AddInstruction(
           new (arena_) HLoadString(graph_->GetCurrentMethod(), instruction.VRegB_21c(), dex_pc));
-      UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction());
+      UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction(), dex_pc);
       break;
     }
 
     case Instruction::CONST_STRING_JUMBO: {
       current_block_->AddInstruction(
           new (arena_) HLoadString(graph_->GetCurrentMethod(), instruction.VRegB_31c(), dex_pc));
-      UpdateLocal(instruction.VRegA_31c(), current_block_->GetLastInstruction());
+      UpdateLocal(instruction.VRegA_31c(), current_block_->GetLastInstruction(), dex_pc);
       break;
     }
 
@@ -2666,19 +2663,19 @@
           *dex_compilation_unit_->GetDexFile(),
           IsOutermostCompilingClass(type_index),
           dex_pc));
-      UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction());
+      UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction(), dex_pc);
       break;
     }
 
     case Instruction::MOVE_EXCEPTION: {
-      current_block_->AddInstruction(new (arena_) HLoadException());
-      UpdateLocal(instruction.VRegA_11x(), current_block_->GetLastInstruction());
-      current_block_->AddInstruction(new (arena_) HClearException());
+      current_block_->AddInstruction(new (arena_) HLoadException(dex_pc));
+      UpdateLocal(instruction.VRegA_11x(), current_block_->GetLastInstruction(), dex_pc);
+      current_block_->AddInstruction(new (arena_) HClearException(dex_pc));
       break;
     }
 
     case Instruction::THROW: {
-      HInstruction* exception = LoadLocal(instruction.VRegA_11x(), Primitive::kPrimNot);
+      HInstruction* exception = LoadLocal(instruction.VRegA_11x(), Primitive::kPrimNot, dex_pc);
       current_block_->AddInstruction(new (arena_) HThrow(exception, dex_pc));
       // A throw instruction must branch to the exit block.
       current_block_->AddSuccessor(exit_block_);
@@ -2709,7 +2706,7 @@
 
     case Instruction::MONITOR_ENTER: {
       current_block_->AddInstruction(new (arena_) HMonitorOperation(
-          LoadLocal(instruction.VRegA_11x(), Primitive::kPrimNot),
+          LoadLocal(instruction.VRegA_11x(), Primitive::kPrimNot, dex_pc),
           HMonitorOperation::kEnter,
           dex_pc));
       break;
@@ -2717,7 +2714,7 @@
 
     case Instruction::MONITOR_EXIT: {
       current_block_->AddInstruction(new (arena_) HMonitorOperation(
-          LoadLocal(instruction.VRegA_11x(), Primitive::kPrimNot),
+          LoadLocal(instruction.VRegA_11x(), Primitive::kPrimNot, dex_pc),
           HMonitorOperation::kExit,
           dex_pc));
       break;
@@ -2748,14 +2745,18 @@
   return locals_.Get(register_index);
 }
 
-void HGraphBuilder::UpdateLocal(int register_index, HInstruction* instruction) const {
+void HGraphBuilder::UpdateLocal(int register_index,
+                                HInstruction* instruction,
+                                uint32_t dex_pc) const {
   HLocal* local = GetLocalAt(register_index);
-  current_block_->AddInstruction(new (arena_) HStoreLocal(local, instruction));
+  current_block_->AddInstruction(new (arena_) HStoreLocal(local, instruction, dex_pc));
 }
 
-HInstruction* HGraphBuilder::LoadLocal(int register_index, Primitive::Type type) const {
+HInstruction* HGraphBuilder::LoadLocal(int register_index,
+                                       Primitive::Type type,
+                                       uint32_t dex_pc) const {
   HLocal* local = GetLocalAt(register_index);
-  current_block_->AddInstruction(new (arena_) HLoadLocal(local, type));
+  current_block_->AddInstruction(new (arena_) HLoadLocal(local, type, dex_pc));
   return current_block_->GetLastInstruction();
 }
 
diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h
index 560ed86..b0238dc 100644
--- a/compiler/optimizing/builder.h
+++ b/compiler/optimizing/builder.h
@@ -131,23 +131,20 @@
 
   void InitializeLocals(uint16_t count);
   HLocal* GetLocalAt(int register_index) const;
-  void UpdateLocal(int register_index, HInstruction* instruction) const;
-  HInstruction* LoadLocal(int register_index, Primitive::Type type) const;
+  void UpdateLocal(int register_index, HInstruction* instruction, uint32_t dex_pc) const;
+  HInstruction* LoadLocal(int register_index, Primitive::Type type, uint32_t dex_pc) const;
   void PotentiallyAddSuspendCheck(HBasicBlock* target, uint32_t dex_pc);
   void InitializeParameters(uint16_t number_of_parameters);
   bool NeedsAccessCheck(uint32_t type_index) const;
 
   template<typename T>
-  void Unop_12x(const Instruction& instruction, Primitive::Type type);
-
-  template<typename T>
-  void Binop_23x(const Instruction& instruction, Primitive::Type type);
+  void Unop_12x(const Instruction& instruction, Primitive::Type type, uint32_t dex_pc);
 
   template<typename T>
   void Binop_23x(const Instruction& instruction, Primitive::Type type, uint32_t dex_pc);
 
   template<typename T>
-  void Binop_23x_shift(const Instruction& instruction, Primitive::Type type);
+  void Binop_23x_shift(const Instruction& instruction, Primitive::Type type, uint32_t dex_pc);
 
   void Binop_23x_cmp(const Instruction& instruction,
                      Primitive::Type type,
@@ -155,19 +152,16 @@
                      uint32_t dex_pc);
 
   template<typename T>
-  void Binop_12x(const Instruction& instruction, Primitive::Type type);
-
-  template<typename T>
   void Binop_12x(const Instruction& instruction, Primitive::Type type, uint32_t dex_pc);
 
   template<typename T>
-  void Binop_12x_shift(const Instruction& instruction, Primitive::Type type);
+  void Binop_12x_shift(const Instruction& instruction, Primitive::Type type, uint32_t dex_pc);
 
   template<typename T>
-  void Binop_22b(const Instruction& instruction, bool reverse);
+  void Binop_22b(const Instruction& instruction, bool reverse, uint32_t dex_pc);
 
   template<typename T>
-  void Binop_22s(const Instruction& instruction, bool reverse);
+  void Binop_22s(const Instruction& instruction, bool reverse, uint32_t dex_pc);
 
   template<typename T> void If_21t(const Instruction& instruction, uint32_t dex_pc);
   template<typename T> void If_22t(const Instruction& instruction, uint32_t dex_pc);
@@ -185,7 +179,7 @@
                           bool second_is_lit,
                           bool is_div);
 
-  void BuildReturn(const Instruction& instruction, Primitive::Type type);
+  void BuildReturn(const Instruction& instruction, Primitive::Type type, uint32_t dex_pc);
 
   // Builds an instance field access node and returns whether the instruction is supported.
   bool BuildInstanceFieldAccess(const Instruction& instruction, uint32_t dex_pc, bool is_put);
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index f5c4498..0bb90b2 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -129,12 +129,12 @@
 }
 
 size_t CodeGenerator::GetCacheOffset(uint32_t index) {
-  return mirror::ObjectArray<mirror::Object>::OffsetOfElement(index).SizeValue();
+  return sizeof(GcRoot<mirror::Object>) * index;
 }
 
 size_t CodeGenerator::GetCachePointerOffset(uint32_t index) {
   auto pointer_size = InstructionSetPointerSize(GetInstructionSet());
-  return mirror::Array::DataOffset(pointer_size).Uint32Value() + pointer_size * index;
+  return pointer_size * index;
 }
 
 void CodeGenerator::CompileBaseline(CodeAllocator* allocator, bool is_leaf) {
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 0640179..a4c58b0 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -48,7 +48,7 @@
 // with baseline.
 static constexpr Register kCoreSavedRegisterForBaseline = R5;
 static constexpr Register kCoreCalleeSaves[] =
-    { R5, R6, R7, R8, R10, R11, PC };
+    { R5, R6, R7, R8, R10, R11, LR };
 static constexpr SRegister kFpuCalleeSaves[] =
     { S16, S17, S18, S19, S20, S21, S22, S23, S24, S25, S26, S27, S28, S29, S30, S31 };
 
@@ -409,8 +409,8 @@
       method_patches_(MethodReferenceComparator(), graph->GetArena()->Adapter()),
       call_patches_(MethodReferenceComparator(), graph->GetArena()->Adapter()),
       relative_call_patches_(graph->GetArena()->Adapter()) {
-  // Save the PC register to mimic Quick.
-  AddAllocatedRegister(Location::RegisterLocation(PC));
+  // Always save the LR register to mimic Quick.
+  AddAllocatedRegister(Location::RegisterLocation(LR));
 }
 
 void CodeGeneratorARM::Finalize(CodeAllocator* allocator) {
@@ -599,12 +599,9 @@
     RecordPcInfo(nullptr, 0);
   }
 
-  // PC is in the list of callee-save to mimic Quick, but we need to push
-  // LR at entry instead.
-  uint32_t push_mask = (core_spill_mask_ & (~(1 << PC))) | 1 << LR;
-  __ PushList(push_mask);
-  __ cfi().AdjustCFAOffset(kArmWordSize * POPCOUNT(push_mask));
-  __ cfi().RelOffsetForMany(DWARFReg(kMethodRegisterArgument), 0, push_mask, kArmWordSize);
+  __ PushList(core_spill_mask_);
+  __ cfi().AdjustCFAOffset(kArmWordSize * POPCOUNT(core_spill_mask_));
+  __ cfi().RelOffsetForMany(DWARFReg(kMethodRegisterArgument), 0, core_spill_mask_, kArmWordSize);
   if (fpu_spill_mask_ != 0) {
     SRegister start_register = SRegister(LeastSignificantBit(fpu_spill_mask_));
     __ vpushs(start_register, POPCOUNT(fpu_spill_mask_));
@@ -632,7 +629,10 @@
     __ cfi().AdjustCFAOffset(-kArmPointerSize * POPCOUNT(fpu_spill_mask_));
     __ cfi().RestoreMany(DWARFReg(SRegister(0)), fpu_spill_mask_);
   }
-  __ PopList(core_spill_mask_);
+  // Pop LR into PC to return.
+  DCHECK_NE(core_spill_mask_ & (1 << LR), 0U);
+  uint32_t pop_mask = (core_spill_mask_ & (~(1 << LR))) | 1 << PC;
+  __ PopList(pop_mask);
   __ cfi().RestoreState();
   __ cfi().DefCFAOffset(GetFrameSize());
 }
@@ -1560,25 +1560,7 @@
     return;
   }
 
-  Register temp = invoke->GetLocations()->GetTemp(0).AsRegister<Register>();
-  uint32_t method_offset = mirror::Class::EmbeddedVTableEntryOffset(
-      invoke->GetVTableIndex(), kArmPointerSize).Uint32Value();
-  LocationSummary* locations = invoke->GetLocations();
-  Location receiver = locations->InAt(0);
-  uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
-  // temp = object->GetClass();
-  DCHECK(receiver.IsRegister());
-  __ LoadFromOffset(kLoadWord, temp, receiver.AsRegister<Register>(), class_offset);
-  codegen_->MaybeRecordImplicitNullCheck(invoke);
-  __ MaybeUnpoisonHeapReference(temp);
-  // temp = temp->GetMethodAt(method_offset);
-  uint32_t entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset(
-      kArmWordSize).Int32Value();
-  __ LoadFromOffset(kLoadWord, temp, temp, method_offset);
-  // LR = temp->GetEntryPoint();
-  __ LoadFromOffset(kLoadWord, LR, temp, entry_point);
-  // LR();
-  __ blx(LR);
+  codegen_->GenerateVirtualCall(invoke, invoke->GetLocations()->GetTemp(0));
   DCHECK(!codegen_->IsLeafMethod());
   codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
 }
@@ -4224,9 +4206,9 @@
     __ LoadFromOffset(kLoadWord,
                       out,
                       current_method,
-                      ArtMethod::DexCacheResolvedTypesOffset().Int32Value());
+                      ArtMethod::DexCacheResolvedTypesOffset(kArmPointerSize).Int32Value());
     __ LoadFromOffset(kLoadWord, out, out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex()));
-    __ MaybeUnpoisonHeapReference(out);
+    // TODO: We will need a read barrier here.
 
     SlowPathCodeARM* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathARM(
         cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck());
@@ -4286,9 +4268,8 @@
   __ LoadFromOffset(
       kLoadWord, out, current_method, ArtMethod::DeclaringClassOffset().Int32Value());
   __ LoadFromOffset(kLoadWord, out, out, mirror::Class::DexCacheStringsOffset().Int32Value());
-  __ MaybeUnpoisonHeapReference(out);
   __ LoadFromOffset(kLoadWord, out, out, CodeGenerator::GetCacheOffset(load->GetStringIndex()));
-  __ MaybeUnpoisonHeapReference(out);
+  // TODO: We will need a read barrier here.
   __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel());
   __ Bind(slow_path->GetExitLabel());
 }
@@ -4564,7 +4545,8 @@
       }
       // temp = current_method->dex_cache_resolved_methods_;
       __ LoadFromOffset(
-          kLoadWord, reg, method_reg, ArtMethod::DexCacheResolvedMethodsOffset().Int32Value());
+          kLoadWord, reg, method_reg, ArtMethod::DexCacheResolvedMethodsOffset(
+              kArmPointerSize).Int32Value());
       // temp = temp[index_in_cache]
       uint32_t index_in_cache = invoke->GetTargetMethod().dex_method_index;
       __ LoadFromOffset(kLoadWord, reg, reg, CodeGenerator::GetCachePointerOffset(index_in_cache));
@@ -4607,6 +4589,28 @@
   DCHECK(!IsLeafMethod());
 }
 
+void CodeGeneratorARM::GenerateVirtualCall(HInvokeVirtual* invoke, Location temp_location) {
+  Register temp = temp_location.AsRegister<Register>();
+  uint32_t method_offset = mirror::Class::EmbeddedVTableEntryOffset(
+      invoke->GetVTableIndex(), kArmPointerSize).Uint32Value();
+  LocationSummary* locations = invoke->GetLocations();
+  Location receiver = locations->InAt(0);
+  uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
+  // temp = object->GetClass();
+  DCHECK(receiver.IsRegister());
+  __ LoadFromOffset(kLoadWord, temp, receiver.AsRegister<Register>(), class_offset);
+  MaybeRecordImplicitNullCheck(invoke);
+  __ MaybeUnpoisonHeapReference(temp);
+  // temp = temp->GetMethodAt(method_offset);
+  uint32_t entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset(
+      kArmWordSize).Int32Value();
+  __ LoadFromOffset(kLoadWord, temp, temp, method_offset);
+  // LR = temp->GetEntryPoint();
+  __ LoadFromOffset(kLoadWord, LR, temp, entry_point);
+  // LR();
+  __ blx(LR);
+}
+
 void CodeGeneratorARM::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) {
   DCHECK(linker_patches->empty());
   size_t size = method_patches_.size() + call_patches_.size() + relative_call_patches_.size();
diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h
index 9528cca..4a0df4e 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -327,6 +327,7 @@
   Label* GetFrameEntryLabel() { return &frame_entry_label_; }
 
   void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp);
+  void GenerateVirtualCall(HInvokeVirtual* invoke, Location temp);
 
   void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE;
 
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 8035461..6b1457b 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -2433,8 +2433,9 @@
       }
 
       // temp = current_method->dex_cache_resolved_methods_;
-      __ Ldr(reg.W(), MemOperand(method_reg.X(),
-                                 ArtMethod::DexCacheResolvedMethodsOffset().Int32Value()));
+      __ Ldr(reg.X(),
+             MemOperand(method_reg.X(),
+                        ArtMethod::DexCacheResolvedMethodsOffset(kArm64WordSize).Int32Value()));
       // temp = temp[index_in_cache];
       uint32_t index_in_cache = invoke->GetTargetMethod().dex_method_index;
     __ Ldr(reg.X(), MemOperand(reg.X(), GetCachePointerOffset(index_in_cache)));
@@ -2473,6 +2474,29 @@
   DCHECK(!IsLeafMethod());
 }
 
+void CodeGeneratorARM64::GenerateVirtualCall(HInvokeVirtual* invoke, Location temp_in) {
+  LocationSummary* locations = invoke->GetLocations();
+  Location receiver = locations->InAt(0);
+  Register temp = XRegisterFrom(temp_in);
+  size_t method_offset = mirror::Class::EmbeddedVTableEntryOffset(
+      invoke->GetVTableIndex(), kArm64PointerSize).SizeValue();
+  Offset class_offset = mirror::Object::ClassOffset();
+  Offset entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArm64WordSize);
+
+  BlockPoolsScope block_pools(GetVIXLAssembler());
+
+  DCHECK(receiver.IsRegister());
+  __ Ldr(temp.W(), HeapOperandFrom(receiver, class_offset));
+  MaybeRecordImplicitNullCheck(invoke);
+  GetAssembler()->MaybeUnpoisonHeapReference(temp.W());
+  // temp = temp->GetMethodAt(method_offset);
+  __ Ldr(temp, MemOperand(temp, method_offset));
+  // lr = temp->GetEntryPoint();
+  __ Ldr(lr, MemOperand(temp, entry_point.SizeValue()));
+  // lr();
+  __ Blr(lr);
+}
+
 void CodeGeneratorARM64::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) {
   DCHECK(linker_patches->empty());
   size_t size =
@@ -2566,26 +2590,7 @@
     return;
   }
 
-  LocationSummary* locations = invoke->GetLocations();
-  Location receiver = locations->InAt(0);
-  Register temp = XRegisterFrom(invoke->GetLocations()->GetTemp(0));
-  size_t method_offset = mirror::Class::EmbeddedVTableEntryOffset(
-      invoke->GetVTableIndex(), kArm64PointerSize).SizeValue();
-  Offset class_offset = mirror::Object::ClassOffset();
-  Offset entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArm64WordSize);
-
-  BlockPoolsScope block_pools(GetVIXLAssembler());
-
-  DCHECK(receiver.IsRegister());
-  __ Ldr(temp.W(), HeapOperandFrom(receiver, class_offset));
-  codegen_->MaybeRecordImplicitNullCheck(invoke);
-  GetAssembler()->MaybeUnpoisonHeapReference(temp.W());
-  // temp = temp->GetMethodAt(method_offset);
-  __ Ldr(temp, MemOperand(temp, method_offset));
-  // lr = temp->GetEntryPoint();
-  __ Ldr(lr, MemOperand(temp, entry_point.SizeValue()));
-  // lr();
-  __ Blr(lr);
+  codegen_->GenerateVirtualCall(invoke, invoke->GetLocations()->GetTemp(0));
   DCHECK(!codegen_->IsLeafMethod());
   codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
 }
@@ -2607,9 +2612,10 @@
     __ Ldr(out, MemOperand(current_method, ArtMethod::DeclaringClassOffset().Int32Value()));
   } else {
     DCHECK(cls->CanCallRuntime());
-    __ Ldr(out, MemOperand(current_method, ArtMethod::DexCacheResolvedTypesOffset().Int32Value()));
-    __ Ldr(out, HeapOperand(out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex())));
-    GetAssembler()->MaybeUnpoisonHeapReference(out.W());
+    MemberOffset resolved_types_offset = ArtMethod::DexCacheResolvedTypesOffset(kArm64PointerSize);
+    __ Ldr(out.X(), MemOperand(current_method, resolved_types_offset.Int32Value()));
+    __ Ldr(out, MemOperand(out.X(), CodeGenerator::GetCacheOffset(cls->GetTypeIndex())));
+    // TODO: We will need a read barrier here.
 
     SlowPathCodeARM64* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathARM64(
         cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck());
@@ -2668,10 +2674,9 @@
   Register out = OutputRegister(load);
   Register current_method = InputRegisterAt(load, 0);
   __ Ldr(out, MemOperand(current_method, ArtMethod::DeclaringClassOffset().Int32Value()));
-  __ Ldr(out, HeapOperand(out, mirror::Class::DexCacheStringsOffset()));
-  GetAssembler()->MaybeUnpoisonHeapReference(out.W());
-  __ Ldr(out, HeapOperand(out, CodeGenerator::GetCacheOffset(load->GetStringIndex())));
-  GetAssembler()->MaybeUnpoisonHeapReference(out.W());
+  __ Ldr(out.X(), HeapOperand(out, mirror::Class::DexCacheStringsOffset()));
+  __ Ldr(out, MemOperand(out.X(), CodeGenerator::GetCacheOffset(load->GetStringIndex())));
+  // TODO: We will need a read barrier here.
   __ Cbz(out, slow_path->GetEntryLabel());
   __ Bind(slow_path->GetExitLabel());
 }
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index 18070fc..12ead7e 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -359,6 +359,7 @@
   }
 
   void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp);
+  void GenerateVirtualCall(HInvokeVirtual* invoke, Location temp);
 
   void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE;
 
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index e4188e4..10942ef 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -2426,10 +2426,10 @@
       }
 
       // temp = temp->dex_cache_resolved_methods_;
-      __ LoadFromOffset(kLoadUnsignedWord,
+      __ LoadFromOffset(kLoadDoubleword,
                         reg,
                         method_reg,
-                        ArtMethod::DexCacheResolvedMethodsOffset().Int32Value());
+                        ArtMethod::DexCacheResolvedMethodsOffset(kMips64PointerSize).Int32Value());
       // temp = temp[index_in_cache]
       uint32_t index_in_cache = invoke->GetTargetMethod().dex_method_index;
       __ LoadFromOffset(kLoadDoubleword,
@@ -2530,9 +2530,10 @@
                       ArtMethod::DeclaringClassOffset().Int32Value());
   } else {
     DCHECK(cls->CanCallRuntime());
-    __ LoadFromOffset(kLoadUnsignedWord, out, current_method,
-                      ArtMethod::DexCacheResolvedTypesOffset().Int32Value());
+    __ LoadFromOffset(kLoadDoubleword, out, current_method,
+                      ArtMethod::DexCacheResolvedTypesOffset(kMips64PointerSize).Int32Value());
     __ LoadFromOffset(kLoadUnsignedWord, out, out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex()));
+    // TODO: We will need a read barrier here.
     SlowPathCodeMIPS64* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathMIPS64(
         cls,
         cls,
@@ -2595,8 +2596,9 @@
   GpuRegister current_method = locations->InAt(0).AsRegister<GpuRegister>();
   __ LoadFromOffset(kLoadUnsignedWord, out, current_method,
                     ArtMethod::DeclaringClassOffset().Int32Value());
-  __ LoadFromOffset(kLoadUnsignedWord, out, out, mirror::Class::DexCacheStringsOffset().Int32Value());
+  __ LoadFromOffset(kLoadDoubleword, out, out, mirror::Class::DexCacheStringsOffset().Int32Value());
   __ LoadFromOffset(kLoadUnsignedWord, out, out, CodeGenerator::GetCacheOffset(load->GetStringIndex()));
+  // TODO: We will need a read barrier here.
   __ Beqzc(out, slow_path->GetEntryLabel());
   __ Bind(slow_path->GetExitLabel());
 }
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index e8aa61d..a5ad226 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -19,6 +19,7 @@
 #include "art_method.h"
 #include "code_generator_utils.h"
 #include "compiled_method.h"
+#include "constant_area_fixups_x86.h"
 #include "entrypoints/quick/quick_entrypoints.h"
 #include "entrypoints/quick/quick_entrypoints_enum.h"
 #include "gc/accounting/card_table.h"
@@ -1548,23 +1549,11 @@
 }
 
 void InstructionCodeGeneratorX86::VisitInvokeVirtual(HInvokeVirtual* invoke) {
-  Register temp = invoke->GetLocations()->GetTemp(0).AsRegister<Register>();
-  uint32_t method_offset = mirror::Class::EmbeddedVTableEntryOffset(
-      invoke->GetVTableIndex(), kX86PointerSize).Uint32Value();
-  LocationSummary* locations = invoke->GetLocations();
-  Location receiver = locations->InAt(0);
-  uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
-  // temp = object->GetClass();
-  DCHECK(receiver.IsRegister());
-  __ movl(temp, Address(receiver.AsRegister<Register>(), class_offset));
-  codegen_->MaybeRecordImplicitNullCheck(invoke);
-  __ MaybeUnpoisonHeapReference(temp);
-  // temp = temp->GetMethodAt(method_offset);
-  __ movl(temp, Address(temp, method_offset));
-  // call temp->GetEntryPoint();
-  __ call(Address(
-      temp, ArtMethod::EntryPointFromQuickCompiledCodeOffset(kX86WordSize).Int32Value()));
+  if (TryGenerateIntrinsicCode(invoke, codegen_)) {
+    return;
+  }
 
+  codegen_->GenerateVirtualCall(invoke, invoke->GetLocations()->GetTemp(0));
   DCHECK(!codegen_->IsLeafMethod());
   codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
 }
@@ -2213,7 +2202,7 @@
     case Primitive::kPrimFloat:
     case Primitive::kPrimDouble: {
       locations->SetInAt(0, Location::RequiresFpuRegister());
-      locations->SetInAt(1, Location::RequiresFpuRegister());
+      locations->SetInAt(1, Location::Any());
       locations->SetOut(Location::SameAsFirstInput());
       break;
     }
@@ -2275,6 +2264,16 @@
     case Primitive::kPrimFloat: {
       if (second.IsFpuRegister()) {
         __ addss(first.AsFpuRegister<XmmRegister>(), second.AsFpuRegister<XmmRegister>());
+      } else if (add->InputAt(1)->IsX86LoadFromConstantTable()) {
+        HX86LoadFromConstantTable* const_area = add->InputAt(1)->AsX86LoadFromConstantTable();
+        DCHECK(!const_area->NeedsMaterialization());
+        __ addss(first.AsFpuRegister<XmmRegister>(),
+                 codegen_->LiteralFloatAddress(
+                   const_area->GetConstant()->AsFloatConstant()->GetValue(),
+                   const_area->GetLocations()->InAt(0).AsRegister<Register>()));
+      } else {
+        DCHECK(second.IsStackSlot());
+        __ addss(first.AsFpuRegister<XmmRegister>(), Address(ESP, second.GetStackIndex()));
       }
       break;
     }
@@ -2282,6 +2281,16 @@
     case Primitive::kPrimDouble: {
       if (second.IsFpuRegister()) {
         __ addsd(first.AsFpuRegister<XmmRegister>(), second.AsFpuRegister<XmmRegister>());
+      } else if (add->InputAt(1)->IsX86LoadFromConstantTable()) {
+        HX86LoadFromConstantTable* const_area = add->InputAt(1)->AsX86LoadFromConstantTable();
+        DCHECK(!const_area->NeedsMaterialization());
+        __ addsd(first.AsFpuRegister<XmmRegister>(),
+                 codegen_->LiteralDoubleAddress(
+                   const_area->GetConstant()->AsDoubleConstant()->GetValue(),
+                   const_area->GetLocations()->InAt(0).AsRegister<Register>()));
+      } else {
+        DCHECK(second.IsDoubleStackSlot());
+        __ addsd(first.AsFpuRegister<XmmRegister>(), Address(ESP, second.GetStackIndex()));
       }
       break;
     }
@@ -2305,7 +2314,7 @@
     case Primitive::kPrimFloat:
     case Primitive::kPrimDouble: {
       locations->SetInAt(0, Location::RequiresFpuRegister());
-      locations->SetInAt(1, Location::RequiresFpuRegister());
+      locations->SetInAt(1, Location::Any());
       locations->SetOut(Location::SameAsFirstInput());
       break;
     }
@@ -2351,12 +2360,36 @@
     }
 
     case Primitive::kPrimFloat: {
-      __ subss(first.AsFpuRegister<XmmRegister>(), second.AsFpuRegister<XmmRegister>());
+      if (second.IsFpuRegister()) {
+        __ subss(first.AsFpuRegister<XmmRegister>(), second.AsFpuRegister<XmmRegister>());
+      } else if (sub->InputAt(1)->IsX86LoadFromConstantTable()) {
+        HX86LoadFromConstantTable* const_area = sub->InputAt(1)->AsX86LoadFromConstantTable();
+        DCHECK(!const_area->NeedsMaterialization());
+        __ subss(first.AsFpuRegister<XmmRegister>(),
+                 codegen_->LiteralFloatAddress(
+                   const_area->GetConstant()->AsFloatConstant()->GetValue(),
+                   const_area->GetLocations()->InAt(0).AsRegister<Register>()));
+      } else {
+        DCHECK(second.IsStackSlot());
+        __ subss(first.AsFpuRegister<XmmRegister>(), Address(ESP, second.GetStackIndex()));
+      }
       break;
     }
 
     case Primitive::kPrimDouble: {
-      __ subsd(first.AsFpuRegister<XmmRegister>(), second.AsFpuRegister<XmmRegister>());
+      if (second.IsFpuRegister()) {
+        __ subsd(first.AsFpuRegister<XmmRegister>(), second.AsFpuRegister<XmmRegister>());
+      } else if (sub->InputAt(1)->IsX86LoadFromConstantTable()) {
+        HX86LoadFromConstantTable* const_area = sub->InputAt(1)->AsX86LoadFromConstantTable();
+        DCHECK(!const_area->NeedsMaterialization());
+        __ subsd(first.AsFpuRegister<XmmRegister>(),
+                 codegen_->LiteralDoubleAddress(
+                     const_area->GetConstant()->AsDoubleConstant()->GetValue(),
+                     const_area->GetLocations()->InAt(0).AsRegister<Register>()));
+      } else {
+        DCHECK(second.IsDoubleStackSlot());
+        __ subsd(first.AsFpuRegister<XmmRegister>(), Address(ESP, second.GetStackIndex()));
+      }
       break;
     }
 
@@ -2391,7 +2424,7 @@
     case Primitive::kPrimFloat:
     case Primitive::kPrimDouble: {
       locations->SetInAt(0, Location::RequiresFpuRegister());
-      locations->SetInAt(1, Location::RequiresFpuRegister());
+      locations->SetInAt(1, Location::Any());
       locations->SetOut(Location::SameAsFirstInput());
       break;
     }
@@ -2507,12 +2540,38 @@
     }
 
     case Primitive::kPrimFloat: {
-      __ mulss(first.AsFpuRegister<XmmRegister>(), second.AsFpuRegister<XmmRegister>());
+      DCHECK(first.Equals(locations->Out()));
+      if (second.IsFpuRegister()) {
+        __ mulss(first.AsFpuRegister<XmmRegister>(), second.AsFpuRegister<XmmRegister>());
+      } else if (mul->InputAt(1)->IsX86LoadFromConstantTable()) {
+        HX86LoadFromConstantTable* const_area = mul->InputAt(1)->AsX86LoadFromConstantTable();
+        DCHECK(!const_area->NeedsMaterialization());
+        __ mulss(first.AsFpuRegister<XmmRegister>(),
+                 codegen_->LiteralFloatAddress(
+                     const_area->GetConstant()->AsFloatConstant()->GetValue(),
+                     const_area->GetLocations()->InAt(0).AsRegister<Register>()));
+      } else {
+        DCHECK(second.IsStackSlot());
+        __ mulss(first.AsFpuRegister<XmmRegister>(), Address(ESP, second.GetStackIndex()));
+      }
       break;
     }
 
     case Primitive::kPrimDouble: {
-      __ mulsd(first.AsFpuRegister<XmmRegister>(), second.AsFpuRegister<XmmRegister>());
+      DCHECK(first.Equals(locations->Out()));
+      if (second.IsFpuRegister()) {
+        __ mulsd(first.AsFpuRegister<XmmRegister>(), second.AsFpuRegister<XmmRegister>());
+      } else if (mul->InputAt(1)->IsX86LoadFromConstantTable()) {
+        HX86LoadFromConstantTable* const_area = mul->InputAt(1)->AsX86LoadFromConstantTable();
+        DCHECK(!const_area->NeedsMaterialization());
+        __ mulsd(first.AsFpuRegister<XmmRegister>(),
+                 codegen_->LiteralDoubleAddress(
+                     const_area->GetConstant()->AsDoubleConstant()->GetValue(),
+                     const_area->GetLocations()->InAt(0).AsRegister<Register>()));
+      } else {
+        DCHECK(second.IsDoubleStackSlot());
+        __ mulsd(first.AsFpuRegister<XmmRegister>(), Address(ESP, second.GetStackIndex()));
+      }
       break;
     }
 
@@ -2855,7 +2914,7 @@
     case Primitive::kPrimFloat:
     case Primitive::kPrimDouble: {
       locations->SetInAt(0, Location::RequiresFpuRegister());
-      locations->SetInAt(1, Location::RequiresFpuRegister());
+      locations->SetInAt(1, Location::Any());
       locations->SetOut(Location::SameAsFirstInput());
       break;
     }
@@ -2867,7 +2926,6 @@
 
 void InstructionCodeGeneratorX86::VisitDiv(HDiv* div) {
   LocationSummary* locations = div->GetLocations();
-  Location out = locations->Out();
   Location first = locations->InAt(0);
   Location second = locations->InAt(1);
 
@@ -2879,14 +2937,36 @@
     }
 
     case Primitive::kPrimFloat: {
-      DCHECK(first.Equals(out));
-      __ divss(first.AsFpuRegister<XmmRegister>(), second.AsFpuRegister<XmmRegister>());
+      if (second.IsFpuRegister()) {
+        __ divss(first.AsFpuRegister<XmmRegister>(), second.AsFpuRegister<XmmRegister>());
+      } else if (div->InputAt(1)->IsX86LoadFromConstantTable()) {
+        HX86LoadFromConstantTable* const_area = div->InputAt(1)->AsX86LoadFromConstantTable();
+        DCHECK(!const_area->NeedsMaterialization());
+        __ divss(first.AsFpuRegister<XmmRegister>(),
+                 codegen_->LiteralFloatAddress(
+                   const_area->GetConstant()->AsFloatConstant()->GetValue(),
+                   const_area->GetLocations()->InAt(0).AsRegister<Register>()));
+      } else {
+        DCHECK(second.IsStackSlot());
+        __ divss(first.AsFpuRegister<XmmRegister>(), Address(ESP, second.GetStackIndex()));
+      }
       break;
     }
 
     case Primitive::kPrimDouble: {
-      DCHECK(first.Equals(out));
-      __ divsd(first.AsFpuRegister<XmmRegister>(), second.AsFpuRegister<XmmRegister>());
+      if (second.IsFpuRegister()) {
+        __ divsd(first.AsFpuRegister<XmmRegister>(), second.AsFpuRegister<XmmRegister>());
+      } else if (div->InputAt(1)->IsX86LoadFromConstantTable()) {
+        HX86LoadFromConstantTable* const_area = div->InputAt(1)->AsX86LoadFromConstantTable();
+        DCHECK(!const_area->NeedsMaterialization());
+        __ divsd(first.AsFpuRegister<XmmRegister>(),
+                 codegen_->LiteralDoubleAddress(
+                   const_area->GetConstant()->AsDoubleConstant()->GetValue(),
+                   const_area->GetLocations()->InAt(0).AsRegister<Register>()));
+      } else {
+        DCHECK(second.IsDoubleStackSlot());
+        __ divsd(first.AsFpuRegister<XmmRegister>(), Address(ESP, second.GetStackIndex()));
+      }
       break;
     }
 
@@ -3534,7 +3614,8 @@
         __ movl(reg, Address(ESP, kCurrentMethodStackOffset));
       }
       // temp = temp->dex_cache_resolved_methods_;
-      __ movl(reg, Address(method_reg, ArtMethod::DexCacheResolvedMethodsOffset().Int32Value()));
+      __ movl(reg, Address(method_reg,
+                           ArtMethod::DexCacheResolvedMethodsOffset(kX86PointerSize).Int32Value()));
       // temp = temp[index_in_cache]
       uint32_t index_in_cache = invoke->GetTargetMethod().dex_method_index;
       __ movl(reg, Address(reg, CodeGenerator::GetCachePointerOffset(index_in_cache)));
@@ -3569,6 +3650,25 @@
   DCHECK(!IsLeafMethod());
 }
 
+void CodeGeneratorX86::GenerateVirtualCall(HInvokeVirtual* invoke, Location temp_in) {
+  Register temp = temp_in.AsRegister<Register>();
+  uint32_t method_offset = mirror::Class::EmbeddedVTableEntryOffset(
+      invoke->GetVTableIndex(), kX86PointerSize).Uint32Value();
+  LocationSummary* locations = invoke->GetLocations();
+  Location receiver = locations->InAt(0);
+  uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
+  // temp = object->GetClass();
+  DCHECK(receiver.IsRegister());
+  __ movl(temp, Address(receiver.AsRegister<Register>(), class_offset));
+  MaybeRecordImplicitNullCheck(invoke);
+  __ MaybeUnpoisonHeapReference(temp);
+  // temp = temp->GetMethodAt(method_offset);
+  __ movl(temp, Address(temp, method_offset));
+  // call temp->GetEntryPoint();
+  __ call(Address(
+      temp, ArtMethod::EntryPointFromQuickCompiledCodeOffset(kX86WordSize).Int32Value()));
+}
+
 void CodeGeneratorX86::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) {
   DCHECK(linker_patches->empty());
   linker_patches->reserve(method_patches_.size() + relative_call_patches_.size());
@@ -4708,9 +4808,9 @@
   } else {
     DCHECK(cls->CanCallRuntime());
     __ movl(out, Address(
-        current_method, ArtMethod::DexCacheResolvedTypesOffset().Int32Value()));
+        current_method, ArtMethod::DexCacheResolvedTypesOffset(kX86PointerSize).Int32Value()));
     __ movl(out, Address(out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex())));
-    __ MaybeUnpoisonHeapReference(out);
+    // TODO: We will need a read barrier here.
 
     SlowPathCodeX86* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathX86(
         cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck());
@@ -4768,9 +4868,8 @@
   Register current_method = locations->InAt(0).AsRegister<Register>();
   __ movl(out, Address(current_method, ArtMethod::DeclaringClassOffset().Int32Value()));
   __ movl(out, Address(out, mirror::Class::DexCacheStringsOffset().Int32Value()));
-  __ MaybeUnpoisonHeapReference(out);
   __ movl(out, Address(out, CodeGenerator::GetCacheOffset(load->GetStringIndex())));
-  __ MaybeUnpoisonHeapReference(out);
+  // TODO: We will need a read barrier here.
   __ testl(out, out);
   __ j(kEqual, slow_path->GetEntryLabel());
   __ Bind(slow_path->GetExitLabel());
@@ -5085,6 +5184,245 @@
   // Will be generated at use site.
 }
 
+void LocationsBuilderX86::VisitX86ComputeBaseMethodAddress(
+    HX86ComputeBaseMethodAddress* insn) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(insn, LocationSummary::kNoCall);
+  locations->SetOut(Location::RequiresRegister());
+}
+
+void InstructionCodeGeneratorX86::VisitX86ComputeBaseMethodAddress(
+    HX86ComputeBaseMethodAddress* insn) {
+  LocationSummary* locations = insn->GetLocations();
+  Register reg = locations->Out().AsRegister<Register>();
+
+  // Generate call to next instruction.
+  Label next_instruction;
+  __ call(&next_instruction);
+  __ Bind(&next_instruction);
+
+  // Remember this offset for later use with constant area.
+  codegen_->SetMethodAddressOffset(GetAssembler()->CodeSize());
+
+  // Grab the return address off the stack.
+  __ popl(reg);
+}
+
+void LocationsBuilderX86::VisitX86LoadFromConstantTable(
+    HX86LoadFromConstantTable* insn) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(insn, LocationSummary::kNoCall);
+
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetInAt(1, Location::ConstantLocation(insn->GetConstant()));
+
+  // If we don't need to be materialized, we only need the inputs to be set.
+  if (!insn->NeedsMaterialization()) {
+    return;
+  }
+
+  switch (insn->GetType()) {
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+      locations->SetOut(Location::RequiresFpuRegister());
+      break;
+
+    case Primitive::kPrimInt:
+      locations->SetOut(Location::RequiresRegister());
+      break;
+
+    default:
+      LOG(FATAL) << "Unsupported x86 constant area type " << insn->GetType();
+  }
+}
+
+void InstructionCodeGeneratorX86::VisitX86LoadFromConstantTable(HX86LoadFromConstantTable* insn) {
+  if (!insn->NeedsMaterialization()) {
+    return;
+  }
+
+  LocationSummary* locations = insn->GetLocations();
+  Location out = locations->Out();
+  Register const_area = locations->InAt(0).AsRegister<Register>();
+  HConstant *value = insn->GetConstant();
+
+  switch (insn->GetType()) {
+    case Primitive::kPrimFloat:
+      __ movss(out.AsFpuRegister<XmmRegister>(),
+               codegen_->LiteralFloatAddress(value->AsFloatConstant()->GetValue(), const_area));
+      break;
+
+    case Primitive::kPrimDouble:
+      __ movsd(out.AsFpuRegister<XmmRegister>(),
+               codegen_->LiteralDoubleAddress(value->AsDoubleConstant()->GetValue(), const_area));
+      break;
+
+    case Primitive::kPrimInt:
+      __ movl(out.AsRegister<Register>(),
+              codegen_->LiteralInt32Address(value->AsIntConstant()->GetValue(), const_area));
+      break;
+
+    default:
+      LOG(FATAL) << "Unsupported x86 constant area type " << insn->GetType();
+  }
+}
+
+void CodeGeneratorX86::Finalize(CodeAllocator* allocator) {
+  // Generate the constant area if needed.
+  X86Assembler* assembler = GetAssembler();
+  if (!assembler->IsConstantAreaEmpty()) {
+    // Align to 4 byte boundary to reduce cache misses, as the data is 4 and 8
+    // byte values.
+    assembler->Align(4, 0);
+    constant_area_start_ = assembler->CodeSize();
+    assembler->AddConstantArea();
+  }
+
+  // And finish up.
+  CodeGenerator::Finalize(allocator);
+}
+
+/**
+ * Class to handle late fixup of offsets into constant area.
+ */
+class RIPFixup : public AssemblerFixup, public ArenaObject<kArenaAllocMisc> {
+ public:
+  RIPFixup(const CodeGeneratorX86& codegen, int offset)
+      : codegen_(codegen), offset_into_constant_area_(offset) {}
+
+ private:
+  void Process(const MemoryRegion& region, int pos) OVERRIDE {
+    // Patch the correct offset for the instruction.  The place to patch is the
+    // last 4 bytes of the instruction.
+    // The value to patch is the distance from the offset in the constant area
+    // from the address computed by the HX86ComputeBaseMethodAddress instruction.
+    int32_t constant_offset = codegen_.ConstantAreaStart() + offset_into_constant_area_;
+    int32_t relative_position = constant_offset - codegen_.GetMethodAddressOffset();;
+
+    // Patch in the right value.
+    region.StoreUnaligned<int32_t>(pos - 4, relative_position);
+  }
+
+  const CodeGeneratorX86& codegen_;
+
+  // Location in constant area that the fixup refers to.
+  int offset_into_constant_area_;
+};
+
+Address CodeGeneratorX86::LiteralDoubleAddress(double v, Register reg) {
+  AssemblerFixup* fixup = new (GetGraph()->GetArena()) RIPFixup(*this, __ AddDouble(v));
+  return Address(reg, kDummy32BitOffset, fixup);
+}
+
+Address CodeGeneratorX86::LiteralFloatAddress(float v, Register reg) {
+  AssemblerFixup* fixup = new (GetGraph()->GetArena()) RIPFixup(*this, __ AddFloat(v));
+  return Address(reg, kDummy32BitOffset, fixup);
+}
+
+Address CodeGeneratorX86::LiteralInt32Address(int32_t v, Register reg) {
+  AssemblerFixup* fixup = new (GetGraph()->GetArena()) RIPFixup(*this, __ AddInt32(v));
+  return Address(reg, kDummy32BitOffset, fixup);
+}
+
+Address CodeGeneratorX86::LiteralInt64Address(int64_t v, Register reg) {
+  AssemblerFixup* fixup = new (GetGraph()->GetArena()) RIPFixup(*this, __ AddInt64(v));
+  return Address(reg, kDummy32BitOffset, fixup);
+}
+
+/**
+ * Finds instructions that need the constant area base as an input.
+ */
+class ConstantHandlerVisitor : public HGraphVisitor {
+ public:
+  explicit ConstantHandlerVisitor(HGraph* graph) : HGraphVisitor(graph), base_(nullptr) {}
+
+ private:
+  void VisitAdd(HAdd* add) OVERRIDE {
+    BinaryFP(add);
+  }
+
+  void VisitSub(HSub* sub) OVERRIDE {
+    BinaryFP(sub);
+  }
+
+  void VisitMul(HMul* mul) OVERRIDE {
+    BinaryFP(mul);
+  }
+
+  void VisitDiv(HDiv* div) OVERRIDE {
+    BinaryFP(div);
+  }
+
+  void VisitReturn(HReturn* ret) OVERRIDE {
+    HConstant* value = ret->InputAt(0)->AsConstant();
+    if ((value != nullptr && Primitive::IsFloatingPointType(value->GetType()))) {
+      ReplaceInput(ret, value, 0, true);
+    }
+  }
+
+  void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) OVERRIDE {
+    HandleInvoke(invoke);
+  }
+
+  void VisitInvokeVirtual(HInvokeVirtual* invoke) OVERRIDE {
+    HandleInvoke(invoke);
+  }
+
+  void VisitInvokeInterface(HInvokeInterface* invoke) OVERRIDE {
+    HandleInvoke(invoke);
+  }
+
+  void BinaryFP(HBinaryOperation* bin) {
+    HConstant* rhs = bin->InputAt(1)->AsConstant();
+    if (rhs != nullptr && Primitive::IsFloatingPointType(bin->GetResultType())) {
+      ReplaceInput(bin, rhs, 1, false);
+    }
+  }
+
+  void InitializeConstantAreaPointer(HInstruction* user) {
+    // Ensure we only initialize the pointer once.
+    if (base_ != nullptr) {
+      return;
+    }
+
+    HGraph* graph = GetGraph();
+    HBasicBlock* entry = graph->GetEntryBlock();
+    base_ = new (graph->GetArena()) HX86ComputeBaseMethodAddress();
+    HInstruction* insert_pos = (user->GetBlock() == entry) ? user : entry->GetLastInstruction();
+    entry->InsertInstructionBefore(base_, insert_pos);
+    DCHECK(base_ != nullptr);
+  }
+
+  void ReplaceInput(HInstruction* insn, HConstant* value, int input_index, bool materialize) {
+    InitializeConstantAreaPointer(insn);
+    HGraph* graph = GetGraph();
+    HBasicBlock* block = insn->GetBlock();
+    HX86LoadFromConstantTable* load_constant =
+        new (graph->GetArena()) HX86LoadFromConstantTable(base_, value, materialize);
+    block->InsertInstructionBefore(load_constant, insn);
+    insn->ReplaceInput(load_constant, input_index);
+  }
+
+  void HandleInvoke(HInvoke* invoke) {
+    // Ensure that we can load FP arguments from the constant area.
+    for (size_t i = 0, e = invoke->InputCount(); i < e; i++) {
+      HConstant* input = invoke->InputAt(i)->AsConstant();
+      if (input != nullptr && Primitive::IsFloatingPointType(input->GetType())) {
+        ReplaceInput(invoke, input, i, true);
+      }
+    }
+  }
+
+  // The generated HX86ComputeBaseMethodAddress in the entry block needed as an
+  // input to the HX86LoadFromConstantTable instructions.
+  HX86ComputeBaseMethodAddress* base_;
+};
+
+void ConstantAreaFixups::Run() {
+  ConstantHandlerVisitor visitor(graph_);
+  visitor.VisitInsertionOrder();
+}
+
 #undef __
 
 }  // namespace x86
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index 17787a8..bd7cb12 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -294,6 +294,8 @@
 
   // Generate a call to a static or direct method.
   void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp);
+  // Generate a call to a virtual method.
+  void GenerateVirtualCall(HInvokeVirtual* invoke, Location temp);
 
   // Emit linker patches.
   void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE;
@@ -325,6 +327,25 @@
     return isa_features_;
   }
 
+  void SetMethodAddressOffset(int32_t offset) {
+    method_address_offset_ = offset;
+  }
+
+  int32_t GetMethodAddressOffset() const {
+    return method_address_offset_;
+  }
+
+  int32_t ConstantAreaStart() const {
+    return constant_area_start_;
+  }
+
+  Address LiteralDoubleAddress(double v, Register reg);
+  Address LiteralFloatAddress(float v, Register reg);
+  Address LiteralInt32Address(int32_t v, Register reg);
+  Address LiteralInt64Address(int64_t v, Register reg);
+
+  void Finalize(CodeAllocator* allocator) OVERRIDE;
+
  private:
   // Labels for each block that will be compiled.
   GrowableArray<Label> block_labels_;
@@ -339,6 +360,20 @@
   ArenaDeque<MethodPatchInfo<Label>> method_patches_;
   ArenaDeque<MethodPatchInfo<Label>> relative_call_patches_;
 
+  // Offset to the start of the constant area in the assembled code.
+  // Used for fixups to the constant area.
+  int32_t constant_area_start_;
+
+  // If there is a HX86ComputeBaseMethodAddress instruction in the graph
+  // (which shall be the sole instruction of this kind), subtracting this offset
+  // from the value contained in the out register of this HX86ComputeBaseMethodAddress
+  // instruction gives the address of the start of this method.
+  int32_t method_address_offset_;
+
+  // When we don't know the proper offset for the value, we use kDummy32BitOffset.
+  // The correct value will be inserted when processing Assembler fixups.
+  static constexpr int32_t kDummy32BitOffset = 256;
+
   DISALLOW_COPY_AND_ASSIGN(CodeGeneratorX86);
 };
 
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index ff52f4f..0f3eb74 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -439,8 +439,9 @@
         __ movq(reg, Address(CpuRegister(RSP), kCurrentMethodStackOffset));
       }
       // temp = temp->dex_cache_resolved_methods_;
-      __ movl(reg, Address(CpuRegister(method_reg),
-                           ArtMethod::DexCacheResolvedMethodsOffset().SizeValue()));
+      __ movq(reg,
+              Address(CpuRegister(method_reg),
+                      ArtMethod::DexCacheResolvedMethodsOffset(kX86_64PointerSize).SizeValue()));
       // temp = temp[index_in_cache]
       uint32_t index_in_cache = invoke->GetTargetMethod().dex_method_index;
       __ movq(reg, Address(reg, CodeGenerator::GetCachePointerOffset(index_in_cache)));
@@ -474,6 +475,25 @@
   DCHECK(!IsLeafMethod());
 }
 
+void CodeGeneratorX86_64::GenerateVirtualCall(HInvokeVirtual* invoke, Location temp_in) {
+  CpuRegister temp = temp_in.AsRegister<CpuRegister>();
+  size_t method_offset = mirror::Class::EmbeddedVTableEntryOffset(
+      invoke->GetVTableIndex(), kX86_64PointerSize).SizeValue();
+  LocationSummary* locations = invoke->GetLocations();
+  Location receiver = locations->InAt(0);
+  size_t class_offset = mirror::Object::ClassOffset().SizeValue();
+  // temp = object->GetClass();
+  DCHECK(receiver.IsRegister());
+  __ movl(temp, Address(receiver.AsRegister<CpuRegister>(), class_offset));
+  MaybeRecordImplicitNullCheck(invoke);
+  __ MaybeUnpoisonHeapReference(temp);
+  // temp = temp->GetMethodAt(method_offset);
+  __ movq(temp, Address(temp, method_offset));
+  // call temp->GetEntryPoint();
+  __ call(Address(temp, ArtMethod::EntryPointFromQuickCompiledCodeOffset(
+      kX86_64WordSize).SizeValue()));
+}
+
 void CodeGeneratorX86_64::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) {
   DCHECK(linker_patches->empty());
   size_t size =
@@ -1708,22 +1728,7 @@
     return;
   }
 
-  CpuRegister temp = invoke->GetLocations()->GetTemp(0).AsRegister<CpuRegister>();
-  size_t method_offset = mirror::Class::EmbeddedVTableEntryOffset(
-      invoke->GetVTableIndex(), kX86_64PointerSize).SizeValue();
-  LocationSummary* locations = invoke->GetLocations();
-  Location receiver = locations->InAt(0);
-  size_t class_offset = mirror::Object::ClassOffset().SizeValue();
-  // temp = object->GetClass();
-  DCHECK(receiver.IsRegister());
-  __ movl(temp, Address(receiver.AsRegister<CpuRegister>(), class_offset));
-  codegen_->MaybeRecordImplicitNullCheck(invoke);
-  __ MaybeUnpoisonHeapReference(temp);
-  // temp = temp->GetMethodAt(method_offset);
-  __ movq(temp, Address(temp, method_offset));
-  // call temp->GetEntryPoint();
-  __ call(Address(temp, ArtMethod::EntryPointFromQuickCompiledCodeOffset(
-      kX86_64WordSize).SizeValue()));
+  codegen_->GenerateVirtualCall(invoke, invoke->GetLocations()->GetTemp(0));
 
   DCHECK(!codegen_->IsLeafMethod());
   codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
@@ -4539,10 +4544,10 @@
     __ movl(out, Address(current_method, ArtMethod::DeclaringClassOffset().Int32Value()));
   } else {
     DCHECK(cls->CanCallRuntime());
-    __ movl(out, Address(
-        current_method, ArtMethod::DexCacheResolvedTypesOffset().Int32Value()));
+    __ movq(out, Address(
+        current_method, ArtMethod::DexCacheResolvedTypesOffset(kX86_64PointerSize).Int32Value()));
     __ movl(out, Address(out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex())));
-    __ MaybeUnpoisonHeapReference(out);
+    // TODO: We will need a read barrier here.
 
     SlowPathCodeX86_64* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathX86_64(
         cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck());
@@ -4590,10 +4595,9 @@
   CpuRegister out = locations->Out().AsRegister<CpuRegister>();
   CpuRegister current_method = locations->InAt(0).AsRegister<CpuRegister>();
   __ movl(out, Address(current_method, ArtMethod::DeclaringClassOffset().Int32Value()));
-  __ movl(out, Address(out, mirror::Class::DexCacheStringsOffset().Int32Value()));
-  __ MaybeUnpoisonHeapReference(out);
+  __ movq(out, Address(out, mirror::Class::DexCacheStringsOffset().Int32Value()));
   __ movl(out, Address(out, CodeGenerator::GetCacheOffset(load->GetStringIndex())));
-  __ MaybeUnpoisonHeapReference(out);
+  // TODO: We will need a read barrier here.
   __ testl(out, out);
   __ j(kEqual, slow_path->GetEntryLabel());
   __ Bind(slow_path->GetExitLabel());
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index 21357be..f9d8e04 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -305,6 +305,7 @@
   }
 
   void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp);
+  void GenerateVirtualCall(HInvokeVirtual* invoke, Location temp);
 
   void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE;
 
diff --git a/compiler/optimizing/constant_area_fixups_x86.h b/compiler/optimizing/constant_area_fixups_x86.h
new file mode 100644
index 0000000..4138039
--- /dev/null
+++ b/compiler/optimizing/constant_area_fixups_x86.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2015 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_COMPILER_OPTIMIZING_CONSTANT_AREA_FIXUPS_X86_H_
+#define ART_COMPILER_OPTIMIZING_CONSTANT_AREA_FIXUPS_X86_H_
+
+#include "nodes.h"
+#include "optimization.h"
+
+namespace art {
+namespace x86 {
+
+class ConstantAreaFixups : public HOptimization {
+ public:
+  ConstantAreaFixups(HGraph* graph, OptimizingCompilerStats* stats)
+      : HOptimization(graph, "constant_area_fixups_x86", stats) {}
+
+  void Run() OVERRIDE;
+};
+
+}  // namespace x86
+}  // namespace art
+
+#endif  // ART_COMPILER_OPTIMIZING_CONSTANT_AREA_FIXUPS_X86_H_
diff --git a/compiler/optimizing/induction_var_analysis.cc b/compiler/optimizing/induction_var_analysis.cc
index 8aaec68..3f5a6e7 100644
--- a/compiler/optimizing/induction_var_analysis.cc
+++ b/compiler/optimizing/induction_var_analysis.cc
@@ -51,14 +51,15 @@
       global_depth_(0),
       stack_(graph->GetArena()->Adapter()),
       scc_(graph->GetArena()->Adapter()),
-      map_(std::less<int>(), graph->GetArena()->Adapter()),
-      cycle_(std::less<int>(), graph->GetArena()->Adapter()),
-      induction_(std::less<int>(), graph->GetArena()->Adapter()) {
+      map_(std::less<HInstruction*>(), graph->GetArena()->Adapter()),
+      cycle_(std::less<HInstruction*>(), graph->GetArena()->Adapter()),
+      induction_(std::less<HLoopInformation*>(), graph->GetArena()->Adapter()) {
 }
 
 void HInductionVarAnalysis::Run() {
-  // Detects sequence variables (generalized induction variables) during an
-  // inner-loop-first traversal of all loops using Gerlek's algorithm.
+  // Detects sequence variables (generalized induction variables) during an inner-loop-first
+  // traversal of all loops using Gerlek's algorithm. The order is only relevant if outer
+  // loops would use induction information of inner loops (not currently done).
   for (HPostOrderIterator it_graph(*graph_); !it_graph.Done(); it_graph.Advance()) {
     HBasicBlock* graph_block = it_graph.Current();
     if (graph_block->IsLoopHeader()) {
@@ -71,38 +72,40 @@
   // Find strongly connected components (SSCs) in the SSA graph of this loop using Tarjan's
   // algorithm. Due to the descendant-first nature, classification happens "on-demand".
   global_depth_ = 0;
-  CHECK(stack_.empty());
+  DCHECK(stack_.empty());
   map_.clear();
 
   for (HBlocksInLoopIterator it_loop(*loop); !it_loop.Done(); it_loop.Advance()) {
     HBasicBlock* loop_block = it_loop.Current();
-    CHECK(loop_block->IsInLoop());
+    DCHECK(loop_block->IsInLoop());
     if (loop_block->GetLoopInformation() != loop) {
       continue;  // Inner loops already visited.
     }
     // Visit phi-operations and instructions.
     for (HInstructionIterator it(loop_block->GetPhis()); !it.Done(); it.Advance()) {
       HInstruction* instruction = it.Current();
-      if (!IsVisitedNode(instruction->GetId())) {
+      if (!IsVisitedNode(instruction)) {
         VisitNode(loop, instruction);
       }
     }
     for (HInstructionIterator it(loop_block->GetInstructions()); !it.Done(); it.Advance()) {
       HInstruction* instruction = it.Current();
-      if (!IsVisitedNode(instruction->GetId())) {
+      if (!IsVisitedNode(instruction)) {
         VisitNode(loop, instruction);
       }
     }
   }
 
-  CHECK(stack_.empty());
+  DCHECK(stack_.empty());
   map_.clear();
+
+  // Determine the loop's trip count.
+  VisitControl(loop);
 }
 
 void HInductionVarAnalysis::VisitNode(HLoopInformation* loop, HInstruction* instruction) {
-  const int id = instruction->GetId();
   const uint32_t d1 = ++global_depth_;
-  map_.Put(id, NodeInfo(d1));
+  map_.Put(instruction, NodeInfo(d1));
   stack_.push_back(instruction);
 
   // Visit all descendants.
@@ -113,7 +116,7 @@
 
   // Lower or found SCC?
   if (low < d1) {
-    map_.find(id)->second.depth = low;
+    map_.find(instruction)->second.depth = low;
   } else {
     scc_.clear();
     cycle_.clear();
@@ -123,7 +126,7 @@
       HInstruction* x = stack_.back();
       scc_.push_back(x);
       stack_.pop_back();
-      map_.find(x->GetId())->second.done = true;
+      map_.find(x)->second.done = true;
       if (x == instruction) {
         break;
       }
@@ -150,12 +153,11 @@
   }
 
   // Inspect descendant node.
-  const int id = instruction->GetId();
-  if (!IsVisitedNode(id)) {
+  if (!IsVisitedNode(instruction)) {
     VisitNode(loop, instruction);
-    return map_.find(id)->second.depth;
+    return map_.find(instruction)->second.depth;
   } else {
-    auto it = map_.find(id);
+    auto it = map_.find(instruction);
     return it->second.done ? global_depth_ : it->second.depth;
   }
 }
@@ -176,8 +178,20 @@
   } else if (instruction->IsMul()) {
     info = TransferMul(LookupInfo(loop, instruction->InputAt(0)),
                        LookupInfo(loop, instruction->InputAt(1)));
+  } else if (instruction->IsShl()) {
+    info = TransferShl(LookupInfo(loop, instruction->InputAt(0)),
+                       LookupInfo(loop, instruction->InputAt(1)),
+                       instruction->InputAt(0)->GetType());
   } else if (instruction->IsNeg()) {
     info = TransferNeg(LookupInfo(loop, instruction->InputAt(0)));
+  } 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?
@@ -188,7 +202,7 @@
 
 void HInductionVarAnalysis::ClassifyNonTrivial(HLoopInformation* loop) {
   const size_t size = scc_.size();
-  CHECK_GE(size, 1u);
+  DCHECK_GE(size, 1u);
   HInstruction* phi = scc_[size - 1];
   if (!IsEntryPhi(loop, phi)) {
     return;
@@ -204,41 +218,74 @@
   if (size == 1) {
     InductionInfo* update = LookupInfo(loop, internal);
     if (update != nullptr) {
-      AssignInfo(loop, phi, NewInductionInfo(kWrapAround, kNop, initial, update, nullptr));
+      AssignInfo(loop, phi, CreateInduction(kWrapAround, initial, update));
     }
     return;
   }
 
   // Inspect remainder of the cycle that resides in scc_. The cycle_ mapping assigns
-  // temporary meaning to its nodes.
-  cycle_.Overwrite(phi->GetId(), nullptr);
+  // temporary meaning to its nodes, seeded from the phi instruction and back.
   for (size_t i = 0; i < size - 1; i++) {
-    HInstruction* operation = scc_[i];
+    HInstruction* instruction = scc_[i];
     InductionInfo* update = nullptr;
-    if (operation->IsPhi()) {
-      update = TransferCycleOverPhi(operation);
-    } else if (operation->IsAdd()) {
-      update = TransferCycleOverAddSub(loop, operation->InputAt(0), operation->InputAt(1), kAdd, true);
-    } else if (operation->IsSub()) {
-      update = TransferCycleOverAddSub(loop, operation->InputAt(0), operation->InputAt(1), kSub, true);
+    if (instruction->IsPhi()) {
+      update = SolvePhi(loop, phi, instruction);
+    } else if (instruction->IsAdd()) {
+      update = SolveAddSub(
+          loop, phi, instruction, instruction->InputAt(0), instruction->InputAt(1), kAdd, true);
+    } else if (instruction->IsSub()) {
+      update = SolveAddSub(
+          loop, phi, instruction, instruction->InputAt(0), instruction->InputAt(1), kSub, true);
     }
     if (update == nullptr) {
       return;
     }
-    cycle_.Overwrite(operation->GetId(), update);
+    cycle_.Put(instruction, update);
   }
 
-  // Success if the internal link received accumulated nonzero update.
-  auto it = cycle_.find(internal->GetId());
-  if (it != cycle_.end() && it->second != nullptr) {
-    // Classify header phi and feed the cycle "on-demand".
-    AssignInfo(loop, phi, NewInductionInfo(kLinear, kNop, it->second, initial, nullptr));
-    for (size_t i = 0; i < size - 1; i++) {
-      ClassifyTrivial(loop, scc_[i]);
+  // Success if the internal link received a meaning.
+  auto it = cycle_.find(internal);
+  if (it != cycle_.end()) {
+    InductionInfo* induction = it->second;
+    switch (induction->induction_class) {
+      case kInvariant:
+        // Classify phi (last element in scc_) and then the rest of the cycle "on-demand".
+        // Statements are scanned in the Tarjan SCC order, with phi first.
+        AssignInfo(loop, phi, CreateInduction(kLinear, induction, initial));
+        for (size_t i = 0; i < size - 1; i++) {
+          ClassifyTrivial(loop, scc_[i]);
+        }
+        break;
+      case kPeriodic:
+        // Classify all elements in the cycle with the found periodic induction while rotating
+        // each first element to the end. Lastly, phi (last element in scc_) is classified.
+        // Statements are scanned in the reverse Tarjan SCC order, with phi last.
+        for (size_t i = 2; i <= size; i++) {
+          AssignInfo(loop, scc_[size - i], induction);
+          induction = RotatePeriodicInduction(induction->op_b, induction->op_a);
+        }
+        AssignInfo(loop, phi, induction);
+        break;
+      default:
+        break;
     }
   }
 }
 
+HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::RotatePeriodicInduction(
+    InductionInfo* induction,
+    InductionInfo* last) {
+  // Rotates a periodic induction of the form
+  //   (a, b, c, d, e)
+  // into
+  //   (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->op_a, RotatePeriodicInduction(induction->op_b, last));
+}
+
 HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferPhi(InductionInfo* a,
                                                                          InductionInfo* b) {
   // Transfer over a phi: if both inputs are identical, result is input.
@@ -251,36 +298,33 @@
 HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferAddSub(InductionInfo* a,
                                                                             InductionInfo* b,
                                                                             InductionOp op) {
-  // Transfer over an addition or subtraction: invariant or linear
-  // inputs combine into new invariant or linear result.
+  // Transfer over an addition or subtraction: any invariant, linear, wrap-around, or periodic
+  // can be combined with an invariant to yield a similar result. Even two linear inputs can
+  // be combined. All other combinations fail, however.
   if (a != nullptr && b != nullptr) {
     if (a->induction_class == kInvariant && b->induction_class == kInvariant) {
-      return NewInductionInfo(kInvariant, op, a, b, nullptr);
-    } else if (a->induction_class == kLinear && b->induction_class == kInvariant) {
-      return NewInductionInfo(
-          kLinear,
-          kNop,
-          a->op_a,
-          NewInductionInfo(kInvariant, op, a->op_b, b, nullptr),
-          nullptr);
-    } else if (a->induction_class == kInvariant && b->induction_class == kLinear) {
-      InductionInfo* ba = b->op_a;
-      if (op == kSub) {  // negation required
-        ba = NewInductionInfo(kInvariant, kNeg, nullptr, ba, nullptr);
-      }
-      return NewInductionInfo(
-          kLinear,
-          kNop,
-          ba,
-          NewInductionInfo(kInvariant, op, a, b->op_b, nullptr),
-          nullptr);
+      return CreateInvariantOp(op, a, b);
     } else if (a->induction_class == kLinear && b->induction_class == kLinear) {
-      return NewInductionInfo(
-          kLinear,
-          kNop,
-          NewInductionInfo(kInvariant, op, a->op_a, b->op_a, nullptr),
-          NewInductionInfo(kInvariant, op, a->op_b, b->op_b, nullptr),
-          nullptr);
+      return CreateInduction(
+          kLinear, TransferAddSub(a->op_a, b->op_a, op), TransferAddSub(a->op_b, b->op_b, op));
+    } else if (a->induction_class == kInvariant) {
+      InductionInfo* new_a = b->op_a;
+      InductionInfo* new_b = TransferAddSub(a, b->op_b, op);
+      if (b->induction_class != kLinear) {
+        DCHECK(b->induction_class == kWrapAround || b->induction_class == kPeriodic);
+        new_a = TransferAddSub(a, new_a, op);
+      } else if (op == kSub) {  // Negation required.
+        new_a = TransferNeg(new_a);
+      }
+      return CreateInduction(b->induction_class, new_a, new_b);
+    } else if (b->induction_class == kInvariant) {
+      InductionInfo* new_a = a->op_a;
+      InductionInfo* new_b = TransferAddSub(a->op_b, b, op);
+      if (a->induction_class != kLinear) {
+        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 nullptr;
@@ -288,141 +332,335 @@
 
 HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferMul(InductionInfo* a,
                                                                          InductionInfo* b) {
-  // Transfer over a multiplication: invariant or linear
-  // inputs combine into new invariant or linear result.
-  // Two linear inputs would become quadratic.
+  // Transfer over a multiplication: any invariant, linear, wrap-around, or periodic
+  // can be multiplied with an invariant to yield a similar but multiplied result.
+  // Two non-invariant inputs cannot be multiplied, however.
   if (a != nullptr && b != nullptr) {
     if (a->induction_class == kInvariant && b->induction_class == kInvariant) {
-      return NewInductionInfo(kInvariant, kMul, a, b, nullptr);
-    } else if (a->induction_class == kLinear && b->induction_class == kInvariant) {
-      return NewInductionInfo(
-          kLinear,
-          kNop,
-          NewInductionInfo(kInvariant, kMul, a->op_a, b, nullptr),
-          NewInductionInfo(kInvariant, kMul, a->op_b, b, nullptr),
-          nullptr);
-    } else if (a->induction_class == kInvariant && b->induction_class == kLinear) {
-      return NewInductionInfo(
-          kLinear,
-          kNop,
-          NewInductionInfo(kInvariant, kMul, a, b->op_a, nullptr),
-          NewInductionInfo(kInvariant, kMul, a, b->op_b, nullptr),
-          nullptr);
+      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));
+    } else if (b->induction_class == kInvariant) {
+      return CreateInduction(a->induction_class, TransferMul(a->op_a, b), TransferMul(a->op_b, b));
+    }
+  }
+  return nullptr;
+}
+
+HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferShl(InductionInfo* a,
+                                                                         InductionInfo* b,
+                                                                         Primitive::Type type) {
+  // Transfer over a shift left: treat shift by restricted constant as equivalent multiplication.
+  int64_t value = -1;
+  if (a != nullptr && IsIntAndGet(b, &value)) {
+    // Obtain the constant needed for the multiplication. This yields an existing instruction
+    // if the constants is already there. Otherwise, this has a side effect on the HIR.
+    // The restriction on the shift factor avoids generating a negative constant
+    // (viz. 1 << 31 and 1L << 63 set the sign bit). The code assumes that generalization
+    // for shift factors outside [0,32) and [0,64) ranges is done by earlier simplification.
+    if ((type == Primitive::kPrimInt  && 0 <= value && value < 31) ||
+        (type == Primitive::kPrimLong && 0 <= value && value < 63)) {
+      return TransferMul(a, CreateConstant(1 << value, type));
     }
   }
   return nullptr;
 }
 
 HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferNeg(InductionInfo* a) {
-  // Transfer over a unary negation: invariant or linear input
-  // yields a similar, but negated result.
+  // Transfer over a unary negation: an invariant, linear, wrap-around, or periodic input
+  // yields a similar but negated induction as result.
   if (a != nullptr) {
     if (a->induction_class == kInvariant) {
-      return NewInductionInfo(kInvariant, kNeg, nullptr, a, nullptr);
-    } else if (a->induction_class == kLinear) {
-      return NewInductionInfo(
-          kLinear,
-          kNop,
-          NewInductionInfo(kInvariant, kNeg, nullptr, a->op_a, nullptr),
-          NewInductionInfo(kInvariant, kNeg, nullptr, a->op_b, nullptr),
-          nullptr);
+      return CreateInvariantOp(kNeg, nullptr, a);
     }
+    return CreateInduction(a->induction_class, TransferNeg(a->op_a), TransferNeg(a->op_b));
   }
   return nullptr;
 }
 
-HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferCycleOverPhi(HInstruction* phi) {
-  // Transfer within a cycle over a phi: only identical inputs
-  // can be combined into that input as result.
-  const size_t count = phi->InputCount();
-  CHECK_GT(count, 0u);
-  auto ita = cycle_.find(phi->InputAt(0)->GetId());
+HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolvePhi(HLoopInformation* loop,
+                                                                      HInstruction* phi,
+                                                                      HInstruction* instruction) {
+  // Solve within a cycle over a phi: identical inputs are combined into that input as result.
+  const size_t count = instruction->InputCount();
+  DCHECK_GT(count, 0u);
+  auto ita = cycle_.find(instruction->InputAt(0));
   if (ita != cycle_.end()) {
     InductionInfo* a = ita->second;
     for (size_t i = 1; i < count; i++) {
-      auto itb = cycle_.find(phi->InputAt(i)->GetId());
-      if (itb == cycle_.end() ||!HInductionVarAnalysis::InductionEqual(a, itb->second)) {
+      auto itb = cycle_.find(instruction->InputAt(i));
+      if (itb == cycle_.end() || !HInductionVarAnalysis::InductionEqual(a, itb->second)) {
         return nullptr;
       }
     }
     return a;
   }
-  return nullptr;
-}
 
-HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferCycleOverAddSub(
-    HLoopInformation* loop,
-    HInstruction* x,
-    HInstruction* y,
-    InductionOp op,
-    bool first) {
-  // Transfer within a cycle over an addition or subtraction: adding or
-  // subtracting an invariant value adds to the stride of the induction,
-  // starting with the phi value denoted by the unusual nullptr value.
-  auto it = cycle_.find(x->GetId());
-  if (it != cycle_.end()) {
-    InductionInfo* a = it->second;
-    InductionInfo* b = LookupInfo(loop, y);
-    if (b != nullptr && b->induction_class == kInvariant) {
-      if (a == nullptr) {
-        if (op == kSub) {  // negation required
-          return NewInductionInfo(kInvariant, kNeg, nullptr, b, nullptr);
+  // Solve within a cycle over another entry-phi: add invariants into a periodic.
+  if (IsEntryPhi(loop, instruction)) {
+    InductionInfo* a = LookupInfo(loop, instruction->InputAt(0));
+    if (a != nullptr && a->induction_class == kInvariant) {
+      if (instruction->InputAt(1) == phi) {
+        InductionInfo* initial = LookupInfo(loop, phi->InputAt(0));
+        return CreateInduction(kPeriodic, a, initial);
+      }
+      auto it = cycle_.find(instruction->InputAt(1));
+      if (it != cycle_.end()) {
+        InductionInfo* b = it->second;
+        if (b->induction_class == kPeriodic) {
+          return CreateInduction(kPeriodic, a, b);
         }
-        return b;
-      } else if (a->induction_class == kInvariant) {
-        return NewInductionInfo(kInvariant, op, a, b, nullptr);
       }
     }
   }
-  // On failure, try alternatives.
+
+  return nullptr;
+}
+
+HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolveAddSub(HLoopInformation* loop,
+                                                                         HInstruction* phi,
+                                                                         HInstruction* instruction,
+                                                                         HInstruction* x,
+                                                                         HInstruction* y,
+                                                                         InductionOp op,
+                                                                         bool is_first_call) {
+  // Solve within a cycle over an addition or subtraction: adding or subtracting an
+  // invariant value, seeded from phi, keeps adding to the stride of the induction.
+  InductionInfo* b = LookupInfo(loop, y);
+  if (b != nullptr && b->induction_class == kInvariant) {
+    if (x == phi) {
+      return (op == kAdd) ? b : CreateInvariantOp(kNeg, nullptr, b);
+    }
+    auto it = cycle_.find(x);
+    if (it != cycle_.end()) {
+      InductionInfo* a = it->second;
+      if (a->induction_class == kInvariant) {
+        return CreateInvariantOp(op, a, b);
+      }
+    }
+  }
+
+  // Try some alternatives before failing.
   if (op == kAdd) {
-    // Try the other way around for an addition.
-    if (first) {
-      return TransferCycleOverAddSub(loop, y, x, op, false);
+    // Try the other way around for an addition if considered for first time.
+    if (is_first_call) {
+      return SolveAddSub(loop, phi, instruction, y, x, op, false);
+    }
+  } else if (op == kSub) {
+    // Solve within a tight cycle for a periodic idiom k = c - k;
+    if (y == phi && instruction == phi->InputAt(1)) {
+      InductionInfo* a = LookupInfo(loop, x);
+      if (a != nullptr && a->induction_class == kInvariant) {
+        InductionInfo* initial = LookupInfo(loop, phi->InputAt(0));
+        return CreateInduction(kPeriodic, CreateInvariantOp(kSub, a, initial), initial);
+      }
     }
   }
+
   return nullptr;
 }
 
-void HInductionVarAnalysis::PutInfo(int loop_id, int id, InductionInfo* info) {
-  auto it = induction_.find(loop_id);
-  if (it == induction_.end()) {
-    it = induction_.Put(
-        loop_id, ArenaSafeMap<int, InductionInfo*>(std::less<int>(), graph_->GetArena()->Adapter()));
-  }
-  it->second.Overwrite(id, info);
-}
-
-HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::GetInfo(int loop_id, int id) {
-  auto it = induction_.find(loop_id);
-  if (it != induction_.end()) {
-    auto loop_it = it->second.find(id);
-    if (loop_it != it->second.end()) {
-      return loop_it->second;
+void HInductionVarAnalysis::VisitControl(HLoopInformation* loop) {
+  HInstruction* control = loop->GetHeader()->GetLastInstruction();
+  if (control->IsIf()) {
+    HIf* ifs = control->AsIf();
+    HBasicBlock* if_true = ifs->IfTrueSuccessor();
+    HBasicBlock* if_false = ifs->IfFalseSuccessor();
+    HInstruction* if_expr = ifs->InputAt(0);
+    // Determine if loop has following structure in header.
+    // loop-header: ....
+    //              if (condition) goto X
+    if (if_expr->IsCondition()) {
+      HCondition* condition = if_expr->AsCondition();
+      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.
+      } 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) {
+        VisitCondition(loop, a, b, type, condition->GetCondition());
+      }
     }
   }
-  return nullptr;
+}
+
+void HInductionVarAnalysis::VisitCondition(HLoopInformation* loop,
+                                           InductionInfo* a,
+                                           InductionInfo* b,
+                                           Primitive::Type type,
+                                           IfCondition cmp) {
+  if (a->induction_class == kInvariant && b->induction_class == kLinear) {
+    // Swap conditions (e.g. U > i is same as i < U).
+    switch (cmp) {
+      case kCondLT: VisitCondition(loop, b, a, type, kCondGT); break;
+      case kCondLE: VisitCondition(loop, b, a, type, kCondGE); break;
+      case kCondGT: VisitCondition(loop, b, a, type, kCondLT); break;
+      case kCondGE: VisitCondition(loop, b, a, type, kCondLE); break;
+      default: break;
+    }
+  } else if (a->induction_class == kLinear && b->induction_class == kInvariant) {
+    // Normalize a linear loop control with a constant, nonzero stride:
+    //   stride > 0, either i < U or i <= U
+    //   stride < 0, either i > U or i >= U
+    InductionInfo* stride = a->op_a;
+    InductionInfo* lo_val = a->op_b;
+    InductionInfo* hi_val = b;
+    int64_t value = -1;
+    if (IsIntAndGet(stride, &value)) {
+      if ((value > 0 && (cmp == kCondLT || cmp == kCondLE)) ||
+          (value < 0 && (cmp == kCondGT || cmp == kCondGE))) {
+        bool is_strict = cmp == kCondLT || cmp == kCondGT;
+        VisitTripCount(loop, lo_val, hi_val, stride, value, type, is_strict);
+      }
+    }
+  }
+}
+
+void HInductionVarAnalysis::VisitTripCount(HLoopInformation* loop,
+                                           InductionInfo* lo_val,
+                                           InductionInfo* hi_val,
+                                           InductionInfo* stride,
+                                           int32_t stride_value,
+                                           Primitive::Type type,
+                                           bool is_strict) {
+  // Any loop of the general form:
+  //
+  //    for (i = L; i <= U; i += S) // S > 0
+  // or for (i = L; i >= U; i += S) // S < 0
+  //      .. i ..
+  //
+  // can be normalized into:
+  //
+  //    for (n = 0; n < TC; n++) // where TC = (U + S - L) / S
+  //      .. L + S * n ..
+  //
+  // NOTE: The TC (trip-count) expression is only valid if the top-test path is taken at
+  //       least once. Otherwise TC is 0. Also, the expression assumes the loop does not
+  //       have any early-exits. Otherwise, TC is an upper bound.
+  //
+  bool cancels = is_strict && abs(stride_value) == 1;  // compensation cancels conversion?
+  if (!cancels) {
+    // Convert exclusive integral inequality into inclusive integral inequality,
+    // viz. condition i < U is i <= U - 1 and condition i > U is i >= U + 1.
+    if (is_strict) {
+      const InductionOp op = stride_value > 0 ? kSub : kAdd;
+      hi_val = CreateInvariantOp(op, hi_val, CreateConstant(1, type));
+    }
+    // Compensate for stride.
+    hi_val = CreateInvariantOp(kAdd, hi_val, stride);
+  }
+
+  // Assign the trip-count expression to the loop control. Clients that use the information
+  // should be aware that due to the L <= U assumption, the expression is only valid in the
+  // loop-body proper, and not yet in the loop-header. If the loop has any early exits, the
+  // trip-count forms a conservative upper bound on the number of loop iterations.
+  InductionInfo* trip_count =
+      CreateInvariantOp(kDiv, CreateInvariantOp(kSub, hi_val, lo_val), stride);
+  AssignInfo(loop, loop->GetHeader()->GetLastInstruction(), trip_count);
 }
 
 void HInductionVarAnalysis::AssignInfo(HLoopInformation* loop,
                                        HInstruction* instruction,
                                        InductionInfo* info) {
-  const int loopId = loop->GetHeader()->GetBlockId();
-  const int id = instruction->GetId();
-  PutInfo(loopId, id, info);
+  auto it = induction_.find(loop);
+  if (it == induction_.end()) {
+    it = induction_.Put(loop,
+                        ArenaSafeMap<HInstruction*, InductionInfo*>(
+                            std::less<HInstruction*>(), graph_->GetArena()->Adapter()));
+  }
+  it->second.Put(instruction, info);
 }
 
-HInductionVarAnalysis::InductionInfo*
-HInductionVarAnalysis::LookupInfo(HLoopInformation* loop,
-                                  HInstruction* instruction) {
-  const int loop_id = loop->GetHeader()->GetBlockId();
-  const int id = instruction->GetId();
-  InductionInfo* info = GetInfo(loop_id, id);
-  if (info == nullptr && IsLoopInvariant(loop, instruction)) {
-    info = NewInductionInfo(kInvariant, kFetch, nullptr, nullptr, instruction);
-    PutInfo(loop_id, id, info);
+HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::LookupInfo(HLoopInformation* loop,
+                                                                        HInstruction* instruction) {
+  auto it = induction_.find(loop);
+  if (it != induction_.end()) {
+    auto loop_it = it->second.find(instruction);
+    if (loop_it != it->second.end()) {
+      return loop_it->second;
+    }
   }
-  return info;
+  if (IsLoopInvariant(loop, instruction)) {
+    InductionInfo* info = CreateInvariantFetch(instruction);
+    AssignInfo(loop, instruction, info);
+    return info;
+  }
+  return nullptr;
+}
+
+HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::CreateConstant(int64_t value,
+                                                                            Primitive::Type type) {
+  if (type == Primitive::kPrimInt) {
+    return CreateInvariantFetch(graph_->GetIntConstant(value));
+  }
+  DCHECK_EQ(type, Primitive::kPrimLong);
+  return CreateInvariantFetch(graph_->GetLongConstant(value));
+}
+
+HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::CreateSimplifiedInvariant(
+    InductionOp op,
+    InductionInfo* a,
+    InductionInfo* b) {
+  // Perform some light-weight simplifications during construction of a new invariant.
+  // This often safes memory and yields a more concise representation of the induction.
+  // More exhaustive simplifications are done by later phases once induction nodes are
+  // translated back into HIR code (e.g. by loop optimizations or BCE).
+  int64_t value = -1;
+  if (IsIntAndGet(a, &value)) {
+    if (value == 0) {
+      // Simplify 0 + b = b, 0 * b = 0.
+      if (op == kAdd) {
+        return b;
+      } else if (op == kMul) {
+        return a;
+      }
+    } else if (op == kMul) {
+      // Simplify 1 * b = b, -1 * b = -b
+      if (value == 1) {
+        return b;
+      } else if (value == -1) {
+        op = kNeg;
+        a = nullptr;
+      }
+    }
+  }
+  if (IsIntAndGet(b, &value)) {
+    if (value == 0) {
+      // Simplify a + 0 = a, a - 0 = a, a * 0 = 0, -0 = 0.
+      if (op == kAdd || op == kSub) {
+        return a;
+      } else if (op == kMul || op == kNeg) {
+        return b;
+      }
+    } else if (op == kMul || op == kDiv) {
+      // Simplify a * 1 = a, a / 1 = a, a * -1 = -a, a / -1 = -a
+      if (value == 1) {
+        return a;
+      } else if (value == -1) {
+        op = kNeg;
+        b = a;
+        a = nullptr;
+      }
+    }
+  } else if (b->operation == kNeg) {
+    // Simplify a + (-b) = a - b, a - (-b) = a + b, -(-b) = b.
+    if (op == kAdd) {
+      op = kSub;
+      b = b->op_b;
+    } else if (op == kSub) {
+      op = kAdd;
+      b = b->op_b;
+    } else if (op == kNeg) {
+      return b->op_b;
+    }
+  }
+  return new (graph_->GetArena()) InductionInfo(kInvariant, op, a, b, nullptr);
 }
 
 bool HInductionVarAnalysis::InductionEqual(InductionInfo* info1,
@@ -440,27 +678,46 @@
   return info1 == info2;
 }
 
+bool HInductionVarAnalysis::IsIntAndGet(InductionInfo* info, int64_t* value) {
+  if (info != nullptr && info->induction_class == kInvariant && info->operation == kFetch) {
+    DCHECK(info->fetch);
+    if (info->fetch->IsIntConstant()) {
+      *value = info->fetch->AsIntConstant()->GetValue();
+      return true;
+    } else if (info->fetch->IsLongConstant()) {
+      *value = info->fetch->AsLongConstant()->GetValue();
+      return true;
+    }
+  }
+  return false;
+}
+
 std::string HInductionVarAnalysis::InductionToString(InductionInfo* info) {
   if (info != nullptr) {
     if (info->induction_class == kInvariant) {
+      int64_t value = -1;
       std::string inv = "(";
       inv += InductionToString(info->op_a);
       switch (info->operation) {
-        case kNop: inv += " ? "; break;
-        case kAdd: inv += " + "; break;
+        case kNop:   inv += " @ "; break;
+        case kAdd:   inv += " + "; break;
         case kSub:
-        case kNeg: inv += " - "; break;
-        case kMul: inv += " * "; break;
-        case kDiv: inv += " / "; break;
+        case kNeg:   inv += " - "; break;
+        case kMul:   inv += " * "; break;
+        case kDiv:   inv += " / "; break;
         case kFetch:
-          CHECK(info->fetch != nullptr);
-          inv += std::to_string(info->fetch->GetId()) + ":" + info->fetch->DebugName();
+          DCHECK(info->fetch);
+          if (IsIntAndGet(info, &value)) {
+            inv += std::to_string(value);
+          } else {
+            inv += std::to_string(info->fetch->GetId()) + ":" + info->fetch->DebugName();
+          }
           break;
       }
       inv += InductionToString(info->op_b);
       return inv + ")";
     } else {
-      CHECK(info->operation == kNop);
+      DCHECK(info->operation == kNop);
       if (info->induction_class == kLinear) {
         return "(" + InductionToString(info->op_a) + " * i + " +
                      InductionToString(info->op_b) + ")";
diff --git a/compiler/optimizing/induction_var_analysis.h b/compiler/optimizing/induction_var_analysis.h
index 09a0a38..8eccf92 100644
--- a/compiler/optimizing/induction_var_analysis.h
+++ b/compiler/optimizing/induction_var_analysis.h
@@ -25,9 +25,11 @@
 namespace art {
 
 /**
- * Induction variable analysis.
+ * Induction variable analysis. This class does not have a direct public API.
+ * Instead, the results of induction variable analysis can be queried through
+ * friend classes, such as InductionVarRange.
  *
- * Based on the paper by M. Gerlek et al.
+ * The analysis implementation is based on the paper by M. Gerlek et al.
  * "Beyond Induction Variables: Detecting and Classifying Sequences Using a Demand-Driven SSA Form"
  * (ACM Transactions on Programming Languages and Systems, Volume 17 Issue 1, Jan. 1995).
  */
@@ -35,16 +37,6 @@
  public:
   explicit HInductionVarAnalysis(HGraph* graph);
 
-  // TODO: design public API useful in later phases
-
-  /**
-   * Returns string representation of induction found for the instruction
-   * in the given loop (for testing and debugging only).
-   */
-  std::string InductionToString(HLoopInformation* loop, HInstruction* instruction) {
-    return InductionToString(LookupInfo(loop, instruction));
-  }
-
   void Run() OVERRIDE;
 
  private:
@@ -57,12 +49,10 @@
   };
 
   enum InductionClass {
-    kNone,
     kInvariant,
     kLinear,
     kWrapAround,
-    kPeriodic,
-    kMonotonic
+    kPeriodic
   };
 
   enum InductionOp {
@@ -79,7 +69,7 @@
    * Defines a detected induction as:
    *   (1) invariant:
    *         operation: a + b, a - b, -b, a * b, a / b
-   *       or
+   *       or:
    *         fetch: fetch from HIR
    *   (2) linear:
    *         nop: a * i + b
@@ -87,8 +77,6 @@
    *         nop: a, then defined by b
    *   (4) periodic
    *         nop: a, then defined by b (repeated when exhausted)
-   *   (5) monotonic
-   *         // TODO: determine representation
    */
   struct InductionInfo : public ArenaObject<kArenaAllocMisc> {
     InductionInfo(InductionClass ic,
@@ -108,17 +96,23 @@
     HInstruction* fetch;
   };
 
-  inline bool IsVisitedNode(int id) const {
-    return map_.find(id) != map_.end();
+  bool IsVisitedNode(HInstruction* instruction) const {
+    return map_.find(instruction) != map_.end();
   }
 
-  inline InductionInfo* NewInductionInfo(
-      InductionClass c,
-      InductionOp op,
-      InductionInfo* a,
-      InductionInfo* b,
-      HInstruction* i) {
-    return new (graph_->GetArena()) InductionInfo(c, op, a, b, i);
+  InductionInfo* CreateInvariantOp(InductionOp op, InductionInfo* a, InductionInfo* b) {
+    DCHECK(((op != kNeg && a != nullptr) || (op == kNeg && a == nullptr)) && b != nullptr);
+    return CreateSimplifiedInvariant(op, a, b);
+  }
+
+  InductionInfo* CreateInvariantFetch(HInstruction* f) {
+    DCHECK(f != nullptr);
+    return new (graph_->GetArena()) InductionInfo(kInvariant, kFetch, nullptr, nullptr, f);
+  }
+
+  InductionInfo* CreateInduction(InductionClass ic, InductionInfo* a, InductionInfo* b) {
+    DCHECK(a != nullptr && b != nullptr);
+    return new (graph_->GetArena()) InductionInfo(ic, kNop, a, b, nullptr);
   }
 
   // Methods for analysis.
@@ -132,36 +126,66 @@
   InductionInfo* TransferPhi(InductionInfo* a, InductionInfo* b);
   InductionInfo* TransferAddSub(InductionInfo* a, InductionInfo* b, InductionOp op);
   InductionInfo* TransferMul(InductionInfo* a, InductionInfo* b);
+  InductionInfo* TransferShl(InductionInfo* a, InductionInfo* b, Primitive::Type type);
   InductionInfo* TransferNeg(InductionInfo* a);
-  InductionInfo* TransferCycleOverPhi(HInstruction* phi);
-  InductionInfo* TransferCycleOverAddSub(HLoopInformation* loop,
-                                         HInstruction* x,
-                                         HInstruction* y,
-                                         InductionOp op,
-                                         bool first);
+
+  // Solvers.
+  InductionInfo* SolvePhi(HLoopInformation* loop,
+                          HInstruction* phi,
+                          HInstruction* instruction);
+  InductionInfo* SolveAddSub(HLoopInformation* loop,
+                             HInstruction* phi,
+                             HInstruction* instruction,
+                             HInstruction* x,
+                             HInstruction* y,
+                             InductionOp op,
+                             bool is_first_call);
+  InductionInfo* RotatePeriodicInduction(InductionInfo* induction, InductionInfo* last);
+
+  // Trip count information.
+  void VisitControl(HLoopInformation* loop);
+  void VisitCondition(HLoopInformation* loop,
+                      InductionInfo* a,
+                      InductionInfo* b,
+                      Primitive::Type type,
+                      IfCondition cmp);
+  void VisitTripCount(HLoopInformation* loop,
+                      InductionInfo* lo_val,
+                      InductionInfo* hi_val,
+                      InductionInfo* stride,
+                      int32_t stride_value,
+                      Primitive::Type type,
+                      bool is_strict);
 
   // Assign and lookup.
-  void PutInfo(int loop_id, int id, InductionInfo* info);
-  InductionInfo* GetInfo(int loop_id, int id);
   void AssignInfo(HLoopInformation* loop, HInstruction* instruction, InductionInfo* info);
   InductionInfo* LookupInfo(HLoopInformation* loop, HInstruction* instruction);
-  bool InductionEqual(InductionInfo* info1, InductionInfo* info2);
-  std::string InductionToString(InductionInfo* info);
+  InductionInfo* CreateConstant(int64_t value, Primitive::Type type);
+  InductionInfo* CreateSimplifiedInvariant(InductionOp op, InductionInfo* a, InductionInfo* b);
 
-  // Bookkeeping during and after analysis.
-  // TODO: fine tune data structures, only keep relevant data
+  // Helpers.
+  static bool InductionEqual(InductionInfo* info1, InductionInfo* info2);
+  static bool IsIntAndGet(InductionInfo* info, int64_t* value);
+  static std::string InductionToString(InductionInfo* info);
 
+  // TODO: fine tune the following data structures, only keep relevant data.
+
+  // Temporary book-keeping during the analysis.
   uint32_t global_depth_;
-
   ArenaVector<HInstruction*> stack_;
   ArenaVector<HInstruction*> scc_;
+  ArenaSafeMap<HInstruction*, NodeInfo> map_;
+  ArenaSafeMap<HInstruction*, InductionInfo*> cycle_;
 
-  // Mappings of instruction id to node and induction information.
-  ArenaSafeMap<int, NodeInfo> map_;
-  ArenaSafeMap<int, InductionInfo*> cycle_;
+  /**
+   * Maintains the results of the analysis as a mapping from loops to a mapping from instructions
+   * to the induction information for that instruction in that loop.
+   */
+  ArenaSafeMap<HLoopInformation*, ArenaSafeMap<HInstruction*, InductionInfo*>> induction_;
 
-  // Mapping from loop id to mapping of instruction id to induction information.
-  ArenaSafeMap<int, ArenaSafeMap<int, InductionInfo*>> induction_;
+  friend class InductionVarAnalysisTest;
+  friend class InductionVarRange;
+  friend class InductionVarRangeTest;
 
   DISALLOW_COPY_AND_ASSIGN(HInductionVarAnalysis);
 };
diff --git a/compiler/optimizing/induction_var_analysis_test.cc b/compiler/optimizing/induction_var_analysis_test.cc
index 2093e33..fca1ca5 100644
--- a/compiler/optimizing/induction_var_analysis_test.cc
+++ b/compiler/optimizing/induction_var_analysis_test.cc
@@ -63,7 +63,7 @@
   // populate the loop with instructions to set up interesting scenarios.
   void BuildLoopNest(int n) {
     ASSERT_LE(n, 10);
-    graph_->SetNumberOfVRegs(n + 2);
+    graph_->SetNumberOfVRegs(n + 3);
 
     // Build basic blocks with entry, nested loop, exit.
     entry_ = new (&allocator_) HBasicBlock(graph_);
@@ -77,47 +77,36 @@
     graph_->SetExitBlock(exit_);
 
     // Provide entry and exit instructions.
-    // 0 : parameter
-    // 1 : constant 0
-    // 2 : constant 1
-    // 3 : constant 100
-    parameter_ = new (&allocator_)
-        HParameterValue(0, Primitive::kPrimNot, true);
+    parameter_ = new (&allocator_) HParameterValue(0, Primitive::kPrimNot, true);
     entry_->AddInstruction(parameter_);
-    constant0_ = new (&allocator_) HConstant(Primitive::kPrimInt);
-    entry_->AddInstruction(constant0_);
-    constant1_ = new (&allocator_) HConstant(Primitive::kPrimInt);
-    entry_->AddInstruction(constant1_);
-    constant100_ = new (&allocator_) HConstant(Primitive::kPrimInt);
-    entry_->AddInstruction(constant100_);
-    exit_->AddInstruction(new (&allocator_) HExit());
+    constant0_ = graph_->GetIntConstant(0);
+    constant1_ = graph_->GetIntConstant(1);
+    constant100_ = graph_->GetIntConstant(100);
     induc_ = new (&allocator_) HLocal(n);
     entry_->AddInstruction(induc_);
     entry_->AddInstruction(new (&allocator_) HStoreLocal(induc_, constant0_));
     tmp_ = new (&allocator_) HLocal(n + 1);
     entry_->AddInstruction(tmp_);
     entry_->AddInstruction(new (&allocator_) HStoreLocal(tmp_, constant100_));
+    dum_ = new (&allocator_) HLocal(n + 2);
+    entry_->AddInstruction(dum_);
+    exit_->AddInstruction(new (&allocator_) HExit());
 
     // Provide loop instructions.
     for (int d = 0; d < n; d++) {
       basic_[d] = new (&allocator_) HLocal(d);
       entry_->AddInstruction(basic_[d]);
-      loop_preheader_[d]->AddInstruction(
-           new (&allocator_) HStoreLocal(basic_[d], constant0_));
-      HInstruction* load = new (&allocator_)
-          HLoadLocal(basic_[d], Primitive::kPrimInt);
+      loop_preheader_[d]->AddInstruction(new (&allocator_) HStoreLocal(basic_[d], constant0_));
+      HInstruction* load = new (&allocator_) HLoadLocal(basic_[d], Primitive::kPrimInt);
       loop_header_[d]->AddInstruction(load);
-      HInstruction* compare = new (&allocator_)
-          HGreaterThanOrEqual(load, constant100_);
+      HInstruction* compare = new (&allocator_) HLessThan(load, constant100_);
       loop_header_[d]->AddInstruction(compare);
       loop_header_[d]->AddInstruction(new (&allocator_) HIf(compare));
       load = new (&allocator_) HLoadLocal(basic_[d], Primitive::kPrimInt);
       loop_body_[d]->AddInstruction(load);
-      increment_[d] = new (&allocator_)
-          HAdd(Primitive::kPrimInt, load, constant1_);
+      increment_[d] = new (&allocator_) HAdd(Primitive::kPrimInt, load, constant1_);
       loop_body_[d]->AddInstruction(increment_[d]);
-      loop_body_[d]->AddInstruction(
-               new (&allocator_) HStoreLocal(basic_[d], increment_[d]));
+      loop_body_[d]->AddInstruction(new (&allocator_) HStoreLocal(basic_[d], increment_[d]));
       loop_body_[d]->AddInstruction(new (&allocator_) HGoto());
     }
   }
@@ -149,8 +138,7 @@
 
   // Inserts local load at depth d.
   HInstruction* InsertLocalLoad(HLocal* local, int d) {
-    return InsertInstruction(
-        new (&allocator_) HLoadLocal(local, Primitive::kPrimInt), d);
+    return InsertInstruction(new (&allocator_) HLoadLocal(local, Primitive::kPrimInt), d);
   }
 
   // Inserts local store at depth d.
@@ -167,9 +155,10 @@
         parameter_, load, constant0_, Primitive::kPrimInt, 0), d);
   }
 
-  // Returns loop information of loop at depth d.
-  HLoopInformation* GetLoopInfo(int d) {
-    return loop_body_[d]->GetLoopInformation();
+  // Returns induction information of instruction in loop at depth d.
+  std::string GetInductionInfo(HInstruction* instruction, int d) {
+    return HInductionVarAnalysis::InductionToString(
+        iva_->LookupInfo(loop_body_[d]->GetLoopInformation(), instruction));
   }
 
   // Performs InductionVarAnalysis (after proper set up).
@@ -194,6 +183,7 @@
   HInstruction* constant100_;
   HLocal* induc_;  // "vreg_n", the "k"
   HLocal* tmp_;    // "vreg_n+1"
+  HLocal* dum_;    // "vreg_n+2"
 
   // Loop specifics.
   HBasicBlock* loop_preheader_[10];
@@ -230,222 +220,159 @@
   ASSERT_EQ(exit_->GetLoopInformation(), nullptr);
 }
 
-TEST_F(InductionVarAnalysisTest, FindBasicInductionVar) {
+TEST_F(InductionVarAnalysisTest, FindBasicInduction) {
   // Setup:
   // for (int i = 0; i < 100; i++) {
-  //    a[i] = 0;
+  //   a[i] = 0;
   // }
   BuildLoopNest(1);
   HInstruction* store = InsertArrayStore(basic_[0], 0);
   PerformInductionVarAnalysis();
 
-  EXPECT_STREQ(
-      "((2:Constant) * i + (1:Constant))",
-      iva_->InductionToString(GetLoopInfo(0), store->InputAt(1)).c_str());
-  EXPECT_STREQ(
-      "((2:Constant) * i + ((1:Constant) + (2:Constant)))",
-      iva_->InductionToString(GetLoopInfo(0), increment_[0]).c_str());
+  EXPECT_STREQ("((1) * i + (0))", GetInductionInfo(store->InputAt(1), 0).c_str());
+  EXPECT_STREQ("((1) * i + (1))", GetInductionInfo(increment_[0], 0).c_str());
+
+  // Trip-count.
+  EXPECT_STREQ("(100)", GetInductionInfo(loop_header_[0]->GetLastInstruction(), 0).c_str());
 }
 
-TEST_F(InductionVarAnalysisTest, FindDerivedInductionVarAdd) {
+TEST_F(InductionVarAnalysisTest, FindDerivedInduction) {
   // Setup:
   // for (int i = 0; i < 100; i++) {
-  //    k = 100 + i;
-  //    a[k] = 0;
+  //   k = 100 + i;
+  //   k = 100 - i;
+  //   k = 100 * i;
+  //   k = i << 1;
+  //   k = - i;
   // }
   BuildLoopNest(1);
   HInstruction *add = InsertInstruction(
-      new (&allocator_) HAdd(
-          Primitive::kPrimInt, constant100_, InsertLocalLoad(basic_[0], 0)), 0);
+      new (&allocator_) HAdd(Primitive::kPrimInt, constant100_, InsertLocalLoad(basic_[0], 0)), 0);
   InsertLocalStore(induc_, add, 0);
-  HInstruction* store = InsertArrayStore(induc_, 0);
-  PerformInductionVarAnalysis();
-
-  EXPECT_STREQ(
-      "((2:Constant) * i + ((3:Constant) + (1:Constant)))",
-      iva_->InductionToString(GetLoopInfo(0), store->InputAt(1)).c_str());
-}
-
-TEST_F(InductionVarAnalysisTest, FindDerivedInductionVarSub) {
-  // Setup:
-  // for (int i = 0; i < 100; i++) {
-  //    k = 100 - i;
-  //    a[k] = 0;
-  // }
-  BuildLoopNest(1);
   HInstruction *sub = InsertInstruction(
-      new (&allocator_) HSub(
-          Primitive::kPrimInt, constant100_, InsertLocalLoad(basic_[0], 0)), 0);
+      new (&allocator_) HSub(Primitive::kPrimInt, constant100_, InsertLocalLoad(basic_[0], 0)), 0);
   InsertLocalStore(induc_, sub, 0);
-  HInstruction* store = InsertArrayStore(induc_, 0);
-  PerformInductionVarAnalysis();
-
-  EXPECT_STREQ(
-      "(( - (2:Constant)) * i + ((3:Constant) - (1:Constant)))",
-      iva_->InductionToString(GetLoopInfo(0), store->InputAt(1)).c_str());
-}
-
-TEST_F(InductionVarAnalysisTest, FindDerivedInductionVarMul) {
-  // Setup:
-  // for (int i = 0; i < 100; i++) {
-  //    k = 100 * i;
-  //    a[k] = 0;
-  // }
-  BuildLoopNest(1);
   HInstruction *mul = InsertInstruction(
-      new (&allocator_) HMul(
-          Primitive::kPrimInt, constant100_, InsertLocalLoad(basic_[0], 0)), 0);
+      new (&allocator_) HMul(Primitive::kPrimInt, constant100_, InsertLocalLoad(basic_[0], 0)), 0);
   InsertLocalStore(induc_, mul, 0);
-  HInstruction* store = InsertArrayStore(induc_, 0);
-  PerformInductionVarAnalysis();
-
-  EXPECT_STREQ(
-      "(((3:Constant) * (2:Constant)) * i + ((3:Constant) * (1:Constant)))",
-      iva_->InductionToString(GetLoopInfo(0), store->InputAt(1)).c_str());
-}
-
-TEST_F(InductionVarAnalysisTest, FindDerivedInductionVarNeg) {
-  // Setup:
-  // for (int i = 0; i < 100; i++) {
-  //    k = - i;
-  //    a[k] = 0;
-  // }
-  BuildLoopNest(1);
+  HInstruction *shl = InsertInstruction(
+      new (&allocator_) HShl(Primitive::kPrimInt, InsertLocalLoad(basic_[0], 0), constant1_), 0);
+  InsertLocalStore(induc_, shl, 0);
   HInstruction *neg = InsertInstruction(
-      new (&allocator_) HNeg(
-          Primitive::kPrimInt, InsertLocalLoad(basic_[0], 0)), 0);
+      new (&allocator_) HNeg(Primitive::kPrimInt, InsertLocalLoad(basic_[0], 0)), 0);
   InsertLocalStore(induc_, neg, 0);
-  HInstruction* store = InsertArrayStore(induc_, 0);
   PerformInductionVarAnalysis();
 
-  EXPECT_STREQ(
-      "(( - (2:Constant)) * i + ( - (1:Constant)))",
-      iva_->InductionToString(GetLoopInfo(0), store->InputAt(1)).c_str());
+  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());
 }
 
 TEST_F(InductionVarAnalysisTest, FindChainInduction) {
   // Setup:
   // k = 0;
   // for (int i = 0; i < 100; i++) {
-  //    k = k + 100;
-  //    a[k] = 0;
-  //    k = k - 1;
-  //    a[k] = 0;
+  //   k = k + 100;
+  //   a[k] = 0;
+  //   k = k - 1;
+  //   a[k] = 0;
   // }
   BuildLoopNest(1);
   HInstruction *add = InsertInstruction(
-      new (&allocator_) HAdd(
-          Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant100_), 0);
+      new (&allocator_) HAdd(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant100_), 0);
   InsertLocalStore(induc_, add, 0);
   HInstruction* store1 = InsertArrayStore(induc_, 0);
   HInstruction *sub = InsertInstruction(
-      new (&allocator_) HSub(
-          Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant1_), 0);
+      new (&allocator_) HSub(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant1_), 0);
   InsertLocalStore(induc_, sub, 0);
   HInstruction* store2 = InsertArrayStore(induc_, 0);
   PerformInductionVarAnalysis();
 
-  EXPECT_STREQ(
-      "(((3:Constant) - (2:Constant)) * i + ((1:Constant) + (3:Constant)))",
-      iva_->InductionToString(GetLoopInfo(0), store1->InputAt(1)).c_str());
-  EXPECT_STREQ(
-      "(((3:Constant) - (2:Constant)) * i + "
-      "(((1:Constant) + (3:Constant)) - (2:Constant)))",
-      iva_->InductionToString(GetLoopInfo(0), store2->InputAt(1)).c_str());
+  EXPECT_STREQ("(((100) - (1)) * i + (100))",
+               GetInductionInfo(store1->InputAt(1), 0).c_str());
+  EXPECT_STREQ("(((100) - (1)) * i + ((100) - (1)))",
+               GetInductionInfo(store2->InputAt(1), 0).c_str());
 }
 
 TEST_F(InductionVarAnalysisTest, FindTwoWayBasicInduction) {
   // Setup:
   // k = 0;
   // for (int i = 0; i < 100; i++) {
-  //    if () k = k + 1;
-  //    else  k = k + 1;
-  //    a[k] = 0;
+  //   if () k = k + 1;
+  //   else  k = k + 1;
+  //   a[k] = 0;
   // }
   BuildLoopNest(1);
   HBasicBlock* ifTrue;
   HBasicBlock* ifFalse;
   BuildIf(0, &ifTrue, &ifFalse);
   // True-branch.
-  HInstruction* load1 = new (&allocator_)
-      HLoadLocal(induc_, Primitive::kPrimInt);
+  HInstruction* load1 = new (&allocator_) HLoadLocal(induc_, Primitive::kPrimInt);
   ifTrue->AddInstruction(load1);
-  HInstruction* inc1 = new (&allocator_)
-      HAdd(Primitive::kPrimInt, load1, constant1_);
+  HInstruction* inc1 = new (&allocator_) HAdd(Primitive::kPrimInt, load1, constant1_);
   ifTrue->AddInstruction(inc1);
   ifTrue->AddInstruction(new (&allocator_) HStoreLocal(induc_, inc1));
   // False-branch.
-  HInstruction* load2 = new (&allocator_)
-      HLoadLocal(induc_, Primitive::kPrimInt);
+  HInstruction* load2 = new (&allocator_) HLoadLocal(induc_, Primitive::kPrimInt);
   ifFalse->AddInstruction(load2);
-  HInstruction* inc2 = new (&allocator_)
-        HAdd(Primitive::kPrimInt, load2, constant1_);
+  HInstruction* inc2 = new (&allocator_) HAdd(Primitive::kPrimInt, load2, constant1_);
   ifFalse->AddInstruction(inc2);
   ifFalse->AddInstruction(new (&allocator_) HStoreLocal(induc_, inc2));
   // Merge over a phi.
   HInstruction* store = InsertArrayStore(induc_, 0);
   PerformInductionVarAnalysis();
 
-  EXPECT_STREQ(
-      "((2:Constant) * i + ((1:Constant) + (2:Constant)))",
-      iva_->InductionToString(GetLoopInfo(0), store->InputAt(1)).c_str());
+  EXPECT_STREQ("((1) * i + (1))", GetInductionInfo(store->InputAt(1), 0).c_str());
 }
 
 TEST_F(InductionVarAnalysisTest, FindTwoWayDerivedInduction) {
   // Setup:
   // for (int i = 0; i < 100; i++) {
-  //    if () k = i + 1;
-  //    else  k = i + 1;
-  //    a[k] = 0;
+  //   if () k = i + 1;
+  //   else  k = i + 1;
+  //   a[k] = 0;
   // }
   BuildLoopNest(1);
   HBasicBlock* ifTrue;
   HBasicBlock* ifFalse;
   BuildIf(0, &ifTrue, &ifFalse);
   // True-branch.
-  HInstruction* load1 = new (&allocator_)
-      HLoadLocal(basic_[0], Primitive::kPrimInt);
+  HInstruction* load1 = new (&allocator_) HLoadLocal(basic_[0], Primitive::kPrimInt);
   ifTrue->AddInstruction(load1);
-  HInstruction* inc1 = new (&allocator_)
-      HAdd(Primitive::kPrimInt, load1, constant1_);
+  HInstruction* inc1 = new (&allocator_) HAdd(Primitive::kPrimInt, load1, constant1_);
   ifTrue->AddInstruction(inc1);
   ifTrue->AddInstruction(new (&allocator_) HStoreLocal(induc_, inc1));
   // False-branch.
-  HInstruction* load2 = new (&allocator_)
-      HLoadLocal(basic_[0], Primitive::kPrimInt);
+  HInstruction* load2 = new (&allocator_) HLoadLocal(basic_[0], Primitive::kPrimInt);
   ifFalse->AddInstruction(load2);
-  HInstruction* inc2 = new (&allocator_)
-        HAdd(Primitive::kPrimInt, load2, constant1_);
+  HInstruction* inc2 = new (&allocator_) HAdd(Primitive::kPrimInt, load2, constant1_);
   ifFalse->AddInstruction(inc2);
   ifFalse->AddInstruction(new (&allocator_) HStoreLocal(induc_, inc2));
   // Merge over a phi.
   HInstruction* store = InsertArrayStore(induc_, 0);
   PerformInductionVarAnalysis();
 
-  EXPECT_STREQ(
-      "((2:Constant) * i + ((1:Constant) + (2:Constant)))",
-      iva_->InductionToString(GetLoopInfo(0), store->InputAt(1)).c_str());
+  EXPECT_STREQ("((1) * i + (1))", GetInductionInfo(store->InputAt(1), 0).c_str());
 }
 
 TEST_F(InductionVarAnalysisTest, FindFirstOrderWrapAroundInduction) {
   // Setup:
   // k = 0;
   // for (int i = 0; i < 100; i++) {
-  //    a[k] = 0;
-  //    k = 100 - i;
+  //   a[k] = 0;
+  //   k = 100 - i;
   // }
   BuildLoopNest(1);
   HInstruction* store = InsertArrayStore(induc_, 0);
   HInstruction *sub = InsertInstruction(
-      new (&allocator_) HSub(
-          Primitive::kPrimInt, constant100_, InsertLocalLoad(basic_[0], 0)), 0);
+      new (&allocator_) HSub(Primitive::kPrimInt, constant100_, InsertLocalLoad(basic_[0], 0)), 0);
   InsertLocalStore(induc_, sub, 0);
   PerformInductionVarAnalysis();
 
-  EXPECT_STREQ(
-      "wrap((1:Constant), "
-      "(( - (2:Constant)) * i + ((3:Constant) - (1:Constant))))",
-      iva_->InductionToString(GetLoopInfo(0), store->InputAt(1)).c_str());
+  EXPECT_STREQ("wrap((0), (( - (1)) * i + (100)))",
+               GetInductionInfo(store->InputAt(1), 0).c_str());
 }
 
 TEST_F(InductionVarAnalysisTest, FindSecondOrderWrapAroundInduction) {
@@ -453,23 +380,144 @@
   // k = 0;
   // t = 100;
   // for (int i = 0; i < 100; i++) {
-  //    a[k] = 0;
-  //    k = t;
-  //    t = 100 - i;
+  //   a[k] = 0;
+  //   k = t;
+  //   t = 100 - i;
   // }
   BuildLoopNest(1);
   HInstruction* store = InsertArrayStore(induc_, 0);
   InsertLocalStore(induc_, InsertLocalLoad(tmp_, 0), 0);
   HInstruction *sub = InsertInstruction(
-       new (&allocator_) HSub(
-           Primitive::kPrimInt, constant100_, InsertLocalLoad(basic_[0], 0)), 0);
+       new (&allocator_) HSub(Primitive::kPrimInt, constant100_, InsertLocalLoad(basic_[0], 0)), 0);
   InsertLocalStore(tmp_, sub, 0);
   PerformInductionVarAnalysis();
 
-  EXPECT_STREQ(
-      "wrap((1:Constant), wrap((3:Constant), "
-      "(( - (2:Constant)) * i + ((3:Constant) - (1:Constant)))))",
-      iva_->InductionToString(GetLoopInfo(0), store->InputAt(1)).c_str());
+  EXPECT_STREQ("wrap((0), wrap((100), (( - (1)) * i + (100))))",
+               GetInductionInfo(store->InputAt(1), 0).c_str());
+}
+
+TEST_F(InductionVarAnalysisTest, FindWrapAroundDerivedInduction) {
+  // Setup:
+  // k = 0;
+  // for (int i = 0; i < 100; i++) {
+  //   t = k + 100;
+  //   t = k - 100;
+  //   t = k * 100;
+  //   t = k << 1;
+  //   t = - k;
+  //   k = i << 1;
+  // }
+  BuildLoopNest(1);
+  HInstruction *add = InsertInstruction(
+      new (&allocator_) HAdd(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant100_), 0);
+  InsertLocalStore(tmp_, add, 0);
+  HInstruction *sub = InsertInstruction(
+       new (&allocator_) HSub(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant100_), 0);
+  InsertLocalStore(tmp_, sub, 0);
+  HInstruction *mul = InsertInstruction(
+       new (&allocator_) HMul(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant100_), 0);
+  InsertLocalStore(tmp_, mul, 0);
+  HInstruction *shl = InsertInstruction(
+       new (&allocator_) HShl(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant1_), 0);
+  InsertLocalStore(tmp_, shl, 0);
+  HInstruction *neg = InsertInstruction(
+       new (&allocator_) HNeg(Primitive::kPrimInt, InsertLocalLoad(induc_, 0)), 0);
+  InsertLocalStore(tmp_, neg, 0);
+  InsertLocalStore(
+      induc_,
+      InsertInstruction(
+          new (&allocator_)
+          HShl(Primitive::kPrimInt, InsertLocalLoad(basic_[0], 0), constant1_), 0), 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());
+}
+
+TEST_F(InductionVarAnalysisTest, FindPeriodicInduction) {
+  // Setup:
+  // k = 0;
+  // t = 100;
+  // for (int i = 0; i < 100; i++) {
+  //   a[k] = 0;
+  //   a[t] = 0;
+  //   // Swap t <-> k.
+  //   d = t;
+  //   t = k;
+  //   k = d;
+  // }
+  BuildLoopNest(1);
+  HInstruction* store1 = InsertArrayStore(induc_, 0);
+  HInstruction* store2 = InsertArrayStore(tmp_, 0);
+  InsertLocalStore(dum_, InsertLocalLoad(tmp_, 0), 0);
+  InsertLocalStore(tmp_, InsertLocalLoad(induc_, 0), 0);
+  InsertLocalStore(induc_, InsertLocalLoad(dum_, 0), 0);
+  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());
+}
+
+TEST_F(InductionVarAnalysisTest, FindIdiomaticPeriodicInduction) {
+  // Setup:
+  // k = 0;
+  // for (int i = 0; i < 100; i++) {
+  //   a[k] = 0;
+  //   k = 1 - k;
+  // }
+  BuildLoopNest(1);
+  HInstruction* store = InsertArrayStore(induc_, 0);
+  HInstruction *sub = InsertInstruction(
+         new (&allocator_) HSub(Primitive::kPrimInt, constant1_, InsertLocalLoad(induc_, 0)), 0);
+  InsertLocalStore(induc_, sub, 0);
+  PerformInductionVarAnalysis();
+
+  EXPECT_STREQ("periodic((0), (1))", GetInductionInfo(store->InputAt(1), 0).c_str());
+  EXPECT_STREQ("periodic((1), (0))", GetInductionInfo(sub, 0).c_str());
+}
+
+TEST_F(InductionVarAnalysisTest, FindDerivedPeriodicInduction) {
+  // Setup:
+  // k = 0;
+  // for (int i = 0; i < 100; i++) {
+  //   k = 1 - k;
+  //   t = k + 100;
+  //   t = k - 100;
+  //   t = k * 100;
+  //   t = k << 1;
+  //   t = - k;
+  // }
+  BuildLoopNest(1);
+  InsertLocalStore(
+      induc_,
+      InsertInstruction(new (&allocator_)
+                        HSub(Primitive::kPrimInt, constant1_, InsertLocalLoad(induc_, 0)), 0), 0);
+  // Derived expressions.
+  HInstruction *add = InsertInstruction(
+       new (&allocator_) HAdd(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant100_), 0);
+  InsertLocalStore(tmp_, add, 0);
+  HInstruction *sub = InsertInstruction(
+       new (&allocator_) HSub(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant100_), 0);
+  InsertLocalStore(tmp_, sub, 0);
+  HInstruction *mul = InsertInstruction(
+       new (&allocator_) HMul(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant100_), 0);
+  InsertLocalStore(tmp_, mul, 0);
+  HInstruction *shl = InsertInstruction(
+       new (&allocator_) HShl(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant1_), 0);
+  InsertLocalStore(tmp_, shl, 0);
+  HInstruction *neg = InsertInstruction(
+       new (&allocator_) HNeg(Primitive::kPrimInt, InsertLocalLoad(induc_, 0)), 0);
+  InsertLocalStore(tmp_, neg, 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());
 }
 
 TEST_F(InductionVarAnalysisTest, FindDeepLoopInduction) {
@@ -485,29 +533,24 @@
   // }
   BuildLoopNest(10);
   HInstruction *inc = InsertInstruction(
-      new (&allocator_) HAdd(
-          Primitive::kPrimInt, constant1_, InsertLocalLoad(induc_, 9)), 9);
+      new (&allocator_) HAdd(Primitive::kPrimInt, constant1_, InsertLocalLoad(induc_, 9)), 9);
   InsertLocalStore(induc_, inc, 9);
   HInstruction* store = InsertArrayStore(induc_, 9);
   PerformInductionVarAnalysis();
 
-  // Match exact number of constants, but be less strict on phi number,
-  // since that depends on the SSA building phase.
-  std::regex r("\\(\\(2:Constant\\) \\* i \\+ "
-               "\\(\\(2:Constant\\) \\+ \\(\\d+:Phi\\)\\)\\)");
+  // Avoid exact phi number, since that depends on the SSA building phase.
+  std::regex r("\\(\\(1\\) \\* i \\+ "
+               "\\(\\(1\\) \\+ \\(\\d+:Phi\\)\\)\\)");
 
   for (int d = 0; d < 10; d++) {
     if (d == 9) {
-      EXPECT_TRUE(std::regex_match(
-          iva_->InductionToString(GetLoopInfo(d), store->InputAt(1)), r));
+      EXPECT_TRUE(std::regex_match(GetInductionInfo(store->InputAt(1), d), r));
     } else {
-      EXPECT_STREQ(
-          "",
-          iva_->InductionToString(GetLoopInfo(d), store->InputAt(1)).c_str());
+      EXPECT_STREQ("", GetInductionInfo(store->InputAt(1), d).c_str());
     }
-    EXPECT_STREQ(
-        "((2:Constant) * i + ((1:Constant) + (2:Constant)))",
-        iva_->InductionToString(GetLoopInfo(d), increment_[d]).c_str());
+    EXPECT_STREQ("((1) * i + (1))", GetInductionInfo(increment_[d], d).c_str());
+    // Trip-count.
+    EXPECT_STREQ("(100)", GetInductionInfo(loop_header_[d]->GetLastInstruction(), d).c_str());
   }
 }
 
diff --git a/compiler/optimizing/induction_var_range.cc b/compiler/optimizing/induction_var_range.cc
new file mode 100644
index 0000000..bd90334
--- /dev/null
+++ b/compiler/optimizing/induction_var_range.cc
@@ -0,0 +1,343 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#include <limits.h>
+
+#include "induction_var_range.h"
+
+namespace art {
+
+static bool IsValidConstant32(int32_t c) {
+  return INT_MIN < c && c < INT_MAX;
+}
+
+static bool IsValidConstant64(int64_t c) {
+  return INT_MIN < c && c < INT_MAX;
+}
+
+/** Returns true if 32-bit addition can be done safely (and is not an unknown range). */
+static bool IsSafeAdd(int32_t c1, int32_t c2) {
+  if (IsValidConstant32(c1) && IsValidConstant32(c2)) {
+    return IsValidConstant64(static_cast<int64_t>(c1) + static_cast<int64_t>(c2));
+  }
+  return false;
+}
+
+/** Returns true if 32-bit subtraction can be done safely (and is not an unknown range). */
+static bool IsSafeSub(int32_t c1, int32_t c2) {
+  if (IsValidConstant32(c1) && IsValidConstant32(c2)) {
+    return IsValidConstant64(static_cast<int64_t>(c1) - static_cast<int64_t>(c2));
+  }
+  return false;
+}
+
+/** Returns true if 32-bit multiplication can be done safely (and is not an unknown range). */
+static bool IsSafeMul(int32_t c1, int32_t c2) {
+  if (IsValidConstant32(c1) && IsValidConstant32(c2)) {
+    return IsValidConstant64(static_cast<int64_t>(c1) * static_cast<int64_t>(c2));
+  }
+  return false;
+}
+
+/** Returns true if 32-bit division can be done safely (and is not an unknown range). */
+static bool IsSafeDiv(int32_t c1, int32_t c2) {
+  if (IsValidConstant32(c1) && IsValidConstant32(c2) && c2 != 0) {
+    return IsValidConstant64(static_cast<int64_t>(c1) / static_cast<int64_t>(c2));
+  }
+  return false;
+}
+
+/** Returns true for 32/64-bit integral constant within known range. */
+static bool IsIntAndGet(HInstruction* instruction, int32_t* value) {
+  if (instruction->IsIntConstant()) {
+    const int32_t c = instruction->AsIntConstant()->GetValue();
+    if (IsValidConstant32(c)) {
+      *value = c;
+      return true;
+    }
+  } else if (instruction->IsLongConstant()) {
+    const int64_t c = instruction->AsLongConstant()->GetValue();
+    if (IsValidConstant64(c)) {
+      *value = c;
+      return true;
+    }
+  }
+  return false;
+}
+
+//
+// Public class methods.
+//
+
+InductionVarRange::InductionVarRange(HInductionVarAnalysis* induction_analysis)
+    : induction_analysis_(induction_analysis) {
+}
+
+InductionVarRange::Value InductionVarRange::GetMinInduction(HInstruction* context,
+                                                            HInstruction* instruction) {
+  HLoopInformation* loop = context->GetBlock()->GetLoopInformation();
+  if (loop != nullptr && induction_analysis_ != nullptr) {
+    return GetMin(induction_analysis_->LookupInfo(loop, instruction), GetTripCount(loop, context));
+  }
+  return Value(INT_MIN);
+}
+
+InductionVarRange::Value InductionVarRange::GetMaxInduction(HInstruction* context,
+                                                            HInstruction* instruction) {
+  HLoopInformation* loop = context->GetBlock()->GetLoopInformation();
+  if (loop != nullptr && induction_analysis_ != nullptr) {
+    return GetMax(induction_analysis_->LookupInfo(loop, instruction), GetTripCount(loop, context));
+  }
+  return Value(INT_MAX);
+}
+
+//
+// Private class methods.
+//
+
+HInductionVarAnalysis::InductionInfo* InductionVarRange::GetTripCount(HLoopInformation* loop,
+                                                                      HInstruction* context) {
+  // The trip-count expression is only valid when the top-test is taken at least once,
+  // that means, when the analyzed context appears outside the loop header itself.
+  // Early-exit loops are okay, since in those cases, the trip-count is conservative.
+  if (context->GetBlock() != loop->GetHeader()) {
+    HInductionVarAnalysis::InductionInfo* trip =
+        induction_analysis_->LookupInfo(loop, loop->GetHeader()->GetLastInstruction());
+    if (trip != nullptr) {
+      // Wrap the trip-count representation in its own unusual NOP node, so that range analysis
+      // is able to determine the [0, TC - 1] interval without having to construct constants.
+      return induction_analysis_->CreateInvariantOp(HInductionVarAnalysis::kNop, trip, trip);
+    }
+  }
+  return nullptr;
+}
+
+InductionVarRange::Value InductionVarRange::GetFetch(HInstruction* instruction,
+                                                     int32_t fail_value) {
+  // Detect constants and chase the fetch a bit deeper into the HIR tree, so that it becomes
+  // more likely range analysis will compare the same instructions as terminal nodes.
+  int32_t value;
+  if (IsIntAndGet(instruction, &value)) {
+    return Value(value);
+  } else if (instruction->IsAdd()) {
+    if (IsIntAndGet(instruction->InputAt(0), &value)) {
+      return AddValue(Value(value), GetFetch(instruction->InputAt(1), fail_value), fail_value);
+    } else if (IsIntAndGet(instruction->InputAt(1), &value)) {
+      return AddValue(GetFetch(instruction->InputAt(0), fail_value), Value(value), fail_value);
+    }
+  }
+  return Value(instruction, 1, 0);
+}
+
+InductionVarRange::Value InductionVarRange::GetMin(HInductionVarAnalysis::InductionInfo* info,
+                                                   HInductionVarAnalysis::InductionInfo* trip) {
+  if (info != nullptr) {
+    switch (info->induction_class) {
+      case HInductionVarAnalysis::kInvariant:
+        // Invariants.
+        switch (info->operation) {
+          case HInductionVarAnalysis::kNop:  // normalized: 0
+            DCHECK_EQ(info->op_a, info->op_b);
+            return Value(0);
+          case HInductionVarAnalysis::kAdd:
+            return AddValue(GetMin(info->op_a, trip), GetMin(info->op_b, trip), INT_MIN);
+          case HInductionVarAnalysis::kSub:  // second max!
+            return SubValue(GetMin(info->op_a, trip), GetMax(info->op_b, trip), INT_MIN);
+          case HInductionVarAnalysis::kNeg:  // second max!
+            return SubValue(Value(0), GetMax(info->op_b, trip), INT_MIN);
+          case HInductionVarAnalysis::kMul:
+            return GetMul(info->op_a, info->op_b, trip, INT_MIN);
+          case HInductionVarAnalysis::kDiv:
+            return GetDiv(info->op_a, info->op_b, trip, INT_MIN);
+          case HInductionVarAnalysis::kFetch:
+            return GetFetch(info->fetch, INT_MIN);
+        }
+        break;
+      case HInductionVarAnalysis::kLinear:
+        // Minimum over linear induction a * i + b, for normalized 0 <= i < TC.
+        return AddValue(GetMul(info->op_a, trip, trip, INT_MIN),
+                        GetMin(info->op_b, trip), INT_MIN);
+      case HInductionVarAnalysis::kWrapAround:
+      case HInductionVarAnalysis::kPeriodic:
+        // Minimum over all values in the wrap-around/periodic.
+        return MinValue(GetMin(info->op_a, trip), GetMin(info->op_b, trip));
+    }
+  }
+  return Value(INT_MIN);
+}
+
+InductionVarRange::Value InductionVarRange::GetMax(HInductionVarAnalysis::InductionInfo* info,
+                                                   HInductionVarAnalysis::InductionInfo* trip) {
+  if (info != nullptr) {
+    switch (info->induction_class) {
+      case HInductionVarAnalysis::kInvariant:
+        // Invariants.
+        switch (info->operation) {
+          case HInductionVarAnalysis::kNop:    // normalized: TC - 1
+            DCHECK_EQ(info->op_a, info->op_b);
+            return SubValue(GetMax(info->op_b, trip), Value(1), INT_MAX);
+          case HInductionVarAnalysis::kAdd:
+            return AddValue(GetMax(info->op_a, trip), GetMax(info->op_b, trip), INT_MAX);
+          case HInductionVarAnalysis::kSub:  // second min!
+            return SubValue(GetMax(info->op_a, trip), GetMin(info->op_b, trip), INT_MAX);
+          case HInductionVarAnalysis::kNeg:  // second min!
+            return SubValue(Value(0), GetMin(info->op_b, trip), INT_MAX);
+          case HInductionVarAnalysis::kMul:
+            return GetMul(info->op_a, info->op_b, trip, INT_MAX);
+          case HInductionVarAnalysis::kDiv:
+            return GetDiv(info->op_a, info->op_b, trip, INT_MAX);
+          case HInductionVarAnalysis::kFetch:
+            return GetFetch(info->fetch, INT_MAX);
+        }
+        break;
+      case HInductionVarAnalysis::kLinear:
+        // Maximum over linear induction a * i + b, for normalized 0 <= i < TC.
+        return AddValue(GetMul(info->op_a, trip, trip, INT_MAX),
+                        GetMax(info->op_b, trip), INT_MAX);
+      case HInductionVarAnalysis::kWrapAround:
+      case HInductionVarAnalysis::kPeriodic:
+        // Maximum over all values in the wrap-around/periodic.
+        return MaxValue(GetMax(info->op_a, trip), GetMax(info->op_b, trip));
+    }
+  }
+  return Value(INT_MAX);
+}
+
+InductionVarRange::Value InductionVarRange::GetMul(HInductionVarAnalysis::InductionInfo* info1,
+                                                   HInductionVarAnalysis::InductionInfo* info2,
+                                                   HInductionVarAnalysis::InductionInfo* trip,
+                                                   int32_t fail_value) {
+  Value v1_min = GetMin(info1, trip);
+  Value v1_max = GetMax(info1, trip);
+  Value v2_min = GetMin(info2, trip);
+  Value v2_max = GetMax(info2, trip);
+  if (v1_min.a_constant == 0 && v1_min.b_constant >= 0) {
+    // Positive range vs. positive or negative range.
+    if (v2_min.a_constant == 0 && v2_min.b_constant >= 0) {
+      return (fail_value < 0) ? MulValue(v1_min, v2_min, fail_value)
+                              : MulValue(v1_max, v2_max, fail_value);
+    } else if (v2_max.a_constant == 0 && v2_max.b_constant <= 0) {
+      return (fail_value < 0) ? MulValue(v1_max, v2_min, fail_value)
+                              : MulValue(v1_min, v2_max, fail_value);
+    }
+  } else if (v1_min.a_constant == 0 && v1_min.b_constant <= 0) {
+    // Negative range vs. positive or negative range.
+    if (v2_min.a_constant == 0 && v2_min.b_constant >= 0) {
+      return (fail_value < 0) ? MulValue(v1_min, v2_max, fail_value)
+                              : MulValue(v1_max, v2_min, fail_value);
+    } else if (v2_max.a_constant == 0 && v2_max.b_constant <= 0) {
+      return (fail_value < 0) ? MulValue(v1_max, v2_max, fail_value)
+                              : MulValue(v1_min, v2_min, fail_value);
+    }
+  }
+  return Value(fail_value);
+}
+
+InductionVarRange::Value InductionVarRange::GetDiv(HInductionVarAnalysis::InductionInfo* info1,
+                                                   HInductionVarAnalysis::InductionInfo* info2,
+                                                   HInductionVarAnalysis::InductionInfo* trip,
+                                                   int32_t fail_value) {
+  Value v1_min = GetMin(info1, trip);
+  Value v1_max = GetMax(info1, trip);
+  Value v2_min = GetMin(info2, trip);
+  Value v2_max = GetMax(info2, trip);
+  if (v1_min.a_constant == 0 && v1_min.b_constant >= 0) {
+    // Positive range vs. positive or negative range.
+    if (v2_min.a_constant == 0 && v2_min.b_constant >= 0) {
+      return (fail_value < 0) ? DivValue(v1_min, v2_max, fail_value)
+                              : DivValue(v1_max, v2_min, fail_value);
+    } else if (v2_max.a_constant == 0 && v2_max.b_constant <= 0) {
+      return (fail_value < 0) ? DivValue(v1_max, v2_max, fail_value)
+                              : DivValue(v1_min, v2_min, fail_value);
+    }
+  } else if (v1_min.a_constant == 0 && v1_min.b_constant <= 0) {
+    // Negative range vs. positive or negative range.
+    if (v2_min.a_constant == 0 && v2_min.b_constant >= 0) {
+      return (fail_value < 0) ? DivValue(v1_min, v2_min, fail_value)
+                              : DivValue(v1_max, v2_max, fail_value);
+    } else if (v2_max.a_constant == 0 && v2_max.b_constant <= 0) {
+      return (fail_value < 0) ? DivValue(v1_max, v2_min, fail_value)
+                              : DivValue(v1_min, v2_max, fail_value);
+    }
+  }
+  return Value(fail_value);
+}
+
+InductionVarRange::Value InductionVarRange::AddValue(Value v1, Value v2, int32_t fail_value) {
+  if (IsSafeAdd(v1.b_constant, v2.b_constant)) {
+    const int32_t b = v1.b_constant + v2.b_constant;
+    if (v1.a_constant == 0) {
+      return Value(v2.instruction, v2.a_constant, b);
+    } else if (v2.a_constant == 0) {
+      return Value(v1.instruction, v1.a_constant, b);
+    } else if (v1.instruction == v2.instruction && IsSafeAdd(v1.a_constant, v2.a_constant)) {
+      return Value(v1.instruction, v1.a_constant + v2.a_constant, b);
+    }
+  }
+  return Value(fail_value);
+}
+
+InductionVarRange::Value InductionVarRange::SubValue(Value v1, Value v2, int32_t fail_value) {
+  if (IsSafeSub(v1.b_constant, v2.b_constant)) {
+    const int32_t b = v1.b_constant - v2.b_constant;
+    if (v1.a_constant == 0 && IsSafeSub(0, v2.a_constant)) {
+      return Value(v2.instruction, -v2.a_constant, b);
+    } else if (v2.a_constant == 0) {
+      return Value(v1.instruction, v1.a_constant, b);
+    } else if (v1.instruction == v2.instruction && IsSafeSub(v1.a_constant, v2.a_constant)) {
+      return Value(v1.instruction, v1.a_constant - v2.a_constant, b);
+    }
+  }
+  return Value(fail_value);
+}
+
+InductionVarRange::Value InductionVarRange::MulValue(Value v1, Value v2, int32_t fail_value) {
+  if (v1.a_constant == 0) {
+    if (IsSafeMul(v1.b_constant, v2.a_constant) && IsSafeMul(v1.b_constant, v2.b_constant)) {
+      return Value(v2.instruction, v1.b_constant * v2.a_constant, v1.b_constant * v2.b_constant);
+    }
+  } else if (v2.a_constant == 0) {
+    if (IsSafeMul(v1.a_constant, v2.b_constant) && IsSafeMul(v1.b_constant, v2.b_constant)) {
+      return Value(v1.instruction, v1.a_constant * v2.b_constant, v1.b_constant * v2.b_constant);
+    }
+  }
+  return Value(fail_value);
+}
+
+InductionVarRange::Value InductionVarRange::DivValue(Value v1, Value v2, int32_t fail_value) {
+  if (v1.a_constant == 0 && v2.a_constant == 0) {
+    if (IsSafeDiv(v1.b_constant, v2.b_constant)) {
+      return Value(v1.b_constant / v2.b_constant);
+    }
+  }
+  return Value(fail_value);
+}
+
+InductionVarRange::Value InductionVarRange::MinValue(Value v1, Value v2) {
+  if (v1.instruction == v2.instruction && v1.a_constant == v2.a_constant) {
+    return Value(v1.instruction, v1.a_constant, std::min(v1.b_constant, v2.b_constant));
+  }
+  return Value(INT_MIN);
+}
+
+InductionVarRange::Value InductionVarRange::MaxValue(Value v1, Value v2) {
+  if (v1.instruction == v2.instruction && v1.a_constant == v2.a_constant) {
+    return Value(v1.instruction, v1.a_constant, std::max(v1.b_constant, v2.b_constant));
+  }
+  return Value(INT_MAX);
+}
+
+}  // namespace art
diff --git a/compiler/optimizing/induction_var_range.h b/compiler/optimizing/induction_var_range.h
new file mode 100644
index 0000000..b079076
--- /dev/null
+++ b/compiler/optimizing/induction_var_range.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2015 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_COMPILER_OPTIMIZING_INDUCTION_VAR_RANGE_H_
+#define ART_COMPILER_OPTIMIZING_INDUCTION_VAR_RANGE_H_
+
+#include "induction_var_analysis.h"
+
+namespace art {
+
+/**
+ * This class implements induction variable based range analysis on expressions within loops.
+ * It takes the results of induction variable analysis in the constructor and provides a public
+ * API to obtain a conservative lower and upper bound value on each instruction in the HIR.
+ *
+ * For example, given a linear induction 2 * i + x where 0 <= i <= 10, range analysis yields lower
+ * bound value x and upper bound value x + 20 for the expression, thus, the range [0, x + 20].
+ */
+class InductionVarRange {
+ public:
+  /*
+   * A value that can be represented as "a * instruction + b" for 32-bit constants, where
+   * Value(INT_MIN) and Value(INT_MAX) denote an unknown lower and upper bound, respectively.
+   * Although range analysis could yield more complex values, the format is sufficiently powerful
+   * to represent useful cases and feeds directly into optimizations like bounds check elimination.
+   */
+  struct Value {
+    Value(HInstruction* i, int32_t a, int32_t b)
+        : instruction(a ? i : nullptr),
+          a_constant(a),
+          b_constant(b) {}
+    explicit Value(int32_t b) : Value(nullptr, 0, b) {}
+    HInstruction* instruction;
+    int32_t a_constant;
+    int32_t b_constant;
+  };
+
+  explicit InductionVarRange(HInductionVarAnalysis* induction);
+
+  /**
+   * Given a context denoted by the first instruction, returns a,
+   * possibly conservative, lower bound on the instruction's value.
+   */
+  Value GetMinInduction(HInstruction* context, HInstruction* instruction);
+
+  /**
+   * Given a context denoted by the first instruction, returns a,
+   * possibly conservative, upper bound on the instruction's value.
+   */
+  Value GetMaxInduction(HInstruction* context, HInstruction* instruction);
+
+ private:
+  //
+  // Private helper methods.
+  //
+
+  HInductionVarAnalysis::InductionInfo* GetTripCount(HLoopInformation* loop,
+                                                     HInstruction* context);
+
+  static Value GetFetch(HInstruction* instruction, int32_t fail_value);
+
+  static Value GetMin(HInductionVarAnalysis::InductionInfo* info,
+                      HInductionVarAnalysis::InductionInfo* trip);
+  static Value GetMax(HInductionVarAnalysis::InductionInfo* info,
+                      HInductionVarAnalysis::InductionInfo* trip);
+  static Value GetMul(HInductionVarAnalysis::InductionInfo* info1,
+                      HInductionVarAnalysis::InductionInfo* info2,
+                      HInductionVarAnalysis::InductionInfo* trip, int32_t fail_value);
+  static Value GetDiv(HInductionVarAnalysis::InductionInfo* info1,
+                      HInductionVarAnalysis::InductionInfo* info2,
+                      HInductionVarAnalysis::InductionInfo* trip, int32_t fail_value);
+
+  static Value AddValue(Value v1, Value v2, int32_t fail_value);
+  static Value SubValue(Value v1, Value v2, int32_t fail_value);
+  static Value MulValue(Value v1, Value v2, int32_t fail_value);
+  static Value DivValue(Value v1, Value v2, int32_t fail_value);
+  static Value MinValue(Value v1, Value v2);
+  static Value MaxValue(Value v1, Value v2);
+
+  /** Results of prior induction variable analysis. */
+  HInductionVarAnalysis *induction_analysis_;
+
+  friend class InductionVarRangeTest;
+
+  DISALLOW_COPY_AND_ASSIGN(InductionVarRange);
+};
+
+}  // namespace art
+
+#endif  // ART_COMPILER_OPTIMIZING_INDUCTION_VAR_RANGE_H_
diff --git a/compiler/optimizing/induction_var_range_test.cc b/compiler/optimizing/induction_var_range_test.cc
new file mode 100644
index 0000000..d3c3518
--- /dev/null
+++ b/compiler/optimizing/induction_var_range_test.cc
@@ -0,0 +1,341 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#include <limits.h>
+
+#include "base/arena_allocator.h"
+#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"
+
+namespace art {
+
+using Value = InductionVarRange::Value;
+
+/**
+ * Fixture class for the InductionVarRange tests.
+ */
+class InductionVarRangeTest : public testing::Test {
+ public:
+  InductionVarRangeTest()  : pool_(), allocator_(&pool_) {
+    graph_ = CreateGraph(&allocator_);
+    iva_ = new (&allocator_) HInductionVarAnalysis(graph_);
+    BuildGraph();
+  }
+
+  ~InductionVarRangeTest() { }
+
+  void ExpectEqual(Value v1, Value v2) {
+    EXPECT_EQ(v1.instruction, v2.instruction);
+    EXPECT_EQ(v1.a_constant, v2.a_constant);
+    EXPECT_EQ(v1.b_constant, v2.b_constant);
+  }
+
+  /** 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);
+  }
+
+  /** Constructs an invariant. */
+  HInductionVarAnalysis::InductionInfo* CreateInvariant(char opc,
+                                                        HInductionVarAnalysis::InductionInfo* a,
+                                                        HInductionVarAnalysis::InductionInfo* b) {
+    HInductionVarAnalysis::InductionOp op;
+    switch (opc) {
+      case '+': op = HInductionVarAnalysis::kAdd; break;
+      case '-': op = HInductionVarAnalysis::kSub; break;
+      case 'n': op = HInductionVarAnalysis::kNeg; break;
+      case '*': op = HInductionVarAnalysis::kMul; break;
+      case '/': op = HInductionVarAnalysis::kDiv; break;
+      default:  op = HInductionVarAnalysis::kNop; break;
+    }
+    return iva_->CreateInvariantOp(op, a, b);
+  }
+
+  /** Constructs a fetch. */
+  HInductionVarAnalysis::InductionInfo* CreateFetch(HInstruction* fetch) {
+    return iva_->CreateInvariantFetch(fetch);
+  }
+
+  /** Constructs a constant. */
+  HInductionVarAnalysis::InductionInfo* CreateConst(int32_t c) {
+    return CreateFetch(graph_->GetIntConstant(c));
+  }
+
+  /** Constructs a trip-count. */
+  HInductionVarAnalysis::InductionInfo* CreateTripCount(int32_t tc) {
+    HInductionVarAnalysis::InductionInfo* trip = CreateConst(tc);
+    return CreateInvariant('@', trip, trip);
+  }
+
+  /** 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));
+  }
+
+  /** 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));
+  }
+
+  /** Constructs a wrap-around induction consisting of a constant, followed by a range. */
+  HInductionVarAnalysis::InductionInfo* CreateWrapAround(int32_t initial, int32_t lo, int32_t hi) {
+    return iva_->CreateInduction(
+        HInductionVarAnalysis::kWrapAround, CreateConst(initial), CreateRange(lo, hi));
+  }
+
+  //
+  // Relay methods.
+  //
+
+  Value GetMin(HInductionVarAnalysis::InductionInfo* info,
+               HInductionVarAnalysis::InductionInfo* induc) {
+    return InductionVarRange::GetMin(info, induc);
+  }
+
+  Value GetMax(HInductionVarAnalysis::InductionInfo* info,
+               HInductionVarAnalysis::InductionInfo* induc) {
+    return InductionVarRange::GetMax(info, induc);
+  }
+
+  Value GetMul(HInductionVarAnalysis::InductionInfo* info1,
+               HInductionVarAnalysis::InductionInfo* info2, int32_t fail_value) {
+    return InductionVarRange::GetMul(info1, info2, nullptr, fail_value);
+  }
+
+  Value GetDiv(HInductionVarAnalysis::InductionInfo* info1,
+               HInductionVarAnalysis::InductionInfo* info2, int32_t fail_value) {
+    return InductionVarRange::GetDiv(info1, info2, nullptr, fail_value);
+  }
+
+  Value AddValue(Value v1, Value v2) { return InductionVarRange::AddValue(v1, v2, INT_MIN); }
+  Value SubValue(Value v1, Value v2) { return InductionVarRange::SubValue(v1, v2, INT_MIN); }
+  Value MulValue(Value v1, Value v2) { return InductionVarRange::MulValue(v1, v2, INT_MIN); }
+  Value DivValue(Value v1, Value v2) { return InductionVarRange::DivValue(v1, v2, INT_MIN); }
+  Value MinValue(Value v1, Value v2) { return InductionVarRange::MinValue(v1, v2); }
+  Value MaxValue(Value v1, Value v2) { return InductionVarRange::MaxValue(v1, v2); }
+
+  // General building fields.
+  ArenaPool pool_;
+  ArenaAllocator allocator_;
+  HGraph* graph_;
+  HInductionVarAnalysis* iva_;
+
+  // Two dummy instructions.
+  HReturnVoid x_;
+  HReturnVoid y_;
+};
+
+//
+// The actual InductionVarRange tests.
+//
+
+TEST_F(InductionVarRangeTest, GetMinMaxNull) {
+  ExpectEqual(Value(INT_MIN), GetMin(nullptr, nullptr));
+  ExpectEqual(Value(INT_MAX), GetMax(nullptr, nullptr));
+}
+
+TEST_F(InductionVarRangeTest, GetMinMaxAdd) {
+  ExpectEqual(Value(12),
+              GetMin(CreateInvariant('+', CreateConst(2), CreateRange(10, 20)), nullptr));
+  ExpectEqual(Value(22),
+              GetMax(CreateInvariant('+', CreateConst(2), CreateRange(10, 20)), nullptr));
+  ExpectEqual(Value(&x_, 1, -20),
+              GetMin(CreateInvariant('+', CreateFetch(&x_), CreateRange(-20, -10)), nullptr));
+  ExpectEqual(Value(&x_, 1, -10),
+              GetMax(CreateInvariant('+', CreateFetch(&x_), CreateRange(-20, -10)), nullptr));
+  ExpectEqual(Value(&x_, 1, 10),
+              GetMin(CreateInvariant('+', CreateRange(10, 20), CreateFetch(&x_)), nullptr));
+  ExpectEqual(Value(&x_, 1, 20),
+              GetMax(CreateInvariant('+', CreateRange(10, 20), CreateFetch(&x_)), nullptr));
+  ExpectEqual(Value(5),
+              GetMin(CreateInvariant('+', CreateRange(-5, -1), CreateRange(10, 20)), nullptr));
+  ExpectEqual(Value(19),
+              GetMax(CreateInvariant('+', CreateRange(-5, -1), CreateRange(10, 20)), nullptr));
+}
+
+TEST_F(InductionVarRangeTest, GetMinMaxSub) {
+  ExpectEqual(Value(-18),
+              GetMin(CreateInvariant('-', CreateConst(2), CreateRange(10, 20)), nullptr));
+  ExpectEqual(Value(-8),
+              GetMax(CreateInvariant('-', CreateConst(2), CreateRange(10, 20)), nullptr));
+  ExpectEqual(Value(&x_, 1, 10),
+              GetMin(CreateInvariant('-', CreateFetch(&x_), CreateRange(-20, -10)), nullptr));
+  ExpectEqual(Value(&x_, 1, 20),
+              GetMax(CreateInvariant('-', CreateFetch(&x_), CreateRange(-20, -10)), nullptr));
+  ExpectEqual(Value(&x_, -1, 10),
+              GetMin(CreateInvariant('-', CreateRange(10, 20), CreateFetch(&x_)), nullptr));
+  ExpectEqual(Value(&x_, -1, 20),
+              GetMax(CreateInvariant('-', CreateRange(10, 20), CreateFetch(&x_)), nullptr));
+  ExpectEqual(Value(-25),
+              GetMin(CreateInvariant('-', CreateRange(-5, -1), CreateRange(10, 20)), nullptr));
+  ExpectEqual(Value(-11),
+              GetMax(CreateInvariant('-', CreateRange(-5, -1), CreateRange(10, 20)), nullptr));
+}
+
+TEST_F(InductionVarRangeTest, GetMinMaxNeg) {
+  ExpectEqual(Value(-20), GetMin(CreateInvariant('n', nullptr, CreateRange(10, 20)), nullptr));
+  ExpectEqual(Value(-10), GetMax(CreateInvariant('n', nullptr, CreateRange(10, 20)), nullptr));
+  ExpectEqual(Value(10), GetMin(CreateInvariant('n', nullptr, CreateRange(-20, -10)), nullptr));
+  ExpectEqual(Value(20), GetMax(CreateInvariant('n', nullptr, CreateRange(-20, -10)), nullptr));
+  ExpectEqual(Value(&x_, -1, 0), GetMin(CreateInvariant('n', nullptr, CreateFetch(&x_)), nullptr));
+  ExpectEqual(Value(&x_, -1, 0), GetMax(CreateInvariant('n', nullptr, CreateFetch(&x_)), nullptr));
+}
+
+TEST_F(InductionVarRangeTest, GetMinMaxMul) {
+  ExpectEqual(Value(20),
+              GetMin(CreateInvariant('*', CreateConst(2), CreateRange(10, 20)), nullptr));
+  ExpectEqual(Value(40),
+              GetMax(CreateInvariant('*', CreateConst(2), CreateRange(10, 20)), nullptr));
+}
+
+TEST_F(InductionVarRangeTest, GetMinMaxDiv) {
+  ExpectEqual(Value(3),
+              GetMin(CreateInvariant('/', CreateRange(12, 20), CreateConst(4)), nullptr));
+  ExpectEqual(Value(5),
+              GetMax(CreateInvariant('/', CreateRange(12, 20), CreateConst(4)), nullptr));
+}
+
+TEST_F(InductionVarRangeTest, GetMinMaxConstant) {
+  ExpectEqual(Value(12345), GetMin(CreateConst(12345), nullptr));
+  ExpectEqual(Value(12345), GetMax(CreateConst(12345), nullptr));
+}
+
+TEST_F(InductionVarRangeTest, GetMinMaxFetch) {
+  ExpectEqual(Value(&x_, 1, 0), GetMin(CreateFetch(&x_), nullptr));
+  ExpectEqual(Value(&x_, 1, 0), GetMax(CreateFetch(&x_), nullptr));
+}
+
+TEST_F(InductionVarRangeTest, GetMinMaxLinear) {
+  ExpectEqual(Value(20), GetMin(CreateLinear(10, 20), CreateTripCount(100)));
+  ExpectEqual(Value(1010), GetMax(CreateLinear(10, 20), CreateTripCount(100)));
+  ExpectEqual(Value(-970), GetMin(CreateLinear(-10, 20), CreateTripCount(100)));
+  ExpectEqual(Value(20), GetMax(CreateLinear(-10, 20), CreateTripCount(100)));
+}
+
+TEST_F(InductionVarRangeTest, GetMinMaxWrapAround) {
+  ExpectEqual(Value(-5), GetMin(CreateWrapAround(-5, -1, 10), nullptr));
+  ExpectEqual(Value(10), GetMax(CreateWrapAround(-5, -1, 10), nullptr));
+  ExpectEqual(Value(-1), GetMin(CreateWrapAround(2, -1, 10), nullptr));
+  ExpectEqual(Value(10), GetMax(CreateWrapAround(2, -1, 10), nullptr));
+  ExpectEqual(Value(-1), GetMin(CreateWrapAround(20, -1, 10), nullptr));
+  ExpectEqual(Value(20), GetMax(CreateWrapAround(20, -1, 10), nullptr));
+}
+
+TEST_F(InductionVarRangeTest, GetMinMaxPeriodic) {
+  ExpectEqual(Value(-2), GetMin(CreateRange(-2, 99), nullptr));
+  ExpectEqual(Value(99), GetMax(CreateRange(-2, 99), nullptr));
+}
+
+TEST_F(InductionVarRangeTest, GetMulMin) {
+  ExpectEqual(Value(6), GetMul(CreateRange(2, 10), CreateRange(3, 5), INT_MIN));
+  ExpectEqual(Value(-50), GetMul(CreateRange(2, 10), CreateRange(-5, -3), INT_MIN));
+  ExpectEqual(Value(-50), GetMul(CreateRange(-10, -2), CreateRange(3, 5), INT_MIN));
+  ExpectEqual(Value(6), GetMul(CreateRange(-10, -2), CreateRange(-5, -3), INT_MIN));
+}
+
+TEST_F(InductionVarRangeTest, GetMulMax) {
+  ExpectEqual(Value(50), GetMul(CreateRange(2, 10), CreateRange(3, 5), INT_MAX));
+  ExpectEqual(Value(-6), GetMul(CreateRange(2, 10), CreateRange(-5, -3), INT_MAX));
+  ExpectEqual(Value(-6), GetMul(CreateRange(-10, -2), CreateRange(3, 5), INT_MAX));
+  ExpectEqual(Value(50), GetMul(CreateRange(-10, -2), CreateRange(-5, -3), INT_MAX));
+}
+
+TEST_F(InductionVarRangeTest, GetDivMin) {
+  ExpectEqual(Value(10), GetDiv(CreateRange(40, 1000), CreateRange(2, 4), INT_MIN));
+  ExpectEqual(Value(-500), GetDiv(CreateRange(40, 1000), CreateRange(-4, -2), INT_MIN));
+  ExpectEqual(Value(-500), GetDiv(CreateRange(-1000, -40), CreateRange(2, 4), INT_MIN));
+  ExpectEqual(Value(10), GetDiv(CreateRange(-1000, -40), CreateRange(-4, -2), INT_MIN));
+}
+
+TEST_F(InductionVarRangeTest, GetDivMax) {
+  ExpectEqual(Value(500), GetDiv(CreateRange(40, 1000), CreateRange(2, 4), INT_MAX));
+  ExpectEqual(Value(-10), GetDiv(CreateRange(40, 1000), CreateRange(-4, -2), INT_MAX));
+  ExpectEqual(Value(-10), GetDiv(CreateRange(-1000, -40), CreateRange(2, 4), INT_MAX));
+  ExpectEqual(Value(500), GetDiv(CreateRange(-1000, -40), CreateRange(-4, -2), INT_MAX));
+}
+
+TEST_F(InductionVarRangeTest, AddValue) {
+  ExpectEqual(Value(110), AddValue(Value(10), Value(100)));
+  ExpectEqual(Value(-5), AddValue(Value(&x_, 1, -4), Value(&x_, -1, -1)));
+  ExpectEqual(Value(&x_, 3, -5), AddValue(Value(&x_, 2, -4), Value(&x_, 1, -1)));
+  ExpectEqual(Value(INT_MIN), AddValue(Value(&x_, 1, 5), Value(&y_, 1, -7)));
+  ExpectEqual(Value(&x_, 1, 23), AddValue(Value(&x_, 1, 20), Value(3)));
+  ExpectEqual(Value(&y_, 1, 5), AddValue(Value(55), Value(&y_, 1, -50)));
+  // Unsafe.
+  ExpectEqual(Value(INT_MIN), AddValue(Value(INT_MAX - 5), Value(6)));
+}
+
+TEST_F(InductionVarRangeTest, SubValue) {
+  ExpectEqual(Value(-90), SubValue(Value(10), Value(100)));
+  ExpectEqual(Value(-3), SubValue(Value(&x_, 1, -4), Value(&x_, 1, -1)));
+  ExpectEqual(Value(&x_, 2, -3), SubValue(Value(&x_, 3, -4), Value(&x_, 1, -1)));
+  ExpectEqual(Value(INT_MIN), SubValue(Value(&x_, 1, 5), Value(&y_, 1, -7)));
+  ExpectEqual(Value(&x_, 1, 17), SubValue(Value(&x_, 1, 20), Value(3)));
+  ExpectEqual(Value(&y_, -4, 105), SubValue(Value(55), Value(&y_, 4, -50)));
+  // Unsafe.
+  ExpectEqual(Value(INT_MIN), SubValue(Value(INT_MIN + 5), Value(6)));
+}
+
+TEST_F(InductionVarRangeTest, MulValue) {
+  ExpectEqual(Value(1000), MulValue(Value(10), Value(100)));
+  ExpectEqual(Value(INT_MIN), MulValue(Value(&x_, 1, -4), Value(&x_, 1, -1)));
+  ExpectEqual(Value(INT_MIN), MulValue(Value(&x_, 1, 5), Value(&y_, 1, -7)));
+  ExpectEqual(Value(&x_, 9, 60), MulValue(Value(&x_, 3, 20), Value(3)));
+  ExpectEqual(Value(&y_, 55, -110), MulValue(Value(55), Value(&y_, 1, -2)));
+  // Unsafe.
+  ExpectEqual(Value(INT_MIN), MulValue(Value(90000), Value(-90000)));
+}
+
+TEST_F(InductionVarRangeTest, DivValue) {
+  ExpectEqual(Value(25), DivValue(Value(100), Value(4)));
+  ExpectEqual(Value(INT_MIN), DivValue(Value(&x_, 1, -4), Value(&x_, 1, -1)));
+  ExpectEqual(Value(INT_MIN), DivValue(Value(&x_, 1, 5), Value(&y_, 1, -7)));
+  ExpectEqual(Value(INT_MIN), DivValue(Value(&x_, 12, 24), Value(3)));
+  ExpectEqual(Value(INT_MIN), DivValue(Value(55), Value(&y_, 1, -50)));
+  // Unsafe.
+  ExpectEqual(Value(INT_MIN), DivValue(Value(1), Value(0)));
+}
+
+TEST_F(InductionVarRangeTest, MinValue) {
+  ExpectEqual(Value(10), MinValue(Value(10), Value(100)));
+  ExpectEqual(Value(&x_, 1, -4), MinValue(Value(&x_, 1, -4), Value(&x_, 1, -1)));
+  ExpectEqual(Value(&x_, 4, -4), MinValue(Value(&x_, 4, -4), Value(&x_, 4, -1)));
+  ExpectEqual(Value(INT_MIN), MinValue(Value(&x_, 1, 5), Value(&y_, 1, -7)));
+  ExpectEqual(Value(INT_MIN), MinValue(Value(&x_, 1, 20), Value(3)));
+  ExpectEqual(Value(INT_MIN), MinValue(Value(55), Value(&y_, 1, -50)));
+}
+
+TEST_F(InductionVarRangeTest, MaxValue) {
+  ExpectEqual(Value(100), MaxValue(Value(10), Value(100)));
+  ExpectEqual(Value(&x_, 1, -1), MaxValue(Value(&x_, 1, -4), Value(&x_, 1, -1)));
+  ExpectEqual(Value(&x_, 4, -1), MaxValue(Value(&x_, 4, -4), Value(&x_, 4, -1)));
+  ExpectEqual(Value(INT_MAX), MaxValue(Value(&x_, 1, 5), Value(&y_, 1, -7)));
+  ExpectEqual(Value(INT_MAX), MaxValue(Value(&x_, 1, 20), Value(3)));
+  ExpectEqual(Value(INT_MAX), MaxValue(Value(55), Value(&y_, 1, -50)));
+}
+
+}  // namespace art
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index bd591f3..2966076 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -502,8 +502,9 @@
       // TODO: we could be more precise by merging the phi inputs but that requires
       // some functionality from the reference type propagation.
       DCHECK(return_replacement->IsPhi());
+      size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
       ReferenceTypeInfo::TypeHandle return_handle =
-        handles_->NewHandle(resolved_method->GetReturnType());
+        handles_->NewHandle(resolved_method->GetReturnType(true /* resolve */, pointer_size));
       return_replacement->SetReferenceTypeInfo(ReferenceTypeInfo::Create(
          return_handle, return_handle->IsFinal() /* is_exact */));
     }
diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc
index 075ec1e..b71fdb8 100644
--- a/compiler/optimizing/intrinsics.cc
+++ b/compiler/optimizing/intrinsics.cc
@@ -16,12 +16,17 @@
 
 #include "intrinsics.h"
 
+#include "art_method.h"
+#include "class_linker.h"
 #include "dex/quick/dex_file_method_inliner.h"
 #include "dex/quick/dex_file_to_method_inliner_map.h"
 #include "driver/compiler_driver.h"
 #include "invoke_type.h"
+#include "mirror/dex_cache-inl.h"
 #include "nodes.h"
 #include "quick/inline_method_analyser.h"
+#include "scoped_thread_state_change.h"
+#include "thread-inl.h"
 #include "utils.h"
 
 namespace art {
@@ -120,6 +125,28 @@
           LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
           UNREACHABLE();
       }
+    case kIntrinsicRotateRight:
+      switch (GetType(method.d.data, true)) {
+        case Primitive::kPrimInt:
+          return Intrinsics::kIntegerRotateRight;
+        case Primitive::kPrimLong:
+          return Intrinsics::kLongRotateRight;
+        default:
+          LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
+          UNREACHABLE();
+      }
+    case kIntrinsicRotateLeft:
+      switch (GetType(method.d.data, true)) {
+        case Primitive::kPrimInt:
+          return Intrinsics::kIntegerRotateLeft;
+        case Primitive::kPrimLong:
+          return Intrinsics::kLongRotateLeft;
+        default:
+          LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
+          UNREACHABLE();
+      }
+
+    // Misc data processing.
     case kIntrinsicNumberOfLeadingZeros:
       switch (GetType(method.d.data, true)) {
         case Primitive::kPrimInt:
@@ -130,6 +157,16 @@
           LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
           UNREACHABLE();
       }
+    case kIntrinsicNumberOfTrailingZeros:
+      switch (GetType(method.d.data, true)) {
+        case Primitive::kPrimInt:
+          return Intrinsics::kIntegerNumberOfTrailingZeros;
+        case Primitive::kPrimLong:
+          return Intrinsics::kLongNumberOfTrailingZeros;
+        default:
+          LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
+          UNREACHABLE();
+      }
 
     // Abs.
     case kIntrinsicAbsDouble:
@@ -328,14 +365,23 @@
   return Intrinsics::kNone;
 }
 
-static bool CheckInvokeType(Intrinsics intrinsic, HInvoke* invoke) {
+static bool CheckInvokeType(Intrinsics intrinsic, HInvoke* invoke, const DexFile& dex_file) {
   // The DexFileMethodInliner should have checked whether the methods are agreeing with
   // what we expect, i.e., static methods are called as such. Add another check here for
   // our expectations:
-  // Whenever the intrinsic is marked as static-or-direct, report an error if we find an
-  // InvokeVirtual. The other direction is not possible: we have intrinsics for virtual
-  // functions that will perform a check inline. If the precise type is known, however,
-  // the instruction will be sharpened to an InvokeStaticOrDirect.
+  //
+  // Whenever the intrinsic is marked as static, report an error if we find an InvokeVirtual.
+  //
+  // Whenever the intrinsic is marked as direct and we find an InvokeVirtual, a devirtualization
+  // failure occured. We might be in a situation where we have inlined a method that calls an
+  // intrinsic, but that method is in a different dex file on which we do not have a
+  // verified_method that would have helped the compiler driver sharpen the call. In that case,
+  // make sure that the intrinsic is actually for some final method (or in a final class), as
+  // otherwise the intrinsics setup is broken.
+  //
+  // For the last direction, we have intrinsics for virtual functions that will perform a check
+  // inline. If the precise type is known, however, the instruction will be sharpened to an
+  // InvokeStaticOrDirect.
   InvokeType intrinsic_type = GetIntrinsicInvokeType(intrinsic);
   InvokeType invoke_type = invoke->IsInvokeStaticOrDirect() ?
       invoke->AsInvokeStaticOrDirect()->GetInvokeType() :
@@ -343,8 +389,22 @@
   switch (intrinsic_type) {
     case kStatic:
       return (invoke_type == kStatic);
+
     case kDirect:
-      return (invoke_type == kDirect);
+      if (invoke_type == kDirect) {
+        return true;
+      }
+      if (invoke_type == kVirtual) {
+        ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+        ScopedObjectAccess soa(Thread::Current());
+        ArtMethod* art_method =
+            class_linker->FindDexCache(soa.Self(), dex_file)->GetResolvedMethod(
+                invoke->GetDexMethodIndex(), class_linker->GetImagePointerSize());
+        return art_method != nullptr &&
+            (art_method->IsFinal() || art_method->GetDeclaringClass()->IsFinal());
+      }
+      return false;
+
     case kVirtual:
       // Call might be devirtualized.
       return (invoke_type == kVirtual || invoke_type == kDirect);
@@ -364,17 +424,18 @@
       if (inst->IsInvoke()) {
         HInvoke* invoke = inst->AsInvoke();
         InlineMethod method;
-        DexFileMethodInliner* inliner =
-            driver_->GetMethodInlinerMap()->GetMethodInliner(&invoke->GetDexFile());
+        const DexFile& dex_file = invoke->GetDexFile();
+        DexFileMethodInliner* inliner = driver_->GetMethodInlinerMap()->GetMethodInliner(&dex_file);
         DCHECK(inliner != nullptr);
         if (inliner->IsIntrinsic(invoke->GetDexMethodIndex(), &method)) {
           Intrinsics intrinsic = GetIntrinsic(method, graph_->GetInstructionSet());
 
           if (intrinsic != Intrinsics::kNone) {
-            if (!CheckInvokeType(intrinsic, invoke)) {
+            if (!CheckInvokeType(intrinsic, invoke, dex_file)) {
               LOG(WARNING) << "Found an intrinsic with unexpected invoke type: "
-                           << intrinsic << " for "
-                           << PrettyMethod(invoke->GetDexMethodIndex(), invoke->GetDexFile());
+                  << intrinsic << " for "
+                  << PrettyMethod(invoke->GetDexMethodIndex(), invoke->GetDexFile())
+                  << invoke->DebugName();
             } else {
               invoke->SetIntrinsic(intrinsic, NeedsEnvironmentOrCache(intrinsic));
             }
diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc
index 69a3e62..cc8ddb6 100644
--- a/compiler/optimizing/intrinsics_arm.cc
+++ b/compiler/optimizing/intrinsics_arm.cc
@@ -103,11 +103,11 @@
     if (invoke_->IsInvokeStaticOrDirect()) {
       codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(),
                                           Location::RegisterLocation(kArtMethodRegister));
-      codegen->RecordPcInfo(invoke_, invoke_->GetDexPc(), this);
     } else {
-      UNIMPLEMENTED(FATAL) << "Non-direct intrinsic slow-path not yet implemented";
-      UNREACHABLE();
+      codegen->GenerateVirtualCall(invoke_->AsInvokeVirtual(),
+                                   Location::RegisterLocation(kArtMethodRegister));
     }
+    codegen->RecordPcInfo(invoke_, invoke_->GetDexPc(), this);
 
     // Copy the result back to the expected output.
     Location out = invoke_->GetLocations()->Out();
@@ -266,6 +266,227 @@
   GenNumberOfLeadingZeros(invoke->GetLocations(), Primitive::kPrimLong, GetAssembler());
 }
 
+static void GenNumberOfTrailingZeros(LocationSummary* locations,
+                                     Primitive::Type type,
+                                     ArmAssembler* assembler) {
+  DCHECK((type == Primitive::kPrimInt) || (type == Primitive::kPrimLong));
+
+  Register out = locations->Out().AsRegister<Register>();
+
+  if (type == Primitive::kPrimLong) {
+    Register in_reg_lo = locations->InAt(0).AsRegisterPairLow<Register>();
+    Register in_reg_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
+    Label end;
+    __ rbit(out, in_reg_lo);
+    __ clz(out, out);
+    __ CompareAndBranchIfNonZero(in_reg_lo, &end);
+    __ rbit(out, in_reg_hi);
+    __ clz(out, out);
+    __ AddConstant(out, 32);
+    __ Bind(&end);
+  } else {
+    Register in = locations->InAt(0).AsRegister<Register>();
+    __ rbit(out, in);
+    __ clz(out, out);
+  }
+}
+
+void IntrinsicLocationsBuilderARM::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
+  LocationSummary* locations = new (arena_) LocationSummary(invoke,
+                                                            LocationSummary::kNoCall,
+                                                            kIntrinsified);
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+void IntrinsicCodeGeneratorARM::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
+  GenNumberOfTrailingZeros(invoke->GetLocations(), Primitive::kPrimInt, GetAssembler());
+}
+
+void IntrinsicLocationsBuilderARM::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
+  LocationSummary* locations = new (arena_) LocationSummary(invoke,
+                                                            LocationSummary::kNoCall,
+                                                            kIntrinsified);
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
+}
+
+void IntrinsicCodeGeneratorARM::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
+  GenNumberOfTrailingZeros(invoke->GetLocations(), Primitive::kPrimLong, GetAssembler());
+}
+
+static void GenIntegerRotate(LocationSummary* locations,
+                             ArmAssembler* assembler,
+                             bool is_left) {
+  Register in = locations->InAt(0).AsRegister<Register>();
+  Location rhs = locations->InAt(1);
+  Register out = locations->Out().AsRegister<Register>();
+
+  if (rhs.IsConstant()) {
+    // Arm32 and Thumb2 assemblers require a rotation on the interval [1,31],
+    // so map all rotations to a +ve. equivalent in that range.
+    // (e.g. left *or* right by -2 bits == 30 bits in the same direction.)
+    uint32_t rot = rhs.GetConstant()->AsIntConstant()->GetValue() & 0x1F;
+    if (rot) {
+      // Rotate, mapping left rotations to right equivalents if necessary.
+      // (e.g. left by 2 bits == right by 30.)
+      __ Ror(out, in, is_left ? (0x20 - rot) : rot);
+    } else if (out != in) {
+      __ Mov(out, in);
+    }
+  } else {
+    if (is_left) {
+      __ rsb(out, rhs.AsRegister<Register>(), ShifterOperand(0));
+      __ Ror(out, in, out);
+    } else {
+      __ Ror(out, in, rhs.AsRegister<Register>());
+    }
+  }
+}
+
+// Gain some speed by mapping all Long rotates onto equivalent pairs of Integer
+// rotates by swapping input regs (effectively rotating by the first 32-bits of
+// a larger rotation) or flipping direction (thus treating larger right/left
+// rotations as sub-word sized rotations in the other direction) as appropriate.
+static void GenLongRotate(LocationSummary* locations,
+                          ArmAssembler* assembler,
+                          bool is_left) {
+  Register in_reg_lo = locations->InAt(0).AsRegisterPairLow<Register>();
+  Register in_reg_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
+  Location rhs = locations->InAt(1);
+  Register out_reg_lo = locations->Out().AsRegisterPairLow<Register>();
+  Register out_reg_hi = locations->Out().AsRegisterPairHigh<Register>();
+
+  if (rhs.IsConstant()) {
+    uint32_t rot = rhs.GetConstant()->AsIntConstant()->GetValue();
+    // Map all left rotations to right equivalents.
+    if (is_left) {
+      rot = 0x40 - rot;
+    }
+    // Map all rotations to +ve. equivalents on the interval [0,63].
+    rot &= 0x3F;
+    // For rotates over a word in size, 'pre-rotate' by 32-bits to keep rotate
+    // logic below to a simple pair of binary orr.
+    // (e.g. 34 bits == in_reg swap + 2 bits right.)
+    if (rot >= 0x20) {
+      rot -= 0x20;
+      std::swap(in_reg_hi, in_reg_lo);
+    }
+    // Rotate, or mov to out for zero or word size rotations.
+    if (rot) {
+      __ Lsr(out_reg_hi, in_reg_hi, rot);
+      __ orr(out_reg_hi, out_reg_hi, ShifterOperand(in_reg_lo, arm::LSL, 0x20 - rot));
+      __ Lsr(out_reg_lo, in_reg_lo, rot);
+      __ orr(out_reg_lo, out_reg_lo, ShifterOperand(in_reg_hi, arm::LSL, 0x20 - rot));
+    } else {
+      __ Mov(out_reg_lo, in_reg_lo);
+      __ Mov(out_reg_hi, in_reg_hi);
+    }
+  } else {
+    Register shift_left = locations->GetTemp(0).AsRegister<Register>();
+    Register shift_right = locations->GetTemp(1).AsRegister<Register>();
+    Label end;
+    Label right;
+
+    __ and_(shift_left, rhs.AsRegister<Register>(), ShifterOperand(0x1F));
+    __ Lsrs(shift_right, rhs.AsRegister<Register>(), 6);
+    __ rsb(shift_right, shift_left, ShifterOperand(0x20), AL, kCcKeep);
+
+    if (is_left) {
+      __ b(&right, CS);
+    } else {
+      __ b(&right, CC);
+      std::swap(shift_left, shift_right);
+    }
+
+    // out_reg_hi = (reg_hi << shift_left) | (reg_lo >> shift_right).
+    // out_reg_lo = (reg_lo << shift_left) | (reg_hi >> shift_right).
+    __ Lsl(out_reg_hi, in_reg_hi, shift_left);
+    __ Lsr(out_reg_lo, in_reg_lo, shift_right);
+    __ add(out_reg_hi, out_reg_hi, ShifterOperand(out_reg_lo));
+    __ Lsl(out_reg_lo, in_reg_lo, shift_left);
+    __ Lsr(shift_left, in_reg_hi, shift_right);
+    __ add(out_reg_lo, out_reg_lo, ShifterOperand(shift_left));
+    __ b(&end);
+
+    // out_reg_hi = (reg_hi >> shift_right) | (reg_lo << shift_left).
+    // out_reg_lo = (reg_lo >> shift_right) | (reg_hi << shift_left).
+    __ Bind(&right);
+    __ Lsr(out_reg_hi, in_reg_hi, shift_right);
+    __ Lsl(out_reg_lo, in_reg_lo, shift_left);
+    __ add(out_reg_hi, out_reg_hi, ShifterOperand(out_reg_lo));
+    __ Lsr(out_reg_lo, in_reg_lo, shift_right);
+    __ Lsl(shift_right, in_reg_hi, shift_left);
+    __ add(out_reg_lo, out_reg_lo, ShifterOperand(shift_right));
+
+    __ Bind(&end);
+  }
+}
+
+void IntrinsicLocationsBuilderARM::VisitIntegerRotateRight(HInvoke* invoke) {
+  LocationSummary* locations = new (arena_) LocationSummary(invoke,
+                                                            LocationSummary::kNoCall,
+                                                            kIntrinsified);
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1)));
+  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+void IntrinsicCodeGeneratorARM::VisitIntegerRotateRight(HInvoke* invoke) {
+  GenIntegerRotate(invoke->GetLocations(), GetAssembler(), false /* is_left */);
+}
+
+void IntrinsicLocationsBuilderARM::VisitLongRotateRight(HInvoke* invoke) {
+  LocationSummary* locations = new (arena_) LocationSummary(invoke,
+                                                            LocationSummary::kNoCall,
+                                                            kIntrinsified);
+  locations->SetInAt(0, Location::RequiresRegister());
+  if (invoke->InputAt(1)->IsConstant()) {
+    locations->SetInAt(1, Location::ConstantLocation(invoke->InputAt(1)->AsConstant()));
+  } else {
+    locations->SetInAt(1, Location::RequiresRegister());
+    locations->AddTemp(Location::RequiresRegister());
+    locations->AddTemp(Location::RequiresRegister());
+  }
+  locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
+}
+
+void IntrinsicCodeGeneratorARM::VisitLongRotateRight(HInvoke* invoke) {
+  GenLongRotate(invoke->GetLocations(), GetAssembler(), false /* is_left */);
+}
+
+void IntrinsicLocationsBuilderARM::VisitIntegerRotateLeft(HInvoke* invoke) {
+  LocationSummary* locations = new (arena_) LocationSummary(invoke,
+                                                            LocationSummary::kNoCall,
+                                                            kIntrinsified);
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1)));
+  locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
+}
+
+void IntrinsicCodeGeneratorARM::VisitIntegerRotateLeft(HInvoke* invoke) {
+  GenIntegerRotate(invoke->GetLocations(), GetAssembler(), true /* is_left */);
+}
+
+void IntrinsicLocationsBuilderARM::VisitLongRotateLeft(HInvoke* invoke) {
+  LocationSummary* locations = new (arena_) LocationSummary(invoke,
+                                                            LocationSummary::kNoCall,
+                                                            kIntrinsified);
+  locations->SetInAt(0, Location::RequiresRegister());
+  if (invoke->InputAt(1)->IsConstant()) {
+    locations->SetInAt(1, Location::ConstantLocation(invoke->InputAt(1)->AsConstant()));
+  } else {
+    locations->SetInAt(1, Location::RequiresRegister());
+    locations->AddTemp(Location::RequiresRegister());
+    locations->AddTemp(Location::RequiresRegister());
+  }
+  locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
+}
+
+void IntrinsicCodeGeneratorARM::VisitLongRotateLeft(HInvoke* invoke) {
+  GenLongRotate(invoke->GetLocations(), GetAssembler(), true /* is_left */);
+}
+
 static void MathAbsFP(LocationSummary* locations, bool is64bit, ArmAssembler* assembler) {
   Location in = locations->InAt(0);
   Location out = locations->Out();
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index 0171d69..b0cfd0d 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -41,12 +41,12 @@
 using helpers::FPRegisterFrom;
 using helpers::HeapOperand;
 using helpers::LocationFrom;
+using helpers::OperandFrom;
 using helpers::RegisterFrom;
 using helpers::SRegisterFrom;
 using helpers::WRegisterFrom;
 using helpers::XRegisterFrom;
 
-
 namespace {
 
 ALWAYS_INLINE inline MemOperand AbsoluteHeapOperandFrom(Location location, size_t offset = 0) {
@@ -112,11 +112,10 @@
     if (invoke_->IsInvokeStaticOrDirect()) {
       codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(),
                                           LocationFrom(kArtMethodRegister));
-      codegen->RecordPcInfo(invoke_, invoke_->GetDexPc(), this);
     } else {
-      UNIMPLEMENTED(FATAL) << "Non-direct intrinsic slow-path not yet implemented";
-      UNREACHABLE();
+      codegen->GenerateVirtualCall(invoke_->AsInvokeVirtual(), LocationFrom(kArtMethodRegister));
     }
+    codegen->RecordPcInfo(invoke_, invoke_->GetDexPc(), this);
 
     // Copy the result back to the expected output.
     Location out = invoke_->GetLocations()->Out();
@@ -287,6 +286,131 @@
   GenNumberOfLeadingZeros(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
 }
 
+static void GenNumberOfTrailingZeros(LocationSummary* locations,
+                                     Primitive::Type type,
+                                     vixl::MacroAssembler* masm) {
+  DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
+
+  Location in = locations->InAt(0);
+  Location out = locations->Out();
+
+  __ Rbit(RegisterFrom(out, type), RegisterFrom(in, type));
+  __ Clz(RegisterFrom(out, type), RegisterFrom(out, type));
+}
+
+void IntrinsicLocationsBuilderARM64::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
+  CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
+  GenNumberOfTrailingZeros(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
+}
+
+void IntrinsicLocationsBuilderARM64::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
+  CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
+  GenNumberOfTrailingZeros(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
+}
+
+static void GenRotateRight(LocationSummary* locations,
+                           Primitive::Type type,
+                           vixl::MacroAssembler* masm) {
+  DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
+
+  Location in = locations->InAt(0);
+  Location out = locations->Out();
+  Operand rhs = OperandFrom(locations->InAt(1), type);
+
+  if (rhs.IsImmediate()) {
+    uint32_t shift = rhs.immediate() & (RegisterFrom(in, type).SizeInBits() - 1);
+    __ Ror(RegisterFrom(out, type),
+           RegisterFrom(in, type),
+           shift);
+  } else {
+    DCHECK(rhs.shift() == vixl::LSL && rhs.shift_amount() == 0);
+    __ Ror(RegisterFrom(out, type),
+           RegisterFrom(in, type),
+           rhs.reg());
+  }
+}
+
+void IntrinsicLocationsBuilderARM64::VisitIntegerRotateRight(HInvoke* invoke) {
+  LocationSummary* locations = new (arena_) LocationSummary(invoke,
+                                                           LocationSummary::kNoCall,
+                                                           kIntrinsified);
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1)));
+  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitIntegerRotateRight(HInvoke* invoke) {
+  GenRotateRight(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
+}
+
+void IntrinsicLocationsBuilderARM64::VisitLongRotateRight(HInvoke* invoke) {
+  LocationSummary* locations = new (arena_) LocationSummary(invoke,
+                                                           LocationSummary::kNoCall,
+                                                           kIntrinsified);
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1)));
+  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitLongRotateRight(HInvoke* invoke) {
+  GenRotateRight(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
+}
+
+static void GenRotateLeft(LocationSummary* locations,
+                           Primitive::Type type,
+                           vixl::MacroAssembler* masm) {
+  DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
+
+  Location in = locations->InAt(0);
+  Location out = locations->Out();
+  Operand rhs = OperandFrom(locations->InAt(1), type);
+
+  if (rhs.IsImmediate()) {
+    uint32_t regsize = RegisterFrom(in, type).SizeInBits();
+    uint32_t shift = (regsize - rhs.immediate()) & (regsize - 1);
+    __ Ror(RegisterFrom(out, type), RegisterFrom(in, type), shift);
+  } else {
+    DCHECK(rhs.shift() == vixl::LSL && rhs.shift_amount() == 0);
+    __ Neg(RegisterFrom(out, type),
+           Operand(RegisterFrom(locations->InAt(1), type)));
+    __ Ror(RegisterFrom(out, type),
+           RegisterFrom(in, type),
+           RegisterFrom(out, type));
+  }
+}
+
+void IntrinsicLocationsBuilderARM64::VisitIntegerRotateLeft(HInvoke* invoke) {
+  LocationSummary* locations = new (arena_) LocationSummary(invoke,
+                                                           LocationSummary::kNoCall,
+                                                           kIntrinsified);
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1)));
+  locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitIntegerRotateLeft(HInvoke* invoke) {
+  GenRotateLeft(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
+}
+
+void IntrinsicLocationsBuilderARM64::VisitLongRotateLeft(HInvoke* invoke) {
+  LocationSummary* locations = new (arena_) LocationSummary(invoke,
+                                                           LocationSummary::kNoCall,
+                                                           kIntrinsified);
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1)));
+  locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitLongRotateLeft(HInvoke* invoke) {
+  GenRotateLeft(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
+}
+
 static void GenReverse(LocationSummary* locations,
                        Primitive::Type type,
                        vixl::MacroAssembler* masm) {
diff --git a/compiler/optimizing/intrinsics_list.h b/compiler/optimizing/intrinsics_list.h
index 7e5339e..bfe5e55 100644
--- a/compiler/optimizing/intrinsics_list.h
+++ b/compiler/optimizing/intrinsics_list.h
@@ -29,9 +29,15 @@
   V(IntegerReverse, kStatic, kNeedsEnvironmentOrCache) \
   V(IntegerReverseBytes, kStatic, kNeedsEnvironmentOrCache) \
   V(IntegerNumberOfLeadingZeros, kStatic, kNeedsEnvironmentOrCache) \
+  V(IntegerNumberOfTrailingZeros, kStatic, kNeedsEnvironmentOrCache) \
+  V(IntegerRotateRight, kStatic, kNeedsEnvironmentOrCache) \
+  V(IntegerRotateLeft, kStatic, kNeedsEnvironmentOrCache) \
   V(LongReverse, kStatic, kNeedsEnvironmentOrCache) \
   V(LongReverseBytes, kStatic, kNeedsEnvironmentOrCache) \
   V(LongNumberOfLeadingZeros, kStatic, kNeedsEnvironmentOrCache) \
+  V(LongNumberOfTrailingZeros, kStatic, kNeedsEnvironmentOrCache) \
+  V(LongRotateRight, kStatic, kNeedsEnvironmentOrCache) \
+  V(LongRotateLeft, kStatic, kNeedsEnvironmentOrCache) \
   V(ShortReverseBytes, kStatic, kNeedsEnvironmentOrCache) \
   V(MathAbsDouble, kStatic, kNeedsEnvironmentOrCache) \
   V(MathAbsFloat, kStatic, kNeedsEnvironmentOrCache) \
diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc
index be076cd..c5d88d2 100644
--- a/compiler/optimizing/intrinsics_x86.cc
+++ b/compiler/optimizing/intrinsics_x86.cc
@@ -141,11 +141,10 @@
     if (invoke_->IsInvokeStaticOrDirect()) {
       codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(),
                                           Location::RegisterLocation(EAX));
-      codegen->RecordPcInfo(invoke_, invoke_->GetDexPc(), this);
     } else {
-      UNIMPLEMENTED(FATAL) << "Non-direct intrinsic slow-path not yet implemented";
-      UNREACHABLE();
+      codegen->GenerateVirtualCall(invoke_->AsInvokeVirtual(), Location::RegisterLocation(EAX));
     }
+    codegen->RecordPcInfo(invoke_, invoke_->GetDexPc(), this);
 
     // Copy the result back to the expected output.
     Location out = invoke_->GetLocations()->Out();
@@ -1957,6 +1956,12 @@
 UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck)
 UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar)
 UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent)
+UNIMPLEMENTED_INTRINSIC(IntegerNumberOfTrailingZeros)
+UNIMPLEMENTED_INTRINSIC(LongNumberOfTrailingZeros)
+UNIMPLEMENTED_INTRINSIC(IntegerRotateRight)
+UNIMPLEMENTED_INTRINSIC(LongRotateRight)
+UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft)
+UNIMPLEMENTED_INTRINSIC(LongRotateLeft)
 
 #undef UNIMPLEMENTED_INTRINSIC
 
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc
index 1f35b59..258ae9a 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -132,11 +132,10 @@
     if (invoke_->IsInvokeStaticOrDirect()) {
       codegen->GenerateStaticOrDirectCall(
           invoke_->AsInvokeStaticOrDirect(), Location::RegisterLocation(RDI));
-      codegen->RecordPcInfo(invoke_, invoke_->GetDexPc(), this);
     } else {
-      UNIMPLEMENTED(FATAL) << "Non-direct intrinsic slow-path not yet implemented";
-      UNREACHABLE();
+      codegen->GenerateVirtualCall(invoke_->AsInvokeVirtual(), Location::RegisterLocation(RDI));
     }
+    codegen->RecordPcInfo(invoke_, invoke_->GetDexPc(), this);
 
     // Copy the result back to the expected output.
     Location out = invoke_->GetLocations()->Out();
@@ -1775,6 +1774,12 @@
 UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck)
 UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar)
 UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent)
+UNIMPLEMENTED_INTRINSIC(IntegerNumberOfTrailingZeros)
+UNIMPLEMENTED_INTRINSIC(LongNumberOfTrailingZeros)
+UNIMPLEMENTED_INTRINSIC(IntegerRotateRight)
+UNIMPLEMENTED_INTRINSIC(LongRotateRight)
+UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft)
+UNIMPLEMENTED_INTRINSIC(LongRotateLeft)
 
 #undef UNIMPLEMENTED_INTRINSIC
 
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 62a460a..b3cf0b3 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -206,7 +206,7 @@
   // Insert a new node between `block` and `successor` to split the
   // critical edge.
   HBasicBlock* new_block = SplitEdge(block, successor);
-  new_block->AddInstruction(new (arena_) HGoto());
+  new_block->AddInstruction(new (arena_) HGoto(successor->GetDexPc()));
   if (successor->IsLoopHeader()) {
     // If we split at a back edge boundary, make the new block the back edge.
     HLoopInformation* info = successor->GetLoopInformation();
@@ -227,7 +227,7 @@
   if (number_of_incomings != 1) {
     HBasicBlock* pre_header = new (arena_) HBasicBlock(this, header->GetDexPc());
     AddBlock(pre_header);
-    pre_header->AddInstruction(new (arena_) HGoto());
+    pre_header->AddInstruction(new (arena_) HGoto(header->GetDexPc()));
 
     for (size_t pred = 0; pred < header->GetPredecessors().size(); ++pred) {
       HBasicBlock* predecessor = header->GetPredecessor(pred);
@@ -408,12 +408,12 @@
   }
 }
 
-HNullConstant* HGraph::GetNullConstant() {
+HNullConstant* HGraph::GetNullConstant(uint32_t dex_pc) {
   // For simplicity, don't bother reviving the cached null constant if it is
   // not null and not in a block. Otherwise, we need to clear the instruction
   // id and/or any invariants the graph is assuming when adding new instructions.
   if ((cached_null_constant_ == nullptr) || (cached_null_constant_->GetBlock() == nullptr)) {
-    cached_null_constant_ = new (arena_) HNullConstant();
+    cached_null_constant_ = new (arena_) HNullConstant(dex_pc);
     InsertConstant(cached_null_constant_);
   }
   return cached_null_constant_;
@@ -425,7 +425,8 @@
   // id and/or any invariants the graph is assuming when adding new instructions.
   if ((cached_current_method_ == nullptr) || (cached_current_method_->GetBlock() == nullptr)) {
     cached_current_method_ = new (arena_) HCurrentMethod(
-        Is64BitInstructionSet(instruction_set_) ? Primitive::kPrimLong : Primitive::kPrimInt);
+        Is64BitInstructionSet(instruction_set_) ? Primitive::kPrimLong : Primitive::kPrimInt,
+        entry_block_->GetDexPc());
     if (entry_block_->GetFirstInstruction() == nullptr) {
       entry_block_->AddInstruction(cached_current_method_);
     } else {
@@ -436,7 +437,7 @@
   return cached_current_method_;
 }
 
-HConstant* HGraph::GetConstant(Primitive::Type type, int64_t value) {
+HConstant* HGraph::GetConstant(Primitive::Type type, int64_t value, uint32_t dex_pc) {
   switch (type) {
     case Primitive::Type::kPrimBoolean:
       DCHECK(IsUint<1>(value));
@@ -446,10 +447,10 @@
     case Primitive::Type::kPrimShort:
     case Primitive::Type::kPrimInt:
       DCHECK(IsInt(Primitive::ComponentSize(type) * kBitsPerByte, value));
-      return GetIntConstant(static_cast<int32_t>(value));
+      return GetIntConstant(static_cast<int32_t>(value), dex_pc);
 
     case Primitive::Type::kPrimLong:
-      return GetLongConstant(value);
+      return GetLongConstant(value, dex_pc);
 
     default:
       LOG(FATAL) << "Unsupported constant type";
@@ -943,11 +944,11 @@
     int32_t value = GetInput()->AsIntConstant()->GetValue();
     switch (GetResultType()) {
       case Primitive::kPrimLong:
-        return graph->GetLongConstant(static_cast<int64_t>(value));
+        return graph->GetLongConstant(static_cast<int64_t>(value), GetDexPc());
       case Primitive::kPrimFloat:
-        return graph->GetFloatConstant(static_cast<float>(value));
+        return graph->GetFloatConstant(static_cast<float>(value), GetDexPc());
       case Primitive::kPrimDouble:
-        return graph->GetDoubleConstant(static_cast<double>(value));
+        return graph->GetDoubleConstant(static_cast<double>(value), GetDexPc());
       default:
         return nullptr;
     }
@@ -955,11 +956,11 @@
     int64_t value = GetInput()->AsLongConstant()->GetValue();
     switch (GetResultType()) {
       case Primitive::kPrimInt:
-        return graph->GetIntConstant(static_cast<int32_t>(value));
+        return graph->GetIntConstant(static_cast<int32_t>(value), GetDexPc());
       case Primitive::kPrimFloat:
-        return graph->GetFloatConstant(static_cast<float>(value));
+        return graph->GetFloatConstant(static_cast<float>(value), GetDexPc());
       case Primitive::kPrimDouble:
-        return graph->GetDoubleConstant(static_cast<double>(value));
+        return graph->GetDoubleConstant(static_cast<double>(value), GetDexPc());
       default:
         return nullptr;
     }
@@ -968,22 +969,22 @@
     switch (GetResultType()) {
       case Primitive::kPrimInt:
         if (std::isnan(value))
-          return graph->GetIntConstant(0);
+          return graph->GetIntConstant(0, GetDexPc());
         if (value >= kPrimIntMax)
-          return graph->GetIntConstant(kPrimIntMax);
+          return graph->GetIntConstant(kPrimIntMax, GetDexPc());
         if (value <= kPrimIntMin)
-          return graph->GetIntConstant(kPrimIntMin);
-        return graph->GetIntConstant(static_cast<int32_t>(value));
+          return graph->GetIntConstant(kPrimIntMin, GetDexPc());
+        return graph->GetIntConstant(static_cast<int32_t>(value), GetDexPc());
       case Primitive::kPrimLong:
         if (std::isnan(value))
-          return graph->GetLongConstant(0);
+          return graph->GetLongConstant(0, GetDexPc());
         if (value >= kPrimLongMax)
-          return graph->GetLongConstant(kPrimLongMax);
+          return graph->GetLongConstant(kPrimLongMax, GetDexPc());
         if (value <= kPrimLongMin)
-          return graph->GetLongConstant(kPrimLongMin);
-        return graph->GetLongConstant(static_cast<int64_t>(value));
+          return graph->GetLongConstant(kPrimLongMin, GetDexPc());
+        return graph->GetLongConstant(static_cast<int64_t>(value), GetDexPc());
       case Primitive::kPrimDouble:
-        return graph->GetDoubleConstant(static_cast<double>(value));
+        return graph->GetDoubleConstant(static_cast<double>(value), GetDexPc());
       default:
         return nullptr;
     }
@@ -992,22 +993,22 @@
     switch (GetResultType()) {
       case Primitive::kPrimInt:
         if (std::isnan(value))
-          return graph->GetIntConstant(0);
+          return graph->GetIntConstant(0, GetDexPc());
         if (value >= kPrimIntMax)
-          return graph->GetIntConstant(kPrimIntMax);
+          return graph->GetIntConstant(kPrimIntMax, GetDexPc());
         if (value <= kPrimLongMin)
-          return graph->GetIntConstant(kPrimIntMin);
-        return graph->GetIntConstant(static_cast<int32_t>(value));
+          return graph->GetIntConstant(kPrimIntMin, GetDexPc());
+        return graph->GetIntConstant(static_cast<int32_t>(value), GetDexPc());
       case Primitive::kPrimLong:
         if (std::isnan(value))
-          return graph->GetLongConstant(0);
+          return graph->GetLongConstant(0, GetDexPc());
         if (value >= kPrimLongMax)
-          return graph->GetLongConstant(kPrimLongMax);
+          return graph->GetLongConstant(kPrimLongMax, GetDexPc());
         if (value <= kPrimLongMin)
-          return graph->GetLongConstant(kPrimLongMin);
-        return graph->GetLongConstant(static_cast<int64_t>(value));
+          return graph->GetLongConstant(kPrimLongMin, GetDexPc());
+        return graph->GetLongConstant(static_cast<int64_t>(value), GetDexPc());
       case Primitive::kPrimFloat:
-        return graph->GetFloatConstant(static_cast<float>(value));
+        return graph->GetFloatConstant(static_cast<float>(value), GetDexPc());
       default:
         return nullptr;
     }
@@ -1121,7 +1122,8 @@
   DCHECK(!graph_->IsInSsaForm()) << "Support for SSA form not implemented";
   DCHECK_EQ(cursor->GetBlock(), this);
 
-  HBasicBlock* new_block = new (GetGraph()->GetArena()) HBasicBlock(GetGraph(), GetDexPc());
+  HBasicBlock* new_block = new (GetGraph()->GetArena()) HBasicBlock(GetGraph(),
+                                                                    cursor->GetDexPc());
   new_block->instructions_.first_instruction_ = cursor;
   new_block->instructions_.last_instruction_ = instructions_.last_instruction_;
   instructions_.last_instruction_ = cursor->previous_;
@@ -1133,7 +1135,7 @@
   }
 
   new_block->instructions_.SetBlockOfInstructions(new_block);
-  AddInstruction(new (GetGraph()->GetArena()) HGoto());
+  AddInstruction(new (GetGraph()->GetArena()) HGoto(new_block->GetDexPc()));
 
   for (HBasicBlock* successor : GetSuccessors()) {
     new_block->successors_.push_back(successor);
@@ -1304,7 +1306,7 @@
     predecessor->RemoveSuccessor(this);
     if (predecessor->GetSuccessors().size() == 1u) {
       DCHECK(last_instruction->IsIf());
-      predecessor->AddInstruction(new (graph_->GetArena()) HGoto());
+      predecessor->AddInstruction(new (graph_->GetArena()) HGoto(last_instruction->GetDexPc()));
     } else {
       // The predecessor has no remaining successors and therefore must be dead.
       // We deliberately leave it without a control-flow instruction so that the
@@ -1551,13 +1553,13 @@
       if (!returns_void) {
         return_value = last->InputAt(0);
       }
-      predecessor->AddInstruction(new (allocator) HGoto());
+      predecessor->AddInstruction(new (allocator) HGoto(last->GetDexPc()));
       predecessor->RemoveInstruction(last);
     } else {
       if (!returns_void) {
         // There will be multiple returns.
         return_value = new (allocator) HPhi(
-            allocator, kNoRegNumber, 0, HPhi::ToPhiType(invoke->GetType()));
+            allocator, kNoRegNumber, 0, HPhi::ToPhiType(invoke->GetType()), to->GetDexPc());
         to->AddPhi(return_value->AsPhi());
       }
       for (HBasicBlock* predecessor : to->GetPredecessors()) {
@@ -1565,7 +1567,7 @@
         if (!returns_void) {
           return_value->AsPhi()->AddInput(last->InputAt(0));
         }
-        predecessor->AddInstruction(new (allocator) HGoto());
+        predecessor->AddInstruction(new (allocator) HGoto(last->GetDexPc()));
         predecessor->RemoveInstruction(last);
       }
     }
@@ -1647,15 +1649,19 @@
   for (HInstructionIterator it(entry_block_->GetInstructions()); !it.Done(); it.Advance()) {
     HInstruction* current = it.Current();
     if (current->IsNullConstant()) {
-      current->ReplaceWith(outer_graph->GetNullConstant());
+      current->ReplaceWith(outer_graph->GetNullConstant(current->GetDexPc()));
     } else if (current->IsIntConstant()) {
-      current->ReplaceWith(outer_graph->GetIntConstant(current->AsIntConstant()->GetValue()));
+      current->ReplaceWith(outer_graph->GetIntConstant(
+          current->AsIntConstant()->GetValue(), current->GetDexPc()));
     } else if (current->IsLongConstant()) {
-      current->ReplaceWith(outer_graph->GetLongConstant(current->AsLongConstant()->GetValue()));
+      current->ReplaceWith(outer_graph->GetLongConstant(
+          current->AsLongConstant()->GetValue(), current->GetDexPc()));
     } else if (current->IsFloatConstant()) {
-      current->ReplaceWith(outer_graph->GetFloatConstant(current->AsFloatConstant()->GetValue()));
+      current->ReplaceWith(outer_graph->GetFloatConstant(
+          current->AsFloatConstant()->GetValue(), current->GetDexPc()));
     } else if (current->IsDoubleConstant()) {
-      current->ReplaceWith(outer_graph->GetDoubleConstant(current->AsDoubleConstant()->GetValue()));
+      current->ReplaceWith(outer_graph->GetDoubleConstant(
+          current->AsDoubleConstant()->GetValue(), current->GetDexPc()));
     } else if (current->IsParameterValue()) {
       if (kIsDebugBuild
           && invoke->IsInvokeStaticOrDirect()
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 06a65e3..5ec3f22 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -79,6 +79,8 @@
 
 static constexpr InvokeType kInvalidInvokeType = static_cast<InvokeType>(-1);
 
+static constexpr uint32_t kNoDexPc = -1;
+
 enum IfCondition {
   kCondEQ,
   kCondNE,
@@ -318,24 +320,24 @@
   // Returns a constant of the given type and value. If it does not exist
   // already, it is created and inserted into the graph. This method is only for
   // integral types.
-  HConstant* GetConstant(Primitive::Type type, int64_t value);
+  HConstant* GetConstant(Primitive::Type type, int64_t value, uint32_t dex_pc = kNoDexPc);
 
   // TODO: This is problematic for the consistency of reference type propagation
   // because it can be created anytime after the pass and thus it will be left
   // with an invalid type.
-  HNullConstant* GetNullConstant();
+  HNullConstant* GetNullConstant(uint32_t dex_pc = kNoDexPc);
 
-  HIntConstant* GetIntConstant(int32_t value) {
-    return CreateConstant(value, &cached_int_constants_);
+  HIntConstant* GetIntConstant(int32_t value, uint32_t dex_pc = kNoDexPc) {
+    return CreateConstant(value, &cached_int_constants_, dex_pc);
   }
-  HLongConstant* GetLongConstant(int64_t value) {
-    return CreateConstant(value, &cached_long_constants_);
+  HLongConstant* GetLongConstant(int64_t value, uint32_t dex_pc = kNoDexPc) {
+    return CreateConstant(value, &cached_long_constants_, dex_pc);
   }
-  HFloatConstant* GetFloatConstant(float value) {
-    return CreateConstant(bit_cast<int32_t, float>(value), &cached_float_constants_);
+  HFloatConstant* GetFloatConstant(float value, uint32_t dex_pc = kNoDexPc) {
+    return CreateConstant(bit_cast<int32_t, float>(value), &cached_float_constants_, dex_pc);
   }
-  HDoubleConstant* GetDoubleConstant(double value) {
-    return CreateConstant(bit_cast<int64_t, double>(value), &cached_double_constants_);
+  HDoubleConstant* GetDoubleConstant(double value, uint32_t dex_pc = kNoDexPc) {
+    return CreateConstant(bit_cast<int64_t, double>(value), &cached_double_constants_, dex_pc);
   }
 
   HCurrentMethod* GetCurrentMethod();
@@ -374,7 +376,8 @@
 
   template <class InstructionType, typename ValueType>
   InstructionType* CreateConstant(ValueType value,
-                                  ArenaSafeMap<ValueType, InstructionType*>* cache) {
+                                  ArenaSafeMap<ValueType, InstructionType*>* cache,
+                                  uint32_t dex_pc = kNoDexPc) {
     // Try to find an existing constant of the given value.
     InstructionType* constant = nullptr;
     auto cached_constant = cache->find(value);
@@ -385,7 +388,7 @@
     // If not found or previously deleted, create and cache a new instruction.
     // Don't bother reviving a previously deleted instruction, for simplicity.
     if (constant == nullptr || constant->GetBlock() == nullptr) {
-      constant = new (arena_) InstructionType(value);
+      constant = new (arena_) InstructionType(value, dex_pc);
       cache->Overwrite(value, constant);
       InsertConstant(constant);
     }
@@ -620,7 +623,6 @@
 };
 
 static constexpr size_t kNoLifetime = -1;
-static constexpr uint32_t kNoDexPc = -1;
 
 // A block in a method. Contains the list of instructions represented
 // as a double linked list. Each block knows its predecessors and
@@ -628,7 +630,7 @@
 
 class HBasicBlock : public ArenaObject<kArenaAllocBasicBlock> {
  public:
-  explicit HBasicBlock(HGraph* graph, uint32_t dex_pc = kNoDexPc)
+  HBasicBlock(HGraph* graph, uint32_t dex_pc = kNoDexPc)
       : graph_(graph),
         predecessors_(graph->GetArena()->Adapter(kArenaAllocPredecessors)),
         successors_(graph->GetArena()->Adapter(kArenaAllocSuccessors)),
@@ -703,6 +705,7 @@
 
   int GetBlockId() const { return block_id_; }
   void SetBlockId(int id) { block_id_ = id; }
+  uint32_t GetDexPc() const { return dex_pc_; }
 
   HBasicBlock* GetDominator() const { return dominator_; }
   void SetDominator(HBasicBlock* dominator) { dominator_ = dominator; }
@@ -942,7 +945,6 @@
   void SetLifetimeStart(size_t start) { lifetime_start_ = start; }
   void SetLifetimeEnd(size_t end) { lifetime_end_ = end; }
 
-  uint32_t GetDexPc() const { return dex_pc_; }
 
   bool EndsWithControlFlowInstruction() const;
   bool EndsWithIf() const;
@@ -1075,7 +1077,9 @@
 
 #define FOR_EACH_CONCRETE_INSTRUCTION_MIPS64(M)
 
-#define FOR_EACH_CONCRETE_INSTRUCTION_X86(M)
+#define FOR_EACH_CONCRETE_INSTRUCTION_X86(M)                            \
+  M(X86ComputeBaseMethodAddress, Instruction)                           \
+  M(X86LoadFromConstantTable, Instruction)
 
 #define FOR_EACH_CONCRETE_INSTRUCTION_X86_64(M)
 
@@ -1688,10 +1692,11 @@
 
 class HInstruction : public ArenaObject<kArenaAllocInstruction> {
  public:
-  explicit HInstruction(SideEffects side_effects)
+  HInstruction(SideEffects side_effects, uint32_t dex_pc = kNoDexPc)
       : previous_(nullptr),
         next_(nullptr),
         block_(nullptr),
+        dex_pc_(dex_pc),
         id_(-1),
         ssa_index_(-1),
         environment_(nullptr),
@@ -1734,9 +1739,9 @@
   }
 
   virtual bool NeedsEnvironment() const { return false; }
-  virtual uint32_t GetDexPc() const {
-    return kNoDexPc;
-  }
+
+  uint32_t GetDexPc() const { return dex_pc_; }
+
   virtual bool IsControlFlow() const { return false; }
 
   virtual bool CanThrow() const { return false; }
@@ -1939,6 +1944,7 @@
   HInstruction* previous_;
   HInstruction* next_;
   HBasicBlock* block_;
+  const uint32_t dex_pc_;
 
   // An instruction gets an id when it is added to the graph.
   // It reflects creation order. A negative id means the instruction
@@ -2043,8 +2049,8 @@
 template<size_t N>
 class HTemplateInstruction: public HInstruction {
  public:
-  HTemplateInstruction<N>(SideEffects side_effects)
-      : HInstruction(side_effects), inputs_() {}
+  HTemplateInstruction<N>(SideEffects side_effects, uint32_t dex_pc = kNoDexPc)
+      : HInstruction(side_effects, dex_pc), inputs_() {}
   virtual ~HTemplateInstruction() {}
 
   size_t InputCount() const OVERRIDE { return N; }
@@ -2070,7 +2076,9 @@
 template<>
 class HTemplateInstruction<0>: public HInstruction {
  public:
-  explicit HTemplateInstruction(SideEffects side_effects) : HInstruction(side_effects) {}
+  explicit HTemplateInstruction<0>(SideEffects side_effects, uint32_t dex_pc = kNoDexPc)
+      : HInstruction(side_effects, dex_pc) {}
+
   virtual ~HTemplateInstruction() {}
 
   size_t InputCount() const OVERRIDE { return 0; }
@@ -2094,8 +2102,8 @@
 template<intptr_t N>
 class HExpression : public HTemplateInstruction<N> {
  public:
-  HExpression<N>(Primitive::Type type, SideEffects side_effects)
-      : HTemplateInstruction<N>(side_effects), type_(type) {}
+  HExpression<N>(Primitive::Type type, SideEffects side_effects, uint32_t dex_pc = kNoDexPc)
+      : HTemplateInstruction<N>(side_effects, dex_pc), type_(type) {}
   virtual ~HExpression() {}
 
   Primitive::Type GetType() const OVERRIDE { return type_; }
@@ -2108,7 +2116,8 @@
 // instruction that branches to the exit block.
 class HReturnVoid : public HTemplateInstruction<0> {
  public:
-  HReturnVoid() : HTemplateInstruction(SideEffects::None()) {}
+  explicit HReturnVoid(uint32_t dex_pc = kNoDexPc)
+      : HTemplateInstruction(SideEffects::None(), dex_pc) {}
 
   bool IsControlFlow() const OVERRIDE { return true; }
 
@@ -2122,7 +2131,8 @@
 // instruction that branches to the exit block.
 class HReturn : public HTemplateInstruction<1> {
  public:
-  explicit HReturn(HInstruction* value) : HTemplateInstruction(SideEffects::None()) {
+  explicit HReturn(HInstruction* value, uint32_t dex_pc = kNoDexPc)
+      : HTemplateInstruction(SideEffects::None(), dex_pc) {
     SetRawInputAt(0, value);
   }
 
@@ -2139,7 +2149,7 @@
 // exit block.
 class HExit : public HTemplateInstruction<0> {
  public:
-  HExit() : HTemplateInstruction(SideEffects::None()) {}
+  explicit HExit(uint32_t dex_pc = kNoDexPc) : HTemplateInstruction(SideEffects::None(), dex_pc) {}
 
   bool IsControlFlow() const OVERRIDE { return true; }
 
@@ -2152,7 +2162,7 @@
 // Jumps from one block to another.
 class HGoto : public HTemplateInstruction<0> {
  public:
-  HGoto() : HTemplateInstruction(SideEffects::None()) {}
+  explicit HGoto(uint32_t dex_pc = kNoDexPc) : HTemplateInstruction(SideEffects::None(), dex_pc) {}
 
   bool IsControlFlow() const OVERRIDE { return true; }
 
@@ -2168,7 +2178,8 @@
 
 class HConstant : public HExpression<0> {
  public:
-  explicit HConstant(Primitive::Type type) : HExpression(type, SideEffects::None()) {}
+  explicit HConstant(Primitive::Type type, uint32_t dex_pc = kNoDexPc)
+      : HExpression(type, SideEffects::None(), dex_pc) {}
 
   bool CanBeMoved() const OVERRIDE { return true; }
 
@@ -2193,7 +2204,7 @@
   DECLARE_INSTRUCTION(NullConstant);
 
  private:
-  HNullConstant() : HConstant(Primitive::kPrimNot) {}
+  explicit HNullConstant(uint32_t dex_pc = kNoDexPc) : HConstant(Primitive::kPrimNot, dex_pc) {}
 
   friend class HGraph;
   DISALLOW_COPY_AND_ASSIGN(HNullConstant);
@@ -2219,8 +2230,10 @@
   DECLARE_INSTRUCTION(IntConstant);
 
  private:
-  explicit HIntConstant(int32_t value) : HConstant(Primitive::kPrimInt), value_(value) {}
-  explicit HIntConstant(bool value) : HConstant(Primitive::kPrimInt), value_(value ? 1 : 0) {}
+  explicit HIntConstant(int32_t value, uint32_t dex_pc = kNoDexPc)
+      : HConstant(Primitive::kPrimInt, dex_pc), value_(value) {}
+  explicit HIntConstant(bool value, uint32_t dex_pc = kNoDexPc)
+      : HConstant(Primitive::kPrimInt, dex_pc), value_(value ? 1 : 0) {}
 
   const int32_t value_;
 
@@ -2248,7 +2261,8 @@
   DECLARE_INSTRUCTION(LongConstant);
 
  private:
-  explicit HLongConstant(int64_t value) : HConstant(Primitive::kPrimLong), value_(value) {}
+  explicit HLongConstant(int64_t value, uint32_t dex_pc = kNoDexPc)
+      : HConstant(Primitive::kPrimLong, dex_pc), value_(value) {}
 
   const int64_t value_;
 
@@ -2260,7 +2274,8 @@
 // two successors.
 class HIf : public HTemplateInstruction<1> {
  public:
-  explicit HIf(HInstruction* input) : HTemplateInstruction(SideEffects::None()) {
+  explicit HIf(HInstruction* input, uint32_t dex_pc = kNoDexPc)
+      : HTemplateInstruction(SideEffects::None(), dex_pc) {
     SetRawInputAt(0, input);
   }
 
@@ -2293,8 +2308,8 @@
     kExit,
   };
 
-  explicit HTryBoundary(BoundaryKind kind)
-      : HTemplateInstruction(SideEffects::None()), kind_(kind) {}
+  explicit HTryBoundary(BoundaryKind kind, uint32_t dex_pc = kNoDexPc)
+      : HTemplateInstruction(SideEffects::None(), dex_pc), kind_(kind) {}
 
   bool IsControlFlow() const OVERRIDE { return true; }
 
@@ -2350,21 +2365,17 @@
 // Deoptimize to interpreter, upon checking a condition.
 class HDeoptimize : public HTemplateInstruction<1> {
  public:
-  HDeoptimize(HInstruction* cond, uint32_t dex_pc)
-      : HTemplateInstruction(SideEffects::None()),
-        dex_pc_(dex_pc) {
+  explicit HDeoptimize(HInstruction* cond, uint32_t dex_pc)
+      : HTemplateInstruction(SideEffects::None(), dex_pc) {
     SetRawInputAt(0, cond);
   }
 
   bool NeedsEnvironment() const OVERRIDE { return true; }
   bool CanThrow() const OVERRIDE { return true; }
-  uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }
 
   DECLARE_INSTRUCTION(Deoptimize);
 
  private:
-  uint32_t dex_pc_;
-
   DISALLOW_COPY_AND_ASSIGN(HDeoptimize);
 };
 
@@ -2373,7 +2384,8 @@
 // instructions that work with the dex cache.
 class HCurrentMethod : public HExpression<0> {
  public:
-  explicit HCurrentMethod(Primitive::Type type) : HExpression(type, SideEffects::None()) {}
+  explicit HCurrentMethod(Primitive::Type type, uint32_t dex_pc = kNoDexPc)
+      : HExpression(type, SideEffects::None(), dex_pc) {}
 
   DECLARE_INSTRUCTION(CurrentMethod);
 
@@ -2383,8 +2395,8 @@
 
 class HUnaryOperation : public HExpression<1> {
  public:
-  HUnaryOperation(Primitive::Type result_type, HInstruction* input)
-      : HExpression(result_type, SideEffects::None()) {
+  HUnaryOperation(Primitive::Type result_type, HInstruction* input, uint32_t dex_pc = kNoDexPc)
+      : HExpression(result_type, SideEffects::None(), dex_pc) {
     SetRawInputAt(0, input);
   }
 
@@ -2417,8 +2429,9 @@
   HBinaryOperation(Primitive::Type result_type,
                    HInstruction* left,
                    HInstruction* right,
-                   SideEffects side_effects = SideEffects::None())
-      : HExpression(result_type, side_effects) {
+                   SideEffects side_effects = SideEffects::None(),
+                   uint32_t dex_pc = kNoDexPc)
+      : HExpression(result_type, side_effects, dex_pc) {
     SetRawInputAt(0, left);
     SetRawInputAt(1, right);
   }
@@ -2510,8 +2523,8 @@
 
 class HCondition : public HBinaryOperation {
  public:
-  HCondition(HInstruction* first, HInstruction* second)
-      : HBinaryOperation(Primitive::kPrimBoolean, first, second),
+  HCondition(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc)
+      : HBinaryOperation(Primitive::kPrimBoolean, first, second, SideEffects::None(), dex_pc),
         needs_materialization_(true),
         bias_(ComparisonBias::kNoBias) {}
 
@@ -2562,18 +2575,20 @@
 // Instruction to check if two inputs are equal to each other.
 class HEqual : public HCondition {
  public:
-  HEqual(HInstruction* first, HInstruction* second)
-      : HCondition(first, second) {}
+  HEqual(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc)
+      : HCondition(first, second, dex_pc) {}
 
   bool IsCommutative() const OVERRIDE { return true; }
 
   template <typename T> bool Compute(T x, T y) const { return x == y; }
 
   HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
-    return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue()));
+    return GetBlock()->GetGraph()->GetIntConstant(
+        Compute(x->GetValue(), y->GetValue()), GetDexPc());
   }
   HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
-    return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue()));
+    return GetBlock()->GetGraph()->GetIntConstant(
+        Compute(x->GetValue(), y->GetValue()), GetDexPc());
   }
 
   DECLARE_INSTRUCTION(Equal);
@@ -2592,18 +2607,20 @@
 
 class HNotEqual : public HCondition {
  public:
-  HNotEqual(HInstruction* first, HInstruction* second)
-      : HCondition(first, second) {}
+  HNotEqual(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc)
+      : HCondition(first, second, dex_pc) {}
 
   bool IsCommutative() const OVERRIDE { return true; }
 
   template <typename T> bool Compute(T x, T y) const { return x != y; }
 
   HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
-    return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue()));
+    return GetBlock()->GetGraph()->GetIntConstant(
+        Compute(x->GetValue(), y->GetValue()), GetDexPc());
   }
   HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
-    return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue()));
+    return GetBlock()->GetGraph()->GetIntConstant(
+        Compute(x->GetValue(), y->GetValue()), GetDexPc());
   }
 
   DECLARE_INSTRUCTION(NotEqual);
@@ -2622,16 +2639,18 @@
 
 class HLessThan : public HCondition {
  public:
-  HLessThan(HInstruction* first, HInstruction* second)
-      : HCondition(first, second) {}
+  HLessThan(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc)
+      : HCondition(first, second, dex_pc) {}
 
   template <typename T> bool Compute(T x, T y) const { return x < y; }
 
   HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
-    return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue()));
+    return GetBlock()->GetGraph()->GetIntConstant(
+        Compute(x->GetValue(), y->GetValue()), GetDexPc());
   }
   HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
-    return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue()));
+    return GetBlock()->GetGraph()->GetIntConstant(
+        Compute(x->GetValue(), y->GetValue()), GetDexPc());
   }
 
   DECLARE_INSTRUCTION(LessThan);
@@ -2650,16 +2669,18 @@
 
 class HLessThanOrEqual : public HCondition {
  public:
-  HLessThanOrEqual(HInstruction* first, HInstruction* second)
-      : HCondition(first, second) {}
+  HLessThanOrEqual(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc)
+      : HCondition(first, second, dex_pc) {}
 
   template <typename T> bool Compute(T x, T y) const { return x <= y; }
 
   HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
-    return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue()));
+    return GetBlock()->GetGraph()->GetIntConstant(
+        Compute(x->GetValue(), y->GetValue()), GetDexPc());
   }
   HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
-    return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue()));
+    return GetBlock()->GetGraph()->GetIntConstant(
+        Compute(x->GetValue(), y->GetValue()), GetDexPc());
   }
 
   DECLARE_INSTRUCTION(LessThanOrEqual);
@@ -2678,16 +2699,18 @@
 
 class HGreaterThan : public HCondition {
  public:
-  HGreaterThan(HInstruction* first, HInstruction* second)
-      : HCondition(first, second) {}
+  HGreaterThan(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc)
+      : HCondition(first, second, dex_pc) {}
 
   template <typename T> bool Compute(T x, T y) const { return x > y; }
 
   HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
-    return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue()));
+    return GetBlock()->GetGraph()->GetIntConstant(
+        Compute(x->GetValue(), y->GetValue()), GetDexPc());
   }
   HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
-    return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue()));
+    return GetBlock()->GetGraph()->GetIntConstant(
+        Compute(x->GetValue(), y->GetValue()), GetDexPc());
   }
 
   DECLARE_INSTRUCTION(GreaterThan);
@@ -2706,16 +2729,18 @@
 
 class HGreaterThanOrEqual : public HCondition {
  public:
-  HGreaterThanOrEqual(HInstruction* first, HInstruction* second)
-      : HCondition(first, second) {}
+  HGreaterThanOrEqual(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc)
+      : HCondition(first, second, dex_pc) {}
 
   template <typename T> bool Compute(T x, T y) const { return x >= y; }
 
   HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
-    return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue()));
+    return GetBlock()->GetGraph()->GetIntConstant(
+        Compute(x->GetValue(), y->GetValue()), GetDexPc());
   }
   HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
-    return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue()));
+    return GetBlock()->GetGraph()->GetIntConstant(
+        Compute(x->GetValue(), y->GetValue()), GetDexPc());
   }
 
   DECLARE_INSTRUCTION(GreaterThanOrEqual);
@@ -2742,9 +2767,12 @@
            HInstruction* second,
            ComparisonBias bias,
            uint32_t dex_pc)
-      : HBinaryOperation(Primitive::kPrimInt, first, second, SideEffectsForArchRuntimeCalls(type)),
-        bias_(bias),
-        dex_pc_(dex_pc) {
+      : HBinaryOperation(Primitive::kPrimInt,
+                         first,
+                         second,
+                         SideEffectsForArchRuntimeCalls(type),
+                         dex_pc),
+        bias_(bias) {
     DCHECK_EQ(type, first->GetType());
     DCHECK_EQ(type, second->GetType());
   }
@@ -2753,10 +2781,12 @@
   int32_t Compute(T x, T y) const { return x == y ? 0 : x > y ? 1 : -1; }
 
   HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
-    return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue()));
+    return GetBlock()->GetGraph()->GetIntConstant(
+        Compute(x->GetValue(), y->GetValue()), GetDexPc());
   }
   HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
-    return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue()));
+    return GetBlock()->GetGraph()->GetIntConstant(
+        Compute(x->GetValue(), y->GetValue()), GetDexPc());
   }
 
   bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
@@ -2767,7 +2797,6 @@
 
   bool IsGtBias() { return bias_ == ComparisonBias::kGtBias; }
 
-  uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }
 
   static SideEffects SideEffectsForArchRuntimeCalls(Primitive::Type type) {
     // MIPS64 uses a runtime call for FP comparisons.
@@ -2778,7 +2807,6 @@
 
  private:
   const ComparisonBias bias_;
-  const uint32_t dex_pc_;
 
   DISALLOW_COPY_AND_ASSIGN(HCompare);
 };
@@ -2787,7 +2815,7 @@
 class HLocal : public HTemplateInstruction<0> {
  public:
   explicit HLocal(uint16_t reg_number)
-      : HTemplateInstruction(SideEffects::None()), reg_number_(reg_number) {}
+      : HTemplateInstruction(SideEffects::None(), kNoDexPc), reg_number_(reg_number) {}
 
   DECLARE_INSTRUCTION(Local);
 
@@ -2803,8 +2831,8 @@
 // Load a given local. The local is an input of this instruction.
 class HLoadLocal : public HExpression<1> {
  public:
-  HLoadLocal(HLocal* local, Primitive::Type type)
-      : HExpression(type, SideEffects::None()) {
+  HLoadLocal(HLocal* local, Primitive::Type type, uint32_t dex_pc = kNoDexPc)
+      : HExpression(type, SideEffects::None(), dex_pc) {
     SetRawInputAt(0, local);
   }
 
@@ -2820,7 +2848,8 @@
 // and the local.
 class HStoreLocal : public HTemplateInstruction<2> {
  public:
-  HStoreLocal(HLocal* local, HInstruction* value) : HTemplateInstruction(SideEffects::None()) {
+  HStoreLocal(HLocal* local, HInstruction* value, uint32_t dex_pc = kNoDexPc)
+      : HTemplateInstruction(SideEffects::None(), dex_pc) {
     SetRawInputAt(0, local);
     SetRawInputAt(1, value);
   }
@@ -2861,9 +2890,10 @@
   DECLARE_INSTRUCTION(FloatConstant);
 
  private:
-  explicit HFloatConstant(float value) : HConstant(Primitive::kPrimFloat), value_(value) {}
-  explicit HFloatConstant(int32_t value)
-      : HConstant(Primitive::kPrimFloat), value_(bit_cast<float, int32_t>(value)) {}
+  explicit HFloatConstant(float value, uint32_t dex_pc = kNoDexPc)
+      : HConstant(Primitive::kPrimFloat, dex_pc), value_(value) {}
+  explicit HFloatConstant(int32_t value, uint32_t dex_pc = kNoDexPc)
+      : HConstant(Primitive::kPrimFloat, dex_pc), value_(bit_cast<float, int32_t>(value)) {}
 
   const float value_;
 
@@ -2901,9 +2931,10 @@
   DECLARE_INSTRUCTION(DoubleConstant);
 
  private:
-  explicit HDoubleConstant(double value) : HConstant(Primitive::kPrimDouble), value_(value) {}
-  explicit HDoubleConstant(int64_t value)
-      : HConstant(Primitive::kPrimDouble), value_(bit_cast<double, int64_t>(value)) {}
+  explicit HDoubleConstant(double value, uint32_t dex_pc = kNoDexPc)
+      : HConstant(Primitive::kPrimDouble, dex_pc), value_(value) {}
+  explicit HDoubleConstant(int64_t value, uint32_t dex_pc = kNoDexPc)
+      : HConstant(Primitive::kPrimDouble, dex_pc), value_(bit_cast<double, int64_t>(value)) {}
 
   const double value_;
 
@@ -2950,7 +2981,6 @@
 
   Primitive::Type GetType() const OVERRIDE { return return_type_; }
 
-  uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }
 
   uint32_t GetDexMethodIndex() const { return dex_method_index_; }
   const DexFile& GetDexFile() const { return GetEnvironment()->GetDexFile(); }
@@ -2983,11 +3013,10 @@
           uint32_t dex_method_index,
           InvokeType original_invoke_type)
     : HInstruction(
-          SideEffects::AllExceptGCDependency()),  // Assume write/read on all fields/arrays.
+          SideEffects::AllExceptGCDependency(), dex_pc),  // Assume write/read on all fields/arrays.
       number_of_arguments_(number_of_arguments),
       inputs_(arena, number_of_arguments),
       return_type_(return_type),
-      dex_pc_(dex_pc),
       dex_method_index_(dex_method_index),
       original_invoke_type_(original_invoke_type),
       intrinsic_(Intrinsics::kNone),
@@ -3004,7 +3033,6 @@
   uint32_t number_of_arguments_;
   GrowableArray<HUserRecord<HInstruction*> > inputs_;
   const Primitive::Type return_type_;
-  const uint32_t dex_pc_;
   const uint32_t dex_method_index_;
   const InvokeType original_invoke_type_;
   Intrinsics intrinsic_;
@@ -3305,15 +3333,13 @@
                uint16_t type_index,
                const DexFile& dex_file,
                QuickEntrypointEnum entrypoint)
-      : HExpression(Primitive::kPrimNot, SideEffects::CanTriggerGC()),
-        dex_pc_(dex_pc),
+      : HExpression(Primitive::kPrimNot, SideEffects::CanTriggerGC(), dex_pc),
         type_index_(type_index),
         dex_file_(dex_file),
         entrypoint_(entrypoint) {
     SetRawInputAt(0, current_method);
   }
 
-  uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }
   uint16_t GetTypeIndex() const { return type_index_; }
   const DexFile& GetDexFile() const { return dex_file_; }
 
@@ -3332,7 +3358,6 @@
   DECLARE_INSTRUCTION(NewInstance);
 
  private:
-  const uint32_t dex_pc_;
   const uint16_t type_index_;
   const DexFile& dex_file_;
   const QuickEntrypointEnum entrypoint_;
@@ -3342,16 +3367,16 @@
 
 class HNeg : public HUnaryOperation {
  public:
-  HNeg(Primitive::Type result_type, HInstruction* input)
-      : HUnaryOperation(result_type, input) {}
+  HNeg(Primitive::Type result_type, HInstruction* input, uint32_t dex_pc = kNoDexPc)
+      : HUnaryOperation(result_type, input, dex_pc) {}
 
   template <typename T> T Compute(T x) const { return -x; }
 
   HConstant* Evaluate(HIntConstant* x) const OVERRIDE {
-    return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue()));
+    return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue()), GetDexPc());
   }
   HConstant* Evaluate(HLongConstant* x) const OVERRIDE {
-    return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue()));
+    return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue()), GetDexPc());
   }
 
   DECLARE_INSTRUCTION(Neg);
@@ -3368,8 +3393,7 @@
             uint16_t type_index,
             const DexFile& dex_file,
             QuickEntrypointEnum entrypoint)
-      : HExpression(Primitive::kPrimNot, SideEffects::CanTriggerGC()),
-        dex_pc_(dex_pc),
+      : HExpression(Primitive::kPrimNot, SideEffects::CanTriggerGC(), dex_pc),
         type_index_(type_index),
         dex_file_(dex_file),
         entrypoint_(entrypoint) {
@@ -3377,7 +3401,6 @@
     SetRawInputAt(1, current_method);
   }
 
-  uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }
   uint16_t GetTypeIndex() const { return type_index_; }
   const DexFile& GetDexFile() const { return dex_file_; }
 
@@ -3394,7 +3417,6 @@
   DECLARE_INSTRUCTION(NewArray);
 
  private:
-  const uint32_t dex_pc_;
   const uint16_t type_index_;
   const DexFile& dex_file_;
   const QuickEntrypointEnum entrypoint_;
@@ -3404,18 +3426,23 @@
 
 class HAdd : public HBinaryOperation {
  public:
-  HAdd(Primitive::Type result_type, HInstruction* left, HInstruction* right)
-      : HBinaryOperation(result_type, left, right) {}
+  HAdd(Primitive::Type result_type,
+       HInstruction* left,
+       HInstruction* right,
+       uint32_t dex_pc = kNoDexPc)
+      : HBinaryOperation(result_type, left, right, SideEffects::None(), dex_pc) {}
 
   bool IsCommutative() const OVERRIDE { return true; }
 
   template <typename T> T Compute(T x, T y) const { return x + y; }
 
   HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
-    return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue()));
+    return GetBlock()->GetGraph()->GetIntConstant(
+        Compute(x->GetValue(), y->GetValue()), GetDexPc());
   }
   HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
-    return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue(), y->GetValue()));
+    return GetBlock()->GetGraph()->GetLongConstant(
+        Compute(x->GetValue(), y->GetValue()), GetDexPc());
   }
 
   DECLARE_INSTRUCTION(Add);
@@ -3426,16 +3453,21 @@
 
 class HSub : public HBinaryOperation {
  public:
-  HSub(Primitive::Type result_type, HInstruction* left, HInstruction* right)
-      : HBinaryOperation(result_type, left, right) {}
+  HSub(Primitive::Type result_type,
+       HInstruction* left,
+       HInstruction* right,
+       uint32_t dex_pc = kNoDexPc)
+      : HBinaryOperation(result_type, left, right, SideEffects::None(), dex_pc) {}
 
   template <typename T> T Compute(T x, T y) const { return x - y; }
 
   HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
-    return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue()));
+    return GetBlock()->GetGraph()->GetIntConstant(
+        Compute(x->GetValue(), y->GetValue()), GetDexPc());
   }
   HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
-    return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue(), y->GetValue()));
+    return GetBlock()->GetGraph()->GetLongConstant(
+        Compute(x->GetValue(), y->GetValue()), GetDexPc());
   }
 
   DECLARE_INSTRUCTION(Sub);
@@ -3446,18 +3478,23 @@
 
 class HMul : public HBinaryOperation {
  public:
-  HMul(Primitive::Type result_type, HInstruction* left, HInstruction* right)
-      : HBinaryOperation(result_type, left, right) {}
+  HMul(Primitive::Type result_type,
+       HInstruction* left,
+       HInstruction* right,
+       uint32_t dex_pc = kNoDexPc)
+      : HBinaryOperation(result_type, left, right, SideEffects::None(), dex_pc) {}
 
   bool IsCommutative() const OVERRIDE { return true; }
 
   template <typename T> T Compute(T x, T y) const { return x * y; }
 
   HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
-    return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue()));
+    return GetBlock()->GetGraph()->GetIntConstant(
+        Compute(x->GetValue(), y->GetValue()), GetDexPc());
   }
   HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
-    return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue(), y->GetValue()));
+    return GetBlock()->GetGraph()->GetLongConstant(
+        Compute(x->GetValue(), y->GetValue()), GetDexPc());
   }
 
   DECLARE_INSTRUCTION(Mul);
@@ -3468,9 +3505,11 @@
 
 class HDiv : public HBinaryOperation {
  public:
-  HDiv(Primitive::Type result_type, HInstruction* left, HInstruction* right, uint32_t dex_pc)
-      : HBinaryOperation(result_type, left, right, SideEffectsForArchRuntimeCalls()),
-        dex_pc_(dex_pc) {}
+  HDiv(Primitive::Type result_type,
+       HInstruction* left,
+       HInstruction* right,
+       uint32_t dex_pc)
+      : HBinaryOperation(result_type, left, right, SideEffectsForArchRuntimeCalls(), dex_pc) {}
 
   template <typename T>
   T Compute(T x, T y) const {
@@ -3482,14 +3521,14 @@
   }
 
   HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
-    return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue()));
+    return GetBlock()->GetGraph()->GetIntConstant(
+        Compute(x->GetValue(), y->GetValue()), GetDexPc());
   }
   HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
-    return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue(), y->GetValue()));
+    return GetBlock()->GetGraph()->GetLongConstant(
+        Compute(x->GetValue(), y->GetValue()), GetDexPc());
   }
 
-  uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }
-
   static SideEffects SideEffectsForArchRuntimeCalls() {
     // The generated code can use a runtime call.
     return SideEffects::CanTriggerGC();
@@ -3498,16 +3537,16 @@
   DECLARE_INSTRUCTION(Div);
 
  private:
-  const uint32_t dex_pc_;
-
   DISALLOW_COPY_AND_ASSIGN(HDiv);
 };
 
 class HRem : public HBinaryOperation {
  public:
-  HRem(Primitive::Type result_type, HInstruction* left, HInstruction* right, uint32_t dex_pc)
-      : HBinaryOperation(result_type, left, right, SideEffectsForArchRuntimeCalls()),
-        dex_pc_(dex_pc) {}
+  HRem(Primitive::Type result_type,
+       HInstruction* left,
+       HInstruction* right,
+       uint32_t dex_pc)
+      : HBinaryOperation(result_type, left, right, SideEffectsForArchRuntimeCalls(), dex_pc) {}
 
   template <typename T>
   T Compute(T x, T y) const {
@@ -3519,13 +3558,14 @@
   }
 
   HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
-    return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue()));
+    return GetBlock()->GetGraph()->GetIntConstant(
+        Compute(x->GetValue(), y->GetValue()), GetDexPc());
   }
   HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
-    return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue(), y->GetValue()));
+    return GetBlock()->GetGraph()->GetLongConstant(
+        Compute(x->GetValue(), y->GetValue()), GetDexPc());
   }
 
-  uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }
 
   static SideEffects SideEffectsForArchRuntimeCalls() {
     return SideEffects::CanTriggerGC();
@@ -3534,15 +3574,13 @@
   DECLARE_INSTRUCTION(Rem);
 
  private:
-  const uint32_t dex_pc_;
-
   DISALLOW_COPY_AND_ASSIGN(HRem);
 };
 
 class HDivZeroCheck : public HExpression<1> {
  public:
   HDivZeroCheck(HInstruction* value, uint32_t dex_pc)
-      : HExpression(value->GetType(), SideEffects::None()), dex_pc_(dex_pc) {
+      : HExpression(value->GetType(), SideEffects::None(), dex_pc) {
     SetRawInputAt(0, value);
   }
 
@@ -3558,20 +3596,19 @@
   bool NeedsEnvironment() const OVERRIDE { return true; }
   bool CanThrow() const OVERRIDE { return true; }
 
-  uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }
-
   DECLARE_INSTRUCTION(DivZeroCheck);
 
  private:
-  const uint32_t dex_pc_;
-
   DISALLOW_COPY_AND_ASSIGN(HDivZeroCheck);
 };
 
 class HShl : public HBinaryOperation {
  public:
-  HShl(Primitive::Type result_type, HInstruction* left, HInstruction* right)
-      : HBinaryOperation(result_type, left, right) {}
+  HShl(Primitive::Type result_type,
+       HInstruction* left,
+       HInstruction* right,
+       uint32_t dex_pc = kNoDexPc)
+      : HBinaryOperation(result_type, left, right, SideEffects::None(), dex_pc) {}
 
   template <typename T, typename U, typename V>
   T Compute(T x, U y, V max_shift_value) const {
@@ -3582,17 +3619,17 @@
 
   HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
     return GetBlock()->GetGraph()->GetIntConstant(
-        Compute(x->GetValue(), y->GetValue(), kMaxIntShiftValue));
+        Compute(x->GetValue(), y->GetValue(), kMaxIntShiftValue), GetDexPc());
   }
   // There is no `Evaluate(HIntConstant* x, HLongConstant* y)`, as this
   // case is handled as `x << static_cast<int>(y)`.
   HConstant* Evaluate(HLongConstant* x, HIntConstant* y) const OVERRIDE {
     return GetBlock()->GetGraph()->GetLongConstant(
-        Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue));
+        Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue), GetDexPc());
   }
   HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
     return GetBlock()->GetGraph()->GetLongConstant(
-        Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue));
+        Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue), GetDexPc());
   }
 
   DECLARE_INSTRUCTION(Shl);
@@ -3603,8 +3640,11 @@
 
 class HShr : public HBinaryOperation {
  public:
-  HShr(Primitive::Type result_type, HInstruction* left, HInstruction* right)
-      : HBinaryOperation(result_type, left, right) {}
+  HShr(Primitive::Type result_type,
+       HInstruction* left,
+       HInstruction* right,
+       uint32_t dex_pc = kNoDexPc)
+      : HBinaryOperation(result_type, left, right, SideEffects::None(), dex_pc) {}
 
   template <typename T, typename U, typename V>
   T Compute(T x, U y, V max_shift_value) const {
@@ -3615,17 +3655,17 @@
 
   HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
     return GetBlock()->GetGraph()->GetIntConstant(
-        Compute(x->GetValue(), y->GetValue(), kMaxIntShiftValue));
+        Compute(x->GetValue(), y->GetValue(), kMaxIntShiftValue), GetDexPc());
   }
   // There is no `Evaluate(HIntConstant* x, HLongConstant* y)`, as this
   // case is handled as `x >> static_cast<int>(y)`.
   HConstant* Evaluate(HLongConstant* x, HIntConstant* y) const OVERRIDE {
     return GetBlock()->GetGraph()->GetLongConstant(
-        Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue));
+        Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue), GetDexPc());
   }
   HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
     return GetBlock()->GetGraph()->GetLongConstant(
-        Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue));
+        Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue), GetDexPc());
   }
 
   DECLARE_INSTRUCTION(Shr);
@@ -3636,8 +3676,11 @@
 
 class HUShr : public HBinaryOperation {
  public:
-  HUShr(Primitive::Type result_type, HInstruction* left, HInstruction* right)
-      : HBinaryOperation(result_type, left, right) {}
+  HUShr(Primitive::Type result_type,
+        HInstruction* left,
+        HInstruction* right,
+        uint32_t dex_pc = kNoDexPc)
+      : HBinaryOperation(result_type, left, right, SideEffects::None(), dex_pc) {}
 
   template <typename T, typename U, typename V>
   T Compute(T x, U y, V max_shift_value) const {
@@ -3649,17 +3692,17 @@
 
   HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
     return GetBlock()->GetGraph()->GetIntConstant(
-        Compute(x->GetValue(), y->GetValue(), kMaxIntShiftValue));
+        Compute(x->GetValue(), y->GetValue(), kMaxIntShiftValue), GetDexPc());
   }
   // There is no `Evaluate(HIntConstant* x, HLongConstant* y)`, as this
   // case is handled as `x >>> static_cast<int>(y)`.
   HConstant* Evaluate(HLongConstant* x, HIntConstant* y) const OVERRIDE {
     return GetBlock()->GetGraph()->GetLongConstant(
-        Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue));
+        Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue), GetDexPc());
   }
   HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
     return GetBlock()->GetGraph()->GetLongConstant(
-        Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue));
+        Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue), GetDexPc());
   }
 
   DECLARE_INSTRUCTION(UShr);
@@ -3670,8 +3713,11 @@
 
 class HAnd : public HBinaryOperation {
  public:
-  HAnd(Primitive::Type result_type, HInstruction* left, HInstruction* right)
-      : HBinaryOperation(result_type, left, right) {}
+  HAnd(Primitive::Type result_type,
+       HInstruction* left,
+       HInstruction* right,
+       uint32_t dex_pc = kNoDexPc)
+      : HBinaryOperation(result_type, left, right, SideEffects::None(), dex_pc) {}
 
   bool IsCommutative() const OVERRIDE { return true; }
 
@@ -3679,16 +3725,20 @@
   auto Compute(T x, U y) const -> decltype(x & y) { return x & y; }
 
   HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
-    return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue()));
+    return GetBlock()->GetGraph()->GetIntConstant(
+        Compute(x->GetValue(), y->GetValue()), GetDexPc());
   }
   HConstant* Evaluate(HIntConstant* x, HLongConstant* y) const OVERRIDE {
-    return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue(), y->GetValue()));
+    return GetBlock()->GetGraph()->GetLongConstant(
+        Compute(x->GetValue(), y->GetValue()), GetDexPc());
   }
   HConstant* Evaluate(HLongConstant* x, HIntConstant* y) const OVERRIDE {
-    return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue(), y->GetValue()));
+    return GetBlock()->GetGraph()->GetLongConstant(
+        Compute(x->GetValue(), y->GetValue()), GetDexPc());
   }
   HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
-    return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue(), y->GetValue()));
+    return GetBlock()->GetGraph()->GetLongConstant(
+        Compute(x->GetValue(), y->GetValue()), GetDexPc());
   }
 
   DECLARE_INSTRUCTION(And);
@@ -3699,8 +3749,11 @@
 
 class HOr : public HBinaryOperation {
  public:
-  HOr(Primitive::Type result_type, HInstruction* left, HInstruction* right)
-      : HBinaryOperation(result_type, left, right) {}
+  HOr(Primitive::Type result_type,
+      HInstruction* left,
+      HInstruction* right,
+      uint32_t dex_pc = kNoDexPc)
+      : HBinaryOperation(result_type, left, right, SideEffects::None(), dex_pc) {}
 
   bool IsCommutative() const OVERRIDE { return true; }
 
@@ -3708,16 +3761,20 @@
   auto Compute(T x, U y) const -> decltype(x | y) { return x | y; }
 
   HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
-    return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue()));
+    return GetBlock()->GetGraph()->GetIntConstant(
+        Compute(x->GetValue(), y->GetValue()), GetDexPc());
   }
   HConstant* Evaluate(HIntConstant* x, HLongConstant* y) const OVERRIDE {
-    return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue(), y->GetValue()));
+    return GetBlock()->GetGraph()->GetLongConstant(
+        Compute(x->GetValue(), y->GetValue()), GetDexPc());
   }
   HConstant* Evaluate(HLongConstant* x, HIntConstant* y) const OVERRIDE {
-    return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue(), y->GetValue()));
+    return GetBlock()->GetGraph()->GetLongConstant(
+        Compute(x->GetValue(), y->GetValue()), GetDexPc());
   }
   HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
-    return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue(), y->GetValue()));
+    return GetBlock()->GetGraph()->GetLongConstant(
+        Compute(x->GetValue(), y->GetValue()), GetDexPc());
   }
 
   DECLARE_INSTRUCTION(Or);
@@ -3728,8 +3785,11 @@
 
 class HXor : public HBinaryOperation {
  public:
-  HXor(Primitive::Type result_type, HInstruction* left, HInstruction* right)
-      : HBinaryOperation(result_type, left, right) {}
+  HXor(Primitive::Type result_type,
+       HInstruction* left,
+       HInstruction* right,
+       uint32_t dex_pc = kNoDexPc)
+      : HBinaryOperation(result_type, left, right, SideEffects::None(), dex_pc) {}
 
   bool IsCommutative() const OVERRIDE { return true; }
 
@@ -3737,16 +3797,20 @@
   auto Compute(T x, U y) const -> decltype(x ^ y) { return x ^ y; }
 
   HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
-    return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue()));
+    return GetBlock()->GetGraph()->GetIntConstant(
+        Compute(x->GetValue(), y->GetValue()), GetDexPc());
   }
   HConstant* Evaluate(HIntConstant* x, HLongConstant* y) const OVERRIDE {
-    return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue(), y->GetValue()));
+    return GetBlock()->GetGraph()->GetLongConstant(
+        Compute(x->GetValue(), y->GetValue()), GetDexPc());
   }
   HConstant* Evaluate(HLongConstant* x, HIntConstant* y) const OVERRIDE {
-    return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue(), y->GetValue()));
+    return GetBlock()->GetGraph()->GetLongConstant(
+        Compute(x->GetValue(), y->GetValue()), GetDexPc());
   }
   HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
-    return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue(), y->GetValue()));
+    return GetBlock()->GetGraph()->GetLongConstant(
+        Compute(x->GetValue(), y->GetValue()), GetDexPc());
   }
 
   DECLARE_INSTRUCTION(Xor);
@@ -3759,8 +3823,10 @@
 // the calling convention.
 class HParameterValue : public HExpression<0> {
  public:
-  HParameterValue(uint8_t index, Primitive::Type parameter_type, bool is_this = false)
-      : HExpression(parameter_type, SideEffects::None()),
+  HParameterValue(uint8_t index,
+                  Primitive::Type parameter_type,
+                  bool is_this = false)
+      : HExpression(parameter_type, SideEffects::None(), kNoDexPc),
         index_(index),
         is_this_(is_this),
         can_be_null_(!is_this) {}
@@ -3789,8 +3855,8 @@
 
 class HNot : public HUnaryOperation {
  public:
-  HNot(Primitive::Type result_type, HInstruction* input)
-      : HUnaryOperation(result_type, input) {}
+  HNot(Primitive::Type result_type, HInstruction* input, uint32_t dex_pc = kNoDexPc)
+      : HUnaryOperation(result_type, input, dex_pc) {}
 
   bool CanBeMoved() const OVERRIDE { return true; }
   bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
@@ -3801,10 +3867,10 @@
   template <typename T> T Compute(T x) const { return ~x; }
 
   HConstant* Evaluate(HIntConstant* x) const OVERRIDE {
-    return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue()));
+    return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue()), GetDexPc());
   }
   HConstant* Evaluate(HLongConstant* x) const OVERRIDE {
-    return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue()));
+    return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue()), GetDexPc());
   }
 
   DECLARE_INSTRUCTION(Not);
@@ -3815,8 +3881,8 @@
 
 class HBooleanNot : public HUnaryOperation {
  public:
-  explicit HBooleanNot(HInstruction* input)
-      : HUnaryOperation(Primitive::Type::kPrimBoolean, input) {}
+  explicit HBooleanNot(HInstruction* input, uint32_t dex_pc = kNoDexPc)
+      : HUnaryOperation(Primitive::Type::kPrimBoolean, input, dex_pc) {}
 
   bool CanBeMoved() const OVERRIDE { return true; }
   bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
@@ -3830,7 +3896,7 @@
   }
 
   HConstant* Evaluate(HIntConstant* x) const OVERRIDE {
-    return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue()));
+    return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue()), GetDexPc());
   }
   HConstant* Evaluate(HLongConstant* x ATTRIBUTE_UNUSED) const OVERRIDE {
     LOG(FATAL) << DebugName() << " is not defined for long values";
@@ -3847,8 +3913,9 @@
  public:
   // Instantiate a type conversion of `input` to `result_type`.
   HTypeConversion(Primitive::Type result_type, HInstruction* input, uint32_t dex_pc)
-      : HExpression(result_type, SideEffectsForArchRuntimeCalls(input->GetType(), result_type)),
-        dex_pc_(dex_pc) {
+      : HExpression(result_type,
+                    SideEffectsForArchRuntimeCalls(input->GetType(), result_type),
+                    dex_pc) {
     SetRawInputAt(0, input);
     DCHECK_NE(input->GetType(), result_type);
   }
@@ -3859,7 +3926,6 @@
 
   // Required by the x86 and ARM code generators when producing calls
   // to the runtime.
-  uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }
 
   bool CanBeMoved() const OVERRIDE { return true; }
   bool InstructionDataEquals(HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; }
@@ -3883,8 +3949,6 @@
   DECLARE_INSTRUCTION(TypeConversion);
 
  private:
-  const uint32_t dex_pc_;
-
   DISALLOW_COPY_AND_ASSIGN(HTypeConversion);
 };
 
@@ -3892,8 +3956,12 @@
 
 class HPhi : public HInstruction {
  public:
-  HPhi(ArenaAllocator* arena, uint32_t reg_number, size_t number_of_inputs, Primitive::Type type)
-      : HInstruction(SideEffects::None()),
+  HPhi(ArenaAllocator* arena,
+       uint32_t reg_number,
+       size_t number_of_inputs,
+       Primitive::Type type,
+       uint32_t dex_pc = kNoDexPc)
+      : HInstruction(SideEffects::None(), dex_pc),
         inputs_(arena, number_of_inputs),
         reg_number_(reg_number),
         type_(type),
@@ -3971,7 +4039,7 @@
 class HNullCheck : public HExpression<1> {
  public:
   HNullCheck(HInstruction* value, uint32_t dex_pc)
-      : HExpression(value->GetType(), SideEffects::None()), dex_pc_(dex_pc) {
+      : HExpression(value->GetType(), SideEffects::None(), dex_pc) {
     SetRawInputAt(0, value);
   }
 
@@ -3987,13 +4055,10 @@
 
   bool CanBeNull() const OVERRIDE { return false; }
 
-  uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }
 
   DECLARE_INSTRUCTION(NullCheck);
 
  private:
-  const uint32_t dex_pc_;
-
   DISALLOW_COPY_AND_ASSIGN(HNullCheck);
 };
 
@@ -4036,10 +4101,11 @@
                     bool is_volatile,
                     uint32_t field_idx,
                     const DexFile& dex_file,
-                    Handle<mirror::DexCache> dex_cache)
+                    Handle<mirror::DexCache> dex_cache,
+                    uint32_t dex_pc = kNoDexPc)
       : HExpression(
             field_type,
-            SideEffects::FieldReadOfType(field_type, is_volatile)),
+            SideEffects::FieldReadOfType(field_type, is_volatile), dex_pc),
         field_info_(field_offset, field_type, is_volatile, field_idx, dex_file, dex_cache) {
     SetRawInputAt(0, value);
   }
@@ -4081,9 +4147,10 @@
                     bool is_volatile,
                     uint32_t field_idx,
                     const DexFile& dex_file,
-                    Handle<mirror::DexCache> dex_cache)
+                    Handle<mirror::DexCache> dex_cache,
+                    uint32_t dex_pc = kNoDexPc)
       : HTemplateInstruction(
-          SideEffects::FieldWriteOfType(field_type, is_volatile)),
+          SideEffects::FieldWriteOfType(field_type, is_volatile), dex_pc),
         field_info_(field_offset, field_type, is_volatile, field_idx, dex_file, dex_cache),
         value_can_be_null_(true) {
     SetRawInputAt(0, object);
@@ -4113,8 +4180,11 @@
 
 class HArrayGet : public HExpression<2> {
  public:
-  HArrayGet(HInstruction* array, HInstruction* index, Primitive::Type type)
-      : HExpression(type, SideEffects::ArrayReadOfType(type)) {
+  HArrayGet(HInstruction* array,
+            HInstruction* index,
+            Primitive::Type type,
+            uint32_t dex_pc = kNoDexPc)
+      : HExpression(type, SideEffects::ArrayReadOfType(type), dex_pc) {
     SetRawInputAt(0, array);
     SetRawInputAt(1, index);
   }
@@ -4154,8 +4224,7 @@
             uint32_t dex_pc)
       : HTemplateInstruction(
             SideEffects::ArrayWriteOfType(expected_component_type).Union(
-                SideEffectsForArchRuntimeCalls(value->GetType()))),
-        dex_pc_(dex_pc),
+                SideEffectsForArchRuntimeCalls(value->GetType())), dex_pc),
         expected_component_type_(expected_component_type),
         needs_type_check_(value->GetType() == Primitive::kPrimNot),
         value_can_be_null_(true) {
@@ -4190,8 +4259,6 @@
   bool GetValueCanBeNull() const { return value_can_be_null_; }
   bool NeedsTypeCheck() const { return needs_type_check_; }
 
-  uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }
-
   HInstruction* GetArray() const { return InputAt(0); }
   HInstruction* GetIndex() const { return InputAt(1); }
   HInstruction* GetValue() const { return InputAt(2); }
@@ -4214,7 +4281,6 @@
   DECLARE_INSTRUCTION(ArraySet);
 
  private:
-  const uint32_t dex_pc_;
   const Primitive::Type expected_component_type_;
   bool needs_type_check_;
   bool value_can_be_null_;
@@ -4224,8 +4290,8 @@
 
 class HArrayLength : public HExpression<1> {
  public:
-  explicit HArrayLength(HInstruction* array)
-      : HExpression(Primitive::kPrimInt, SideEffects::None()) {
+  explicit HArrayLength(HInstruction* array, uint32_t dex_pc = kNoDexPc)
+      : HExpression(Primitive::kPrimInt, SideEffects::None(), dex_pc) {
     // Note that arrays do not change length, so the instruction does not
     // depend on any write.
     SetRawInputAt(0, array);
@@ -4249,7 +4315,7 @@
 class HBoundsCheck : public HExpression<2> {
  public:
   HBoundsCheck(HInstruction* index, HInstruction* length, uint32_t dex_pc)
-      : HExpression(index->GetType(), SideEffects::None()), dex_pc_(dex_pc) {
+      : HExpression(index->GetType(), SideEffects::None(), dex_pc) {
     DCHECK(index->GetType() == Primitive::kPrimInt);
     SetRawInputAt(0, index);
     SetRawInputAt(1, length);
@@ -4265,13 +4331,10 @@
 
   bool CanThrow() const OVERRIDE { return true; }
 
-  uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }
 
   DECLARE_INSTRUCTION(BoundsCheck);
 
  private:
-  const uint32_t dex_pc_;
-
   DISALLOW_COPY_AND_ASSIGN(HBoundsCheck);
 };
 
@@ -4284,7 +4347,8 @@
  */
 class HTemporary : public HTemplateInstruction<0> {
  public:
-  explicit HTemporary(size_t index) : HTemplateInstruction(SideEffects::None()), index_(index) {}
+  explicit HTemporary(size_t index, uint32_t dex_pc = kNoDexPc)
+      : HTemplateInstruction(SideEffects::None(), dex_pc), index_(index) {}
 
   size_t GetIndex() const { return index_; }
 
@@ -4298,28 +4362,24 @@
 
  private:
   const size_t index_;
-
   DISALLOW_COPY_AND_ASSIGN(HTemporary);
 };
 
 class HSuspendCheck : public HTemplateInstruction<0> {
  public:
   explicit HSuspendCheck(uint32_t dex_pc)
-      : HTemplateInstruction(SideEffects::CanTriggerGC()), dex_pc_(dex_pc), slow_path_(nullptr) {}
+      : HTemplateInstruction(SideEffects::CanTriggerGC(), dex_pc), slow_path_(nullptr) {}
 
   bool NeedsEnvironment() const OVERRIDE {
     return true;
   }
 
-  uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }
   void SetSlowPath(SlowPathCode* slow_path) { slow_path_ = slow_path; }
   SlowPathCode* GetSlowPath() const { return slow_path_; }
 
   DECLARE_INSTRUCTION(SuspendCheck);
 
  private:
-  const uint32_t dex_pc_;
-
   // Only used for code generation, in order to share the same slow path between back edges
   // of a same loop.
   SlowPathCode* slow_path_;
@@ -4337,11 +4397,10 @@
              const DexFile& dex_file,
              bool is_referrers_class,
              uint32_t dex_pc)
-      : HExpression(Primitive::kPrimNot, SideEffectsForArchRuntimeCalls()),
+      : HExpression(Primitive::kPrimNot, SideEffectsForArchRuntimeCalls(), dex_pc),
         type_index_(type_index),
         dex_file_(dex_file),
         is_referrers_class_(is_referrers_class),
-        dex_pc_(dex_pc),
         generate_clinit_check_(false),
         loaded_class_rti_(ReferenceTypeInfo::CreateInvalid()) {
     SetRawInputAt(0, current_method);
@@ -4355,7 +4414,6 @@
 
   size_t ComputeHashCode() const OVERRIDE { return type_index_; }
 
-  uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }
   uint16_t GetTypeIndex() const { return type_index_; }
   bool IsReferrersClass() const { return is_referrers_class_; }
   bool CanBeNull() const OVERRIDE { return false; }
@@ -4408,7 +4466,6 @@
   const uint16_t type_index_;
   const DexFile& dex_file_;
   const bool is_referrers_class_;
-  const uint32_t dex_pc_;
   // Whether this instruction must generate the initialization check.
   // Used for code generation.
   bool generate_clinit_check_;
@@ -4421,9 +4478,8 @@
 class HLoadString : public HExpression<1> {
  public:
   HLoadString(HCurrentMethod* current_method, uint32_t string_index, uint32_t dex_pc)
-      : HExpression(Primitive::kPrimNot, SideEffectsForArchRuntimeCalls()),
-        string_index_(string_index),
-        dex_pc_(dex_pc) {
+      : HExpression(Primitive::kPrimNot, SideEffectsForArchRuntimeCalls(), dex_pc),
+        string_index_(string_index) {
     SetRawInputAt(0, current_method);
   }
 
@@ -4435,7 +4491,6 @@
 
   size_t ComputeHashCode() const OVERRIDE { return string_index_; }
 
-  uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }
   uint32_t GetStringIndex() const { return string_index_; }
 
   // TODO: Can we deopt or debug when we resolve a string?
@@ -4451,7 +4506,6 @@
 
  private:
   const uint32_t string_index_;
-  const uint32_t dex_pc_;
 
   DISALLOW_COPY_AND_ASSIGN(HLoadString);
 };
@@ -4464,8 +4518,8 @@
   HClinitCheck(HLoadClass* constant, uint32_t dex_pc)
       : HExpression(
             Primitive::kPrimNot,
-            SideEffects::AllChanges()),  // Assume write/read on all fields/arrays.
-        dex_pc_(dex_pc) {
+            SideEffects::AllChanges(),  // Assume write/read on all fields/arrays.
+            dex_pc) {
     SetRawInputAt(0, constant);
   }
 
@@ -4480,15 +4534,12 @@
     return true;
   }
 
-  uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }
 
   HLoadClass* GetLoadClass() const { return InputAt(0)->AsLoadClass(); }
 
   DECLARE_INSTRUCTION(ClinitCheck);
 
  private:
-  const uint32_t dex_pc_;
-
   DISALLOW_COPY_AND_ASSIGN(HClinitCheck);
 };
 
@@ -4500,10 +4551,11 @@
                   bool is_volatile,
                   uint32_t field_idx,
                   const DexFile& dex_file,
-                  Handle<mirror::DexCache> dex_cache)
+                  Handle<mirror::DexCache> dex_cache,
+                  uint32_t dex_pc = kNoDexPc)
       : HExpression(
             field_type,
-            SideEffects::FieldReadOfType(field_type, is_volatile)),
+            SideEffects::FieldReadOfType(field_type, is_volatile), dex_pc),
         field_info_(field_offset, field_type, is_volatile, field_idx, dex_file, dex_cache) {
     SetRawInputAt(0, cls);
   }
@@ -4542,9 +4594,10 @@
                   bool is_volatile,
                   uint32_t field_idx,
                   const DexFile& dex_file,
-                  Handle<mirror::DexCache> dex_cache)
+                  Handle<mirror::DexCache> dex_cache,
+                  uint32_t dex_pc = kNoDexPc)
       : HTemplateInstruction(
-          SideEffects::FieldWriteOfType(field_type, is_volatile)),
+          SideEffects::FieldWriteOfType(field_type, is_volatile), dex_pc),
         field_info_(field_offset, field_type, is_volatile, field_idx, dex_file, dex_cache),
         value_can_be_null_(true) {
     SetRawInputAt(0, cls);
@@ -4572,7 +4625,8 @@
 // Implement the move-exception DEX instruction.
 class HLoadException : public HExpression<0> {
  public:
-  HLoadException() : HExpression(Primitive::kPrimNot, SideEffects::None()) {}
+  explicit HLoadException(uint32_t dex_pc = kNoDexPc)
+      : HExpression(Primitive::kPrimNot, SideEffects::None(), dex_pc) {}
 
   bool CanBeNull() const OVERRIDE { return false; }
 
@@ -4586,7 +4640,8 @@
 // Must not be removed because the runtime expects the TLS to get cleared.
 class HClearException : public HTemplateInstruction<0> {
  public:
-  HClearException() : HTemplateInstruction(SideEffects::AllWrites()) {}
+  explicit HClearException(uint32_t dex_pc = kNoDexPc)
+      : HTemplateInstruction(SideEffects::AllWrites(), dex_pc) {}
 
   DECLARE_INSTRUCTION(ClearException);
 
@@ -4597,7 +4652,7 @@
 class HThrow : public HTemplateInstruction<1> {
  public:
   HThrow(HInstruction* exception, uint32_t dex_pc)
-      : HTemplateInstruction(SideEffects::CanTriggerGC()), dex_pc_(dex_pc) {
+      : HTemplateInstruction(SideEffects::CanTriggerGC(), dex_pc) {
     SetRawInputAt(0, exception);
   }
 
@@ -4607,13 +4662,10 @@
 
   bool CanThrow() const OVERRIDE { return true; }
 
-  uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }
 
   DECLARE_INSTRUCTION(Throw);
 
  private:
-  const uint32_t dex_pc_;
-
   DISALLOW_COPY_AND_ASSIGN(HThrow);
 };
 
@@ -4623,10 +4675,11 @@
               HLoadClass* constant,
               bool class_is_final,
               uint32_t dex_pc)
-      : HExpression(Primitive::kPrimBoolean, SideEffectsForArchRuntimeCalls(class_is_final)),
+      : HExpression(Primitive::kPrimBoolean,
+                    SideEffectsForArchRuntimeCalls(class_is_final),
+                    dex_pc),
         class_is_final_(class_is_final),
-        must_do_null_check_(true),
-        dex_pc_(dex_pc) {
+        must_do_null_check_(true) {
     SetRawInputAt(0, object);
     SetRawInputAt(1, constant);
   }
@@ -4641,8 +4694,6 @@
     return false;
   }
 
-  uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }
-
   bool IsClassFinal() const { return class_is_final_; }
 
   // Used only in code generation.
@@ -4658,7 +4709,6 @@
  private:
   const bool class_is_final_;
   bool must_do_null_check_;
-  const uint32_t dex_pc_;
 
   DISALLOW_COPY_AND_ASSIGN(HInstanceOf);
 };
@@ -4667,8 +4717,11 @@
  public:
   // Constructs an HBoundType with the given upper_bound.
   // Ensures that the upper_bound is valid.
-  HBoundType(HInstruction* input, ReferenceTypeInfo upper_bound, bool upper_can_be_null)
-      : HExpression(Primitive::kPrimNot, SideEffects::None()),
+  HBoundType(HInstruction* input,
+             ReferenceTypeInfo upper_bound,
+             bool upper_can_be_null,
+             uint32_t dex_pc = kNoDexPc)
+      : HExpression(Primitive::kPrimNot, SideEffects::None(), dex_pc),
         upper_bound_(upper_bound),
         upper_can_be_null_(upper_can_be_null),
         can_be_null_(upper_can_be_null) {
@@ -4712,10 +4765,9 @@
              HLoadClass* constant,
              bool class_is_final,
              uint32_t dex_pc)
-      : HTemplateInstruction(SideEffects::CanTriggerGC()),
+      : HTemplateInstruction(SideEffects::CanTriggerGC(), dex_pc),
         class_is_final_(class_is_final),
-        must_do_null_check_(true),
-        dex_pc_(dex_pc) {
+        must_do_null_check_(true) {
     SetRawInputAt(0, object);
     SetRawInputAt(1, constant);
   }
@@ -4736,7 +4788,6 @@
   bool MustDoNullCheck() const { return must_do_null_check_; }
   void ClearMustDoNullCheck() { must_do_null_check_ = false; }
 
-  uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }
 
   bool IsClassFinal() const { return class_is_final_; }
 
@@ -4745,16 +4796,15 @@
  private:
   const bool class_is_final_;
   bool must_do_null_check_;
-  const uint32_t dex_pc_;
 
   DISALLOW_COPY_AND_ASSIGN(HCheckCast);
 };
 
 class HMemoryBarrier : public HTemplateInstruction<0> {
  public:
-  explicit HMemoryBarrier(MemBarrierKind barrier_kind)
+  explicit HMemoryBarrier(MemBarrierKind barrier_kind, uint32_t dex_pc = kNoDexPc)
       : HTemplateInstruction(
-            SideEffects::AllWritesAndReads()),  // Assume write/read on all fields/arrays.
+            SideEffects::AllWritesAndReads(), dex_pc),  // Assume write/read on all fields/arrays.
         barrier_kind_(barrier_kind) {}
 
   MemBarrierKind GetBarrierKind() { return barrier_kind_; }
@@ -4776,8 +4826,8 @@
 
   HMonitorOperation(HInstruction* object, OperationKind kind, uint32_t dex_pc)
     : HTemplateInstruction(
-          SideEffects::AllExceptGCDependency()),  // Assume write/read on all fields/arrays.
-      kind_(kind), dex_pc_(dex_pc) {
+          SideEffects::AllExceptGCDependency(), dex_pc),  // Assume write/read on all fields/arrays.
+      kind_(kind) {
     SetRawInputAt(0, object);
   }
 
@@ -4791,7 +4841,6 @@
     return IsEnter();
   }
 
-  uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }
 
   bool IsEnter() const { return kind_ == kEnter; }
 
@@ -4799,7 +4848,6 @@
 
  private:
   const OperationKind kind_;
-  const uint32_t dex_pc_;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(HMonitorOperation);
@@ -4814,7 +4862,8 @@
  */
 class HFakeString : public HTemplateInstruction<0> {
  public:
-  HFakeString() : HTemplateInstruction(SideEffects::None()) {}
+  explicit HFakeString(uint32_t dex_pc = kNoDexPc)
+      : HTemplateInstruction(SideEffects::None(), dex_pc) {}
 
   Primitive::Type GetType() const OVERRIDE { return Primitive::kPrimNot; }
 
@@ -4902,8 +4951,8 @@
 
 class HParallelMove : public HTemplateInstruction<0> {
  public:
-  explicit HParallelMove(ArenaAllocator* arena)
-      : HTemplateInstruction(SideEffects::None()), moves_(arena, kDefaultNumberOfMoves) {}
+  explicit HParallelMove(ArenaAllocator* arena, uint32_t dex_pc = kNoDexPc)
+      : HTemplateInstruction(SideEffects::None(), dex_pc), moves_(arena, kDefaultNumberOfMoves) {}
 
   void AddMove(Location source,
                Location destination,
@@ -4953,6 +5002,14 @@
   DISALLOW_COPY_AND_ASSIGN(HParallelMove);
 };
 
+}  // namespace art
+
+#ifdef ART_ENABLE_CODEGEN_x86
+#include "nodes_x86.h"
+#endif
+
+namespace art {
+
 class HGraphVisitor : public ValueObject {
  public:
   explicit HGraphVisitor(HGraph* graph) : graph_(graph) {}
diff --git a/compiler/optimizing/nodes_x86.h b/compiler/optimizing/nodes_x86.h
new file mode 100644
index 0000000..ddc5730
--- /dev/null
+++ b/compiler/optimizing/nodes_x86.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2015 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_COMPILER_OPTIMIZING_NODES_X86_H_
+#define ART_COMPILER_OPTIMIZING_NODES_X86_H_
+
+namespace art {
+
+// Compute the address of the method for X86 Constant area support.
+class HX86ComputeBaseMethodAddress : public HExpression<0> {
+ public:
+  // Treat the value as an int32_t, but it is really a 32 bit native pointer.
+  HX86ComputeBaseMethodAddress() : HExpression(Primitive::kPrimInt, SideEffects::None()) {}
+
+  DECLARE_INSTRUCTION(X86ComputeBaseMethodAddress);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(HX86ComputeBaseMethodAddress);
+};
+
+// Load a constant value from the constant table.
+class HX86LoadFromConstantTable : public HExpression<2> {
+ public:
+  HX86LoadFromConstantTable(HX86ComputeBaseMethodAddress* method_base,
+                            HConstant* constant,
+                            bool needs_materialization = true)
+      : HExpression(constant->GetType(), SideEffects::None()),
+        needs_materialization_(needs_materialization) {
+    SetRawInputAt(0, method_base);
+    SetRawInputAt(1, constant);
+  }
+
+  bool NeedsMaterialization() const { return needs_materialization_; }
+
+  HX86ComputeBaseMethodAddress* GetBaseMethodAddress() const {
+    return InputAt(0)->AsX86ComputeBaseMethodAddress();
+  }
+
+  HConstant* GetConstant() const {
+    return InputAt(1)->AsConstant();
+  }
+
+  DECLARE_INSTRUCTION(X86LoadFromConstantTable);
+
+ private:
+  const bool needs_materialization_;
+
+  DISALLOW_COPY_AND_ASSIGN(HX86LoadFromConstantTable);
+};
+
+}  // namespace art
+
+#endif  // ART_COMPILER_OPTIMIZING_NODES_X86_H_
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 91b03d4..f549ba8 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -23,6 +23,10 @@
 #include "instruction_simplifier_arm64.h"
 #endif
 
+#ifdef ART_ENABLE_CODEGEN_x86
+#include "constant_area_fixups_x86.h"
+#endif
+
 #include "art_method-inl.h"
 #include "base/arena_allocator.h"
 #include "base/arena_containers.h"
@@ -424,6 +428,17 @@
       break;
     }
 #endif
+#ifdef ART_ENABLE_CODEGEN_x86
+    case kX86: {
+      x86::ConstantAreaFixups* constant_area_fixups =
+          new (arena) x86::ConstantAreaFixups(graph, stats);
+      HOptimization* x86_optimizations[] = {
+        constant_area_fixups
+      };
+      RunOptimizations(x86_optimizations, arraysize(x86_optimizations), pass_observer);
+      break;
+    }
+#endif
     default:
       break;
   }
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index b887b89..0384e46 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -636,9 +636,9 @@
   ScopedObjectAccess soa(Thread::Current());
   ClassLinker* cl = Runtime::Current()->GetClassLinker();
   mirror::DexCache* dex_cache = cl->FindDexCache(soa.Self(), instr->GetDexFile());
-  ArtMethod* method = dex_cache->GetResolvedMethod(
-      instr->GetDexMethodIndex(), cl->GetImagePointerSize());
-  mirror::Class* klass = (method == nullptr) ? nullptr : method->GetReturnType(false);
+  size_t pointer_size = cl->GetImagePointerSize();
+  ArtMethod* method = dex_cache->GetResolvedMethod(instr->GetDexMethodIndex(), pointer_size);
+  mirror::Class* klass = (method == nullptr) ? nullptr : method->GetReturnType(false, pointer_size);
   SetClassAsTypeInfo(instr, klass, /* is_exact */ false);
 }
 
diff --git a/compiler/utils/arm/assembler_arm.h b/compiler/utils/arm/assembler_arm.h
index 7825457..a4d1837 100644
--- a/compiler/utils/arm/assembler_arm.h
+++ b/compiler/utils/arm/assembler_arm.h
@@ -495,6 +495,7 @@
   virtual void clz(Register rd, Register rm, Condition cond = AL) = 0;
   virtual void movw(Register rd, uint16_t imm16, Condition cond = AL) = 0;
   virtual void movt(Register rd, uint16_t imm16, Condition cond = AL) = 0;
+  virtual void rbit(Register rd, Register rm, Condition cond = AL) = 0;
 
   // Multiply instructions.
   virtual void mul(Register rd, Register rn, Register rm, Condition cond = AL) = 0;
@@ -668,11 +669,14 @@
   virtual void LoadLiteral(DRegister dd, Literal* literal) = 0;
 
   // Add signed constant value to rd. May clobber IP.
-  virtual void AddConstant(Register rd, int32_t value, Condition cond = AL) = 0;
   virtual void AddConstant(Register rd, Register rn, int32_t value,
-                           Condition cond = AL) = 0;
-  virtual void AddConstantSetFlags(Register rd, Register rn, int32_t value,
-                                   Condition cond = AL) = 0;
+                           Condition cond = AL, SetCc set_cc = kCcDontCare) = 0;
+  void AddConstantSetFlags(Register rd, Register rn, int32_t value, Condition cond = AL) {
+    AddConstant(rd, rn, value, cond, kCcSet);
+  }
+  void AddConstant(Register rd, int32_t value, Condition cond = AL, SetCc set_cc = kCcDontCare) {
+    AddConstant(rd, rd, value, cond, set_cc);
+  }
 
   // Load and Store. May clobber IP.
   virtual void LoadImmediate(Register rd, int32_t value, Condition cond = AL) = 0;
diff --git a/compiler/utils/arm/assembler_arm32.cc b/compiler/utils/arm/assembler_arm32.cc
index d91ddee..f7772ae 100644
--- a/compiler/utils/arm/assembler_arm32.cc
+++ b/compiler/utils/arm/assembler_arm32.cc
@@ -735,6 +735,20 @@
 }
 
 
+void Arm32Assembler::rbit(Register rd, Register rm, Condition cond) {
+  CHECK_NE(rd, kNoRegister);
+  CHECK_NE(rm, kNoRegister);
+  CHECK_NE(cond, kNoCondition);
+  CHECK_NE(rd, PC);
+  CHECK_NE(rm, PC);
+  int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) |
+                     B26 | B25 | B23 | B22 | B21 | B20 | (0xf << 16) |
+                     (static_cast<int32_t>(rd) << kRdShift) |
+                     (0xf << 8) | B5 | B4 | static_cast<int32_t>(rm);
+  Emit(encoding);
+}
+
+
 void Arm32Assembler::EmitMulOp(Condition cond, int32_t opcode,
                                Register rd, Register rn,
                                Register rm, Register rs) {
@@ -1321,16 +1335,12 @@
   UNREACHABLE();
 }
 
-void Arm32Assembler::AddConstant(Register rd, int32_t value, Condition cond) {
-  AddConstant(rd, rd, value, cond);
-}
-
 
 void Arm32Assembler::AddConstant(Register rd, Register rn, int32_t value,
-                                 Condition cond) {
-  if (value == 0) {
+                                 Condition cond, SetCc set_cc) {
+  if (value == 0 && set_cc != kCcSet) {
     if (rd != rn) {
-      mov(rd, ShifterOperand(rn), cond);
+      mov(rd, ShifterOperand(rn), cond, set_cc);
     }
     return;
   }
@@ -1339,55 +1349,29 @@
   // the readability of generated code for some constants.
   ShifterOperand shifter_op;
   if (ShifterOperandCanHoldArm32(value, &shifter_op)) {
-    add(rd, rn, shifter_op, cond);
+    add(rd, rn, shifter_op, cond, set_cc);
   } else if (ShifterOperandCanHoldArm32(-value, &shifter_op)) {
-    sub(rd, rn, shifter_op, cond);
+    sub(rd, rn, shifter_op, cond, set_cc);
   } else {
     CHECK(rn != IP);
     if (ShifterOperandCanHoldArm32(~value, &shifter_op)) {
-      mvn(IP, shifter_op, cond);
-      add(rd, rn, ShifterOperand(IP), cond);
+      mvn(IP, shifter_op, cond, kCcKeep);
+      add(rd, rn, ShifterOperand(IP), cond, set_cc);
     } else if (ShifterOperandCanHoldArm32(~(-value), &shifter_op)) {
-      mvn(IP, shifter_op, cond);
-      sub(rd, rn, ShifterOperand(IP), cond);
+      mvn(IP, shifter_op, cond, kCcKeep);
+      sub(rd, rn, ShifterOperand(IP), cond, set_cc);
     } else {
       movw(IP, Low16Bits(value), cond);
       uint16_t value_high = High16Bits(value);
       if (value_high != 0) {
         movt(IP, value_high, cond);
       }
-      add(rd, rn, ShifterOperand(IP), cond);
+      add(rd, rn, ShifterOperand(IP), cond, set_cc);
     }
   }
 }
 
 
-void Arm32Assembler::AddConstantSetFlags(Register rd, Register rn, int32_t value,
-                                         Condition cond) {
-  ShifterOperand shifter_op;
-  if (ShifterOperandCanHoldArm32(value, &shifter_op)) {
-    add(rd, rn, shifter_op, cond, kCcSet);
-  } else if (ShifterOperandCanHoldArm32(-value, &shifter_op)) {
-    sub(rd, rn, shifter_op, cond, kCcSet);
-  } else {
-    CHECK(rn != IP);
-    if (ShifterOperandCanHoldArm32(~value, &shifter_op)) {
-      mvn(IP, shifter_op, cond);
-      add(rd, rn, ShifterOperand(IP), cond, kCcSet);
-    } else if (ShifterOperandCanHoldArm32(~(-value), &shifter_op)) {
-      mvn(IP, shifter_op, cond);
-      sub(rd, rn, ShifterOperand(IP), cond, kCcSet);
-    } else {
-      movw(IP, Low16Bits(value), cond);
-      uint16_t value_high = High16Bits(value);
-      if (value_high != 0) {
-        movt(IP, value_high, cond);
-      }
-      add(rd, rn, ShifterOperand(IP), cond, kCcSet);
-    }
-  }
-}
-
 void Arm32Assembler::LoadImmediate(Register rd, int32_t value, Condition cond) {
   ShifterOperand shifter_op;
   if (ShifterOperandCanHoldArm32(value, &shifter_op)) {
diff --git a/compiler/utils/arm/assembler_arm32.h b/compiler/utils/arm/assembler_arm32.h
index b96bb74..3407369 100644
--- a/compiler/utils/arm/assembler_arm32.h
+++ b/compiler/utils/arm/assembler_arm32.h
@@ -87,6 +87,7 @@
   void clz(Register rd, Register rm, Condition cond = AL) OVERRIDE;
   void movw(Register rd, uint16_t imm16, Condition cond = AL) OVERRIDE;
   void movt(Register rd, uint16_t imm16, Condition cond = AL) OVERRIDE;
+  void rbit(Register rd, Register rm, Condition cond = AL) OVERRIDE;
 
   // Multiply instructions.
   void mul(Register rd, Register rn, Register rm, Condition cond = AL) OVERRIDE;
@@ -254,11 +255,8 @@
   void LoadLiteral(DRegister dd, Literal* literal) OVERRIDE;
 
   // Add signed constant value to rd. May clobber IP.
-  void AddConstant(Register rd, int32_t value, Condition cond = AL) OVERRIDE;
   void AddConstant(Register rd, Register rn, int32_t value,
-                   Condition cond = AL) OVERRIDE;
-  void AddConstantSetFlags(Register rd, Register rn, int32_t value,
-                           Condition cond = AL) OVERRIDE;
+                   Condition cond = AL, SetCc set_cc = kCcDontCare) OVERRIDE;
 
   // Load and Store. May clobber IP.
   void LoadImmediate(Register rd, int32_t value, Condition cond = AL) OVERRIDE;
diff --git a/compiler/utils/arm/assembler_arm32_test.cc b/compiler/utils/arm/assembler_arm32_test.cc
index e6412ac..2a0912e 100644
--- a/compiler/utils/arm/assembler_arm32_test.cc
+++ b/compiler/utils/arm/assembler_arm32_test.cc
@@ -883,4 +883,8 @@
   DriverStr(expected, "strexd");
 }
 
+TEST_F(AssemblerArm32Test, rbit) {
+  T3Helper(&arm::Arm32Assembler::rbit, true, "rbit{cond} {reg1}, {reg2}", "rbit");
+}
+
 }  // namespace art
diff --git a/compiler/utils/arm/assembler_thumb2.cc b/compiler/utils/arm/assembler_thumb2.cc
index 90ed10c..0f6c4f5 100644
--- a/compiler/utils/arm/assembler_thumb2.cc
+++ b/compiler/utils/arm/assembler_thumb2.cc
@@ -2426,6 +2426,25 @@
 }
 
 
+void Thumb2Assembler::rbit(Register rd, Register rm, Condition cond) {
+  CHECK_NE(rd, kNoRegister);
+  CHECK_NE(rm, kNoRegister);
+  CheckCondition(cond);
+  CHECK_NE(rd, PC);
+  CHECK_NE(rm, PC);
+  CHECK_NE(rd, SP);
+  CHECK_NE(rm, SP);
+  int32_t encoding = B31 | B30 | B29 | B28 | B27 |
+      B25 | B23 | B20 |
+      static_cast<uint32_t>(rm) << 16 |
+      0xf << 12 |
+      static_cast<uint32_t>(rd) << 8 |
+      B7 | B5 |
+      static_cast<uint32_t>(rm);
+  Emit32(encoding);
+}
+
+
 void Thumb2Assembler::ldrex(Register rt, Register rn, uint16_t imm, Condition cond) {
   CHECK_NE(rn, kNoRegister);
   CHECK_NE(rt, kNoRegister);
@@ -3192,14 +3211,10 @@
   DCHECK_EQ(location + GetFixup(fixup_id)->GetSizeInBytes(), buffer_.Size());
 }
 
-void Thumb2Assembler::AddConstant(Register rd, int32_t value, Condition cond) {
-  AddConstant(rd, rd, value, cond);
-}
-
 
 void Thumb2Assembler::AddConstant(Register rd, Register rn, int32_t value,
-                                  Condition cond) {
-  if (value == 0) {
+                                  Condition cond, SetCc set_cc) {
+  if (value == 0 && set_cc != kCcSet) {
     if (rd != rn) {
       mov(rd, ShifterOperand(rn), cond);
     }
@@ -3210,51 +3225,24 @@
   // the readability of generated code for some constants.
   ShifterOperand shifter_op;
   if (ShifterOperandCanHold(rd, rn, ADD, value, &shifter_op)) {
-    add(rd, rn, shifter_op, cond);
+    add(rd, rn, shifter_op, cond, set_cc);
   } else if (ShifterOperandCanHold(rd, rn, SUB, -value, &shifter_op)) {
-    sub(rd, rn, shifter_op, cond);
+    sub(rd, rn, shifter_op, cond, set_cc);
   } else {
     CHECK(rn != IP);
     if (ShifterOperandCanHold(rd, rn, MVN, ~value, &shifter_op)) {
-      mvn(IP, shifter_op, cond);
-      add(rd, rn, ShifterOperand(IP), cond);
+      mvn(IP, shifter_op, cond, kCcKeep);
+      add(rd, rn, ShifterOperand(IP), cond, set_cc);
     } else if (ShifterOperandCanHold(rd, rn, MVN, ~(-value), &shifter_op)) {
-      mvn(IP, shifter_op, cond);
-      sub(rd, rn, ShifterOperand(IP), cond);
+      mvn(IP, shifter_op, cond, kCcKeep);
+      sub(rd, rn, ShifterOperand(IP), cond, set_cc);
     } else {
       movw(IP, Low16Bits(value), cond);
       uint16_t value_high = High16Bits(value);
       if (value_high != 0) {
         movt(IP, value_high, cond);
       }
-      add(rd, rn, ShifterOperand(IP), cond);
-    }
-  }
-}
-
-
-void Thumb2Assembler::AddConstantSetFlags(Register rd, Register rn, int32_t value,
-                                          Condition cond) {
-  ShifterOperand shifter_op;
-  if (ShifterOperandCanHold(rd, rn, ADD, value, &shifter_op)) {
-    add(rd, rn, shifter_op, cond, kCcSet);
-  } else if (ShifterOperandCanHold(rd, rn, ADD, -value, &shifter_op)) {
-    sub(rd, rn, shifter_op, cond, kCcSet);
-  } else {
-    CHECK(rn != IP);
-    if (ShifterOperandCanHold(rd, rn, MVN, ~value, &shifter_op)) {
-      mvn(IP, shifter_op, cond);
-      add(rd, rn, ShifterOperand(IP), cond, kCcSet);
-    } else if (ShifterOperandCanHold(rd, rn, MVN, ~(-value), &shifter_op)) {
-      mvn(IP, shifter_op, cond);
-      sub(rd, rn, ShifterOperand(IP), cond, kCcSet);
-    } else {
-      movw(IP, Low16Bits(value), cond);
-      uint16_t value_high = High16Bits(value);
-      if (value_high != 0) {
-        movt(IP, value_high, cond);
-      }
-      add(rd, rn, ShifterOperand(IP), cond, kCcSet);
+      add(rd, rn, ShifterOperand(IP), cond, set_cc);
     }
   }
 }
diff --git a/compiler/utils/arm/assembler_thumb2.h b/compiler/utils/arm/assembler_thumb2.h
index c802c27..a1a8927 100644
--- a/compiler/utils/arm/assembler_thumb2.h
+++ b/compiler/utils/arm/assembler_thumb2.h
@@ -111,6 +111,7 @@
   void clz(Register rd, Register rm, Condition cond = AL) OVERRIDE;
   void movw(Register rd, uint16_t imm16, Condition cond = AL) OVERRIDE;
   void movt(Register rd, uint16_t imm16, Condition cond = AL) OVERRIDE;
+  void rbit(Register rd, Register rm, Condition cond = AL) OVERRIDE;
 
   // Multiply instructions.
   void mul(Register rd, Register rn, Register rm, Condition cond = AL) OVERRIDE;
@@ -297,11 +298,8 @@
   void LoadLiteral(DRegister dd, Literal* literal) OVERRIDE;
 
   // Add signed constant value to rd. May clobber IP.
-  void AddConstant(Register rd, int32_t value, Condition cond = AL) OVERRIDE;
   void AddConstant(Register rd, Register rn, int32_t value,
-                   Condition cond = AL) OVERRIDE;
-  void AddConstantSetFlags(Register rd, Register rn, int32_t value,
-                           Condition cond = AL) OVERRIDE;
+                   Condition cond = AL, SetCc set_cc = kCcDontCare) OVERRIDE;
 
   // Load and Store. May clobber IP.
   void LoadImmediate(Register rd, int32_t value, Condition cond = AL) OVERRIDE;
diff --git a/compiler/utils/arm/assembler_thumb2_test.cc b/compiler/utils/arm/assembler_thumb2_test.cc
index 84f5cb1..9c08ce0 100644
--- a/compiler/utils/arm/assembler_thumb2_test.cc
+++ b/compiler/utils/arm/assembler_thumb2_test.cc
@@ -1019,4 +1019,12 @@
   DriverStr(expected, "clz");
 }
 
+TEST_F(AssemblerThumb2Test, rbit) {
+  __ rbit(arm::R1, arm::R0);
+
+  const char* expected = "rbit r1, r0\n";
+
+  DriverStr(expected, "rbit");
+}
+
 }  // namespace art
diff --git a/compiler/utils/dex_cache_arrays_layout-inl.h b/compiler/utils/dex_cache_arrays_layout-inl.h
deleted file mode 100644
index fec981a..0000000
--- a/compiler/utils/dex_cache_arrays_layout-inl.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2015 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_COMPILER_UTILS_DEX_CACHE_ARRAYS_LAYOUT_INL_H_
-#define ART_COMPILER_UTILS_DEX_CACHE_ARRAYS_LAYOUT_INL_H_
-
-#include "dex_cache_arrays_layout.h"
-
-#include "base/bit_utils.h"
-#include "base/logging.h"
-#include "globals.h"
-#include "mirror/array-inl.h"
-#include "primitive.h"
-
-namespace art {
-
-inline DexCacheArraysLayout::DexCacheArraysLayout(size_t pointer_size, const DexFile* dex_file)
-    : /* types_offset_ is always 0u */
-      pointer_size_(pointer_size),
-      methods_offset_(types_offset_ + TypesSize(dex_file->NumTypeIds())),
-      strings_offset_(methods_offset_ + MethodsSize(dex_file->NumMethodIds())),
-      fields_offset_(strings_offset_ + StringsSize(dex_file->NumStringIds())),
-      size_(fields_offset_ + FieldsSize(dex_file->NumFieldIds())) {
-  DCHECK(ValidPointerSize(pointer_size)) << pointer_size;
-}
-
-inline size_t DexCacheArraysLayout::TypeOffset(uint32_t type_idx) const {
-  return types_offset_ + ElementOffset(sizeof(mirror::HeapReference<mirror::Class>), type_idx);
-}
-
-inline size_t DexCacheArraysLayout::TypesSize(size_t num_elements) const {
-  return ArraySize(sizeof(mirror::HeapReference<mirror::Class>), num_elements);
-}
-
-inline size_t DexCacheArraysLayout::MethodOffset(uint32_t method_idx) const {
-  return methods_offset_ + ElementOffset(pointer_size_, method_idx);
-}
-
-inline size_t DexCacheArraysLayout::MethodsSize(size_t num_elements) const {
-  return ArraySize(pointer_size_, num_elements);
-}
-
-inline size_t DexCacheArraysLayout::StringOffset(uint32_t string_idx) const {
-  return strings_offset_ + ElementOffset(sizeof(mirror::HeapReference<mirror::String>), string_idx);
-}
-
-inline size_t DexCacheArraysLayout::StringsSize(size_t num_elements) const {
-  return ArraySize(sizeof(mirror::HeapReference<mirror::String>), num_elements);
-}
-
-inline size_t DexCacheArraysLayout::FieldOffset(uint32_t field_idx) const {
-  return fields_offset_ + ElementOffset(pointer_size_, field_idx);
-}
-
-inline size_t DexCacheArraysLayout::FieldsSize(size_t num_elements) const {
-  return ArraySize(pointer_size_, num_elements);
-}
-
-inline size_t DexCacheArraysLayout::ElementOffset(size_t element_size, uint32_t idx) {
-  return mirror::Array::DataOffset(element_size).Uint32Value() + element_size * idx;
-}
-
-inline size_t DexCacheArraysLayout::ArraySize(size_t element_size, uint32_t num_elements) {
-  size_t array_size = mirror::ComputeArraySize(num_elements, ComponentSizeShiftWidth(element_size));
-  DCHECK_NE(array_size, 0u);  // No overflow expected for dex cache arrays.
-  return RoundUp(array_size, kObjectAlignment);
-}
-
-}  // namespace art
-
-#endif  // ART_COMPILER_UTILS_DEX_CACHE_ARRAYS_LAYOUT_INL_H_
diff --git a/compiler/utils/swap_space.h b/compiler/utils/swap_space.h
index f7c772d..9127b6b 100644
--- a/compiler/utils/swap_space.h
+++ b/compiler/utils/swap_space.h
@@ -163,7 +163,9 @@
   pointer allocate(size_type n, SwapAllocator<void>::pointer hint ATTRIBUTE_UNUSED = nullptr) {
     DCHECK_LE(n, max_size());
     if (swap_space_ == nullptr) {
-      return reinterpret_cast<T*>(malloc(n * sizeof(T)));
+      T* result = reinterpret_cast<T*>(malloc(n * sizeof(T)));
+      CHECK(result != nullptr || n == 0u);  // Abort if malloc() fails.
+      return result;
     } else {
       return reinterpret_cast<T*>(swap_space_->Alloc(n * sizeof(T)));
     }
diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc
index a03f857..e3962b4 100644
--- a/compiler/utils/x86/assembler_x86.cc
+++ b/compiler/utils/x86/assembler_x86.cc
@@ -1750,6 +1750,10 @@
   for (int i = 1; i < length; i++) {
     EmitUint8(operand.encoding_[i]);
   }
+  AssemblerFixup* fixup = operand.GetFixup();
+  if (fixup != nullptr) {
+    EmitFixup(fixup);
+  }
 }
 
 
@@ -2322,5 +2326,56 @@
 #undef __
 }
 
+void X86Assembler::AddConstantArea() {
+  const std::vector<int32_t>& area = constant_area_.GetBuffer();
+  // Generate the data for the literal area.
+  for (size_t i = 0, e = area.size(); i < e; i++) {
+    AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+    EmitInt32(area[i]);
+  }
+}
+
+int ConstantArea::AddInt32(int32_t v) {
+  for (size_t i = 0, e = buffer_.size(); i < e; i++) {
+    if (v == buffer_[i]) {
+      return i * kEntrySize;
+    }
+  }
+
+  // Didn't match anything.
+  int result = buffer_.size() * kEntrySize;
+  buffer_.push_back(v);
+  return result;
+}
+
+int ConstantArea::AddInt64(int64_t v) {
+  int32_t v_low = Low32Bits(v);
+  int32_t v_high = High32Bits(v);
+  if (buffer_.size() > 1) {
+    // Ensure we don't pass the end of the buffer.
+    for (size_t i = 0, e = buffer_.size() - 1; i < e; i++) {
+      if (v_low == buffer_[i] && v_high == buffer_[i + 1]) {
+        return i * kEntrySize;
+      }
+    }
+  }
+
+  // Didn't match anything.
+  int result = buffer_.size() * kEntrySize;
+  buffer_.push_back(v_low);
+  buffer_.push_back(v_high);
+  return result;
+}
+
+int ConstantArea::AddDouble(double v) {
+  // Treat the value as a 64-bit integer value.
+  return AddInt64(bit_cast<int64_t, double>(v));
+}
+
+int ConstantArea::AddFloat(float v) {
+  // Treat the value as a 32-bit integer value.
+  return AddInt32(bit_cast<int32_t, float>(v));
+}
+
 }  // namespace x86
 }  // namespace art
diff --git a/compiler/utils/x86/assembler_x86.h b/compiler/utils/x86/assembler_x86.h
index 0c90f28..7d7b3d3 100644
--- a/compiler/utils/x86/assembler_x86.h
+++ b/compiler/utils/x86/assembler_x86.h
@@ -86,7 +86,7 @@
 
  protected:
   // Operand can be sub classed (e.g: Address).
-  Operand() : length_(0) { }
+  Operand() : length_(0), fixup_(nullptr) { }
 
   void SetModRM(int mod_in, Register rm_in) {
     CHECK_EQ(mod_in & ~3, 0);
@@ -113,11 +113,23 @@
     length_ += disp_size;
   }
 
+  AssemblerFixup* GetFixup() const {
+    return fixup_;
+  }
+
+  void SetFixup(AssemblerFixup* fixup) {
+    fixup_ = fixup;
+  }
+
  private:
   uint8_t length_;
   uint8_t encoding_[6];
 
-  explicit Operand(Register reg) { SetModRM(3, reg); }
+  // A fixup can be associated with the operand, in order to be applied after the
+  // code has been generated. This is used for constant area fixups.
+  AssemblerFixup* fixup_;
+
+  explicit Operand(Register reg) : fixup_(nullptr) { SetModRM(3, reg); }
 
   // Get the operand encoding byte at the given index.
   uint8_t encoding_at(int index_in) const {
@@ -136,6 +148,11 @@
     Init(base_in, disp);
   }
 
+  Address(Register base_in, int32_t disp, AssemblerFixup *fixup) {
+    Init(base_in, disp);
+    SetFixup(fixup);
+  }
+
   Address(Register base_in, Offset disp) {
     Init(base_in, disp.Int32Value());
   }
@@ -226,6 +243,50 @@
   DISALLOW_COPY_AND_ASSIGN(NearLabel);
 };
 
+/**
+ * Class to handle constant area values.
+ */
+class ConstantArea {
+ public:
+  ConstantArea() {}
+
+  // Add a double to the constant area, returning the offset into
+  // the constant area where the literal resides.
+  int AddDouble(double v);
+
+  // Add a float to the constant area, returning the offset into
+  // the constant area where the literal resides.
+  int AddFloat(float v);
+
+  // Add an int32_t to the constant area, returning the offset into
+  // the constant area where the literal resides.
+  int AddInt32(int32_t v);
+
+  // Add an int64_t to the constant area, returning the offset into
+  // the constant area where the literal resides.
+  int AddInt64(int64_t v);
+
+  bool IsEmpty() const {
+    return buffer_.size() == 0;
+  }
+
+  const std::vector<int32_t>& GetBuffer() const {
+    return buffer_;
+  }
+
+  void AddFixup(AssemblerFixup* fixup) {
+    fixups_.push_back(fixup);
+  }
+
+  const std::vector<AssemblerFixup*>& GetFixups() const {
+    return fixups_;
+  }
+
+ private:
+  static constexpr size_t kEntrySize = sizeof(int32_t);
+  std::vector<int32_t> buffer_;
+  std::vector<AssemblerFixup*> fixups_;
+};
 
 class X86Assembler FINAL : public Assembler {
  public:
@@ -667,6 +728,29 @@
     }
   }
 
+  // Add a double to the constant area, returning the offset into
+  // the constant area where the literal resides.
+  int AddDouble(double v) { return constant_area_.AddDouble(v); }
+
+  // Add a float to the constant area, returning the offset into
+  // the constant area where the literal resides.
+  int AddFloat(float v)   { return constant_area_.AddFloat(v); }
+
+  // Add an int32_t to the constant area, returning the offset into
+  // the constant area where the literal resides.
+  int AddInt32(int32_t v) { return constant_area_.AddInt32(v); }
+
+  // Add an int64_t to the constant area, returning the offset into
+  // the constant area where the literal resides.
+  int AddInt64(int64_t v) { return constant_area_.AddInt64(v); }
+
+  // Add the contents of the constant area to the assembler buffer.
+  void AddConstantArea();
+
+  // Is the constant area empty? Return true if there are no literals in the constant area.
+  bool IsConstantAreaEmpty() const { return constant_area_.IsEmpty(); }
+  void AddConstantAreaFixup(AssemblerFixup* fixup) { constant_area_.AddFixup(fixup); }
+
  private:
   inline void EmitUint8(uint8_t value);
   inline void EmitInt32(int32_t value);
@@ -685,6 +769,8 @@
   void EmitGenericShift(int rm, const Operand& operand, const Immediate& imm);
   void EmitGenericShift(int rm, const Operand& operand, Register shifter);
 
+  ConstantArea constant_area_;
+
   DISALLOW_COPY_AND_ASSIGN(X86Assembler);
 };
 
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index b59edc9..e248604 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -44,6 +44,7 @@
 #include "mapping_table.h"
 #include "mirror/array-inl.h"
 #include "mirror/class-inl.h"
+#include "mirror/dex_cache-inl.h"
 #include "mirror/object-inl.h"
 #include "mirror/object_array-inl.h"
 #include "oat.h"
@@ -1605,22 +1606,19 @@
       // stack. Need to revoke the thread-local allocation stacks that
       // point into it.
       ScopedThreadSuspension sts(self, kNative);
-      ThreadList* thread_list = Runtime::Current()->GetThreadList();
-      thread_list->SuspendAll(__FUNCTION__);
+      ScopedSuspendAll ssa(__FUNCTION__);
       heap->RevokeAllThreadLocalAllocationStacks(self);
-      thread_list->ResumeAll();
     }
     {
       // Mark dex caches.
-      dex_cache_arrays_.clear();
+      dex_caches_.clear();
       {
         ReaderMutexLock mu(self, *class_linker->DexLock());
         for (jobject weak_root : class_linker->GetDexCaches()) {
           mirror::DexCache* dex_cache =
               down_cast<mirror::DexCache*>(self->DecodeJObject(weak_root));
           if (dex_cache != nullptr) {
-            dex_cache_arrays_.insert(dex_cache->GetResolvedFields());
-            dex_cache_arrays_.insert(dex_cache->GetResolvedMethods());
+            dex_caches_.insert(dex_cache);
           }
         }
       }
@@ -1656,22 +1654,25 @@
     const auto& bitmap_section = image_header_.GetImageSection(ImageHeader::kSectionImageBitmap);
     const auto& field_section = image_header_.GetImageSection(ImageHeader::kSectionArtFields);
     const auto& method_section = image_header_.GetMethodsSection();
+    const auto& dex_cache_arrays_section = image_header_.GetImageSection(
+        ImageHeader::kSectionDexCacheArrays);
     const auto& intern_section = image_header_.GetImageSection(
         ImageHeader::kSectionInternedStrings);
     stats_.header_bytes = header_bytes;
     stats_.alignment_bytes += RoundUp(header_bytes, kObjectAlignment) - header_bytes;
     // Add padding between the field and method section.
     // (Field section is 4-byte aligned, method section is 8-byte aligned on 64-bit targets.)
-    stats_.alignment_bytes +=
-        method_section.Offset() - (field_section.Offset() + field_section.Size());
-    // Add padding between the method section and the intern table.
-    // (Method section is 4-byte aligned on 32-bit targets, intern table is 8-byte aligned.)
-    stats_.alignment_bytes +=
-        intern_section.Offset() - (method_section.Offset() + method_section.Size());
+    stats_.alignment_bytes += method_section.Offset() -
+        (field_section.Offset() + field_section.Size());
+    // Add padding between the dex cache arrays section and the intern table. (Dex cache
+    // arrays section is 4-byte aligned on 32-bit targets, intern table is 8-byte aligned.)
+    stats_.alignment_bytes += intern_section.Offset() -
+        (dex_cache_arrays_section.Offset() + dex_cache_arrays_section.Size());
     stats_.alignment_bytes += bitmap_section.Offset() - image_header_.GetImageSize();
     stats_.bitmap_bytes += bitmap_section.Size();
     stats_.art_field_bytes += field_section.Size();
     stats_.art_method_bytes += method_section.Size();
+    stats_.dex_cache_arrays_bytes += dex_cache_arrays_section.Size();
     stats_.interned_strings_bytes += intern_section.Size();
     stats_.Dump(os, indent_os);
     os << "\n";
@@ -1878,33 +1879,73 @@
         }
       }
     } else {
-      auto it = state->dex_cache_arrays_.find(obj);
-      if (it != state->dex_cache_arrays_.end()) {
+      auto it = state->dex_caches_.find(obj);
+      if (it != state->dex_caches_.end()) {
+        auto* dex_cache = down_cast<mirror::DexCache*>(obj);
         const auto& field_section = state->image_header_.GetImageSection(
             ImageHeader::kSectionArtFields);
         const auto& method_section = state->image_header_.GetMethodsSection();
-        auto* arr = down_cast<mirror::PointerArray*>(obj);
-        for (int32_t i = 0, length = arr->GetLength(); i < length; i++) {
-          void* elem = arr->GetElementPtrSize<void*>(i, image_pointer_size);
-          size_t run = 0;
-          for (int32_t j = i + 1; j < length &&
-              elem == arr->GetElementPtrSize<void*>(j, image_pointer_size); j++, run++) { }
-          if (run == 0) {
-            os << StringPrintf("%d: ", i);
-          } else {
-            os << StringPrintf("%d to %zd: ", i, i + run);
-            i = i + run;
+        size_t num_methods = dex_cache->NumResolvedMethods();
+        if (num_methods != 0u) {
+          os << "Methods (size=" << num_methods << "):";
+          ScopedIndentation indent2(&state->vios_);
+          auto* resolved_methods = dex_cache->GetResolvedMethods();
+          for (size_t i = 0, length = dex_cache->NumResolvedMethods(); i < length; ++i) {
+            auto* elem = mirror::DexCache::GetElementPtrSize(resolved_methods, i, image_pointer_size);
+            size_t run = 0;
+            for (size_t j = i + 1;
+                j != length && elem == mirror::DexCache::GetElementPtrSize(resolved_methods,
+                                                                           j,
+                                                                           image_pointer_size);
+                ++j, ++run) {}
+            if (run == 0) {
+              os << StringPrintf("%zd: ", i);
+            } else {
+              os << StringPrintf("%zd to %zd: ", i, i + run);
+              i = i + run;
+            }
+            std::string msg;
+            if (elem == nullptr) {
+              msg = "null";
+            } else if (method_section.Contains(
+                reinterpret_cast<uint8_t*>(elem) - state->image_space_.Begin())) {
+              msg = PrettyMethod(reinterpret_cast<ArtMethod*>(elem));
+            } else {
+              msg = "<not in method section>";
+            }
+            os << StringPrintf("%p   %s\n", elem, msg.c_str());
           }
-          auto offset = reinterpret_cast<uint8_t*>(elem) - state->image_space_.Begin();
-          std::string msg;
-          if (field_section.Contains(offset)) {
-            msg = PrettyField(reinterpret_cast<ArtField*>(elem));
-          } else if (method_section.Contains(offset)) {
-            msg = PrettyMethod(reinterpret_cast<ArtMethod*>(elem));
-          } else {
-            msg = "Unknown type";
+        }
+        size_t num_fields = dex_cache->NumResolvedFields();
+        if (num_fields != 0u) {
+          os << "Fields (size=" << num_fields << "):";
+          ScopedIndentation indent2(&state->vios_);
+          auto* resolved_fields = dex_cache->GetResolvedFields();
+          for (size_t i = 0, length = dex_cache->NumResolvedFields(); i < length; ++i) {
+            auto* elem = mirror::DexCache::GetElementPtrSize(resolved_fields, i, image_pointer_size);
+            size_t run = 0;
+            for (size_t j = i + 1;
+                j != length && elem == mirror::DexCache::GetElementPtrSize(resolved_fields,
+                                                                           j,
+                                                                           image_pointer_size);
+                ++j, ++run) {}
+            if (run == 0) {
+              os << StringPrintf("%zd: ", i);
+            } else {
+              os << StringPrintf("%zd to %zd: ", i, i + run);
+              i = i + run;
+            }
+            std::string msg;
+            if (elem == nullptr) {
+              msg = "null";
+            } else if (field_section.Contains(
+                reinterpret_cast<uint8_t*>(elem) - state->image_space_.Begin())) {
+              msg = PrettyField(reinterpret_cast<ArtField*>(elem));
+            } else {
+              msg = "<not in field section>";
+            }
+            os << StringPrintf("%p   %s\n", elem, msg.c_str());
           }
-          os << StringPrintf("%p   %s\n", elem, msg.c_str());
         }
       }
     }
@@ -2019,6 +2060,7 @@
     size_t object_bytes;
     size_t art_field_bytes;
     size_t art_method_bytes;
+    size_t dex_cache_arrays_bytes;
     size_t interned_strings_bytes;
     size_t bitmap_bytes;
     size_t alignment_bytes;
@@ -2049,6 +2091,7 @@
           object_bytes(0),
           art_field_bytes(0),
           art_method_bytes(0),
+          dex_cache_arrays_bytes(0),
           interned_strings_bytes(0),
           bitmap_bytes(0),
           alignment_bytes(0),
@@ -2206,24 +2249,27 @@
       {
         os << "art_file_bytes = " << PrettySize(file_bytes) << "\n\n"
            << "art_file_bytes = header_bytes + object_bytes + alignment_bytes\n";
-        indent_os << StringPrintf("header_bytes          =  %8zd (%2.0f%% of art file bytes)\n"
-                                  "object_bytes          =  %8zd (%2.0f%% of art file bytes)\n"
-                                  "art_field_bytes       =  %8zd (%2.0f%% of art file bytes)\n"
-                                  "art_method_bytes      =  %8zd (%2.0f%% of art file bytes)\n"
-                                  "interned_string_bytes =  %8zd (%2.0f%% of art file bytes)\n"
-                                  "bitmap_bytes          =  %8zd (%2.0f%% of art file bytes)\n"
-                                  "alignment_bytes       =  %8zd (%2.0f%% of art file bytes)\n\n",
+        indent_os << StringPrintf("header_bytes           =  %8zd (%2.0f%% of art file bytes)\n"
+                                  "object_bytes           =  %8zd (%2.0f%% of art file bytes)\n"
+                                  "art_field_bytes        =  %8zd (%2.0f%% of art file bytes)\n"
+                                  "art_method_bytes       =  %8zd (%2.0f%% of art file bytes)\n"
+                                  "dex_cache_arrays_bytes =  %8zd (%2.0f%% of art file bytes)\n"
+                                  "interned_string_bytes  =  %8zd (%2.0f%% of art file bytes)\n"
+                                  "bitmap_bytes           =  %8zd (%2.0f%% of art file bytes)\n"
+                                  "alignment_bytes        =  %8zd (%2.0f%% of art file bytes)\n\n",
                                   header_bytes, PercentOfFileBytes(header_bytes),
                                   object_bytes, PercentOfFileBytes(object_bytes),
                                   art_field_bytes, PercentOfFileBytes(art_field_bytes),
                                   art_method_bytes, PercentOfFileBytes(art_method_bytes),
+                                  dex_cache_arrays_bytes,
+                                  PercentOfFileBytes(dex_cache_arrays_bytes),
                                   interned_strings_bytes,
                                   PercentOfFileBytes(interned_strings_bytes),
                                   bitmap_bytes, PercentOfFileBytes(bitmap_bytes),
                                   alignment_bytes, PercentOfFileBytes(alignment_bytes))
             << std::flush;
         CHECK_EQ(file_bytes, header_bytes + object_bytes + art_field_bytes + art_method_bytes +
-                 interned_strings_bytes + bitmap_bytes + alignment_bytes);
+                 dex_cache_arrays_bytes + interned_strings_bytes + bitmap_bytes + alignment_bytes);
       }
 
       os << "object_bytes breakdown:\n";
@@ -2309,7 +2355,7 @@
   const ImageHeader& image_header_;
   std::unique_ptr<OatDumper> oat_dumper_;
   OatDumperOptions* oat_dumper_options_;
-  std::set<mirror::Object*> dex_cache_arrays_;
+  std::set<mirror::Object*> dex_caches_;
 
   DISALLOW_COPY_AND_ASSIGN(ImageDumper);
 };
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc
index a71197a..88622cc 100644
--- a/patchoat/patchoat.cc
+++ b/patchoat/patchoat.cc
@@ -523,18 +523,60 @@
   auto* dex_caches = down_cast<mirror::ObjectArray<mirror::DexCache>*>(
       img_roots->Get(ImageHeader::kDexCaches));
   for (size_t i = 0, count = dex_caches->GetLength(); i < count; ++i) {
-    auto* dex_cache = dex_caches->GetWithoutChecks(i);
-    auto* fields = dex_cache->GetResolvedFields();
-    if (fields != nullptr) {
-      CHECK(!fields->IsObjectArray());
-      CHECK(fields->IsArrayInstance());
-      FixupNativePointerArray(fields);
+    auto* orig_dex_cache = dex_caches->GetWithoutChecks(i);
+    auto* copy_dex_cache = RelocatedCopyOf(orig_dex_cache);
+    const size_t pointer_size = InstructionSetPointerSize(isa_);
+    // Though the DexCache array fields are usually treated as native pointers, we set the full
+    // 64-bit values here, clearing the top 32 bits for 32-bit targets. The zero-extension is
+    // done by casting to the unsigned type uintptr_t before casting to int64_t, i.e.
+    //     static_cast<int64_t>(reinterpret_cast<uintptr_t>(image_begin_ + offset))).
+    GcRoot<mirror::String>* orig_strings = orig_dex_cache->GetStrings();
+    GcRoot<mirror::String>* relocated_strings = RelocatedAddressOfPointer(orig_strings);
+    copy_dex_cache->SetField64<false>(
+        mirror::DexCache::StringsOffset(),
+        static_cast<int64_t>(reinterpret_cast<uintptr_t>(relocated_strings)));
+    if (orig_strings != nullptr) {
+      GcRoot<mirror::String>* copy_strings = RelocatedCopyOf(orig_strings);
+      for (size_t j = 0, num = orig_dex_cache->NumStrings(); j != num; ++j) {
+        copy_strings[j] = GcRoot<mirror::String>(RelocatedAddressOfPointer(orig_strings[j].Read()));
+      }
     }
-    auto* methods = dex_cache->GetResolvedMethods();
-    if (methods != nullptr) {
-      CHECK(!methods->IsObjectArray());
-      CHECK(methods->IsArrayInstance());
-      FixupNativePointerArray(methods);
+    GcRoot<mirror::Class>* orig_types = orig_dex_cache->GetResolvedTypes();
+    GcRoot<mirror::Class>* relocated_types = RelocatedAddressOfPointer(orig_types);
+    copy_dex_cache->SetField64<false>(
+        mirror::DexCache::ResolvedTypesOffset(),
+        static_cast<int64_t>(reinterpret_cast<uintptr_t>(relocated_types)));
+    if (orig_types != nullptr) {
+      GcRoot<mirror::Class>* copy_types = RelocatedCopyOf(orig_types);
+      for (size_t j = 0, num = orig_dex_cache->NumResolvedTypes(); j != num; ++j) {
+        copy_types[j] = GcRoot<mirror::Class>(RelocatedAddressOfPointer(orig_types[j].Read()));
+      }
+    }
+    ArtMethod** orig_methods = orig_dex_cache->GetResolvedMethods();
+    ArtMethod** relocated_methods = RelocatedAddressOfPointer(orig_methods);
+    copy_dex_cache->SetField64<false>(
+        mirror::DexCache::ResolvedMethodsOffset(),
+        static_cast<int64_t>(reinterpret_cast<uintptr_t>(relocated_methods)));
+    if (orig_methods != nullptr) {
+      ArtMethod** copy_methods = RelocatedCopyOf(orig_methods);
+      for (size_t j = 0, num = orig_dex_cache->NumResolvedMethods(); j != num; ++j) {
+        ArtMethod* orig = mirror::DexCache::GetElementPtrSize(orig_methods, j, pointer_size);
+        ArtMethod* copy = RelocatedAddressOfPointer(orig);
+        mirror::DexCache::SetElementPtrSize(copy_methods, j, copy, pointer_size);
+      }
+    }
+    ArtField** orig_fields = orig_dex_cache->GetResolvedFields();
+    ArtField** relocated_fields = RelocatedAddressOfPointer(orig_fields);
+    copy_dex_cache->SetField64<false>(
+        mirror::DexCache::ResolvedFieldsOffset(),
+        static_cast<int64_t>(reinterpret_cast<uintptr_t>(relocated_fields)));
+    if (orig_fields != nullptr) {
+      ArtField** copy_fields = RelocatedCopyOf(orig_fields);
+      for (size_t j = 0, num = orig_dex_cache->NumResolvedFields(); j != num; ++j) {
+        ArtField* orig = mirror::DexCache::GetElementPtrSize(orig_fields, j, pointer_size);
+        ArtField* copy = RelocatedAddressOfPointer(orig);
+        mirror::DexCache::SetElementPtrSize(copy_fields, j, copy, pointer_size);
+      }
     }
   }
 }
@@ -627,6 +669,7 @@
   if (object->IsClass<kVerifyNone>()) {
     auto* klass = object->AsClass();
     auto* copy_klass = down_cast<mirror::Class*>(copy);
+    copy_klass->SetDexCacheStrings(RelocatedAddressOfPointer(klass->GetDexCacheStrings()));
     copy_klass->SetSFieldsPtrUnchecked(RelocatedAddressOfPointer(klass->GetSFieldsPtr()));
     copy_klass->SetIFieldsPtrUnchecked(RelocatedAddressOfPointer(klass->GetIFieldsPtr()));
     copy_klass->SetDirectMethodsPtrUnchecked(
@@ -673,8 +716,10 @@
   // Just update the entry points if it looks like we should.
   // TODO: sanity check all the pointers' values
   copy->SetDeclaringClass(RelocatedAddressOfPointer(object->GetDeclaringClass()));
-  copy->SetDexCacheResolvedMethods(RelocatedAddressOfPointer(object->GetDexCacheResolvedMethods()));
-  copy->SetDexCacheResolvedTypes(RelocatedAddressOfPointer(object->GetDexCacheResolvedTypes()));
+  copy->SetDexCacheResolvedMethods(
+      RelocatedAddressOfPointer(object->GetDexCacheResolvedMethods(pointer_size)), pointer_size);
+  copy->SetDexCacheResolvedTypes(
+      RelocatedAddressOfPointer(object->GetDexCacheResolvedTypes(pointer_size)), pointer_size);
   copy->SetEntryPointFromQuickCompiledCodePtrSize(RelocatedAddressOfPointer(
       object->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size)), pointer_size);
   copy->SetEntryPointFromJniPtrSize(RelocatedAddressOfPointer(
diff --git a/runtime/Android.mk b/runtime/Android.mk
index 8f70d30..995a1d5 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -99,7 +99,11 @@
   jit/jit.cc \
   jit/jit_code_cache.cc \
   jit/jit_instrumentation.cc \
+  jit/profiling_info.cc \
+  lambda/art_lambda_method.cc \
   lambda/box_table.cc \
+  lambda/closure.cc \
+  lambda/closure_builder.cc \
   jni_internal.cc \
   jobject_comparator.cc \
   linear_alloc.cc \
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index 1498a4b..861f802 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -926,13 +926,11 @@
     int3
 #endif
     // Might need a special macro since rsi and edx is 32b/64b mismatched.
-    movl ART_METHOD_DEX_CACHE_TYPES_OFFSET(%rsi), %edx  // Load dex cache resolved types array
-    UNPOISON_HEAP_REF edx
+    movq ART_METHOD_DEX_CACHE_TYPES_OFFSET_64(%rsi), %rdx  // Load dex cache resolved types array
     // TODO: Add read barrier when this function is used.
     // Might need to break down into multiple instructions to get the base address in a register.
                                                                // Load the class
-    movl MIRROR_OBJECT_ARRAY_DATA_OFFSET(%rdx, %rdi, MIRROR_OBJECT_ARRAY_COMPONENT_SIZE), %edx
-    UNPOISON_HEAP_REF edx
+    movl 0(%rdx, %rdi, COMPRESSED_REFERENCE_SIZE), %edx
     testl %edx, %edx                                           // Check null class
     jz   .Lart_quick_alloc_object_tlab_slow_path
                                                                // Check class status.
diff --git a/runtime/art_field-inl.h b/runtime/art_field-inl.h
index 5138cc9..4166e22 100644
--- a/runtime/art_field-inl.h
+++ b/runtime/art_field-inl.h
@@ -24,7 +24,7 @@
 #include "gc_root-inl.h"
 #include "gc/accounting/card_table-inl.h"
 #include "jvalue.h"
-#include "mirror/dex_cache.h"
+#include "mirror/dex_cache-inl.h"
 #include "mirror/object-inl.h"
 #include "primitive.h"
 #include "thread-inl.h"
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index d38cc56..a84c20a 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -22,11 +22,13 @@
 #include "art_field.h"
 #include "base/logging.h"
 #include "class_linker-inl.h"
+#include "common_throws.h"
 #include "dex_file.h"
 #include "dex_file-inl.h"
 #include "gc_root-inl.h"
+#include "jit/profiling_info.h"
 #include "mirror/class-inl.h"
-#include "mirror/dex_cache.h"
+#include "mirror/dex_cache-inl.h"
 #include "mirror/object-inl.h"
 #include "mirror/object_array.h"
 #include "oat.h"
@@ -95,14 +97,20 @@
   return dex_method_index_;
 }
 
-inline mirror::PointerArray* ArtMethod::GetDexCacheResolvedMethods() {
-  GcRootSource gc_root_source(this);
-  return dex_cache_resolved_methods_.Read(&gc_root_source);
+inline ArtMethod** ArtMethod::GetDexCacheResolvedMethods(size_t pointer_size) {
+  return GetNativePointer<ArtMethod**>(DexCacheResolvedMethodsOffset(pointer_size),
+                                       pointer_size);
 }
 
 inline ArtMethod* ArtMethod::GetDexCacheResolvedMethod(uint16_t method_index, size_t ptr_size) {
-  auto* method = GetDexCacheResolvedMethods()->GetElementPtrSize<ArtMethod*>(
-      method_index, ptr_size);
+  // NOTE: Unchecked, i.e. not throwing AIOOB. We don't even know the length here
+  // without accessing the DexCache and we don't want to do that in release build.
+  DCHECK_LT(method_index,
+            GetInterfaceMethodIfProxy(ptr_size)->GetDeclaringClass()
+                ->GetDexCache()->NumResolvedMethods());
+  ArtMethod* method = mirror::DexCache::GetElementPtrSize(GetDexCacheResolvedMethods(ptr_size),
+                                                          method_index,
+                                                          ptr_size);
   if (LIKELY(method != nullptr)) {
     auto* declaring_class = method->GetDeclaringClass();
     if (LIKELY(declaring_class == nullptr || !declaring_class->IsErroneous())) {
@@ -112,52 +120,70 @@
   return nullptr;
 }
 
-inline void ArtMethod::SetDexCacheResolvedMethod(uint16_t method_idx, ArtMethod* new_method,
+inline void ArtMethod::SetDexCacheResolvedMethod(uint16_t method_index, ArtMethod* new_method,
                                                  size_t ptr_size) {
+  // NOTE: Unchecked, i.e. not throwing AIOOB. We don't even know the length here
+  // without accessing the DexCache and we don't want to do that in release build.
+  DCHECK_LT(method_index,
+            GetInterfaceMethodIfProxy(ptr_size)->GetDeclaringClass()
+                ->GetDexCache()->NumResolvedMethods());
   DCHECK(new_method == nullptr || new_method->GetDeclaringClass() != nullptr);
-  GetDexCacheResolvedMethods()->SetElementPtrSize(method_idx, new_method, ptr_size);
+  mirror::DexCache::SetElementPtrSize(GetDexCacheResolvedMethods(ptr_size),
+                                      method_index,
+                                      new_method,
+                                      ptr_size);
 }
 
-inline bool ArtMethod::HasDexCacheResolvedMethods() {
-  return GetDexCacheResolvedMethods() != nullptr;
+inline bool ArtMethod::HasDexCacheResolvedMethods(size_t pointer_size) {
+  return GetDexCacheResolvedMethods(pointer_size) != nullptr;
 }
 
-inline bool ArtMethod::HasSameDexCacheResolvedMethods(mirror::PointerArray* other_cache) {
-  return GetDexCacheResolvedMethods() == other_cache;
+inline bool ArtMethod::HasSameDexCacheResolvedMethods(ArtMethod** other_cache,
+                                                      size_t pointer_size) {
+  return GetDexCacheResolvedMethods(pointer_size) == other_cache;
 }
 
-inline bool ArtMethod::HasSameDexCacheResolvedMethods(ArtMethod* other) {
-  return GetDexCacheResolvedMethods() == other->GetDexCacheResolvedMethods();
+inline bool ArtMethod::HasSameDexCacheResolvedMethods(ArtMethod* other, size_t pointer_size) {
+  return GetDexCacheResolvedMethods(pointer_size) ==
+      other->GetDexCacheResolvedMethods(pointer_size);
 }
 
-inline mirror::ObjectArray<mirror::Class>* ArtMethod::GetDexCacheResolvedTypes() {
-  GcRootSource gc_root_source(this);
-  return dex_cache_resolved_types_.Read(&gc_root_source);
+inline GcRoot<mirror::Class>* ArtMethod::GetDexCacheResolvedTypes(size_t pointer_size) {
+  return GetNativePointer<GcRoot<mirror::Class>*>(DexCacheResolvedTypesOffset(pointer_size),
+                                                  pointer_size);
 }
 
 template <bool kWithCheck>
-inline mirror::Class* ArtMethod::GetDexCacheResolvedType(uint32_t type_index) {
-  mirror::Class* klass = kWithCheck ?
-      GetDexCacheResolvedTypes()->Get(type_index) :
-      GetDexCacheResolvedTypes()->GetWithoutChecks(type_index);
+inline mirror::Class* ArtMethod::GetDexCacheResolvedType(uint32_t type_index, size_t ptr_size) {
+  if (kWithCheck) {
+    mirror::DexCache* dex_cache =
+        GetInterfaceMethodIfProxy(ptr_size)->GetDeclaringClass()->GetDexCache();
+    if (UNLIKELY(type_index >= dex_cache->NumResolvedTypes())) {
+      ThrowArrayIndexOutOfBoundsException(type_index, dex_cache->NumResolvedTypes());
+      return nullptr;
+    }
+  }
+  mirror::Class* klass = GetDexCacheResolvedTypes(ptr_size)[type_index].Read();
   return (klass != nullptr && !klass->IsErroneous()) ? klass : nullptr;
 }
 
-inline bool ArtMethod::HasDexCacheResolvedTypes() {
-  return GetDexCacheResolvedTypes() != nullptr;
+inline bool ArtMethod::HasDexCacheResolvedTypes(size_t pointer_size) {
+  return GetDexCacheResolvedTypes(pointer_size) != nullptr;
 }
 
-inline bool ArtMethod::HasSameDexCacheResolvedTypes(
-    mirror::ObjectArray<mirror::Class>* other_cache) {
-  return GetDexCacheResolvedTypes() == other_cache;
+inline bool ArtMethod::HasSameDexCacheResolvedTypes(GcRoot<mirror::Class>* other_cache,
+                                                    size_t pointer_size) {
+  return GetDexCacheResolvedTypes(pointer_size) == other_cache;
 }
 
-inline bool ArtMethod::HasSameDexCacheResolvedTypes(ArtMethod* other) {
-  return GetDexCacheResolvedTypes() == other->GetDexCacheResolvedTypes();
+inline bool ArtMethod::HasSameDexCacheResolvedTypes(ArtMethod* other, size_t pointer_size) {
+  return GetDexCacheResolvedTypes(pointer_size) == other->GetDexCacheResolvedTypes(pointer_size);
 }
 
-inline mirror::Class* ArtMethod::GetClassFromTypeIndex(uint16_t type_idx, bool resolve) {
-  mirror::Class* type = GetDexCacheResolvedType(type_idx);
+inline mirror::Class* ArtMethod::GetClassFromTypeIndex(uint16_t type_idx,
+                                                       bool resolve,
+                                                       size_t ptr_size) {
+  mirror::Class* type = GetDexCacheResolvedType(type_idx, ptr_size);
   if (type == nullptr && resolve) {
     type = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, this);
     CHECK(type != nullptr || Thread::Current()->IsExceptionPending());
@@ -391,9 +417,9 @@
   return GetDeclaringClass()->GetDexFile().GetCodeItem(GetCodeItemOffset());
 }
 
-inline bool ArtMethod::IsResolvedTypeIdx(uint16_t type_idx) {
+inline bool ArtMethod::IsResolvedTypeIdx(uint16_t type_idx, size_t ptr_size) {
   DCHECK(!IsProxyMethod());
-  return GetDexCacheResolvedType(type_idx) != nullptr;
+  return GetDexCacheResolvedType(type_idx, ptr_size) != nullptr;
 }
 
 inline int32_t ArtMethod::GetLineNumFromDexPC(uint32_t dex_pc) {
@@ -467,30 +493,33 @@
     return this;
   }
   mirror::Class* klass = GetDeclaringClass();
-  auto interface_method = GetDexCacheResolvedMethods()->GetElementPtrSize<ArtMethod*>(
-      GetDexMethodIndex(), pointer_size);
+  ArtMethod* interface_method = mirror::DexCache::GetElementPtrSize(
+      GetDexCacheResolvedMethods(pointer_size),
+      GetDexMethodIndex(),
+      pointer_size);
   DCHECK(interface_method != nullptr);
   DCHECK_EQ(interface_method,
             Runtime::Current()->GetClassLinker()->FindMethodForProxy(klass, this));
   return interface_method;
 }
 
-inline void ArtMethod::SetDexCacheResolvedMethods(mirror::PointerArray* new_dex_cache_methods) {
-  dex_cache_resolved_methods_ = GcRoot<mirror::PointerArray>(new_dex_cache_methods);
+inline void ArtMethod::SetDexCacheResolvedMethods(ArtMethod** new_dex_cache_methods,
+                                                  size_t ptr_size) {
+  SetNativePointer(DexCacheResolvedMethodsOffset(ptr_size), new_dex_cache_methods, ptr_size);
 }
 
-inline void ArtMethod::SetDexCacheResolvedTypes(
-    mirror::ObjectArray<mirror::Class>* new_dex_cache_types) {
-  dex_cache_resolved_types_ = GcRoot<mirror::ObjectArray<mirror::Class>>(new_dex_cache_types);
+inline void ArtMethod::SetDexCacheResolvedTypes(GcRoot<mirror::Class>* new_dex_cache_types,
+                                                size_t ptr_size) {
+  SetNativePointer(DexCacheResolvedTypesOffset(ptr_size), new_dex_cache_types, ptr_size);
 }
 
-inline mirror::Class* ArtMethod::GetReturnType(bool resolve) {
+inline mirror::Class* ArtMethod::GetReturnType(bool resolve, size_t ptr_size) {
   DCHECK(!IsProxyMethod());
   const DexFile* dex_file = GetDexFile();
   const DexFile::MethodId& method_id = dex_file->GetMethodId(GetDexMethodIndex());
   const DexFile::ProtoId& proto_id = dex_file->GetMethodPrototype(method_id);
   uint16_t return_type_idx = proto_id.return_type_idx_;
-  mirror::Class* type = GetDexCacheResolvedType(return_type_idx);
+  mirror::Class* type = GetDexCacheResolvedType(return_type_idx, ptr_size);
   if (type == nullptr && resolve) {
     type = Runtime::Current()->GetClassLinker()->ResolveType(return_type_idx, this);
     CHECK(type != nullptr || Thread::Current()->IsExceptionPending());
@@ -500,19 +529,33 @@
 
 template<typename RootVisitorType>
 void ArtMethod::VisitRoots(RootVisitorType& visitor) {
+  ArtMethod* interface_method = nullptr;
+  mirror::Class* klass = declaring_class_.Read();
+  if (UNLIKELY(klass != nullptr && klass->IsProxyClass())) {
+    // For normal methods, dex cache shortcuts will be visited through the declaring class.
+    // However, for proxies we need to keep the interface method alive, so we visit its roots.
+    size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
+    interface_method = mirror::DexCache::GetElementPtrSize(
+        GetDexCacheResolvedMethods(pointer_size),
+        GetDexMethodIndex(),
+        pointer_size);
+    DCHECK(interface_method != nullptr);
+    DCHECK_EQ(interface_method,
+              Runtime::Current()->GetClassLinker()->FindMethodForProxy(klass, this));
+    interface_method->VisitRoots(visitor);
+  }
+
   visitor.VisitRootIfNonNull(declaring_class_.AddressWithoutBarrier());
-  visitor.VisitRootIfNonNull(dex_cache_resolved_methods_.AddressWithoutBarrier());
-  visitor.VisitRootIfNonNull(dex_cache_resolved_types_.AddressWithoutBarrier());
+  ProfilingInfo* profiling_info = GetProfilingInfo();
+  if (hotness_count_ != 0 && !IsNative() && profiling_info != nullptr) {
+    profiling_info->VisitRoots(visitor);
+  }
 }
 
 inline void ArtMethod::CopyFrom(const ArtMethod* src, size_t image_pointer_size) {
   memcpy(reinterpret_cast<void*>(this), reinterpret_cast<const void*>(src),
          Size(image_pointer_size));
   declaring_class_ = GcRoot<mirror::Class>(const_cast<ArtMethod*>(src)->GetDeclaringClass());
-  dex_cache_resolved_methods_ = GcRoot<mirror::PointerArray>(
-      const_cast<ArtMethod*>(src)->GetDexCacheResolvedMethods());
-  dex_cache_resolved_types_ = GcRoot<mirror::ObjectArray<mirror::Class>>(
-      const_cast<ArtMethod*>(src)->GetDexCacheResolvedTypes());
 }
 
 }  // namespace art
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index e46402d..5dbea52 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -30,6 +30,7 @@
 #include "interpreter/interpreter.h"
 #include "jit/jit.h"
 #include "jit/jit_code_cache.h"
+#include "jit/profiling_info.h"
 #include "jni_internal.h"
 #include "mapping_table.h"
 #include "mirror/abstract_method.h"
@@ -125,8 +126,9 @@
   } else {
     // Method didn't override superclass method so search interfaces
     if (IsProxyMethod()) {
-      result = GetDexCacheResolvedMethods()->GetElementPtrSize<ArtMethod*>(
-          GetDexMethodIndex(), pointer_size);
+      result = mirror::DexCache::GetElementPtrSize(GetDexCacheResolvedMethods(pointer_size),
+                                                   GetDexMethodIndex(),
+                                                   pointer_size);
       CHECK_EQ(result,
                Runtime::Current()->GetClassLinker()->FindMethodForProxy(GetDeclaringClass(), this));
     } else {
@@ -261,6 +263,7 @@
   // Default to handler not found.
   uint32_t found_dex_pc = DexFile::kDexNoIndex;
   // Iterate over the catch handlers associated with dex_pc.
+  size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
   for (CatchHandlerIterator it(*code_item, dex_pc); it.HasNext(); it.Next()) {
     uint16_t iter_type_idx = it.GetHandlerTypeIndex();
     // Catch all case
@@ -269,7 +272,9 @@
       break;
     }
     // Does this catch exception type apply?
-    mirror::Class* iter_exception_type = GetClassFromTypeIndex(iter_type_idx, true);
+    mirror::Class* iter_exception_type = GetClassFromTypeIndex(iter_type_idx,
+                                                               true /* resolve */,
+                                                               pointer_size);
     if (UNLIKELY(iter_exception_type == nullptr)) {
       // Now have a NoClassDefFoundError as exception. Ignore in case the exception class was
       // removed by a pro-guard like tool.
@@ -575,4 +580,16 @@
   return oat_method.GetVmapTable();
 }
 
+ProfilingInfo* ArtMethod::CreateProfilingInfo() {
+  ProfilingInfo* info = ProfilingInfo::Create(this);
+  MemberOffset offset = ArtMethod::EntryPointFromJniOffset(sizeof(void*));
+  uintptr_t pointer = reinterpret_cast<uintptr_t>(this) + offset.Uint32Value();
+  if (!reinterpret_cast<Atomic<ProfilingInfo*>*>(pointer)->
+          CompareExchangeStrongSequentiallyConsistent(nullptr, info)) {
+    return GetProfilingInfo();
+  } else {
+    return info;
+  }
+}
+
 }  // namespace art
diff --git a/runtime/art_method.h b/runtime/art_method.h
index 6cdc4a6..3f2161f 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -17,6 +17,7 @@
 #ifndef ART_RUNTIME_ART_METHOD_H_
 #define ART_RUNTIME_ART_METHOD_H_
 
+#include "base/casts.h"
 #include "dex_file.h"
 #include "gc_root.h"
 #include "invoke_type.h"
@@ -32,6 +33,7 @@
 namespace art {
 
 union JValue;
+class ProfilingInfo;
 class ScopedObjectAccessAlreadyRunnable;
 class StringPiece;
 class ShadowFrame;
@@ -212,41 +214,35 @@
     dex_method_index_ = new_idx;
   }
 
-  static MemberOffset DexCacheResolvedMethodsOffset() {
-    return OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_cache_resolved_methods_);
-  }
-
-  static MemberOffset DexCacheResolvedTypesOffset() {
-    return OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_cache_resolved_types_);
-  }
-
-  ALWAYS_INLINE mirror::PointerArray* GetDexCacheResolvedMethods()
+  ALWAYS_INLINE ArtMethod** GetDexCacheResolvedMethods(size_t pointer_size)
       SHARED_REQUIRES(Locks::mutator_lock_);
-  ALWAYS_INLINE ArtMethod* GetDexCacheResolvedMethod(uint16_t method_idx, size_t ptr_size)
+  ALWAYS_INLINE ArtMethod* GetDexCacheResolvedMethod(uint16_t method_index, size_t ptr_size)
       SHARED_REQUIRES(Locks::mutator_lock_);
-  ALWAYS_INLINE void SetDexCacheResolvedMethod(uint16_t method_idx, ArtMethod* new_method,
+  ALWAYS_INLINE void SetDexCacheResolvedMethod(uint16_t method_index,
+                                               ArtMethod* new_method,
                                                size_t ptr_size)
       SHARED_REQUIRES(Locks::mutator_lock_);
-  ALWAYS_INLINE void SetDexCacheResolvedMethods(mirror::PointerArray* new_dex_cache_methods)
+  ALWAYS_INLINE void SetDexCacheResolvedMethods(ArtMethod** new_dex_cache_methods, size_t ptr_size)
       SHARED_REQUIRES(Locks::mutator_lock_);
-  bool HasDexCacheResolvedMethods() SHARED_REQUIRES(Locks::mutator_lock_);
-  bool HasSameDexCacheResolvedMethods(ArtMethod* other)
+  bool HasDexCacheResolvedMethods(size_t pointer_size) SHARED_REQUIRES(Locks::mutator_lock_);
+  bool HasSameDexCacheResolvedMethods(ArtMethod* other, size_t pointer_size)
       SHARED_REQUIRES(Locks::mutator_lock_);
-  bool HasSameDexCacheResolvedMethods(mirror::PointerArray* other_cache)
+  bool HasSameDexCacheResolvedMethods(ArtMethod** other_cache, size_t pointer_size)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
   template <bool kWithCheck = true>
-  mirror::Class* GetDexCacheResolvedType(uint32_t type_idx)
+  mirror::Class* GetDexCacheResolvedType(uint32_t type_idx, size_t ptr_size)
       SHARED_REQUIRES(Locks::mutator_lock_);
-  void SetDexCacheResolvedTypes(mirror::ObjectArray<mirror::Class>* new_dex_cache_types)
+  void SetDexCacheResolvedTypes(GcRoot<mirror::Class>* new_dex_cache_types, size_t ptr_size)
       SHARED_REQUIRES(Locks::mutator_lock_);
-  bool HasDexCacheResolvedTypes() SHARED_REQUIRES(Locks::mutator_lock_);
-  bool HasSameDexCacheResolvedTypes(ArtMethod* other) SHARED_REQUIRES(Locks::mutator_lock_);
-  bool HasSameDexCacheResolvedTypes(mirror::ObjectArray<mirror::Class>* other_cache)
+  bool HasDexCacheResolvedTypes(size_t pointer_size) SHARED_REQUIRES(Locks::mutator_lock_);
+  bool HasSameDexCacheResolvedTypes(ArtMethod* other, size_t pointer_size)
+      SHARED_REQUIRES(Locks::mutator_lock_);
+  bool HasSameDexCacheResolvedTypes(GcRoot<mirror::Class>* other_cache, size_t pointer_size)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
   // Get the Class* from the type index into this method's dex cache.
-  mirror::Class* GetClassFromTypeIndex(uint16_t type_idx, bool resolve)
+  mirror::Class* GetClassFromTypeIndex(uint16_t type_idx, bool resolve, size_t ptr_size)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
   // Find the method that this method overrides.
@@ -267,7 +263,7 @@
     return GetEntryPointFromQuickCompiledCodePtrSize(sizeof(void*));
   }
   ALWAYS_INLINE const void* GetEntryPointFromQuickCompiledCodePtrSize(size_t pointer_size) {
-    return GetEntryPoint<const void*>(
+    return GetNativePointer<const void*>(
         EntryPointFromQuickCompiledCodeOffset(pointer_size), pointer_size);
   }
 
@@ -277,8 +273,8 @@
   }
   ALWAYS_INLINE void SetEntryPointFromQuickCompiledCodePtrSize(
       const void* entry_point_from_quick_compiled_code, size_t pointer_size) {
-    SetEntryPoint(EntryPointFromQuickCompiledCodeOffset(pointer_size),
-                  entry_point_from_quick_compiled_code, pointer_size);
+    SetNativePointer(EntryPointFromQuickCompiledCodeOffset(pointer_size),
+                     entry_point_from_quick_compiled_code, pointer_size);
   }
 
   uint32_t GetCodeSize() SHARED_REQUIRES(Locks::mutator_lock_);
@@ -374,6 +370,16 @@
 
   void UnregisterNative() SHARED_REQUIRES(Locks::mutator_lock_);
 
+  static MemberOffset DexCacheResolvedMethodsOffset(size_t pointer_size) {
+    return MemberOffset(PtrSizedFieldsOffset(pointer_size) + OFFSETOF_MEMBER(
+        PtrSizedFields, dex_cache_resolved_methods_) / sizeof(void*) * pointer_size);
+  }
+
+  static MemberOffset DexCacheResolvedTypesOffset(size_t pointer_size) {
+    return MemberOffset(PtrSizedFieldsOffset(pointer_size) + OFFSETOF_MEMBER(
+        PtrSizedFields, dex_cache_resolved_types_) / sizeof(void*) * pointer_size);
+  }
+
   static MemberOffset EntryPointFromJniOffset(size_t pointer_size) {
     return MemberOffset(PtrSizedFieldsOffset(pointer_size) + OFFSETOF_MEMBER(
         PtrSizedFields, entry_point_from_jni_) / sizeof(void*) * pointer_size);
@@ -384,18 +390,27 @@
         PtrSizedFields, entry_point_from_quick_compiled_code_) / sizeof(void*) * pointer_size);
   }
 
+  ProfilingInfo* CreateProfilingInfo() SHARED_REQUIRES(Locks::mutator_lock_);
+
+  ProfilingInfo* GetProfilingInfo() {
+    return reinterpret_cast<ProfilingInfo*>(GetEntryPointFromJni());
+  }
+
   void* GetEntryPointFromJni() {
     return GetEntryPointFromJniPtrSize(sizeof(void*));
   }
+
   ALWAYS_INLINE void* GetEntryPointFromJniPtrSize(size_t pointer_size) {
-    return GetEntryPoint<void*>(EntryPointFromJniOffset(pointer_size), pointer_size);
+    return GetNativePointer<void*>(EntryPointFromJniOffset(pointer_size), pointer_size);
   }
 
   void SetEntryPointFromJni(const void* entrypoint) SHARED_REQUIRES(Locks::mutator_lock_) {
+    DCHECK(IsNative());
     SetEntryPointFromJniPtrSize(entrypoint, sizeof(void*));
   }
+
   ALWAYS_INLINE void SetEntryPointFromJniPtrSize(const void* entrypoint, size_t pointer_size) {
-    SetEntryPoint(EntryPointFromJniOffset(pointer_size), entrypoint, pointer_size);
+    SetNativePointer(EntryPointFromJniOffset(pointer_size), entrypoint, pointer_size);
   }
 
   // Is this a CalleSaveMethod or ResolutionMethod and therefore doesn't adhere to normal
@@ -464,7 +479,7 @@
 
   const DexFile::CodeItem* GetCodeItem() SHARED_REQUIRES(Locks::mutator_lock_);
 
-  bool IsResolvedTypeIdx(uint16_t type_idx) SHARED_REQUIRES(Locks::mutator_lock_);
+  bool IsResolvedTypeIdx(uint16_t type_idx, size_t ptr_size) SHARED_REQUIRES(Locks::mutator_lock_);
 
   int32_t GetLineNumFromDexPC(uint32_t dex_pc) SHARED_REQUIRES(Locks::mutator_lock_);
 
@@ -485,7 +500,8 @@
 
   // May cause thread suspension due to GetClassFromTypeIdx calling ResolveType this caused a large
   // number of bugs at call sites.
-  mirror::Class* GetReturnType(bool resolve = true) SHARED_REQUIRES(Locks::mutator_lock_);
+  mirror::Class* GetReturnType(bool resolve, size_t ptr_size)
+      SHARED_REQUIRES(Locks::mutator_lock_);
 
   mirror::ClassLoader* GetClassLoader() SHARED_REQUIRES(Locks::mutator_lock_);
 
@@ -514,20 +530,18 @@
   void CopyFrom(const ArtMethod* src, size_t image_pointer_size)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
-  ALWAYS_INLINE mirror::ObjectArray<mirror::Class>* GetDexCacheResolvedTypes()
+  ALWAYS_INLINE GcRoot<mirror::Class>* GetDexCacheResolvedTypes(size_t pointer_size)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
+  uint16_t IncrementCounter() {
+    return ++hotness_count_;
+  }
+
  protected:
   // Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses".
   // The class we are a part of.
   GcRoot<mirror::Class> declaring_class_;
 
-  // Short cuts to declaring_class_->dex_cache_ member for fast compiled code access.
-  GcRoot<mirror::PointerArray> dex_cache_resolved_methods_;
-
-  // Short cuts to declaring_class_->dex_cache_ member for fast compiled code access.
-  GcRoot<mirror::ObjectArray<mirror::Class>> dex_cache_resolved_types_;
-
   // Access flags; low 16 bits are defined by spec.
   uint32_t access_flags_;
 
@@ -544,7 +558,11 @@
   // Entry within a dispatch table for this method. For static/direct methods the index is into
   // the declaringClass.directMethods, for virtual methods the vtable and for interface methods the
   // ifTable.
-  uint32_t method_index_;
+  uint16_t method_index_;
+
+  // The hotness we measure for this method. Incremented by the interpreter. Not atomic, as we allow
+  // missing increments: if the method is hot, we will see it eventually.
+  uint16_t hotness_count_;
 
   // Fake padding field gets inserted here.
 
@@ -552,7 +570,14 @@
   // PACKED(4) is necessary for the correctness of
   // RoundUp(OFFSETOF_MEMBER(ArtMethod, ptr_sized_fields_), pointer_size).
   struct PACKED(4) PtrSizedFields {
-    // Pointer to JNI function registered to this method, or a function to resolve the JNI function.
+    // Short cuts to declaring_class_->dex_cache_ member for fast compiled code access.
+    ArtMethod** dex_cache_resolved_methods_;
+
+    // Short cuts to declaring_class_->dex_cache_ member for fast compiled code access.
+    GcRoot<mirror::Class>* dex_cache_resolved_types_;
+
+    // Pointer to JNI function registered to this method, or a function to resolve the JNI function,
+    // or the profiling data for non-native methods.
     void* entry_point_from_jni_;
 
     // Method dispatch from quick compiled code invokes this pointer which may cause bridging into
@@ -567,26 +592,26 @@
   }
 
   template<typename T>
-  ALWAYS_INLINE T GetEntryPoint(MemberOffset offset, size_t pointer_size) const {
+  ALWAYS_INLINE T GetNativePointer(MemberOffset offset, size_t pointer_size) const {
+    static_assert(std::is_pointer<T>::value, "T must be a pointer type");
     DCHECK(ValidPointerSize(pointer_size)) << pointer_size;
     const auto addr = reinterpret_cast<uintptr_t>(this) + offset.Uint32Value();
     if (pointer_size == sizeof(uint32_t)) {
       return reinterpret_cast<T>(*reinterpret_cast<const uint32_t*>(addr));
     } else {
       auto v = *reinterpret_cast<const uint64_t*>(addr);
-      DCHECK_EQ(reinterpret_cast<uint64_t>(reinterpret_cast<T>(v)), v) << "Conversion lost bits";
-      return reinterpret_cast<T>(v);
+      return reinterpret_cast<T>(dchecked_integral_cast<uintptr_t>(v));
     }
   }
 
   template<typename T>
-  ALWAYS_INLINE void SetEntryPoint(MemberOffset offset, T new_value, size_t pointer_size) {
+  ALWAYS_INLINE void SetNativePointer(MemberOffset offset, T new_value, size_t pointer_size) {
+    static_assert(std::is_pointer<T>::value, "T must be a pointer type");
     DCHECK(ValidPointerSize(pointer_size)) << pointer_size;
     const auto addr = reinterpret_cast<uintptr_t>(this) + offset.Uint32Value();
     if (pointer_size == sizeof(uint32_t)) {
       uintptr_t ptr = reinterpret_cast<uintptr_t>(new_value);
-      DCHECK_EQ(static_cast<uint32_t>(ptr), ptr) << "Conversion lost bits";
-      *reinterpret_cast<uint32_t*>(addr) = static_cast<uint32_t>(ptr);
+      *reinterpret_cast<uint32_t*>(addr) = dchecked_integral_cast<uint32_t>(ptr);
     } else {
       *reinterpret_cast<uint64_t*>(addr) = reinterpret_cast<uintptr_t>(new_value);
     }
diff --git a/runtime/asm_support.h b/runtime/asm_support.h
index 5c1922e..04ff120 100644
--- a/runtime/asm_support.h
+++ b/runtime/asm_support.h
@@ -138,13 +138,13 @@
 #define MIRROR_CLASS_COMPONENT_TYPE_OFFSET (4 + MIRROR_OBJECT_HEADER_SIZE)
 ADD_TEST_EQ(MIRROR_CLASS_COMPONENT_TYPE_OFFSET,
             art::mirror::Class::ComponentTypeOffset().Int32Value())
-#define MIRROR_CLASS_ACCESS_FLAGS_OFFSET (36 + MIRROR_OBJECT_HEADER_SIZE)
+#define MIRROR_CLASS_ACCESS_FLAGS_OFFSET (72 + MIRROR_OBJECT_HEADER_SIZE)
 ADD_TEST_EQ(MIRROR_CLASS_ACCESS_FLAGS_OFFSET,
             art::mirror::Class::AccessFlagsOffset().Int32Value())
-#define MIRROR_CLASS_OBJECT_SIZE_OFFSET (100 + MIRROR_OBJECT_HEADER_SIZE)
+#define MIRROR_CLASS_OBJECT_SIZE_OFFSET (104 + MIRROR_OBJECT_HEADER_SIZE)
 ADD_TEST_EQ(MIRROR_CLASS_OBJECT_SIZE_OFFSET,
             art::mirror::Class::ObjectSizeOffset().Int32Value())
-#define MIRROR_CLASS_STATUS_OFFSET (112 + MIRROR_OBJECT_HEADER_SIZE)
+#define MIRROR_CLASS_STATUS_OFFSET (116 + MIRROR_OBJECT_HEADER_SIZE)
 ADD_TEST_EQ(MIRROR_CLASS_STATUS_OFFSET,
             art::mirror::Class::StatusOffset().Int32Value())
 
@@ -184,19 +184,27 @@
 ADD_TEST_EQ(MIRROR_STRING_VALUE_OFFSET, art::mirror::String::ValueOffset().Int32Value())
 
 // Offsets within java.lang.reflect.ArtMethod.
-#define ART_METHOD_DEX_CACHE_METHODS_OFFSET 4
-ADD_TEST_EQ(ART_METHOD_DEX_CACHE_METHODS_OFFSET,
-            art::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value())
+#define ART_METHOD_DEX_CACHE_METHODS_OFFSET_32 20
+ADD_TEST_EQ(ART_METHOD_DEX_CACHE_METHODS_OFFSET_32,
+            art::ArtMethod::DexCacheResolvedMethodsOffset(4).Int32Value())
 
-#define ART_METHOD_DEX_CACHE_TYPES_OFFSET 8
-ADD_TEST_EQ(ART_METHOD_DEX_CACHE_TYPES_OFFSET,
-            art::ArtMethod::DexCacheResolvedTypesOffset().Int32Value())
+#define ART_METHOD_DEX_CACHE_METHODS_OFFSET_64 24
+ADD_TEST_EQ(ART_METHOD_DEX_CACHE_METHODS_OFFSET_64,
+            art::ArtMethod::DexCacheResolvedMethodsOffset(8).Int32Value())
+
+#define ART_METHOD_DEX_CACHE_TYPES_OFFSET_32 24
+ADD_TEST_EQ(ART_METHOD_DEX_CACHE_TYPES_OFFSET_32,
+            art::ArtMethod::DexCacheResolvedTypesOffset(4).Int32Value())
+
+#define ART_METHOD_DEX_CACHE_TYPES_OFFSET_64 32
+ADD_TEST_EQ(ART_METHOD_DEX_CACHE_TYPES_OFFSET_64,
+            art::ArtMethod::DexCacheResolvedTypesOffset(8).Int32Value())
 
 #define ART_METHOD_QUICK_CODE_OFFSET_32 32
 ADD_TEST_EQ(ART_METHOD_QUICK_CODE_OFFSET_32,
             art::ArtMethod::EntryPointFromQuickCompiledCodeOffset(4).Int32Value())
 
-#define ART_METHOD_QUICK_CODE_OFFSET_64 40
+#define ART_METHOD_QUICK_CODE_OFFSET_64 48
 ADD_TEST_EQ(ART_METHOD_QUICK_CODE_OFFSET_64,
             art::ArtMethod::EntryPointFromQuickCompiledCodeOffset(8).Int32Value())
 
diff --git a/runtime/base/variant_map.h b/runtime/base/variant_map.h
index 1d7596a..82e5d2e 100644
--- a/runtime/base/variant_map.h
+++ b/runtime/base/variant_map.h
@@ -257,8 +257,7 @@
     if (ptr != nullptr) {
       return std::move(*ptr);
     } else {
-      TValue default_value = key.CreateDefaultValue();
-      return std::move(default_value);
+      return key.CreateDefaultValue();
     }
   }
 
diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h
index d2dbff6..88a3996 100644
--- a/runtime/class_linker-inl.h
+++ b/runtime/class_linker-inl.h
@@ -60,10 +60,11 @@
   return array_class;
 }
 
-inline mirror::String* ClassLinker::ResolveString(uint32_t string_idx,
-                                                  ArtMethod* referrer) {
+inline mirror::String* ClassLinker::ResolveString(uint32_t string_idx, ArtMethod* referrer) {
   mirror::Class* declaring_class = referrer->GetDeclaringClass();
-  mirror::String* resolved_string = declaring_class->GetDexCacheStrings()->Get(string_idx);
+  // MethodVerifier refuses methods with string_idx out of bounds.
+  DCHECK_LT(string_idx, declaring_class->GetDexCache()->NumStrings());
+  mirror::String* resolved_string = declaring_class->GetDexCacheStrings()[string_idx].Read();
   if (UNLIKELY(resolved_string == nullptr)) {
     StackHandleScope<1> hs(Thread::Current());
     Handle<mirror::DexCache> dex_cache(hs.NewHandle(declaring_class->GetDexCache()));
@@ -76,9 +77,8 @@
   return resolved_string;
 }
 
-inline mirror::Class* ClassLinker::ResolveType(uint16_t type_idx,
-                                               ArtMethod* referrer) {
-  mirror::Class* resolved_type = referrer->GetDexCacheResolvedType(type_idx);
+inline mirror::Class* ClassLinker::ResolveType(uint16_t type_idx, ArtMethod* referrer) {
+  mirror::Class* resolved_type = referrer->GetDexCacheResolvedType(type_idx, image_pointer_size_);
   if (UNLIKELY(resolved_type == nullptr)) {
     mirror::Class* declaring_class = referrer->GetDeclaringClass();
     StackHandleScope<2> hs(Thread::Current());
@@ -109,16 +109,17 @@
 }
 
 inline ArtMethod* ClassLinker::GetResolvedMethod(uint32_t method_idx, ArtMethod* referrer) {
-  ArtMethod* resolved_method = referrer->GetDexCacheResolvedMethod(
-      method_idx, image_pointer_size_);
+  ArtMethod* resolved_method = referrer->GetDexCacheResolvedMethod(method_idx, image_pointer_size_);
   if (resolved_method == nullptr || resolved_method->IsRuntimeMethod()) {
     return nullptr;
   }
   return resolved_method;
 }
 
-inline ArtMethod* ClassLinker::ResolveMethod(Thread* self, uint32_t method_idx,
-                                             ArtMethod* referrer, InvokeType type) {
+inline ArtMethod* ClassLinker::ResolveMethod(Thread* self,
+                                             uint32_t method_idx,
+                                             ArtMethod* referrer,
+                                             InvokeType type) {
   ArtMethod* resolved_method = GetResolvedMethod(method_idx, referrer);
   if (UNLIKELY(resolved_method == nullptr)) {
     mirror::Class* declaring_class = referrer->GetDeclaringClass();
@@ -126,7 +127,11 @@
     Handle<mirror::DexCache> h_dex_cache(hs.NewHandle(declaring_class->GetDexCache()));
     Handle<mirror::ClassLoader> h_class_loader(hs.NewHandle(declaring_class->GetClassLoader()));
     const DexFile* dex_file = h_dex_cache->GetDexFile();
-    resolved_method = ResolveMethod(*dex_file, method_idx, h_dex_cache, h_class_loader, referrer,
+    resolved_method = ResolveMethod(*dex_file,
+                                    method_idx,
+                                    h_dex_cache,
+                                    h_class_loader,
+                                    referrer,
                                     type);
   }
   // Note: We cannot check here to see whether we added the method to the cache. It
@@ -160,7 +165,8 @@
 }
 
 inline mirror::Object* ClassLinker::AllocObject(Thread* self) {
-  return GetClassRoot(kJavaLangObject)->Alloc<true, false>(self,
+  return GetClassRoot(kJavaLangObject)->Alloc<true, false>(
+      self,
       Runtime::Current()->GetHeap()->GetCurrentAllocator());
 }
 
@@ -176,13 +182,15 @@
 
 inline mirror::ObjectArray<mirror::String>* ClassLinker::AllocStringArray(Thread* self,
                                                                           size_t length) {
-  return mirror::ObjectArray<mirror::String>::Alloc(self, GetClassRoot(kJavaLangStringArrayClass),
+  return mirror::ObjectArray<mirror::String>::Alloc(self,
+                                                    GetClassRoot(kJavaLangStringArrayClass),
                                                     length);
 }
 
 inline mirror::IfTable* ClassLinker::AllocIfTable(Thread* self, size_t ifcount) {
   return down_cast<mirror::IfTable*>(
-      mirror::IfTable::Alloc(self, GetClassRoot(kObjectArrayClass),
+      mirror::IfTable::Alloc(self,
+                             GetClassRoot(kObjectArrayClass),
                              ifcount * mirror::IfTable::kMax));
 }
 
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 287aca9..73da2cb 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -80,6 +80,7 @@
 #include "handle_scope-inl.h"
 #include "thread-inl.h"
 #include "utils.h"
+#include "utils/dex_cache_arrays_layout-inl.h"
 #include "verifier/method_verifier.h"
 #include "well_known_classes.h"
 
@@ -420,6 +421,7 @@
   Handle<mirror::Class> java_lang_DexCache(hs.NewHandle(
       AllocClass(self, java_lang_Class.Get(), mirror::DexCache::ClassSize(image_pointer_size_))));
   SetClassRoot(kJavaLangDexCache, java_lang_DexCache.Get());
+  java_lang_DexCache->SetDexCacheClass();
   java_lang_DexCache->SetObjectSize(mirror::DexCache::InstanceSize());
   mirror::Class::SetStatus(java_lang_DexCache, mirror::Class::kStatusResolved, self);
 
@@ -783,7 +785,8 @@
                           // be from multidex, which resolves correctly).
 };
 
-static void AddDexFilesFromOat(const OatFile* oat_file, bool already_loaded,
+static void AddDexFilesFromOat(const OatFile* oat_file,
+                               bool already_loaded,
                                std::priority_queue<DexFileAndClassPair>* heap) {
   const std::vector<const OatDexFile*>& oat_dex_files = oat_file->GetOatDexFiles();
   for (const OatDexFile* oat_dex_file : oat_dex_files) {
@@ -1025,7 +1028,8 @@
   return nullptr;
 }
 
-static void SanityCheckArtMethod(ArtMethod* m, mirror::Class* expected_class,
+static void SanityCheckArtMethod(ArtMethod* m,
+                                 mirror::Class* expected_class,
                                  gc::space::ImageSpace* space)
     SHARED_REQUIRES(Locks::mutator_lock_) {
   if (m->IsRuntimeMethod()) {
@@ -1043,9 +1047,11 @@
   }
 }
 
-static void SanityCheckArtMethodPointerArray(
-    mirror::PointerArray* arr, mirror::Class* expected_class, size_t pointer_size,
-    gc::space::ImageSpace* space) SHARED_REQUIRES(Locks::mutator_lock_) {
+static void SanityCheckArtMethodPointerArray(mirror::PointerArray* arr,
+                                             mirror::Class* expected_class,
+                                             size_t pointer_size,
+                                             gc::space::ImageSpace* space)
+    SHARED_REQUIRES(Locks::mutator_lock_) {
   CHECK(arr != nullptr);
   for (int32_t j = 0; j < arr->GetLength(); ++j) {
     auto* method = arr->GetElementPtrSize<ArtMethod*>(j, pointer_size);
@@ -1059,6 +1065,26 @@
   }
 }
 
+static void SanityCheckArtMethodPointerArray(
+    ArtMethod** arr,
+    size_t size,
+    size_t pointer_size,
+    gc::space::ImageSpace* space) SHARED_REQUIRES(Locks::mutator_lock_) {
+  CHECK_EQ(arr != nullptr, size != 0u);
+  if (arr != nullptr) {
+    auto offset = reinterpret_cast<uint8_t*>(arr) - space->Begin();
+    CHECK(space->GetImageHeader().GetImageSection(
+        ImageHeader::kSectionDexCacheArrays).Contains(offset));
+  }
+  for (size_t j = 0; j < size; ++j) {
+    ArtMethod* method = mirror::DexCache::GetElementPtrSize(arr, j, pointer_size);
+    // expected_class == null means we are a dex cache.
+    if (method != nullptr) {
+      SanityCheckArtMethod(method, nullptr, space);
+    }
+  }
+}
+
 static void SanityCheckObjectsCallback(mirror::Object* obj, void* arg ATTRIBUTE_UNUSED)
     SHARED_REQUIRES(Locks::mutator_lock_) {
   DCHECK(obj != nullptr);
@@ -1188,8 +1214,10 @@
     }
 
     if (kSanityCheckObjects) {
-      SanityCheckArtMethodPointerArray(dex_cache->GetResolvedMethods(), nullptr,
-                                       image_pointer_size_, space);
+      SanityCheckArtMethodPointerArray(dex_cache->GetResolvedMethods(),
+                                       dex_cache->NumResolvedMethods(),
+                                       image_pointer_size_,
+                                       space);
     }
 
     CHECK_EQ(dex_file->GetLocationChecksum(), oat_dex_file->GetDexFileLocationChecksum());
@@ -1267,7 +1295,8 @@
 }
 
 void ClassLinker::VisitClassRoots(RootVisitor* visitor, VisitRootFlags flags) {
-  WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
+  Thread* const self = Thread::Current();
+  WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
   BufferedRootVisitor<kDefaultBufferedRootCount> buffered_visitor(
       visitor, RootInfo(kRootStickyClass));
   if ((flags & kVisitRootFlagAllRoots) != 0) {
@@ -1287,9 +1316,13 @@
     // Need to make sure to not copy ArtMethods without doing read barriers since the roots are
     // marked concurrently and we don't hold the classlinker_classes_lock_ when we do the copy.
     boot_class_table_.VisitRoots(buffered_visitor);
-    for (GcRoot<mirror::ClassLoader>& root : class_loaders_) {
-      // May be null for boot ClassLoader.
-      root.VisitRoot(visitor, RootInfo(kRootVMInternal));
+    // TODO: Avoid marking these to enable class unloading.
+    JavaVMExt* const vm = Runtime::Current()->GetJavaVM();
+    for (jweak weak_root : class_loaders_) {
+      mirror::Object* class_loader =
+          down_cast<mirror::ClassLoader*>(vm->DecodeWeakGlobal(self, weak_root));
+      // Don't need to update anything since the class loaders will be updated by SweepSystemWeaks.
+      visitor->VisitRootIfNonNull(&class_loader, RootInfo(kRootVMInternal));
     }
   } else if ((flags & kVisitRootFlagNewRoots) != 0) {
     for (auto& root : new_class_roots_) {
@@ -1325,14 +1358,31 @@
   }
 }
 
+class VisitClassLoaderClassesVisitor : public ClassLoaderVisitor {
+ public:
+  explicit VisitClassLoaderClassesVisitor(ClassVisitor* visitor)
+      : visitor_(visitor),
+        done_(false) {}
+
+  void Visit(mirror::ClassLoader* class_loader)
+      SHARED_REQUIRES(Locks::classlinker_classes_lock_, Locks::mutator_lock_) OVERRIDE {
+    ClassTable* const class_table = class_loader->GetClassTable();
+    if (!done_ && class_table != nullptr && !class_table->Visit(visitor_)) {
+      // If the visitor ClassTable returns false it means that we don't need to continue.
+      done_ = true;
+    }
+  }
+
+ private:
+  ClassVisitor* const visitor_;
+  // If done is true then we don't need to do any more visiting.
+  bool done_;
+};
+
 void ClassLinker::VisitClassesInternal(ClassVisitor* visitor) {
   if (boot_class_table_.Visit(visitor)) {
-    for (GcRoot<mirror::ClassLoader>& root : class_loaders_) {
-      ClassTable* const class_table = root.Read()->GetClassTable();
-      if (class_table != nullptr && !class_table->Visit(visitor)) {
-        return;
-      }
-    }
+    VisitClassLoaderClassesVisitor loader_visitor(visitor);
+    VisitClassLoaders(&loader_visitor);
   }
 }
 
@@ -1451,10 +1501,17 @@
   mirror::LongArray::ResetArrayClass();
   mirror::ShortArray::ResetArrayClass();
   STLDeleteElements(&oat_files_);
-  for (GcRoot<mirror::ClassLoader>& root : class_loaders_) {
-    ClassTable* const class_table = root.Read()->GetClassTable();
-    delete class_table;
+  Thread* const self = Thread::Current();
+  JavaVMExt* const vm = Runtime::Current()->GetJavaVM();
+  for (jweak weak_root : class_loaders_) {
+    auto* const class_loader = down_cast<mirror::ClassLoader*>(
+        vm->DecodeWeakGlobal(self, weak_root));
+    if (class_loader != nullptr) {
+      delete class_loader->GetClassTable();
+    }
+    vm->DeleteWeakGlobalRef(self, weak_root);
   }
+  class_loaders_.clear();
 }
 
 mirror::PointerArray* ClassLinker::AllocPointerArray(Thread* self, size_t length) {
@@ -1476,28 +1533,44 @@
     self->AssertPendingOOMException();
     return nullptr;
   }
-  auto strings(hs.NewHandle(AllocStringArray(self, dex_file.NumStringIds())));
-  if (strings.Get() == nullptr) {
-    self->AssertPendingOOMException();
-    return nullptr;
+  DexCacheArraysLayout layout(image_pointer_size_, &dex_file);
+  uint8_t* raw_arrays = nullptr;
+  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.
+    DCHECK(image_pointer_size_ == 4u || image_pointer_size_ == 8u);
+    if (sizeof(void*) == 8u && image_pointer_size_ == 4u) {
+      // When cross-compiling for a 32-bit target on a 64-bit host, we need these arrays
+      // in the low 4GiB address space so that we can store pointers in 32-bit fields.
+      // This is conveniently provided by the linear allocator.
+      raw_arrays = reinterpret_cast<uint8_t*>(
+          Runtime::Current()->GetLinearAlloc()->Alloc(self, layout.Size()));  // Zero-initialized.
+    } else {
+      raw_arrays = reinterpret_cast<uint8_t*>(calloc(layout.Size(), 1u));  // Zero-initialized.
+      if (raw_arrays == nullptr) {
+        return nullptr;
+      }
+    }
   }
-  auto types(hs.NewHandle(AllocClassArray(self, dex_file.NumTypeIds())));
-  if (types.Get() == nullptr) {
-    self->AssertPendingOOMException();
-    return nullptr;
-  }
-  auto methods(hs.NewHandle(AllocPointerArray(self, dex_file.NumMethodIds())));
-  if (methods.Get() == nullptr) {
-    self->AssertPendingOOMException();
-    return nullptr;
-  }
-  auto fields(hs.NewHandle(AllocPointerArray(self, dex_file.NumFieldIds())));
-  if (fields.Get() == nullptr) {
-    self->AssertPendingOOMException();
-    return nullptr;
-  }
-  dex_cache->Init(&dex_file, location.Get(), strings.Get(), types.Get(), methods.Get(),
-                  fields.Get(), image_pointer_size_);
+  GcRoot<mirror::String>* strings = (dex_file.NumStringIds() == 0u) ? nullptr :
+      reinterpret_cast<GcRoot<mirror::String>*>(raw_arrays + layout.StringsOffset());
+  GcRoot<mirror::Class>* types = (dex_file.NumTypeIds() == 0u) ? nullptr :
+      reinterpret_cast<GcRoot<mirror::Class>*>(raw_arrays + layout.TypesOffset());
+  ArtMethod** methods = (dex_file.NumMethodIds() == 0u) ? nullptr :
+      reinterpret_cast<ArtMethod**>(raw_arrays + layout.MethodsOffset());
+  ArtField** fields = (dex_file.NumFieldIds() == 0u) ? nullptr :
+      reinterpret_cast<ArtField**>(raw_arrays + layout.FieldsOffset());
+  dex_cache->Init(&dex_file,
+                  location.Get(),
+                  strings,
+                  dex_file.NumStringIds(),
+                  types,
+                  dex_file.NumTypeIds(),
+                  methods,
+                  dex_file.NumMethodIds(),
+                  fields,
+                  dex_file.NumFieldIds(),
+                  image_pointer_size_);
   return dex_cache.Get();
 }
 
@@ -1521,12 +1594,14 @@
 }
 
 mirror::ObjectArray<mirror::StackTraceElement>* ClassLinker::AllocStackTraceElementArray(
-    Thread* self, size_t length) {
+    Thread* self,
+    size_t length) {
   return mirror::ObjectArray<mirror::StackTraceElement>::Alloc(
       self, GetClassRoot(kJavaLangStackTraceElementArrayClass), length);
 }
 
-mirror::Class* ClassLinker::EnsureResolved(Thread* self, const char* descriptor,
+mirror::Class* ClassLinker::EnsureResolved(Thread* self,
+                                           const char* descriptor,
                                            mirror::Class* klass) {
   DCHECK(klass != nullptr);
 
@@ -1604,7 +1679,8 @@
 }
 
 bool ClassLinker::FindClassInPathClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
-                                             Thread* self, const char* descriptor,
+                                             Thread* self,
+                                             const char* descriptor,
                                              size_t hash,
                                              Handle<mirror::ClassLoader> class_loader,
                                              mirror::Class** result) {
@@ -1714,7 +1790,8 @@
   return true;
 }
 
-mirror::Class* ClassLinker::FindClass(Thread* self, const char* descriptor,
+mirror::Class* ClassLinker::FindClass(Thread* self,
+                                      const char* descriptor,
                                       Handle<mirror::ClassLoader> class_loader) {
   DCHECK_NE(*descriptor, '\0') << "descriptor is empty string";
   DCHECK(self != nullptr);
@@ -1803,7 +1880,9 @@
   UNREACHABLE();
 }
 
-mirror::Class* ClassLinker::DefineClass(Thread* self, const char* descriptor, size_t hash,
+mirror::Class* ClassLinker::DefineClass(Thread* self,
+                                        const char* descriptor,
+                                        size_t hash,
                                         Handle<mirror::ClassLoader> class_loader,
                                         const DexFile& dex_file,
                                         const DexFile::ClassDef& dex_class_def) {
@@ -1972,11 +2051,18 @@
       }
     }
   }
-  return mirror::Class::ComputeClassSize(false, 0, num_8, num_16, num_32, num_64, num_ref,
+  return mirror::Class::ComputeClassSize(false,
+                                         0,
+                                         num_8,
+                                         num_16,
+                                         num_32,
+                                         num_64,
+                                         num_ref,
                                          image_pointer_size_);
 }
 
-OatFile::OatClass ClassLinker::FindOatClass(const DexFile& dex_file, uint16_t class_def_idx,
+OatFile::OatClass ClassLinker::FindOatClass(const DexFile& dex_file,
+                                            uint16_t class_def_idx,
                                             bool* found) {
   DCHECK_NE(class_def_idx, DexFile::kDexNoIndex16);
   const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile();
@@ -1988,7 +2074,8 @@
   return oat_dex_file->GetOatClass(class_def_idx);
 }
 
-static uint32_t GetOatMethodIndexFromMethodIndex(const DexFile& dex_file, uint16_t class_def_idx,
+static uint32_t GetOatMethodIndexFromMethodIndex(const DexFile& dex_file,
+                                                 uint16_t class_def_idx,
                                                  uint32_t method_idx) {
   const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_idx);
   const uint8_t* class_data = dex_file.GetClassData(class_def);
@@ -2108,7 +2195,8 @@
   return nullptr;
 }
 
-const void* ClassLinker::GetQuickOatCodeFor(const DexFile& dex_file, uint16_t class_def_idx,
+const void* ClassLinker::GetQuickOatCodeFor(const DexFile& dex_file,
+                                            uint16_t class_def_idx,
                                             uint32_t method_idx) {
   bool found;
   OatFile::OatClass oat_class = FindOatClass(dex_file, class_def_idx, &found);
@@ -2161,7 +2249,8 @@
     it.Next();
   }
   bool has_oat_class;
-  OatFile::OatClass oat_class = FindOatClass(dex_file, klass->GetDexClassDefIndex(),
+  OatFile::OatClass oat_class = FindOatClass(dex_file,
+                                             klass->GetDexClassDefIndex(),
                                              &has_oat_class);
   // Link the code of methods skipped by LinkCode.
   for (size_t method_index = 0; it.HasNextDirectMethod(); ++method_index, it.Next()) {
@@ -2242,8 +2331,10 @@
   }
 }
 
-void ClassLinker::SetupClass(const DexFile& dex_file, const DexFile::ClassDef& dex_class_def,
-                             Handle<mirror::Class> klass, mirror::ClassLoader* class_loader) {
+void ClassLinker::SetupClass(const DexFile& dex_file,
+                             const DexFile::ClassDef& dex_class_def,
+                             Handle<mirror::Class> klass,
+                             mirror::ClassLoader* class_loader) {
   CHECK(klass.Get() != nullptr);
   CHECK(klass->GetDexCache() != nullptr);
   CHECK_EQ(mirror::Class::kStatusNotReady, klass->GetStatus());
@@ -2263,7 +2354,8 @@
   CHECK(klass->GetDexCacheStrings() != nullptr);
 }
 
-void ClassLinker::LoadClass(Thread* self, const DexFile& dex_file,
+void ClassLinker::LoadClass(Thread* self,
+                            const DexFile& dex_file,
                             const DexFile::ClassDef& dex_class_def,
                             Handle<mirror::Class> klass) {
   const uint8_t* class_data = dex_file.GetClassData(dex_class_def);
@@ -2314,7 +2406,8 @@
   return ret;
 }
 
-void ClassLinker::LoadClassMembers(Thread* self, const DexFile& dex_file,
+void ClassLinker::LoadClassMembers(Thread* self,
+                                   const DexFile& dex_file,
                                    const uint8_t* class_data,
                                    Handle<mirror::Class> klass,
                                    const OatFile::OatClass* oat_class) {
@@ -2399,7 +2492,8 @@
   self->AllowThreadSuspension();
 }
 
-void ClassLinker::LoadField(const ClassDataItemIterator& it, Handle<mirror::Class> klass,
+void ClassLinker::LoadField(const ClassDataItemIterator& it,
+                            Handle<mirror::Class> klass,
                             ArtField* dst) {
   const uint32_t field_idx = it.GetMemberIndex();
   dst->SetDexFieldIndex(field_idx);
@@ -2407,8 +2501,11 @@
   dst->SetAccessFlags(it.GetFieldAccessFlags());
 }
 
-void ClassLinker::LoadMethod(Thread* self, const DexFile& dex_file, const ClassDataItemIterator& it,
-                             Handle<mirror::Class> klass, ArtMethod* dst) {
+void ClassLinker::LoadMethod(Thread* self,
+                             const DexFile& dex_file,
+                             const ClassDataItemIterator& it,
+                             Handle<mirror::Class> klass,
+                             ArtMethod* dst) {
   uint32_t dex_method_idx = it.GetMemberIndex();
   const DexFile::MethodId& method_id = dex_file.GetMethodId(dex_method_idx);
   const char* method_name = dex_file.StringDataByIdx(method_id.name_idx_);
@@ -2418,8 +2515,8 @@
   dst->SetDeclaringClass(klass.Get());
   dst->SetCodeItemOffset(it.GetMethodCodeItemOffset());
 
-  dst->SetDexCacheResolvedMethods(klass->GetDexCache()->GetResolvedMethods());
-  dst->SetDexCacheResolvedTypes(klass->GetDexCache()->GetResolvedTypes());
+  dst->SetDexCacheResolvedMethods(klass->GetDexCache()->GetResolvedMethods(), image_pointer_size_);
+  dst->SetDexCacheResolvedTypes(klass->GetDexCache()->GetResolvedTypes(), image_pointer_size_);
 
   uint32_t access_flags = it.GetMethodAccessFlags();
 
@@ -2543,8 +2640,7 @@
                                                   bool allow_failure) {
   // Search assuming unique-ness of dex file.
   JavaVMExt* const vm = self->GetJniEnv()->vm;
-  for (jobject weak_root : dex_caches_) {
-    DCHECK_EQ(GetIndirectRefKind(weak_root), kWeakGlobal);
+  for (jweak weak_root : dex_caches_) {
     mirror::DexCache* dex_cache = down_cast<mirror::DexCache*>(
         vm->DecodeWeakGlobal(self, weak_root));
     if (dex_cache != nullptr && dex_cache->GetDexFile() == &dex_file) {
@@ -2841,7 +2937,9 @@
   return class_table != nullptr && class_table->Remove(descriptor);
 }
 
-mirror::Class* ClassLinker::LookupClass(Thread* self, const char* descriptor, size_t hash,
+mirror::Class* ClassLinker::LookupClass(Thread* self,
+                                        const char* descriptor,
+                                        size_t hash,
                                         mirror::ClassLoader* class_loader) {
   {
     ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_);
@@ -2892,9 +2990,9 @@
   ClassTable* const class_table = InsertClassTableForClassLoader(nullptr);
   for (int32_t i = 0; i < dex_caches->GetLength(); i++) {
     mirror::DexCache* dex_cache = dex_caches->Get(i);
-    mirror::ObjectArray<mirror::Class>* types = dex_cache->GetResolvedTypes();
-    for (int32_t j = 0; j < types->GetLength(); j++) {
-      mirror::Class* klass = types->Get(j);
+    GcRoot<mirror::Class>* types = dex_cache->GetResolvedTypes();
+    for (int32_t j = 0, num_types = dex_cache->NumResolvedTypes(); j < num_types; j++) {
+      mirror::Class* klass = types[j].Read();
       if (klass != nullptr) {
         DCHECK(klass->GetClassLoader() == nullptr);
         const char* descriptor = klass->GetDescriptor(&temp);
@@ -2915,15 +3013,25 @@
   dex_cache_image_class_lookup_required_ = false;
 }
 
-void ClassLinker::MoveClassTableToPreZygote() {
-  WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
-  boot_class_table_.FreezeSnapshot();
-  for (GcRoot<mirror::ClassLoader>& root : class_loaders_) {
-    ClassTable* const class_table = root.Read()->GetClassTable();
+class MoveClassTableToPreZygoteVisitor : public ClassLoaderVisitor {
+ public:
+  explicit MoveClassTableToPreZygoteVisitor() {}
+
+  void Visit(mirror::ClassLoader* class_loader)
+      REQUIRES(Locks::classlinker_classes_lock_)
+      SHARED_REQUIRES(Locks::mutator_lock_) OVERRIDE {
+    ClassTable* const class_table = class_loader->GetClassTable();
     if (class_table != nullptr) {
       class_table->FreezeSnapshot();
     }
   }
+};
+
+void ClassLinker::MoveClassTableToPreZygote() {
+  WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
+  boot_class_table_.FreezeSnapshot();
+  MoveClassTableToPreZygoteVisitor visitor;
+  VisitClassLoadersAndRemoveClearedLoaders(&visitor);
 }
 
 mirror::Class* ClassLinker::LookupClassFromImage(const char* descriptor) {
@@ -2949,25 +3057,43 @@
   return nullptr;
 }
 
+// Look up classes by hash and descriptor and put all matching ones in the result array.
+class LookupClassesVisitor : public ClassLoaderVisitor {
+ public:
+  LookupClassesVisitor(const char* descriptor, size_t hash, std::vector<mirror::Class*>* result)
+     : descriptor_(descriptor),
+       hash_(hash),
+       result_(result) {}
+
+  void Visit(mirror::ClassLoader* class_loader)
+      SHARED_REQUIRES(Locks::classlinker_classes_lock_, Locks::mutator_lock_) OVERRIDE {
+    ClassTable* const class_table = class_loader->GetClassTable();
+    mirror::Class* klass = class_table->Lookup(descriptor_, hash_);
+    if (klass != nullptr) {
+      result_->push_back(klass);
+    }
+  }
+
+ private:
+  const char* const descriptor_;
+  const size_t hash_;
+  std::vector<mirror::Class*>* const result_;
+};
+
 void ClassLinker::LookupClasses(const char* descriptor, std::vector<mirror::Class*>& result) {
   result.clear();
   if (dex_cache_image_class_lookup_required_) {
     MoveImageClassesToClassTable();
   }
-  WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
+  Thread* const self = Thread::Current();
+  ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_);
   const size_t hash = ComputeModifiedUtf8Hash(descriptor);
   mirror::Class* klass = boot_class_table_.Lookup(descriptor, hash);
   if (klass != nullptr) {
     result.push_back(klass);
   }
-  for (GcRoot<mirror::ClassLoader>& root : class_loaders_) {
-    // There can only be one class with the same descriptor per class loader.
-    ClassTable* const class_table = root.Read()->GetClassTable();
-    klass = class_table->Lookup(descriptor, hash);
-    if (klass != nullptr) {
-      result.push_back(klass);
-    }
-  }
+  LookupClassesVisitor visitor(descriptor, hash, &result);
+  VisitClassLoaders(&visitor);
 }
 
 void ClassLinker::VerifyClass(Thread* self, Handle<mirror::Class> klass) {
@@ -3067,7 +3193,8 @@
   verifier::MethodVerifier::FailureKind verifier_failure = verifier::MethodVerifier::kNoFailure;
   std::string error_msg;
   if (!preverified) {
-    verifier_failure = verifier::MethodVerifier::VerifyClass(self, klass.Get(),
+    verifier_failure = verifier::MethodVerifier::VerifyClass(self,
+                                                             klass.Get(),
                                                              Runtime::Current()->IsAotCompiler(),
                                                              &error_msg);
   }
@@ -3131,7 +3258,8 @@
   }
 }
 
-bool ClassLinker::VerifyClassUsingOatFile(const DexFile& dex_file, mirror::Class* klass,
+bool ClassLinker::VerifyClassUsingOatFile(const DexFile& dex_file,
+                                          mirror::Class* klass,
                                           mirror::Class::Status& oat_file_class_status) {
   // If we're compiling, we can only verify the class using the oat file if
   // we are not compiling the image or if the class we're verifying is not part of
@@ -3253,9 +3381,12 @@
   }
 }
 
-mirror::Class* ClassLinker::CreateProxyClass(ScopedObjectAccessAlreadyRunnable& soa, jstring name,
-                                             jobjectArray interfaces, jobject loader,
-                                             jobjectArray methods, jobjectArray throws) {
+mirror::Class* ClassLinker::CreateProxyClass(ScopedObjectAccessAlreadyRunnable& soa,
+                                             jstring name,
+                                             jobjectArray interfaces,
+                                             jobject loader,
+                                             jobjectArray methods,
+                                             jobjectArray throws) {
   Thread* self = soa.Self();
   StackHandleScope<10> hs(self);
   MutableHandle<mirror::Class> klass(hs.NewHandle(
@@ -3415,7 +3546,8 @@
     for (jobject weak_root : dex_caches_) {
       mirror::DexCache* dex_cache = down_cast<mirror::DexCache*>(self->DecodeJObject(weak_root));
       if (dex_cache != nullptr &&
-          proxy_method->HasSameDexCacheResolvedTypes(dex_cache->GetResolvedTypes())) {
+          proxy_method->HasSameDexCacheResolvedTypes(dex_cache->GetResolvedTypes(),
+                                                     image_pointer_size_)) {
         ArtMethod* resolved_method = dex_cache->GetResolvedMethod(
             proxy_method->GetDexMethodIndex(), image_pointer_size_);
         CHECK(resolved_method != nullptr);
@@ -3488,8 +3620,8 @@
 
   // The proxy method doesn't have its own dex cache or dex file and so it steals those of its
   // interface prototype. The exception to this are Constructors and the Class of the Proxy itself.
-  CHECK(prototype->HasSameDexCacheResolvedMethods(method));
-  CHECK(prototype->HasSameDexCacheResolvedTypes(method));
+  CHECK(prototype->HasSameDexCacheResolvedMethods(method, image_pointer_size_));
+  CHECK(prototype->HasSameDexCacheResolvedTypes(method, image_pointer_size_));
   auto* np = method->GetInterfaceMethodIfProxy(image_pointer_size_);
   CHECK_EQ(prototype->GetDeclaringClass()->GetDexCache(), np->GetDexCache());
   CHECK_EQ(prototype->GetDexMethodIndex(), method->GetDexMethodIndex());
@@ -3497,7 +3629,8 @@
   CHECK_STREQ(np->GetName(), prototype->GetName());
   CHECK_STREQ(np->GetShorty(), prototype->GetShorty());
   // More complex sanity - via dex cache
-  CHECK_EQ(np->GetReturnType(), prototype->GetReturnType());
+  CHECK_EQ(np->GetReturnType(true /* resolve */, image_pointer_size_),
+           prototype->GetReturnType(true /* resolve */, image_pointer_size_));
 }
 
 bool ClassLinker::CanWeInitializeClass(mirror::Class* klass, bool can_init_statics,
@@ -3734,7 +3867,8 @@
   return success;
 }
 
-bool ClassLinker::WaitForInitializeClass(Handle<mirror::Class> klass, Thread* self,
+bool ClassLinker::WaitForInitializeClass(Handle<mirror::Class> klass,
+                                         Thread* self,
                                          ObjectLock<mirror::Class>& lock)
     SHARED_REQUIRES(Locks::mutator_lock_) {
   while (true) {
@@ -3803,7 +3937,8 @@
                                                    Handle<mirror::Class> super_klass,
                                                    ArtMethod* method,
                                                    ArtMethod* m,
-                                                   uint32_t index, uint32_t arg_type_idx)
+                                                   uint32_t index,
+                                                   uint32_t arg_type_idx)
     SHARED_REQUIRES(Locks::mutator_lock_) {
   DCHECK(Thread::Current()->IsExceptionPending());
   DCHECK(!m->IsProxyMethod());
@@ -3835,6 +3970,7 @@
 }
 
 static bool HasSameSignatureWithDifferentClassLoaders(Thread* self,
+                                                      size_t pointer_size,
                                                       Handle<mirror::Class> klass,
                                                       Handle<mirror::Class> super_klass,
                                                       ArtMethod* method1,
@@ -3842,12 +3978,14 @@
     SHARED_REQUIRES(Locks::mutator_lock_) {
   {
     StackHandleScope<1> hs(self);
-    Handle<mirror::Class> return_type(hs.NewHandle(method1->GetReturnType()));
+    Handle<mirror::Class> return_type(hs.NewHandle(method1->GetReturnType(true /* resolve */,
+                                                                          pointer_size)));
     if (UNLIKELY(return_type.Get() == nullptr)) {
       ThrowSignatureCheckResolveReturnTypeException(klass, super_klass, method1, method1);
       return false;
     }
-    mirror::Class* other_return_type = method2->GetReturnType();
+    mirror::Class* other_return_type = method2->GetReturnType(true /* resolve */,
+                                                              pointer_size);
     if (UNLIKELY(other_return_type == nullptr)) {
       ThrowSignatureCheckResolveReturnTypeException(klass, super_klass, method1, method2);
       return false;
@@ -3892,7 +4030,7 @@
     StackHandleScope<1> hs(self);
     uint32_t param_type_idx = types1->GetTypeItem(i).type_idx_;
     Handle<mirror::Class> param_type(hs.NewHandle(
-        method1->GetClassFromTypeIndex(param_type_idx, true)));
+        method1->GetClassFromTypeIndex(param_type_idx, true /* resolve */, pointer_size)));
     if (UNLIKELY(param_type.Get() == nullptr)) {
       ThrowSignatureCheckResolveArgException(klass, super_klass, method1,
                                              method1, i, param_type_idx);
@@ -3900,7 +4038,7 @@
     }
     uint32_t other_param_type_idx = types2->GetTypeItem(i).type_idx_;
     mirror::Class* other_param_type =
-        method2->GetClassFromTypeIndex(other_param_type_idx, true);
+        method2->GetClassFromTypeIndex(other_param_type_idx, true /* resolve */, pointer_size);
     if (UNLIKELY(other_param_type == nullptr)) {
       ThrowSignatureCheckResolveArgException(klass, super_klass, method1,
                                              method2, i, other_param_type_idx);
@@ -3936,7 +4074,8 @@
       auto* m = klass->GetVTableEntry(i, image_pointer_size_);
       auto* super_m = klass->GetSuperClass()->GetVTableEntry(i, image_pointer_size_);
       if (m != super_m) {
-        if (UNLIKELY(!HasSameSignatureWithDifferentClassLoaders(self, klass, super_klass,
+        if (UNLIKELY(!HasSameSignatureWithDifferentClassLoaders(self, image_pointer_size_,
+                                                                klass, super_klass,
                                                                 m, super_m))) {
           self->AssertPendingException();
           return false;
@@ -3953,7 +4092,8 @@
             j, image_pointer_size_);
         auto* super_m = super_klass->GetVirtualMethod(j, image_pointer_size_);
         if (m != super_m) {
-          if (UNLIKELY(!HasSameSignatureWithDifferentClassLoaders(self, klass, super_klass,
+          if (UNLIKELY(!HasSameSignatureWithDifferentClassLoaders(self, image_pointer_size_,
+                                                                  klass, super_klass,
                                                                   m, super_m))) {
             self->AssertPendingException();
             return false;
@@ -4025,7 +4165,8 @@
   ClassTable* class_table = class_loader->GetClassTable();
   if (class_table == nullptr) {
     class_table = new ClassTable;
-    class_loaders_.push_back(class_loader);
+    Thread* const self = Thread::Current();
+    class_loaders_.push_back(self->GetJniEnv()->vm->AddWeakGlobalRef(self, class_loader));
     // Don't already have a class table, add it to the class loader.
     class_loader->SetClassTable(class_table);
   }
@@ -4036,7 +4177,9 @@
   return class_loader == nullptr ? &boot_class_table_ : class_loader->GetClassTable();
 }
 
-bool ClassLinker::LinkClass(Thread* self, const char* descriptor, Handle<mirror::Class> klass,
+bool ClassLinker::LinkClass(Thread* self,
+                            const char* descriptor,
+                            Handle<mirror::Class> klass,
                             Handle<mirror::ObjectArray<mirror::Class>> interfaces,
                             MutableHandle<mirror::Class>* h_new_class_out) {
   CHECK_EQ(mirror::Class::kStatusLoaded, klass->GetStatus());
@@ -4186,8 +4329,10 @@
   }
 }
 
-static std::string DumpClasses(const DexFile& dex_file1, const DexFile::ClassDef& dex_class_def1,
-                               const DexFile& dex_file2, const DexFile::ClassDef& dex_class_def2) {
+static std::string DumpClasses(const DexFile& dex_file1,
+                               const DexFile::ClassDef& dex_class_def1,
+                               const DexFile& dex_file2,
+                               const DexFile::ClassDef& dex_class_def2) {
   std::ostringstream os;
   DumpClass(os, dex_file1, dex_class_def1, " (Compile time)");
   DumpClass(os, dex_file2, dex_class_def2, " (Runtime)");
@@ -4197,43 +4342,59 @@
 
 // Very simple structural check on whether the classes match. Only compares the number of
 // methods and fields.
-static bool SimpleStructuralCheck(const DexFile& dex_file1, const DexFile::ClassDef& dex_class_def1,
-                                  const DexFile& dex_file2, const DexFile::ClassDef& dex_class_def2,
+static bool SimpleStructuralCheck(const DexFile& dex_file1,
+                                  const DexFile::ClassDef& dex_class_def1,
+                                  const DexFile& dex_file2,
+                                  const DexFile::ClassDef& dex_class_def2,
                                   std::string* error_msg) {
   ClassDataItemIterator dex_data1(dex_file1, dex_file1.GetClassData(dex_class_def1));
   ClassDataItemIterator dex_data2(dex_file2, dex_file2.GetClassData(dex_class_def2));
 
   // Counters for current dex file.
   size_t dex_virtual_methods1, dex_direct_methods1, dex_static_fields1, dex_instance_fields1;
-  CountMethodsAndFields(dex_data1, &dex_virtual_methods1, &dex_direct_methods1, &dex_static_fields1,
+  CountMethodsAndFields(dex_data1,
+                        &dex_virtual_methods1,
+                        &dex_direct_methods1,
+                        &dex_static_fields1,
                         &dex_instance_fields1);
   // Counters for compile-time dex file.
   size_t dex_virtual_methods2, dex_direct_methods2, dex_static_fields2, dex_instance_fields2;
-  CountMethodsAndFields(dex_data2, &dex_virtual_methods2, &dex_direct_methods2, &dex_static_fields2,
+  CountMethodsAndFields(dex_data2,
+                        &dex_virtual_methods2,
+                        &dex_direct_methods2,
+                        &dex_static_fields2,
                         &dex_instance_fields2);
 
   if (dex_virtual_methods1 != dex_virtual_methods2) {
     std::string class_dump = DumpClasses(dex_file1, dex_class_def1, dex_file2, dex_class_def2);
-    *error_msg = StringPrintf("Virtual method count off: %zu vs %zu\n%s", dex_virtual_methods1,
-                              dex_virtual_methods2, class_dump.c_str());
+    *error_msg = StringPrintf("Virtual method count off: %zu vs %zu\n%s",
+                              dex_virtual_methods1,
+                              dex_virtual_methods2,
+                              class_dump.c_str());
     return false;
   }
   if (dex_direct_methods1 != dex_direct_methods2) {
     std::string class_dump = DumpClasses(dex_file1, dex_class_def1, dex_file2, dex_class_def2);
-    *error_msg = StringPrintf("Direct method count off: %zu vs %zu\n%s", dex_direct_methods1,
-                              dex_direct_methods2, class_dump.c_str());
+    *error_msg = StringPrintf("Direct method count off: %zu vs %zu\n%s",
+                              dex_direct_methods1,
+                              dex_direct_methods2,
+                              class_dump.c_str());
     return false;
   }
   if (dex_static_fields1 != dex_static_fields2) {
     std::string class_dump = DumpClasses(dex_file1, dex_class_def1, dex_file2, dex_class_def2);
-    *error_msg = StringPrintf("Static field count off: %zu vs %zu\n%s", dex_static_fields1,
-                              dex_static_fields2, class_dump.c_str());
+    *error_msg = StringPrintf("Static field count off: %zu vs %zu\n%s",
+                              dex_static_fields1,
+                              dex_static_fields2,
+                              class_dump.c_str());
     return false;
   }
   if (dex_instance_fields1 != dex_instance_fields2) {
     std::string class_dump = DumpClasses(dex_file1, dex_class_def1, dex_file2, dex_class_def2);
-    *error_msg = StringPrintf("Instance field count off: %zu vs %zu\n%s", dex_instance_fields1,
-                              dex_instance_fields2, class_dump.c_str());
+    *error_msg = StringPrintf("Instance field count off: %zu vs %zu\n%s",
+                              dex_instance_fields1,
+                              dex_instance_fields2,
+                              class_dump.c_str());
     return false;
   }
 
@@ -4341,7 +4502,8 @@
       // Verify
       if (!klass->CanAccess(interface)) {
         // TODO: the RI seemed to ignore this in my testing.
-        ThrowIllegalAccessError(klass.Get(), "Interface %s implemented by class %s is inaccessible",
+        ThrowIllegalAccessError(klass.Get(),
+                                "Interface %s implemented by class %s is inaccessible",
                                 PrettyDescriptor(interface).c_str(),
                                 PrettyDescriptor(klass.Get()).c_str());
         return false;
@@ -4370,7 +4532,8 @@
   }
   // Verify
   if (super->IsFinal() || super->IsInterface()) {
-    ThrowIncompatibleClassChangeError(klass.Get(), "Superclass %s of %s is %s",
+    ThrowIncompatibleClassChangeError(klass.Get(),
+                                      "Superclass %s of %s is %s",
                                       PrettyDescriptor(super).c_str(),
                                       PrettyDescriptor(klass.Get()).c_str(),
                                       super->IsFinal() ? "declared final" : "an interface");
@@ -4419,7 +4582,8 @@
 }
 
 // Populate the class vtable and itable. Compute return type indices.
-bool ClassLinker::LinkMethods(Thread* self, Handle<mirror::Class> klass,
+bool ClassLinker::LinkMethods(Thread* self,
+                              Handle<mirror::Class> klass,
                               Handle<mirror::ObjectArray<mirror::Class>> interfaces,
                               ArtMethod** out_imt) {
   self->AllowThreadSuspension();
@@ -4489,12 +4653,17 @@
 
 class LinkVirtualHashTable {
  public:
-  LinkVirtualHashTable(Handle<mirror::Class> klass, size_t hash_size, uint32_t* hash_table,
+  LinkVirtualHashTable(Handle<mirror::Class> klass,
+                       size_t hash_size,
+                       uint32_t* hash_table,
                        size_t image_pointer_size)
-     : klass_(klass), hash_size_(hash_size), hash_table_(hash_table),
+     : klass_(klass),
+       hash_size_(hash_size),
+       hash_table_(hash_table),
        image_pointer_size_(image_pointer_size) {
     std::fill(hash_table_, hash_table_ + hash_size_, invalid_index_);
   }
+
   void Add(uint32_t virtual_method_index) SHARED_REQUIRES(Locks::mutator_lock_) {
     ArtMethod* local_method = klass_->GetVirtualMethodDuringLinking(
         virtual_method_index, image_pointer_size_);
@@ -4509,6 +4678,7 @@
     }
     hash_table_[index] = virtual_method_index;
   }
+
   uint32_t FindAndRemove(MethodNameAndSignatureComparator* comparator)
       SHARED_REQUIRES(Locks::mutator_lock_) {
     const char* name = comparator->GetName();
@@ -4536,6 +4706,7 @@
     }
     return GetNotFoundIndex();
   }
+
   static uint32_t GetNotFoundIndex() {
     return invalid_index_;
   }
@@ -4693,7 +4864,8 @@
   return true;
 }
 
-bool ClassLinker::LinkInterfaceMethods(Thread* self, Handle<mirror::Class> klass,
+bool ClassLinker::LinkInterfaceMethods(Thread* self,
+                                       Handle<mirror::Class> klass,
                                        Handle<mirror::ObjectArray<mirror::Class>> interfaces,
                                        ArtMethod** out_imt) {
   StackHandleScope<3> hs(self);
@@ -4701,8 +4873,9 @@
   const bool has_superclass = klass->HasSuperClass();
   const size_t super_ifcount = has_superclass ? klass->GetSuperClass()->GetIfTableCount() : 0U;
   const bool have_interfaces = interfaces.Get() != nullptr;
-  const size_t num_interfaces =
-      have_interfaces ? interfaces->GetLength() : klass->NumDirectInterfaces();
+  const size_t num_interfaces = have_interfaces
+      ? interfaces->GetLength()
+      : klass->NumDirectInterfaces();
   const size_t method_alignment = ArtMethod::Alignment(image_pointer_size_);
   const size_t method_size = ArtMethod::Size(image_pointer_size_);
   if (num_interfaces == 0) {
@@ -4729,12 +4902,14 @@
   }
   size_t ifcount = super_ifcount + num_interfaces;
   for (size_t i = 0; i < num_interfaces; i++) {
-    mirror::Class* interface = have_interfaces ?
-        interfaces->GetWithoutChecks(i) : mirror::Class::GetDirectInterface(self, klass, i);
+    mirror::Class* interface = have_interfaces
+        ? interfaces->GetWithoutChecks(i)
+        : mirror::Class::GetDirectInterface(self, klass, i);
     DCHECK(interface != nullptr);
     if (UNLIKELY(!interface->IsInterface())) {
       std::string temp;
-      ThrowIncompatibleClassChangeError(klass.Get(), "Class %s implements non-interface class %s",
+      ThrowIncompatibleClassChangeError(klass.Get(),
+                                        "Class %s implements non-interface class %s",
                                         PrettyDescriptor(klass.Get()).c_str(),
                                         PrettyDescriptor(interface->GetDescriptor(&temp)).c_str());
       return false;
@@ -4914,8 +5089,7 @@
         continue;
       }
       for (size_t j = 0; j < num_methods; ++j) {
-        auto* interface_method = iftable->GetInterface(i)->GetVirtualMethod(
-            j, image_pointer_size_);
+        auto* interface_method = iftable->GetInterface(i)->GetVirtualMethod(j, image_pointer_size_);
         MethodNameAndSignatureComparator interface_name_comparator(
             interface_method->GetInterfaceMethodIfProxy(image_pointer_size_));
         int32_t k;
@@ -5088,8 +5262,8 @@
     // Check that there are no stale methods are in the dex cache array.
     if (kIsDebugBuild) {
       auto* resolved_methods = klass->GetDexCache()->GetResolvedMethods();
-      for (size_t i = 0, count = resolved_methods->GetLength(); i < count; ++i) {
-        auto* m = resolved_methods->GetElementPtrSize<ArtMethod*>(i, image_pointer_size_);
+      for (size_t i = 0, count = klass->GetDexCache()->NumResolvedMethods(); i < count; ++i) {
+        auto* m = mirror::DexCache::GetElementPtrSize(resolved_methods, i, image_pointer_size_);
         CHECK(move_table.find(m) == move_table.end()) << PrettyMethod(m);
       }
     }
@@ -5153,7 +5327,9 @@
   }
 };
 
-bool ClassLinker::LinkFields(Thread* self, Handle<mirror::Class> klass, bool is_static,
+bool ClassLinker::LinkFields(Thread* self,
+                             Handle<mirror::Class> klass,
+                             bool is_static,
                              size_t* class_size) {
   self->AllowThreadSuspension();
   const size_t num_fields = is_static ? klass->NumStaticFields() : klass->NumInstanceFields();
@@ -5352,7 +5528,8 @@
   klass->SetReferenceInstanceOffsets(reference_offsets);
 }
 
-mirror::String* ClassLinker::ResolveString(const DexFile& dex_file, uint32_t string_idx,
+mirror::String* ClassLinker::ResolveString(const DexFile& dex_file,
+                                           uint32_t string_idx,
                                            Handle<mirror::DexCache> dex_cache) {
   DCHECK(dex_cache.Get() != nullptr);
   mirror::String* resolved = dex_cache->GetResolvedString(string_idx);
@@ -5366,7 +5543,8 @@
   return string;
 }
 
-mirror::Class* ClassLinker::ResolveType(const DexFile& dex_file, uint16_t type_idx,
+mirror::Class* ClassLinker::ResolveType(const DexFile& dex_file,
+                                        uint16_t type_idx,
                                         mirror::Class* referrer) {
   StackHandleScope<2> hs(Thread::Current());
   Handle<mirror::DexCache> dex_cache(hs.NewHandle(referrer->GetDexCache()));
@@ -5374,7 +5552,8 @@
   return ResolveType(dex_file, type_idx, dex_cache, class_loader);
 }
 
-mirror::Class* ClassLinker::ResolveType(const DexFile& dex_file, uint16_t type_idx,
+mirror::Class* ClassLinker::ResolveType(const DexFile& dex_file,
+                                        uint16_t type_idx,
                                         Handle<mirror::DexCache> dex_cache,
                                         Handle<mirror::ClassLoader> class_loader) {
   DCHECK(dex_cache.Get() != nullptr);
@@ -5407,10 +5586,12 @@
   return resolved;
 }
 
-ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, uint32_t method_idx,
+ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file,
+                                      uint32_t method_idx,
                                       Handle<mirror::DexCache> dex_cache,
                                       Handle<mirror::ClassLoader> class_loader,
-                                      ArtMethod* referrer, InvokeType type) {
+                                      ArtMethod* referrer,
+                                      InvokeType type) {
   DCHECK(dex_cache.Get() != nullptr);
   // Check for hit in the dex cache.
   ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx, image_pointer_size_);
@@ -5499,7 +5680,9 @@
         mirror::Class* methods_class = resolved->GetDeclaringClass();
         mirror::Class* referring_class = referrer->GetDeclaringClass();
         if (!referring_class->CanAccess(methods_class)) {
-          ThrowIllegalAccessErrorClassForMethodDispatch(referring_class, methods_class, resolved,
+          ThrowIllegalAccessErrorClassForMethodDispatch(referring_class,
+                                                        methods_class,
+                                                        resolved,
                                                         type);
           exception_generated = true;
         } else if (!referring_class->CanAccessMember(methods_class, resolved->GetAccessFlags())) {
@@ -5594,9 +5777,11 @@
   return resolved;
 }
 
-ArtField* ClassLinker::ResolveField(const DexFile& dex_file, uint32_t field_idx,
+ArtField* ClassLinker::ResolveField(const DexFile& dex_file,
+                                    uint32_t field_idx,
                                     Handle<mirror::DexCache> dex_cache,
-                                    Handle<mirror::ClassLoader> class_loader, bool is_static) {
+                                    Handle<mirror::ClassLoader> class_loader,
+                                    bool is_static) {
   DCHECK(dex_cache.Get() != nullptr);
   ArtField* resolved = dex_cache->GetResolvedField(field_idx, image_pointer_size_);
   if (resolved != nullptr) {
@@ -5635,7 +5820,8 @@
   return resolved;
 }
 
-ArtField* ClassLinker::ResolveFieldJLS(const DexFile& dex_file, uint32_t field_idx,
+ArtField* ClassLinker::ResolveFieldJLS(const DexFile& dex_file,
+                                       uint32_t field_idx,
                                        Handle<mirror::DexCache> dex_cache,
                                        Handle<mirror::ClassLoader> class_loader) {
   DCHECK(dex_cache.Get() != nullptr);
@@ -5665,7 +5851,8 @@
   return resolved;
 }
 
-const char* ClassLinker::MethodShorty(uint32_t method_idx, ArtMethod* referrer,
+const char* ClassLinker::MethodShorty(uint32_t method_idx,
+                                      ArtMethod* referrer,
                                       uint32_t* length) {
   mirror::Class* declaring_class = referrer->GetDeclaringClass();
   mirror::DexCache* dex_cache = declaring_class->GetDexCache();
@@ -5745,26 +5932,33 @@
      << NumNonZygoteClasses() << "\n";
 }
 
-size_t ClassLinker::NumZygoteClasses() const {
-  size_t sum = boot_class_table_.NumZygoteClasses();
-  for (const GcRoot<mirror::ClassLoader>& root : class_loaders_) {
-    ClassTable* const class_table = root.Read()->GetClassTable();
+class CountClassesVisitor : public ClassLoaderVisitor {
+ public:
+  CountClassesVisitor() : num_zygote_classes(0), num_non_zygote_classes(0) {}
+
+  void Visit(mirror::ClassLoader* class_loader)
+      SHARED_REQUIRES(Locks::classlinker_classes_lock_, Locks::mutator_lock_) OVERRIDE {
+    ClassTable* const class_table = class_loader->GetClassTable();
     if (class_table != nullptr) {
-      sum += class_table->NumZygoteClasses();
+      num_zygote_classes += class_table->NumZygoteClasses();
+      num_non_zygote_classes += class_table->NumNonZygoteClasses();
     }
   }
-  return sum;
+
+  size_t num_zygote_classes;
+  size_t num_non_zygote_classes;
+};
+
+size_t ClassLinker::NumZygoteClasses() const {
+  CountClassesVisitor visitor;
+  VisitClassLoaders(&visitor);
+  return visitor.num_zygote_classes + boot_class_table_.NumZygoteClasses();
 }
 
 size_t ClassLinker::NumNonZygoteClasses() const {
-  size_t sum = boot_class_table_.NumNonZygoteClasses();
-  for (const GcRoot<mirror::ClassLoader>& root : class_loaders_) {
-    ClassTable* const class_table = root.Read()->GetClassTable();
-    if (class_table != nullptr) {
-      sum += class_table->NumNonZygoteClasses();
-    }
-  }
-  return sum;
+  CountClassesVisitor visitor;
+  VisitClassLoaders(&visitor);
+  return visitor.num_non_zygote_classes + boot_class_table_.NumNonZygoteClasses();
 }
 
 size_t ClassLinker::NumLoadedClasses() {
@@ -5977,4 +6171,35 @@
   find_array_class_cache_next_victim_ = 0;
 }
 
+void ClassLinker::VisitClassLoadersAndRemoveClearedLoaders(ClassLoaderVisitor* visitor) {
+  Thread* const self = Thread::Current();
+  Locks::classlinker_classes_lock_->AssertExclusiveHeld(self);
+  JavaVMExt* const vm = self->GetJniEnv()->vm;
+  for (auto it = class_loaders_.begin(); it != class_loaders_.end();) {
+    const jweak weak_root = *it;
+    mirror::ClassLoader* const class_loader = down_cast<mirror::ClassLoader*>(
+        vm->DecodeWeakGlobal(self, weak_root));
+    if (class_loader != nullptr) {
+      visitor->Visit(class_loader);
+      ++it;
+    } else {
+      // Remove the cleared weak reference from the array.
+      vm->DeleteWeakGlobalRef(self, weak_root);
+      it = class_loaders_.erase(it);
+    }
+  }
+}
+
+void ClassLinker::VisitClassLoaders(ClassLoaderVisitor* visitor) const {
+  Thread* const self = Thread::Current();
+  JavaVMExt* const vm = self->GetJniEnv()->vm;
+  for (jweak weak_root : class_loaders_) {
+    mirror::ClassLoader* const class_loader = down_cast<mirror::ClassLoader*>(
+        vm->DecodeWeakGlobal(self, weak_root));
+    if (class_loader != nullptr) {
+      visitor->Visit(class_loader);
+    }
+  }
+}
+
 }  // namespace art
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 2a7162b..fee7066 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -59,6 +59,13 @@
 
 enum VisitRootFlags : uint8_t;
 
+class ClassLoaderVisitor {
+ public:
+  virtual ~ClassLoaderVisitor() {}
+  virtual void Visit(mirror::ClassLoader* class_loader)
+      SHARED_REQUIRES(Locks::classlinker_classes_lock_, Locks::mutator_lock_) = 0;
+};
+
 class ClassLinker {
  public:
   // Well known mirror::Class roots accessed via GetClassRoot.
@@ -108,16 +115,19 @@
 
   // Initialize class linker by bootstraping from dex files.
   void InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> boot_class_path)
-      SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_);
+      SHARED_REQUIRES(Locks::mutator_lock_)
+      REQUIRES(!dex_lock_);
 
   // Initialize class linker from one or more images.
   void InitFromImage() SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_);
 
   // Finds a class by its descriptor, loading it if necessary.
   // If class_loader is null, searches boot_class_path_.
-  mirror::Class* FindClass(Thread* self, const char* descriptor,
+  mirror::Class* FindClass(Thread* self,
+                           const char* descriptor,
                            Handle<mirror::ClassLoader> class_loader)
-      SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_);
+      SHARED_REQUIRES(Locks::mutator_lock_)
+      REQUIRES(!dex_lock_);
 
   // Finds a class in the path class loader, loading it if necessary without using JNI. Hash
   // function is supposed to be ComputeModifiedUtf8Hash(descriptor). Returns true if the
@@ -125,19 +135,24 @@
   // was encountered while walking the parent chain (currently only BootClassLoader and
   // PathClassLoader are supported).
   bool FindClassInPathClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
-                                  Thread* self, const char* descriptor, size_t hash,
+                                  Thread* self,
+                                  const char* descriptor,
+                                  size_t hash,
                                   Handle<mirror::ClassLoader> class_loader,
                                   mirror::Class** result)
-      SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_);
+      SHARED_REQUIRES(Locks::mutator_lock_)
+      REQUIRES(!dex_lock_);
 
   // Finds a class by its descriptor using the "system" class loader, ie by searching the
   // boot_class_path_.
   mirror::Class* FindSystemClass(Thread* self, const char* descriptor)
-      SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_);
+      SHARED_REQUIRES(Locks::mutator_lock_)
+      REQUIRES(!dex_lock_);
 
   // Finds the array class given for the element class.
   mirror::Class* FindArrayClass(Thread* self, mirror::Class** element_class)
-      SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_);
+      SHARED_REQUIRES(Locks::mutator_lock_)
+      REQUIRES(!dex_lock_);
 
   // Returns true if the class linker is initialized.
   bool IsInitialized() const {
@@ -145,14 +160,20 @@
   }
 
   // Define a new a class based on a ClassDef from a DexFile
-  mirror::Class* DefineClass(Thread* self, const char* descriptor, size_t hash,
+  mirror::Class* DefineClass(Thread* self,
+                             const char* descriptor,
+                             size_t hash,
                              Handle<mirror::ClassLoader> class_loader,
-                             const DexFile& dex_file, const DexFile::ClassDef& dex_class_def)
-      SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_);
+                             const DexFile& dex_file,
+                             const DexFile::ClassDef& dex_class_def)
+      SHARED_REQUIRES(Locks::mutator_lock_)
+      REQUIRES(!dex_lock_);
 
   // Finds a class by its descriptor, returning null if it isn't wasn't loaded
   // by the given 'class_loader'.
-  mirror::Class* LookupClass(Thread* self, const char* descriptor, size_t hash,
+  mirror::Class* LookupClass(Thread* self,
+                             const char* descriptor,
+                             size_t hash,
                              mirror::ClassLoader* class_loader)
       REQUIRES(!Locks::classlinker_classes_lock_)
       SHARED_REQUIRES(Locks::mutator_lock_);
@@ -167,16 +188,18 @@
   // General class unloading is not supported, this is used to prune
   // unwanted classes during image writing.
   bool RemoveClass(const char* descriptor, mirror::ClassLoader* class_loader)
-      REQUIRES(!Locks::classlinker_classes_lock_) SHARED_REQUIRES(Locks::mutator_lock_);
+      REQUIRES(!Locks::classlinker_classes_lock_)
+      SHARED_REQUIRES(Locks::mutator_lock_);
 
   void DumpAllClasses(int flags)
-      REQUIRES(!Locks::classlinker_classes_lock_) SHARED_REQUIRES(Locks::mutator_lock_);
+      REQUIRES(!Locks::classlinker_classes_lock_)
+      SHARED_REQUIRES(Locks::mutator_lock_);
 
-  void DumpForSigQuit(std::ostream& os)
-      REQUIRES(!Locks::classlinker_classes_lock_);
+  void DumpForSigQuit(std::ostream& os) REQUIRES(!Locks::classlinker_classes_lock_);
 
   size_t NumLoadedClasses()
-      REQUIRES(!Locks::classlinker_classes_lock_) SHARED_REQUIRES(Locks::mutator_lock_);
+      REQUIRES(!Locks::classlinker_classes_lock_)
+      SHARED_REQUIRES(Locks::mutator_lock_);
 
   // Resolve a String with the given index from the DexFile, storing the
   // result in the DexCache. The referrer is used to identify the
@@ -194,41 +217,50 @@
   // result in the DexCache. The referrer is used to identity the
   // target DexCache and ClassLoader to use for resolution.
   mirror::Class* ResolveType(const DexFile& dex_file, uint16_t type_idx, mirror::Class* referrer)
-      SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_, !Roles::uninterruptible_);
+      SHARED_REQUIRES(Locks::mutator_lock_)
+      REQUIRES(!dex_lock_, !Roles::uninterruptible_);
 
   // Resolve a Type with the given index from the DexFile, storing the
   // result in the DexCache. The referrer is used to identify the
   // target DexCache and ClassLoader to use for resolution.
   mirror::Class* ResolveType(uint16_t type_idx, ArtMethod* referrer)
-      SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_, !Roles::uninterruptible_);
+      SHARED_REQUIRES(Locks::mutator_lock_)
+      REQUIRES(!dex_lock_, !Roles::uninterruptible_);
 
   mirror::Class* ResolveType(uint16_t type_idx, ArtField* referrer)
-      SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_, !Roles::uninterruptible_);
+      SHARED_REQUIRES(Locks::mutator_lock_)
+      REQUIRES(!dex_lock_, !Roles::uninterruptible_);
 
   // Resolve a type with the given ID from the DexFile, storing the
   // result in DexCache. The ClassLoader is used to search for the
   // type, since it may be referenced from but not contained within
   // the given DexFile.
-  mirror::Class* ResolveType(const DexFile& dex_file, uint16_t type_idx,
+  mirror::Class* ResolveType(const DexFile& dex_file,
+                             uint16_t type_idx,
                              Handle<mirror::DexCache> dex_cache,
                              Handle<mirror::ClassLoader> class_loader)
-      SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_, !Roles::uninterruptible_);
+      SHARED_REQUIRES(Locks::mutator_lock_)
+      REQUIRES(!dex_lock_, !Roles::uninterruptible_);
 
   // Resolve a method with a given ID from the DexFile, storing the
   // result in DexCache. The ClassLinker and ClassLoader are used as
   // in ResolveType. What is unique is the method type argument which
   // is used to determine if this method is a direct, static, or
   // virtual method.
-  ArtMethod* ResolveMethod(const DexFile& dex_file, uint32_t method_idx,
+  ArtMethod* ResolveMethod(const DexFile& dex_file,
+                           uint32_t method_idx,
                            Handle<mirror::DexCache> dex_cache,
-                           Handle<mirror::ClassLoader> class_loader, ArtMethod* referrer,
+                           Handle<mirror::ClassLoader> class_loader,
+                           ArtMethod* referrer,
                            InvokeType type)
-      SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_, !Roles::uninterruptible_);
+      SHARED_REQUIRES(Locks::mutator_lock_)
+      REQUIRES(!dex_lock_, !Roles::uninterruptible_);
 
   ArtMethod* GetResolvedMethod(uint32_t method_idx, ArtMethod* referrer)
       SHARED_REQUIRES(Locks::mutator_lock_);
   ArtMethod* ResolveMethod(Thread* self, uint32_t method_idx, ArtMethod* referrer, InvokeType type)
-      SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_, !Roles::uninterruptible_);
+      SHARED_REQUIRES(Locks::mutator_lock_)
+      REQUIRES(!dex_lock_, !Roles::uninterruptible_);
   ArtMethod* ResolveMethodWithoutInvokeType(const DexFile& dex_file,
                                             uint32_t method_idx,
                                             Handle<mirror::DexCache> dex_cache,
@@ -241,7 +273,8 @@
   ArtField* GetResolvedField(uint32_t field_idx, mirror::DexCache* dex_cache)
       SHARED_REQUIRES(Locks::mutator_lock_);
   ArtField* ResolveField(uint32_t field_idx, ArtMethod* referrer, bool is_static)
-      SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_, !Roles::uninterruptible_);
+      SHARED_REQUIRES(Locks::mutator_lock_)
+      REQUIRES(!dex_lock_, !Roles::uninterruptible_);
 
   // Resolve a field with a given ID from the DexFile, storing the
   // result in DexCache. The ClassLinker and ClassLoader are used as
@@ -251,16 +284,19 @@
   ArtField* ResolveField(const DexFile& dex_file, uint32_t field_idx,
                          Handle<mirror::DexCache> dex_cache,
                          Handle<mirror::ClassLoader> class_loader, bool is_static)
-      SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_, !Roles::uninterruptible_);
+      SHARED_REQUIRES(Locks::mutator_lock_)
+      REQUIRES(!dex_lock_, !Roles::uninterruptible_);
 
   // Resolve a field with a given ID from the DexFile, storing the
   // result in DexCache. The ClassLinker and ClassLoader are used as
   // in ResolveType. No is_static argument is provided so that Java
   // field resolution semantics are followed.
-  ArtField* ResolveFieldJLS(const DexFile& dex_file, uint32_t field_idx,
+  ArtField* ResolveFieldJLS(const DexFile& dex_file,
+                            uint32_t field_idx,
                             Handle<mirror::DexCache> dex_cache,
                             Handle<mirror::ClassLoader> class_loader)
-      SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_, !Roles::uninterruptible_);
+      SHARED_REQUIRES(Locks::mutator_lock_)
+      REQUIRES(!dex_lock_, !Roles::uninterruptible_);
 
   // Get shorty from method index without resolution. Used to do handlerization.
   const char* MethodShorty(uint32_t method_idx, ArtMethod* referrer, uint32_t* length)
@@ -269,19 +305,25 @@
   // Returns true on success, false if there's an exception pending.
   // can_run_clinit=false allows the compiler to attempt to init a class,
   // given the restriction that no <clinit> execution is possible.
-  bool EnsureInitialized(Thread* self, Handle<mirror::Class> c, bool can_init_fields,
+  bool EnsureInitialized(Thread* self,
+                         Handle<mirror::Class> c,
+                         bool can_init_fields,
                          bool can_init_parents)
-      SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_, !Roles::uninterruptible_);
+      SHARED_REQUIRES(Locks::mutator_lock_)
+      REQUIRES(!dex_lock_, !Roles::uninterruptible_);
 
   // Initializes classes that have instances in the image but that have
   // <clinit> methods so they could not be initialized by the compiler.
-  void RunRootClinits() SHARED_REQUIRES(Locks::mutator_lock_)
+  void RunRootClinits()
+      SHARED_REQUIRES(Locks::mutator_lock_)
       REQUIRES(!dex_lock_, !Roles::uninterruptible_);
 
   mirror::DexCache* RegisterDexFile(const DexFile& dex_file)
-      REQUIRES(!dex_lock_) SHARED_REQUIRES(Locks::mutator_lock_);
+      REQUIRES(!dex_lock_)
+      SHARED_REQUIRES(Locks::mutator_lock_);
   void RegisterDexFile(const DexFile& dex_file, Handle<mirror::DexCache> dex_cache)
-      REQUIRES(!dex_lock_) SHARED_REQUIRES(Locks::mutator_lock_);
+      REQUIRES(!dex_lock_)
+      SHARED_REQUIRES(Locks::mutator_lock_);
 
   const OatFile* RegisterOatFile(const OatFile* oat_file)
       REQUIRES(!dex_lock_);
@@ -295,26 +337,32 @@
       REQUIRES(!dex_lock_);
 
   void VisitClasses(ClassVisitor* visitor)
-      REQUIRES(!Locks::classlinker_classes_lock_) SHARED_REQUIRES(Locks::mutator_lock_);
+      REQUIRES(!Locks::classlinker_classes_lock_)
+      SHARED_REQUIRES(Locks::mutator_lock_);
 
   // Less efficient variant of VisitClasses that copies the class_table_ into secondary storage
   // so that it can visit individual classes without holding the doesn't hold the
   // Locks::classlinker_classes_lock_. As the Locks::classlinker_classes_lock_ isn't held this code
   // can race with insertion and deletion of classes while the visitor is being called.
   void VisitClassesWithoutClassesLock(ClassVisitor* visitor)
-      SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_);
+      SHARED_REQUIRES(Locks::mutator_lock_)
+      REQUIRES(!dex_lock_);
 
   void VisitClassRoots(RootVisitor* visitor, VisitRootFlags flags)
-      REQUIRES(!Locks::classlinker_classes_lock_) SHARED_REQUIRES(Locks::mutator_lock_);
+      REQUIRES(!Locks::classlinker_classes_lock_)
+      SHARED_REQUIRES(Locks::mutator_lock_);
   void VisitRoots(RootVisitor* visitor, VisitRootFlags flags)
-      REQUIRES(!dex_lock_) SHARED_REQUIRES(Locks::mutator_lock_);
+      REQUIRES(!dex_lock_)
+      SHARED_REQUIRES(Locks::mutator_lock_);
 
   mirror::DexCache* FindDexCache(Thread* self,
                                  const DexFile& dex_file,
                                  bool allow_failure = false)
-      REQUIRES(!dex_lock_) SHARED_REQUIRES(Locks::mutator_lock_);
+      REQUIRES(!dex_lock_)
+      SHARED_REQUIRES(Locks::mutator_lock_);
   void FixupDexCaches(ArtMethod* resolution_method)
-      REQUIRES(!dex_lock_) SHARED_REQUIRES(Locks::mutator_lock_);
+      REQUIRES(!dex_lock_)
+      SHARED_REQUIRES(Locks::mutator_lock_);
 
   // Finds or creates the oat file holding dex_location. Then loads and returns
   // all corresponding dex files (there may be more than one dex file loaded
@@ -331,52 +379,68 @@
   // could end up starving GC if we need to generate or relocate any oat
   // files.
   std::vector<std::unique_ptr<const DexFile>> OpenDexFilesFromOat(
-      const char* dex_location, const char* oat_location,
+      const char* dex_location,
+      const char* oat_location,
       std::vector<std::string>* error_msgs)
       REQUIRES(!dex_lock_, !Locks::mutator_lock_);
 
   // Allocate an instance of a java.lang.Object.
-  mirror::Object* AllocObject(Thread* self) SHARED_REQUIRES(Locks::mutator_lock_)
+  mirror::Object* AllocObject(Thread* self)
+      SHARED_REQUIRES(Locks::mutator_lock_)
       REQUIRES(!Roles::uninterruptible_);
 
   // TODO: replace this with multiple methods that allocate the correct managed type.
   template <class T>
   mirror::ObjectArray<T>* AllocObjectArray(Thread* self, size_t length)
-      SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
+      SHARED_REQUIRES(Locks::mutator_lock_)
+      REQUIRES(!Roles::uninterruptible_);
 
   mirror::ObjectArray<mirror::Class>* AllocClassArray(Thread* self, size_t length)
-      SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
+      SHARED_REQUIRES(Locks::mutator_lock_)
+      REQUIRES(!Roles::uninterruptible_);
 
   mirror::ObjectArray<mirror::String>* AllocStringArray(Thread* self, size_t length)
-      SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
+      SHARED_REQUIRES(Locks::mutator_lock_)
+      REQUIRES(!Roles::uninterruptible_);
 
   LengthPrefixedArray<ArtField>* AllocArtFieldArray(Thread* self, size_t length);
 
   LengthPrefixedArray<ArtMethod>* AllocArtMethodArray(Thread* self, size_t length);
 
   mirror::PointerArray* AllocPointerArray(Thread* self, size_t length)
-      SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
+      SHARED_REQUIRES(Locks::mutator_lock_)
+      REQUIRES(!Roles::uninterruptible_);
 
   mirror::IfTable* AllocIfTable(Thread* self, size_t ifcount)
-      SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
+      SHARED_REQUIRES(Locks::mutator_lock_)
+      REQUIRES(!Roles::uninterruptible_);
 
-  mirror::ObjectArray<mirror::StackTraceElement>* AllocStackTraceElementArray(
-      Thread* self, size_t length) SHARED_REQUIRES(Locks::mutator_lock_)
-          REQUIRES(!Roles::uninterruptible_);
+  mirror::ObjectArray<mirror::StackTraceElement>* AllocStackTraceElementArray(Thread* self,
+                                                                              size_t length)
+      SHARED_REQUIRES(Locks::mutator_lock_)
+      REQUIRES(!Roles::uninterruptible_);
 
   void VerifyClass(Thread* self, Handle<mirror::Class> klass)
-      SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_);
-  bool VerifyClassUsingOatFile(const DexFile& dex_file, mirror::Class* klass,
+      SHARED_REQUIRES(Locks::mutator_lock_)
+      REQUIRES(!dex_lock_);
+  bool VerifyClassUsingOatFile(const DexFile& dex_file,
+                               mirror::Class* klass,
                                mirror::Class::Status& oat_file_class_status)
-      SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_);
+      SHARED_REQUIRES(Locks::mutator_lock_)
+      REQUIRES(!dex_lock_);
   void ResolveClassExceptionHandlerTypes(const DexFile& dex_file,
                                          Handle<mirror::Class> klass)
-      SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_);
+      SHARED_REQUIRES(Locks::mutator_lock_)
+      REQUIRES(!dex_lock_);
   void ResolveMethodExceptionHandlerTypes(const DexFile& dex_file, ArtMethod* klass)
-      SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_);
+      SHARED_REQUIRES(Locks::mutator_lock_)
+      REQUIRES(!dex_lock_);
 
-  mirror::Class* CreateProxyClass(ScopedObjectAccessAlreadyRunnable& soa, jstring name,
-                                  jobjectArray interfaces, jobject loader, jobjectArray methods,
+  mirror::Class* CreateProxyClass(ScopedObjectAccessAlreadyRunnable& soa,
+                                  jstring name,
+                                  jobjectArray interfaces,
+                                  jobject loader,
+                                  jobjectArray methods,
                                   jobjectArray throws)
       SHARED_REQUIRES(Locks::mutator_lock_);
   std::string GetDescriptorForProxy(mirror::Class* proxy_class)
@@ -390,7 +454,8 @@
       SHARED_REQUIRES(Locks::mutator_lock_);
 
   // Get the oat code for a method from a method index.
-  const void* GetQuickOatCodeFor(const DexFile& dex_file, uint16_t class_def_idx,
+  const void* GetQuickOatCodeFor(const DexFile& dex_file,
+                                 uint16_t class_def_idx,
                                  uint32_t method_idx)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
@@ -462,7 +527,8 @@
   // Creates a GlobalRef PathClassLoader that can be used to load classes from the given dex files.
   // Note: the objects are not completely set up. Do not use this outside of tests and the compiler.
   jobject CreatePathClassLoader(Thread* self, std::vector<const DexFile*>& dex_files)
-      SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_);
+      SHARED_REQUIRES(Locks::mutator_lock_)
+      REQUIRES(!dex_lock_);
 
   size_t GetImagePointerSize() const {
     DCHECK(ValidPointerSize(image_pointer_size_)) << image_pointer_size_;
@@ -481,8 +547,18 @@
   void DropFindArrayClassCache() SHARED_REQUIRES(Locks::mutator_lock_);
 
  private:
+  // The RemoveClearedLoaders version removes cleared weak global class loaders and frees their
+  // class tables. This version can only be called with reader access to the
+  // classlinker_classes_lock_ since it modifies the class_loaders_ list.
+  void VisitClassLoadersAndRemoveClearedLoaders(ClassLoaderVisitor* visitor)
+      REQUIRES(Locks::classlinker_classes_lock_)
+      SHARED_REQUIRES(Locks::mutator_lock_);
+  void VisitClassLoaders(ClassLoaderVisitor* visitor) const
+      SHARED_REQUIRES(Locks::classlinker_classes_lock_, Locks::mutator_lock_);
+
+
   void VisitClassesInternal(ClassVisitor* visitor)
-      REQUIRES(Locks::classlinker_classes_lock_) SHARED_REQUIRES(Locks::mutator_lock_);
+      SHARED_REQUIRES(Locks::classlinker_classes_lock_, Locks::mutator_lock_);
 
   // Returns the number of zygote and image classes.
   size_t NumZygoteClasses() const
@@ -495,36 +571,48 @@
       SHARED_REQUIRES(Locks::mutator_lock_);
 
   OatFile& GetImageOatFile(gc::space::ImageSpace* space)
-      REQUIRES(!dex_lock_) SHARED_REQUIRES(Locks::mutator_lock_);
+      REQUIRES(!dex_lock_)
+      SHARED_REQUIRES(Locks::mutator_lock_);
 
-  void FinishInit(Thread* self) SHARED_REQUIRES(Locks::mutator_lock_)
+  void FinishInit(Thread* self)
+  SHARED_REQUIRES(Locks::mutator_lock_)
       REQUIRES(!dex_lock_, !Roles::uninterruptible_);
 
   // For early bootstrapping by Init
   mirror::Class* AllocClass(Thread* self, mirror::Class* java_lang_Class, uint32_t class_size)
-      SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
+      SHARED_REQUIRES(Locks::mutator_lock_)
+      REQUIRES(!Roles::uninterruptible_);
 
   // Alloc* convenience functions to avoid needing to pass in mirror::Class*
   // values that are known to the ClassLinker such as
   // kObjectArrayClass and kJavaLangString etc.
   mirror::Class* AllocClass(Thread* self, uint32_t class_size)
-      SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
+      SHARED_REQUIRES(Locks::mutator_lock_)
+      REQUIRES(!Roles::uninterruptible_);
   mirror::DexCache* AllocDexCache(Thread* self, const DexFile& dex_file)
-      SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
+      SHARED_REQUIRES(Locks::mutator_lock_)
+      REQUIRES(!Roles::uninterruptible_);
 
   mirror::Class* CreatePrimitiveClass(Thread* self, Primitive::Type type)
-      SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
+      SHARED_REQUIRES(Locks::mutator_lock_)
+      REQUIRES(!Roles::uninterruptible_);
   mirror::Class* InitializePrimitiveClass(mirror::Class* primitive_class, Primitive::Type type)
-      SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
+      SHARED_REQUIRES(Locks::mutator_lock_)
+      REQUIRES(!Roles::uninterruptible_);
 
-  mirror::Class* CreateArrayClass(Thread* self, const char* descriptor, size_t hash,
+  mirror::Class* CreateArrayClass(Thread* self,
+                                  const char* descriptor,
+                                  size_t hash,
                                   Handle<mirror::ClassLoader> class_loader)
-      SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_, !Roles::uninterruptible_);
+      SHARED_REQUIRES(Locks::mutator_lock_)
+      REQUIRES(!dex_lock_, !Roles::uninterruptible_);
 
   void AppendToBootClassPath(Thread* self, const DexFile& dex_file)
-      SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_);
+      SHARED_REQUIRES(Locks::mutator_lock_)
+      REQUIRES(!dex_lock_);
   void AppendToBootClassPath(const DexFile& dex_file, Handle<mirror::DexCache> dex_cache)
-      SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_);
+      SHARED_REQUIRES(Locks::mutator_lock_)
+      REQUIRES(!dex_lock_);
 
   // Precomputes size needed for Class, in the case of a non-temporary class this size must be
   // sufficient to hold all static fields.
@@ -533,22 +621,30 @@
 
   // Setup the classloader, class def index, type idx so that we can insert this class in the class
   // table.
-  void SetupClass(const DexFile& dex_file, const DexFile::ClassDef& dex_class_def,
-                  Handle<mirror::Class> klass, mirror::ClassLoader* class_loader)
+  void SetupClass(const DexFile& dex_file,
+                  const DexFile::ClassDef& dex_class_def,
+                  Handle<mirror::Class> klass,
+                  mirror::ClassLoader* class_loader)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
-  void LoadClass(Thread* self, const DexFile& dex_file, const DexFile::ClassDef& dex_class_def,
+  void LoadClass(Thread* self,
+                 const DexFile& dex_file,
+                 const DexFile::ClassDef& dex_class_def,
                  Handle<mirror::Class> klass)
       SHARED_REQUIRES(Locks::mutator_lock_);
-  void LoadClassMembers(Thread* self, const DexFile& dex_file, const uint8_t* class_data,
-                        Handle<mirror::Class> klass, const OatFile::OatClass* oat_class)
+  void LoadClassMembers(Thread* self,
+                        const DexFile& dex_file,
+                        const uint8_t* class_data,
+                        Handle<mirror::Class> klass,
+                        const OatFile::OatClass* oat_class)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
-  void LoadField(const ClassDataItemIterator& it, Handle<mirror::Class> klass,
-                 ArtField* dst)
+  void LoadField(const ClassDataItemIterator& it, Handle<mirror::Class> klass, ArtField* dst)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
-  void LoadMethod(Thread* self, const DexFile& dex_file, const ClassDataItemIterator& it,
+  void LoadMethod(Thread* self,
+                  const DexFile& dex_file,
+                  const ClassDataItemIterator& it,
                   Handle<mirror::Class> klass, ArtMethod* dst)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
@@ -560,40 +656,50 @@
       SHARED_REQUIRES(Locks::mutator_lock_);
 
   void RegisterDexFileLocked(const DexFile& dex_file, Handle<mirror::DexCache> dex_cache)
-      REQUIRES(dex_lock_) SHARED_REQUIRES(Locks::mutator_lock_);
-  mirror::DexCache* FindDexCacheLocked(Thread* self,
-                                       const DexFile& dex_file,
-                                       bool allow_failure)
+      REQUIRES(dex_lock_)
+      SHARED_REQUIRES(Locks::mutator_lock_);
+  mirror::DexCache* FindDexCacheLocked(Thread* self, const DexFile& dex_file, bool allow_failure)
       REQUIRES(dex_lock_)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
-  bool InitializeClass(Thread* self, Handle<mirror::Class> klass, bool can_run_clinit,
+  bool InitializeClass(Thread* self,
+                       Handle<mirror::Class> klass,
+                       bool can_run_clinit,
                        bool can_init_parents)
-      SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_);
-  bool WaitForInitializeClass(Handle<mirror::Class> klass, Thread* self,
+      SHARED_REQUIRES(Locks::mutator_lock_)
+      REQUIRES(!dex_lock_);
+  bool WaitForInitializeClass(Handle<mirror::Class> klass,
+                              Thread* self,
                               ObjectLock<mirror::Class>& lock);
   bool ValidateSuperClassDescriptors(Handle<mirror::Class> klass)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
-  bool IsSameDescriptorInDifferentClassContexts(Thread* self, const char* descriptor,
+  bool IsSameDescriptorInDifferentClassContexts(Thread* self,
+                                                const char* descriptor,
                                                 Handle<mirror::ClassLoader> class_loader1,
                                                 Handle<mirror::ClassLoader> class_loader2)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
-  bool IsSameMethodSignatureInDifferentClassContexts(Thread* self, ArtMethod* method,
-                                                     mirror::Class* klass1, mirror::Class* klass2)
+  bool IsSameMethodSignatureInDifferentClassContexts(Thread* self,
+                                                     ArtMethod* method,
+                                                     mirror::Class* klass1,
+                                                     mirror::Class* klass2)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
-  bool LinkClass(Thread* self, const char* descriptor, Handle<mirror::Class> klass,
+  bool LinkClass(Thread* self,
+                 const char* descriptor,
+                 Handle<mirror::Class> klass,
                  Handle<mirror::ObjectArray<mirror::Class>> interfaces,
                  MutableHandle<mirror::Class>* h_new_class_out)
-      SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Locks::classlinker_classes_lock_);
+      SHARED_REQUIRES(Locks::mutator_lock_)
+      REQUIRES(!Locks::classlinker_classes_lock_);
 
   bool LinkSuperClass(Handle<mirror::Class> klass)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
   bool LoadSuperAndInterfaces(Handle<mirror::Class> klass, const DexFile& dex_file)
-      SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_);
+      SHARED_REQUIRES(Locks::mutator_lock_)
+      REQUIRES(!dex_lock_);
 
   bool LinkMethods(Thread* self,
                    Handle<mirror::Class> klass,
@@ -604,7 +710,8 @@
   bool LinkVirtualMethods(Thread* self, Handle<mirror::Class> klass)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
-  bool LinkInterfaceMethods(Thread* self, Handle<mirror::Class> klass,
+  bool LinkInterfaceMethods(Thread* self,
+                            Handle<mirror::Class> klass,
                             Handle<mirror::ObjectArray<mirror::Class>> interfaces,
                             ArtMethod** out_imt)
       SHARED_REQUIRES(Locks::mutator_lock_);
@@ -615,7 +722,8 @@
       SHARED_REQUIRES(Locks::mutator_lock_);
   bool LinkFields(Thread* self, Handle<mirror::Class> klass, bool is_static, size_t* class_size)
       SHARED_REQUIRES(Locks::mutator_lock_);
-  void LinkCode(ArtMethod* method, const OatFile::OatClass* oat_class,
+  void LinkCode(ArtMethod* method,
+                const OatFile::OatClass* oat_class,
                 uint32_t class_def_method_index)
       SHARED_REQUIRES(Locks::mutator_lock_);
   void CreateReferenceInstanceOffsets(Handle<mirror::Class> klass)
@@ -628,13 +736,14 @@
 
   // For use by ImageWriter to find DexCaches for its roots
   ReaderWriterMutex* DexLock()
-      SHARED_REQUIRES(Locks::mutator_lock_) LOCK_RETURNED(dex_lock_) {
+      SHARED_REQUIRES(Locks::mutator_lock_)
+      LOCK_RETURNED(dex_lock_) {
     return &dex_lock_;
   }
   size_t GetDexCacheCount() SHARED_REQUIRES(Locks::mutator_lock_, dex_lock_) {
     return dex_caches_.size();
   }
-  const std::list<jobject>& GetDexCaches() SHARED_REQUIRES(Locks::mutator_lock_, dex_lock_) {
+  const std::list<jweak>& GetDexCaches() SHARED_REQUIRES(Locks::mutator_lock_, dex_lock_) {
     return dex_caches_;
   }
 
@@ -662,7 +771,8 @@
       SHARED_REQUIRES(Locks::mutator_lock_, Locks::classlinker_classes_lock_);
   // Insert a new class table if not found.
   ClassTable* InsertClassTableForClassLoader(mirror::ClassLoader* class_loader)
-      SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(Locks::classlinker_classes_lock_);
+      SHARED_REQUIRES(Locks::mutator_lock_)
+      REQUIRES(Locks::classlinker_classes_lock_);
 
   // EnsureResolved is called to make sure that a class in the class_table_ has been resolved
   // before returning it to the caller. Its the responsibility of the thread that placed the class
@@ -671,7 +781,9 @@
   // retire a class, the version of the class in the table is returned and this may differ from
   // the class passed in.
   mirror::Class* EnsureResolved(Thread* self, const char* descriptor, mirror::Class* klass)
-      WARN_UNUSED SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_);
+      WARN_UNUSED
+      SHARED_REQUIRES(Locks::mutator_lock_)
+      REQUIRES(!dex_lock_);
 
   void FixupTemporaryDeclaringClass(mirror::Class* temp_class, mirror::Class* new_class)
       SHARED_REQUIRES(Locks::mutator_lock_);
@@ -686,7 +798,8 @@
   // class.
   // Note: Currently we only store the descriptor, so we cannot throw the exact throwable, only
   //       a recreation with a custom string.
-  void ThrowEarlierClassFailure(mirror::Class* c) SHARED_REQUIRES(Locks::mutator_lock_)
+  void ThrowEarlierClassFailure(mirror::Class* c)
+      SHARED_REQUIRES(Locks::mutator_lock_)
       REQUIRES(!dex_lock_);
 
   // Check for duplicate class definitions of the given oat file against all open oat files.
@@ -709,12 +822,12 @@
   mutable ReaderWriterMutex dex_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
   // JNI weak globals to allow dex caches to get unloaded. We lazily delete weak globals when we
   // register new dex files.
-  std::list<jobject> dex_caches_ GUARDED_BY(dex_lock_);
+  std::list<jweak> dex_caches_ GUARDED_BY(dex_lock_);
   std::vector<const OatFile*> oat_files_ GUARDED_BY(dex_lock_);
 
-  // This contains the class laoders which have class tables. It is populated by
-  // InsertClassTableForClassLoader.
-  std::vector<GcRoot<mirror::ClassLoader>> class_loaders_
+  // This contains the class loaders which have class tables. It is populated by
+  // InsertClassTableForClassLoader. Weak roots to enable class unloading.
+  std::list<jweak> class_loaders_
       GUARDED_BY(Locks::classlinker_classes_lock_);
 
   // Boot class path table. Since the class loader for this is null.
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index c3191fa..b4ea3b3 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -165,12 +165,14 @@
     EXPECT_TRUE(method->GetName() != nullptr);
     EXPECT_TRUE(method->GetSignature() != Signature::NoSignature());
 
-    EXPECT_TRUE(method->HasDexCacheResolvedMethods());
-    EXPECT_TRUE(method->HasDexCacheResolvedTypes());
+    EXPECT_TRUE(method->HasDexCacheResolvedMethods(sizeof(void*)));
+    EXPECT_TRUE(method->HasDexCacheResolvedTypes(sizeof(void*)));
     EXPECT_TRUE(method->HasSameDexCacheResolvedMethods(
-        method->GetDeclaringClass()->GetDexCache()->GetResolvedMethods()));
+        method->GetDeclaringClass()->GetDexCache()->GetResolvedMethods(),
+        sizeof(void*)));
     EXPECT_TRUE(method->HasSameDexCacheResolvedTypes(
-        method->GetDeclaringClass()->GetDexCache()->GetResolvedTypes()));
+        method->GetDeclaringClass()->GetDexCache()->GetResolvedTypes(),
+        sizeof(void*)));
   }
 
   void AssertField(mirror::Class* klass, ArtField* field)
@@ -357,8 +359,9 @@
     // Verify the dex cache has resolution methods in all resolved method slots
     mirror::DexCache* dex_cache = class_linker_->FindDexCache(Thread::Current(), dex);
     auto* resolved_methods = dex_cache->GetResolvedMethods();
-    for (size_t i = 0; i < static_cast<size_t>(resolved_methods->GetLength()); i++) {
-      EXPECT_TRUE(resolved_methods->GetElementPtrSize<ArtMethod*>(i, sizeof(void*)) != nullptr)
+    for (size_t i = 0, num_methods = dex_cache->NumResolvedMethods(); i != num_methods; ++i) {
+      EXPECT_TRUE(
+          mirror::DexCache::GetElementPtrSize(resolved_methods, i, sizeof(void*)) != nullptr)
           << dex.GetLocation() << " i=" << i;
     }
   }
@@ -565,6 +568,10 @@
     addOffset(OFFSETOF_MEMBER(mirror::DexCache, dex_), "dex");
     addOffset(OFFSETOF_MEMBER(mirror::DexCache, dex_file_), "dexFile");
     addOffset(OFFSETOF_MEMBER(mirror::DexCache, location_), "location");
+    addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_resolved_fields_), "numResolvedFields");
+    addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_resolved_methods_), "numResolvedMethods");
+    addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_resolved_types_), "numResolvedTypes");
+    addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_strings_), "numStrings");
     addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_fields_), "resolvedFields");
     addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_methods_), "resolvedMethods");
     addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_types_), "resolvedTypes");
diff --git a/runtime/class_table.h b/runtime/class_table.h
index 6b18d90..727392e 100644
--- a/runtime/class_table.h
+++ b/runtime/class_table.h
@@ -58,10 +58,10 @@
       REQUIRES(Locks::classlinker_classes_lock_) SHARED_REQUIRES(Locks::mutator_lock_);
 
   // Returns the number of classes in previous snapshots.
-  size_t NumZygoteClasses() const REQUIRES(Locks::classlinker_classes_lock_);
+  size_t NumZygoteClasses() const SHARED_REQUIRES(Locks::classlinker_classes_lock_);
 
   // Returns all off the classes in the lastest snapshot.
-  size_t NumNonZygoteClasses() const REQUIRES(Locks::classlinker_classes_lock_);
+  size_t NumNonZygoteClasses() const SHARED_REQUIRES(Locks::classlinker_classes_lock_);
 
   // 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)
@@ -79,7 +79,7 @@
 
   // Return false if the callback told us to exit.
   bool Visit(ClassVisitor* visitor)
-      REQUIRES(Locks::classlinker_classes_lock_) SHARED_REQUIRES(Locks::mutator_lock_);
+      SHARED_REQUIRES(Locks::classlinker_classes_lock_, Locks::mutator_lock_);
 
   mirror::Class* Lookup(const char* descriptor, size_t hash)
       SHARED_REQUIRES(Locks::classlinker_classes_lock_, Locks::mutator_lock_);
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index ffca01d..d5691af 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -218,6 +218,17 @@
                << " " << dex_pc_offset;
   }
 
+  // We only care about invokes in the Jit.
+  void InvokeVirtualOrInterface(Thread* thread ATTRIBUTE_UNUSED,
+                                mirror::Object*,
+                                ArtMethod* method,
+                                uint32_t dex_pc,
+                                ArtMethod*)
+      OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
+    LOG(ERROR) << "Unexpected invoke event in debugger " << PrettyMethod(method)
+               << " " << dex_pc;
+  }
+
  private:
   static bool IsReturn(ArtMethod* method, uint32_t dex_pc)
       SHARED_REQUIRES(Locks::mutator_lock_) {
@@ -567,7 +578,7 @@
   }
 
   Runtime* runtime = Runtime::Current();
-  runtime->GetThreadList()->SuspendAll(__FUNCTION__);
+  ScopedSuspendAll ssa(__FUNCTION__);
   Thread* self = Thread::Current();
   ThreadState old_state = self->SetStateUnsafe(kRunnable);
   CHECK_NE(old_state, kRunnable);
@@ -577,8 +588,6 @@
   instrumentation_events_ = 0;
   gDebuggerActive = true;
   CHECK_EQ(self->SetStateUnsafe(old_state), kRunnable);
-  runtime->GetThreadList()->ResumeAll();
-
   LOG(INFO) << "Debugger is active";
 }
 
@@ -591,32 +600,32 @@
   // to kRunnable to avoid scoped object access transitions. Remove the debugger as a listener
   // and clear the object registry.
   Runtime* runtime = Runtime::Current();
-  runtime->GetThreadList()->SuspendAll(__FUNCTION__);
   Thread* self = Thread::Current();
-  ThreadState old_state = self->SetStateUnsafe(kRunnable);
-
-  // Debugger may not be active at this point.
-  if (IsDebuggerActive()) {
-    {
-      // Since we're going to disable deoptimization, we clear the deoptimization requests queue.
-      // This prevents us from having any pending deoptimization request when the debugger attaches
-      // to us again while no event has been requested yet.
-      MutexLock mu(Thread::Current(), *Locks::deoptimization_lock_);
-      deoptimization_requests_.clear();
-      full_deoptimization_event_count_ = 0U;
+  {
+    ScopedSuspendAll ssa(__FUNCTION__);
+    ThreadState old_state = self->SetStateUnsafe(kRunnable);
+    // Debugger may not be active at this point.
+    if (IsDebuggerActive()) {
+      {
+        // Since we're going to disable deoptimization, we clear the deoptimization requests queue.
+        // This prevents us from having any pending deoptimization request when the debugger attaches
+        // to us again while no event has been requested yet.
+        MutexLock mu(Thread::Current(), *Locks::deoptimization_lock_);
+        deoptimization_requests_.clear();
+        full_deoptimization_event_count_ = 0U;
+      }
+      if (instrumentation_events_ != 0) {
+        runtime->GetInstrumentation()->RemoveListener(&gDebugInstrumentationListener,
+                                                      instrumentation_events_);
+        instrumentation_events_ = 0;
+      }
+      if (RequiresDeoptimization()) {
+        runtime->GetInstrumentation()->DisableDeoptimization(kDbgInstrumentationKey);
+      }
+      gDebuggerActive = false;
     }
-    if (instrumentation_events_ != 0) {
-      runtime->GetInstrumentation()->RemoveListener(&gDebugInstrumentationListener,
-                                                    instrumentation_events_);
-      instrumentation_events_ = 0;
-    }
-    if (RequiresDeoptimization()) {
-      runtime->GetInstrumentation()->DisableDeoptimization(kDbgInstrumentationKey);
-    }
-    gDebuggerActive = false;
+    CHECK_EQ(self->SetStateUnsafe(old_state), kRunnable);
   }
-  CHECK_EQ(self->SetStateUnsafe(old_state), kRunnable);
-  runtime->GetThreadList()->ResumeAll();
 
   {
     ScopedObjectAccess soa(self);
@@ -740,9 +749,8 @@
   MonitorInfo monitor_info;
   {
     ScopedThreadSuspension sts(self, kSuspended);
-    Runtime::Current()->GetThreadList()->SuspendAll(__FUNCTION__);
+    ScopedSuspendAll ssa(__FUNCTION__);
     monitor_info = MonitorInfo(o);
-    Runtime::Current()->GetThreadList()->ResumeAll();
   }
   if (monitor_info.owner_ != nullptr) {
     expandBufAddObjectId(reply, gRegistry->Add(monitor_info.owner_->GetPeer()));
@@ -3150,8 +3158,7 @@
   CHECK_EQ(self->GetState(), kRunnable);
   ScopedThreadSuspension sts(self, kWaitingForDeoptimization);
   // We need to suspend mutator threads first.
-  Runtime* const runtime = Runtime::Current();
-  runtime->GetThreadList()->SuspendAll(__FUNCTION__);
+  ScopedSuspendAll ssa(__FUNCTION__);
   const ThreadState old_state = self->SetStateUnsafe(kRunnable);
   {
     MutexLock mu(self, *Locks::deoptimization_lock_);
@@ -3163,7 +3170,6 @@
     deoptimization_requests_.clear();
   }
   CHECK_EQ(self->SetStateUnsafe(old_state), kRunnable);
-  runtime->GetThreadList()->ResumeAll();
 }
 
 static bool IsMethodPossiblyInlined(Thread* self, ArtMethod* m)
@@ -3490,6 +3496,62 @@
   return instrumentation->IsDeoptimized(m);
 }
 
+struct NeedsDeoptimizationVisitor : public StackVisitor {
+ public:
+  explicit NeedsDeoptimizationVisitor(Thread* self)
+      SHARED_REQUIRES(Locks::mutator_lock_)
+    : StackVisitor(self, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames),
+      needs_deoptimization_(false) {}
+
+  bool VisitFrame() OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
+    // The visitor is meant to be used when handling exception from compiled code only.
+    CHECK(!IsShadowFrame()) << "We only expect to visit compiled frame: " << PrettyMethod(GetMethod());
+    ArtMethod* method = GetMethod();
+    if (method == nullptr) {
+      // We reach an upcall and don't need to deoptimize this part of the stack (ManagedFragment)
+      // so we can stop the visit.
+      DCHECK(!needs_deoptimization_);
+      return false;
+    }
+    if (Runtime::Current()->GetInstrumentation()->InterpretOnly()) {
+      // We found a compiled frame in the stack but instrumentation is set to interpret
+      // everything: we need to deoptimize.
+      needs_deoptimization_ = true;
+      return false;
+    }
+    if (Runtime::Current()->GetInstrumentation()->IsDeoptimized(method)) {
+      // We found a deoptimized method in the stack.
+      needs_deoptimization_ = true;
+      return false;
+    }
+    return true;
+  }
+
+  bool NeedsDeoptimization() const {
+    return needs_deoptimization_;
+  }
+
+ private:
+  // Do we need to deoptimize the stack?
+  bool needs_deoptimization_;
+
+  DISALLOW_COPY_AND_ASSIGN(NeedsDeoptimizationVisitor);
+};
+
+// Do we need to deoptimize the stack to handle an exception?
+bool Dbg::IsForcedInterpreterNeededForExceptionImpl(Thread* thread) {
+  const SingleStepControl* const ssc = thread->GetSingleStepControl();
+  if (ssc != nullptr) {
+    // We deopt to step into the catch handler.
+    return true;
+  }
+  // Deoptimization is required if at least one method in the stack needs it. However we
+  // skip frames that will be unwound (thus not executed).
+  NeedsDeoptimizationVisitor visitor(thread);
+  visitor.WalkStack(true);  // includes upcall.
+  return visitor.NeedsDeoptimization();
+}
+
 // Scoped utility class to suspend a thread so that we may do tasks such as walk its stack. Doesn't
 // cause suspension if the thread is the current thread.
 class ScopedDebuggerThreadSuspension {
@@ -3821,7 +3883,9 @@
         if (shorty[i + 1] == 'L') {
           // Did we really get an argument of an appropriate reference type?
           mirror::Class* parameter_type =
-              m->GetClassFromTypeIndex(types->GetTypeItem(i).type_idx_, true);
+              m->GetClassFromTypeIndex(types->GetTypeItem(i).type_idx_,
+                                       true /* resolve */,
+                                       sizeof(void*));
           mirror::Object* argument = gRegistry->Get<mirror::Object*>(arg_values[i], &error);
           if (error != JDWP::ERR_NONE) {
             return JDWP::ERR_INVALID_OBJECT;
@@ -4655,13 +4719,9 @@
         // Need to acquire the mutator lock before the heap bitmap lock with exclusive access since
         // RosAlloc's internal logic doesn't know to release and reacquire the heap bitmap lock.
         ScopedThreadSuspension sts(self, kSuspended);
-        ThreadList* tl = Runtime::Current()->GetThreadList();
-        tl->SuspendAll(__FUNCTION__);
-        {
-          ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
-          space->AsRosAllocSpace()->Walk(HeapChunkContext::HeapChunkJavaCallback, &context);
-        }
-        tl->ResumeAll();
+        ScopedSuspendAll ssa(__FUNCTION__);
+        ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
+        space->AsRosAllocSpace()->Walk(HeapChunkContext::HeapChunkJavaCallback, &context);
       } else if (space->IsBumpPointerSpace()) {
         ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
         context.SetChunkOverhead(0);
@@ -4671,13 +4731,11 @@
         heap->IncrementDisableMovingGC(self);
         {
           ScopedThreadSuspension sts(self, kSuspended);
-          ThreadList* tl = Runtime::Current()->GetThreadList();
-          tl->SuspendAll(__FUNCTION__);
+          ScopedSuspendAll ssa(__FUNCTION__);
           ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
           context.SetChunkOverhead(0);
           space->AsRegionSpace()->Walk(BumpPointerSpaceCallback, &context);
           HeapChunkContext::HeapChunkJavaCallback(nullptr, nullptr, 0, &context);
-          tl->ResumeAll();
         }
         heap->DecrementDisableMovingGC(self);
       } else {
diff --git a/runtime/debugger.h b/runtime/debugger.h
index a9fa6ce..8278fc6 100644
--- a/runtime/debugger.h
+++ b/runtime/debugger.h
@@ -576,6 +576,19 @@
     return IsForcedInterpreterNeededForUpcallImpl(thread, m);
   }
 
+  // Indicates whether we need to force the use of interpreter when handling an
+  // exception. This allows to deoptimize the stack and continue execution with
+  // the interpreter.
+  // Note: the interpreter will start by handling the exception when executing
+  // the deoptimized frames.
+  static bool IsForcedInterpreterNeededForException(Thread* thread)
+      SHARED_REQUIRES(Locks::mutator_lock_) {
+    if (!IsDebuggerActive()) {
+      return false;
+    }
+    return IsForcedInterpreterNeededForExceptionImpl(thread);
+  }
+
   // Single-stepping.
   static JDWP::JdwpError ConfigureStep(JDWP::ObjectId thread_id, JDWP::JdwpStepSize size,
                                        JDWP::JdwpStepDepth depth)
@@ -734,6 +747,9 @@
   static bool IsForcedInterpreterNeededForUpcallImpl(Thread* thread, ArtMethod* m)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
+  static bool IsForcedInterpreterNeededForExceptionImpl(Thread* thread)
+      SHARED_REQUIRES(Locks::mutator_lock_);
+
   // Indicates whether the debugger is making requests.
   static bool gDebuggerActive;
 
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index a2af9d0..ae62e2b 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -1228,7 +1228,9 @@
   AnnotationValue annotation_value;
   StackHandleScope<2> hs(Thread::Current());
   Handle<mirror::Class> h_klass(hs.NewHandle(klass));
-  Handle<mirror::Class> return_type(hs.NewHandle(method->GetReturnType()));
+  size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
+  Handle<mirror::Class> return_type(hs.NewHandle(
+      method->GetReturnType(true /* resolve */, pointer_size)));
   if (!ProcessAnnotationValue(h_klass, &annotation, &annotation_value, return_type, kAllObjects)) {
     return nullptr;
   }
@@ -1491,7 +1493,9 @@
   if (annotation_method == nullptr) {
     return nullptr;
   }
-  Handle<mirror::Class> method_return(hs.NewHandle(annotation_method->GetReturnType()));
+  size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
+  Handle<mirror::Class> method_return(hs.NewHandle(
+      annotation_method->GetReturnType(true /* resolve */, pointer_size)));
 
   AnnotationValue annotation_value;
   if (!ProcessAnnotationValue(klass, annotation, &annotation_value, method_return, kAllObjects)) {
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index 98d4e59..47e5c12 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -1275,6 +1275,8 @@
   // pointer to the OatDexFile it was loaded from. Otherwise oat_dex_file_ is
   // null.
   const OatDexFile* oat_dex_file_;
+
+  friend class DexFileVerifierTest;
 };
 
 struct DexFileReference {
@@ -1459,6 +1461,9 @@
   uint32_t GetMethodCodeItemOffset() const {
     return method_.code_off_;
   }
+  const uint8_t* DataPointer() const {
+    return ptr_pos_;
+  }
   const uint8_t* EndDataPointer() const {
     CHECK(!HasNext());
     return ptr_pos_;
diff --git a/runtime/dex_file_verifier.cc b/runtime/dex_file_verifier.cc
index eec4983..09416cc 100644
--- a/runtime/dex_file_verifier.cc
+++ b/runtime/dex_file_verifier.cc
@@ -16,7 +16,9 @@
 
 #include "dex_file_verifier.h"
 
+#include <inttypes.h>
 #include <zlib.h>
+
 #include <memory>
 
 #include "base/stringprintf.h"
@@ -444,66 +446,86 @@
   return true;
 }
 
-bool DexFileVerifier::CheckClassDataItemField(uint32_t idx, uint32_t access_flags,
+bool DexFileVerifier::CheckClassDataItemField(uint32_t idx,
+                                              uint32_t access_flags,
+                                              uint32_t class_access_flags,
+                                              uint16_t class_type_index,
                                               bool expect_static) {
+  // Check for overflow.
   if (!CheckIndex(idx, header_->field_ids_size_, "class_data_item field_idx")) {
     return false;
   }
 
+  // Check that it's the right class.
+  uint16_t my_class_index =
+      (reinterpret_cast<const DexFile::FieldId*>(begin_ + header_->field_ids_off_) + idx)->
+          class_idx_;
+  if (class_type_index != my_class_index) {
+    ErrorStringPrintf("Field's class index unexpected, %" PRIu16 "vs %" PRIu16,
+                      my_class_index,
+                      class_type_index);
+    return false;
+  }
+
+  // Check that it falls into the right class-data list.
   bool is_static = (access_flags & kAccStatic) != 0;
   if (UNLIKELY(is_static != expect_static)) {
     ErrorStringPrintf("Static/instance field not in expected list");
     return false;
   }
 
-  if (UNLIKELY((access_flags & ~kAccJavaFlagsMask) != 0)) {
-    ErrorStringPrintf("Bad class_data_item field access_flags %x", access_flags);
+  // Check field access flags.
+  std::string error_msg;
+  if (!CheckFieldAccessFlags(access_flags, class_access_flags, &error_msg)) {
+    ErrorStringPrintf("%s", error_msg.c_str());
     return false;
   }
 
   return true;
 }
 
-bool DexFileVerifier::CheckClassDataItemMethod(uint32_t idx, uint32_t access_flags,
+bool DexFileVerifier::CheckClassDataItemMethod(uint32_t idx,
+                                               uint32_t access_flags,
+                                               uint32_t class_access_flags,
+                                               uint16_t class_type_index,
                                                uint32_t code_offset,
-                                               std::unordered_set<uint32_t>& direct_method_indexes,
+                                               std::unordered_set<uint32_t>* direct_method_indexes,
                                                bool expect_direct) {
+  DCHECK(direct_method_indexes != nullptr);
+  // Check for overflow.
   if (!CheckIndex(idx, header_->method_ids_size_, "class_data_item method_idx")) {
     return false;
   }
 
-  bool is_direct = (access_flags & (kAccStatic | kAccPrivate | kAccConstructor)) != 0;
-  bool expect_code = (access_flags & (kAccNative | kAccAbstract)) == 0;
-  bool is_synchronized = (access_flags & kAccSynchronized) != 0;
-  bool allow_synchronized = (access_flags & kAccNative) != 0;
-
-  if (UNLIKELY(is_direct != expect_direct)) {
-    ErrorStringPrintf("Direct/virtual method not in expected list");
+  // Check that it's the right class.
+  uint16_t my_class_index =
+      (reinterpret_cast<const DexFile::MethodId*>(begin_ + header_->method_ids_off_) + idx)->
+          class_idx_;
+  if (class_type_index != my_class_index) {
+    ErrorStringPrintf("Method's class index unexpected, %" PRIu16 "vs %" PRIu16,
+                      my_class_index,
+                      class_type_index);
     return false;
   }
 
+  // Check that it's not defined as both direct and virtual.
   if (expect_direct) {
-    direct_method_indexes.insert(idx);
-  } else if (direct_method_indexes.find(idx) != direct_method_indexes.end()) {
+    direct_method_indexes->insert(idx);
+  } else if (direct_method_indexes->find(idx) != direct_method_indexes->end()) {
     ErrorStringPrintf("Found virtual method with same index as direct method: %d", idx);
     return false;
   }
 
-  constexpr uint32_t access_method_mask = kAccJavaFlagsMask | kAccConstructor |
-      kAccDeclaredSynchronized;
-  if (UNLIKELY(((access_flags & ~access_method_mask) != 0) ||
-               (is_synchronized && !allow_synchronized))) {
-    ErrorStringPrintf("Bad class_data_item method access_flags %x", access_flags);
-    return false;
-  }
-
-  if (UNLIKELY(expect_code && (code_offset == 0))) {
-    ErrorStringPrintf("Unexpected zero value for class_data_item method code_off with access "
-                      "flags %x", access_flags);
-    return false;
-  } else if (UNLIKELY(!expect_code && (code_offset != 0))) {
-    ErrorStringPrintf("Unexpected non-zero value %x for class_data_item method code_off"
-                      " with access flags %x", code_offset, access_flags);
+  // Check method access flags.
+  bool has_code = (code_offset != 0);
+  std::string error_msg;
+  if (!CheckMethodAccessFlags(idx,
+                              access_flags,
+                              class_access_flags,
+                              has_code,
+                              expect_direct,
+                              &error_msg)) {
+    ErrorStringPrintf("%s", error_msg.c_str());
     return false;
   }
 
@@ -689,60 +711,185 @@
   return true;
 }
 
+bool DexFileVerifier::FindClassFlags(uint32_t index,
+                                     bool is_field,
+                                     uint16_t* class_type_index,
+                                     uint32_t* class_access_flags) {
+  DCHECK(class_type_index != nullptr);
+  DCHECK(class_access_flags != nullptr);
+
+  // First check if the index is valid.
+  if (index >= (is_field ? header_->field_ids_size_ : header_->method_ids_size_)) {
+    return false;
+  }
+
+  // Next get the type index.
+  if (is_field) {
+    *class_type_index =
+        (reinterpret_cast<const DexFile::FieldId*>(begin_ + header_->field_ids_off_) + index)->
+            class_idx_;
+  } else {
+    *class_type_index =
+        (reinterpret_cast<const DexFile::MethodId*>(begin_ + header_->method_ids_off_) + index)->
+            class_idx_;
+  }
+
+  // Check if that is valid.
+  if (*class_type_index >= header_->type_ids_size_) {
+    return false;
+  }
+
+  // Now search for the class def. This is basically a specialized version of the DexFile code, as
+  // we should not trust that this is a valid DexFile just yet.
+  const DexFile::ClassDef* class_def_begin =
+      reinterpret_cast<const DexFile::ClassDef*>(begin_ + header_->class_defs_off_);
+  for (size_t i = 0; i < header_->class_defs_size_; ++i) {
+    const DexFile::ClassDef* class_def = class_def_begin + i;
+    if (class_def->class_idx_ == *class_type_index) {
+      *class_access_flags = class_def->access_flags_;
+      return true;
+    }
+  }
+
+  // Didn't find the class-def, not defined here...
+  return false;
+}
+
+bool DexFileVerifier::CheckOrderAndGetClassFlags(bool is_field,
+                                                 const char* type_descr,
+                                                 uint32_t curr_index,
+                                                 uint32_t prev_index,
+                                                 bool* have_class,
+                                                 uint16_t* class_type_index,
+                                                 uint32_t* class_access_flags) {
+  if (curr_index < prev_index) {
+    ErrorStringPrintf("out-of-order %s indexes %" PRIu32 " and %" PRIu32,
+                      type_descr,
+                      prev_index,
+                      curr_index);
+    return false;
+  }
+
+  if (!*have_class) {
+    *have_class = FindClassFlags(curr_index, is_field, class_type_index, class_access_flags);
+    if (!*have_class) {
+      // Should have really found one.
+      ErrorStringPrintf("could not find declaring class for %s index %" PRIu32,
+                        type_descr,
+                        curr_index);
+      return false;
+    }
+  }
+  return true;
+}
+
+template <bool kStatic>
+bool DexFileVerifier::CheckIntraClassDataItemFields(ClassDataItemIterator* it,
+                                                    bool* have_class,
+                                                    uint16_t* class_type_index,
+                                                    uint32_t* class_access_flags) {
+  DCHECK(it != nullptr);
+  // These calls use the raw access flags to check whether the whole dex field is valid.
+  uint32_t prev_index = 0;
+  for (; kStatic ? it->HasNextStaticField() : it->HasNextInstanceField(); it->Next()) {
+    uint32_t curr_index = it->GetMemberIndex();
+    if (!CheckOrderAndGetClassFlags(true,
+                                    kStatic ? "static field" : "instance field",
+                                    curr_index,
+                                    prev_index,
+                                    have_class,
+                                    class_type_index,
+                                    class_access_flags)) {
+      return false;
+    }
+    prev_index = curr_index;
+
+    if (!CheckClassDataItemField(curr_index,
+                                 it->GetRawMemberAccessFlags(),
+                                 *class_access_flags,
+                                 *class_type_index,
+                                 kStatic)) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+template <bool kDirect>
+bool DexFileVerifier::CheckIntraClassDataItemMethods(
+    ClassDataItemIterator* it,
+    std::unordered_set<uint32_t>* direct_method_indexes,
+    bool* have_class,
+    uint16_t* class_type_index,
+    uint32_t* class_access_flags) {
+  uint32_t prev_index = 0;
+  for (; kDirect ? it->HasNextDirectMethod() : it->HasNextVirtualMethod(); it->Next()) {
+    uint32_t curr_index = it->GetMemberIndex();
+    if (!CheckOrderAndGetClassFlags(false,
+                                    kDirect ? "direct method" : "virtual method",
+                                    curr_index,
+                                    prev_index,
+                                    have_class,
+                                    class_type_index,
+                                    class_access_flags)) {
+      return false;
+    }
+    prev_index = curr_index;
+
+    if (!CheckClassDataItemMethod(curr_index,
+                                  it->GetRawMemberAccessFlags(),
+                                  *class_access_flags,
+                                  *class_type_index,
+                                  it->GetMethodCodeItemOffset(),
+                                  direct_method_indexes,
+                                  kDirect)) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
 bool DexFileVerifier::CheckIntraClassDataItem() {
   ClassDataItemIterator it(*dex_file_, ptr_);
   std::unordered_set<uint32_t> direct_method_indexes;
 
-  // These calls use the raw access flags to check whether the whole dex field is valid.
-  uint32_t prev_index = 0;
-  for (; it.HasNextStaticField(); it.Next()) {
-    uint32_t curr_index = it.GetMemberIndex();
-    if (curr_index < prev_index) {
-      ErrorStringPrintf("out-of-order static field indexes %d and %d", prev_index, curr_index);
-      return false;
-    }
-    prev_index = curr_index;
-    if (!CheckClassDataItemField(curr_index, it.GetRawMemberAccessFlags(), true)) {
-      return false;
-    }
+  // This code is complicated by the fact that we don't directly know which class this belongs to.
+  // So we need to explicitly search with the first item we find (either field or method), and then,
+  // as the lookup is expensive, cache the result.
+  bool have_class = false;
+  uint16_t class_type_index;
+  uint32_t class_access_flags;
+
+  // Check fields.
+  if (!CheckIntraClassDataItemFields<true>(&it,
+                                           &have_class,
+                                           &class_type_index,
+                                           &class_access_flags)) {
+    return false;
   }
-  prev_index = 0;
-  for (; it.HasNextInstanceField(); it.Next()) {
-    uint32_t curr_index = it.GetMemberIndex();
-    if (curr_index < prev_index) {
-      ErrorStringPrintf("out-of-order instance field indexes %d and %d", prev_index, curr_index);
-      return false;
-    }
-    prev_index = curr_index;
-    if (!CheckClassDataItemField(curr_index, it.GetRawMemberAccessFlags(), false)) {
-      return false;
-    }
+  if (!CheckIntraClassDataItemFields<false>(&it,
+                                            &have_class,
+                                            &class_type_index,
+                                            &class_access_flags)) {
+    return false;
   }
-  prev_index = 0;
-  for (; it.HasNextDirectMethod(); it.Next()) {
-    uint32_t curr_index = it.GetMemberIndex();
-    if (curr_index < prev_index) {
-      ErrorStringPrintf("out-of-order direct method indexes %d and %d", prev_index, curr_index);
-      return false;
-    }
-    prev_index = curr_index;
-    if (!CheckClassDataItemMethod(curr_index, it.GetRawMemberAccessFlags(),
-        it.GetMethodCodeItemOffset(), direct_method_indexes, true)) {
-      return false;
-    }
+
+  // Check methods.
+  if (!CheckIntraClassDataItemMethods<true>(&it,
+                                            &direct_method_indexes,
+                                            &have_class,
+                                            &class_type_index,
+                                            &class_access_flags)) {
+    return false;
   }
-  prev_index = 0;
-  for (; it.HasNextVirtualMethod(); it.Next()) {
-    uint32_t curr_index = it.GetMemberIndex();
-    if (curr_index < prev_index) {
-      ErrorStringPrintf("out-of-order virtual method indexes %d and %d", prev_index, curr_index);
-      return false;
-    }
-    prev_index = curr_index;
-    if (!CheckClassDataItemMethod(curr_index, it.GetRawMemberAccessFlags(),
-        it.GetMethodCodeItemOffset(), direct_method_indexes, false)) {
-      return false;
-    }
+  if (!CheckIntraClassDataItemMethods<false>(&it,
+                                             &direct_method_indexes,
+                                             &have_class,
+                                             &class_type_index,
+                                             &class_access_flags)) {
+    return false;
   }
 
   ptr_ = it.EndDataPointer();
@@ -2149,4 +2296,259 @@
   va_end(ap);
 }
 
+// Fields and methods may have only one of public/protected/private.
+static bool CheckAtMostOneOfPublicProtectedPrivate(uint32_t flags) {
+  size_t count = (((flags & kAccPublic) == 0) ? 0 : 1) +
+                 (((flags & kAccProtected) == 0) ? 0 : 1) +
+                 (((flags & kAccPrivate) == 0) ? 0 : 1);
+  return count <= 1;
+}
+
+bool DexFileVerifier::CheckFieldAccessFlags(uint32_t field_access_flags,
+                                            uint32_t class_access_flags,
+                                            std::string* error_msg) {
+  // Generally sort out >16-bit flags.
+  if ((field_access_flags & ~kAccJavaFlagsMask) != 0) {
+    *error_msg = StringPrintf("Bad class_data_item field access_flags %x", field_access_flags);
+    return false;
+  }
+
+  // Flags allowed on fields, in general. Other lower-16-bit flags are to be ignored.
+  constexpr uint32_t kFieldAccessFlags = kAccPublic |
+                                         kAccPrivate |
+                                         kAccProtected |
+                                         kAccStatic |
+                                         kAccFinal |
+                                         kAccVolatile |
+                                         kAccTransient |
+                                         kAccSynthetic |
+                                         kAccEnum;
+
+  // Fields may have only one of public/protected/final.
+  if (!CheckAtMostOneOfPublicProtectedPrivate(field_access_flags)) {
+    *error_msg = StringPrintf("Field may have only one of public/protected/private, %x",
+                              field_access_flags);
+    return false;
+  }
+
+  // Interfaces have a pretty restricted list.
+  if ((class_access_flags & kAccInterface) != 0) {
+    // Interface fields must be public final static.
+    constexpr uint32_t kPublicFinalStatic = kAccPublic | kAccFinal | kAccStatic;
+    if ((field_access_flags & kPublicFinalStatic) != kPublicFinalStatic) {
+      *error_msg = StringPrintf("Interface field is not public final static: %x",
+                                field_access_flags);
+      return false;
+    }
+    // Interface fields may be synthetic, but may not have other flags.
+    constexpr uint32_t kDisallowed = ~(kPublicFinalStatic | kAccSynthetic);
+    if ((field_access_flags & kFieldAccessFlags & kDisallowed) != 0) {
+      *error_msg = StringPrintf("Interface field has disallowed flag: %x", field_access_flags);
+      return false;
+    }
+    return true;
+  }
+
+  // Volatile fields may not be final.
+  constexpr uint32_t kVolatileFinal = kAccVolatile | kAccFinal;
+  if ((field_access_flags & kVolatileFinal) == kVolatileFinal) {
+    *error_msg = "Fields may not be volatile and final";
+    return false;
+  }
+
+  return true;
+}
+
+// Try to find the name of the method with the given index. We do not want to rely on DexFile
+// infrastructure at this point, so do it all by hand. begin and header correspond to begin_ and
+// header_ of the DexFileVerifier. str will contain the pointer to the method name on success
+// (flagged by the return value), otherwise error_msg will contain an error string.
+static bool FindMethodName(uint32_t method_index,
+                           const uint8_t* begin,
+                           const DexFile::Header* header,
+                           const char** str,
+                           std::string* error_msg) {
+  if (method_index >= header->method_ids_size_) {
+    *error_msg = "Method index not available for method flags verification";
+    return false;
+  }
+  uint32_t string_idx =
+      (reinterpret_cast<const DexFile::MethodId*>(begin + header->method_ids_off_) +
+          method_index)->name_idx_;
+  if (string_idx >= header->string_ids_size_) {
+    *error_msg = "String index not available for method flags verification";
+    return false;
+  }
+  uint32_t string_off =
+      (reinterpret_cast<const DexFile::StringId*>(begin + header->string_ids_off_) + string_idx)->
+          string_data_off_;
+  if (string_off >= header->file_size_) {
+    *error_msg = "String offset out of bounds for method flags verification";
+    return false;
+  }
+  const uint8_t* str_data_ptr = begin + string_off;
+  DecodeUnsignedLeb128(&str_data_ptr);
+  *str = reinterpret_cast<const char*>(str_data_ptr);
+  return true;
+}
+
+bool DexFileVerifier::CheckMethodAccessFlags(uint32_t method_index,
+                                             uint32_t method_access_flags,
+                                             uint32_t class_access_flags,
+                                             bool has_code,
+                                             bool expect_direct,
+                                             std::string* error_msg) {
+  // Generally sort out >16-bit flags, except dex knows Constructor and DeclaredSynchronized.
+  constexpr uint32_t kAllMethodFlags =
+      kAccJavaFlagsMask | kAccConstructor | kAccDeclaredSynchronized;
+  if ((method_access_flags & ~kAllMethodFlags) != 0) {
+    *error_msg = StringPrintf("Bad class_data_item method access_flags %x", method_access_flags);
+    return false;
+  }
+
+  // Flags allowed on fields, in general. Other lower-16-bit flags are to be ignored.
+  constexpr uint32_t kMethodAccessFlags = kAccPublic |
+                                          kAccPrivate |
+                                          kAccProtected |
+                                          kAccStatic |
+                                          kAccFinal |
+                                          kAccSynthetic |
+                                          kAccSynchronized |
+                                          kAccBridge |
+                                          kAccVarargs |
+                                          kAccNative |
+                                          kAccAbstract |
+                                          kAccStrict;
+
+  // Methods may have only one of public/protected/final.
+  if (!CheckAtMostOneOfPublicProtectedPrivate(method_access_flags)) {
+    *error_msg = StringPrintf("Method may have only one of public/protected/private, %x",
+                              method_access_flags);
+    return false;
+  }
+
+  // Try to find the name, to check for constructor properties.
+  const char* str;
+  if (!FindMethodName(method_index, begin_, header_, &str, error_msg)) {
+    return false;
+  }
+  bool is_init_by_name = false;
+  constexpr const char* kInitName = "<init>";
+  size_t str_offset = (reinterpret_cast<const uint8_t*>(str) - begin_);
+  if (header_->file_size_ - str_offset >= sizeof(kInitName)) {
+    is_init_by_name = strcmp(kInitName, str) == 0;
+  }
+  bool is_clinit_by_name = false;
+  constexpr const char* kClinitName = "<clinit>";
+  if (header_->file_size_ - str_offset >= sizeof(kClinitName)) {
+    is_clinit_by_name = strcmp(kClinitName, str) == 0;
+  }
+  bool is_constructor = is_init_by_name || is_clinit_by_name;
+
+  // Only methods named "<clinit>" or "<init>" may be marked constructor. Note: we cannot enforce
+  // the reverse for backwards compatibility reasons.
+  if (((method_access_flags & kAccConstructor) != 0) && !is_constructor) {
+    *error_msg = StringPrintf("Method %" PRIu32 " is marked constructor, but doesn't match name",
+                              method_index);
+    return false;
+  }
+  // Check that the static constructor (= static initializer) is named "<clinit>" and that the
+  // instance constructor is called "<init>".
+  if (is_constructor) {
+    bool is_static = (method_access_flags & kAccStatic) != 0;
+    if (is_static ^ is_clinit_by_name) {
+      *error_msg = StringPrintf("Constructor %" PRIu32 " is not flagged correctly wrt/ static.",
+                                method_index);
+      return false;
+    }
+  }
+  // Check that static and private methods, as well as constructors, are in the direct methods list,
+  // and other methods in the virtual methods list.
+  bool is_direct = (method_access_flags & (kAccStatic | kAccPrivate)) != 0 || is_constructor;
+  if (is_direct != expect_direct) {
+    *error_msg = StringPrintf("Direct/virtual method %" PRIu32 " not in expected list %d",
+                              method_index,
+                              expect_direct);
+    return false;
+  }
+
+
+  // From here on out it is easier to mask out the bits we're supposed to ignore.
+  method_access_flags &= kMethodAccessFlags;
+
+  // If there aren't any instructions, make sure that's expected.
+  if (!has_code) {
+    // Only native or abstract methods may not have code.
+    if ((method_access_flags & (kAccNative | kAccAbstract)) == 0) {
+      *error_msg = StringPrintf("Method %" PRIu32 " has no code, but is not marked native or "
+                                "abstract",
+                                method_index);
+      return false;
+    }
+    // Constructors must always have code.
+    if (is_constructor) {
+      *error_msg = StringPrintf("Constructor %u must not be abstract or native", method_index);
+      return false;
+    }
+    if ((method_access_flags & kAccAbstract) != 0) {
+      // Abstract methods are not allowed to have the following flags.
+      constexpr uint32_t kForbidden =
+          kAccPrivate | kAccStatic | kAccFinal | kAccNative | kAccStrict | kAccSynchronized;
+      if ((method_access_flags & kForbidden) != 0) {
+        *error_msg = StringPrintf("Abstract method %" PRIu32 " has disallowed access flags %x",
+                                  method_index,
+                                  method_access_flags);
+        return false;
+      }
+      // Abstract methods must be in an abstract class or interface.
+      if ((class_access_flags & (kAccInterface | kAccAbstract)) == 0) {
+        *error_msg = StringPrintf("Method %" PRIu32 " is abstract, but the declaring class "
+                                  "is neither abstract nor an interface", method_index);
+        return false;
+      }
+    }
+    // Interfaces are special.
+    if ((class_access_flags & kAccInterface) != 0) {
+      // Interface methods must be public and abstract.
+      if ((method_access_flags & (kAccPublic | kAccAbstract)) != (kAccPublic | kAccAbstract)) {
+        *error_msg = StringPrintf("Interface method %" PRIu32 " is not public and abstract",
+                                  method_index);
+        return false;
+      }
+      // At this point, we know the method is public and abstract. This means that all the checks
+      // for invalid combinations above applies. In addition, interface methods must not be
+      // protected. This is caught by the check for only-one-of-public-protected-private.
+    }
+    return true;
+  }
+
+  // When there's code, the method must not be native or abstract.
+  if ((method_access_flags & (kAccNative | kAccAbstract)) != 0) {
+    *error_msg = StringPrintf("Method %" PRIu32 " has code, but is marked native or abstract",
+                              method_index);
+    return false;
+  }
+
+  // Only the static initializer may have code in an interface.
+  if (((class_access_flags & kAccInterface) != 0) && !is_clinit_by_name) {
+    *error_msg = StringPrintf("Non-clinit interface method %" PRIu32 " should not have code",
+                              method_index);
+    return false;
+  }
+
+  // Instance constructors must not be synchronized and a few other flags.
+  if (is_init_by_name) {
+    static constexpr uint32_t kInitAllowed =
+        kAccPrivate | kAccProtected | kAccPublic | kAccStrict | kAccVarargs | kAccSynthetic;
+    if ((method_access_flags & ~kInitAllowed) != 0) {
+      *error_msg = StringPrintf("Constructor %" PRIu32 " flagged inappropriately %x",
+                                method_index,
+                                method_access_flags);
+      return false;
+    }
+  }
+
+  return true;
+}
+
 }  // namespace art
diff --git a/runtime/dex_file_verifier.h b/runtime/dex_file_verifier.h
index ccc40d4..4f15357 100644
--- a/runtime/dex_file_verifier.h
+++ b/runtime/dex_file_verifier.h
@@ -57,16 +57,48 @@
   uint32_t ReadUnsignedLittleEndian(uint32_t size);
   bool CheckAndGetHandlerOffsets(const DexFile::CodeItem* code_item,
                                  uint32_t* handler_offsets, uint32_t handlers_size);
-  bool CheckClassDataItemField(uint32_t idx, uint32_t access_flags, bool expect_static);
-  bool CheckClassDataItemMethod(uint32_t idx, uint32_t access_flags, uint32_t code_offset,
-                                std::unordered_set<uint32_t>& direct_method_indexes,
+  bool CheckClassDataItemField(uint32_t idx,
+                               uint32_t access_flags,
+                               uint32_t class_access_flags,
+                               uint16_t class_type_index,
+                               bool expect_static);
+  bool CheckClassDataItemMethod(uint32_t idx,
+                                uint32_t access_flags,
+                                uint32_t class_access_flags,
+                                uint16_t class_type_index,
+                                uint32_t code_offset,
+                                std::unordered_set<uint32_t>* direct_method_indexes,
                                 bool expect_direct);
+  bool CheckOrderAndGetClassFlags(bool is_field,
+                                  const char* type_descr,
+                                  uint32_t curr_index,
+                                  uint32_t prev_index,
+                                  bool* have_class,
+                                  uint16_t* class_type_index,
+                                  uint32_t* class_access_flags);
+
   bool CheckPadding(size_t offset, uint32_t aligned_offset);
   bool CheckEncodedValue();
   bool CheckEncodedArray();
   bool CheckEncodedAnnotation();
 
   bool CheckIntraClassDataItem();
+  // Check all fields of the given type from the given iterator. Load the class data from the first
+  // field, if necessary (and return it), or use the given values.
+  template <bool kStatic>
+  bool CheckIntraClassDataItemFields(ClassDataItemIterator* it,
+                                     bool* have_class,
+                                     uint16_t* class_type_index,
+                                     uint32_t* class_access_flags);
+  // Check all methods of the given type from the given iterator. Load the class data from the first
+  // method, if necessary (and return it), or use the given values.
+  template <bool kDirect>
+  bool CheckIntraClassDataItemMethods(ClassDataItemIterator* it,
+                                      std::unordered_set<uint32_t>* direct_method_indexes,
+                                      bool* have_class,
+                                      uint16_t* class_type_index,
+                                      uint32_t* class_access_flags);
+
   bool CheckIntraCodeItem();
   bool CheckIntraStringDataItem();
   bool CheckIntraDebugInfoItem();
@@ -112,6 +144,31 @@
   void ErrorStringPrintf(const char* fmt, ...)
       __attribute__((__format__(__printf__, 2, 3))) COLD_ATTR;
 
+  // Retrieve class index and class access flag from the given member. index is the member index,
+  // which is taken as either a field or a method index (as designated by is_field). The result,
+  // if the member and declaring class could be found, is stored in class_type_index and
+  // class_access_flags.
+  // This is an expensive lookup, as we have to find the class-def by type index, which is a
+  // linear search. The output values should thus be cached by the caller.
+  bool FindClassFlags(uint32_t index,
+                      bool is_field,
+                      uint16_t* class_type_index,
+                      uint32_t* class_access_flags);
+
+  // Check validity of the given access flags, interpreted for a field in the context of a class
+  // with the given second access flags.
+  static bool CheckFieldAccessFlags(uint32_t field_access_flags,
+                                    uint32_t class_access_flags,
+                                    std::string* error_msg);
+  // Check validity of the given method and access flags, in the context of a class with the given
+  // second access flags.
+  bool CheckMethodAccessFlags(uint32_t method_index,
+                              uint32_t method_access_flags,
+                              uint32_t class_access_flags,
+                              bool has_code,
+                              bool expect_direct,
+                              std::string* error_msg);
+
   const DexFile* const dex_file_;
   const uint8_t* const begin_;
   const size_t size_;
diff --git a/runtime/dex_file_verifier_test.cc b/runtime/dex_file_verifier_test.cc
index 9f1ffec..1b529c9 100644
--- a/runtime/dex_file_verifier_test.cc
+++ b/runtime/dex_file_verifier_test.cc
@@ -18,18 +18,20 @@
 
 #include "sys/mman.h"
 #include "zlib.h"
+#include <functional>
 #include <memory>
 
 #include "base/unix_file/fd_file.h"
+#include "base/bit_utils.h"
 #include "base/macros.h"
 #include "common_runtime_test.h"
+#include "dex_file-inl.h"
+#include "leb128.h"
 #include "scoped_thread_state_change.h"
 #include "thread-inl.h"
 
 namespace art {
 
-class DexFileVerifierTest : public CommonRuntimeTest {};
-
 static const uint8_t kBase64Map[256] = {
   255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
   255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
@@ -101,6 +103,64 @@
   return dst.release();
 }
 
+static void FixUpChecksum(uint8_t* dex_file) {
+  DexFile::Header* header = reinterpret_cast<DexFile::Header*>(dex_file);
+  uint32_t expected_size = header->file_size_;
+  uint32_t adler_checksum = adler32(0L, Z_NULL, 0);
+  const uint32_t non_sum = sizeof(DexFile::Header::magic_) + sizeof(DexFile::Header::checksum_);
+  const uint8_t* non_sum_ptr = dex_file + non_sum;
+  adler_checksum = adler32(adler_checksum, non_sum_ptr, expected_size - non_sum);
+  header->checksum_ = adler_checksum;
+}
+
+// Custom deleter. Necessary to clean up the memory we use (to be able to mutate).
+struct DexFileDeleter {
+  void operator()(DexFile* in) {
+    if (in != nullptr) {
+      delete in->Begin();
+      delete in;
+    }
+  }
+};
+
+using DexFileUniquePtr = std::unique_ptr<DexFile, DexFileDeleter>;
+
+class DexFileVerifierTest : public CommonRuntimeTest {
+ protected:
+  void VerifyModification(const char* dex_file_base64_content,
+                          const char* location,
+                          std::function<void(DexFile*)> f,
+                          const char* expected_error) {
+    DexFileUniquePtr dex_file(WrapAsDexFile(dex_file_base64_content));
+    f(dex_file.get());
+    FixUpChecksum(const_cast<uint8_t*>(dex_file->Begin()));
+
+    std::string error_msg;
+    bool success = DexFileVerifier::Verify(dex_file.get(),
+                                           dex_file->Begin(),
+                                           dex_file->Size(),
+                                           location,
+                                           &error_msg);
+    if (expected_error == nullptr) {
+      EXPECT_TRUE(success) << error_msg;
+    } else {
+      EXPECT_FALSE(success) << "Expected " << expected_error;
+      if (!success) {
+        EXPECT_NE(error_msg.find(expected_error), std::string::npos) << error_msg;
+      }
+    }
+  }
+
+ private:
+  static DexFile* WrapAsDexFile(const char* dex_file_content_in_base_64) {
+    // Decode base64.
+    size_t length;
+    uint8_t* dex_bytes = DecodeBase64(dex_file_content_in_base_64, &length);
+    CHECK(dex_bytes != nullptr);
+    return new DexFile(dex_bytes, length, "tmp", 0, nullptr, nullptr);
+  }
+};
+
 static std::unique_ptr<const DexFile> OpenDexFileBase64(const char* base64,
                                                         const char* location,
                                                         std::string* error_msg) {
@@ -133,7 +193,6 @@
   return dex_file;
 }
 
-
 // For reference.
 static const char kGoodTestDex[] =
     "ZGV4CjAzNQDrVbyVkxX1HljTznNf95AglkUAhQuFtmKkAgAAcAAAAHhWNBIAAAAAAAAAAAQCAAAN"
@@ -157,92 +216,1003 @@
   ASSERT_TRUE(raw.get() != nullptr) << error_msg;
 }
 
-static void FixUpChecksum(uint8_t* dex_file) {
-  DexFile::Header* header = reinterpret_cast<DexFile::Header*>(dex_file);
-  uint32_t expected_size = header->file_size_;
-  uint32_t adler_checksum = adler32(0L, Z_NULL, 0);
-  const uint32_t non_sum = sizeof(DexFile::Header::magic_) + sizeof(DexFile::Header::checksum_);
-  const uint8_t* non_sum_ptr = dex_file + non_sum;
-  adler_checksum = adler32(adler_checksum, non_sum_ptr, expected_size - non_sum);
-  header->checksum_ = adler_checksum;
-}
-
-static std::unique_ptr<const DexFile> FixChecksumAndOpen(uint8_t* bytes, size_t length,
-                                                         const char* location,
-                                                         std::string* error_msg) {
-  // Check data.
-  CHECK(bytes != nullptr);
-
-  // Fixup of checksum.
-  FixUpChecksum(bytes);
-
-  // write to provided file
-  std::unique_ptr<File> file(OS::CreateEmptyFile(location));
-  CHECK(file.get() != nullptr);
-  if (!file->WriteFully(bytes, length)) {
-    PLOG(FATAL) << "Failed to write base64 as dex file";
-  }
-  if (file->FlushCloseOrErase() != 0) {
-    PLOG(FATAL) << "Could not flush and close test file.";
-  }
-  file.reset();
-
-  // read dex file
-  ScopedObjectAccess soa(Thread::Current());
-  std::vector<std::unique_ptr<const DexFile>> tmp;
-  if (!DexFile::Open(location, location, error_msg, &tmp)) {
-    return nullptr;
-  }
-  EXPECT_EQ(1U, tmp.size());
-  std::unique_ptr<const DexFile> dex_file = std::move(tmp[0]);
-  EXPECT_EQ(PROT_READ, dex_file->GetPermissions());
-  EXPECT_TRUE(dex_file->IsReadOnly());
-  return dex_file;
-}
-
-static bool ModifyAndLoad(const char* dex_file_content, const char* location, size_t offset,
-                          uint8_t new_val, std::string* error_msg) {
-  // Decode base64.
-  size_t length;
-  std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(dex_file_content, &length));
-  CHECK(dex_bytes.get() != nullptr);
-
-  // Make modifications.
-  dex_bytes.get()[offset] = new_val;
-
-  // Fixup and load.
-  std::unique_ptr<const DexFile> file(FixChecksumAndOpen(dex_bytes.get(), length, location,
-                                                         error_msg));
-  return file.get() != nullptr;
-}
-
 TEST_F(DexFileVerifierTest, MethodId) {
-  {
-    // Class error.
-    ScratchFile tmp;
-    std::string error_msg;
-    bool success = !ModifyAndLoad(kGoodTestDex, tmp.GetFilename().c_str(), 220, 0xFFU, &error_msg);
-    ASSERT_TRUE(success);
-    ASSERT_NE(error_msg.find("inter_method_id_item class_idx"), std::string::npos) << error_msg;
+  // Class idx error.
+  VerifyModification(
+      kGoodTestDex,
+      "method_id_class_idx",
+      [](DexFile* dex_file) {
+        DexFile::MethodId* method_id = const_cast<DexFile::MethodId*>(&dex_file->GetMethodId(0));
+        method_id->class_idx_ = 0xFF;
+      },
+      "could not find declaring class for direct method index 0");
+
+  // Proto idx error.
+  VerifyModification(
+      kGoodTestDex,
+      "method_id_proto_idx",
+      [](DexFile* dex_file) {
+        DexFile::MethodId* method_id = const_cast<DexFile::MethodId*>(&dex_file->GetMethodId(0));
+        method_id->proto_idx_ = 0xFF;
+      },
+      "inter_method_id_item proto_idx");
+
+  // Name idx error.
+  VerifyModification(
+      kGoodTestDex,
+      "method_id_name_idx",
+      [](DexFile* dex_file) {
+        DexFile::MethodId* method_id = const_cast<DexFile::MethodId*>(&dex_file->GetMethodId(0));
+        method_id->name_idx_ = 0xFF;
+      },
+      "String index not available for method flags verification");
+}
+
+// Method flags test class generated from the following smali code. The declared-synchronized
+// flags are there to enforce a 3-byte uLEB128 encoding so we don't have to relayout
+// the code, but we need to remove them before doing tests.
+//
+// .class public LMethodFlags;
+// .super Ljava/lang/Object;
+//
+// .method public static constructor <clinit>()V
+// .registers 1
+//     return-void
+// .end method
+//
+// .method public constructor <init>()V
+// .registers 1
+//     return-void
+// .end method
+//
+// .method private declared-synchronized foo()V
+// .registers 1
+//     return-void
+// .end method
+//
+// .method public declared-synchronized bar()V
+// .registers 1
+//     return-void
+// .end method
+
+static const char kMethodFlagsTestDex[] =
+    "ZGV4CjAzNQCyOQrJaDBwiIWv5MIuYKXhxlLLsQcx5SwgAgAAcAAAAHhWNBIAAAAAAAAAAJgBAAAH"
+    "AAAAcAAAAAMAAACMAAAAAQAAAJgAAAAAAAAAAAAAAAQAAACkAAAAAQAAAMQAAAA8AQAA5AAAAOQA"
+    "AADuAAAA9gAAAAUBAAAZAQAAHAEAACEBAAACAAAAAwAAAAQAAAAEAAAAAgAAAAAAAAAAAAAAAAAA"
+    "AAAAAAABAAAAAAAAAAUAAAAAAAAABgAAAAAAAAABAAAAAQAAAAAAAAD/////AAAAAHoBAAAAAAAA"
+    "CDxjbGluaXQ+AAY8aW5pdD4ADUxNZXRob2RGbGFnczsAEkxqYXZhL2xhbmcvT2JqZWN0OwABVgAD"
+    "YmFyAANmb28AAAAAAAAAAQAAAAAAAAAAAAAAAQAAAA4AAAABAAEAAAAAAAAAAAABAAAADgAAAAEA"
+    "AQAAAAAAAAAAAAEAAAAOAAAAAQABAAAAAAAAAAAAAQAAAA4AAAADAQCJgASsAgGBgATAAgKCgAjU"
+    "AgKBgAjoAgAACwAAAAAAAAABAAAAAAAAAAEAAAAHAAAAcAAAAAIAAAADAAAAjAAAAAMAAAABAAAA"
+    "mAAAAAUAAAAEAAAApAAAAAYAAAABAAAAxAAAAAIgAAAHAAAA5AAAAAMQAAABAAAAKAEAAAEgAAAE"
+    "AAAALAEAAAAgAAABAAAAegEAAAAQAAABAAAAmAEAAA==";
+
+// Find the method data for the first method with the given name (from class 0). Note: the pointer
+// is to the access flags, so that the caller doesn't have to handle the leb128-encoded method-index
+// delta.
+static const uint8_t* FindMethodData(const DexFile* dex_file, const char* name) {
+  const DexFile::ClassDef& class_def = dex_file->GetClassDef(0);
+  const uint8_t* class_data = dex_file->GetClassData(class_def);
+
+  ClassDataItemIterator it(*dex_file, class_data);
+
+  const uint8_t* trailing = class_data;
+  // Need to manually decode the four entries. DataPointer() doesn't work for this, as the first
+  // element has already been loaded into the iterator.
+  DecodeUnsignedLeb128(&trailing);
+  DecodeUnsignedLeb128(&trailing);
+  DecodeUnsignedLeb128(&trailing);
+  DecodeUnsignedLeb128(&trailing);
+
+  // Skip all fields.
+  while (it.HasNextStaticField() || it.HasNextInstanceField()) {
+    trailing = it.DataPointer();
+    it.Next();
   }
 
-  {
-    // Proto error.
-    ScratchFile tmp;
-    std::string error_msg;
-    bool success = !ModifyAndLoad(kGoodTestDex, tmp.GetFilename().c_str(), 222, 0xFFU, &error_msg);
-    ASSERT_TRUE(success);
-    ASSERT_NE(error_msg.find("inter_method_id_item proto_idx"), std::string::npos) << error_msg;
+  while (it.HasNextDirectMethod() || it.HasNextVirtualMethod()) {
+    uint32_t method_index = it.GetMemberIndex();
+    uint32_t name_index = dex_file->GetMethodId(method_index).name_idx_;
+    const DexFile::StringId& string_id = dex_file->GetStringId(name_index);
+    const char* str = dex_file->GetStringData(string_id);
+    if (strcmp(name, str) == 0) {
+      DecodeUnsignedLeb128(&trailing);
+      return trailing;
+    }
+
+    trailing = it.DataPointer();
+    it.Next();
   }
 
-  {
-    // Name error.
-    ScratchFile tmp;
-    std::string error_msg;
-    bool success = !ModifyAndLoad(kGoodTestDex, tmp.GetFilename().c_str(), 224, 0xFFU, &error_msg);
-    ASSERT_TRUE(success);
-    ASSERT_NE(error_msg.find("inter_method_id_item name_idx"), std::string::npos) << error_msg;
+  return nullptr;
+}
+
+// Set the method flags to the given value.
+static void SetMethodFlags(DexFile* dex_file, const char* method, uint32_t mask) {
+  uint8_t* method_flags_ptr = const_cast<uint8_t*>(FindMethodData(dex_file, method));
+  CHECK(method_flags_ptr != nullptr) << method;
+
+    // Unroll this, as we only have three bytes, anyways.
+  uint8_t base1 = static_cast<uint8_t>(mask & 0x7F);
+  *(method_flags_ptr++) = (base1 | 0x80);
+  mask >>= 7;
+
+  uint8_t base2 = static_cast<uint8_t>(mask & 0x7F);
+  *(method_flags_ptr++) = (base2 | 0x80);
+  mask >>= 7;
+
+  uint8_t base3 = static_cast<uint8_t>(mask & 0x7F);
+  *method_flags_ptr = base3;
+}
+
+static uint32_t GetMethodFlags(DexFile* dex_file, const char* method) {
+  const uint8_t* method_flags_ptr = const_cast<uint8_t*>(FindMethodData(dex_file, method));
+  CHECK(method_flags_ptr != nullptr) << method;
+  return DecodeUnsignedLeb128(&method_flags_ptr);
+}
+
+// Apply the given mask to method flags.
+static void ApplyMaskToMethodFlags(DexFile* dex_file, const char* method, uint32_t mask) {
+  uint32_t value = GetMethodFlags(dex_file, method);
+  value &= mask;
+  SetMethodFlags(dex_file, method, value);
+}
+
+// Apply the given mask to method flags.
+static void OrMaskToMethodFlags(DexFile* dex_file, const char* method, uint32_t mask) {
+  uint32_t value = GetMethodFlags(dex_file, method);
+  value |= mask;
+  SetMethodFlags(dex_file, method, value);
+}
+
+// Set code_off to 0 for the method.
+static void RemoveCode(DexFile* dex_file, const char* method) {
+  const uint8_t* ptr = FindMethodData(dex_file, method);
+  // Next is flags, pass.
+  DecodeUnsignedLeb128(&ptr);
+
+  // Figure out how many bytes the code_off is.
+  const uint8_t* tmp = ptr;
+  DecodeUnsignedLeb128(&tmp);
+  size_t bytes = tmp - ptr;
+
+  uint8_t* mod = const_cast<uint8_t*>(ptr);
+  for (size_t i = 1; i < bytes; ++i) {
+    *(mod++) = 0x80;
   }
+  *mod = 0x00;
+}
+
+TEST_F(DexFileVerifierTest, MethodAccessFlagsBase) {
+  // Check that it's OK when the wrong declared-synchronized flag is removed from "foo."
+  VerifyModification(
+      kMethodFlagsTestDex,
+      "method_flags_ok",
+      [](DexFile* dex_file) {
+        ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+        ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+      },
+      nullptr);
+}
+
+TEST_F(DexFileVerifierTest, MethodAccessFlagsConstructors) {
+  // Make sure we still accept constructors without their flags.
+  VerifyModification(
+      kMethodFlagsTestDex,
+      "method_flags_missing_constructor_tag_ok",
+      [](DexFile* dex_file) {
+        ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+        ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+        ApplyMaskToMethodFlags(dex_file, "<init>", ~kAccConstructor);
+        ApplyMaskToMethodFlags(dex_file, "<clinit>", ~kAccConstructor);
+      },
+      nullptr);
+
+  constexpr const char* kConstructors[] = { "<clinit>", "<init>"};
+  for (size_t i = 0; i < 2; ++i) {
+    // Constructor with code marked native.
+    VerifyModification(
+        kMethodFlagsTestDex,
+        "method_flags_constructor_native",
+        [&](DexFile* dex_file) {
+          ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+          ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+          OrMaskToMethodFlags(dex_file, kConstructors[i], kAccNative);
+        },
+        "has code, but is marked native or abstract");
+    // Constructor with code marked abstract.
+    VerifyModification(
+        kMethodFlagsTestDex,
+        "method_flags_constructor_abstract",
+        [&](DexFile* dex_file) {
+          ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+          ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+          OrMaskToMethodFlags(dex_file, kConstructors[i], kAccAbstract);
+        },
+        "has code, but is marked native or abstract");
+    // Constructor as-is without code.
+    VerifyModification(
+        kMethodFlagsTestDex,
+        "method_flags_constructor_nocode",
+        [&](DexFile* dex_file) {
+          ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+          ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+          RemoveCode(dex_file, kConstructors[i]);
+        },
+        "has no code, but is not marked native or abstract");
+    // Constructor without code marked native.
+    VerifyModification(
+        kMethodFlagsTestDex,
+        "method_flags_constructor_native_nocode",
+        [&](DexFile* dex_file) {
+          ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+          ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+          OrMaskToMethodFlags(dex_file, kConstructors[i], kAccNative);
+          RemoveCode(dex_file, kConstructors[i]);
+        },
+        "must not be abstract or native");
+    // Constructor without code marked abstract.
+    VerifyModification(
+        kMethodFlagsTestDex,
+        "method_flags_constructor_abstract_nocode",
+        [&](DexFile* dex_file) {
+          ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+          ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+          OrMaskToMethodFlags(dex_file, kConstructors[i], kAccAbstract);
+          RemoveCode(dex_file, kConstructors[i]);
+        },
+        "must not be abstract or native");
+  }
+  // <init> may only have (modulo ignored):
+  // kAccPrivate | kAccProtected | kAccPublic | kAccStrict | kAccVarargs | kAccSynthetic
+  static constexpr uint32_t kInitAllowed[] = {
+      0,
+      kAccPrivate,
+      kAccProtected,
+      kAccPublic,
+      kAccStrict,
+      kAccVarargs,
+      kAccSynthetic
+  };
+  for (size_t i = 0; i < arraysize(kInitAllowed); ++i) {
+    VerifyModification(
+        kMethodFlagsTestDex,
+        "init_allowed_flags",
+        [&](DexFile* dex_file) {
+          ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+          ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+          ApplyMaskToMethodFlags(dex_file, "<init>", ~kAccPublic);
+          OrMaskToMethodFlags(dex_file, "<init>", kInitAllowed[i]);
+        },
+        nullptr);
+  }
+  // Only one of public-private-protected.
+  for (size_t i = 1; i < 8; ++i) {
+    if (POPCOUNT(i) < 2) {
+      continue;
+    }
+    // Technically the flags match, but just be defensive here.
+    uint32_t mask = ((i & 1) != 0 ? kAccPrivate : 0) |
+                    ((i & 2) != 0 ? kAccProtected : 0) |
+                    ((i & 4) != 0 ? kAccPublic : 0);
+    VerifyModification(
+        kMethodFlagsTestDex,
+        "init_one_of_ppp",
+        [&](DexFile* dex_file) {
+          ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+          ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+          ApplyMaskToMethodFlags(dex_file, "<init>", ~kAccPublic);
+          OrMaskToMethodFlags(dex_file, "<init>", mask);
+        },
+        "Method may have only one of public/protected/private");
+  }
+  // <init> doesn't allow
+  // kAccStatic | kAccFinal | kAccSynchronized | kAccBridge
+  // Need to handle static separately as it has its own error message.
+  VerifyModification(
+      kMethodFlagsTestDex,
+      "init_not_allowed_flags",
+      [&](DexFile* dex_file) {
+        ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+        ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+        ApplyMaskToMethodFlags(dex_file, "<init>", ~kAccPublic);
+        OrMaskToMethodFlags(dex_file, "<init>", kAccStatic);
+      },
+      "Constructor 1 is not flagged correctly wrt/ static");
+  static constexpr uint32_t kInitNotAllowed[] = {
+      kAccFinal,
+      kAccSynchronized,
+      kAccBridge
+  };
+  for (size_t i = 0; i < arraysize(kInitNotAllowed); ++i) {
+    VerifyModification(
+        kMethodFlagsTestDex,
+        "init_not_allowed_flags",
+        [&](DexFile* dex_file) {
+          ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+          ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+          ApplyMaskToMethodFlags(dex_file, "<init>", ~kAccPublic);
+          OrMaskToMethodFlags(dex_file, "<init>", kInitNotAllowed[i]);
+        },
+        "Constructor 1 flagged inappropriately");
+  }
+}
+
+TEST_F(DexFileVerifierTest, MethodAccessFlagsMethods) {
+  constexpr const char* kMethods[] = { "foo", "bar"};
+  for (size_t i = 0; i < arraysize(kMethods); ++i) {
+    // Make sure we reject non-constructors marked as constructors.
+    VerifyModification(
+        kMethodFlagsTestDex,
+        "method_flags_non_constructor",
+        [&](DexFile* dex_file) {
+          ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+          ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+          OrMaskToMethodFlags(dex_file, kMethods[i], kAccConstructor);
+        },
+        "is marked constructor, but doesn't match name");
+
+    VerifyModification(
+        kMethodFlagsTestDex,
+        "method_flags_native_with_code",
+        [&](DexFile* dex_file) {
+          ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+          ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+          OrMaskToMethodFlags(dex_file, kMethods[i], kAccNative);
+        },
+        "has code, but is marked native or abstract");
+
+    VerifyModification(
+        kMethodFlagsTestDex,
+        "method_flags_abstract_with_code",
+        [&](DexFile* dex_file) {
+          ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+          ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+          OrMaskToMethodFlags(dex_file, kMethods[i], kAccAbstract);
+        },
+        "has code, but is marked native or abstract");
+
+    VerifyModification(
+        kMethodFlagsTestDex,
+        "method_flags_non_abstract_native_no_code",
+        [&](DexFile* dex_file) {
+          ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+          ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+          RemoveCode(dex_file, kMethods[i]);
+        },
+        "has no code, but is not marked native or abstract");
+
+    // Abstract methods may not have the following flags.
+    constexpr uint32_t kAbstractDisallowed[] = {
+        kAccPrivate,
+        kAccStatic,
+        kAccFinal,
+        kAccNative,
+        kAccStrict,
+        kAccSynchronized,
+    };
+    for (size_t j = 0; j < arraysize(kAbstractDisallowed); ++j) {
+      VerifyModification(
+          kMethodFlagsTestDex,
+          "method_flags_abstract_and_disallowed_no_code",
+          [&](DexFile* dex_file) {
+            ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+            ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+            RemoveCode(dex_file, kMethods[i]);
+
+            // Can't check private and static with foo, as it's in the virtual list and gives a
+            // different error.
+            if (((GetMethodFlags(dex_file, kMethods[i]) & kAccPublic) != 0) &&
+                ((kAbstractDisallowed[j] & (kAccPrivate | kAccStatic)) != 0)) {
+              // Use another breaking flag.
+              OrMaskToMethodFlags(dex_file, kMethods[i], kAccAbstract | kAccFinal);
+            } else {
+              OrMaskToMethodFlags(dex_file, kMethods[i], kAccAbstract | kAbstractDisallowed[j]);
+            }
+          },
+          "has disallowed access flags");
+    }
+
+    // Only one of public-private-protected.
+    for (size_t j = 1; j < 8; ++j) {
+      if (POPCOUNT(j) < 2) {
+        continue;
+      }
+      // Technically the flags match, but just be defensive here.
+      uint32_t mask = ((j & 1) != 0 ? kAccPrivate : 0) |
+                      ((j & 2) != 0 ? kAccProtected : 0) |
+                      ((j & 4) != 0 ? kAccPublic : 0);
+      VerifyModification(
+          kMethodFlagsTestDex,
+          "method_flags_one_of_ppp",
+          [&](DexFile* dex_file) {
+            ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+            ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+            ApplyMaskToMethodFlags(dex_file, kMethods[i], ~kAccPublic);
+            OrMaskToMethodFlags(dex_file, kMethods[i], mask);
+          },
+          "Method may have only one of public/protected/private");
+    }
+  }
+}
+
+TEST_F(DexFileVerifierTest, MethodAccessFlagsIgnoredOK) {
+  constexpr const char* kMethods[] = { "<clinit>", "<init>", "foo", "bar"};
+  for (size_t i = 0; i < arraysize(kMethods); ++i) {
+    // All interesting method flags, other flags are to be ignored.
+    constexpr uint32_t kAllMethodFlags =
+        kAccPublic |
+        kAccPrivate |
+        kAccProtected |
+        kAccStatic |
+        kAccFinal |
+        kAccSynchronized |
+        kAccBridge |
+        kAccVarargs |
+        kAccNative |
+        kAccAbstract |
+        kAccStrict |
+        kAccSynthetic;
+    constexpr uint32_t kIgnoredMask = ~kAllMethodFlags & 0xFFFF;
+    VerifyModification(
+        kMethodFlagsTestDex,
+        "method_flags_ignored",
+        [&](DexFile* dex_file) {
+          ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+          ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+          OrMaskToMethodFlags(dex_file, kMethods[i], kIgnoredMask);
+        },
+        nullptr);
+  }
+}
+
+// Set of dex files for interface method tests. As it's not as easy to mutate method names, it's
+// just easier to break up bad cases.
+
+// Interface with an instance constructor.
+//
+// .class public interface LInterfaceMethodFlags;
+// .super Ljava/lang/Object;
+//
+// .method public static constructor <clinit>()V
+// .registers 1
+//     return-void
+// .end method
+//
+// .method public constructor <init>()V
+// .registers 1
+//     return-void
+// .end method
+static const char kMethodFlagsInterfaceWithInit[] =
+    "ZGV4CjAzNQDRNt+hZ6X3I+xe66iVlCW7h9I38HmN4SvUAQAAcAAAAHhWNBIAAAAAAAAAAEwBAAAF"
+    "AAAAcAAAAAMAAACEAAAAAQAAAJAAAAAAAAAAAAAAAAIAAACcAAAAAQAAAKwAAAAIAQAAzAAAAMwA"
+    "AADWAAAA3gAAAPYAAAAKAQAAAgAAAAMAAAAEAAAABAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAQAA"
+    "AAAAAAABAgAAAQAAAAAAAAD/////AAAAADoBAAAAAAAACDxjbGluaXQ+AAY8aW5pdD4AFkxJbnRl"
+    "cmZhY2VNZXRob2RGbGFnczsAEkxqYXZhL2xhbmcvT2JqZWN0OwABVgAAAAAAAAAAAQAAAAAAAAAA"
+    "AAAAAQAAAA4AAAABAAEAAAAAAAAAAAABAAAADgAAAAIAAImABJQCAYGABKgCAAALAAAAAAAAAAEA"
+    "AAAAAAAAAQAAAAUAAABwAAAAAgAAAAMAAACEAAAAAwAAAAEAAACQAAAABQAAAAIAAACcAAAABgAA"
+    "AAEAAACsAAAAAiAAAAUAAADMAAAAAxAAAAEAAAAQAQAAASAAAAIAAAAUAQAAACAAAAEAAAA6AQAA"
+    "ABAAAAEAAABMAQAA";
+
+// Standard interface. Use declared-synchronized again for 3B encoding.
+//
+// .class public interface LInterfaceMethodFlags;
+// .super Ljava/lang/Object;
+//
+// .method public static constructor <clinit>()V
+// .registers 1
+//     return-void
+// .end method
+//
+// .method public abstract declared-synchronized foo()V
+// .end method
+static const char kMethodFlagsInterface[] =
+    "ZGV4CjAzNQCOM0odZ5bws1d9GSmumXaK5iE/7XxFpOm8AQAAcAAAAHhWNBIAAAAAAAAAADQBAAAF"
+    "AAAAcAAAAAMAAACEAAAAAQAAAJAAAAAAAAAAAAAAAAIAAACcAAAAAQAAAKwAAADwAAAAzAAAAMwA"
+    "AADWAAAA7gAAAAIBAAAFAQAAAQAAAAIAAAADAAAAAwAAAAIAAAAAAAAAAAAAAAAAAAAAAAAABAAA"
+    "AAAAAAABAgAAAQAAAAAAAAD/////AAAAACIBAAAAAAAACDxjbGluaXQ+ABZMSW50ZXJmYWNlTWV0"
+    "aG9kRmxhZ3M7ABJMamF2YS9sYW5nL09iamVjdDsAAVYAA2ZvbwAAAAAAAAABAAAAAAAAAAAAAAAB"
+    "AAAADgAAAAEBAImABJACAYGICAAAAAALAAAAAAAAAAEAAAAAAAAAAQAAAAUAAABwAAAAAgAAAAMA"
+    "AACEAAAAAwAAAAEAAACQAAAABQAAAAIAAACcAAAABgAAAAEAAACsAAAAAiAAAAUAAADMAAAAAxAA"
+    "AAEAAAAMAQAAASAAAAEAAAAQAQAAACAAAAEAAAAiAQAAABAAAAEAAAA0AQAA";
+
+// To simplify generation of interesting "sub-states" of src_value, allow a "simple" mask to apply
+// to a src_value, such that mask bit 0 applies to the lowest set bit in src_value, and so on.
+static uint32_t ApplyMaskShifted(uint32_t src_value, uint32_t mask) {
+  uint32_t result = 0;
+  uint32_t mask_index = 0;
+  while (src_value != 0) {
+    uint32_t index = CTZ(src_value);
+    if (((src_value & (1 << index)) != 0) &&
+        ((mask & (1 << mask_index)) != 0)) {
+      result |= (1 << index);
+    }
+    src_value &= ~(1 << index);
+    mask_index++;
+  }
+  return result;
+}
+
+TEST_F(DexFileVerifierTest, MethodAccessFlagsInterfaces) {
+  // Reject interface with <init>.
+  VerifyModification(
+      kMethodFlagsInterfaceWithInit,
+      "method_flags_interface_with_init",
+      [](DexFile* dex_file ATTRIBUTE_UNUSED) {},
+      "Non-clinit interface method 1 should not have code");
+
+  VerifyModification(
+      kMethodFlagsInterface,
+      "method_flags_interface_ok",
+      [](DexFile* dex_file) {
+        ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+      },
+      nullptr);
+
+  VerifyModification(
+      kMethodFlagsInterface,
+      "method_flags_interface_non_public",
+      [](DexFile* dex_file) {
+        ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+
+        ApplyMaskToMethodFlags(dex_file, "foo", ~kAccPublic);
+      },
+      "Interface method 1 is not public and abstract");
+  VerifyModification(
+      kMethodFlagsInterface,
+      "method_flags_interface_non_abstract",
+      [](DexFile* dex_file) {
+        ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+
+        ApplyMaskToMethodFlags(dex_file, "foo", ~kAccAbstract);
+      },
+      "Method 1 has no code, but is not marked native or abstract");
+
+  VerifyModification(
+      kMethodFlagsInterface,
+      "method_flags_interface_static",
+      [](DexFile* dex_file) {
+        ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+
+        OrMaskToMethodFlags(dex_file, "foo", kAccStatic);
+      },
+      "Direct/virtual method 1 not in expected list 0");
+  VerifyModification(
+      kMethodFlagsInterface,
+      "method_flags_interface_private",
+      [](DexFile* dex_file) {
+        ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+
+        ApplyMaskToMethodFlags(dex_file, "foo", ~kAccPublic);
+        OrMaskToMethodFlags(dex_file, "foo", kAccPrivate);
+      },
+      "Direct/virtual method 1 not in expected list 0");
+
+  VerifyModification(
+      kMethodFlagsInterface,
+      "method_flags_interface_non_public",
+      [](DexFile* dex_file) {
+        ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+
+        ApplyMaskToMethodFlags(dex_file, "foo", ~kAccPublic);
+      },
+      "Interface method 1 is not public and abstract");
+  VerifyModification(
+      kMethodFlagsInterface,
+      "method_flags_interface_protected",
+      [](DexFile* dex_file) {
+        ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+
+        ApplyMaskToMethodFlags(dex_file, "foo", ~kAccPublic);
+        OrMaskToMethodFlags(dex_file, "foo", kAccProtected);
+      },
+      "Interface method 1 is not public and abstract");
+
+  constexpr uint32_t kAllMethodFlags =
+      kAccPublic |
+      kAccPrivate |
+      kAccProtected |
+      kAccStatic |
+      kAccFinal |
+      kAccSynchronized |
+      kAccBridge |
+      kAccVarargs |
+      kAccNative |
+      kAccAbstract |
+      kAccStrict |
+      kAccSynthetic;
+  constexpr uint32_t kInterfaceMethodFlags =
+      kAccPublic | kAccAbstract | kAccVarargs | kAccBridge | kAccSynthetic;
+  constexpr uint32_t kInterfaceDisallowed = kAllMethodFlags &
+                                            ~kInterfaceMethodFlags &
+                                            // Already tested, needed to be separate.
+                                            ~kAccStatic &
+                                            ~kAccPrivate &
+                                            ~kAccProtected;
+  static_assert(kInterfaceDisallowed != 0, "There should be disallowed flags.");
+
+  uint32_t bits = POPCOUNT(kInterfaceDisallowed);
+  for (uint32_t i = 1; i < (1u << bits); ++i) {
+    VerifyModification(
+        kMethodFlagsInterface,
+        "method_flags_interface_non_abstract",
+        [&](DexFile* dex_file) {
+          ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+
+          uint32_t mask = ApplyMaskShifted(kInterfaceDisallowed, i);
+          if ((mask & kAccProtected) != 0) {
+            mask &= ~kAccProtected;
+            ApplyMaskToMethodFlags(dex_file, "foo", ~kAccPublic);
+          }
+          OrMaskToMethodFlags(dex_file, "foo", mask);
+        },
+        "Abstract method 1 has disallowed access flags");
+  }
+}
+
+///////////////////////////////////////////////////////////////////
+
+// Field flags.
+
+// Find the method data for the first method with the given name (from class 0). Note: the pointer
+// is to the access flags, so that the caller doesn't have to handle the leb128-encoded method-index
+// delta.
+static const uint8_t* FindFieldData(const DexFile* dex_file, const char* name) {
+  const DexFile::ClassDef& class_def = dex_file->GetClassDef(0);
+  const uint8_t* class_data = dex_file->GetClassData(class_def);
+
+  ClassDataItemIterator it(*dex_file, class_data);
+
+  const uint8_t* trailing = class_data;
+  // Need to manually decode the four entries. DataPointer() doesn't work for this, as the first
+  // element has already been loaded into the iterator.
+  DecodeUnsignedLeb128(&trailing);
+  DecodeUnsignedLeb128(&trailing);
+  DecodeUnsignedLeb128(&trailing);
+  DecodeUnsignedLeb128(&trailing);
+
+  while (it.HasNextStaticField() || it.HasNextInstanceField()) {
+    uint32_t field_index = it.GetMemberIndex();
+    uint32_t name_index = dex_file->GetFieldId(field_index).name_idx_;
+    const DexFile::StringId& string_id = dex_file->GetStringId(name_index);
+    const char* str = dex_file->GetStringData(string_id);
+    if (strcmp(name, str) == 0) {
+      DecodeUnsignedLeb128(&trailing);
+      return trailing;
+    }
+
+    trailing = it.DataPointer();
+    it.Next();
+  }
+
+  return nullptr;
+}
+
+// Set the method flags to the given value.
+static void SetFieldFlags(DexFile* dex_file, const char* field, uint32_t mask) {
+  uint8_t* field_flags_ptr = const_cast<uint8_t*>(FindFieldData(dex_file, field));
+  CHECK(field_flags_ptr != nullptr) << field;
+
+    // Unroll this, as we only have three bytes, anyways.
+  uint8_t base1 = static_cast<uint8_t>(mask & 0x7F);
+  *(field_flags_ptr++) = (base1 | 0x80);
+  mask >>= 7;
+
+  uint8_t base2 = static_cast<uint8_t>(mask & 0x7F);
+  *(field_flags_ptr++) = (base2 | 0x80);
+  mask >>= 7;
+
+  uint8_t base3 = static_cast<uint8_t>(mask & 0x7F);
+  *field_flags_ptr = base3;
+}
+
+static uint32_t GetFieldFlags(DexFile* dex_file, const char* field) {
+  const uint8_t* field_flags_ptr = const_cast<uint8_t*>(FindFieldData(dex_file, field));
+  CHECK(field_flags_ptr != nullptr) << field;
+  return DecodeUnsignedLeb128(&field_flags_ptr);
+}
+
+// Apply the given mask to method flags.
+static void ApplyMaskToFieldFlags(DexFile* dex_file, const char* field, uint32_t mask) {
+  uint32_t value = GetFieldFlags(dex_file, field);
+  value &= mask;
+  SetFieldFlags(dex_file, field, value);
+}
+
+// Apply the given mask to method flags.
+static void OrMaskToFieldFlags(DexFile* dex_file, const char* field, uint32_t mask) {
+  uint32_t value = GetFieldFlags(dex_file, field);
+  value |= mask;
+  SetFieldFlags(dex_file, field, value);
+}
+
+// Standard class. Use declared-synchronized again for 3B encoding.
+//
+// .class public LFieldFlags;
+// .super Ljava/lang/Object;
+//
+// .field declared-synchronized public foo:I
+//
+// .field declared-synchronized public static bar:I
+
+static const char kFieldFlagsTestDex[] =
+    "ZGV4CjAzNQBtLw7hydbfv4TdXidZyzAB70W7w3vnYJRwAQAAcAAAAHhWNBIAAAAAAAAAAAABAAAF"
+    "AAAAcAAAAAMAAACEAAAAAAAAAAAAAAACAAAAkAAAAAAAAAAAAAAAAQAAAKAAAACwAAAAwAAAAMAA"
+    "AADDAAAA0QAAAOUAAADqAAAAAAAAAAEAAAACAAAAAQAAAAMAAAABAAAABAAAAAEAAAABAAAAAgAA"
+    "AAAAAAD/////AAAAAPQAAAAAAAAAAUkADExGaWVsZEZsYWdzOwASTGphdmEvbGFuZy9PYmplY3Q7"
+    "AANiYXIAA2ZvbwAAAAAAAAEBAAAAiYAIAYGACAkAAAAAAAAAAQAAAAAAAAABAAAABQAAAHAAAAAC"
+    "AAAAAwAAAIQAAAAEAAAAAgAAAJAAAAAGAAAAAQAAAKAAAAACIAAABQAAAMAAAAADEAAAAQAAAPAA"
+    "AAAAIAAAAQAAAPQAAAAAEAAAAQAAAAABAAA=";
+
+TEST_F(DexFileVerifierTest, FieldAccessFlagsBase) {
+  // Check that it's OK when the wrong declared-synchronized flag is removed from "foo."
+  VerifyModification(
+      kFieldFlagsTestDex,
+      "field_flags_ok",
+      [](DexFile* dex_file) {
+        ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+        ApplyMaskToFieldFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+      },
+      nullptr);
+}
+
+TEST_F(DexFileVerifierTest, FieldAccessFlagsWrongList) {
+  // Mark the field so that it should appear in the opposite list (instance vs static).
+  VerifyModification(
+      kFieldFlagsTestDex,
+      "field_flags_wrong_list",
+      [](DexFile* dex_file) {
+        ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+        ApplyMaskToFieldFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+        OrMaskToFieldFlags(dex_file, "foo", kAccStatic);
+      },
+      "Static/instance field not in expected list");
+  VerifyModification(
+      kFieldFlagsTestDex,
+      "field_flags_wrong_list",
+      [](DexFile* dex_file) {
+        ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+        ApplyMaskToFieldFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+        ApplyMaskToFieldFlags(dex_file, "bar", ~kAccStatic);
+      },
+      "Static/instance field not in expected list");
+}
+
+TEST_F(DexFileVerifierTest, FieldAccessFlagsPPP) {
+  static const char* kFields[] = { "foo", "bar" };
+  for (size_t i = 0; i < arraysize(kFields); ++i) {
+    // Should be OK to remove public.
+    VerifyModification(
+        kFieldFlagsTestDex,
+        "field_flags_non_public",
+        [&](DexFile* dex_file) {
+          ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+          ApplyMaskToFieldFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+          ApplyMaskToFieldFlags(dex_file, kFields[i], ~kAccPublic);
+        },
+        nullptr);
+    constexpr uint32_t kAccFlags = kAccPublic | kAccPrivate | kAccProtected;
+    uint32_t bits = POPCOUNT(kAccFlags);
+    for (uint32_t j = 1; j < (1u << bits); ++j) {
+      if (POPCOUNT(j) < 2) {
+        continue;
+      }
+      VerifyModification(
+           kFieldFlagsTestDex,
+           "field_flags_ppp",
+           [&](DexFile* dex_file) {
+             ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+             ApplyMaskToFieldFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+             ApplyMaskToFieldFlags(dex_file, kFields[i], ~kAccPublic);
+             uint32_t mask = ApplyMaskShifted(kAccFlags, j);
+             OrMaskToFieldFlags(dex_file, kFields[i], mask);
+           },
+           "Field may have only one of public/protected/private");
+    }
+  }
+}
+
+TEST_F(DexFileVerifierTest, FieldAccessFlagsIgnoredOK) {
+  constexpr const char* kFields[] = { "foo", "bar"};
+  for (size_t i = 0; i < arraysize(kFields); ++i) {
+    // All interesting method flags, other flags are to be ignored.
+    constexpr uint32_t kAllFieldFlags =
+        kAccPublic |
+        kAccPrivate |
+        kAccProtected |
+        kAccStatic |
+        kAccFinal |
+        kAccVolatile |
+        kAccTransient |
+        kAccSynthetic |
+        kAccEnum;
+    constexpr uint32_t kIgnoredMask = ~kAllFieldFlags & 0xFFFF;
+    VerifyModification(
+        kFieldFlagsTestDex,
+        "field_flags_ignored",
+        [&](DexFile* dex_file) {
+          ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+          ApplyMaskToFieldFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+          OrMaskToFieldFlags(dex_file, kFields[i], kIgnoredMask);
+        },
+        nullptr);
+  }
+}
+
+TEST_F(DexFileVerifierTest, FieldAccessFlagsVolatileFinal) {
+  constexpr const char* kFields[] = { "foo", "bar"};
+  for (size_t i = 0; i < arraysize(kFields); ++i) {
+    VerifyModification(
+        kFieldFlagsTestDex,
+        "field_flags_final_and_volatile",
+        [&](DexFile* dex_file) {
+          ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+          ApplyMaskToFieldFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+          OrMaskToFieldFlags(dex_file, kFields[i], kAccVolatile | kAccFinal);
+        },
+        "Fields may not be volatile and final");
+  }
+}
+
+// Standard interface. Needs to be separate from class as interfaces do not allow instance fields.
+// Use declared-synchronized again for 3B encoding.
+//
+// .class public interface LInterfaceFieldFlags;
+// .super Ljava/lang/Object;
+//
+// .field declared-synchronized public static final foo:I
+
+static const char kFieldFlagsInterfaceTestDex[] =
+    "ZGV4CjAzNQCVMHfEimR1zZPk6hl6O9GPAYqkl3u0umFkAQAAcAAAAHhWNBIAAAAAAAAAAPQAAAAE"
+    "AAAAcAAAAAMAAACAAAAAAAAAAAAAAAABAAAAjAAAAAAAAAAAAAAAAQAAAJQAAACwAAAAtAAAALQA"
+    "AAC3AAAAzgAAAOIAAAAAAAAAAQAAAAIAAAABAAAAAwAAAAEAAAABAgAAAgAAAAAAAAD/////AAAA"
+    "AOwAAAAAAAAAAUkAFUxJbnRlcmZhY2VGaWVsZEZsYWdzOwASTGphdmEvbGFuZy9PYmplY3Q7AANm"
+    "b28AAAAAAAABAAAAAJmACAkAAAAAAAAAAQAAAAAAAAABAAAABAAAAHAAAAACAAAAAwAAAIAAAAAE"
+    "AAAAAQAAAIwAAAAGAAAAAQAAAJQAAAACIAAABAAAALQAAAADEAAAAQAAAOgAAAAAIAAAAQAAAOwA"
+    "AAAAEAAAAQAAAPQAAAA=";
+
+TEST_F(DexFileVerifierTest, FieldAccessFlagsInterface) {
+  VerifyModification(
+      kFieldFlagsInterfaceTestDex,
+      "field_flags_interface",
+      [](DexFile* dex_file) {
+        ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+      },
+      nullptr);
+
+  VerifyModification(
+      kFieldFlagsInterfaceTestDex,
+      "field_flags_interface_non_public",
+      [](DexFile* dex_file) {
+        ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+
+        ApplyMaskToFieldFlags(dex_file, "foo", ~kAccPublic);
+      },
+      "Interface field is not public final static");
+  VerifyModification(
+      kFieldFlagsInterfaceTestDex,
+      "field_flags_interface_non_final",
+      [](DexFile* dex_file) {
+        ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+
+        ApplyMaskToFieldFlags(dex_file, "foo", ~kAccFinal);
+      },
+      "Interface field is not public final static");
+  VerifyModification(
+      kFieldFlagsInterfaceTestDex,
+      "field_flags_interface_protected",
+      [](DexFile* dex_file) {
+        ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+
+        ApplyMaskToFieldFlags(dex_file, "foo", ~kAccPublic);
+        OrMaskToFieldFlags(dex_file, "foo", kAccProtected);
+      },
+      "Interface field is not public final static");
+  VerifyModification(
+      kFieldFlagsInterfaceTestDex,
+      "field_flags_interface_private",
+      [](DexFile* dex_file) {
+        ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+
+        ApplyMaskToFieldFlags(dex_file, "foo", ~kAccPublic);
+        OrMaskToFieldFlags(dex_file, "foo", kAccPrivate);
+      },
+      "Interface field is not public final static");
+
+  VerifyModification(
+      kFieldFlagsInterfaceTestDex,
+      "field_flags_interface_synthetic",
+      [](DexFile* dex_file) {
+        ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+
+        OrMaskToFieldFlags(dex_file, "foo", kAccSynthetic);
+      },
+      nullptr);
+
+  constexpr uint32_t kAllFieldFlags =
+      kAccPublic |
+      kAccPrivate |
+      kAccProtected |
+      kAccStatic |
+      kAccFinal |
+      kAccVolatile |
+      kAccTransient |
+      kAccSynthetic |
+      kAccEnum;
+  constexpr uint32_t kInterfaceFieldFlags = kAccPublic | kAccStatic | kAccFinal | kAccSynthetic;
+  constexpr uint32_t kInterfaceDisallowed = kAllFieldFlags &
+                                            ~kInterfaceFieldFlags &
+                                            ~kAccProtected &
+                                            ~kAccPrivate;
+  static_assert(kInterfaceDisallowed != 0, "There should be disallowed flags.");
+
+  uint32_t bits = POPCOUNT(kInterfaceDisallowed);
+  for (uint32_t i = 1; i < (1u << bits); ++i) {
+    VerifyModification(
+        kFieldFlagsInterfaceTestDex,
+        "field_flags_interface_disallowed",
+        [&](DexFile* dex_file) {
+          ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+
+          uint32_t mask = ApplyMaskShifted(kInterfaceDisallowed, i);
+          if ((mask & kAccProtected) != 0) {
+            mask &= ~kAccProtected;
+            ApplyMaskToFieldFlags(dex_file, "foo", ~kAccPublic);
+          }
+          OrMaskToFieldFlags(dex_file, "foo", mask);
+        },
+        "Interface field has disallowed flag");
+  }
+}
+
+// Standard bad interface. Needs to be separate from class as interfaces do not allow instance
+// fields. Use declared-synchronized again for 3B encoding.
+//
+// .class public interface LInterfaceFieldFlags;
+// .super Ljava/lang/Object;
+//
+// .field declared-synchronized public final foo:I
+
+static const char kFieldFlagsInterfaceBadTestDex[] =
+    "ZGV4CjAzNQByMUnqYKHBkUpvvNp+9CnZ2VyDkKnRN6VkAQAAcAAAAHhWNBIAAAAAAAAAAPQAAAAE"
+    "AAAAcAAAAAMAAACAAAAAAAAAAAAAAAABAAAAjAAAAAAAAAAAAAAAAQAAAJQAAACwAAAAtAAAALQA"
+    "AAC3AAAAzgAAAOIAAAAAAAAAAQAAAAIAAAABAAAAAwAAAAEAAAABAgAAAgAAAAAAAAD/////AAAA"
+    "AOwAAAAAAAAAAUkAFUxJbnRlcmZhY2VGaWVsZEZsYWdzOwASTGphdmEvbGFuZy9PYmplY3Q7AANm"
+    "b28AAAAAAAAAAQAAAJGACAkAAAAAAAAAAQAAAAAAAAABAAAABAAAAHAAAAACAAAAAwAAAIAAAAAE"
+    "AAAAAQAAAIwAAAAGAAAAAQAAAJQAAAACIAAABAAAALQAAAADEAAAAQAAAOgAAAAAIAAAAQAAAOwA"
+    "AAAAEAAAAQAAAPQAAAA=";
+
+TEST_F(DexFileVerifierTest, FieldAccessFlagsInterfaceNonStatic) {
+  VerifyModification(
+      kFieldFlagsInterfaceBadTestDex,
+      "field_flags_interface_non_static",
+      [](DexFile* dex_file) {
+        ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+      },
+      "Interface field is not public final static");
 }
 
 // Generated from:
@@ -305,15 +1275,14 @@
     ASSERT_TRUE(raw.get() != nullptr) << error_msg;
   }
 
-  {
-    // Modify the debug information entry.
-    ScratchFile tmp;
-    std::string error_msg;
-    bool success = !ModifyAndLoad(kDebugInfoTestDex, tmp.GetFilename().c_str(), 416, 0x14U,
-                                  &error_msg);
-    ASSERT_TRUE(success);
-    ASSERT_NE(error_msg.find("DBG_START_LOCAL type_idx"), std::string::npos) << error_msg;
-  }
+  // Modify the debug information entry.
+  VerifyModification(
+      kDebugInfoTestDex,
+      "debug_start_type_idx",
+      [](DexFile* dex_file) {
+        *(const_cast<uint8_t*>(dex_file->Begin()) + 416) = 0x14U;
+      },
+      "DBG_START_LOCAL type_idx");
 }
 
 }  // namespace art
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index 66e88ba..cc3eefe 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -120,9 +120,11 @@
 inline mirror::Class* CheckObjectAlloc(uint32_t type_idx,
                                        ArtMethod* method,
                                        Thread* self, bool* slow_path) {
-  mirror::Class* klass = method->GetDexCacheResolvedType<false>(type_idx);
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  size_t pointer_size = class_linker->GetImagePointerSize();
+  mirror::Class* klass = method->GetDexCacheResolvedType<false>(type_idx, pointer_size);
   if (UNLIKELY(klass == nullptr)) {
-    klass = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, method);
+    klass = class_linker->ResolveType(type_idx, method);
     *slow_path = true;
     if (klass == nullptr) {
       DCHECK(self->IsExceptionPending());
@@ -258,9 +260,11 @@
     *slow_path = true;
     return nullptr;  // Failure
   }
-  mirror::Class* klass = method->GetDexCacheResolvedType<false>(type_idx);
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  size_t pointer_size = class_linker->GetImagePointerSize();
+  mirror::Class* klass = method->GetDexCacheResolvedType<false>(type_idx, pointer_size);
   if (UNLIKELY(klass == nullptr)) {  // Not in dex cache so try to resolve
-    klass = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, method);
+    klass = class_linker->ResolveType(type_idx, method);
     *slow_path = true;
     if (klass == nullptr) {  // Error
       DCHECK(Thread::Current()->IsExceptionPending());
diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc
index eaf33f6..94aced2 100644
--- a/runtime/entrypoints/entrypoint_utils.cc
+++ b/runtime/entrypoints/entrypoint_utils.cc
@@ -43,9 +43,11 @@
     ThrowNegativeArraySizeException(component_count);
     return nullptr;  // Failure
   }
-  mirror::Class* klass = referrer->GetDexCacheResolvedType<false>(type_idx);
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  size_t pointer_size = class_linker->GetImagePointerSize();
+  mirror::Class* klass = referrer->GetDexCacheResolvedType<false>(type_idx, pointer_size);
   if (UNLIKELY(klass == nullptr)) {  // Not in dex cache so try to resolve
-    klass = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, referrer);
+    klass = class_linker->ResolveType(type_idx, referrer);
     if (klass == nullptr) {  // Error
       DCHECK(self->IsExceptionPending());
       return nullptr;  // Failure
@@ -214,7 +216,8 @@
     return;
   }
   // Make sure that the result is an instance of the type this method was expected to return.
-  mirror::Class* return_type = self->GetCurrentMethod(nullptr)->GetReturnType();
+  mirror::Class* return_type = self->GetCurrentMethod(nullptr)->GetReturnType(true /* resolve */,
+                                                                              sizeof(void*));
 
   if (!o->InstanceOf(return_type)) {
     Runtime::Current()->GetJavaVM()->JniAbortF(nullptr,
@@ -277,7 +280,9 @@
       StackHandleScope<1> hs(soa.Self());
       auto h_interface_method(hs.NewHandle(soa.Decode<mirror::Method*>(interface_method_jobj)));
       // This can cause thread suspension.
-      mirror::Class* result_type = h_interface_method->GetArtMethod()->GetReturnType();
+      size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
+      mirror::Class* result_type =
+          h_interface_method->GetArtMethod()->GetReturnType(true /* resolve */, pointer_size);
       mirror::Object* result_ref = soa.Decode<mirror::Object*>(result);
       JValue result_unboxed;
       if (!UnboxPrimitiveForResult(result_ref, result_type, &result_unboxed)) {
diff --git a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
index 9311791..28c62a8 100644
--- a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
@@ -33,7 +33,7 @@
     SHARED_REQUIRES(Locks::mutator_lock_) { \
   ScopedQuickEntrypointChecks sqec(self); \
   if (kUseTlabFastPath && !instrumented_bool && allocator_type == gc::kAllocatorTypeTLAB) { \
-    mirror::Class* klass = method->GetDexCacheResolvedType<false>(type_idx); \
+    mirror::Class* klass = method->GetDexCacheResolvedType<false>(type_idx, sizeof(void*)); \
     if (LIKELY(klass != nullptr && klass->IsInitialized() && !klass->IsFinalizable())) { \
       size_t byte_count = klass->GetObjectSize(); \
       byte_count = RoundUp(byte_count, gc::space::BumpPointerSpace::kAlignment); \
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index 0c7caf3..1302c5f 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -980,7 +980,7 @@
       // FindVirtualMethodFor... This is ok for FindDexMethodIndexInOtherDexFile that only cares
       // about the name and signature.
       uint32_t update_dex_cache_method_index = called->GetDexMethodIndex();
-      if (!called->HasSameDexCacheResolvedMethods(caller)) {
+      if (!called->HasSameDexCacheResolvedMethods(caller, sizeof(void*))) {
         // Calling from one dex file to another, need to compute the method index appropriate to
         // the caller's dex file. Since we get here only if the original called was a runtime
         // method, we've got the correct dex_file and a dex_method_idx from above.
diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc
index 7ddc7cc..089f453 100644
--- a/runtime/gc/collector/mark_sweep.cc
+++ b/runtime/gc/collector/mark_sweep.cc
@@ -421,12 +421,11 @@
           if (heap_bitmap_exclusive_locked) {
             Locks::heap_bitmap_lock_->ExclusiveUnlock(self);
           }
-          Locks::mutator_lock_->SharedUnlock(self);
-          ThreadList* tl = Runtime::Current()->GetThreadList();
-          tl->SuspendAll(__FUNCTION__);
-          mark_sweep_->VerifyRoots();
-          tl->ResumeAll();
-          Locks::mutator_lock_->SharedLock(self);
+          {
+            ScopedThreadSuspension(self, kSuspended);
+            ScopedSuspendAll ssa(__FUNCTION__);
+            mark_sweep_->VerifyRoots();
+          }
           if (heap_bitmap_exclusive_locked) {
             Locks::heap_bitmap_lock_->ExclusiveLock(self);
           }
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 9292c7a..cfe7713 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -639,10 +639,9 @@
     background_collector_type_ = foreground_collector_type_;
   }
   TransitionCollector(foreground_collector_type_);
-  ThreadList* tl = Runtime::Current()->GetThreadList();
-  Thread* self = Thread::Current();
+  Thread* const self = Thread::Current();
   ScopedThreadStateChange tsc(self, kSuspended);
-  tl->SuspendAll(__FUNCTION__);
+  ScopedSuspendAll ssa(__FUNCTION__);
   // Something may have caused the transition to fail.
   if (!IsMovingGc(collector_type_) && non_moving_space_ != main_space_) {
     CHECK(main_space_ != nullptr);
@@ -657,7 +656,6 @@
     non_moving_space_ = main_space_;
     CHECK(!non_moving_space_->CanMoveObjects());
   }
-  tl->ResumeAll();
 }
 
 std::string Heap::SafeGetClassDescriptor(mirror::Class* klass) {
@@ -889,11 +887,9 @@
     IncrementDisableMovingGC(self);
     {
       ScopedThreadSuspension sts(self, kWaitingForVisitObjects);
-      ThreadList* tl = Runtime::Current()->GetThreadList();
-      tl->SuspendAll(__FUNCTION__);
+      ScopedSuspendAll ssa(__FUNCTION__);
       VisitObjectsInternalRegionSpace(callback, arg);
       VisitObjectsInternal(callback, arg);
-      tl->ResumeAll();
     }
     DecrementDisableMovingGC(self);
   } else {
@@ -1267,12 +1263,13 @@
     // Deflate the monitors, this can cause a pause but shouldn't matter since we don't care
     // about pauses.
     Runtime* runtime = Runtime::Current();
-    runtime->GetThreadList()->SuspendAll(__FUNCTION__);
-    uint64_t start_time = NanoTime();
-    size_t count = runtime->GetMonitorList()->DeflateMonitors();
-    VLOG(heap) << "Deflating " << count << " monitors took "
-        << PrettyDuration(NanoTime() - start_time);
-    runtime->GetThreadList()->ResumeAll();
+    {
+      ScopedSuspendAll ssa(__FUNCTION__);
+      uint64_t start_time = NanoTime();
+      size_t count = runtime->GetMonitorList()->DeflateMonitors();
+      VLOG(heap) << "Deflating " << count << " monitors took "
+          << PrettyDuration(NanoTime() - start_time);
+    }
     ATRACE_END();
   }
   TrimIndirectReferenceTables(self);
@@ -1749,19 +1746,15 @@
 }
 
 size_t Heap::GetObjectsAllocated() const {
-  Thread* self = Thread::Current();
+  Thread* const self = Thread::Current();
   ScopedThreadStateChange tsc(self, kWaitingForGetObjectsAllocated);
-  auto* tl = Runtime::Current()->GetThreadList();
   // Need SuspendAll here to prevent lock violation if RosAlloc does it during InspectAll.
-  tl->SuspendAll(__FUNCTION__);
+  ScopedSuspendAll ssa(__FUNCTION__);
+  ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
   size_t total = 0;
-  {
-    ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
-    for (space::AllocSpace* space : alloc_spaces_) {
-      total += space->GetObjectsAllocated();
-    }
+  for (space::AllocSpace* space : alloc_spaces_) {
+    total += space->GetObjectsAllocated();
   }
-  tl->ResumeAll();
   return total;
 }
 
@@ -1911,7 +1904,6 @@
   // Inc requested homogeneous space compaction.
   count_requested_homogeneous_space_compaction_++;
   // Store performed homogeneous space compaction at a new request arrival.
-  ThreadList* tl = Runtime::Current()->GetThreadList();
   ScopedThreadStateChange tsc(self, kWaitingPerformingGc);
   Locks::mutator_lock_->AssertNotHeld(self);
   {
@@ -1938,34 +1930,34 @@
     FinishGC(self, collector::kGcTypeNone);
     return HomogeneousSpaceCompactResult::kErrorVMShuttingDown;
   }
-  // Suspend all threads.
-  tl->SuspendAll(__FUNCTION__);
-  uint64_t start_time = NanoTime();
-  // Launch compaction.
-  space::MallocSpace* to_space = main_space_backup_.release();
-  space::MallocSpace* from_space = main_space_;
-  to_space->GetMemMap()->Protect(PROT_READ | PROT_WRITE);
-  const uint64_t space_size_before_compaction = from_space->Size();
-  AddSpace(to_space);
-  // Make sure that we will have enough room to copy.
-  CHECK_GE(to_space->GetFootprintLimit(), from_space->GetFootprintLimit());
-  collector::GarbageCollector* collector = Compact(to_space, from_space,
-                                                   kGcCauseHomogeneousSpaceCompact);
-  const uint64_t space_size_after_compaction = to_space->Size();
-  main_space_ = to_space;
-  main_space_backup_.reset(from_space);
-  RemoveSpace(from_space);
-  SetSpaceAsDefault(main_space_);  // Set as default to reset the proper dlmalloc space.
-  // Update performed homogeneous space compaction count.
-  count_performed_homogeneous_space_compaction_++;
-  // Print statics log and resume all threads.
-  uint64_t duration = NanoTime() - start_time;
-  VLOG(heap) << "Heap homogeneous space compaction took " << PrettyDuration(duration) << " size: "
-             << PrettySize(space_size_before_compaction) << " -> "
-             << PrettySize(space_size_after_compaction) << " compact-ratio: "
-             << std::fixed << static_cast<double>(space_size_after_compaction) /
-             static_cast<double>(space_size_before_compaction);
-  tl->ResumeAll();
+  collector::GarbageCollector* collector;
+  {
+    ScopedSuspendAll ssa(__FUNCTION__);
+    uint64_t start_time = NanoTime();
+    // Launch compaction.
+    space::MallocSpace* to_space = main_space_backup_.release();
+    space::MallocSpace* from_space = main_space_;
+    to_space->GetMemMap()->Protect(PROT_READ | PROT_WRITE);
+    const uint64_t space_size_before_compaction = from_space->Size();
+    AddSpace(to_space);
+    // Make sure that we will have enough room to copy.
+    CHECK_GE(to_space->GetFootprintLimit(), from_space->GetFootprintLimit());
+    collector = Compact(to_space, from_space, kGcCauseHomogeneousSpaceCompact);
+    const uint64_t space_size_after_compaction = to_space->Size();
+    main_space_ = to_space;
+    main_space_backup_.reset(from_space);
+    RemoveSpace(from_space);
+    SetSpaceAsDefault(main_space_);  // Set as default to reset the proper dlmalloc space.
+    // Update performed homogeneous space compaction count.
+    count_performed_homogeneous_space_compaction_++;
+    // Print statics log and resume all threads.
+    uint64_t duration = NanoTime() - start_time;
+    VLOG(heap) << "Heap homogeneous space compaction took " << PrettyDuration(duration) << " size: "
+               << PrettySize(space_size_before_compaction) << " -> "
+               << PrettySize(space_size_after_compaction) << " compact-ratio: "
+               << std::fixed << static_cast<double>(space_size_after_compaction) /
+               static_cast<double>(space_size_before_compaction);
+  }
   // Finish GC.
   reference_processor_->EnqueueClearedReferences(self);
   GrowForUtilization(semi_space_collector_);
@@ -1983,7 +1975,6 @@
   uint64_t start_time = NanoTime();
   uint32_t before_allocated = num_bytes_allocated_.LoadSequentiallyConsistent();
   Runtime* const runtime = Runtime::Current();
-  ThreadList* const tl = runtime->GetThreadList();
   Thread* const self = Thread::Current();
   ScopedThreadStateChange tsc(self, kWaitingPerformingGc);
   Locks::mutator_lock_->AssertNotHeld(self);
@@ -2021,84 +2012,91 @@
     return;
   }
   collector::GarbageCollector* collector = nullptr;
-  tl->SuspendAll(__FUNCTION__);
-  switch (collector_type) {
-    case kCollectorTypeSS: {
-      if (!IsMovingGc(collector_type_)) {
-        // Create the bump pointer space from the backup space.
-        CHECK(main_space_backup_ != nullptr);
-        std::unique_ptr<MemMap> mem_map(main_space_backup_->ReleaseMemMap());
-        // We are transitioning from non moving GC -> moving GC, since we copied from the bump
-        // pointer space last transition it will be protected.
-        CHECK(mem_map != nullptr);
-        mem_map->Protect(PROT_READ | PROT_WRITE);
-        bump_pointer_space_ = space::BumpPointerSpace::CreateFromMemMap("Bump pointer space",
-                                                                        mem_map.release());
-        AddSpace(bump_pointer_space_);
-        collector = Compact(bump_pointer_space_, main_space_, kGcCauseCollectorTransition);
-        // Use the now empty main space mem map for the bump pointer temp space.
-        mem_map.reset(main_space_->ReleaseMemMap());
-        // Unset the pointers just in case.
-        if (dlmalloc_space_ == main_space_) {
-          dlmalloc_space_ = nullptr;
-        } else if (rosalloc_space_ == main_space_) {
-          rosalloc_space_ = nullptr;
-        }
-        // Remove the main space so that we don't try to trim it, this doens't work for debug
-        // builds since RosAlloc attempts to read the magic number from a protected page.
-        RemoveSpace(main_space_);
-        RemoveRememberedSet(main_space_);
-        delete main_space_;  // Delete the space since it has been removed.
-        main_space_ = nullptr;
-        RemoveRememberedSet(main_space_backup_.get());
-        main_space_backup_.reset(nullptr);  // Deletes the space.
-        temp_space_ = space::BumpPointerSpace::CreateFromMemMap("Bump pointer space 2",
-                                                                mem_map.release());
-        AddSpace(temp_space_);
-      }
-      break;
-    }
-    case kCollectorTypeMS:
-      // Fall through.
-    case kCollectorTypeCMS: {
-      if (IsMovingGc(collector_type_)) {
-        CHECK(temp_space_ != nullptr);
-        std::unique_ptr<MemMap> mem_map(temp_space_->ReleaseMemMap());
-        RemoveSpace(temp_space_);
-        temp_space_ = nullptr;
-        mem_map->Protect(PROT_READ | PROT_WRITE);
-        CreateMainMallocSpace(mem_map.get(), kDefaultInitialSize,
-                              std::min(mem_map->Size(), growth_limit_), mem_map->Size());
-        mem_map.release();
-        // Compact to the main space from the bump pointer space, don't need to swap semispaces.
-        AddSpace(main_space_);
-        collector = Compact(main_space_, bump_pointer_space_, kGcCauseCollectorTransition);
-        mem_map.reset(bump_pointer_space_->ReleaseMemMap());
-        RemoveSpace(bump_pointer_space_);
-        bump_pointer_space_ = nullptr;
-        const char* name = kUseRosAlloc ? kRosAllocSpaceName[1] : kDlMallocSpaceName[1];
-        // Temporarily unprotect the backup mem map so rosalloc can write the debug magic number.
-        if (kIsDebugBuild && kUseRosAlloc) {
+  {
+    ScopedSuspendAll ssa(__FUNCTION__);
+    switch (collector_type) {
+      case kCollectorTypeSS: {
+        if (!IsMovingGc(collector_type_)) {
+          // Create the bump pointer space from the backup space.
+          CHECK(main_space_backup_ != nullptr);
+          std::unique_ptr<MemMap> mem_map(main_space_backup_->ReleaseMemMap());
+          // We are transitioning from non moving GC -> moving GC, since we copied from the bump
+          // pointer space last transition it will be protected.
+          CHECK(mem_map != nullptr);
           mem_map->Protect(PROT_READ | PROT_WRITE);
+          bump_pointer_space_ = space::BumpPointerSpace::CreateFromMemMap("Bump pointer space",
+                                                                          mem_map.release());
+          AddSpace(bump_pointer_space_);
+          collector = Compact(bump_pointer_space_, main_space_, kGcCauseCollectorTransition);
+          // Use the now empty main space mem map for the bump pointer temp space.
+          mem_map.reset(main_space_->ReleaseMemMap());
+          // Unset the pointers just in case.
+          if (dlmalloc_space_ == main_space_) {
+            dlmalloc_space_ = nullptr;
+          } else if (rosalloc_space_ == main_space_) {
+            rosalloc_space_ = nullptr;
+          }
+          // Remove the main space so that we don't try to trim it, this doens't work for debug
+          // builds since RosAlloc attempts to read the magic number from a protected page.
+          RemoveSpace(main_space_);
+          RemoveRememberedSet(main_space_);
+          delete main_space_;  // Delete the space since it has been removed.
+          main_space_ = nullptr;
+          RemoveRememberedSet(main_space_backup_.get());
+          main_space_backup_.reset(nullptr);  // Deletes the space.
+          temp_space_ = space::BumpPointerSpace::CreateFromMemMap("Bump pointer space 2",
+                                                                  mem_map.release());
+          AddSpace(temp_space_);
         }
-        main_space_backup_.reset(CreateMallocSpaceFromMemMap(
-            mem_map.get(), kDefaultInitialSize, std::min(mem_map->Size(), growth_limit_),
-            mem_map->Size(), name, true));
-        if (kIsDebugBuild && kUseRosAlloc) {
-          mem_map->Protect(PROT_NONE);
-        }
-        mem_map.release();
+        break;
       }
-      break;
+      case kCollectorTypeMS:
+        // Fall through.
+      case kCollectorTypeCMS: {
+        if (IsMovingGc(collector_type_)) {
+          CHECK(temp_space_ != nullptr);
+          std::unique_ptr<MemMap> mem_map(temp_space_->ReleaseMemMap());
+          RemoveSpace(temp_space_);
+          temp_space_ = nullptr;
+          mem_map->Protect(PROT_READ | PROT_WRITE);
+          CreateMainMallocSpace(mem_map.get(),
+                                kDefaultInitialSize,
+                                std::min(mem_map->Size(), growth_limit_),
+                                mem_map->Size());
+          mem_map.release();
+          // Compact to the main space from the bump pointer space, don't need to swap semispaces.
+          AddSpace(main_space_);
+          collector = Compact(main_space_, bump_pointer_space_, kGcCauseCollectorTransition);
+          mem_map.reset(bump_pointer_space_->ReleaseMemMap());
+          RemoveSpace(bump_pointer_space_);
+          bump_pointer_space_ = nullptr;
+          const char* name = kUseRosAlloc ? kRosAllocSpaceName[1] : kDlMallocSpaceName[1];
+          // Temporarily unprotect the backup mem map so rosalloc can write the debug magic number.
+          if (kIsDebugBuild && kUseRosAlloc) {
+            mem_map->Protect(PROT_READ | PROT_WRITE);
+          }
+          main_space_backup_.reset(CreateMallocSpaceFromMemMap(
+              mem_map.get(),
+              kDefaultInitialSize,
+              std::min(mem_map->Size(), growth_limit_),
+              mem_map->Size(),
+              name,
+              true));
+          if (kIsDebugBuild && kUseRosAlloc) {
+            mem_map->Protect(PROT_NONE);
+          }
+          mem_map.release();
+        }
+        break;
+      }
+      default: {
+        LOG(FATAL) << "Attempted to transition to invalid collector type "
+                   << static_cast<size_t>(collector_type);
+        break;
+      }
     }
-    default: {
-      LOG(FATAL) << "Attempted to transition to invalid collector type "
-                 << static_cast<size_t>(collector_type);
-      break;
-    }
+    ChangeCollector(collector_type);
   }
-  ChangeCollector(collector_type);
-  tl->ResumeAll();
   // Can't call into java code with all threads suspended.
   reference_processor_->EnqueueClearedReferences(self);
   uint64_t duration = NanoTime() - start_time;
diff --git a/runtime/gc/space/rosalloc_space.cc b/runtime/gc/space/rosalloc_space.cc
index d8072ea..49126d2 100644
--- a/runtime/gc/space/rosalloc_space.cc
+++ b/runtime/gc/space/rosalloc_space.cc
@@ -303,17 +303,13 @@
     void* arg, bool do_null_callback_at_end) NO_THREAD_SAFETY_ANALYSIS {
   // TODO: NO_THREAD_SAFETY_ANALYSIS.
   Thread* self = Thread::Current();
-  ThreadList* tl = Runtime::Current()->GetThreadList();
-  tl->SuspendAll(__FUNCTION__);
-  {
-    MutexLock mu(self, *Locks::runtime_shutdown_lock_);
-    MutexLock mu2(self, *Locks::thread_list_lock_);
-    rosalloc_->InspectAll(callback, arg);
-    if (do_null_callback_at_end) {
-      callback(nullptr, nullptr, 0, arg);  // Indicate end of a space.
-    }
+  ScopedSuspendAll ssa(__FUNCTION__);
+  MutexLock mu(self, *Locks::runtime_shutdown_lock_);
+  MutexLock mu2(self, *Locks::thread_list_lock_);
+  rosalloc_->InspectAll(callback, arg);
+  if (do_null_callback_at_end) {
+    callback(nullptr, nullptr, 0, arg);  // Indicate end of a space.
   }
-  tl->ResumeAll();
 }
 
 void RosAllocSpace::InspectAllRosAlloc(void (*callback)(void *start, void *end, size_t num_bytes, void* callback_arg),
diff --git a/runtime/gc_root.h b/runtime/gc_root.h
index 83471e6..477e67b 100644
--- a/runtime/gc_root.h
+++ b/runtime/gc_root.h
@@ -90,16 +90,16 @@
   virtual ~RootVisitor() { }
 
   // Single root version, not overridable.
-  ALWAYS_INLINE void VisitRoot(mirror::Object** roots, const RootInfo& info)
+  ALWAYS_INLINE void VisitRoot(mirror::Object** root, const RootInfo& info)
       SHARED_REQUIRES(Locks::mutator_lock_) {
-    VisitRoots(&roots, 1, info);
+    VisitRoots(&root, 1, info);
   }
 
   // Single root version, not overridable.
-  ALWAYS_INLINE void VisitRootIfNonNull(mirror::Object** roots, const RootInfo& info)
+  ALWAYS_INLINE void VisitRootIfNonNull(mirror::Object** root, const RootInfo& info)
       SHARED_REQUIRES(Locks::mutator_lock_) {
-    if (*roots != nullptr) {
-      VisitRoot(roots, info);
+    if (*root != nullptr) {
+      VisitRoot(root, info);
     }
   }
 
diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc
index e2094dc..dfc1f5f 100644
--- a/runtime/hprof/hprof.cc
+++ b/runtime/hprof/hprof.cc
@@ -1403,10 +1403,11 @@
     // comment in Heap::VisitObjects().
     heap->IncrementDisableMovingGC(self);
   }
-  Runtime::Current()->GetThreadList()->SuspendAll(__FUNCTION__, true /* long suspend */);
-  Hprof hprof(filename, fd, direct_to_ddms);
-  hprof.Dump();
-  Runtime::Current()->GetThreadList()->ResumeAll();
+  {
+    ScopedSuspendAll ssa(__FUNCTION__, true /* long suspend */);
+    Hprof hprof(filename, fd, direct_to_ddms);
+    hprof.Dump();
+  }
   if (heap->IsGcConcurrentAndMoving()) {
     heap->DecrementDisableMovingGC(self);
   }
diff --git a/runtime/image.cc b/runtime/image.cc
index 8df17c6..42b348a 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -24,7 +24,7 @@
 namespace art {
 
 const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
-const uint8_t ImageHeader::kImageVersion[] = { '0', '2', '0', '\0' };
+const uint8_t ImageHeader::kImageVersion[] = { '0', '2', '1', '\0' };
 
 ImageHeader::ImageHeader(uint32_t image_begin,
                          uint32_t image_size,
diff --git a/runtime/image.h b/runtime/image.h
index e2d59f9..20e4159 100644
--- a/runtime/image.h
+++ b/runtime/image.h
@@ -168,6 +168,7 @@
     kSectionObjects,
     kSectionArtFields,
     kSectionArtMethods,
+    kSectionDexCacheArrays,
     kSectionInternedStrings,
     kSectionImageBitmap,
     kSectionCount,  // Number of elements in enum.
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index 63c02ed..7e2a84d 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -407,6 +407,10 @@
     backward_branch_listeners_.push_back(listener);
     have_backward_branch_listeners_ = true;
   }
+  if (HasEvent(kInvokeVirtualOrInterface, events)) {
+    invoke_virtual_or_interface_listeners_.push_back(listener);
+    have_invoke_virtual_or_interface_listeners_ = true;
+  }
   if (HasEvent(kDexPcMoved, events)) {
     std::list<InstrumentationListener*>* modified;
     if (have_dex_pc_listeners_) {
@@ -466,13 +470,17 @@
     have_method_exit_listeners_ = !method_exit_listeners_.empty();
   }
   if (HasEvent(kMethodUnwind, events) && have_method_unwind_listeners_) {
-      method_unwind_listeners_.remove(listener);
-      have_method_unwind_listeners_ = !method_unwind_listeners_.empty();
+    method_unwind_listeners_.remove(listener);
+    have_method_unwind_listeners_ = !method_unwind_listeners_.empty();
   }
   if (HasEvent(kBackwardBranch, events) && have_backward_branch_listeners_) {
-      backward_branch_listeners_.remove(listener);
-      have_backward_branch_listeners_ = !backward_branch_listeners_.empty();
-    }
+    backward_branch_listeners_.remove(listener);
+    have_backward_branch_listeners_ = !backward_branch_listeners_.empty();
+  }
+  if (HasEvent(kInvokeVirtualOrInterface, events) && have_invoke_virtual_or_interface_listeners_) {
+    invoke_virtual_or_interface_listeners_.remove(listener);
+    have_invoke_virtual_or_interface_listeners_ = !invoke_virtual_or_interface_listeners_.empty();
+  }
   if (HasEvent(kDexPcMoved, events) && have_dex_pc_listeners_) {
     std::list<InstrumentationListener*>* modified =
         new std::list<InstrumentationListener*>(*dex_pc_listeners_.get());
@@ -602,19 +610,17 @@
 void Instrumentation::SetEntrypointsInstrumented(bool instrumented) {
   Thread* self = Thread::Current();
   Runtime* runtime = Runtime::Current();
-  ThreadList* tl = runtime->GetThreadList();
   Locks::mutator_lock_->AssertNotHeld(self);
   Locks::instrument_entrypoints_lock_->AssertHeld(self);
   if (runtime->IsStarted()) {
-    tl->SuspendAll(__FUNCTION__);
-  }
-  {
+    ScopedSuspendAll ssa(__FUNCTION__);
     MutexLock mu(self, *Locks::runtime_shutdown_lock_);
     SetQuickAllocEntryPointsInstrumented(instrumented);
     ResetQuickAllocEntryPoints();
-  }
-  if (runtime->IsStarted()) {
-    tl->ResumeAll();
+  } else {
+    MutexLock mu(self, *Locks::runtime_shutdown_lock_);
+    SetQuickAllocEntryPointsInstrumented(instrumented);
+    ResetQuickAllocEntryPoints();
   }
 }
 
@@ -908,6 +914,16 @@
   }
 }
 
+void Instrumentation::InvokeVirtualOrInterfaceImpl(Thread* thread,
+                                                   mirror::Object* this_object,
+                                                   ArtMethod* caller,
+                                                   uint32_t dex_pc,
+                                                   ArtMethod* callee) const {
+  for (InstrumentationListener* listener : invoke_virtual_or_interface_listeners_) {
+    listener->InvokeVirtualOrInterface(thread, this_object, caller, dex_pc, callee);
+  }
+}
+
 void Instrumentation::FieldReadEventImpl(Thread* thread, mirror::Object* this_object,
                                          ArtMethod* method, uint32_t dex_pc,
                                          ArtField* field) const {
diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h
index 93ff567..6711ac3 100644
--- a/runtime/instrumentation.h
+++ b/runtime/instrumentation.h
@@ -97,6 +97,14 @@
   // Call-back for when we get a backward branch.
   virtual void BackwardBranch(Thread* thread, ArtMethod* method, int32_t dex_pc_offset)
       SHARED_REQUIRES(Locks::mutator_lock_) = 0;
+
+  // Call-back for when we get an invokevirtual or an invokeinterface.
+  virtual void InvokeVirtualOrInterface(Thread* thread,
+                                        mirror::Object* this_object,
+                                        ArtMethod* caller,
+                                        uint32_t dex_pc,
+                                        ArtMethod* callee)
+      SHARED_REQUIRES(Locks::mutator_lock_) = 0;
 };
 
 // Instrumentation is a catch-all for when extra information is required from the runtime. The
@@ -114,6 +122,7 @@
     kFieldWritten = 0x20,
     kExceptionCaught = 0x40,
     kBackwardBranch = 0x80,
+    kInvokeVirtualOrInterface = 0x100,
   };
 
   enum class InstrumentationLevel {
@@ -257,6 +266,10 @@
     return have_backward_branch_listeners_;
   }
 
+  bool HasInvokeVirtualOrInterfaceListeners() const SHARED_REQUIRES(Locks::mutator_lock_) {
+    return have_invoke_virtual_or_interface_listeners_;
+  }
+
   bool IsActive() const SHARED_REQUIRES(Locks::mutator_lock_) {
     return have_dex_pc_listeners_ || have_method_entry_listeners_ || have_method_exit_listeners_ ||
         have_field_read_listeners_ || have_field_write_listeners_ ||
@@ -325,6 +338,17 @@
     }
   }
 
+  void InvokeVirtualOrInterface(Thread* thread,
+                                mirror::Object* this_object,
+                                ArtMethod* caller,
+                                uint32_t dex_pc,
+                                ArtMethod* callee) const
+      SHARED_REQUIRES(Locks::mutator_lock_) {
+    if (UNLIKELY(HasInvokeVirtualOrInterfaceListeners())) {
+      InvokeVirtualOrInterfaceImpl(thread, this_object, caller, dex_pc, callee);
+    }
+  }
+
   // Inform listeners that an exception was caught.
   void ExceptionCaughtEvent(Thread* thread, mirror::Throwable* exception_object) const
       SHARED_REQUIRES(Locks::mutator_lock_);
@@ -385,6 +409,12 @@
       SHARED_REQUIRES(Locks::mutator_lock_);
   void BackwardBranchImpl(Thread* thread, ArtMethod* method, int32_t offset) const
       SHARED_REQUIRES(Locks::mutator_lock_);
+  void InvokeVirtualOrInterfaceImpl(Thread* thread,
+                                    mirror::Object* this_object,
+                                    ArtMethod* caller,
+                                    uint32_t dex_pc,
+                                    ArtMethod* callee) const
+      SHARED_REQUIRES(Locks::mutator_lock_);
   void FieldReadEventImpl(Thread* thread, mirror::Object* this_object,
                            ArtMethod* method, uint32_t dex_pc,
                            ArtField* field) const
@@ -451,6 +481,9 @@
   // Do we have any backward branch listeners? Short-cut to avoid taking the instrumentation_lock_.
   bool have_backward_branch_listeners_ GUARDED_BY(Locks::mutator_lock_);
 
+  // Do we have any invoke listeners? Short-cut to avoid taking the instrumentation_lock_.
+  bool have_invoke_virtual_or_interface_listeners_ GUARDED_BY(Locks::mutator_lock_);
+
   // Contains the instrumentation level required by each client of the instrumentation identified
   // by a string key.
   typedef SafeMap<const char*, InstrumentationLevel> InstrumentationLevelTable;
@@ -461,6 +494,8 @@
   std::list<InstrumentationListener*> method_exit_listeners_ GUARDED_BY(Locks::mutator_lock_);
   std::list<InstrumentationListener*> method_unwind_listeners_ GUARDED_BY(Locks::mutator_lock_);
   std::list<InstrumentationListener*> backward_branch_listeners_ GUARDED_BY(Locks::mutator_lock_);
+  std::list<InstrumentationListener*> invoke_virtual_or_interface_listeners_
+      GUARDED_BY(Locks::mutator_lock_);
   std::shared_ptr<std::list<InstrumentationListener*>> dex_pc_listeners_
       GUARDED_BY(Locks::mutator_lock_);
   std::shared_ptr<std::list<InstrumentationListener*>> field_read_listeners_
diff --git a/runtime/instrumentation_test.cc b/runtime/instrumentation_test.cc
index 56fe9ef..d98d246 100644
--- a/runtime/instrumentation_test.cc
+++ b/runtime/instrumentation_test.cc
@@ -36,7 +36,8 @@
     : received_method_enter_event(false), received_method_exit_event(false),
       received_method_unwind_event(false), received_dex_pc_moved_event(false),
       received_field_read_event(false), received_field_written_event(false),
-      received_exception_caught_event(false), received_backward_branch_event(false) {}
+      received_exception_caught_event(false), received_backward_branch_event(false),
+      received_invoke_virtual_or_interface_event(false) {}
 
   virtual ~TestInstrumentationListener() {}
 
@@ -105,6 +106,15 @@
     received_backward_branch_event = true;
   }
 
+  void InvokeVirtualOrInterface(Thread* thread ATTRIBUTE_UNUSED,
+                                mirror::Object* this_object ATTRIBUTE_UNUSED,
+                                ArtMethod* caller ATTRIBUTE_UNUSED,
+                                uint32_t dex_pc ATTRIBUTE_UNUSED,
+                                ArtMethod* callee ATTRIBUTE_UNUSED)
+      OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
+    received_invoke_virtual_or_interface_event = true;
+  }
+
   void Reset() {
     received_method_enter_event = false;
     received_method_exit_event = false;
@@ -114,6 +124,7 @@
     received_field_written_event = false;
     received_exception_caught_event = false;
     received_backward_branch_event = false;
+    received_invoke_virtual_or_interface_event = false;
   }
 
   bool received_method_enter_event;
@@ -124,6 +135,7 @@
   bool received_field_written_event;
   bool received_exception_caught_event;
   bool received_backward_branch_event;
+  bool received_invoke_virtual_or_interface_event;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(TestInstrumentationListener);
@@ -138,13 +150,9 @@
   void CheckConfigureStubs(const char* key, Instrumentation::InstrumentationLevel level) {
     ScopedObjectAccess soa(Thread::Current());
     instrumentation::Instrumentation* instr = Runtime::Current()->GetInstrumentation();
-    {
-      ScopedThreadSuspension sts(soa.Self(), kSuspended);
-      Runtime* runtime = Runtime::Current();
-      runtime->GetThreadList()->SuspendAll("Instrumentation::ConfigureStubs");
-      instr->ConfigureStubs(key, level);
-      runtime->GetThreadList()->ResumeAll();
-    }
+    ScopedThreadSuspension sts(soa.Self(), kSuspended);
+    ScopedSuspendAll ssa("Instrumentation::ConfigureStubs");
+    instr->ConfigureStubs(key, level);
   }
 
   Instrumentation::InstrumentationLevel GetCurrentInstrumentationLevel() {
@@ -162,10 +170,8 @@
     TestInstrumentationListener listener;
     {
       ScopedThreadSuspension sts(soa.Self(), kSuspended);
-      Runtime* runtime = Runtime::Current();
-      runtime->GetThreadList()->SuspendAll("Add instrumentation listener");
+      ScopedSuspendAll ssa("Add instrumentation listener");
       instr->AddListener(&listener, instrumentation_event);
-      runtime->GetThreadList()->ResumeAll();
     }
 
     ArtMethod* const event_method = nullptr;
@@ -181,10 +187,8 @@
     listener.Reset();
     {
       ScopedThreadSuspension sts(soa.Self(), kSuspended);
-      Runtime* runtime = Runtime::Current();
-      runtime->GetThreadList()->SuspendAll("Remove instrumentation listener");
+      ScopedSuspendAll ssa("Remove instrumentation listener");
       instr->RemoveListener(&listener, instrumentation_event);
-      runtime->GetThreadList()->ResumeAll();
     }
 
     // Check the listener is not registered and is not notified of the event.
@@ -199,12 +203,11 @@
     Runtime* runtime = Runtime::Current();
     instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation();
     ScopedThreadSuspension sts(self, kSuspended);
-    runtime->GetThreadList()->SuspendAll("Single method deoptimization");
+    ScopedSuspendAll ssa("Single method deoptimization");
     if (enable_deoptimization) {
       instrumentation->EnableDeoptimization();
     }
     instrumentation->Deoptimize(method);
-    runtime->GetThreadList()->ResumeAll();
   }
 
   void UndeoptimizeMethod(Thread* self, ArtMethod* method,
@@ -213,12 +216,11 @@
     Runtime* runtime = Runtime::Current();
     instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation();
     ScopedThreadSuspension sts(self, kSuspended);
-    runtime->GetThreadList()->SuspendAll("Single method undeoptimization");
+    ScopedSuspendAll ssa("Single method undeoptimization");
     instrumentation->Undeoptimize(method);
     if (disable_deoptimization) {
       instrumentation->DisableDeoptimization(key);
     }
-    runtime->GetThreadList()->ResumeAll();
   }
 
   void DeoptimizeEverything(Thread* self, const char* key, bool enable_deoptimization)
@@ -226,12 +228,11 @@
     Runtime* runtime = Runtime::Current();
     instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation();
     ScopedThreadSuspension sts(self, kSuspended);
-    runtime->GetThreadList()->SuspendAll("Full deoptimization");
+    ScopedSuspendAll ssa("Full deoptimization");
     if (enable_deoptimization) {
       instrumentation->EnableDeoptimization();
     }
     instrumentation->DeoptimizeEverything(key);
-    runtime->GetThreadList()->ResumeAll();
   }
 
   void UndeoptimizeEverything(Thread* self, const char* key, bool disable_deoptimization)
@@ -239,12 +240,11 @@
     Runtime* runtime = Runtime::Current();
     instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation();
     ScopedThreadSuspension sts(self, kSuspended);
-    runtime->GetThreadList()->SuspendAll("Full undeoptimization");
+    ScopedSuspendAll ssa("Full undeoptimization");
     instrumentation->UndeoptimizeEverything(key);
     if (disable_deoptimization) {
       instrumentation->DisableDeoptimization(key);
     }
-    runtime->GetThreadList()->ResumeAll();
   }
 
   void EnableMethodTracing(Thread* self, const char* key, bool needs_interpreter)
@@ -252,9 +252,8 @@
     Runtime* runtime = Runtime::Current();
     instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation();
     ScopedThreadSuspension sts(self, kSuspended);
-    runtime->GetThreadList()->SuspendAll("EnableMethodTracing");
+    ScopedSuspendAll ssa("EnableMethodTracing");
     instrumentation->EnableMethodTracing(key, needs_interpreter);
-    runtime->GetThreadList()->ResumeAll();
   }
 
   void DisableMethodTracing(Thread* self, const char* key)
@@ -262,9 +261,8 @@
     Runtime* runtime = Runtime::Current();
     instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation();
     ScopedThreadSuspension sts(self, kSuspended);
-    runtime->GetThreadList()->SuspendAll("EnableMethodTracing");
+    ScopedSuspendAll ssa("EnableMethodTracing");
     instrumentation->DisableMethodTracing(key);
-    runtime->GetThreadList()->ResumeAll();
   }
 
  private:
@@ -287,6 +285,8 @@
         return instr->HasExceptionCaughtListeners();
       case instrumentation::Instrumentation::kBackwardBranch:
         return instr->HasBackwardBranchListeners();
+      case instrumentation::Instrumentation::kInvokeVirtualOrInterface:
+        return instr->HasInvokeVirtualOrInterfaceListeners();
       default:
         LOG(FATAL) << "Unknown instrumentation event " << event_type;
         UNREACHABLE();
@@ -330,6 +330,9 @@
       case instrumentation::Instrumentation::kBackwardBranch:
         instr->BackwardBranch(self, method, dex_pc);
         break;
+      case instrumentation::Instrumentation::kInvokeVirtualOrInterface:
+        instr->InvokeVirtualOrInterface(self, obj, method, dex_pc, method);
+        break;
       default:
         LOG(FATAL) << "Unknown instrumentation event " << event_type;
         UNREACHABLE();
@@ -355,6 +358,8 @@
         return listener.received_exception_caught_event;
       case instrumentation::Instrumentation::kBackwardBranch:
         return listener.received_backward_branch_event;
+      case instrumentation::Instrumentation::kInvokeVirtualOrInterface:
+        return listener.received_invoke_virtual_or_interface_event;
       default:
         LOG(FATAL) << "Unknown instrumentation event " << event_type;
         UNREACHABLE();
@@ -418,6 +423,10 @@
   TestEvent(instrumentation::Instrumentation::kBackwardBranch);
 }
 
+TEST_F(InstrumentationTest, InvokeVirtualOrInterfaceEvent) {
+  TestEvent(instrumentation::Instrumentation::kInvokeVirtualOrInterface);
+}
+
 TEST_F(InstrumentationTest, DeoptimizeDirectMethod) {
   ScopedObjectAccess soa(Thread::Current());
   jobject class_loader = LoadDex("Instrumentation");
diff --git a/runtime/intern_table.cc b/runtime/intern_table.cc
index 3ff7017..179353e 100644
--- a/runtime/intern_table.cc
+++ b/runtime/intern_table.cc
@@ -22,7 +22,7 @@
 #include "gc/collector/garbage_collector.h"
 #include "gc/space/image_space.h"
 #include "gc/weak_root_state.h"
-#include "mirror/dex_cache.h"
+#include "mirror/dex_cache-inl.h"
 #include "mirror/object_array-inl.h"
 #include "mirror/object-inl.h"
 #include "mirror/string-inl.h"
@@ -165,8 +165,7 @@
       mirror::ObjectArray<mirror::DexCache>* dex_caches = root->AsObjectArray<mirror::DexCache>();
       for (int32_t i = 0; i < dex_caches->GetLength(); ++i) {
         mirror::DexCache* dex_cache = dex_caches->Get(i);
-        const DexFile* dex_file = dex_cache->GetDexFile();
-        const size_t num_strings = dex_file->NumStringIds();
+        const size_t num_strings = dex_cache->NumStrings();
         for (size_t j = 0; j < num_strings; ++j) {
           mirror::String* image_string = dex_cache->GetResolvedString(j);
           if (image_string != nullptr) {
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index 6c6232c..3ac80c6 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -399,14 +399,19 @@
   JValue value;
   // Set value to last known result in case the shadow frame chain is empty.
   value.SetJ(ret_val->GetJ());
+  // Are we executing the first shadow frame?
+  bool first = true;
   while (shadow_frame != nullptr) {
     self->SetTopOfShadowStack(shadow_frame);
     const DexFile::CodeItem* code_item = shadow_frame->GetMethod()->GetCodeItem();
     const uint32_t dex_pc = shadow_frame->GetDexPC();
     uint32_t new_dex_pc;
     if (UNLIKELY(self->IsExceptionPending())) {
+      // If we deoptimize from the QuickExceptionHandler, we already reported the exception to
+      // the instrumentation. To prevent from reporting it a second time, we simply pass a
+      // null Instrumentation*.
       const instrumentation::Instrumentation* const instrumentation =
-          Runtime::Current()->GetInstrumentation();
+          first ? nullptr : Runtime::Current()->GetInstrumentation();
       uint32_t found_dex_pc = FindNextInstructionFollowingException(self, *shadow_frame, dex_pc,
                                                                     instrumentation);
       new_dex_pc = found_dex_pc;  // the dex pc of a matching catch handler
@@ -424,6 +429,7 @@
     ShadowFrame* old_frame = shadow_frame;
     shadow_frame = shadow_frame->GetLink();
     ShadowFrame::DeleteDeoptimizedFrame(old_frame);
+    first = false;
   }
   ret_val->SetJ(value.GetJ());
 }
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index f923b84..6602840 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -414,20 +414,21 @@
 #undef EXPLICIT_DO_IPUT_QUICK_ALL_TEMPLATE_DECL
 #undef EXPLICIT_DO_IPUT_QUICK_TEMPLATE_DECL
 
+// We accept a null Instrumentation* meaning we must not report anything to the instrumentation.
 uint32_t FindNextInstructionFollowingException(
     Thread* self, ShadowFrame& shadow_frame, uint32_t dex_pc,
     const instrumentation::Instrumentation* instrumentation) {
   self->VerifyStack();
   StackHandleScope<2> hs(self);
   Handle<mirror::Throwable> exception(hs.NewHandle(self->GetException()));
-  if (instrumentation->HasExceptionCaughtListeners()
+  if (instrumentation != nullptr && instrumentation->HasExceptionCaughtListeners()
       && self->IsExceptionThrownByCurrentMethod(exception.Get())) {
     instrumentation->ExceptionCaughtEvent(self, exception.Get());
   }
   bool clear_exception = false;
   uint32_t found_dex_pc = shadow_frame.GetMethod()->FindCatchBlock(
       hs.NewHandle(exception->GetClass()), dex_pc, &clear_exception);
-  if (found_dex_pc == DexFile::kDexNoIndex) {
+  if (found_dex_pc == DexFile::kDexNoIndex && instrumentation != nullptr) {
     // Exception is not caught by the current method. We will unwind to the
     // caller. Notify any instrumentation listener.
     instrumentation->MethodUnwindEvent(self, shadow_frame.GetThisObject(),
@@ -616,9 +617,10 @@
         case 'L': {
           Object* o = shadow_frame.GetVRegReference(src_reg);
           if (do_assignability_check && o != nullptr) {
+            size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
             Class* arg_type =
                 new_shadow_frame->GetMethod()->GetClassFromTypeIndex(
-                    params->GetTypeItem(shorty_pos).type_idx_, true);
+                    params->GetTypeItem(shorty_pos).type_idx_, true /* resolve */, pointer_size);
             if (arg_type == nullptr) {
               CHECK(self->IsExceptionPending());
               return false;
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index 6468659..7398778 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -265,6 +265,13 @@
     result->SetJ(0);
     return false;
   } else {
+    if (type == kVirtual || type == kInterface) {
+      instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
+      if (UNLIKELY(instrumentation->HasInvokeVirtualOrInterfaceListeners())) {
+        instrumentation->InvokeVirtualOrInterface(
+            self, receiver, sf_method, shadow_frame.GetDexPC(), called_method);
+      }
+    }
     return DoCall<is_range, do_access_check>(called_method, self, shadow_frame, inst, inst_data,
                                              result);
   }
@@ -297,6 +304,11 @@
     result->SetJ(0);
     return false;
   } else {
+    instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
+    if (UNLIKELY(instrumentation->HasInvokeVirtualOrInterfaceListeners())) {
+      instrumentation->InvokeVirtualOrInterface(
+          self, receiver, shadow_frame.GetMethod(), shadow_frame.GetDexPC(), called_method);
+    }
     // No need to check since we've been quickened.
     return DoCall<is_range, false>(called_method, self, shadow_frame, inst, inst_data, result);
   }
@@ -344,7 +356,9 @@
   }
   ArtMethod* method = shadow_frame.GetMethod();
   mirror::Class* declaring_class = method->GetDeclaringClass();
-  mirror::String* s = declaring_class->GetDexCacheStrings()->Get(string_idx);
+  // MethodVerifier refuses methods with string_idx out of bounds.
+  DCHECK_LT(string_idx, declaring_class->GetDexCache()->NumStrings());
+  mirror::String* s = declaring_class->GetDexCacheStrings()[string_idx].Read();
   if (UNLIKELY(s == nullptr)) {
     StackHandleScope<1> hs(self);
     Handle<mirror::DexCache> dex_cache(hs.NewHandle(declaring_class->GetDexCache()));
diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc
index 7027cbf..72e2ba0 100644
--- a/runtime/interpreter/interpreter_goto_table_impl.cc
+++ b/runtime/interpreter/interpreter_goto_table_impl.cc
@@ -327,7 +327,9 @@
     const uint8_t vreg_index = inst->VRegA_11x(inst_data);
     Object* obj_result = shadow_frame.GetVRegReference(vreg_index);
     if (do_assignability_check && obj_result != nullptr) {
-      Class* return_type = shadow_frame.GetMethod()->GetReturnType();
+      size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
+      Class* return_type = shadow_frame.GetMethod()->GetReturnType(true /* resolve */,
+                                                                   pointer_size);
       obj_result = shadow_frame.GetVRegReference(vreg_index);
       if (return_type == nullptr) {
         // Return the pending exception.
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index 544f788..b5cc11e 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -225,7 +225,9 @@
         const size_t ref_idx = inst->VRegA_11x(inst_data);
         Object* obj_result = shadow_frame.GetVRegReference(ref_idx);
         if (do_assignability_check && obj_result != nullptr) {
-          Class* return_type = shadow_frame.GetMethod()->GetReturnType();
+          size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
+          Class* return_type = shadow_frame.GetMethod()->GetReturnType(true /* resolve */,
+                                                                       pointer_size);
           // Re-load since it might have moved.
           obj_result = shadow_frame.GetVRegReference(ref_idx);
           if (return_type == nullptr) {
diff --git a/runtime/java_vm_ext.cc b/runtime/java_vm_ext.cc
index d6c798a..63e8887 100644
--- a/runtime/java_vm_ext.cc
+++ b/runtime/java_vm_ext.cc
@@ -580,9 +580,12 @@
 }
 
 inline bool JavaVMExt::MayAccessWeakGlobalsUnlocked(Thread* self) const {
-  return kUseReadBarrier
-      ? self->GetWeakRefAccessEnabled()
-      : allow_accessing_weak_globals_.LoadSequentiallyConsistent();
+  if (kUseReadBarrier) {
+    // self can be null during a runtime shutdown. ~Runtime()->~ClassLinker()->DecodeWeakGlobal().
+    return self != nullptr ? self->GetWeakRefAccessEnabled() : true;
+  } else {
+    return allow_accessing_weak_globals_.LoadSequentiallyConsistent();
+  }
 }
 
 mirror::Object* JavaVMExt::DecodeWeakGlobal(Thread* self, IndirectRef ref) {
@@ -592,6 +595,7 @@
   // This only applies in the case where MayAccessWeakGlobals goes from false to true. In the other
   // case, it may be racy, this is benign since DecodeWeakGlobalLocked does the correct behavior
   // if MayAccessWeakGlobals is false.
+  DCHECK_EQ(GetIndirectRefKind(ref), kWeakGlobal);
   if (LIKELY(MayAccessWeakGlobalsUnlocked(self))) {
     return weak_globals_.SynchronizedGet(ref);
   }
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index 26a4fe4..0607493 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -39,6 +39,8 @@
       options.GetOrDefault(RuntimeArgumentMap::JITCodeCacheCapacity);
   jit_options->compile_threshold_ =
       options.GetOrDefault(RuntimeArgumentMap::JITCompileThreshold);
+  jit_options->warmup_threshold_ =
+      options.GetOrDefault(RuntimeArgumentMap::JITWarmupThreshold);
   jit_options->dump_info_on_shutdown_ =
       options.Exists(RuntimeArgumentMap::DumpJITInfoOnShutdown);
   return jit_options;
@@ -160,18 +162,18 @@
   }
 }
 
-void Jit::CreateInstrumentationCache(size_t compile_threshold) {
+void Jit::CreateInstrumentationCache(size_t compile_threshold, size_t warmup_threshold) {
   CHECK_GT(compile_threshold, 0U);
-  Runtime* const runtime = Runtime::Current();
-  runtime->GetThreadList()->SuspendAll(__FUNCTION__);
+  ScopedSuspendAll ssa(__FUNCTION__);
   // Add Jit interpreter instrumentation, tells the interpreter when to notify the jit to compile
   // something.
-  instrumentation_cache_.reset(new jit::JitInstrumentationCache(compile_threshold));
-  runtime->GetInstrumentation()->AddListener(
+  instrumentation_cache_.reset(
+      new jit::JitInstrumentationCache(compile_threshold, warmup_threshold));
+  Runtime::Current()->GetInstrumentation()->AddListener(
       new jit::JitInstrumentationListener(instrumentation_cache_.get()),
       instrumentation::Instrumentation::kMethodEntered |
-      instrumentation::Instrumentation::kBackwardBranch);
-  runtime->GetThreadList()->ResumeAll();
+      instrumentation::Instrumentation::kBackwardBranch |
+      instrumentation::Instrumentation::kInvokeVirtualOrInterface);
 }
 
 }  // namespace jit
diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h
index ca6e7ea..643bc23 100644
--- a/runtime/jit/jit.h
+++ b/runtime/jit/jit.h
@@ -43,13 +43,14 @@
 class Jit {
  public:
   static constexpr bool kStressMode = kIsDebugBuild;
-  static constexpr size_t kDefaultCompileThreshold = kStressMode ? 1 : 1000;
+  static constexpr size_t kDefaultCompileThreshold = kStressMode ? 2 : 1000;
+  static constexpr size_t kDefaultWarmupThreshold = kDefaultCompileThreshold / 2;
 
   virtual ~Jit();
   static Jit* Create(JitOptions* options, std::string* error_msg);
   bool CompileMethod(ArtMethod* method, Thread* self)
       SHARED_REQUIRES(Locks::mutator_lock_);
-  void CreateInstrumentationCache(size_t compile_threshold);
+  void CreateInstrumentationCache(size_t compile_threshold, size_t warmup_threshold);
   void CreateThreadPool();
   CompilerCallbacks* GetCompilerCallbacks() {
     return compiler_callbacks_;
@@ -95,6 +96,9 @@
   size_t GetCompileThreshold() const {
     return compile_threshold_;
   }
+  size_t GetWarmupThreshold() const {
+    return warmup_threshold_;
+  }
   size_t GetCodeCacheCapacity() const {
     return code_cache_capacity_;
   }
@@ -112,6 +116,7 @@
   bool use_jit_;
   size_t code_cache_capacity_;
   size_t compile_threshold_;
+  size_t warmup_threshold_;
   bool dump_info_on_shutdown_;
 
   JitOptions() : use_jit_(false), code_cache_capacity_(0), compile_threshold_(0),
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index cd5f4cb..4c53162 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -82,9 +82,19 @@
   return code_cache_ptr_ - size;
 }
 
+uint8_t* JitCodeCache::ReserveData(Thread* self, size_t size) {
+  MutexLock mu(self, lock_);
+  size = RoundUp(size, sizeof(void*));
+  if (size > DataCacheRemain()) {
+    return nullptr;
+  }
+  data_cache_ptr_ += size;
+  return data_cache_ptr_ - size;
+}
+
 uint8_t* JitCodeCache::AddDataArray(Thread* self, const uint8_t* begin, const uint8_t* end) {
   MutexLock mu(self, lock_);
-  const size_t size = end - begin;
+  const size_t size = RoundUp(end - begin, sizeof(void*));
   if (size > DataCacheRemain()) {
     return nullptr;  // Out of space in the data cache.
   }
diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h
index 9707f6f..f485e4a 100644
--- a/runtime/jit/jit_code_cache.h
+++ b/runtime/jit/jit_code_cache.h
@@ -86,6 +86,9 @@
   // Reserve a region of code of size at least "size". Returns null if there is no more room.
   uint8_t* ReserveCode(Thread* self, size_t size) REQUIRES(!lock_);
 
+  // Reserve a region of data of size at least "size". Returns null if there is no more room.
+  uint8_t* ReserveData(Thread* self, size_t size) REQUIRES(!lock_);
+
   // Add a data array of size (end - begin) with the associated contents, returns null if there
   // is no more room.
   uint8_t* AddDataArray(Thread* self, const uint8_t* begin, const uint8_t* end)
diff --git a/runtime/jit/jit_instrumentation.cc b/runtime/jit/jit_instrumentation.cc
index 258c29d..f485682 100644
--- a/runtime/jit/jit_instrumentation.cc
+++ b/runtime/jit/jit_instrumentation.cc
@@ -26,16 +26,12 @@
 
 class JitCompileTask : public Task {
  public:
-  JitCompileTask(ArtMethod* method, JitInstrumentationCache* cache)
-      : method_(method), cache_(cache) {
-  }
+  explicit JitCompileTask(ArtMethod* method) : method_(method) {}
 
   virtual void Run(Thread* self) OVERRIDE {
     ScopedObjectAccess soa(self);
     VLOG(jit) << "JitCompileTask compiling method " << PrettyMethod(method_);
-    if (Runtime::Current()->GetJit()->CompileMethod(method_, self)) {
-      cache_->SignalCompiled(self, method_);
-    } else {
+    if (!Runtime::Current()->GetJit()->CompileMethod(method_, self)) {
       VLOG(jit) << "Failed to compile method " << PrettyMethod(method_);
     }
   }
@@ -46,13 +42,14 @@
 
  private:
   ArtMethod* const method_;
-  JitInstrumentationCache* const cache_;
 
   DISALLOW_IMPLICIT_CONSTRUCTORS(JitCompileTask);
 };
 
-JitInstrumentationCache::JitInstrumentationCache(size_t hot_method_threshold)
-    : lock_("jit instrumentation lock"), hot_method_threshold_(hot_method_threshold) {
+JitInstrumentationCache::JitInstrumentationCache(size_t hot_method_threshold,
+                                                 size_t warm_method_threshold)
+    : hot_method_threshold_(hot_method_threshold),
+      warm_method_threshold_(warm_method_threshold) {
 }
 
 void JitInstrumentationCache::CreateThreadPool() {
@@ -60,20 +57,11 @@
 }
 
 void JitInstrumentationCache::DeleteThreadPool() {
+  DCHECK(Runtime::Current()->IsShuttingDown(Thread::Current()));
   thread_pool_.reset();
 }
 
-void JitInstrumentationCache::SignalCompiled(Thread* self, ArtMethod* method) {
-  ScopedObjectAccessUnchecked soa(self);
-  jmethodID method_id = soa.EncodeMethod(method);
-  MutexLock mu(self, lock_);
-  auto it = samples_.find(method_id);
-  if (it != samples_.end()) {
-    samples_.erase(it);
-  }
-}
-
-void JitInstrumentationCache::AddSamples(Thread* self, ArtMethod* method, size_t count) {
+void JitInstrumentationCache::AddSamples(Thread* self, ArtMethod* method, size_t) {
   ScopedObjectAccessUnchecked soa(self);
   // Since we don't have on-stack replacement, some methods can remain in the interpreter longer
   // than we want resulting in samples even after the method is compiled.
@@ -81,34 +69,21 @@
       Runtime::Current()->GetJit()->GetCodeCache()->ContainsMethod(method)) {
     return;
   }
-  jmethodID method_id = soa.EncodeMethod(method);
-  bool is_hot = false;
-  {
-    MutexLock mu(self, lock_);
-    size_t sample_count = 0;
-    auto it = samples_.find(method_id);
-    if (it != samples_.end()) {
-      it->second += count;
-      sample_count = it->second;
-    } else {
-      sample_count = count;
-      samples_.insert(std::make_pair(method_id, count));
-    }
-    // If we have enough samples, mark as hot and request Jit compilation.
-    if (sample_count >= hot_method_threshold_ && sample_count - count < hot_method_threshold_) {
-      is_hot = true;
+  if (thread_pool_.get() == nullptr) {
+    DCHECK(Runtime::Current()->IsShuttingDown(self));
+    return;
+  }
+  uint16_t sample_count = method->IncrementCounter();
+  if (sample_count == warm_method_threshold_) {
+    ProfilingInfo* info = method->CreateProfilingInfo();
+    if (info != nullptr) {
+      VLOG(jit) << "Start profiling " << PrettyMethod(method);
     }
   }
-  if (is_hot) {
-    if (thread_pool_.get() != nullptr) {
-      thread_pool_->AddTask(self, new JitCompileTask(
-          method->GetInterfaceMethodIfProxy(sizeof(void*)), this));
-      thread_pool_->StartWorkers(self);
-    } else {
-      VLOG(jit) << "Compiling hot method " << PrettyMethod(method);
-      Runtime::Current()->GetJit()->CompileMethod(
-          method->GetInterfaceMethodIfProxy(sizeof(void*)), self);
-    }
+  if (sample_count == hot_method_threshold_) {
+    thread_pool_->AddTask(self, new JitCompileTask(
+        method->GetInterfaceMethodIfProxy(sizeof(void*))));
+    thread_pool_->StartWorkers(self);
   }
 }
 
@@ -117,5 +92,17 @@
   CHECK(instrumentation_cache_ != nullptr);
 }
 
+void JitInstrumentationListener::InvokeVirtualOrInterface(Thread* thread,
+                                                          mirror::Object* this_object,
+                                                          ArtMethod* caller,
+                                                          uint32_t dex_pc,
+                                                          ArtMethod* callee ATTRIBUTE_UNUSED) {
+  DCHECK(this_object != nullptr);
+  ProfilingInfo* info = caller->GetProfilingInfo();
+  if (info != nullptr) {
+    info->AddInvokeInfo(thread, dex_pc, this_object->GetClass());
+  }
+}
+
 }  // namespace jit
 }  // namespace art
diff --git a/runtime/jit/jit_instrumentation.h b/runtime/jit/jit_instrumentation.h
index 0deaf8a..6fdef65 100644
--- a/runtime/jit/jit_instrumentation.h
+++ b/runtime/jit/jit_instrumentation.h
@@ -45,18 +45,15 @@
 // Keeps track of which methods are hot.
 class JitInstrumentationCache {
  public:
-  explicit JitInstrumentationCache(size_t hot_method_threshold);
+  JitInstrumentationCache(size_t hot_method_threshold, size_t warm_method_threshold);
   void AddSamples(Thread* self, ArtMethod* method, size_t samples)
-      SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!lock_);
-  void SignalCompiled(Thread* self, ArtMethod* method)
-      SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!lock_);
+      SHARED_REQUIRES(Locks::mutator_lock_);
   void CreateThreadPool();
   void DeleteThreadPool();
 
  private:
-  Mutex lock_;
-  std::unordered_map<jmethodID, size_t> samples_;
   size_t hot_method_threshold_;
+  size_t warm_method_threshold_;
   std::unique_ptr<ThreadPool> thread_pool_;
 
   DISALLOW_IMPLICIT_CONSTRUCTORS(JitInstrumentationCache);
@@ -66,37 +63,43 @@
  public:
   explicit JitInstrumentationListener(JitInstrumentationCache* cache);
 
-  virtual void MethodEntered(Thread* thread, mirror::Object* /*this_object*/,
-                             ArtMethod* method, uint32_t /*dex_pc*/)
+  void MethodEntered(Thread* thread, mirror::Object* /*this_object*/,
+                     ArtMethod* method, uint32_t /*dex_pc*/)
       OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
     instrumentation_cache_->AddSamples(thread, method, 1);
   }
-  virtual void MethodExited(Thread* /*thread*/, mirror::Object* /*this_object*/,
-                            ArtMethod* /*method*/, uint32_t /*dex_pc*/,
-                            const JValue& /*return_value*/)
+  void MethodExited(Thread* /*thread*/, mirror::Object* /*this_object*/,
+                    ArtMethod* /*method*/, uint32_t /*dex_pc*/,
+                    const JValue& /*return_value*/)
       OVERRIDE { }
-  virtual void MethodUnwind(Thread* /*thread*/, mirror::Object* /*this_object*/,
-                            ArtMethod* /*method*/, uint32_t /*dex_pc*/) OVERRIDE { }
-  virtual void FieldRead(Thread* /*thread*/, mirror::Object* /*this_object*/,
-                         ArtMethod* /*method*/, uint32_t /*dex_pc*/,
-                         ArtField* /*field*/) OVERRIDE { }
-  virtual void FieldWritten(Thread* /*thread*/, mirror::Object* /*this_object*/,
-                            ArtMethod* /*method*/, uint32_t /*dex_pc*/,
-                            ArtField* /*field*/, const JValue& /*field_value*/)
+  void MethodUnwind(Thread* /*thread*/, mirror::Object* /*this_object*/,
+                    ArtMethod* /*method*/, uint32_t /*dex_pc*/) OVERRIDE { }
+  void FieldRead(Thread* /*thread*/, mirror::Object* /*this_object*/,
+                 ArtMethod* /*method*/, uint32_t /*dex_pc*/,
+                 ArtField* /*field*/) OVERRIDE { }
+  void FieldWritten(Thread* /*thread*/, mirror::Object* /*this_object*/,
+                    ArtMethod* /*method*/, uint32_t /*dex_pc*/,
+                    ArtField* /*field*/, const JValue& /*field_value*/)
       OVERRIDE { }
-  virtual void ExceptionCaught(Thread* /*thread*/,
-                               mirror::Throwable* /*exception_object*/) OVERRIDE { }
+  void ExceptionCaught(Thread* /*thread*/,
+                       mirror::Throwable* /*exception_object*/) OVERRIDE { }
 
-  virtual void DexPcMoved(Thread* /*self*/, mirror::Object* /*this_object*/,
-                          ArtMethod* /*method*/, uint32_t /*new_dex_pc*/) OVERRIDE { }
+  void DexPcMoved(Thread* /*self*/, mirror::Object* /*this_object*/,
+                  ArtMethod* /*method*/, uint32_t /*new_dex_pc*/) OVERRIDE { }
 
-  // We only care about how many dex instructions were executed in the Jit.
-  virtual void BackwardBranch(Thread* thread, ArtMethod* method, int32_t dex_pc_offset)
+  void BackwardBranch(Thread* thread, ArtMethod* method, int32_t dex_pc_offset)
       OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
     CHECK_LE(dex_pc_offset, 0);
     instrumentation_cache_->AddSamples(thread, method, 1);
   }
 
+  void InvokeVirtualOrInterface(Thread* thread,
+                                mirror::Object* this_object,
+                                ArtMethod* caller,
+                                uint32_t dex_pc,
+                                ArtMethod* callee)
+      OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_);
+
  private:
   JitInstrumentationCache* const instrumentation_cache_;
 
diff --git a/runtime/jit/profiling_info.cc b/runtime/jit/profiling_info.cc
new file mode 100644
index 0000000..0c039f2
--- /dev/null
+++ b/runtime/jit/profiling_info.cc
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#include "profiling_info.h"
+
+#include "art_method-inl.h"
+#include "dex_instruction.h"
+#include "jit/jit.h"
+#include "jit/jit_code_cache.h"
+#include "scoped_thread_state_change.h"
+#include "thread.h"
+
+namespace art {
+
+ProfilingInfo* ProfilingInfo::Create(ArtMethod* method) {
+  // Walk over the dex instructions of the method and keep track of
+  // instructions we are interested in profiling.
+  const uint16_t* code_ptr = nullptr;
+  const uint16_t* code_end = nullptr;
+  {
+    ScopedObjectAccess soa(Thread::Current());
+    DCHECK(!method->IsNative());
+    const DexFile::CodeItem& code_item = *method->GetCodeItem();
+    code_ptr = code_item.insns_;
+    code_end = code_item.insns_ + code_item.insns_size_in_code_units_;
+  }
+
+  uint32_t dex_pc = 0;
+  std::vector<uint32_t> entries;
+  while (code_ptr < code_end) {
+    const Instruction& instruction = *Instruction::At(code_ptr);
+    switch (instruction.Opcode()) {
+      case Instruction::INVOKE_VIRTUAL:
+      case Instruction::INVOKE_VIRTUAL_RANGE:
+      case Instruction::INVOKE_VIRTUAL_QUICK:
+      case Instruction::INVOKE_VIRTUAL_RANGE_QUICK:
+      case Instruction::INVOKE_INTERFACE:
+      case Instruction::INVOKE_INTERFACE_RANGE:
+        entries.push_back(dex_pc);
+        break;
+
+      default:
+        break;
+    }
+    dex_pc += instruction.SizeInCodeUnits();
+    code_ptr += instruction.SizeInCodeUnits();
+  }
+
+  // If there is no instruction we are interested in, no need to create a `ProfilingInfo`
+  // object, it will never be filled.
+  if (entries.empty()) {
+    return nullptr;
+  }
+
+  // Allocate the `ProfilingInfo` object int the JIT's data space.
+  jit::JitCodeCache* code_cache = Runtime::Current()->GetJit()->GetCodeCache();
+  size_t profile_info_size = sizeof(ProfilingInfo) + sizeof(InlineCache) * entries.size();
+  uint8_t* data = code_cache->ReserveData(Thread::Current(), profile_info_size);
+
+  if (data == nullptr) {
+    VLOG(jit) << "Cannot allocate profiling info anymore";
+    return nullptr;
+  }
+
+  return new (data) ProfilingInfo(entries);
+}
+
+void ProfilingInfo::AddInvokeInfo(Thread* self, uint32_t dex_pc, mirror::Class* cls) {
+  InlineCache* cache = nullptr;
+  // TODO: binary search if array is too long.
+  for (size_t i = 0; i < number_of_inline_caches_; ++i) {
+    if (cache_[i].dex_pc == dex_pc) {
+      cache = &cache_[i];
+      break;
+    }
+  }
+  DCHECK(cache != nullptr);
+
+  ScopedObjectAccess soa(self);
+  for (size_t i = 0; i < InlineCache::kIndividualCacheSize; ++i) {
+    mirror::Class* existing = cache->classes_[i].Read<kWithoutReadBarrier>();
+    if (existing == cls) {
+      // Receiver type is already in the cache, nothing else to do.
+      return;
+    } else if (existing == nullptr) {
+      // Cache entry is empty, try to put `cls` in it.
+      GcRoot<mirror::Class> expected_root(nullptr);
+      GcRoot<mirror::Class> desired_root(cls);
+      if (!reinterpret_cast<Atomic<GcRoot<mirror::Class>>*>(&cache->classes_[i])->
+              CompareExchangeStrongSequentiallyConsistent(expected_root, desired_root)) {
+        // Some other thread put a class in the cache, continue iteration starting at this
+        // entry in case the entry contains `cls`.
+        --i;
+      } else {
+        // We successfully set `cls`, just return.
+        return;
+      }
+    }
+  }
+  // Unsuccessfull - cache is full, making it megamorphic.
+  DCHECK(cache->IsMegamorphic());
+}
+
+}  // namespace art
diff --git a/runtime/jit/profiling_info.h b/runtime/jit/profiling_info.h
new file mode 100644
index 0000000..73ca41a
--- /dev/null
+++ b/runtime/jit/profiling_info.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2015 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_JIT_PROFILING_INFO_H_
+#define ART_RUNTIME_JIT_PROFILING_INFO_H_
+
+#include <vector>
+
+#include "base/macros.h"
+#include "gc_root.h"
+
+namespace art {
+
+class ArtMethod;
+
+namespace mirror {
+class Class;
+}
+
+/**
+ * Profiling info for a method, created and filled by the interpreter once the
+ * method is warm, and used by the compiler to drive optimizations.
+ */
+class ProfilingInfo {
+ public:
+  static ProfilingInfo* Create(ArtMethod* method);
+
+  // Add information from an executed INVOKE instruction to the profile.
+  void AddInvokeInfo(Thread* self, uint32_t dex_pc, mirror::Class* cls);
+
+  // NO_THREAD_SAFETY_ANALYSIS since we don't know what the callback requires.
+  template<typename RootVisitorType>
+  void VisitRoots(RootVisitorType& visitor) NO_THREAD_SAFETY_ANALYSIS {
+    for (size_t i = 0; i < number_of_inline_caches_; ++i) {
+      InlineCache* cache = &cache_[i];
+      for (size_t j = 0; j < InlineCache::kIndividualCacheSize; ++j) {
+        visitor.VisitRootIfNonNull(cache->classes_[j].AddressWithoutBarrier());
+      }
+    }
+  }
+
+ private:
+  // Structure to store the classes seen at runtime for a specific instruction.
+  // Once the classes_ array is full, we consider the INVOKE to be megamorphic.
+  struct InlineCache {
+    bool IsMonomorphic() const {
+      DCHECK_GE(kIndividualCacheSize, 2);
+      return !classes_[0].IsNull() && classes_[1].IsNull();
+    }
+
+    bool IsMegamorphic() const {
+      for (size_t i = 0; i < kIndividualCacheSize; ++i) {
+        if (classes_[i].IsNull()) {
+          return false;
+        }
+      }
+      return true;
+    }
+
+    bool IsUnitialized() const {
+      return classes_[0].IsNull();
+    }
+
+    bool IsPolymorphic() const {
+      DCHECK_GE(kIndividualCacheSize, 3);
+      return !classes_[1].IsNull() && classes_[kIndividualCacheSize - 1].IsNull();
+    }
+
+    static constexpr uint16_t kIndividualCacheSize = 5;
+    uint32_t dex_pc;
+    GcRoot<mirror::Class> classes_[kIndividualCacheSize];
+  };
+
+  explicit ProfilingInfo(const std::vector<uint32_t>& entries)
+      : number_of_inline_caches_(entries.size()) {
+    memset(&cache_, 0, number_of_inline_caches_ * sizeof(InlineCache));
+    for (size_t i = 0; i < number_of_inline_caches_; ++i) {
+      cache_[i].dex_pc = entries[i];
+    }
+  }
+
+  // Number of instructions we are profiling in the ArtMethod.
+  const uint32_t number_of_inline_caches_;
+
+  // Dynamically allocated array of size `number_of_inline_caches_`.
+  InlineCache cache_[0];
+
+  DISALLOW_COPY_AND_ASSIGN(ProfilingInfo);
+};
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_JIT_PROFILING_INFO_H_
diff --git a/runtime/lambda/art_lambda_method.cc b/runtime/lambda/art_lambda_method.cc
new file mode 100644
index 0000000..6f9f8bb
--- /dev/null
+++ b/runtime/lambda/art_lambda_method.cc
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#include "lambda/art_lambda_method.h"
+
+#include "base/logging.h"
+#include "lambda/shorty_field_type.h"
+
+namespace art {
+namespace lambda {
+
+ArtLambdaMethod::ArtLambdaMethod(ArtMethod* target_method,
+                                 const char* captured_variables_type_descriptor,
+                                 const char* captured_variables_shorty,
+                                 bool innate_lambda)
+    : method_(target_method),
+      captured_variables_type_descriptor_(captured_variables_type_descriptor),
+      captured_variables_shorty_(captured_variables_shorty),
+      innate_lambda_(innate_lambda) {
+  DCHECK(target_method != nullptr);
+  DCHECK(captured_variables_type_descriptor != nullptr);
+  DCHECK(captured_variables_shorty != nullptr);
+
+  // Calculate the static closure size from the captured variables.
+  size_t size = sizeof(ArtLambdaMethod*);  // Initial size is just this method.
+  bool static_size = true;
+  const char* shorty = captured_variables_shorty_;
+  while (shorty != nullptr && *shorty != '\0') {
+    // Each captured variable also appends to the size.
+    ShortyFieldType shorty_field{*shorty};  // NOLINT [readability/braces] [4]
+    size += shorty_field.GetStaticSize();
+    static_size &= shorty_field.IsStaticSize();
+    ++shorty;
+  }
+  closure_size_ = size;
+
+  // We determine whether or not the size is dynamic by checking for nested lambdas.
+  //
+  // This is conservative, since in theory an optimization could determine the size
+  // of the nested lambdas recursively. In practice it's probably better to flatten out
+  // nested lambdas and inline all their code if they are known statically.
+  dynamic_size_ = !static_size;
+
+  if (kIsDebugBuild) {
+    // Double check that the number of captured variables match in both strings.
+    size_t shorty_count = strlen(captured_variables_shorty);
+
+    size_t long_count = 0;
+    const char* long_type = captured_variables_type_descriptor;
+    ShortyFieldType out;
+    while ((long_type = ShortyFieldType::ParseFromFieldTypeDescriptor(long_type, &out))
+           != nullptr) {
+      ++long_count;
+    }
+
+    DCHECK_EQ(shorty_count, long_count)
+        << "number of captured variables in long type '" << captured_variables_type_descriptor
+        << "' (" << long_count << ")" << " did not match short type '"
+        << captured_variables_shorty << "' (" << shorty_count << ")";
+  }
+}
+
+}  // namespace lambda
+}  // namespace art
diff --git a/runtime/lambda/art_lambda_method.h b/runtime/lambda/art_lambda_method.h
new file mode 100644
index 0000000..892d8c6
--- /dev/null
+++ b/runtime/lambda/art_lambda_method.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2015 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_LAMBDA_ART_LAMBDA_METHOD_H_
+#define ART_RUNTIME_LAMBDA_ART_LAMBDA_METHOD_H_
+
+#include "base/macros.h"
+#include "art_method.h"
+
+#include <stdint.h>
+
+namespace art {
+namespace lambda {
+
+class ArtLambdaMethod {
+ public:
+  // Construct an art lambda method.
+  // The target method is the one invoked by invoke-lambda.
+  // The type descriptor describes the types of variables captured, e.g. "ZFLObject;\FI;[Z"
+  // The shorty drops the object name and treats arrays as objects, e.g. "ZFL\L"
+  // Innate lambda means that the lambda was originally created via invoke-lambda.
+  // -- Non-innate lambdas (learned lambdas) come from a regular class that was boxed to lambda.
+  // (Ownership of strings is retained by the caller and the lifetime should exceed this class).
+  ArtLambdaMethod(ArtMethod* target_method,
+                  const char* captured_variables_type_descriptor,
+                  const char* captured_variables_shorty_,
+                  bool innate_lambda = true);
+
+  // Get the target method for this lambda that would be used by the invoke-lambda dex instruction.
+  ArtMethod* GetArtMethod() const {
+    return method_;
+  }
+
+  // Get the compile-time size of lambda closures for this method in bytes.
+  // This is circular (that is, it includes the size of the ArtLambdaMethod pointer).
+  // One should also check if the size is dynamic since nested lambdas have a runtime size.
+  size_t GetStaticClosureSize() const {
+    return closure_size_;
+  }
+
+  // Get the type descriptor for the list of captured variables.
+  // e.g. "ZFLObject;\FI;[Z" means a captured int, float, class Object, lambda FI, array of ints
+  const char* GetCapturedVariablesTypeDescriptor() const {
+    return captured_variables_type_descriptor_;
+  }
+
+  // Get the shorty 'field' type descriptor list of captured variables.
+  // This follows the same rules as a string of ShortyFieldType in the dex specification.
+  // Every captured variable is represented by exactly one character.
+  // - Objects become 'L'.
+  // - Arrays become 'L'.
+  // - Lambdas become '\'.
+  const char* GetCapturedVariablesShortyTypeDescriptor() const {
+    return captured_variables_shorty_;
+  }
+
+  // Will the size of this lambda change at runtime?
+  // Only returns true if there is a nested lambda that we can't determine statically the size of.
+  bool IsDynamicSize() const {
+    return dynamic_size_;
+  }
+
+  // Will the size of this lambda always be constant at runtime?
+  // This generally means there's no nested lambdas, or we were able to successfully determine
+  // their size statically at compile time.
+  bool IsStaticSize() const {
+    return !IsDynamicSize();
+  }
+  // Is this a lambda that was originally created via invoke-lambda?
+  // -- Non-innate lambdas (learned lambdas) come from a regular class that was boxed to lambda.
+  bool IsInnateLambda() const {
+    return innate_lambda_;
+  }
+
+  // How many variables were captured?
+  // (Each nested lambda counts as 1 captured var regardless of how many captures it itself has).
+  size_t GetNumberOfCapturedVariables() const {
+    return strlen(captured_variables_shorty_);
+  }
+
+ private:
+  // TODO: ArtMethod, or at least the entry points should be inlined into this struct
+  // to avoid an extra indirect load when doing invokes.
+  // Target method that invoke-lambda will jump to.
+  ArtMethod* method_;
+  // How big the closure is (in bytes). Only includes the constant size.
+  size_t closure_size_;
+  // The type descriptor for the captured variables, e.g. "IS" for [int, short]
+  const char* captured_variables_type_descriptor_;
+  // The shorty type descriptor for captured vars, (e.g. using 'L' instead of 'LObject;')
+  const char* captured_variables_shorty_;
+  // Whether or not the size is dynamic. If it is, copiers need to read the Closure size at runtime.
+  bool dynamic_size_;
+  // True if this lambda was originally made with create-lambda,
+  // false if it came from a class instance (through new-instance and then unbox-lambda).
+  bool innate_lambda_;
+
+  DISALLOW_COPY_AND_ASSIGN(ArtLambdaMethod);
+};
+
+}  // namespace lambda
+}  // namespace art
+
+#endif  // ART_RUNTIME_LAMBDA_ART_LAMBDA_METHOD_H_
diff --git a/runtime/lambda/closure.cc b/runtime/lambda/closure.cc
new file mode 100644
index 0000000..95a17c6
--- /dev/null
+++ b/runtime/lambda/closure.cc
@@ -0,0 +1,365 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#include "lambda/closure.h"
+
+#include "base/logging.h"
+#include "lambda/art_lambda_method.h"
+#include "runtime/mirror/object_reference.h"
+
+static constexpr const bool kClosureSupportsReferences = false;
+static constexpr const bool kClosureSupportsGarbageCollection = false;
+
+namespace art {
+namespace lambda {
+
+template <typename T>
+// TODO: can I return T __attribute__((__aligned__(1)))* here instead?
+const uint8_t* Closure::GetUnsafeAtOffset(size_t offset) const {
+  // Do not DCHECK here with existing helpers since most of them will call into this function.
+  return reinterpret_cast<const uint8_t*>(captured_) + offset;
+}
+
+size_t Closure::GetCapturedVariableSize(ShortyFieldType variable_type, size_t offset) const {
+  switch (variable_type) {
+    case ShortyFieldType::kLambda:
+    {
+      return GetClosureSize(GetUnsafeAtOffset<Closure>(offset));
+    }
+    default:
+      DCHECK(variable_type.IsStaticSize());
+      return variable_type.GetStaticSize();
+  }
+}
+
+// Templatize the flags to give the compiler a fighting chance to eliminate
+// any unnecessary code through different uses of this function.
+template <Closure::VariableInfo::Flags flags>
+inline Closure::VariableInfo Closure::ParseTypeDescriptor(const char* type_descriptor,
+                                                          size_t upto_index) const {
+  DCHECK(type_descriptor != nullptr);
+
+  VariableInfo result;
+
+  ShortyFieldType last_type;
+  size_t offset = (flags & VariableInfo::kOffset) ? GetStartingOffset() : 0;
+  size_t prev_offset = 0;
+  size_t count = 0;
+
+  while ((type_descriptor =
+      ShortyFieldType::ParseFromFieldTypeDescriptor(type_descriptor, &last_type)) != nullptr) {
+    count++;
+
+    if (flags & VariableInfo::kOffset) {
+      // Accumulate the sizes of all preceding captured variables as the current offset only.
+      offset += prev_offset;
+      prev_offset = GetCapturedVariableSize(last_type, offset);
+    }
+
+    if ((count > upto_index)) {
+      break;
+    }
+  }
+
+  if (flags & VariableInfo::kVariableType) {
+    result.variable_type_ = last_type;
+  }
+
+  if (flags & VariableInfo::kIndex) {
+    result.index_ = count;
+  }
+
+  if (flags & VariableInfo::kCount) {
+    result.count_ = count;
+  }
+
+  if (flags & VariableInfo::kOffset) {
+    result.offset_ = offset;
+  }
+
+  // TODO: We should probably store the result of this in the ArtLambdaMethod,
+  // to avoid re-computing the data every single time for static closures.
+  return result;
+}
+
+size_t Closure::GetCapturedVariablesSize() const {
+  const size_t captured_variable_offset = offsetof(Closure, captured_);
+  DCHECK_GE(GetSize(), captured_variable_offset);  // Prevent underflows.
+  return GetSize() - captured_variable_offset;
+}
+
+size_t Closure::GetSize() const {
+  const size_t static_closure_size = lambda_info_->GetStaticClosureSize();
+  if (LIKELY(lambda_info_->IsStaticSize())) {
+    return static_closure_size;
+  }
+
+  DCHECK_GE(static_closure_size, sizeof(captured_[0].dynamic_.size_));
+  const size_t dynamic_closure_size = captured_[0].dynamic_.size_;
+  // The dynamic size better be at least as big as the static size.
+  DCHECK_GE(dynamic_closure_size, static_closure_size);
+
+  return dynamic_closure_size;
+}
+
+void Closure::CopyTo(void* target, size_t target_size) const {
+  DCHECK_GE(target_size, GetSize());
+
+  // TODO: using memcpy is unsafe with read barriers, fix this once we add reference support
+  static_assert(kClosureSupportsReferences == false,
+                "Do not use memcpy with readbarrier references");
+  memcpy(target, this, GetSize());
+}
+
+size_t Closure::GetNumberOfCapturedVariables() const {
+  // TODO: refactor into art_lambda_method.h. Parsing should only be required here as a DCHECK.
+  VariableInfo variable_info =
+      ParseTypeDescriptor<VariableInfo::kCount>(GetCapturedVariablesTypeDescriptor(),
+                                                VariableInfo::kUpToIndexMax);
+  size_t count = variable_info.count_;
+  // Assuming each variable was 1 byte, the size should always be greater or equal than the count.
+  DCHECK_LE(count, GetCapturedVariablesSize());
+  return count;
+}
+
+const char* Closure::GetCapturedVariablesTypeDescriptor() const {
+  return lambda_info_->GetCapturedVariablesTypeDescriptor();
+}
+
+ShortyFieldType Closure::GetCapturedShortyType(size_t index) const {
+  DCHECK_LT(index, GetNumberOfCapturedVariables());
+
+  VariableInfo variable_info =
+      ParseTypeDescriptor<VariableInfo::kVariableType>(GetCapturedVariablesTypeDescriptor(),
+                                                       index);
+
+  return variable_info.variable_type_;
+}
+
+uint32_t Closure::GetCapturedPrimitiveNarrow(size_t index) const {
+  DCHECK(GetCapturedShortyType(index).IsPrimitiveNarrow());
+
+  ShortyFieldType variable_type;
+  size_t offset;
+  GetCapturedVariableTypeAndOffset(index, &variable_type, &offset);
+
+  // TODO: Restructure to use template specialization, e.g. GetCapturedPrimitive<T>
+  // so that we can avoid this nonsense regarding memcpy always overflowing.
+  // Plus, this additional switching seems redundant since the interpreter
+  // would've done it already, and knows the exact type.
+  uint32_t result = 0;
+  static_assert(ShortyFieldTypeTraits::IsPrimitiveNarrowType<decltype(result)>(),
+                "result must be a primitive narrow type");
+  switch (variable_type) {
+    case ShortyFieldType::kBoolean:
+      CopyUnsafeAtOffset<bool>(offset, &result);
+      break;
+    case ShortyFieldType::kByte:
+      CopyUnsafeAtOffset<uint8_t>(offset, &result);
+      break;
+    case ShortyFieldType::kChar:
+      CopyUnsafeAtOffset<uint16_t>(offset, &result);
+      break;
+    case ShortyFieldType::kShort:
+      CopyUnsafeAtOffset<int16_t>(offset, &result);
+      break;
+    case ShortyFieldType::kInt:
+      CopyUnsafeAtOffset<int32_t>(offset, &result);
+      break;
+    case ShortyFieldType::kFloat:
+      // XX: Maybe there should just be a GetCapturedPrimitive<T> to avoid this shuffle?
+      // The interpreter's invoke seems to only special case references and wides,
+      // everything else is treated as a generic 32-bit pattern.
+      CopyUnsafeAtOffset<float>(offset, &result);
+      break;
+    default:
+      LOG(FATAL)
+          << "expected a valid narrow primitive shorty type but got "
+          << static_cast<char>(variable_type);
+      UNREACHABLE();
+  }
+
+  return result;
+}
+
+uint64_t Closure::GetCapturedPrimitiveWide(size_t index) const {
+  DCHECK(GetCapturedShortyType(index).IsPrimitiveWide());
+
+  ShortyFieldType variable_type;
+  size_t offset;
+  GetCapturedVariableTypeAndOffset(index, &variable_type, &offset);
+
+  // TODO: Restructure to use template specialization, e.g. GetCapturedPrimitive<T>
+  // so that we can avoid this nonsense regarding memcpy always overflowing.
+  // Plus, this additional switching seems redundant since the interpreter
+  // would've done it already, and knows the exact type.
+  uint64_t result = 0;
+  static_assert(ShortyFieldTypeTraits::IsPrimitiveWideType<decltype(result)>(),
+                "result must be a primitive wide type");
+  switch (variable_type) {
+    case ShortyFieldType::kLong:
+      CopyUnsafeAtOffset<int64_t>(offset, &result);
+      break;
+    case ShortyFieldType::kDouble:
+      CopyUnsafeAtOffset<double>(offset, &result);
+      break;
+    default:
+      LOG(FATAL)
+          << "expected a valid primitive wide shorty type but got "
+          << static_cast<char>(variable_type);
+      UNREACHABLE();
+  }
+
+  return result;
+}
+
+mirror::Object* Closure::GetCapturedObject(size_t index) const {
+  DCHECK(GetCapturedShortyType(index).IsObject());
+
+  ShortyFieldType variable_type;
+  size_t offset;
+  GetCapturedVariableTypeAndOffset(index, &variable_type, &offset);
+
+  // TODO: Restructure to use template specialization, e.g. GetCapturedPrimitive<T>
+  // so that we can avoid this nonsense regarding memcpy always overflowing.
+  // Plus, this additional switching seems redundant since the interpreter
+  // would've done it already, and knows the exact type.
+  mirror::Object* result = nullptr;
+  static_assert(ShortyFieldTypeTraits::IsObjectType<decltype(result)>(),
+                "result must be an object type");
+  switch (variable_type) {
+    case ShortyFieldType::kObject:
+      // TODO: This seems unsafe. This may need to use gcroots.
+      static_assert(kClosureSupportsGarbageCollection == false,
+                    "May need GcRoots and definitely need mutator locks");
+      {
+        mirror::CompressedReference<mirror::Object> compressed_result;
+        CopyUnsafeAtOffset<uint32_t>(offset, &compressed_result);
+        result = compressed_result.AsMirrorPtr();
+      }
+      break;
+    default:
+      CHECK(false)
+          << "expected a valid shorty type but got " << static_cast<char>(variable_type);
+      UNREACHABLE();
+  }
+
+  return result;
+}
+
+size_t Closure::GetCapturedClosureSize(size_t index) const {
+  DCHECK(GetCapturedShortyType(index).IsLambda());
+  size_t offset = GetCapturedVariableOffset(index);
+
+  auto* captured_ptr = reinterpret_cast<const uint8_t*>(&captured_);
+  size_t closure_size = GetClosureSize(captured_ptr + offset);
+
+  return closure_size;
+}
+
+void Closure::CopyCapturedClosure(size_t index, void* destination, size_t destination_room) const {
+  DCHECK(GetCapturedShortyType(index).IsLambda());
+  size_t offset = GetCapturedVariableOffset(index);
+
+  auto* captured_ptr = reinterpret_cast<const uint8_t*>(&captured_);
+  size_t closure_size = GetClosureSize(captured_ptr + offset);
+
+  static_assert(ShortyFieldTypeTraits::IsLambdaType<Closure*>(),
+                "result must be a lambda type");
+
+  CopyUnsafeAtOffset<Closure>(offset, destination, closure_size, destination_room);
+}
+
+size_t Closure::GetCapturedVariableOffset(size_t index) const {
+  VariableInfo variable_info =
+      ParseTypeDescriptor<VariableInfo::kOffset>(GetCapturedVariablesTypeDescriptor(),
+                                                 index);
+
+  size_t offset = variable_info.offset_;
+
+  return offset;
+}
+
+void Closure::GetCapturedVariableTypeAndOffset(size_t index,
+                                               ShortyFieldType* out_type,
+                                               size_t* out_offset) const {
+  DCHECK(out_type != nullptr);
+  DCHECK(out_offset != nullptr);
+
+  static constexpr const VariableInfo::Flags kVariableTypeAndOffset =
+      static_cast<VariableInfo::Flags>(VariableInfo::kVariableType | VariableInfo::kOffset);
+  VariableInfo variable_info =
+      ParseTypeDescriptor<kVariableTypeAndOffset>(GetCapturedVariablesTypeDescriptor(),
+                                                  index);
+
+  ShortyFieldType variable_type = variable_info.variable_type_;
+  size_t offset = variable_info.offset_;
+
+  *out_type = variable_type;
+  *out_offset = offset;
+}
+
+template <typename T>
+void Closure::CopyUnsafeAtOffset(size_t offset,
+                                 void* destination,
+                                 size_t src_size,
+                                 size_t destination_room) const {
+  DCHECK_GE(destination_room, src_size);
+  const uint8_t* data_ptr = GetUnsafeAtOffset<T>(offset);
+  memcpy(destination, data_ptr, sizeof(T));
+}
+
+// TODO: This is kind of ugly. I would prefer an unaligned_ptr<Closure> here.
+// Unfortunately C++ doesn't let you lower the alignment (i.e. alignas(1) Closure*) is not legal.
+size_t Closure::GetClosureSize(const uint8_t* closure) {
+  DCHECK(closure != nullptr);
+
+  static_assert(!std::is_base_of<mirror::Object, Closure>::value,
+                "It might be unsafe to call memcpy on a managed object");
+
+  // Safe as long as it's not a mirror Object.
+  // TODO: Should probably wrap this in like MemCpyNative or some such which statically asserts
+  // we aren't trying to copy mirror::Object data around.
+  ArtLambdaMethod* closure_info;
+  memcpy(&closure_info, closure + offsetof(Closure, lambda_info_), sizeof(closure_info));
+
+  if (LIKELY(closure_info->IsStaticSize())) {
+    return closure_info->GetStaticClosureSize();
+  }
+
+  // The size is dynamic, so we need to read it from captured_variables_ portion.
+  size_t dynamic_size;
+  memcpy(&dynamic_size,
+         closure + offsetof(Closure, captured_[0].dynamic_.size_),
+         sizeof(dynamic_size));
+  static_assert(sizeof(dynamic_size) == sizeof(captured_[0].dynamic_.size_),
+                "Dynamic size type must match the structural type of the size");
+
+  DCHECK_GE(dynamic_size, closure_info->GetStaticClosureSize());
+  return dynamic_size;
+}
+
+size_t Closure::GetStartingOffset() const {
+  static constexpr const size_t captured_offset = offsetof(Closure, captured_);
+  if (LIKELY(lambda_info_->IsStaticSize())) {
+    return offsetof(Closure, captured_[0].static_variables_) - captured_offset;
+  } else {
+    return offsetof(Closure, captured_[0].dynamic_.variables_) - captured_offset;
+  }
+}
+
+}  // namespace lambda
+}  // namespace art
diff --git a/runtime/lambda/closure.h b/runtime/lambda/closure.h
new file mode 100644
index 0000000..60d117e
--- /dev/null
+++ b/runtime/lambda/closure.h
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2015 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_LAMBDA_CLOSURE_H_
+#define ART_RUNTIME_LAMBDA_CLOSURE_H_
+
+#include "base/macros.h"
+#include "base/mutex.h"  // For Locks::mutator_lock_.
+#include "lambda/shorty_field_type.h"
+
+#include <stdint.h>
+
+namespace art {
+class ArtMethod;  // forward declaration
+
+namespace mirror {
+class Object;  // forward declaration
+}  // namespace mirror
+
+namespace lambda {
+class ArtLambdaMethod;  // forward declaration
+class ClosureBuilder;   // forward declaration
+
+// Inline representation of a lambda closure.
+// Contains the target method and the set of packed captured variables as a copy.
+//
+// The closure itself is logically immutable, although in practice any object references
+// it (recursively) contains can be moved and updated by the GC.
+struct PACKED(sizeof(ArtLambdaMethod*)) Closure {
+  // Get the size of the Closure in bytes.
+  // This is necessary in order to allocate a large enough area to copy the Closure into.
+  // Do *not* copy the closure with memcpy, since references also need to get moved.
+  size_t GetSize() const;
+
+  // Copy this closure into the target, whose memory size is specified by target_size.
+  // Any object references are fixed up during the copy (if there was a read barrier).
+  // The target_size must be at least as large as GetSize().
+  void CopyTo(void* target, size_t target_size) const;
+
+  // How many variables were captured?
+  size_t GetNumberOfCapturedVariables() const;
+
+  // Returns a type descriptor string that represents each captured variable.
+  // e.g. "Ljava/lang/Object;ZB" would mean a capture tuple of (Object, boolean, byte)
+  const char* GetCapturedVariablesTypeDescriptor() const;
+
+  // Returns the short type for the captured variable at index.
+  // Index must be less than the number of captured variables.
+  ShortyFieldType GetCapturedShortyType(size_t index) const;
+
+  // Returns the 32-bit representation of a non-wide primitive at the captured variable index.
+  // Smaller types are zero extended.
+  // Index must be less than the number of captured variables.
+  uint32_t GetCapturedPrimitiveNarrow(size_t index) const;
+  // Returns the 64-bit representation of a wide primitive at the captured variable index.
+  // Smaller types are zero extended.
+  // Index must be less than the number of captured variables.
+  uint64_t GetCapturedPrimitiveWide(size_t index) const;
+  // Returns the object reference at the captured variable index.
+  // The type at the index *must* be an object reference or a CHECK failure will occur.
+  // Index must be less than the number of captured variables.
+  mirror::Object* GetCapturedObject(size_t index) const SHARED_REQUIRES(Locks::mutator_lock_);
+
+  // Gets the size of a nested capture closure in bytes, at the captured variable index.
+  // The type at the index *must* be a lambda closure or a CHECK failure will occur.
+  size_t GetCapturedClosureSize(size_t index) const;
+
+  // Copies a nested lambda closure at the captured variable index.
+  // The destination must have enough room for the closure (see GetCapturedClosureSize).
+  void CopyCapturedClosure(size_t index, void* destination, size_t destination_room) const;
+
+ private:
+  // Read out any non-lambda value as a copy.
+  template <typename T>
+  T GetCapturedVariable(size_t index) const;
+
+  // Reconstruct the closure's captured variable info at runtime.
+  struct VariableInfo {
+    size_t index_;
+    ShortyFieldType variable_type_;
+    size_t offset_;
+    size_t count_;
+
+    enum Flags {
+      kIndex = 0x1,
+      kVariableType = 0x2,
+      kOffset = 0x4,
+      kCount = 0x8,
+    };
+
+    // Traverse to the end of the type descriptor list instead of stopping at some particular index.
+    static constexpr size_t kUpToIndexMax = static_cast<size_t>(-1);
+  };
+
+  // Parse a type descriptor, stopping at index "upto_index".
+  // Returns only the information requested in flags. All other fields are indeterminate.
+  template <VariableInfo::Flags flags>
+  inline VariableInfo ALWAYS_INLINE ParseTypeDescriptor(const char* type_descriptor,
+                                                        size_t upto_index) const;
+
+  // Convenience function to call ParseTypeDescriptor with just the type and offset.
+  void GetCapturedVariableTypeAndOffset(size_t index,
+                                        ShortyFieldType* out_type,
+                                        size_t* out_offset) const;
+
+  // How many bytes do the captured variables take up? Runtime sizeof(captured_variables).
+  size_t GetCapturedVariablesSize() const;
+  // Get the size in bytes of the variable_type which is potentially stored at offset.
+  size_t GetCapturedVariableSize(ShortyFieldType variable_type, size_t offset) const;
+  // Get the starting offset (in bytes) for the 0th captured variable.
+  // All offsets are relative to 'captured_'.
+  size_t GetStartingOffset() const;
+  // Get the offset for this index.
+  // All offsets are relative to 'captuerd_'.
+  size_t GetCapturedVariableOffset(size_t index) const;
+
+  // Cast the data at '(char*)captured_[offset]' into T, returning its address.
+  // This value should not be de-referenced directly since its unaligned.
+  template <typename T>
+  inline const uint8_t* GetUnsafeAtOffset(size_t offset) const;
+
+  // Copy the data at the offset into the destination. DCHECKs that
+  // the destination_room is large enough (in bytes) to fit the data.
+  template <typename T>
+  inline void CopyUnsafeAtOffset(size_t offset,
+                                 void* destination,
+                                 size_t src_size = sizeof(T),
+                                 size_t destination_room = sizeof(T)) const;
+
+  // Get the closure size from an unaligned (i.e. interior) closure pointer.
+  static size_t GetClosureSize(const uint8_t* closure);
+
+  ///////////////////////////////////////////////////////////////////////////////////
+
+  // Compile-time known lambda information such as the type descriptor and size.
+  ArtLambdaMethod* lambda_info_;
+
+  // A contiguous list of captured variables, and possibly the closure size.
+  // The runtime size can always be determined through GetSize().
+  union {
+    // Read from here if the closure size is static (ArtLambdaMethod::IsStatic)
+    uint8_t static_variables_[0];
+    struct {
+      // Read from here if the closure size is dynamic (ArtLambdaMethod::IsDynamic)
+      size_t size_;  // The lambda_info_ and the size_ itself is also included as part of the size.
+      uint8_t variables_[0];
+    } dynamic_;
+  } captured_[0];
+  // captured_ will always consist of one array element at runtime.
+  // Set to [0] so that 'size_' is not counted in sizeof(Closure).
+
+  friend class ClosureBuilder;
+  friend class ClosureTest;
+};
+
+}  // namespace lambda
+}  // namespace art
+
+#endif  // ART_RUNTIME_LAMBDA_CLOSURE_H_
diff --git a/runtime/lambda/closure_builder-inl.h b/runtime/lambda/closure_builder-inl.h
new file mode 100644
index 0000000..41a803b
--- /dev/null
+++ b/runtime/lambda/closure_builder-inl.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2015 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_LAMBDA_CLOSURE_BUILDER_INL_H_
+#define ART_RUNTIME_LAMBDA_CLOSURE_BUILDER_INL_H_
+
+#include "lambda/closure_builder.h"
+#include <string.h>
+
+namespace art {
+namespace lambda {
+
+template <typename T, ClosureBuilder::ShortyTypeEnum kShortyType>
+void ClosureBuilder::CaptureVariablePrimitive(T value) {
+  static_assert(ShortyFieldTypeTraits::IsPrimitiveType<T>(), "T must be a primitive type");
+  const size_t type_size = ShortyFieldType(kShortyType).GetStaticSize();
+  DCHECK_EQ(type_size, sizeof(T));
+
+  // Copy the data while retaining the bit pattern. Strict-aliasing safe.
+  ShortyFieldTypeTraits::MaxType value_storage = 0;
+  memcpy(&value_storage, &value, sizeof(T));
+
+  values_.push_back(value_storage);
+  size_ += sizeof(T);
+}
+
+}  // namespace lambda
+}  // namespace art
+
+#endif  // ART_RUNTIME_LAMBDA_CLOSURE_BUILDER_INL_H_
diff --git a/runtime/lambda/closure_builder.cc b/runtime/lambda/closure_builder.cc
new file mode 100644
index 0000000..9c37db8
--- /dev/null
+++ b/runtime/lambda/closure_builder.cc
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+#include "lambda/closure_builder.h"
+
+#include "base/macros.h"
+#include "base/value_object.h"
+#include "lambda/art_lambda_method.h"
+#include "lambda/closure.h"
+#include "lambda/shorty_field_type.h"
+#include "runtime/mirror/object_reference.h"
+
+#include <stdint.h>
+#include <vector>
+
+namespace art {
+namespace lambda {
+
+/*
+ * GC support TODOs:
+ * (Although there's some code for storing objects, it is UNIMPLEMENTED(FATAL) because it is
+ * incomplete).
+ *
+ * 1) GC needs to be able to traverse the Closure and visit any references.
+ *    It might be possible to get away with global roots in the short term.
+ *
+ * 2) Add brooks read barrier support. We can store the black/gray/white bits
+ *    in the lower 2 bits of the lambda art method pointer. Whenever a closure is copied
+ *    [to the stack] we'd need to add a cold path to turn it black.
+ *    (since there's only 3 colors, I can use the 4th value to indicate no-refs).
+ *    e.g. 0x0 = gray, 0x1 = white, 0x2 = black, 0x3 = no-nested-references
+ *    - Alternatively the GC can mark reference-less closures as always-black,
+ *      although it would need extra work to check for references.
+ */
+
+void ClosureBuilder::CaptureVariableObject(mirror::Object* object) {
+  auto compressed_reference = mirror::CompressedReference<mirror::Object>::FromMirrorPtr(object);
+  ShortyFieldTypeTraits::MaxType storage = 0;
+
+  static_assert(sizeof(storage) >= sizeof(compressed_reference),
+                "not enough room to store a compressed reference");
+  memcpy(&storage, &compressed_reference, sizeof(compressed_reference));
+
+  values_.push_back(storage);
+  size_ += kObjectReferenceSize;
+
+  static_assert(kObjectReferenceSize == sizeof(compressed_reference), "reference size mismatch");
+
+  // TODO: needs more work to support concurrent GC
+  if (kIsDebugBuild) {
+    if (kUseReadBarrier) {
+      UNIMPLEMENTED(FATAL) << "can't yet safely capture objects with read barrier";
+    }
+  }
+}
+
+void ClosureBuilder::CaptureVariableLambda(Closure* closure) {
+  DCHECK(closure != nullptr);  // null closures not allowed, target method must be null instead.
+  values_.push_back(reinterpret_cast<ShortyFieldTypeTraits::MaxType>(closure));
+
+  if (LIKELY(is_dynamic_size_ == false)) {
+    // Write in the extra bytes to store the dynamic size the first time.
+    is_dynamic_size_ = true;
+    size_ += sizeof(Closure::captured_[0].dynamic_.size_);
+  }
+
+  // A closure may be sized dynamically, so always query it for the true size.
+  size_ += closure->GetSize();
+}
+
+size_t ClosureBuilder::GetSize() const {
+  return size_;
+}
+
+size_t ClosureBuilder::GetCaptureCount() const {
+  return values_.size();
+}
+
+Closure* ClosureBuilder::CreateInPlace(void* memory, ArtLambdaMethod* target_method) const {
+  DCHECK(memory != nullptr);
+  DCHECK(target_method != nullptr);
+  DCHECK_EQ(is_dynamic_size_, target_method->IsDynamicSize());
+
+  CHECK_EQ(target_method->GetNumberOfCapturedVariables(), values_.size())
+    << "number of variables captured at runtime does not match "
+    << "number of variables captured at compile time";
+
+  Closure* closure = new (memory) Closure;
+  closure->lambda_info_ = target_method;
+
+  static_assert(offsetof(Closure, captured_) == kInitialSize, "wrong initial size");
+
+  size_t written_size;
+  if (UNLIKELY(is_dynamic_size_)) {
+    // The closure size must be set dynamically (i.e. nested lambdas).
+    closure->captured_[0].dynamic_.size_ = GetSize();
+    size_t header_size = offsetof(Closure, captured_[0].dynamic_.variables_);
+    DCHECK_LE(header_size, GetSize());
+    size_t variables_size = GetSize() - header_size;
+    written_size =
+        WriteValues(target_method,
+                    closure->captured_[0].dynamic_.variables_,
+                    header_size,
+                    variables_size);
+  } else {
+    // The closure size is known statically (i.e. no nested lambdas).
+    DCHECK(GetSize() == target_method->GetStaticClosureSize());
+    size_t header_size = offsetof(Closure, captured_[0].static_variables_);
+    DCHECK_LE(header_size, GetSize());
+    size_t variables_size = GetSize() - header_size;
+    written_size =
+        WriteValues(target_method,
+                    closure->captured_[0].static_variables_,
+                    header_size,
+                    variables_size);
+  }
+
+  DCHECK_EQ(written_size, closure->GetSize());
+
+  return closure;
+}
+
+size_t ClosureBuilder::WriteValues(ArtLambdaMethod* target_method,
+                                   uint8_t variables[],
+                                   size_t header_size,
+                                   size_t variables_size) const {
+  size_t total_size = header_size;
+  const char* shorty_types = target_method->GetCapturedVariablesShortyTypeDescriptor();
+
+  size_t variables_offset = 0;
+  size_t remaining_size = variables_size;
+
+  const size_t shorty_count = target_method->GetNumberOfCapturedVariables();
+  for (size_t i = 0; i < shorty_count; ++i) {
+    ShortyFieldType shorty{shorty_types[i]};  // NOLINT [readability/braces] [4]
+
+    size_t var_size;
+    if (LIKELY(shorty.IsStaticSize())) {
+      // TODO: needs more work to support concurrent GC, e.g. read barriers
+      if (kUseReadBarrier == false) {
+        if (UNLIKELY(shorty.IsObject())) {
+          UNIMPLEMENTED(FATAL) << "can't yet safely write objects with read barrier";
+        }
+      } else {
+        if (UNLIKELY(shorty.IsObject())) {
+          UNIMPLEMENTED(FATAL) << "writing objects not yet supported, no GC support";
+        }
+      }
+
+      var_size = shorty.GetStaticSize();
+      DCHECK_LE(var_size, sizeof(values_[i]));
+
+      // Safe even for objects (non-read barrier case) if we never suspend
+      // while the ClosureBuilder is live.
+      // FIXME: Need to add GC support for references in a closure.
+      memcpy(&variables[variables_offset], &values_[i], var_size);
+    } else {
+      DCHECK(shorty.IsLambda())
+          << " don't support writing dynamically sized types other than lambda";
+
+      ShortyFieldTypeTraits::MaxType closure_raw = values_[i];
+      Closure* nested_closure = reinterpret_cast<Closure*>(closure_raw);
+
+      DCHECK(nested_closure != nullptr);
+      nested_closure->CopyTo(&variables[variables_offset], remaining_size);
+
+      var_size = nested_closure->GetSize();
+    }
+
+    total_size += var_size;
+    DCHECK_GE(remaining_size, var_size);
+    remaining_size -= var_size;
+
+    variables_offset += var_size;
+  }
+
+  DCHECK_EQ('\0', shorty_types[shorty_count]);
+  DCHECK_EQ(variables_offset, variables_size);
+
+  return total_size;
+}
+
+
+}  // namespace lambda
+}  // namespace art
diff --git a/runtime/lambda/closure_builder.h b/runtime/lambda/closure_builder.h
new file mode 100644
index 0000000..542e12a
--- /dev/null
+++ b/runtime/lambda/closure_builder.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2015 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_LAMBDA_CLOSURE_BUILDER_H_
+#define ART_RUNTIME_LAMBDA_CLOSURE_BUILDER_H_
+
+#include "base/macros.h"
+#include "base/mutex.h"  // For Locks::mutator_lock_.
+#include "base/value_object.h"
+#include "lambda/shorty_field_type.h"
+
+#include <stdint.h>
+#include <vector>
+
+namespace art {
+class ArtMethod;  // forward declaration
+
+namespace mirror {
+class Object;  // forward declaration
+}  // namespace mirror
+
+namespace lambda {
+class ArtLambdaMethod;  // forward declaration
+
+// Build a closure by capturing variables one at a time.
+// When all variables have been marked captured, the closure can be created in-place into
+// a target memory address.
+//
+// The mutator lock must be held for the duration of the lifetime of this object,
+// since it needs to temporarily store heap references into an internal list.
+class ClosureBuilder : ValueObject {
+ public:
+  using ShortyTypeEnum = decltype(ShortyFieldType::kByte);
+
+
+  // Mark this primitive value to be captured as the specified type.
+  template <typename T, ShortyTypeEnum kShortyType>
+  void CaptureVariablePrimitive(T value);
+
+  // Mark this object reference to be captured.
+  void CaptureVariableObject(mirror::Object* object) SHARED_REQUIRES(Locks::mutator_lock_);
+
+  // Mark this lambda closure to be captured.
+  void CaptureVariableLambda(Closure* closure);
+
+  // Get the size (in bytes) of the closure.
+  // This size is used to be able to allocate memory large enough to write the closure into.
+  // Call 'CreateInPlace' to actually write the closure out.
+  size_t GetSize() const;
+
+  // Returns how many variables have been captured so far.
+  size_t GetCaptureCount() const;
+
+  // Creates a closure in-place and writes out the data into 'memory'.
+  // Memory must be at least 'GetSize' bytes large.
+  // All previously marked data to be captured is now written out.
+  Closure* CreateInPlace(void* memory, ArtLambdaMethod* target_method) const
+      SHARED_REQUIRES(Locks::mutator_lock_);
+
+  // Locks need to be held for entire lifetime of ClosureBuilder.
+  ClosureBuilder() SHARED_REQUIRES(Locks::mutator_lock_)
+  {}
+
+  // Locks need to be held for entire lifetime of ClosureBuilder.
+  ~ClosureBuilder() SHARED_REQUIRES(Locks::mutator_lock_)
+  {}
+
+ private:
+  // Initial size a closure starts out before any variables are written.
+  // Header size only.
+  static constexpr size_t kInitialSize = sizeof(ArtLambdaMethod*);
+
+  // Write a Closure's variables field from the captured variables.
+  // variables_size specified in bytes, and only includes enough room to write variables into.
+  // Returns the calculated actual size of the closure.
+  size_t WriteValues(ArtLambdaMethod* target_method,
+                     uint8_t variables[],
+                     size_t header_size,
+                     size_t variables_size) const SHARED_REQUIRES(Locks::mutator_lock_);
+
+  size_t size_ = kInitialSize;
+  bool is_dynamic_size_ = false;
+  std::vector<ShortyFieldTypeTraits::MaxType> values_;
+};
+
+}  // namespace lambda
+}  // namespace art
+
+#endif  // ART_RUNTIME_LAMBDA_CLOSURE_BUILDER_H_
diff --git a/runtime/lambda/closure_test.cc b/runtime/lambda/closure_test.cc
new file mode 100644
index 0000000..7c1bd0d
--- /dev/null
+++ b/runtime/lambda/closure_test.cc
@@ -0,0 +1,356 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#include "art_method.h"
+#include "lambda/art_lambda_method.h"
+#include "lambda/closure.h"
+#include "lambda/closure_builder.h"
+#include "lambda/closure_builder-inl.h"
+#include "utils.h"
+
+#include <numeric>
+#include <stdint.h>
+#include <type_traits>
+#include "gtest/gtest.h"
+
+// Turn this on for some extra printfs to help with debugging, since some code is optimized out.
+static constexpr const bool kDebuggingClosureTest = true;
+
+namespace std {
+  using Closure = art::lambda::Closure;
+
+  // Specialize std::default_delete so it knows how to properly delete closures
+  // through the way we allocate them in this test.
+  //
+  // This is test-only because we don't want the rest of Art to do this.
+  template <>
+  struct default_delete<Closure> {
+    void operator()(Closure* closure) const {
+      delete[] reinterpret_cast<char*>(closure);
+    }
+  };
+}  // namespace std
+
+namespace art {
+
+// Fake lock acquisition to please clang lock checker.
+// This doesn't actually acquire any locks because we don't need multiple threads in this gtest.
+struct SCOPED_CAPABILITY ScopedFakeLock {
+  explicit ScopedFakeLock(MutatorMutex& mu) ACQUIRE(mu)
+      : mu_(mu) {
+  }
+
+  ~ScopedFakeLock() RELEASE()
+  {}
+
+  MutatorMutex& mu_;
+};
+
+namespace lambda {
+
+class ClosureTest : public ::testing::Test {
+ public:
+  ClosureTest() = default;
+  ~ClosureTest() = default;
+
+ protected:
+  static void SetUpTestCase() {
+  }
+
+  virtual void SetUp() {
+    // Create a completely dummy method here.
+    // It's "OK" because the Closure never needs to look inside of the ArtMethod
+    // (it just needs to be non-null).
+    uintptr_t ignore = 0xbadbad;
+    fake_method_ = reinterpret_cast<ArtMethod*>(ignore);
+  }
+
+  static ::testing::AssertionResult IsResultSuccessful(bool result) {
+    if (result) {
+      return ::testing::AssertionSuccess();
+    } else {
+      return ::testing::AssertionFailure();
+    }
+  }
+
+  // Create a closure that captures the static variables from 'args' by-value.
+  // The lambda method's captured variables types must match the ones in 'args'.
+  // -- This creates the closure directly in-memory by using memcpy.
+  template <typename ... Args>
+  static std::unique_ptr<Closure> CreateClosureStaticVariables(ArtLambdaMethod* lambda_method,
+                                                               Args&& ... args) {
+    constexpr size_t header_size = sizeof(ArtLambdaMethod*);
+    const size_t static_size = GetArgsSize(args ...) + header_size;
+    EXPECT_GE(static_size, sizeof(Closure));
+
+    // Can't just 'new' the Closure since we don't know the size up front.
+    char* closure_as_char_array = new char[static_size];
+    Closure* closure_ptr = new (closure_as_char_array) Closure;
+
+    // Set up the data
+    closure_ptr->lambda_info_ = lambda_method;
+    CopyArgs(closure_ptr->captured_[0].static_variables_, args ...);
+
+    // Make sure the entire thing is deleted once the unique_ptr goes out of scope.
+    return std::unique_ptr<Closure>(closure_ptr);  // NOLINT [whitespace/braces] [5]
+  }
+
+  // Copy variadic arguments into the destination array with memcpy.
+  template <typename T, typename ... Args>
+  static void CopyArgs(uint8_t destination[], T&& arg, Args&& ... args) {
+    memcpy(destination, &arg, sizeof(arg));
+    CopyArgs(destination + sizeof(arg), args ...);
+  }
+
+  // Base case: Done.
+  static void CopyArgs(uint8_t destination[]) {
+    UNUSED(destination);
+  }
+
+  // Create a closure that captures the static variables from 'args' by-value.
+  // The lambda method's captured variables types must match the ones in 'args'.
+  // -- This uses ClosureBuilder interface to set up the closure indirectly.
+  template <typename ... Args>
+  static std::unique_ptr<Closure> CreateClosureStaticVariablesFromBuilder(
+      ArtLambdaMethod* lambda_method,
+      Args&& ... args) {
+    // Acquire a fake lock since closure_builder needs it.
+    ScopedFakeLock fake_lock(*Locks::mutator_lock_);
+
+    ClosureBuilder closure_builder;
+    CaptureVariableFromArgsList(/*out*/closure_builder, args ...);
+
+    EXPECT_EQ(sizeof...(args), closure_builder.GetCaptureCount());
+
+    constexpr size_t header_size = sizeof(ArtLambdaMethod*);
+    const size_t static_size = GetArgsSize(args ...) + header_size;
+    EXPECT_GE(static_size, sizeof(Closure));
+
+    // For static variables, no nested closure, so size must match exactly.
+    EXPECT_EQ(static_size, closure_builder.GetSize());
+
+    // Can't just 'new' the Closure since we don't know the size up front.
+    char* closure_as_char_array = new char[static_size];
+    Closure* closure_ptr = new (closure_as_char_array) Closure;
+
+    // The closure builder packs the captured variables into a Closure.
+    closure_builder.CreateInPlace(closure_ptr, lambda_method);
+
+    // Make sure the entire thing is deleted once the unique_ptr goes out of scope.
+    return std::unique_ptr<Closure>(closure_ptr);  // NOLINT [whitespace/braces] [5]
+  }
+
+  // Call the correct ClosureBuilder::CaptureVariableXYZ function based on the type of args.
+  // Invokes for each arg in args.
+  template <typename ... Args>
+  static void CaptureVariableFromArgsList(/*out*/ClosureBuilder& closure_builder, Args ... args) {
+    int ignore[] = {
+        (CaptureVariableFromArgs(/*out*/closure_builder, args),0)...  // NOLINT [whitespace/comma] [3]
+    };
+    UNUSED(ignore);
+  }
+
+  // ClosureBuilder::CaptureVariablePrimitive for types that are primitive only.
+  template <typename T>
+  typename std::enable_if<ShortyFieldTypeTraits::IsPrimitiveType<T>()>::type
+  static CaptureVariableFromArgs(/*out*/ClosureBuilder& closure_builder, T value) {
+    static_assert(ShortyFieldTypeTraits::IsPrimitiveType<T>(), "T must be a shorty primitive");
+    closure_builder.CaptureVariablePrimitive<T, ShortyFieldTypeSelectEnum<T>::value>(value);
+  }
+
+  // ClosureBuilder::CaptureVariableObject for types that are objects only.
+  template <typename T>
+  typename std::enable_if<ShortyFieldTypeTraits::IsObjectType<T>()>::type
+  static CaptureVariableFromArgs(/*out*/ClosureBuilder& closure_builder, const T* object) {
+    ScopedFakeLock fake_lock(*Locks::mutator_lock_);
+    closure_builder.CaptureVariableObject(object);
+  }
+
+  // Sum of sizeof(Args...).
+  template <typename T, typename ... Args>
+  static constexpr size_t GetArgsSize(T&& arg, Args&& ... args) {
+    return sizeof(arg) + GetArgsSize(args ...);
+  }
+
+  // Base case: Done.
+  static constexpr size_t GetArgsSize() {
+    return 0;
+  }
+
+  // Take "U" and memcpy it into a "T". T starts out as (T)0.
+  template <typename T, typename U>
+  static T ExpandingBitCast(const U& val) {
+    static_assert(sizeof(T) >= sizeof(U), "U too large");
+    T new_val = static_cast<T>(0);
+    memcpy(&new_val, &val, sizeof(U));
+    return new_val;
+  }
+
+  // Templatized extraction from closures by checking their type with enable_if.
+  template <typename T>
+  static typename std::enable_if<ShortyFieldTypeTraits::IsPrimitiveNarrowType<T>()>::type
+  ExpectCapturedVariable(const Closure* closure, size_t index, T value) {
+    EXPECT_EQ(ExpandingBitCast<uint32_t>(value), closure->GetCapturedPrimitiveNarrow(index))
+        << " with index " << index;
+  }
+
+  template <typename T>
+  static typename std::enable_if<ShortyFieldTypeTraits::IsPrimitiveWideType<T>()>::type
+  ExpectCapturedVariable(const Closure* closure, size_t index, T value) {
+    EXPECT_EQ(ExpandingBitCast<uint64_t>(value), closure->GetCapturedPrimitiveWide(index))
+        << " with index " << index;
+  }
+
+  // Templatized SFINAE for Objects so we can get better error messages.
+  template <typename T>
+  static typename std::enable_if<ShortyFieldTypeTraits::IsObjectType<T>()>::type
+  ExpectCapturedVariable(const Closure* closure, size_t index, const T* object) {
+    EXPECT_EQ(object, closure->GetCapturedObject(index))
+        << " with index " << index;
+  }
+
+  template <typename ... Args>
+  void TestPrimitive(const char *descriptor, Args ... args) {
+    const char* shorty = descriptor;
+
+    SCOPED_TRACE(descriptor);
+
+    ASSERT_EQ(strlen(shorty), sizeof...(args))
+        << "test error: descriptor must have same # of types as the # of captured variables";
+
+    // Important: This fake lambda method needs to out-live any Closures we create with it.
+    ArtLambdaMethod lambda_method{fake_method_,                    // NOLINT [whitespace/braces] [5]
+                                  descriptor,                      // NOLINT [whitespace/blank_line] [2]
+                                  shorty,
+                                 };
+
+    std::unique_ptr<Closure> closure_a;
+    std::unique_ptr<Closure> closure_b;
+
+    // Test the closure twice when it's constructed in different ways.
+    {
+      // Create the closure in a "raw" manner, that is directly with memcpy
+      // since we know the underlying data format.
+      // This simulates how the compiler would lay out the data directly.
+      SCOPED_TRACE("raw closure");
+      std::unique_ptr<Closure> closure_raw = CreateClosureStaticVariables(&lambda_method, args ...);
+
+      if (kDebuggingClosureTest) {
+        std::cerr << "closure raw address: " << closure_raw.get() << std::endl;
+      }
+      TestPrimitiveWithClosure(closure_raw.get(), descriptor, shorty, args ...);
+      closure_a = std::move(closure_raw);
+    }
+
+    {
+      // Create the closure with the ClosureBuilder, which is done indirectly.
+      // This simulates how the interpreter would create the closure dynamically at runtime.
+      SCOPED_TRACE("closure from builder");
+      std::unique_ptr<Closure> closure_built =
+          CreateClosureStaticVariablesFromBuilder(&lambda_method, args ...);
+      if (kDebuggingClosureTest) {
+        std::cerr << "closure built address: " << closure_built.get() << std::endl;
+      }
+      TestPrimitiveWithClosure(closure_built.get(), descriptor, shorty, args ...);
+      closure_b = std::move(closure_built);
+    }
+
+    // The closures should be identical memory-wise as well.
+    EXPECT_EQ(closure_a->GetSize(), closure_b->GetSize());
+    EXPECT_TRUE(memcmp(closure_a.get(),
+                       closure_b.get(),
+                       std::min(closure_a->GetSize(), closure_b->GetSize())) == 0);
+  }
+
+  template <typename ... Args>
+  static void TestPrimitiveWithClosure(Closure* closure,
+                                       const char* descriptor,
+                                       const char* shorty,
+                                       Args ... args) {
+    EXPECT_EQ(sizeof(ArtLambdaMethod*) + GetArgsSize(args...), closure->GetSize());
+    EXPECT_EQ(sizeof...(args), closure->GetNumberOfCapturedVariables());
+    EXPECT_STREQ(descriptor, closure->GetCapturedVariablesTypeDescriptor());
+    TestPrimitiveExpects(closure, shorty, /*index*/0, args ...);
+  }
+
+  // Call EXPECT_EQ for each argument in the closure's #GetCapturedX.
+  template <typename T, typename ... Args>
+  static void TestPrimitiveExpects(
+      const Closure* closure, const char* shorty, size_t index, T arg, Args ... args) {
+    ASSERT_EQ(ShortyFieldType(shorty[index]).GetStaticSize(), sizeof(T))
+        << "Test error: Type mismatch at index " << index;
+    ExpectCapturedVariable(closure, index, arg);
+    EXPECT_EQ(ShortyFieldType(shorty[index]), closure->GetCapturedShortyType(index));
+    TestPrimitiveExpects(closure, shorty, index + 1, args ...);
+  }
+
+  // Base case for EXPECT_EQ.
+  static void TestPrimitiveExpects(const Closure* closure, const char* shorty, size_t index) {
+    UNUSED(closure, shorty, index);
+  }
+
+  ArtMethod* fake_method_;
+};
+
+TEST_F(ClosureTest, TestTrivial) {
+  ArtLambdaMethod lambda_method{fake_method_,                    // NOLINT [whitespace/braces] [5]
+                                "",  // No captured variables    // NOLINT [whitespace/blank_line] [2]
+                                "",  // No captured variables
+                               };
+
+  std::unique_ptr<Closure> closure = CreateClosureStaticVariables(&lambda_method);
+
+  EXPECT_EQ(sizeof(ArtLambdaMethod*), closure->GetSize());
+  EXPECT_EQ(0u, closure->GetNumberOfCapturedVariables());
+}  // TEST_F
+
+TEST_F(ClosureTest, TestPrimitiveSingle) {
+  TestPrimitive("Z", true);
+  TestPrimitive("B", int8_t(0xde));
+  TestPrimitive("C", uint16_t(0xbeef));
+  TestPrimitive("S", int16_t(0xdead));
+  TestPrimitive("I", int32_t(0xdeadbeef));
+  TestPrimitive("F", 0.123f);
+  TestPrimitive("J", int64_t(0xdeadbeef00c0ffee));
+  TestPrimitive("D", 123.456);
+}  // TEST_F
+
+TEST_F(ClosureTest, TestPrimitiveMany) {
+  TestPrimitive("ZZ", true, false);
+  TestPrimitive("ZZZ", true, false, true);
+  TestPrimitive("BBBB", int8_t(0xde), int8_t(0xa0), int8_t(0xff), int8_t(0xcc));
+  TestPrimitive("CC", uint16_t(0xbeef), uint16_t(0xdead));
+  TestPrimitive("SSSS", int16_t(0xdead), int16_t(0xc0ff), int16_t(0xf000), int16_t(0xbaba));
+  TestPrimitive("III", int32_t(0xdeadbeef), int32_t(0xc0ffee), int32_t(0xbeefdead));
+  TestPrimitive("FF", 0.123f, 555.666f);
+  TestPrimitive("JJJ", int64_t(0xdeadbeef00c0ffee), int64_t(0x123), int64_t(0xc0ffee));
+  TestPrimitive("DD", 123.456, 777.888);
+}  // TEST_F
+
+TEST_F(ClosureTest, TestPrimitiveMixed) {
+  TestPrimitive("ZZBBCCSSIIFFJJDD",
+                true, false,
+                int8_t(0xde), int8_t(0xa0),
+                uint16_t(0xbeef), uint16_t(0xdead),
+                int16_t(0xdead), int16_t(0xc0ff),
+                int32_t(0xdeadbeef), int32_t(0xc0ffee),
+                0.123f, 555.666f,
+                int64_t(0xdeadbeef00c0ffee), int64_t(0x123),
+                123.456, 777.888);
+}  // TEST_F
+
+}  // namespace lambda
+}  // namespace art
diff --git a/runtime/lambda/shorty_field_type.h b/runtime/lambda/shorty_field_type.h
new file mode 100644
index 0000000..46ddaa9
--- /dev/null
+++ b/runtime/lambda/shorty_field_type.h
@@ -0,0 +1,475 @@
+/*
+ * Copyright (C) 2015 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_LAMBDA_SHORTY_FIELD_TYPE_H_
+#define ART_RUNTIME_LAMBDA_SHORTY_FIELD_TYPE_H_
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/value_object.h"
+#include "globals.h"
+#include "runtime/primitive.h"
+
+#include <ostream>
+
+namespace art {
+
+namespace mirror {
+class Object;  // forward declaration
+}  // namespace mirror
+
+namespace lambda {
+
+struct Closure;  // forward declaration
+
+// TODO: Refactor together with primitive.h
+
+// The short form of a field type descriptor. Corresponds to ShortyFieldType in dex specification.
+// Only types usable by a field (and locals) are allowed (i.e. no void type).
+// Note that arrays and objects are treated both as 'L'.
+//
+// This is effectively a 'char' enum-like zero-cost type-safe wrapper with extra helper functions.
+struct ShortyFieldType : ValueObject {
+  // Use as if this was an enum class, e.g. 'ShortyFieldType::kBoolean'.
+  enum : char {
+    // Primitives (Narrow):
+    kBoolean = 'Z',
+    kByte = 'B',
+    kChar = 'C',
+    kShort = 'S',
+    kInt = 'I',
+    kFloat = 'F',
+    // Primitives (Wide):
+    kLong = 'J',
+    kDouble = 'D',
+    // Managed types:
+    kObject = 'L',  // This can also be an array (which is otherwise '[' in a non-shorty).
+    kLambda = '\\',
+  };  // NOTE: This is an anonymous enum so we can get exhaustive switch checking from the compiler.
+
+  // Implicitly construct from the enum above. Value must be one of the enum list members above.
+  // Always safe to use, does not do any DCHECKs.
+  inline constexpr ShortyFieldType(decltype(kByte) c) : value_(c) {
+  }
+
+  // Default constructor. The initial value is undefined. Initialize before calling methods.
+  // This is very unsafe but exists as a convenience to having undefined values.
+  explicit ShortyFieldType() : value_(StaticCastValue(0)) {
+  }
+
+  // Explicitly construct from a char. Value must be one of the enum list members above.
+  // Conversion is potentially unsafe, so DCHECKing is performed.
+  explicit inline ShortyFieldType(char c) : value_(StaticCastValue(c)) {
+    if (kIsDebugBuild) {
+      // Verify at debug-time that our conversion is safe.
+      ShortyFieldType ignored;
+      DCHECK(MaybeCreate(c, &ignored)) << "unknown shorty field type '" << c << "'";
+    }
+  }
+
+  // Attempts to parse the character in 'shorty_field_type' into its strongly typed version.
+  // Returns false if the character was out of range of the grammar.
+  static bool MaybeCreate(char shorty_field_type, ShortyFieldType* out) {
+    DCHECK(out != nullptr);
+    switch (shorty_field_type) {
+      case kBoolean:
+      case kByte:
+      case kChar:
+      case kShort:
+      case kInt:
+      case kFloat:
+      case kLong:
+      case kDouble:
+      case kObject:
+      case kLambda:
+        *out = ShortyFieldType(static_cast<decltype(kByte)>(shorty_field_type));
+        return true;
+      default:
+        break;
+    }
+
+    return false;
+  }
+
+  // Convert the first type in a field type descriptor string into a shorty.
+  // Arrays are converted into objects.
+  // Does not work for 'void' types (as they are illegal in a field type descriptor).
+  static ShortyFieldType CreateFromFieldTypeDescriptor(const char* field_type_descriptor) {
+    DCHECK(field_type_descriptor != nullptr);
+    char c = *field_type_descriptor;
+    if (UNLIKELY(c == kArray)) {  // Arrays are treated as object references.
+      c = kObject;
+    }
+    return ShortyFieldType{c};  // NOLINT [readability/braces] [4]
+  }
+
+  // Parse the first type in the field type descriptor string into a shorty.
+  // See CreateFromFieldTypeDescriptor for more details.
+  //
+  // Returns the pointer offset into the middle of the field_type_descriptor
+  // that would either point to the next shorty type, or to null if there are
+  // no more types.
+  //
+  // DCHECKs that each of the nested types is a valid shorty field type. This
+  // means the type descriptor must be already valid.
+  static const char* ParseFromFieldTypeDescriptor(const char* field_type_descriptor,
+                                                  ShortyFieldType* out_type) {
+    DCHECK(field_type_descriptor != nullptr);
+
+    if (UNLIKELY(field_type_descriptor[0] == '\0')) {
+      // Handle empty strings by immediately returning null.
+      return nullptr;
+    }
+
+    // All non-empty strings must be a valid list of field type descriptors, otherwise
+    // the DCHECKs will kick in and the program will crash.
+    const char shorter_type = *field_type_descriptor;
+
+    ShortyFieldType safe_type;
+    bool type_set = MaybeCreate(shorter_type, &safe_type);
+
+    // Lambda that keeps skipping characters until it sees ';'.
+    // Stops one character -after- the ';'.
+    auto skip_until_semicolon = [&field_type_descriptor]() {
+      while (*field_type_descriptor != ';' && *field_type_descriptor != '\0') {
+        ++field_type_descriptor;
+      }
+      DCHECK_NE(*field_type_descriptor, '\0')
+          << " type descriptor terminated too early: " << field_type_descriptor;
+      ++field_type_descriptor;  // Skip the ';'
+    };
+
+    ++field_type_descriptor;
+    switch (shorter_type) {
+      case kObject:
+        skip_until_semicolon();
+
+        DCHECK(type_set);
+        DCHECK(safe_type == kObject);
+        break;
+      case kArray:
+        // Strip out all of the leading [[[[[s, we don't care if it's a multi-dimensional array.
+        while (*field_type_descriptor == '[' && *field_type_descriptor != '\0') {
+          ++field_type_descriptor;
+        }
+        DCHECK_NE(*field_type_descriptor, '\0')
+            << " type descriptor terminated too early: " << field_type_descriptor;
+        // Either a primitive, object, or closure left. No more arrays.
+        {
+          // Now skip all the characters that form the array's interior-most element type
+          // (which itself is guaranteed not to be an array).
+          ShortyFieldType array_interior_type;
+          type_set = MaybeCreate(*field_type_descriptor, &array_interior_type);
+          DCHECK(type_set) << " invalid remaining type descriptor " << field_type_descriptor;
+
+          // Handle array-of-objects case like [[[[[LObject; and array-of-closures like [[[[[\Foo;
+          if (*field_type_descriptor == kObject || *field_type_descriptor == kLambda) {
+            skip_until_semicolon();
+          } else {
+            // Handle primitives which are exactly one character we can skip.
+            DCHECK(array_interior_type.IsPrimitive());
+            ++field_type_descriptor;
+          }
+        }
+
+        safe_type = kObject;
+        type_set = true;
+        break;
+      case kLambda:
+        skip_until_semicolon();
+
+        DCHECK(safe_type == kLambda);
+        DCHECK(type_set);
+        break;
+      default:
+        DCHECK_NE(kVoid, shorter_type) << "cannot make a ShortyFieldType from a void type";
+        break;
+    }
+
+    DCHECK(type_set) << "invalid shorty type descriptor " << shorter_type;
+
+    *out_type = safe_type;
+    return type_set ? field_type_descriptor : nullptr;
+  }
+
+  // Explicitly convert to a char.
+  inline explicit operator char() const {
+    return value_;
+  }
+
+  // Is this a primitive?
+  inline bool IsPrimitive() const {
+    return IsPrimitiveNarrow() || IsPrimitiveWide();
+  }
+
+  // Is this a narrow primitive (i.e. can fit into 1 virtual register)?
+  inline bool IsPrimitiveNarrow() const {
+    switch (value_) {
+      case kBoolean:
+      case kByte:
+      case kChar:
+      case kShort:
+      case kInt:
+      case kFloat:
+        return true;
+      default:
+        return false;
+    }
+  }
+
+  // Is this a wide primitive (i.e. needs exactly 2 virtual registers)?
+  inline bool IsPrimitiveWide() const {
+    switch (value_) {
+      case kLong:
+      case kDouble:
+        return true;
+      default:
+        return false;
+    }
+  }
+
+  // Is this an object reference (which can also be an array)?
+  inline bool IsObject() const {
+    return value_ == kObject;
+  }
+
+  // Is this a lambda?
+  inline bool IsLambda() const {
+    return value_ == kLambda;
+  }
+
+  // Is the size of this (to store inline as a field) always known at compile-time?
+  inline bool IsStaticSize() const {
+    return !IsLambda();
+  }
+
+  // Get the compile-time size (to be able to store it inline as a field or on stack).
+  // Dynamically-sized values such as lambdas return the guaranteed lower bound.
+  inline size_t GetStaticSize() const {
+    switch (value_) {
+      case kBoolean:
+        return sizeof(bool);
+      case kByte:
+        return sizeof(uint8_t);
+      case kChar:
+        return sizeof(int16_t);
+      case kShort:
+        return sizeof(uint16_t);
+      case kInt:
+        return sizeof(int32_t);
+      case kLong:
+        return sizeof(int64_t);
+      case kFloat:
+        return sizeof(float);
+      case kDouble:
+        return sizeof(double);
+      case kObject:
+        return kObjectReferenceSize;
+      case kLambda:
+        return sizeof(void*);  // Large enough to store the ArtLambdaMethod
+      default:
+        DCHECK(false) << "unknown shorty field type '" << static_cast<char>(value_) << "'";
+        UNREACHABLE();
+    }
+  }
+
+  // Implicitly convert to the anonymous nested inner type. Used for exhaustive switch detection.
+  inline operator decltype(kByte)() const {
+    return value_;
+  }
+
+  // Returns a read-only static string representing the enum name, useful for printing/debug only.
+  inline const char* ToString() const {
+    switch (value_) {
+      case kBoolean:
+        return "kBoolean";
+      case kByte:
+        return "kByte";
+      case kChar:
+        return "kChar";
+      case kShort:
+        return "kShort";
+      case kInt:
+        return "kInt";
+      case kLong:
+        return "kLong";
+      case kFloat:
+        return "kFloat";
+      case kDouble:
+        return "kDouble";
+      case kObject:
+        return "kObject";
+      case kLambda:
+        return "kLambda";
+      default:
+        // Undefined behavior if we get this far. Pray the compiler gods are merciful.
+        return "<undefined>";
+    }
+  }
+
+ private:
+  static constexpr const char kArray = '[';
+  static constexpr const char kVoid  = 'V';
+
+  // Helper to statically cast anything into our nested anonymous enum type.
+  template <typename T>
+  inline static decltype(kByte) StaticCastValue(const T& anything) {
+    return static_cast<decltype(value_)>(anything);
+  }
+
+  // The only field in this struct.
+  decltype(kByte) value_;
+};
+
+
+  // Print to an output stream.
+inline std::ostream& operator<<(std::ostream& ostream, ShortyFieldType shorty) {
+  return ostream << shorty.ToString();
+}
+
+static_assert(sizeof(ShortyFieldType) == sizeof(char),
+              "ShortyFieldType must be lightweight just like a char");
+
+// Compile-time trait information regarding the ShortyFieldType.
+// Used by static_asserts to verify that the templates are correctly used at compile-time.
+//
+// For example,
+//     ShortyFieldTypeTraits::IsPrimitiveNarrowType<int64_t>() == true
+//     ShortyFieldTypeTraits::IsObjectType<mirror::Object*>() == true
+struct ShortyFieldTypeTraits {
+  // A type guaranteed to be large enough to holds any of the shorty field types.
+  using MaxType = uint64_t;
+
+  // Type traits: Returns true if 'T' is a valid type that can be represented by a shorty field type.
+  template <typename T>
+  static inline constexpr bool IsType() {
+    return IsPrimitiveType<T>() || IsObjectType<T>() || IsLambdaType<T>();
+  }
+
+  // Returns true if 'T' is a primitive type (i.e. a built-in without nested references).
+  template <typename T>
+  static inline constexpr bool IsPrimitiveType() {
+    return IsPrimitiveNarrowType<T>() || IsPrimitiveWideType<T>();
+  }
+
+  // Returns true if 'T' is a primitive type that is narrow (i.e. can be stored into 1 vreg).
+  template <typename T>
+  static inline constexpr bool IsPrimitiveNarrowType() {
+    return IsPrimitiveNarrowTypeImpl(static_cast<T* const>(nullptr));
+  }
+
+  // Returns true if 'T' is a primitive type that is wide (i.e. needs 2 vregs for storage).
+  template <typename T>
+  static inline constexpr bool IsPrimitiveWideType() {
+    return IsPrimitiveWideTypeImpl(static_cast<T* const>(nullptr));
+  }
+
+  // Returns true if 'T' is an object (i.e. it is a managed GC reference).
+  // Note: This is equivalent to std::base_of<mirror::Object*, T>::value
+  template <typename T>
+  static inline constexpr bool IsObjectType() {
+    return IsObjectTypeImpl(static_cast<T* const>(nullptr));
+  }
+
+  // Returns true if 'T' is a lambda (i.e. it is a closure with unknown static data);
+  template <typename T>
+  static inline constexpr bool IsLambdaType() {
+    return IsLambdaTypeImpl(static_cast<T* const>(nullptr));
+  }
+
+ private:
+#define IS_VALID_TYPE_SPECIALIZATION(type, name) \
+  static inline constexpr bool Is ## name ## TypeImpl(type* const  = 0) { \
+    return true; \
+  } \
+  \
+  static_assert(sizeof(MaxType) >= sizeof(type), "MaxType too small")
+
+  IS_VALID_TYPE_SPECIALIZATION(bool, PrimitiveNarrow);
+  IS_VALID_TYPE_SPECIALIZATION(int8_t, PrimitiveNarrow);
+  IS_VALID_TYPE_SPECIALIZATION(uint8_t, PrimitiveNarrow);  // Not strictly true, but close enough.
+  IS_VALID_TYPE_SPECIALIZATION(int16_t, PrimitiveNarrow);
+  IS_VALID_TYPE_SPECIALIZATION(uint16_t, PrimitiveNarrow);  // Chars are unsigned.
+  IS_VALID_TYPE_SPECIALIZATION(int32_t, PrimitiveNarrow);
+  IS_VALID_TYPE_SPECIALIZATION(uint32_t, PrimitiveNarrow);  // Not strictly true, but close enough.
+  IS_VALID_TYPE_SPECIALIZATION(float, PrimitiveNarrow);
+  IS_VALID_TYPE_SPECIALIZATION(int64_t, PrimitiveWide);
+  IS_VALID_TYPE_SPECIALIZATION(uint64_t, PrimitiveWide);  // Not strictly true, but close enough.
+  IS_VALID_TYPE_SPECIALIZATION(double, PrimitiveWide);
+  IS_VALID_TYPE_SPECIALIZATION(mirror::Object*, Object);
+  IS_VALID_TYPE_SPECIALIZATION(Closure*, Lambda);
+#undef IS_VALID_TYPE_SPECIALIZATION
+
+#define IS_VALID_TYPE_SPECIALIZATION_IMPL(name) \
+  template <typename T> \
+  static inline constexpr bool Is ## name ## TypeImpl(T* const = 0) { \
+    return false; \
+  }
+
+  IS_VALID_TYPE_SPECIALIZATION_IMPL(PrimitiveNarrow);
+  IS_VALID_TYPE_SPECIALIZATION_IMPL(PrimitiveWide);
+  IS_VALID_TYPE_SPECIALIZATION_IMPL(Object);
+  IS_VALID_TYPE_SPECIALIZATION_IMPL(Lambda);
+
+#undef IS_VALID_TYPE_SPECIALIZATION_IMPL
+};
+
+// Maps the ShortyFieldType enum into it's C++ type equivalent, into the "type" typedef.
+// For example:
+//     ShortyFieldTypeSelectType<ShortyFieldType::kBoolean>::type => bool
+//     ShortyFieldTypeSelectType<ShortyFieldType::kLong>::type => int64_t
+//
+// Invalid enums will not have the type defined.
+template <decltype(ShortyFieldType::kByte) Shorty>
+struct ShortyFieldTypeSelectType {
+};
+
+// Maps the C++ type into it's ShortyFieldType enum equivalent, into the "value" constexpr.
+// For example:
+//     ShortyFieldTypeSelectEnum<bool>::value => ShortyFieldType::kBoolean
+//     ShortyFieldTypeSelectEnum<int64_t>::value => ShortyFieldType::kLong
+//
+// Signed-ness must match for a valid select, e.g. uint64_t will not map to kLong, but int64_t will.
+// Invalid types will not have the value defined (see e.g. ShortyFieldTypeTraits::IsType<T>())
+template <typename T>
+struct ShortyFieldTypeSelectEnum {
+};
+
+#define SHORTY_FIELD_TYPE_SELECT_IMPL(cpp_type, enum_element)      \
+template <> \
+struct ShortyFieldTypeSelectType<ShortyFieldType::enum_element> { \
+  using type = cpp_type; \
+}; \
+\
+template <> \
+struct ShortyFieldTypeSelectEnum<cpp_type> { \
+  static constexpr const auto value = ShortyFieldType::enum_element; \
+}; \
+
+SHORTY_FIELD_TYPE_SELECT_IMPL(bool, kBoolean);
+SHORTY_FIELD_TYPE_SELECT_IMPL(int8_t, kByte);
+SHORTY_FIELD_TYPE_SELECT_IMPL(int16_t, kShort);
+SHORTY_FIELD_TYPE_SELECT_IMPL(uint16_t, kChar);
+SHORTY_FIELD_TYPE_SELECT_IMPL(int32_t, kInt);
+SHORTY_FIELD_TYPE_SELECT_IMPL(float, kFloat);
+SHORTY_FIELD_TYPE_SELECT_IMPL(int64_t, kLong);
+SHORTY_FIELD_TYPE_SELECT_IMPL(double, kDouble);
+SHORTY_FIELD_TYPE_SELECT_IMPL(mirror::Object*, kObject);
+SHORTY_FIELD_TYPE_SELECT_IMPL(Closure*, kLambda);
+
+}  // namespace lambda
+}  // namespace art
+
+#endif  // ART_RUNTIME_LAMBDA_SHORTY_FIELD_TYPE_H_
diff --git a/runtime/lambda/shorty_field_type_test.cc b/runtime/lambda/shorty_field_type_test.cc
new file mode 100644
index 0000000..32bade9
--- /dev/null
+++ b/runtime/lambda/shorty_field_type_test.cc
@@ -0,0 +1,354 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#include "lambda/shorty_field_type.h"
+#include "mirror/object_reference.h"
+
+#include "utils.h"
+#include <numeric>
+#include <stdint.h>
+#include "gtest/gtest.h"
+
+#define EXPECT_NULL(expected) EXPECT_EQ(reinterpret_cast<const void*>(expected), \
+                                        reinterpret_cast<void*>(nullptr));
+
+namespace art {
+namespace lambda {
+
+class ShortyFieldTypeTest : public ::testing::Test {
+ public:
+  ShortyFieldTypeTest() = default;
+  ~ShortyFieldTypeTest() = default;
+
+ protected:
+  static void SetUpTestCase() {
+  }
+
+  virtual void SetUp() {
+  }
+
+  static ::testing::AssertionResult IsResultSuccessful(bool result) {
+    if (result) {
+      return ::testing::AssertionSuccess();
+    } else {
+      return ::testing::AssertionFailure();
+    }
+  }
+
+  template <typename T>
+  static std::string ListToString(const T& list) {
+    std::stringstream stream;
+
+    stream << "[";
+    for (auto&& val : list) {
+      stream << val << ", ";
+    }
+    stream << "]";
+
+    return stream.str();
+  }
+
+  // Compare two vector-like types for equality.
+  template <typename T>
+  static ::testing::AssertionResult AreListsEqual(const T& expected, const T& actual) {
+    bool success = true;
+    std::stringstream stream;
+
+    if (expected.size() != actual.size()) {
+      success = false;
+      stream << "Expected list size: " << expected.size()
+             << ", but got list size: " << actual.size();
+      stream << std::endl;
+    }
+
+    for (size_t j = 0; j < std::min(expected.size(), actual.size()); ++j) {
+      if (expected[j] != actual[j]) {
+        success = false;
+        stream << "Expected element '" << j << "' to be '" << expected[j] << "', but got actual: '"
+               << actual[j] << "'.";
+        stream << std::endl;
+      }
+    }
+
+    if (success) {
+      return ::testing::AssertionSuccess();
+    }
+
+    stream << "Expected list was: " << ListToString(expected)
+           << ", actual list was: " << ListToString(actual);
+
+    return ::testing::AssertionFailure() << stream.str();
+  }
+
+  static std::vector<ShortyFieldType> ParseLongTypeDescriptorsToList(const char* type_descriptor) {
+    std::vector<ShortyFieldType> lst;
+
+    ShortyFieldType shorty;
+
+    const char* parsed = type_descriptor;
+    while ((parsed = ShortyFieldType::ParseFromFieldTypeDescriptor(parsed, &shorty)) != nullptr) {
+      lst.push_back(shorty);
+    }
+
+    return lst;
+  }
+
+ protected:
+  // Shorthands for the ShortyFieldType constants.
+  // The letters are the same as JNI letters, with kS_ being a lambda since \ is not available.
+  static constexpr ShortyFieldType kSZ = ShortyFieldType::kBoolean;
+  static constexpr ShortyFieldType kSB = ShortyFieldType::kByte;
+  static constexpr ShortyFieldType kSC = ShortyFieldType::kChar;
+  static constexpr ShortyFieldType kSS = ShortyFieldType::kShort;
+  static constexpr ShortyFieldType kSI = ShortyFieldType::kInt;
+  static constexpr ShortyFieldType kSF = ShortyFieldType::kFloat;
+  static constexpr ShortyFieldType kSJ = ShortyFieldType::kLong;
+  static constexpr ShortyFieldType kSD = ShortyFieldType::kDouble;
+  static constexpr ShortyFieldType kSL = ShortyFieldType::kObject;
+  static constexpr ShortyFieldType kS_ = ShortyFieldType::kLambda;
+};
+
+TEST_F(ShortyFieldTypeTest, TestMaybeCreate) {
+  ShortyFieldType shorty;
+
+  std::vector<char> shorties = {'Z', 'B', 'C', 'S', 'I', 'F', 'J', 'D', 'L', '\\'};
+
+  // All valid 'shorty' characters are created successfully.
+  for (const char c : shorties) {
+    EXPECT_TRUE(ShortyFieldType::MaybeCreate(c, &shorty)) << c;
+    EXPECT_EQ(c, static_cast<char>(c));
+  }
+
+  // All other characters can never be created.
+  for (unsigned char c = 0; c < std::numeric_limits<unsigned char>::max(); ++c) {
+    // Skip the valid characters.
+    if (std::find(shorties.begin(), shorties.end(), c) != shorties.end()) { continue; }
+    // All invalid characters should fail.
+    EXPECT_FALSE(ShortyFieldType::MaybeCreate(static_cast<char>(c), &shorty)) << c;
+  }
+}  // TEST_F
+
+TEST_F(ShortyFieldTypeTest, TestCreateFromFieldTypeDescriptor) {
+  // Sample input.
+  std::vector<const char*> lengthies = {
+      "Z", "B", "C", "S", "I", "F", "J", "D", "LObject;", "\\Closure;",
+      "[Z", "[[B", "[[LObject;"
+  };
+
+  // Expected output.
+  std::vector<ShortyFieldType> expected = {
+      ShortyFieldType::kBoolean,
+      ShortyFieldType::kByte,
+      ShortyFieldType::kChar,
+      ShortyFieldType::kShort,
+      ShortyFieldType::kInt,
+      ShortyFieldType::kFloat,
+      ShortyFieldType::kLong,
+      ShortyFieldType::kDouble,
+      ShortyFieldType::kObject,
+      ShortyFieldType::kLambda,
+      // Arrays are always treated as objects.
+      ShortyFieldType::kObject,
+      ShortyFieldType::kObject,
+      ShortyFieldType::kObject,
+  };
+
+  // All valid lengthy types are correctly turned into the expected shorty type.
+  for (size_t i = 0; i < lengthies.size(); ++i) {
+    EXPECT_EQ(expected[i], ShortyFieldType::CreateFromFieldTypeDescriptor(lengthies[i]));
+  }
+}  // TEST_F
+
+TEST_F(ShortyFieldTypeTest, TestParseFromFieldTypeDescriptor) {
+  // Sample input.
+  std::vector<const char*> lengthies = {
+      // Empty list
+      "",
+      // Primitives
+      "Z", "B", "C", "S", "I", "F", "J", "D",
+      // Non-primitives
+      "LObject;", "\\Closure;",
+      // Arrays. The biggest PITA.
+      "[Z", "[[B", "[[LObject;", "[[[[\\Closure;",
+      // Multiple things at once:
+      "ZBCSIFJD",
+      "LObject;LObject;SSI",
+      "[[ZDDZ",
+      "[[LObject;[[Z[F\\Closure;LObject;",
+  };
+
+  // Expected output.
+  std::vector<std::vector<ShortyFieldType>> expected = {
+      // Empty list
+      {},
+      // Primitives
+      {kSZ}, {kSB}, {kSC}, {kSS}, {kSI}, {kSF}, {kSJ}, {kSD},
+      // Non-primitives.
+      { ShortyFieldType::kObject }, { ShortyFieldType::kLambda },
+      // Arrays are always treated as objects.
+      { kSL }, { kSL }, { kSL }, { kSL },
+      // Multiple things at once:
+      { kSZ, kSB, kSC, kSS, kSI, kSF, kSJ, kSD },
+      { kSL, kSL, kSS, kSS, kSI },
+      { kSL, kSD, kSD, kSZ },
+      { kSL, kSL, kSL, kS_, kSL },
+  };
+
+  // Sanity check that the expected/actual lists are the same size.. when adding new entries.
+  ASSERT_EQ(expected.size(), lengthies.size());
+
+  // All valid lengthy types are correctly turned into the expected shorty type.
+  for (size_t i = 0; i < expected.size(); ++i) {
+    const std::vector<ShortyFieldType>& expected_list = expected[i];
+    std::vector<ShortyFieldType> actual_list = ParseLongTypeDescriptorsToList(lengthies[i]);
+    EXPECT_TRUE(AreListsEqual(expected_list, actual_list));
+  }
+}  // TEST_F
+
+// Helper class to probe a shorty's characteristics by minimizing copy-and-paste tests.
+template <typename T, decltype(ShortyFieldType::kByte) kShortyEnum>
+struct ShortyTypeCharacteristics {
+  bool is_primitive_ = false;
+  bool is_primitive_narrow_ = false;
+  bool is_primitive_wide_ = false;
+  bool is_object_ = false;
+  bool is_lambda_ = false;
+  size_t size_ = sizeof(T);
+  bool is_dynamic_sized_ = false;
+
+  void CheckExpects() {
+    ShortyFieldType shorty = kShortyEnum;
+
+    // Test the main non-parsing-related ShortyFieldType characteristics.
+    EXPECT_EQ(is_primitive_, shorty.IsPrimitive());
+    EXPECT_EQ(is_primitive_narrow_, shorty.IsPrimitiveNarrow());
+    EXPECT_EQ(is_primitive_wide_, shorty.IsPrimitiveWide());
+    EXPECT_EQ(is_object_, shorty.IsObject());
+    EXPECT_EQ(is_lambda_, shorty.IsLambda());
+    EXPECT_EQ(size_, shorty.GetStaticSize());
+    EXPECT_EQ(is_dynamic_sized_, !shorty.IsStaticSize());
+
+    // Test compile-time ShortyFieldTypeTraits.
+    EXPECT_TRUE(ShortyFieldTypeTraits::IsType<T>());
+    EXPECT_EQ(is_primitive_, ShortyFieldTypeTraits::IsPrimitiveType<T>());
+    EXPECT_EQ(is_primitive_narrow_, ShortyFieldTypeTraits::IsPrimitiveNarrowType<T>());
+    EXPECT_EQ(is_primitive_wide_, ShortyFieldTypeTraits::IsPrimitiveWideType<T>());
+    EXPECT_EQ(is_object_, ShortyFieldTypeTraits::IsObjectType<T>());
+    EXPECT_EQ(is_lambda_, ShortyFieldTypeTraits::IsLambdaType<T>());
+
+    // Test compile-time ShortyFieldType selectors
+    static_assert(std::is_same<T, typename ShortyFieldTypeSelectType<kShortyEnum>::type>::value,
+                  "ShortyFieldType Enum->Type incorrect mapping");
+    auto kActualEnum = ShortyFieldTypeSelectEnum<T>::value;  // Do not ODR-use, avoid linker error.
+    EXPECT_EQ(kShortyEnum, kActualEnum);
+  }
+};
+
+TEST_F(ShortyFieldTypeTest, TestCharacteristicsAndTraits) {
+  // Boolean test
+  {
+    SCOPED_TRACE("boolean");
+    ShortyTypeCharacteristics<bool, ShortyFieldType::kBoolean> chars;
+    chars.is_primitive_ = true;
+    chars.is_primitive_narrow_ = true;
+    chars.CheckExpects();
+  }
+
+  // Byte test
+  {
+    SCOPED_TRACE("byte");
+    ShortyTypeCharacteristics<int8_t, ShortyFieldType::kByte> chars;
+    chars.is_primitive_ = true;
+    chars.is_primitive_narrow_ = true;
+    chars.CheckExpects();
+  }
+
+  // Char test
+  {
+    SCOPED_TRACE("char");
+    ShortyTypeCharacteristics<uint16_t, ShortyFieldType::kChar> chars;  // Char is unsigned.
+    chars.is_primitive_ = true;
+    chars.is_primitive_narrow_ = true;
+    chars.CheckExpects();
+  }
+
+  // Short test
+  {
+    SCOPED_TRACE("short");
+    ShortyTypeCharacteristics<int16_t, ShortyFieldType::kShort> chars;
+    chars.is_primitive_ = true;
+    chars.is_primitive_narrow_ = true;
+    chars.CheckExpects();
+  }
+
+  // Int test
+  {
+    SCOPED_TRACE("int");
+    ShortyTypeCharacteristics<int32_t, ShortyFieldType::kInt> chars;
+    chars.is_primitive_ = true;
+    chars.is_primitive_narrow_ = true;
+    chars.CheckExpects();
+  }
+
+  // Long test
+  {
+    SCOPED_TRACE("long");
+    ShortyTypeCharacteristics<int64_t, ShortyFieldType::kLong> chars;
+    chars.is_primitive_ = true;
+    chars.is_primitive_wide_ = true;
+    chars.CheckExpects();
+  }
+
+  // Float test
+  {
+    SCOPED_TRACE("float");
+    ShortyTypeCharacteristics<float, ShortyFieldType::kFloat> chars;
+    chars.is_primitive_ = true;
+    chars.is_primitive_narrow_ = true;
+    chars.CheckExpects();
+  }
+
+  // Double test
+  {
+    SCOPED_TRACE("double");
+    ShortyTypeCharacteristics<double, ShortyFieldType::kDouble> chars;
+    chars.is_primitive_ = true;
+    chars.is_primitive_wide_ = true;
+    chars.CheckExpects();
+  }
+
+  // Object test
+  {
+    SCOPED_TRACE("object");
+    ShortyTypeCharacteristics<mirror::Object*, ShortyFieldType::kObject> chars;
+    chars.is_object_ = true;
+    chars.size_ = kObjectReferenceSize;
+    chars.CheckExpects();
+    EXPECT_EQ(kObjectReferenceSize, sizeof(mirror::CompressedReference<mirror::Object>));
+  }
+
+  // Lambda test
+  {
+    SCOPED_TRACE("lambda");
+    ShortyTypeCharacteristics<Closure*, ShortyFieldType::kLambda> chars;
+    chars.is_lambda_ = true;
+    chars.is_dynamic_sized_ = true;
+    chars.CheckExpects();
+  }
+}
+
+}  // namespace lambda
+}  // namespace art
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index b2c6e4d..10b381d 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -817,12 +817,12 @@
   }
 }
 
-inline void Class::SetDexCacheStrings(ObjectArray<String>* new_dex_cache_strings) {
-  SetFieldObject<false>(DexCacheStringsOffset(), new_dex_cache_strings);
+inline void Class::SetDexCacheStrings(GcRoot<String>* new_dex_cache_strings) {
+  SetFieldPtr<false>(DexCacheStringsOffset(), new_dex_cache_strings);
 }
 
-inline ObjectArray<String>* Class::GetDexCacheStrings() {
-  return GetFieldObject<ObjectArray<String>>(DexCacheStringsOffset());
+inline GcRoot<String>* Class::GetDexCacheStrings() {
+  return GetFieldPtr<GcRoot<String>*>(DexCacheStringsOffset());
 }
 
 template<class Visitor>
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 1420e5b..9422432 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -250,6 +250,14 @@
     SetClassFlags(kClassFlagClassLoader);
   }
 
+  ALWAYS_INLINE bool IsDexCacheClass() SHARED_REQUIRES(Locks::mutator_lock_) {
+    return (GetClassFlags() & kClassFlagDexCache) != 0;
+  }
+
+  ALWAYS_INLINE void SetDexCacheClass() SHARED_REQUIRES(Locks::mutator_lock_) {
+    SetClassFlags(GetClassFlags() | kClassFlagDexCache);
+  }
+
   // Returns true if the class is abstract.
   ALWAYS_INLINE bool IsAbstract() SHARED_REQUIRES(Locks::mutator_lock_) {
     return (GetAccessFlags() & kAccAbstract) != 0;
@@ -1077,8 +1085,8 @@
   bool GetSlowPathEnabled() SHARED_REQUIRES(Locks::mutator_lock_);
   void SetSlowPath(bool enabled) SHARED_REQUIRES(Locks::mutator_lock_);
 
-  ObjectArray<String>* GetDexCacheStrings() SHARED_REQUIRES(Locks::mutator_lock_);
-  void SetDexCacheStrings(ObjectArray<String>* new_dex_cache_strings)
+  GcRoot<String>* GetDexCacheStrings() SHARED_REQUIRES(Locks::mutator_lock_);
+  void SetDexCacheStrings(GcRoot<String>* new_dex_cache_strings)
       SHARED_REQUIRES(Locks::mutator_lock_);
   static MemberOffset DexCacheStringsOffset() {
     return OFFSET_OF_OBJECT_MEMBER(Class, dex_cache_strings_);
@@ -1173,9 +1181,6 @@
   // runtime such as arrays and primitive classes).
   HeapReference<DexCache> dex_cache_;
 
-  // Short cuts to dex_cache_ member for fast compiled code access.
-  HeapReference<ObjectArray<String>> dex_cache_strings_;
-
   // The interface table (iftable_) contains pairs of a interface class and an array of the
   // interface methods. There is one pair per interface supported by this class.  That means one
   // pair for each interface we support directly, indirectly via superclass, or indirectly via a
@@ -1209,9 +1214,8 @@
   // virtual_ methods_ for miranda methods.
   HeapReference<PointerArray> vtable_;
 
-  // Access flags; low 16 bits are defined by VM spec.
-  // Note: Shuffled back.
-  uint32_t access_flags_;
+  // Short cuts to dex_cache_ member for fast compiled code access.
+  uint64_t dex_cache_strings_;
 
   // static, private, and <init> methods. Pointer to an ArtMethod length-prefixed array.
   uint64_t direct_methods_;
@@ -1234,6 +1238,9 @@
   // length-prefixed array.
   uint64_t virtual_methods_;
 
+  // Access flags; low 16 bits are defined by VM spec.
+  uint32_t access_flags_;
+
   // Class flags to help speed up visiting object references.
   uint32_t class_flags_;
 
diff --git a/runtime/mirror/class_flags.h b/runtime/mirror/class_flags.h
index eb2e2eb..139c4cb 100644
--- a/runtime/mirror/class_flags.h
+++ b/runtime/mirror/class_flags.h
@@ -41,17 +41,20 @@
 // Class is ClassLoader or one of its subclasses.
 static constexpr uint32_t kClassFlagClassLoader        = 0x00000020;
 
+// Class is DexCache.
+static constexpr uint32_t kClassFlagDexCache           = 0x00000040;
+
 // Class is a soft/weak/phantom class.
-static constexpr uint32_t kClassFlagSoftReference      = 0x00000040;
+static constexpr uint32_t kClassFlagSoftReference      = 0x00000080;
 
 // Class is a weak reference class.
-static constexpr uint32_t kClassFlagWeakReference      = 0x00000080;
+static constexpr uint32_t kClassFlagWeakReference      = 0x00000100;
 
 // Class is a finalizer reference class.
-static constexpr uint32_t kClassFlagFinalizerReference = 0x00000100;
+static constexpr uint32_t kClassFlagFinalizerReference = 0x00000200;
 
 // Class is the phantom reference class.
-static constexpr uint32_t kClassFlagPhantomReference   = 0x00000200;
+static constexpr uint32_t kClassFlagPhantomReference   = 0x00000400;
 
 // Combination of flags to figure out if the class is either the weak/soft/phantom/finalizer
 // reference class.
diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h
index 4b5063a..f8ccfb1 100644
--- a/runtime/mirror/dex_cache-inl.h
+++ b/runtime/mirror/dex_cache-inl.h
@@ -21,6 +21,7 @@
 
 #include "art_field-inl.h"
 #include "art_method-inl.h"
+#include "base/casts.h"
 #include "base/logging.h"
 #include "mirror/class.h"
 #include "runtime.h"
@@ -33,29 +34,53 @@
   return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0, 0, 0, pointer_size);
 }
 
-inline void DexCache::SetResolvedType(uint32_t type_idx, Class* resolved) {
-  // TODO default transaction support.
-  DCHECK(resolved == nullptr || !resolved->IsErroneous());
-  GetResolvedTypes()->Set(type_idx, resolved);
+inline String* DexCache::GetResolvedString(uint32_t string_idx) {
+  DCHECK_LT(string_idx, NumStrings());
+  return GetStrings()[string_idx].Read();
 }
 
-inline ArtField* DexCache::GetResolvedField(uint32_t idx, size_t ptr_size) {
+inline void DexCache::SetResolvedString(uint32_t string_idx, String* resolved) {
+  DCHECK_LT(string_idx, NumStrings());
+  // TODO default transaction support.
+  GetStrings()[string_idx] = GcRoot<String>(resolved);
+  // TODO: Fine-grained marking, so that we don't need to go through all arrays in full.
+  Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(this);
+}
+
+inline Class* DexCache::GetResolvedType(uint32_t type_idx) {
+  DCHECK_LT(type_idx, NumResolvedTypes());
+  return GetResolvedTypes()[type_idx].Read();
+}
+
+inline void DexCache::SetResolvedType(uint32_t type_idx, Class* resolved) {
+  DCHECK_LT(type_idx, NumResolvedTypes());  // NOTE: Unchecked, i.e. not throwing AIOOB.
+  // TODO default transaction support.
+  DCHECK(resolved == nullptr || !resolved->IsErroneous());
+  GetResolvedTypes()[type_idx] = GcRoot<Class>(resolved);
+  // TODO: Fine-grained marking, so that we don't need to go through all arrays in full.
+  Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(this);
+}
+
+inline ArtField* DexCache::GetResolvedField(uint32_t field_idx, size_t ptr_size) {
   DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), ptr_size);
-  auto* field = GetResolvedFields()->GetElementPtrSize<ArtField*>(idx, ptr_size);
+  DCHECK_LT(field_idx, NumResolvedFields());  // NOTE: Unchecked, i.e. not throwing AIOOB.
+  ArtField* field = GetElementPtrSize(GetResolvedFields(), field_idx, ptr_size);
   if (field == nullptr || field->GetDeclaringClass()->IsErroneous()) {
     return nullptr;
   }
   return field;
 }
 
-inline void DexCache::SetResolvedField(uint32_t idx, ArtField* field, size_t ptr_size) {
+inline void DexCache::SetResolvedField(uint32_t field_idx, ArtField* field, size_t ptr_size) {
   DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), ptr_size);
-  GetResolvedFields()->SetElementPtrSize(idx, field, ptr_size);
+  DCHECK_LT(field_idx, NumResolvedFields());  // NOTE: Unchecked, i.e. not throwing AIOOB.
+  SetElementPtrSize(GetResolvedFields(), field_idx, field, ptr_size);
 }
 
 inline ArtMethod* DexCache::GetResolvedMethod(uint32_t method_idx, size_t ptr_size) {
   DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), ptr_size);
-  auto* method = GetResolvedMethods()->GetElementPtrSize<ArtMethod*>(method_idx, ptr_size);
+  DCHECK_LT(method_idx, NumResolvedMethods());  // NOTE: Unchecked, i.e. not throwing AIOOB.
+  ArtMethod* method = GetElementPtrSize<ArtMethod*>(GetResolvedMethods(), method_idx, ptr_size);
   // Hide resolution trampoline methods from the caller
   if (method != nullptr && method->IsRuntimeMethod()) {
     DCHECK_EQ(method, Runtime::Current()->GetResolutionMethod());
@@ -64,9 +89,52 @@
   return method;
 }
 
-inline void DexCache::SetResolvedMethod(uint32_t idx, ArtMethod* method, size_t ptr_size) {
+inline void DexCache::SetResolvedMethod(uint32_t method_idx, ArtMethod* method, size_t ptr_size) {
   DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), ptr_size);
-  GetResolvedMethods()->SetElementPtrSize(idx, method, ptr_size);
+  DCHECK_LT(method_idx, NumResolvedMethods());  // NOTE: Unchecked, i.e. not throwing AIOOB.
+  SetElementPtrSize(GetResolvedMethods(), method_idx, method, ptr_size);
+}
+
+template <typename PtrType>
+inline PtrType DexCache::GetElementPtrSize(PtrType* ptr_array, size_t idx, size_t ptr_size) {
+  if (ptr_size == 8u) {
+    uint64_t element = reinterpret_cast<const uint64_t*>(ptr_array)[idx];
+    return reinterpret_cast<PtrType>(dchecked_integral_cast<uintptr_t>(element));
+  } else {
+    DCHECK_EQ(ptr_size, 4u);
+    uint32_t element = reinterpret_cast<const uint32_t*>(ptr_array)[idx];
+    return reinterpret_cast<PtrType>(dchecked_integral_cast<uintptr_t>(element));
+  }
+}
+
+template <typename PtrType>
+inline void DexCache::SetElementPtrSize(PtrType* ptr_array,
+                                        size_t idx,
+                                        PtrType ptr,
+                                        size_t ptr_size) {
+  if (ptr_size == 8u) {
+    reinterpret_cast<uint64_t*>(ptr_array)[idx] =
+        dchecked_integral_cast<uint64_t>(reinterpret_cast<uintptr_t>(ptr));
+  } else {
+    DCHECK_EQ(ptr_size, 4u);
+    reinterpret_cast<uint32_t*>(ptr_array)[idx] =
+        dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(ptr));
+  }
+}
+
+template <VerifyObjectFlags kVerifyFlags, typename Visitor>
+inline void DexCache::VisitReferences(mirror::Class* klass, const Visitor& visitor) {
+  // Visit instance fields first.
+  VisitInstanceFieldsReferences(klass, visitor);
+  // Visit arrays after.
+  GcRoot<mirror::String>* strings = GetStrings();
+  for (size_t i = 0, num_strings = NumStrings(); i != num_strings; ++i) {
+    visitor.VisitRootIfNonNull(strings[i].AddressWithoutBarrier());
+  }
+  GcRoot<mirror::Class>* resolved_types = GetResolvedTypes();
+  for (size_t i = 0, num_types = NumResolvedTypes(); i != num_types; ++i) {
+    visitor.VisitRootIfNonNull(resolved_types[i].AddressWithoutBarrier());
+  }
 }
 
 }  // namespace mirror
diff --git a/runtime/mirror/dex_cache.cc b/runtime/mirror/dex_cache.cc
index 630faee..349a319 100644
--- a/runtime/mirror/dex_cache.cc
+++ b/runtime/mirror/dex_cache.cc
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "dex_cache.h"
+#include "dex_cache-inl.h"
 
 #include "art_method-inl.h"
 #include "base/logging.h"
@@ -31,22 +31,34 @@
 namespace art {
 namespace mirror {
 
-void DexCache::Init(const DexFile* dex_file, String* location, ObjectArray<String>* strings,
-                    ObjectArray<Class>* resolved_types, PointerArray* resolved_methods,
-                    PointerArray* resolved_fields, size_t pointer_size) {
+void DexCache::Init(const DexFile* dex_file,
+                    String* location,
+                    GcRoot<String>* strings,
+                    uint32_t num_strings,
+                    GcRoot<Class>* resolved_types,
+                    uint32_t num_resolved_types,
+                    ArtMethod** resolved_methods,
+                    uint32_t num_resolved_methods,
+                    ArtField** resolved_fields,
+                    uint32_t num_resolved_fields,
+                    size_t pointer_size) {
   CHECK(dex_file != nullptr);
   CHECK(location != nullptr);
-  CHECK(strings != nullptr);
-  CHECK(resolved_types != nullptr);
-  CHECK(resolved_methods != nullptr);
-  CHECK(resolved_fields != nullptr);
+  CHECK_EQ(num_strings != 0u, strings != nullptr);
+  CHECK_EQ(num_resolved_types != 0u, resolved_types != nullptr);
+  CHECK_EQ(num_resolved_methods != 0u, resolved_methods != nullptr);
+  CHECK_EQ(num_resolved_fields != 0u, resolved_fields != nullptr);
 
   SetDexFile(dex_file);
   SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(DexCache, location_), location);
-  SetFieldObject<false>(StringsOffset(), strings);
-  SetFieldObject<false>(ResolvedFieldsOffset(), resolved_fields);
-  SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_types_), resolved_types);
-  SetFieldObject<false>(ResolvedMethodsOffset(), resolved_methods);
+  SetField64<false>(StringsOffset(), reinterpret_cast<uintptr_t>(strings));
+  SetField64<false>(ResolvedTypesOffset(), reinterpret_cast<uintptr_t>(resolved_types));
+  SetField64<false>(ResolvedMethodsOffset(), reinterpret_cast<uintptr_t>(resolved_methods));
+  SetField64<false>(ResolvedFieldsOffset(), reinterpret_cast<uintptr_t>(resolved_fields));
+  SetField32<false>(NumStringsOffset(), num_strings);
+  SetField32<false>(NumResolvedTypesOffset(), num_resolved_types);
+  SetField32<false>(NumResolvedMethodsOffset(), num_resolved_methods);
+  SetField32<false>(NumResolvedFieldsOffset(), num_resolved_fields);
 
   Runtime* const runtime = Runtime::Current();
   if (runtime->HasResolutionMethod()) {
@@ -60,9 +72,9 @@
   CHECK(trampoline != nullptr);
   CHECK(trampoline->IsRuntimeMethod());
   auto* resolved_methods = GetResolvedMethods();
-  for (size_t i = 0, length = resolved_methods->GetLength(); i < length; i++) {
-    if (resolved_methods->GetElementPtrSize<ArtMethod*>(i, pointer_size) == nullptr) {
-      resolved_methods->SetElementPtrSize(i, trampoline, pointer_size);
+  for (size_t i = 0, length = NumResolvedMethods(); i < length; i++) {
+    if (GetElementPtrSize<ArtMethod*>(resolved_methods, i, pointer_size) == nullptr) {
+      SetElementPtrSize(resolved_methods, i, trampoline, pointer_size);
     }
   }
 }
diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h
index ba49a15..3144553 100644
--- a/runtime/mirror/dex_cache.h
+++ b/runtime/mirror/dex_cache.h
@@ -46,8 +46,16 @@
     return sizeof(DexCache);
   }
 
-  void Init(const DexFile* dex_file, String* location, ObjectArray<String>* strings,
-            ObjectArray<Class>* types, PointerArray* methods, PointerArray* fields,
+  void Init(const DexFile* dex_file,
+            String* location,
+            GcRoot<String>* strings,
+            uint32_t num_strings,
+            GcRoot<Class>* resolved_types,
+            uint32_t num_resolved_types,
+            ArtMethod** resolved_methods,
+            uint32_t num_resolved_methods,
+            ArtField** resolved_fields,
+            uint32_t num_resolved_fields,
             size_t pointer_size) SHARED_REQUIRES(Locks::mutator_lock_);
 
   void Fixup(ArtMethod* trampoline, size_t pointer_size)
@@ -65,6 +73,10 @@
     return OFFSET_OF_OBJECT_MEMBER(DexCache, strings_);
   }
 
+  static MemberOffset ResolvedTypesOffset() {
+    return OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_types_);
+  }
+
   static MemberOffset ResolvedFieldsOffset() {
     return OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_fields_);
   }
@@ -73,40 +85,32 @@
     return OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_methods_);
   }
 
-  size_t NumStrings() SHARED_REQUIRES(Locks::mutator_lock_) {
-    return GetStrings()->GetLength();
+  static MemberOffset NumStringsOffset() {
+    return OFFSET_OF_OBJECT_MEMBER(DexCache, num_strings_);
   }
 
-  size_t NumResolvedTypes() SHARED_REQUIRES(Locks::mutator_lock_) {
-    return GetResolvedTypes()->GetLength();
+  static MemberOffset NumResolvedTypesOffset() {
+    return OFFSET_OF_OBJECT_MEMBER(DexCache, num_resolved_types_);
   }
 
-  size_t NumResolvedMethods() SHARED_REQUIRES(Locks::mutator_lock_) {
-    return GetResolvedMethods()->GetLength();
+  static MemberOffset NumResolvedFieldsOffset() {
+    return OFFSET_OF_OBJECT_MEMBER(DexCache, num_resolved_fields_);
   }
 
-  size_t NumResolvedFields() SHARED_REQUIRES(Locks::mutator_lock_) {
-    return GetResolvedFields()->GetLength();
+  static MemberOffset NumResolvedMethodsOffset() {
+    return OFFSET_OF_OBJECT_MEMBER(DexCache, num_resolved_methods_);
   }
 
-  String* GetResolvedString(uint32_t string_idx) SHARED_REQUIRES(Locks::mutator_lock_) {
-    return GetStrings()->Get(string_idx);
-  }
+  String* GetResolvedString(uint32_t string_idx) ALWAYS_INLINE
+      SHARED_REQUIRES(Locks::mutator_lock_);
 
   void SetResolvedString(uint32_t string_idx, String* resolved) ALWAYS_INLINE
-      SHARED_REQUIRES(Locks::mutator_lock_) {
-    // TODO default transaction support.
-    GetStrings()->Set(string_idx, resolved);
-  }
-
-  Class* GetResolvedType(uint32_t type_idx) ALWAYS_INLINE
-      SHARED_REQUIRES(Locks::mutator_lock_) {
-    return GetResolvedTypes()->Get(type_idx);
-  }
-
-  void SetResolvedType(uint32_t type_idx, Class* resolved)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
+  Class* GetResolvedType(uint32_t type_idx) SHARED_REQUIRES(Locks::mutator_lock_);
+
+  void SetResolvedType(uint32_t type_idx, Class* resolved) SHARED_REQUIRES(Locks::mutator_lock_);
+
   ALWAYS_INLINE ArtMethod* GetResolvedMethod(uint32_t method_idx, size_t ptr_size)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
@@ -121,21 +125,36 @@
   ALWAYS_INLINE void SetResolvedField(uint32_t idx, ArtField* field, size_t ptr_size)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
-  ObjectArray<String>* GetStrings() ALWAYS_INLINE SHARED_REQUIRES(Locks::mutator_lock_) {
-    return GetFieldObject<ObjectArray<String>>(StringsOffset());
+  GcRoot<String>* GetStrings() ALWAYS_INLINE SHARED_REQUIRES(Locks::mutator_lock_) {
+    return GetFieldPtr<GcRoot<String>*>(StringsOffset());
   }
 
-  ObjectArray<Class>* GetResolvedTypes() ALWAYS_INLINE SHARED_REQUIRES(Locks::mutator_lock_) {
-    return GetFieldObject<ObjectArray<Class>>(
-        OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_types_));
+  GcRoot<Class>* GetResolvedTypes() ALWAYS_INLINE SHARED_REQUIRES(Locks::mutator_lock_) {
+    return GetFieldPtr<GcRoot<Class>*>(ResolvedTypesOffset());
   }
 
-  PointerArray* GetResolvedMethods() ALWAYS_INLINE SHARED_REQUIRES(Locks::mutator_lock_) {
-    return GetFieldObject<PointerArray>(ResolvedMethodsOffset());
+  ArtMethod** GetResolvedMethods() ALWAYS_INLINE SHARED_REQUIRES(Locks::mutator_lock_) {
+    return GetFieldPtr<ArtMethod**>(ResolvedMethodsOffset());
   }
 
-  PointerArray* GetResolvedFields() ALWAYS_INLINE SHARED_REQUIRES(Locks::mutator_lock_) {
-    return GetFieldObject<PointerArray>(ResolvedFieldsOffset());
+  ArtField** GetResolvedFields() ALWAYS_INLINE SHARED_REQUIRES(Locks::mutator_lock_) {
+    return GetFieldPtr<ArtField**>(ResolvedFieldsOffset());
+  }
+
+  size_t NumStrings() SHARED_REQUIRES(Locks::mutator_lock_) {
+    return GetField32(NumStringsOffset());
+  }
+
+  size_t NumResolvedTypes() SHARED_REQUIRES(Locks::mutator_lock_) {
+    return GetField32(NumResolvedTypesOffset());
+  }
+
+  size_t NumResolvedMethods() SHARED_REQUIRES(Locks::mutator_lock_) {
+    return GetField32(NumResolvedMethodsOffset());
+  }
+
+  size_t NumResolvedFields() SHARED_REQUIRES(Locks::mutator_lock_) {
+    return GetField32(NumResolvedFieldsOffset());
   }
 
   const DexFile* GetDexFile() ALWAYS_INLINE SHARED_REQUIRES(Locks::mutator_lock_) {
@@ -147,17 +166,36 @@
     return SetFieldPtr<false>(OFFSET_OF_OBJECT_MEMBER(DexCache, dex_file_), dex_file);
   }
 
+  // NOTE: Get/SetElementPtrSize() are intended for working with ArtMethod** and ArtField**
+  // provided by GetResolvedMethods/Fields() and ArtMethod::GetDexCacheResolvedMethods(),
+  // so they need to be public.
+
+  template <typename PtrType>
+  static PtrType GetElementPtrSize(PtrType* ptr_array, size_t idx, size_t ptr_size);
+
+  template <typename PtrType>
+  static void SetElementPtrSize(PtrType* ptr_array, size_t idx, PtrType ptr, size_t ptr_size);
+
  private:
+  // Visit instance fields of the dex cache as well as its associated arrays.
+  template <VerifyObjectFlags kVerifyFlags, typename Visitor>
+  void VisitReferences(mirror::Class* klass, const Visitor& visitor)
+      SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_);
+
   HeapReference<Object> dex_;
   HeapReference<String> location_;
-  // Either an int array or long array based on runtime ISA since these arrays hold pointers.
-  HeapReference<PointerArray> resolved_fields_;
-  HeapReference<PointerArray> resolved_methods_;
-  HeapReference<ObjectArray<Class>> resolved_types_;
-  HeapReference<ObjectArray<String>> strings_;
-  uint64_t dex_file_;
+  uint64_t dex_file_;           // const DexFile*
+  uint64_t resolved_fields_;    // ArtField*, array with num_resolved_fields_ elements.
+  uint64_t resolved_methods_;   // ArtMethod*, array with num_resolved_methods_ elements.
+  uint64_t resolved_types_;     // GcRoot<Class>*, array with num_resolved_types_ elements.
+  uint64_t strings_;            // GcRoot<String>*, array with num_strings_ elements.
+  uint32_t num_resolved_fields_;    // Number of elements in the resolved_fields_ array.
+  uint32_t num_resolved_methods_;   // Number of elements in the resolved_methods_ array.
+  uint32_t num_resolved_types_;     // Number of elements in the resolved_types_ array.
+  uint32_t num_strings_;            // Number of elements in the strings_ array.
 
   friend struct art::DexCacheOffsets;  // for verifying offset information
+  friend class Object;  // For VisitReferences
   DISALLOW_IMPLICIT_CONSTRUCTORS(DexCache);
 };
 
diff --git a/runtime/mirror/dex_cache_test.cc b/runtime/mirror/dex_cache_test.cc
index 228fce5..8fb860f 100644
--- a/runtime/mirror/dex_cache_test.cc
+++ b/runtime/mirror/dex_cache_test.cc
@@ -43,19 +43,6 @@
   EXPECT_EQ(java_lang_dex_file_->NumTypeIds(),   dex_cache->NumResolvedTypes());
   EXPECT_EQ(java_lang_dex_file_->NumMethodIds(), dex_cache->NumResolvedMethods());
   EXPECT_EQ(java_lang_dex_file_->NumFieldIds(),  dex_cache->NumResolvedFields());
-
-  EXPECT_LE(0, dex_cache->GetStrings()->GetLength());
-  EXPECT_LE(0, dex_cache->GetResolvedTypes()->GetLength());
-  EXPECT_LE(0, dex_cache->GetResolvedMethods()->GetLength());
-  EXPECT_LE(0u, dex_cache->NumResolvedFields());
-
-  EXPECT_EQ(java_lang_dex_file_->NumStringIds(),
-            static_cast<uint32_t>(dex_cache->GetStrings()->GetLength()));
-  EXPECT_EQ(java_lang_dex_file_->NumTypeIds(),
-            static_cast<uint32_t>(dex_cache->GetResolvedTypes()->GetLength()));
-  EXPECT_EQ(java_lang_dex_file_->NumMethodIds(),
-            static_cast<uint32_t>(dex_cache->GetResolvedMethods()->GetLength()));
-  EXPECT_EQ(java_lang_dex_file_->NumFieldIds(), dex_cache->NumResolvedFields());
 }
 
 }  // namespace mirror
diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h
index e35ddcc..90180c5 100644
--- a/runtime/mirror/object-inl.h
+++ b/runtime/mirror/object-inl.h
@@ -27,6 +27,7 @@
 #include "class_flags.h"
 #include "class_linker.h"
 #include "class_loader-inl.h"
+#include "dex_cache-inl.h"
 #include "lock_word-inl.h"
 #include "monitor.h"
 #include "object_array-inl.h"
@@ -1006,6 +1007,17 @@
   return down_cast<mirror::ClassLoader*>(this);
 }
 
+template<VerifyObjectFlags kVerifyFlags>
+inline bool Object::IsDexCache() {
+  return GetClass<kVerifyFlags>()->IsDexCacheClass();
+}
+
+template<VerifyObjectFlags kVerifyFlags>
+inline mirror::DexCache* Object::AsDexCache() {
+  DCHECK(IsDexCache<kVerifyFlags>());
+  return down_cast<mirror::DexCache*>(this);
+}
+
 template <VerifyObjectFlags kVerifyFlags, typename Visitor, typename JavaLangRefVisitor>
 inline void Object::VisitReferences(const Visitor& visitor,
                                     const JavaLangRefVisitor& ref_visitor) {
@@ -1031,6 +1043,9 @@
       } else if ((class_flags & kClassFlagReference) != 0) {
         VisitInstanceFieldsReferences(klass, visitor);
         ref_visitor(klass, AsReference());
+      } else if (class_flags == kClassFlagDexCache) {
+        mirror::DexCache* const dex_cache = AsDexCache<kVerifyFlags>();
+        dex_cache->VisitReferences<kVerifyFlags>(klass, visitor);
       } else {
         mirror::ClassLoader* const class_loader = AsClassLoader<kVerifyFlags>();
         class_loader->VisitReferences<kVerifyFlags>(klass, visitor);
diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h
index 3cec29c..50490bb 100644
--- a/runtime/mirror/object.h
+++ b/runtime/mirror/object.h
@@ -38,6 +38,7 @@
 class Array;
 class Class;
 class ClassLoader;
+class DexCache;
 class FinalizerReference;
 template<class T> class ObjectArray;
 template<class T> class PrimitiveArray;
@@ -162,6 +163,11 @@
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   ClassLoader* AsClassLoader() SHARED_REQUIRES(Locks::mutator_lock_);
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+  bool IsDexCache() SHARED_REQUIRES(Locks::mutator_lock_);
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+  DexCache* AsDexCache() SHARED_REQUIRES(Locks::mutator_lock_);
+
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
            ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
   bool IsArrayInstance() SHARED_REQUIRES(Locks::mutator_lock_);
diff --git a/runtime/native/dalvik_system_VMStack.cc b/runtime/native/dalvik_system_VMStack.cc
index 7910f94..9e12806 100644
--- a/runtime/native/dalvik_system_VMStack.cc
+++ b/runtime/native/dalvik_system_VMStack.cc
@@ -35,7 +35,7 @@
     trace = soa.Self()->CreateInternalStackTrace<false>(soa);
   } else {
     // Suspend thread to build stack trace.
-    ScopedThreadSuspension sts(soa.Self(), kSuspended);
+    ScopedThreadSuspension sts(soa.Self(), kNative);
     ThreadList* thread_list = Runtime::Current()->GetThreadList();
     bool timed_out;
     Thread* thread = thread_list->SuspendThreadByPeer(peer, true, false, &timed_out);
@@ -47,11 +47,9 @@
       }
       // Restart suspended thread.
       thread_list->Resume(thread, false);
-    } else {
-      if (timed_out) {
-        LOG(ERROR) << "Trying to get thread's stack failed as the thread failed to suspend within a "
-            "generous timeout.";
-      }
+    } else if (timed_out) {
+      LOG(ERROR) << "Trying to get thread's stack failed as the thread failed to suspend within a "
+          "generous timeout.";
     }
   }
   return trace;
diff --git a/runtime/native/java_lang_DexCache.cc b/runtime/native/java_lang_DexCache.cc
index a2d9797..994ccb1 100644
--- a/runtime/native/java_lang_DexCache.cc
+++ b/runtime/native/java_lang_DexCache.cc
@@ -52,12 +52,14 @@
 static jobject DexCache_getResolvedType(JNIEnv* env, jobject javaDexCache, jint type_index) {
   ScopedFastNativeObjectAccess soa(env);
   mirror::DexCache* dex_cache = soa.Decode<mirror::DexCache*>(javaDexCache);
+  CHECK_LT(static_cast<size_t>(type_index), dex_cache->NumResolvedTypes());
   return soa.AddLocalReference<jobject>(dex_cache->GetResolvedType(type_index));
 }
 
 static jobject DexCache_getResolvedString(JNIEnv* env, jobject javaDexCache, jint string_index) {
   ScopedFastNativeObjectAccess soa(env);
   mirror::DexCache* dex_cache = soa.Decode<mirror::DexCache*>(javaDexCache);
+  CHECK_LT(static_cast<size_t>(string_index), dex_cache->NumStrings());
   return soa.AddLocalReference<jobject>(dex_cache->GetResolvedString(string_index));
 }
 
@@ -65,6 +67,7 @@
                                      jobject type) {
   ScopedFastNativeObjectAccess soa(env);
   mirror::DexCache* dex_cache = soa.Decode<mirror::DexCache*>(javaDexCache);
+  CHECK_LT(static_cast<size_t>(type_index), dex_cache->NumResolvedTypes());
   dex_cache->SetResolvedType(type_index, soa.Decode<mirror::Class*>(type));
 }
 
@@ -72,6 +75,7 @@
                                        jobject string) {
   ScopedFastNativeObjectAccess soa(env);
   mirror::DexCache* dex_cache = soa.Decode<mirror::DexCache*>(javaDexCache);
+  CHECK_LT(static_cast<size_t>(string_index), dex_cache->NumStrings());
   dex_cache->SetResolvedString(string_index, soa.Decode<mirror::String*>(string));
 }
 
diff --git a/runtime/oat.h b/runtime/oat.h
index 1520a9b..b8b8d30 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -32,7 +32,7 @@
 class PACKED(4) OatHeader {
  public:
   static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' };
-  static constexpr uint8_t kOatVersion[] = { '0', '6', '9', '\0' };
+  static constexpr uint8_t kOatVersion[] = { '0', '7', '0', '\0' };
 
   static constexpr const char* kImageLocationKey = "image-location";
   static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index 25b5e49..50e2053 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -158,6 +158,9 @@
       .Define("-Xjitthreshold:_")
           .WithType<unsigned int>()
           .IntoKey(M::JITCompileThreshold)
+      .Define("-Xjitwarmupthreshold:_")
+          .WithType<unsigned int>()
+          .IntoKey(M::JITWarmupThreshold)
       .Define("-XX:HspaceCompactForOOMMinIntervalMs=_")  // in ms
           .WithType<MillisecondsToNanoseconds>()  // store as ns
           .IntoKey(M::HSpaceCompactForOOMMinIntervalsMs)
diff --git a/runtime/quick/inline_method_analyser.h b/runtime/quick/inline_method_analyser.h
index dd3703c..64c2249 100644
--- a/runtime/quick/inline_method_analyser.h
+++ b/runtime/quick/inline_method_analyser.h
@@ -40,6 +40,9 @@
   kIntrinsicReverseBits,
   kIntrinsicReverseBytes,
   kIntrinsicNumberOfLeadingZeros,
+  kIntrinsicNumberOfTrailingZeros,
+  kIntrinsicRotateRight,
+  kIntrinsicRotateLeft,
   kIntrinsicAbsInt,
   kIntrinsicAbsLong,
   kIntrinsicAbsFloat,
diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc
index 9d5ce9f..60defba 100644
--- a/runtime/quick_exception_handler.cc
+++ b/runtime/quick_exception_handler.cc
@@ -40,7 +40,7 @@
     handler_dex_pc_(0), clear_exception_(false), handler_frame_depth_(kInvalidFrameDepth) {
 }
 
-// Finds catch handler or prepares for deoptimization.
+// Finds catch handler.
 class CatchBlockStackVisitor FINAL : public StackVisitor {
  public:
   CatchBlockStackVisitor(Thread* self, Context* context, Handle<mirror::Throwable>* exception,
@@ -125,7 +125,7 @@
   StackHandleScope<1> hs(self_);
   Handle<mirror::Throwable> exception_ref(hs.NewHandle(exception));
 
-  // Walk the stack to find catch handler or prepare for deoptimization.
+  // Walk the stack to find catch handler.
   CatchBlockStackVisitor visitor(self_, context_, &exception_ref, this);
   visitor.WalkStack(true);
 
@@ -146,16 +146,6 @@
     // Put exception back in root set with clear throw location.
     self_->SetException(exception_ref.Get());
   }
-  // The debugger may suspend this thread and walk its stack. Let's do this before popping
-  // instrumentation frames.
-  instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
-  if (instrumentation->HasExceptionCaughtListeners()
-      && self_->IsExceptionThrownByCurrentMethod(exception)) {
-    instrumentation->ExceptionCaughtEvent(self_, exception_ref.Get());
-    // Instrumentation may have been updated.
-    method_tracing_active_ = is_deoptimization_ ||
-        Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled();
-  }
 }
 
 // Prepares deoptimization.
@@ -189,6 +179,12 @@
       // Ignore callee save method.
       DCHECK(method->IsCalleeSaveMethod());
       return true;
+    } else if (method->IsNative()) {
+      // If we return from JNI with a pending exception and want to deoptimize, we need to skip
+      // the native method.
+      // The top method is a runtime method, the native method comes next.
+      CHECK_EQ(GetFrameDepth(), 1U);
+      return true;
     } else {
       return HandleDeoptimization(method);
     }
@@ -201,7 +197,7 @@
 
   bool HandleDeoptimization(ArtMethod* m) SHARED_REQUIRES(Locks::mutator_lock_) {
     const DexFile::CodeItem* code_item = m->GetCodeItem();
-    CHECK(code_item != nullptr);
+    CHECK(code_item != nullptr) << "No code item for " << PrettyMethod(m);
     uint16_t num_regs = code_item->registers_size_;
     uint32_t dex_pc = GetDexPc();
     StackHandleScope<2> hs(self_);  // Dex cache, class loader and method.
diff --git a/runtime/quick_exception_handler.h b/runtime/quick_exception_handler.h
index e934834..4db95a8 100644
--- a/runtime/quick_exception_handler.h
+++ b/runtime/quick_exception_handler.h
@@ -43,9 +43,18 @@
     UNREACHABLE();
   }
 
+  // Find the catch handler for the given exception.
   void FindCatch(mirror::Throwable* exception) SHARED_REQUIRES(Locks::mutator_lock_);
+
+  // Deoptimize the stack to the upcall. For every compiled frame, we create a "copy"
+  // shadow frame that will be executed with the interpreter.
   void DeoptimizeStack() SHARED_REQUIRES(Locks::mutator_lock_);
+
+  // Update the instrumentation stack by removing all methods that will be unwound
+  // by the exception being thrown.
   void UpdateInstrumentationStack() SHARED_REQUIRES(Locks::mutator_lock_);
+
+  // Long jump either to a catch handler or to the upcall.
   NO_RETURN void DoLongJump() SHARED_REQUIRES(Locks::mutator_lock_);
 
   void SetHandlerQuickFrame(ArtMethod** handler_quick_frame) {
@@ -83,9 +92,10 @@
  private:
   Thread* const self_;
   Context* const context_;
+  // Should we deoptimize the stack?
   const bool is_deoptimization_;
   // Is method tracing active?
-  bool method_tracing_active_;
+  const bool method_tracing_active_;
   // Quick frame with found handler or last frame if no handler found.
   ArtMethod** handler_quick_frame_;
   // PC to branch to for the handler.
diff --git a/runtime/reflection.cc b/runtime/reflection.cc
index 2fe1e64..324bd9f 100644
--- a/runtime/reflection.cc
+++ b/runtime/reflection.cc
@@ -223,8 +223,11 @@
     for (size_t i = 1, args_offset = 0; i < shorty_len_; ++i, ++args_offset) {
       mirror::Object* arg = args->Get(args_offset);
       if (((shorty_[i] == 'L') && (arg != nullptr)) || ((arg == nullptr && shorty_[i] != 'L'))) {
+        size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
         mirror::Class* dst_class =
-            m->GetClassFromTypeIndex(classes->GetTypeItem(args_offset).type_idx_, true);
+            m->GetClassFromTypeIndex(classes->GetTypeItem(args_offset).type_idx_,
+                                     true /* resolve */,
+                                     pointer_size);
         if (UNLIKELY(arg == nullptr || !arg->InstanceOf(dst_class))) {
           ThrowIllegalArgumentException(
               StringPrintf("method %s argument %zd has type %s, got %s",
@@ -356,9 +359,12 @@
   }
   // TODO: If args contain object references, it may cause problems.
   Thread* const self = Thread::Current();
+  size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
   for (uint32_t i = 0; i < num_params; i++) {
     uint16_t type_idx = params->GetTypeItem(i).type_idx_;
-    mirror::Class* param_type = m->GetClassFromTypeIndex(type_idx, true);
+    mirror::Class* param_type = m->GetClassFromTypeIndex(type_idx,
+                                                         true /* resolve*/,
+                                                         pointer_size);
     if (param_type == nullptr) {
       CHECK(self->IsExceptionPending());
       LOG(ERROR) << "Internal error: unresolvable type for argument type in JNI invoke: "
@@ -453,7 +459,7 @@
   }
   mirror::Object* receiver = method->IsStatic() ? nullptr : soa.Decode<mirror::Object*>(obj);
   uint32_t shorty_len = 0;
-  const char* shorty = method->GetShorty(&shorty_len);
+  const char* shorty = method->GetInterfaceMethodIfProxy(sizeof(void*))->GetShorty(&shorty_len);
   JValue result;
   ArgArray arg_array(shorty, shorty_len);
   arg_array.BuildArgArrayFromVarArgs(soa, receiver, args);
@@ -483,7 +489,7 @@
   }
   mirror::Object* receiver = method->IsStatic() ? nullptr : soa.Decode<mirror::Object*>(obj);
   uint32_t shorty_len = 0;
-  const char* shorty = method->GetShorty(&shorty_len);
+  const char* shorty = method->GetInterfaceMethodIfProxy(sizeof(void*))->GetShorty(&shorty_len);
   JValue result;
   ArgArray arg_array(shorty, shorty_len);
   arg_array.BuildArgArrayFromJValues(soa, receiver, args);
@@ -514,7 +520,7 @@
     receiver = nullptr;
   }
   uint32_t shorty_len = 0;
-  const char* shorty = method->GetShorty(&shorty_len);
+  const char* shorty = method->GetInterfaceMethodIfProxy(sizeof(void*))->GetShorty(&shorty_len);
   JValue result;
   ArgArray arg_array(shorty, shorty_len);
   arg_array.BuildArgArrayFromJValues(soa, receiver, args);
@@ -545,7 +551,7 @@
     receiver = nullptr;
   }
   uint32_t shorty_len = 0;
-  const char* shorty = method->GetShorty(&shorty_len);
+  const char* shorty = method->GetInterfaceMethodIfProxy(sizeof(void*))->GetShorty(&shorty_len);
   JValue result;
   ArgArray arg_array(shorty, shorty_len);
   arg_array.BuildArgArrayFromVarArgs(soa, receiver, args);
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 3119cce..bbadb1e 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1751,7 +1751,8 @@
   jit_.reset(jit::Jit::Create(jit_options_.get(), &error_msg));
   if (jit_.get() != nullptr) {
     compiler_callbacks_ = jit_->GetCompilerCallbacks();
-    jit_->CreateInstrumentationCache(jit_options_->GetCompileThreshold());
+    jit_->CreateInstrumentationCache(jit_options_->GetCompileThreshold(),
+                                     jit_options_->GetWarmupThreshold());
     jit_->CreateThreadPool();
   } else {
     LOG(WARNING) << "Failed to create JIT " << error_msg;
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index 02ed3a2..d88e84b 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -68,6 +68,7 @@
 RUNTIME_OPTIONS_KEY (bool,                EnableHSpaceCompactForOOM,      true)
 RUNTIME_OPTIONS_KEY (bool,                UseJIT,                         false)
 RUNTIME_OPTIONS_KEY (unsigned int,        JITCompileThreshold,            jit::Jit::kDefaultCompileThreshold)
+RUNTIME_OPTIONS_KEY (unsigned int,        JITWarmupThreshold,             jit::Jit::kDefaultWarmupThreshold)
 RUNTIME_OPTIONS_KEY (MemoryKiB,           JITCodeCacheCapacity,           jit::JitCodeCache::kDefaultCapacity)
 RUNTIME_OPTIONS_KEY (MillisecondsToNanoseconds, \
                                           HSpaceCompactForOOMMinIntervalsMs,\
diff --git a/runtime/thread.cc b/runtime/thread.cc
index af5830a..86ac140 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -2344,10 +2344,31 @@
   // Get exception from thread.
   mirror::Throwable* exception = GetException();
   CHECK(exception != nullptr);
+  bool is_deoptimization = (exception == GetDeoptimizationException());
+  if (!is_deoptimization) {
+    // This is a real exception: let the instrumentation know about it.
+    instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
+    if (instrumentation->HasExceptionCaughtListeners() &&
+        IsExceptionThrownByCurrentMethod(exception)) {
+      // Instrumentation may cause GC so keep the exception object safe.
+      StackHandleScope<1> hs(this);
+      HandleWrapper<mirror::Throwable> h_exception(hs.NewHandleWrapper(&exception));
+      instrumentation->ExceptionCaughtEvent(this, exception);
+    }
+    // Does instrumentation need to deoptimize the stack?
+    // Note: we do this *after* reporting the exception to instrumentation in case it
+    // now requires deoptimization. It may happen if a debugger is attached and requests
+    // new events (single-step, breakpoint, ...) when the exception is reported.
+    is_deoptimization = Dbg::IsForcedInterpreterNeededForException(this);
+    if (is_deoptimization) {
+      // Save the exception into the deoptimization context so it can be restored
+      // before entering the interpreter.
+      PushDeoptimizationContext(JValue(), false, exception);
+    }
+  }
   // Don't leave exception visible while we try to find the handler, which may cause class
   // resolution.
   ClearException();
-  bool is_deoptimization = (exception == GetDeoptimizationException());
   QuickExceptionHandler exception_handler(this, is_deoptimization);
   if (is_deoptimization) {
     exception_handler.DeoptimizeStack();
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index d63781b..6176acd 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -1282,4 +1282,12 @@
   allocated_ids_.reset(id);
 }
 
+ScopedSuspendAll::ScopedSuspendAll(const char* cause, bool long_suspend) {
+  Runtime::Current()->GetThreadList()->SuspendAll(cause, long_suspend);
+}
+
+ScopedSuspendAll::~ScopedSuspendAll() {
+  Runtime::Current()->GetThreadList()->ResumeAll();
+}
+
 }  // namespace art
diff --git a/runtime/thread_list.h b/runtime/thread_list.h
index 4c50181..c727432 100644
--- a/runtime/thread_list.h
+++ b/runtime/thread_list.h
@@ -19,6 +19,7 @@
 
 #include "base/histogram.h"
 #include "base/mutex.h"
+#include "base/value_object.h"
 #include "gc_root.h"
 #include "jni.h"
 #include "object_callbacks.h"
@@ -60,12 +61,13 @@
       REQUIRES(!Locks::thread_suspend_count_lock_);
 
   // Suspends all threads and gets exclusive access to the mutator_lock_.
-  // If long suspend is true, then other people who try to suspend will never timeout. Long suspend
-  // is currenly used for hprof since large heaps take a long time.
+  // If long_suspend is true, then other threads who try to suspend will never timeout.
+  // long_suspend is currenly used for hprof since large heaps take a long time.
   void SuspendAll(const char* cause, bool long_suspend = false)
       EXCLUSIVE_LOCK_FUNCTION(Locks::mutator_lock_)
-      REQUIRES(!Locks::thread_list_lock_, !Locks::thread_suspend_count_lock_);
-
+      REQUIRES(!Locks::thread_list_lock_,
+               !Locks::thread_suspend_count_lock_,
+               !Locks::mutator_lock_);
 
   // Suspend a thread using a peer, typically used by the debugger. Returns the thread on success,
   // else null. The peer is used to identify the thread to avoid races with the thread terminating.
@@ -188,6 +190,20 @@
   DISALLOW_COPY_AND_ASSIGN(ThreadList);
 };
 
+// Helper for suspending all threads and
+class ScopedSuspendAll : public ValueObject {
+ public:
+  ScopedSuspendAll(const char* cause, bool long_suspend = false)
+     EXCLUSIVE_LOCK_FUNCTION(Locks::mutator_lock_)
+     REQUIRES(!Locks::thread_list_lock_,
+              !Locks::thread_suspend_count_lock_,
+              !Locks::mutator_lock_);
+  // No REQUIRES(mutator_lock_) since the unlock function already asserts this.
+  ~ScopedSuspendAll()
+      UNLOCK_FUNCTION(Locks::mutator_lock_)
+      REQUIRES(!Locks::thread_list_lock_, !Locks::thread_suspend_count_lock_);
+};
+
 }  // namespace art
 
 #endif  // ART_RUNTIME_THREAD_LIST_H_
diff --git a/runtime/trace.cc b/runtime/trace.cc
index 4ab5c0e..5a44947 100644
--- a/runtime/trace.cc
+++ b/runtime/trace.cc
@@ -293,13 +293,11 @@
         break;
       }
     }
-
-    runtime->GetThreadList()->SuspendAll(__FUNCTION__);
     {
+      ScopedSuspendAll ssa(__FUNCTION__);
       MutexLock mu(self, *Locks::thread_list_lock_);
       runtime->GetThreadList()->ForEach(GetSample, the_trace);
     }
-    runtime->GetThreadList()->ResumeAll();
     ATRACE_END();
   }
 
@@ -348,10 +346,9 @@
   // Enable count of allocs if specified in the flags.
   bool enable_stats = false;
 
-  runtime->GetThreadList()->SuspendAll(__FUNCTION__);
-
   // Create Trace object.
   {
+    ScopedSuspendAll ssa(__FUNCTION__);
     MutexLock mu(self, *Locks::trace_lock_);
     if (the_trace_ != nullptr) {
       LOG(ERROR) << "Trace already in progress, ignoring this request";
@@ -375,8 +372,6 @@
     }
   }
 
-  runtime->GetThreadList()->ResumeAll();
-
   // Can't call this when holding the mutator lock.
   if (enable_stats) {
     runtime->SetStatsEnabled(true);
@@ -405,40 +400,41 @@
     CHECK_PTHREAD_CALL(pthread_join, (sampling_pthread, nullptr), "sampling thread shutdown");
     sampling_pthread_ = 0U;
   }
-  runtime->GetThreadList()->SuspendAll(__FUNCTION__);
 
-  if (the_trace != nullptr) {
-    stop_alloc_counting = (the_trace->flags_ & Trace::kTraceCountAllocs) != 0;
-    if (finish_tracing) {
-      the_trace->FinishTracing();
-    }
+  {
+    ScopedSuspendAll ssa(__FUNCTION__);
+    if (the_trace != nullptr) {
+      stop_alloc_counting = (the_trace->flags_ & Trace::kTraceCountAllocs) != 0;
+      if (finish_tracing) {
+        the_trace->FinishTracing();
+      }
 
-    if (the_trace->trace_mode_ == TraceMode::kSampling) {
-      MutexLock mu(Thread::Current(), *Locks::thread_list_lock_);
-      runtime->GetThreadList()->ForEach(ClearThreadStackTraceAndClockBase, nullptr);
-    } else {
-      runtime->GetInstrumentation()->DisableMethodTracing(kTracerInstrumentationKey);
-      runtime->GetInstrumentation()->RemoveListener(
-          the_trace, instrumentation::Instrumentation::kMethodEntered |
-          instrumentation::Instrumentation::kMethodExited |
-          instrumentation::Instrumentation::kMethodUnwind);
-    }
-    if (the_trace->trace_file_.get() != nullptr) {
-      // Do not try to erase, so flush and close explicitly.
-      if (flush_file) {
-        if (the_trace->trace_file_->Flush() != 0) {
-          PLOG(WARNING) << "Could not flush trace file.";
-        }
+      if (the_trace->trace_mode_ == TraceMode::kSampling) {
+        MutexLock mu(Thread::Current(), *Locks::thread_list_lock_);
+        runtime->GetThreadList()->ForEach(ClearThreadStackTraceAndClockBase, nullptr);
       } else {
-        the_trace->trace_file_->MarkUnchecked();  // Do not trigger guard.
+        runtime->GetInstrumentation()->DisableMethodTracing(kTracerInstrumentationKey);
+        runtime->GetInstrumentation()->RemoveListener(
+            the_trace, instrumentation::Instrumentation::kMethodEntered |
+            instrumentation::Instrumentation::kMethodExited |
+            instrumentation::Instrumentation::kMethodUnwind);
       }
-      if (the_trace->trace_file_->Close() != 0) {
-        PLOG(ERROR) << "Could not close trace file.";
+      if (the_trace->trace_file_.get() != nullptr) {
+        // Do not try to erase, so flush and close explicitly.
+        if (flush_file) {
+          if (the_trace->trace_file_->Flush() != 0) {
+            PLOG(WARNING) << "Could not flush trace file.";
+          }
+        } else {
+          the_trace->trace_file_->MarkUnchecked();  // Do not trigger guard.
+        }
+        if (the_trace->trace_file_->Close() != 0) {
+          PLOG(ERROR) << "Could not close trace file.";
+        }
       }
+      delete the_trace;
     }
-    delete the_trace;
   }
-  runtime->GetThreadList()->ResumeAll();
   if (stop_alloc_counting) {
     // Can be racy since SetStatsEnabled is not guarded by any locks.
     runtime->SetStatsEnabled(false);
@@ -492,7 +488,7 @@
   }
 
   if (the_trace != nullptr) {
-    runtime->GetThreadList()->SuspendAll(__FUNCTION__);
+    ScopedSuspendAll ssa(__FUNCTION__);
     stop_alloc_counting = (the_trace->flags_ & Trace::kTraceCountAllocs) != 0;
 
     if (the_trace->trace_mode_ == TraceMode::kSampling) {
@@ -500,12 +496,12 @@
       runtime->GetThreadList()->ForEach(ClearThreadStackTraceAndClockBase, nullptr);
     } else {
       runtime->GetInstrumentation()->DisableMethodTracing(kTracerInstrumentationKey);
-      runtime->GetInstrumentation()->RemoveListener(the_trace,
-                                                    instrumentation::Instrumentation::kMethodEntered |
-                                                    instrumentation::Instrumentation::kMethodExited |
-                                                    instrumentation::Instrumentation::kMethodUnwind);
+      runtime->GetInstrumentation()->RemoveListener(
+          the_trace,
+          instrumentation::Instrumentation::kMethodEntered |
+          instrumentation::Instrumentation::kMethodExited |
+          instrumentation::Instrumentation::kMethodUnwind);
     }
-    runtime->GetThreadList()->ResumeAll();
   }
 
   if (stop_alloc_counting) {
@@ -531,23 +527,23 @@
   // Enable count of allocs if specified in the flags.
   bool enable_stats = (the_trace->flags_ && kTraceCountAllocs) != 0;
 
-  runtime->GetThreadList()->SuspendAll(__FUNCTION__);
+  {
+    ScopedSuspendAll ssa(__FUNCTION__);
 
-  // Reenable.
-  if (the_trace->trace_mode_ == TraceMode::kSampling) {
-    CHECK_PTHREAD_CALL(pthread_create, (&sampling_pthread_, nullptr, &RunSamplingThread,
-        reinterpret_cast<void*>(the_trace->interval_us_)), "Sampling profiler thread");
-  } else {
-    runtime->GetInstrumentation()->AddListener(the_trace,
-                                               instrumentation::Instrumentation::kMethodEntered |
-                                               instrumentation::Instrumentation::kMethodExited |
-                                               instrumentation::Instrumentation::kMethodUnwind);
-    // TODO: In full-PIC mode, we don't need to fully deopt.
-    runtime->GetInstrumentation()->EnableMethodTracing(kTracerInstrumentationKey);
+    // Reenable.
+    if (the_trace->trace_mode_ == TraceMode::kSampling) {
+      CHECK_PTHREAD_CALL(pthread_create, (&sampling_pthread_, nullptr, &RunSamplingThread,
+          reinterpret_cast<void*>(the_trace->interval_us_)), "Sampling profiler thread");
+    } else {
+      runtime->GetInstrumentation()->AddListener(the_trace,
+                                                 instrumentation::Instrumentation::kMethodEntered |
+                                                 instrumentation::Instrumentation::kMethodExited |
+                                                 instrumentation::Instrumentation::kMethodUnwind);
+      // TODO: In full-PIC mode, we don't need to fully deopt.
+      runtime->GetInstrumentation()->EnableMethodTracing(kTracerInstrumentationKey);
+    }
   }
 
-  runtime->GetThreadList()->ResumeAll();
-
   // Can't call this when holding the mutator lock.
   if (enable_stats) {
     runtime->SetStatsEnabled(true);
@@ -806,6 +802,15 @@
   LOG(ERROR) << "Unexpected backward branch event in tracing" << PrettyMethod(method);
 }
 
+void Trace::InvokeVirtualOrInterface(Thread*,
+                                     mirror::Object*,
+                                     ArtMethod* method,
+                                     uint32_t dex_pc,
+                                     ArtMethod*) {
+  LOG(ERROR) << "Unexpected invoke event in tracing" << PrettyMethod(method)
+             << " " << dex_pc;
+}
+
 void Trace::ReadClocks(Thread* thread, uint32_t* thread_clock_diff, uint32_t* wall_clock_diff) {
   if (UseThreadCpuClock()) {
     uint64_t clock_base = thread->GetTraceClockBase();
diff --git a/runtime/trace.h b/runtime/trace.h
index 04be3dd..87a691d 100644
--- a/runtime/trace.h
+++ b/runtime/trace.h
@@ -166,6 +166,12 @@
       SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_) OVERRIDE;
   void BackwardBranch(Thread* thread, ArtMethod* method, int32_t dex_pc_offset)
       SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_) OVERRIDE;
+  void InvokeVirtualOrInterface(Thread* thread,
+                                mirror::Object* this_object,
+                                ArtMethod* caller,
+                                uint32_t dex_pc,
+                                ArtMethod* callee)
+      SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_) OVERRIDE;
   // Reuse an old stack trace if it exists, otherwise allocate a new one.
   static std::vector<ArtMethod*>* AllocStackTrace();
   // Clear and store an old stack trace for later use.
diff --git a/runtime/utils/dex_cache_arrays_layout-inl.h b/runtime/utils/dex_cache_arrays_layout-inl.h
new file mode 100644
index 0000000..4f662d5
--- /dev/null
+++ b/runtime/utils/dex_cache_arrays_layout-inl.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2015 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_UTILS_DEX_CACHE_ARRAYS_LAYOUT_INL_H_
+#define ART_RUNTIME_UTILS_DEX_CACHE_ARRAYS_LAYOUT_INL_H_
+
+#include "dex_cache_arrays_layout.h"
+
+#include "base/bit_utils.h"
+#include "base/logging.h"
+#include "gc_root.h"
+#include "globals.h"
+#include "primitive.h"
+
+namespace art {
+
+inline DexCacheArraysLayout::DexCacheArraysLayout(size_t pointer_size, const DexFile* dex_file)
+    : pointer_size_(pointer_size),
+      /* types_offset_ is always 0u, so it's constexpr */
+      methods_offset_(types_offset_ +
+                      RoundUp(TypesSize(dex_file->NumTypeIds()), MethodsAlignment())),
+      strings_offset_(methods_offset_ +
+                      RoundUp(MethodsSize(dex_file->NumMethodIds()), StringsAlignment())),
+      fields_offset_(strings_offset_ +
+                     RoundUp(StringsSize(dex_file->NumStringIds()), FieldsAlignment())),
+      size_(fields_offset_ +
+            RoundUp(FieldsSize(dex_file->NumFieldIds()), Alignment())) {
+  DCHECK(ValidPointerSize(pointer_size)) << pointer_size;
+}
+
+inline size_t DexCacheArraysLayout::Alignment() const {
+  // GcRoot<> alignment is 4, i.e. lower than or equal to the pointer alignment.
+  static_assert(alignof(GcRoot<mirror::Class>) == 4, "Expecting alignof(GcRoot<>) == 4");
+  static_assert(alignof(GcRoot<mirror::String>) == 4, "Expecting alignof(GcRoot<>) == 4");
+  DCHECK(pointer_size_ == 4u || pointer_size_ == 8u);
+  // Pointer alignment is the same as pointer size.
+  return pointer_size_;
+}
+
+inline size_t DexCacheArraysLayout::TypeOffset(uint32_t type_idx) const {
+  return types_offset_ + ElementOffset(sizeof(GcRoot<mirror::Class>), type_idx);
+}
+
+inline size_t DexCacheArraysLayout::TypesSize(size_t num_elements) const {
+  return ArraySize(sizeof(GcRoot<mirror::Class>), num_elements);
+}
+
+inline size_t DexCacheArraysLayout::TypesAlignment() const {
+  return alignof(GcRoot<mirror::Class>);
+}
+
+inline size_t DexCacheArraysLayout::MethodOffset(uint32_t method_idx) const {
+  return methods_offset_ + ElementOffset(pointer_size_, method_idx);
+}
+
+inline size_t DexCacheArraysLayout::MethodsSize(size_t num_elements) const {
+  return ArraySize(pointer_size_, num_elements);
+}
+
+inline size_t DexCacheArraysLayout::MethodsAlignment() const {
+  return pointer_size_;
+}
+
+inline size_t DexCacheArraysLayout::StringOffset(uint32_t string_idx) const {
+  return strings_offset_ + ElementOffset(sizeof(GcRoot<mirror::String>), string_idx);
+}
+
+inline size_t DexCacheArraysLayout::StringsSize(size_t num_elements) const {
+  return ArraySize(sizeof(GcRoot<mirror::String>), num_elements);
+}
+
+inline size_t DexCacheArraysLayout::StringsAlignment() const {
+  return alignof(GcRoot<mirror::String>);
+}
+
+inline size_t DexCacheArraysLayout::FieldOffset(uint32_t field_idx) const {
+  return fields_offset_ + ElementOffset(pointer_size_, field_idx);
+}
+
+inline size_t DexCacheArraysLayout::FieldsSize(size_t num_elements) const {
+  return ArraySize(pointer_size_, num_elements);
+}
+
+inline size_t DexCacheArraysLayout::FieldsAlignment() const {
+  return pointer_size_;
+}
+
+inline size_t DexCacheArraysLayout::ElementOffset(size_t element_size, uint32_t idx) {
+  return element_size * idx;
+}
+
+inline size_t DexCacheArraysLayout::ArraySize(size_t element_size, uint32_t num_elements) {
+  return element_size * num_elements;
+}
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_UTILS_DEX_CACHE_ARRAYS_LAYOUT_INL_H_
diff --git a/compiler/utils/dex_cache_arrays_layout.h b/runtime/utils/dex_cache_arrays_layout.h
similarity index 86%
rename from compiler/utils/dex_cache_arrays_layout.h
rename to runtime/utils/dex_cache_arrays_layout.h
index 2a109bd..d50be5a 100644
--- a/compiler/utils/dex_cache_arrays_layout.h
+++ b/runtime/utils/dex_cache_arrays_layout.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_COMPILER_UTILS_DEX_CACHE_ARRAYS_LAYOUT_H_
-#define ART_COMPILER_UTILS_DEX_CACHE_ARRAYS_LAYOUT_H_
+#ifndef ART_RUNTIME_UTILS_DEX_CACHE_ARRAYS_LAYOUT_H_
+#define ART_RUNTIME_UTILS_DEX_CACHE_ARRAYS_LAYOUT_H_
 
 namespace art {
 
@@ -47,6 +47,8 @@
     return size_;
   }
 
+  size_t Alignment() const;
+
   size_t TypesOffset() const {
     return types_offset_;
   }
@@ -55,6 +57,8 @@
 
   size_t TypesSize(size_t num_elements) const;
 
+  size_t TypesAlignment() const;
+
   size_t MethodsOffset() const {
     return methods_offset_;
   }
@@ -63,6 +67,8 @@
 
   size_t MethodsSize(size_t num_elements) const;
 
+  size_t MethodsAlignment() const;
+
   size_t StringsOffset() const {
     return strings_offset_;
   }
@@ -71,6 +77,8 @@
 
   size_t StringsSize(size_t num_elements) const;
 
+  size_t StringsAlignment() const;
+
   size_t FieldsOffset() const {
     return fields_offset_;
   }
@@ -79,6 +87,8 @@
 
   size_t FieldsSize(size_t num_elements) const;
 
+  size_t FieldsAlignment() const;
+
  private:
   static constexpr size_t types_offset_ = 0u;
   const size_t pointer_size_;  // Must be first for construction initialization order.
@@ -87,6 +97,8 @@
   const size_t fields_offset_;
   const size_t size_;
 
+  static size_t Alignment(size_t pointer_size);
+
   static size_t ElementOffset(size_t element_size, uint32_t idx);
 
   static size_t ArraySize(size_t element_size, uint32_t num_elements);
@@ -94,4 +106,4 @@
 
 }  // namespace art
 
-#endif  // ART_COMPILER_UTILS_DEX_CACHE_ARRAYS_LAYOUT_H_
+#endif  // ART_RUNTIME_UTILS_DEX_CACHE_ARRAYS_LAYOUT_H_
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 1d71c3f..3d4f04c 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -425,6 +425,7 @@
       has_virtual_or_interface_invokes_(false),
       verify_to_dump_(verify_to_dump),
       allow_thread_suspension_(allow_thread_suspension),
+      is_constructor_(false),
       link_(nullptr) {
   self->PushVerifier(this);
   DCHECK(class_def != nullptr);
@@ -556,15 +557,124 @@
 }
 
 bool MethodVerifier::Verify() {
-  // If there aren't any instructions, make sure that's expected, then exit successfully.
-  if (code_item_ == nullptr) {
-    if ((method_access_flags_ & (kAccNative | kAccAbstract)) == 0) {
-      Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "zero-length code in concrete non-native method";
+  // Some older code doesn't correctly mark constructors as such. Test for this case by looking at
+  // the name.
+  const DexFile::MethodId& method_id = dex_file_->GetMethodId(dex_method_idx_);
+  const char* method_name = dex_file_->StringDataByIdx(method_id.name_idx_);
+  bool instance_constructor_by_name = strcmp("<init>", method_name) == 0;
+  bool static_constructor_by_name = strcmp("<clinit>", method_name) == 0;
+  bool constructor_by_name = instance_constructor_by_name || static_constructor_by_name;
+  // Check that only constructors are tagged, and check for bad code that doesn't tag constructors.
+  if ((method_access_flags_ & kAccConstructor) != 0) {
+    if (!constructor_by_name) {
+      Fail(VERIFY_ERROR_BAD_CLASS_HARD)
+            << "method is marked as constructor, but not named accordingly";
       return false;
-    } else {
-      return true;
+    }
+    is_constructor_ = true;
+  } else if (constructor_by_name) {
+    LOG(WARNING) << "Method " << PrettyMethod(dex_method_idx_, *dex_file_)
+                 << " not marked as constructor.";
+    is_constructor_ = true;
+  }
+  // If it's a constructor, check whether IsStatic() matches the name.
+  // This should have been rejected by the dex file verifier. Only do in debug build.
+  if (kIsDebugBuild) {
+    if (IsConstructor()) {
+      if (IsStatic() ^ static_constructor_by_name) {
+        Fail(VERIFY_ERROR_BAD_CLASS_HARD)
+              << "constructor name doesn't match static flag";
+        return false;
+      }
     }
   }
+
+  // Methods may only have one of public/protected/private.
+  // This should have been rejected by the dex file verifier. Only do in debug build.
+  if (kIsDebugBuild) {
+    size_t access_mod_count =
+        (((method_access_flags_ & kAccPublic) == 0) ? 0 : 1) +
+        (((method_access_flags_ & kAccProtected) == 0) ? 0 : 1) +
+        (((method_access_flags_ & kAccPrivate) == 0) ? 0 : 1);
+    if (access_mod_count > 1) {
+      Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "method has more than one of public/protected/private";
+      return false;
+    }
+  }
+
+  // If there aren't any instructions, make sure that's expected, then exit successfully.
+  if (code_item_ == nullptr) {
+    // This should have been rejected by the dex file verifier. Only do in debug build.
+    if (kIsDebugBuild) {
+      // Only native or abstract methods may not have code.
+      if ((method_access_flags_ & (kAccNative | kAccAbstract)) == 0) {
+        Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "zero-length code in concrete non-native method";
+        return false;
+      }
+      if ((method_access_flags_ & kAccAbstract) != 0) {
+        // Abstract methods are not allowed to have the following flags.
+        static constexpr uint32_t kForbidden =
+            kAccPrivate |
+            kAccStatic |
+            kAccFinal |
+            kAccNative |
+            kAccStrict |
+            kAccSynchronized;
+        if ((method_access_flags_ & kForbidden) != 0) {
+          Fail(VERIFY_ERROR_BAD_CLASS_HARD)
+                << "method can't be abstract and private/static/final/native/strict/synchronized";
+          return false;
+        }
+      }
+      if ((class_def_->GetJavaAccessFlags() & kAccInterface) != 0) {
+        // Interface methods must be public and abstract.
+        if ((method_access_flags_ & (kAccPublic | kAccAbstract)) != (kAccPublic | kAccAbstract)) {
+          Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "interface methods must be public and abstract";
+          return false;
+        }
+        // In addition to the above, interface methods must not be protected.
+        static constexpr uint32_t kForbidden = kAccProtected;
+        if ((method_access_flags_ & kForbidden) != 0) {
+          Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "interface methods can't be protected";
+          return false;
+        }
+      }
+      // We also don't allow constructors to be abstract or native.
+      if (IsConstructor()) {
+        Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "constructors can't be abstract or native";
+        return false;
+      }
+    }
+    return true;
+  }
+
+  // This should have been rejected by the dex file verifier. Only do in debug build.
+  if (kIsDebugBuild) {
+    // When there's code, the method must not be native or abstract.
+    if ((method_access_flags_ & (kAccNative | kAccAbstract)) != 0) {
+      Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "non-zero-length code in abstract or native method";
+      return false;
+    }
+
+    // Only the static initializer may have code in an interface.
+    if ((class_def_->GetJavaAccessFlags() & kAccInterface) != 0) {
+      // Interfaces may have static initializers for their fields.
+      if (!IsConstructor() || !IsStatic()) {
+        Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "interface methods must be abstract";
+        return false;
+      }
+    }
+
+    // Instance constructors must not be synchronized.
+    if (IsInstanceConstructor()) {
+      static constexpr uint32_t kForbidden = kAccSynchronized;
+      if ((method_access_flags_ & kForbidden) != 0) {
+        Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "constructors can't be synchronized";
+        return false;
+      }
+    }
+  }
+
   // Sanity-check the register counts. ins + locals = registers, so make sure that ins <= registers.
   if (code_item_->ins_size_ > code_item_->registers_size_) {
     Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad register counts (ins=" << code_item_->ins_size_
@@ -2542,8 +2652,9 @@
       ArtMethod* called_method = VerifyInvocationArgs(inst, METHOD_VIRTUAL, is_range, is_super);
       const RegType* return_type = nullptr;
       if (called_method != nullptr) {
-        StackHandleScope<1> hs(self_);
-        mirror::Class* return_type_class = called_method->GetReturnType(can_load_classes_);
+        size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
+        mirror::Class* return_type_class = called_method->GetReturnType(can_load_classes_,
+                                                                        pointer_size);
         if (return_type_class != nullptr) {
           return_type = &FromClass(called_method->GetReturnTypeDescriptor(),
                                    return_type_class,
@@ -2584,8 +2695,9 @@
       } else {
         is_constructor = called_method->IsConstructor();
         return_type_descriptor = called_method->GetReturnTypeDescriptor();
-        StackHandleScope<1> hs(self_);
-        mirror::Class* return_type_class = called_method->GetReturnType(can_load_classes_);
+        size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
+        mirror::Class* return_type_class = called_method->GetReturnType(can_load_classes_,
+                                                                        pointer_size);
         if (return_type_class != nullptr) {
           return_type = &FromClass(return_type_descriptor,
                                    return_type_class,
@@ -4494,7 +4606,9 @@
 const RegType& MethodVerifier::GetMethodReturnType() {
   if (return_type_ == nullptr) {
     if (mirror_method_ != nullptr) {
-      mirror::Class* return_type_class = mirror_method_->GetReturnType(can_load_classes_);
+      size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
+      mirror::Class* return_type_class = mirror_method_->GetReturnType(can_load_classes_,
+                                                                       pointer_size);
       if (return_type_class != nullptr) {
         return_type_ = &FromClass(mirror_method_->GetReturnTypeDescriptor(),
                                   return_type_class,
diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h
index 0295ef8..ba694b7 100644
--- a/runtime/verifier/method_verifier.h
+++ b/runtime/verifier/method_verifier.h
@@ -263,20 +263,6 @@
   ArtField* GetQuickFieldAccess(const Instruction* inst, RegisterLine* reg_line)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
-  // Is the method being verified a constructor?
-  bool IsConstructor() const {
-    return (method_access_flags_ & kAccConstructor) != 0;
-  }
-
-  // Is the method verified static?
-  bool IsStatic() const {
-    return (method_access_flags_ & kAccStatic) != 0;
-  }
-
-  bool IsInstanceConstructor() const {
-    return IsConstructor() && !IsStatic();
-  }
-
   SafeMap<uint32_t, std::set<uint32_t>>& GetStringInitPcRegMap() {
     return string_init_pc_reg_map_;
   }
@@ -285,7 +271,21 @@
     return encountered_failure_types_;
   }
 
+  bool IsInstanceConstructor() const {
+    return IsConstructor() && !IsStatic();
+  }
+
  private:
+  // Is the method being verified a constructor? See the comment on the field.
+  bool IsConstructor() const {
+    return is_constructor_;
+  }
+
+  // Is the method verified static?
+  bool IsStatic() const {
+    return (method_access_flags_ & kAccStatic) != 0;
+  }
+
   // Private constructor for dumping.
   MethodVerifier(Thread* self, const DexFile* dex_file, Handle<mirror::DexCache> dex_cache,
                  Handle<mirror::ClassLoader> class_loader, const DexFile::ClassDef* class_def,
@@ -781,6 +781,13 @@
   // FindLocksAtDexPC, resulting in deadlocks.
   const bool allow_thread_suspension_;
 
+  // Whether the method seems to be a constructor. Note that this field exists as we can't trust
+  // the flags in the dex file. Some older code does not mark methods named "<init>" and "<clinit>"
+  // correctly.
+  //
+  // Note: this flag is only valid once Verify() has started.
+  bool is_constructor_;
+
   // Link, for the method verifier root linked list.
   MethodVerifier* link_;
 
diff --git a/test/044-proxy/expected.txt b/test/044-proxy/expected.txt
index bcce019..f86948a 100644
--- a/test/044-proxy/expected.txt
+++ b/test/044-proxy/expected.txt
@@ -93,3 +93,4 @@
 Got expected exception
 Proxy narrowed invocation return type passed
 5.8
+callback
diff --git a/test/044-proxy/native_proxy.cc b/test/044-proxy/native_proxy.cc
new file mode 100644
index 0000000..f168719
--- /dev/null
+++ b/test/044-proxy/native_proxy.cc
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#include "jni.h"
+
+#include "base/logging.h"
+
+namespace art {
+
+extern "C" JNIEXPORT void JNICALL Java_NativeProxy_nativeCall(
+    JNIEnv* env, jclass clazz ATTRIBUTE_UNUSED, jobject inf_ref) {
+  jclass native_inf_class = env->FindClass("NativeInterface");
+  CHECK(native_inf_class != nullptr);
+  jmethodID mid = env->GetMethodID(native_inf_class, "callback", "()V");
+  CHECK(mid != nullptr);
+  env->CallVoidMethod(inf_ref, mid);
+}
+
+}  // namespace art
diff --git a/test/044-proxy/src/Main.java b/test/044-proxy/src/Main.java
index 9580871..05e8e5b 100644
--- a/test/044-proxy/src/Main.java
+++ b/test/044-proxy/src/Main.java
@@ -28,5 +28,6 @@
         WrappedThrow.main(null);
         NarrowingTest.main(null);
         FloatSelect.main(null);
+        NativeProxy.main(args);
     }
 }
diff --git a/test/044-proxy/src/NativeProxy.java b/test/044-proxy/src/NativeProxy.java
new file mode 100644
index 0000000..b425da8
--- /dev/null
+++ b/test/044-proxy/src/NativeProxy.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.Arrays;
+import java.util.Comparator;
+
+/**
+ * Test invoking a proxy method from native code.
+ */
+
+interface NativeInterface {
+    public void callback();
+}
+
+public class NativeProxy {
+
+    public static void main(String[] args) {
+        System.loadLibrary(args[0]);
+
+        try {
+            NativeInterface inf = (NativeInterface)Proxy.newProxyInstance(
+                    NativeProxy.class.getClassLoader(),
+                    new Class[] { NativeInterface.class },
+                    new NativeInvocationHandler());
+
+            nativeCall(inf);
+        } catch (Exception exc) {
+            throw new RuntimeException(exc);
+        }
+    }
+
+    public static class NativeInvocationHandler implements InvocationHandler {
+        public Object invoke(final Object proxy,
+                             final Method method,
+                             final Object[] args) throws Throwable {
+            System.out.println(method.getName());
+            return null;
+        }
+    }
+
+    public static native void nativeCall(NativeInterface inf);
+}
diff --git a/test/082-inline-execute/src/Main.java b/test/082-inline-execute/src/Main.java
index 08ccf0e..5913c40 100644
--- a/test/082-inline-execute/src/Main.java
+++ b/test/082-inline-execute/src/Main.java
@@ -78,6 +78,14 @@
     test_Memory_pokeShort();
     test_Memory_pokeInt();
     test_Memory_pokeLong();
+    test_Integer_numberOfTrailingZeros();
+    test_Long_numberOfTrailingZeros();
+    test_Integer_rotateRight();
+    test_Long_rotateRight();
+    test_Integer_rotateLeft();
+    test_Long_rotateLeft();
+    test_Integer_rotateRightLeft();
+    test_Long_rotateRightLeft();
   }
 
   /**
@@ -1360,4 +1368,136 @@
     poke_long.invoke(null, address + 1, (long)0x2122232425262728L, false);
     Assert.assertTrue(Arrays.equals(ru, b));
   }
+
+  public static void test_Integer_numberOfTrailingZeros() {
+    Assert.assertEquals(Integer.numberOfTrailingZeros(0), Integer.SIZE);
+    for (int i = 0; i < Integer.SIZE; i++) {
+      Assert.assertEquals(
+        Integer.numberOfTrailingZeros(0x80000000 >> i),
+        Integer.SIZE - 1 - i);
+      Assert.assertEquals(
+        Integer.numberOfTrailingZeros((0x80000000 >> i) | 0x80000000),
+        Integer.SIZE - 1 - i);
+      Assert.assertEquals(Integer.numberOfTrailingZeros(1 << i), i);
+    }
+  }
+
+  public static void test_Long_numberOfTrailingZeros() {
+    Assert.assertEquals(Long.numberOfTrailingZeros(0), Long.SIZE);
+    for (int i = 0; i < Long.SIZE; i++) {
+      Assert.assertEquals(
+        Long.numberOfTrailingZeros(0x8000000000000000L >> i),
+        Long.SIZE - 1 - i);
+      Assert.assertEquals(
+        Long.numberOfTrailingZeros((0x8000000000000000L >> i) | 0x8000000000000000L),
+        Long.SIZE - 1 - i);
+      Assert.assertEquals(Long.numberOfTrailingZeros(1L << i), i);
+    }
+  }
+
+  public static void test_Integer_rotateRight() throws Exception {
+    Assert.assertEquals(Integer.rotateRight(0x11, 0), 0x11);
+
+    Assert.assertEquals(Integer.rotateRight(0x11, 1), 0x80000008);
+    Assert.assertEquals(Integer.rotateRight(0x11, Integer.SIZE - 1), 0x22);
+    Assert.assertEquals(Integer.rotateRight(0x11, Integer.SIZE), 0x11);
+    Assert.assertEquals(Integer.rotateRight(0x11, Integer.SIZE + 1), 0x80000008);
+
+    Assert.assertEquals(Integer.rotateRight(0x11, -1), 0x22);
+    Assert.assertEquals(Integer.rotateRight(0x11, -(Integer.SIZE - 1)), 0x80000008);
+    Assert.assertEquals(Integer.rotateRight(0x11, -Integer.SIZE), 0x11);
+    Assert.assertEquals(Integer.rotateRight(0x11, -(Integer.SIZE + 1)), 0x22);
+
+    Assert.assertEquals(Integer.rotateRight(0x80000000, 1), 0x40000000);
+
+    for (int i = 0; i < Integer.SIZE; i++) {
+      Assert.assertEquals(
+        Integer.rotateRight(0xBBAAAADD, i),
+        (0xBBAAAADD >>> i) | (0xBBAAAADD << (Integer.SIZE - i)));
+    }
+  }
+
+  public static void test_Long_rotateRight() throws Exception {
+    Assert.assertEquals(Long.rotateRight(0x11, 0), 0x11);
+
+    Assert.assertEquals(Long.rotateRight(0x11, 1), 0x8000000000000008L);
+    Assert.assertEquals(Long.rotateRight(0x11, Long.SIZE - 1), 0x22);
+    Assert.assertEquals(Long.rotateRight(0x11, Long.SIZE), 0x11);
+    Assert.assertEquals(Long.rotateRight(0x11, Long.SIZE + 1), 0x8000000000000008L);
+
+    Assert.assertEquals(Long.rotateRight(0x11, -1), 0x22);
+    Assert.assertEquals(Long.rotateRight(0x11, -(Long.SIZE - 1)), 0x8000000000000008L);
+    Assert.assertEquals(Long.rotateRight(0x11, -Long.SIZE), 0x11);
+    Assert.assertEquals(Long.rotateRight(0x11, -(Long.SIZE + 1)), 0x22);
+
+    Assert.assertEquals(Long.rotateRight(0x8000000000000000L, 1), 0x4000000000000000L);
+
+    for (int i = 0; i < Long.SIZE; i++) {
+      Assert.assertEquals(
+        Long.rotateRight(0xBBAAAADDFF0000DDL, i),
+        (0xBBAAAADDFF0000DDL >>> i) | (0xBBAAAADDFF0000DDL << (Long.SIZE - i)));
+    }
+  }
+
+  public static void test_Integer_rotateLeft() throws Exception {
+    Assert.assertEquals(Integer.rotateLeft(0x11, 0), 0x11);
+
+    Assert.assertEquals(Integer.rotateLeft(0x11, 1), 0x22);
+    Assert.assertEquals(Integer.rotateLeft(0x11, Integer.SIZE - 1), 0x80000008);
+    Assert.assertEquals(Integer.rotateLeft(0x11, Integer.SIZE), 0x11);
+    Assert.assertEquals(Integer.rotateLeft(0x11, Integer.SIZE + 1), 0x22);
+
+    Assert.assertEquals(Integer.rotateLeft(0x11, -1), 0x80000008);
+    Assert.assertEquals(Integer.rotateLeft(0x11, -(Integer.SIZE - 1)), 0x22);
+    Assert.assertEquals(Integer.rotateLeft(0x11, -Integer.SIZE), 0x11);
+    Assert.assertEquals(Integer.rotateLeft(0x11, -(Integer.SIZE + 1)), 0x80000008);
+
+    Assert.assertEquals(Integer.rotateLeft(0xC0000000, 1), 0x80000001);
+
+    for (int i = 0; i < Integer.SIZE; i++) {
+      Assert.assertEquals(
+        Integer.rotateLeft(0xBBAAAADD, i),
+        (0xBBAAAADD << i) | (0xBBAAAADD >>> (Integer.SIZE - i)));
+    }
+  }
+
+  public static void test_Long_rotateLeft() throws Exception {
+    Assert.assertEquals(Long.rotateLeft(0x11, 0), 0x11);
+
+    Assert.assertEquals(Long.rotateLeft(0x11, 1), 0x22);
+    Assert.assertEquals(Long.rotateLeft(0x11, Long.SIZE - 1), 0x8000000000000008L);
+    Assert.assertEquals(Long.rotateLeft(0x11, Long.SIZE), 0x11);
+    Assert.assertEquals(Long.rotateLeft(0x11, Long.SIZE + 1), 0x22);
+
+    Assert.assertEquals(Long.rotateLeft(0x11, -1), 0x8000000000000008L);
+    Assert.assertEquals(Long.rotateLeft(0x11, -(Long.SIZE - 1)), 0x22);
+    Assert.assertEquals(Long.rotateLeft(0x11, -Long.SIZE), 0x11);
+    Assert.assertEquals(Long.rotateLeft(0x11, -(Long.SIZE + 1)), 0x8000000000000008L);
+
+    Assert.assertEquals(Long.rotateLeft(0xC000000000000000L, 1), 0x8000000000000001L);
+
+    for (int i = 0; i < Long.SIZE; i++) {
+      Assert.assertEquals(
+        Long.rotateLeft(0xBBAAAADDFF0000DDL, i),
+        (0xBBAAAADDFF0000DDL << i) | (0xBBAAAADDFF0000DDL >>> (Long.SIZE - i)));
+    }
+  }
+
+  public static void test_Integer_rotateRightLeft() throws Exception {
+    for (int i = 0; i < Integer.SIZE * 2; i++) {
+      Assert.assertEquals(Integer.rotateLeft(0xBBAAAADD, i),
+                          Integer.rotateRight(0xBBAAAADD, -i));
+      Assert.assertEquals(Integer.rotateLeft(0xBBAAAADD, -i),
+                          Integer.rotateRight(0xBBAAAADD, i));
+    }
+  }
+
+  public static void test_Long_rotateRightLeft() throws Exception {
+    for (int i = 0; i < Long.SIZE * 2; i++) {
+      Assert.assertEquals(Long.rotateLeft(0xBBAAAADDFF0000DDL, i),
+                          Long.rotateRight(0xBBAAAADDFF0000DDL, -i));
+      Assert.assertEquals(Long.rotateLeft(0xBBAAAADDFF0000DDL, -i),
+                          Long.rotateRight(0xBBAAAADDFF0000DDL, i));
+    }
+  }
 }
diff --git a/test/466-get-live-vreg/get_live_vreg_jni.cc b/test/466-get-live-vreg/get_live_vreg_jni.cc
index 9b32fc3..e3e0091 100644
--- a/test/466-get-live-vreg/get_live_vreg_jni.cc
+++ b/test/466-get-live-vreg/get_live_vreg_jni.cc
@@ -42,7 +42,7 @@
     } else if (m_name.compare("testIntervalHole") == 0) {
       found_method_ = true;
       uint32_t value = 0;
-      if (m->IsOptimized(sizeof(void*))) {
+      if (GetCurrentQuickFrame() != nullptr && m->IsOptimized(sizeof(void*))) {
         CHECK_EQ(GetVReg(m, 0, kIntVReg, &value), false);
       } else {
         CHECK(GetVReg(m, 0, kIntVReg, &value));
diff --git a/test/497-inlining-and-class-loader/clear_dex_cache.cc b/test/497-inlining-and-class-loader/clear_dex_cache.cc
index f9b33a2..50d1a63 100644
--- a/test/497-inlining-and-class-loader/clear_dex_cache.cc
+++ b/test/497-inlining-and-class-loader/clear_dex_cache.cc
@@ -24,20 +24,45 @@
 
 namespace {
 
-extern "C" JNIEXPORT jobject JNICALL Java_Main_cloneResolvedMethods(JNIEnv*, jclass, jclass cls) {
+extern "C" JNIEXPORT jobject JNICALL Java_Main_cloneResolvedMethods(JNIEnv* env,
+                                                                    jclass,
+                                                                    jclass cls) {
   ScopedObjectAccess soa(Thread::Current());
-  return soa.Vm()->AddGlobalRef(
-      soa.Self(),
-      soa.Decode<mirror::Class*>(cls)->GetDexCache()->GetResolvedMethods()->Clone(soa.Self()));
+  mirror::DexCache* dex_cache = soa.Decode<mirror::Class*>(cls)->GetDexCache();
+  size_t num_methods = dex_cache->NumResolvedMethods();
+  ArtMethod** methods = dex_cache->GetResolvedMethods();
+  CHECK_EQ(num_methods != 0u, methods != nullptr);
+  if (num_methods == 0u) {
+    return nullptr;
+  }
+  jarray array;
+  if (sizeof(void*) == 4) {
+    array = env->NewIntArray(num_methods);
+  } else {
+    array = env->NewLongArray(num_methods);
+  }
+  CHECK(array != nullptr);
+  mirror::PointerArray* pointer_array = soa.Decode<mirror::PointerArray*>(array);
+  for (size_t i = 0; i != num_methods; ++i) {
+    ArtMethod* method = mirror::DexCache::GetElementPtrSize(methods, i, sizeof(void*));
+    pointer_array->SetElementPtrSize(i, method, sizeof(void*));
+  }
+  return array;
 }
 
 extern "C" JNIEXPORT void JNICALL Java_Main_restoreResolvedMethods(
     JNIEnv*, jclass, jclass cls, jobject old_cache) {
   ScopedObjectAccess soa(Thread::Current());
-  mirror::PointerArray* now = soa.Decode<mirror::Class*>(cls)->GetDexCache()->GetResolvedMethods();
+  mirror::DexCache* dex_cache = soa.Decode<mirror::Class*>(cls)->GetDexCache();
+  size_t num_methods = dex_cache->NumResolvedMethods();
+  ArtMethod** methods = soa.Decode<mirror::Class*>(cls)->GetDexCache()->GetResolvedMethods();
+  CHECK_EQ(num_methods != 0u, methods != nullptr);
   mirror::PointerArray* old = soa.Decode<mirror::PointerArray*>(old_cache);
-  for (size_t i = 0, e = old->GetLength(); i < e; ++i) {
-    now->SetElementPtrSize(i, old->GetElementPtrSize<void*>(i, sizeof(void*)), sizeof(void*));
+  CHECK_EQ(methods != nullptr, old != nullptr);
+  CHECK_EQ(num_methods, static_cast<size_t>(old->GetLength()));
+  for (size_t i = 0; i != num_methods; ++i) {
+    ArtMethod* method = old->GetElementPtrSize<ArtMethod*>(i, sizeof(void*));
+    mirror::DexCache::SetElementPtrSize(methods, i, method, sizeof(void*));
   }
 }
 
diff --git a/test/800-smali/smali/b_18380491AbstractBase.smali b/test/800-smali/smali/b_18380491AbstractBase.smali
index 7aa1b1a..cc05221 100644
--- a/test/800-smali/smali/b_18380491AbstractBase.smali
+++ b/test/800-smali/smali/b_18380491AbstractBase.smali
@@ -1,4 +1,4 @@
-.class public LB18380491ActractBase;
+.class public abstract LB18380491AbstractBase;
 
 .super Ljava/lang/Object;
 
diff --git a/test/800-smali/smali/b_18380491ConcreteClass.smali b/test/800-smali/smali/b_18380491ConcreteClass.smali
index db5ef3b..1ba684f 100644
--- a/test/800-smali/smali/b_18380491ConcreteClass.smali
+++ b/test/800-smali/smali/b_18380491ConcreteClass.smali
@@ -1,10 +1,10 @@
 .class public LB18380491ConcreteClass;
 
-.super LB18380491ActractBase;
+.super LB18380491AbstractBase;
 
 .method public constructor <init>()V
     .locals 0
-    invoke-direct {p0}, LB18380491ActractBase;-><init>()V
+    invoke-direct {p0}, LB18380491AbstractBase;-><init>()V
     return-void
 .end method
 
@@ -13,7 +13,7 @@
   if-eqz p1, :invoke_super_abstract
   return p1
   :invoke_super_abstract
-  invoke-super {p0, p1}, LB18380491ActractBase;->foo(I)I
+  invoke-super {p0, p1}, LB18380491AbstractBase;->foo(I)I
   move-result v0
   return v0
 .end method
diff --git a/test/998-scoped-primitive-array/check b/test/998-scoped-primitive-array/check
new file mode 100755
index 0000000..842bdc6
--- /dev/null
+++ b/test/998-scoped-primitive-array/check
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Copyright (C) 2015 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.
+
+# Check that the string "error" isn't present
+if grep error "$2"; then
+    exit 1
+else
+    exit 0
+fi
diff --git a/test/998-scoped-primitive-array/expected.txt b/test/998-scoped-primitive-array/expected.txt
new file mode 100644
index 0000000..a965a70
--- /dev/null
+++ b/test/998-scoped-primitive-array/expected.txt
@@ -0,0 +1 @@
+Done
diff --git a/test/998-scoped-primitive-array/info.txt b/test/998-scoped-primitive-array/info.txt
new file mode 100644
index 0000000..93abb7c
--- /dev/null
+++ b/test/998-scoped-primitive-array/info.txt
@@ -0,0 +1 @@
+Tests for measuring performance of ScopedPrimitiveArray.
diff --git a/test/998-scoped-primitive-array/scoped_primitive_array.cc b/test/998-scoped-primitive-array/scoped_primitive_array.cc
new file mode 100644
index 0000000..c224a06
--- /dev/null
+++ b/test/998-scoped-primitive-array/scoped_primitive_array.cc
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#include "jni.h"
+#include "ScopedPrimitiveArray.h"
+
+extern "C" JNIEXPORT jlong JNICALL Java_Main_measureByteArray(JNIEnv* env,
+                                                              jclass,
+                                                              jlong reps,
+                                                              jbyteArray arr) {
+  jlong ret = 0;
+  for (jlong i = 0; i < reps; ++i) {
+    ScopedByteArrayRO sc(env, arr);
+    ret += sc[0] + sc[sc.size() - 1];
+  }
+  return ret;
+}
+
+extern "C" JNIEXPORT jlong JNICALL Java_Main_measureShortArray(JNIEnv* env,
+                                                               jclass,
+                                                               jlong reps,
+                                                               jshortArray arr) {
+  jlong ret = 0;
+  for (jlong i = 0; i < reps; ++i) {
+    ScopedShortArrayRO sc(env, arr);
+    ret += sc[0] + sc[sc.size() - 1];
+  }
+  return ret;
+}
+
+extern "C" JNIEXPORT jlong JNICALL Java_Main_measureIntArray(JNIEnv* env,
+                                                             jclass,
+                                                             jlong reps,
+                                                             jintArray arr) {
+  jlong ret = 0;
+  for (jlong i = 0; i < reps; ++i) {
+    ScopedIntArrayRO sc(env, arr);
+    ret += sc[0] + sc[sc.size() - 1];
+  }
+  return ret;
+}
+
+extern "C" JNIEXPORT jlong JNICALL Java_Main_measureLongArray(JNIEnv* env,
+                                                              jclass,
+                                                              jlong reps,
+                                                              jlongArray arr) {
+  jlong ret = 0;
+  for (jlong i = 0; i < reps; ++i) {
+    ScopedLongArrayRO sc(env, arr);
+    ret += sc[0] + sc[sc.size() - 1];
+  }
+  return ret;
+}
diff --git a/test/998-scoped-primitive-array/src/Main.java b/test/998-scoped-primitive-array/src/Main.java
new file mode 100644
index 0000000..630e0dc
--- /dev/null
+++ b/test/998-scoped-primitive-array/src/Main.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+public class Main {
+  public Main() {}
+
+  // Measure adds the first and last element of the array by using ScopedPrimitiveArray.
+  static native long measureByteArray(long reps, byte[] arr);
+  static native long measureShortArray(long reps, short[] arr);
+  static native long measureIntArray(long reps, int[] arr);
+  static native long measureLongArray(long reps, long[] arr);
+
+  static void checkEq(long expected, long value) {
+    if (expected != value) {
+      System.out.println("error: Expected " + expected + " but got " + value);
+    }
+  }
+
+  static void runPerfTest(long reps) {
+    for (int length = 1; length <= 8192; length *= 8) {
+      byte[] bytes = new byte[length];
+      bytes[0] = 1;
+      bytes[length - 1] = 2;
+      short[] shorts = new short[length];
+      shorts[0] = 1;
+      shorts[length - 1] = 2;
+      int[] ints = new int[length];
+      ints[0] = 1;
+      ints[length - 1] = 2;
+      long[] longs = new long[length];
+      longs[0] = 1;
+      longs[length - 1] = 2;
+      long value = 0;
+      long elapsed = 0;
+      long start = 0;
+
+      start = System.nanoTime();
+      value = measureByteArray(reps, bytes);
+      elapsed = System.nanoTime() - start;
+      System.out.println("Byte length=" + length + " ns/op=" + (double) elapsed / reps);
+      checkEq(value, reps * (long) (bytes[0] + bytes[length - 1]));
+
+      start = System.nanoTime();
+      value = measureShortArray(reps, shorts);
+      elapsed = System.nanoTime() - start;
+      System.out.println("Short length=" + length + " ns/op=" + (double) elapsed / reps);
+      checkEq(value, reps * (long) (shorts[0] + shorts[length - 1]));
+
+      start = System.nanoTime();
+      value = measureIntArray(reps, ints);
+      elapsed = System.nanoTime() - start;
+      System.out.println("Int length=" + length + " ns/op=" + (double) elapsed / reps);
+      checkEq(value, reps * (ints[0] + ints[length - 1]));
+
+      start = System.nanoTime();
+      value = measureLongArray(reps, longs);
+      elapsed = System.nanoTime() - start;
+      System.out.println("Long length=" + length + " ns/op=" + (double) elapsed / reps);
+      checkEq(value, reps * (longs[0] + longs[length - 1]));
+    }
+  }
+
+  public static void main(String[] args) {
+    System.loadLibrary(args[0]);
+    long iterations = 2000000;
+    if (args.length > 1) {
+      iterations = Long.parseLong(args[1], 10);
+    }
+    runPerfTest(iterations);
+    System.out.println("Done");
+  }
+}
diff --git a/test/Android.libarttest.mk b/test/Android.libarttest.mk
index c3d1576..af945fb 100644
--- a/test/Android.libarttest.mk
+++ b/test/Android.libarttest.mk
@@ -24,6 +24,7 @@
   004-ReferenceMap/stack_walk_refmap_jni.cc \
   004-StackWalk/stack_walk_jni.cc \
   004-UnsafeTest/unsafe_test.cc \
+  044-proxy/native_proxy.cc \
   051-thread/thread_test.cc \
   088-monitor-verification/stack_inspect.cc \
   116-nodex2oat/nodex2oat.cc \
@@ -38,6 +39,7 @@
   461-get-reference-vreg/get_reference_vreg_jni.cc \
   466-get-live-vreg/get_live_vreg_jni.cc \
   497-inlining-and-class-loader/clear_dex_cache.cc \
+  998-scoped-primitive-array/scoped_primitive_array.cc \
   999-jni-perf/perf-jni.cc
 
 ART_TARGET_LIBARTTEST_$(ART_PHONY_TEST_TARGET_SUFFIX) += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libarttest.so
@@ -72,7 +74,7 @@
     LOCAL_MODULE_TAGS := tests
   endif
   LOCAL_SRC_FILES := $(LIBARTTEST_COMMON_SRC_FILES)
-  LOCAL_SHARED_LIBRARIES += libart$$(suffix) libbacktrace
+  LOCAL_SHARED_LIBRARIES += libart$$(suffix) libbacktrace libnativehelper
   LOCAL_C_INCLUDES += $(ART_C_INCLUDES) art/runtime
   LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk
   LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.libarttest.mk
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index ad3fb41..efc0bfb 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -39,7 +39,11 @@
 SECONDARY_DEX=""
 TIME_OUT="gdb"  # "n" (disabled), "timeout" (use timeout), "gdb" (use gdb)
 # Value in seconds
-TIME_OUT_VALUE=600  # 10 minutes.
+if [ "$ART_USE_READ_BARRIER" = "true" ]; then
+  TIME_OUT_VALUE=900  # 15 minutes.
+else
+  TIME_OUT_VALUE=600  # 10 minutes.
+fi
 USE_GDB="n"
 USE_JVM="n"
 VERIFY="y" # y=yes,n=no,s=softfail
diff --git a/test/run-test b/test/run-test
index 424c2e4..73c92d4 100755
--- a/test/run-test
+++ b/test/run-test
@@ -799,11 +799,11 @@
         echo '#################### info'
         cat "${td_info}" | sed 's/^/# /g'
         echo '#################### diffs'
-        diff --strip-trailing-cr -u "$expected" "$output" | tail -n 2000
+        diff --strip-trailing-cr -u "$expected" "$output" | tail -n 3000
         echo '####################'
         if [ "$strace" = "yes" ]; then
             echo '#################### strace output'
-            tail -n 2000 "$tmp_dir/$strace_output"
+            tail -n 3000 "$tmp_dir/$strace_output"
             echo '####################'
         fi
         echo ' '
diff --git a/tools/run-jdwp-tests.sh b/tools/run-jdwp-tests.sh
index 116a611..104cba7 100755
--- a/tools/run-jdwp-tests.sh
+++ b/tools/run-jdwp-tests.sh
@@ -40,6 +40,9 @@
 vm_command="--vm-command=$art"
 image_compiler_option=""
 debug="no"
+verbose="no"
+# By default, we run the whole JDWP test suite.
+test="org.apache.harmony.jpda.tests.share.AllTests"
 
 while true; do
   if [[ "$1" == "--mode=host" ]]; then
@@ -65,6 +68,19 @@
     # Remove the --debug from the arguments.
     args=${args/$1}
     shift
+  elif [[ $1 == "--verbose" ]]; then
+    verbose="yes"
+    # Remove the --verbose from the arguments.
+    args=${args/$1}
+    shift
+  elif [[ $1 == "--test" ]]; then
+    # Remove the --test from the arguments.
+    args=${args/$1}
+    shift
+    test=$1
+    # Remove the test from the arguments.
+    args=${args/$1}
+    shift
   elif [[ "$1" == "" ]]; then
     break
   else
@@ -78,6 +94,10 @@
   art_debugee="$art_debugee -d"
   vm_args="$vm_args --vm-arg -XXlib:libartd.so"
 fi
+if [[ $verbose == "yes" ]]; then
+  # Enable JDWP logs in the debuggee.
+  art_debugee="$art_debugee -verbose:jdwp"
+fi
 
 # Run the tests using vogar.
 vogar $vm_command \
@@ -93,4 +113,4 @@
       --vm-arg -Djpda.settings.debuggeeJavaPath="\"$art_debugee $image $debuggee_args\"" \
       --classpath $test_jar \
       --vm-arg -Xcompiler-option --vm-arg --debuggable \
-      org.apache.harmony.jpda.tests.share.AllTests
+      $test
diff --git a/tools/setup-buildbot-device.sh b/tools/setup-buildbot-device.sh
index 8466bb3..7faf86e 100755
--- a/tools/setup-buildbot-device.sh
+++ b/tools/setup-buildbot-device.sh
@@ -30,6 +30,3 @@
 
 echo -e "${green}List properties${nc}"
 adb shell getprop
-
-echo -e "${green}Stopping framework${nc}"
-adb shell stop