Merge "Add conditional branches, and build dominator tree."
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index c419268..67f09f9 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -17,6 +17,7 @@
 LOCAL_PATH := art
 
 TEST_COMMON_SRC_FILES := \
+	compiler/dex/local_value_numbering_test.cc \
 	compiler/driver/compiler_driver_test.cc \
 	compiler/elf_writer_test.cc \
 	compiler/image_test.cc \
@@ -49,7 +50,9 @@
 	runtime/exception_test.cc \
 	runtime/gc/accounting/space_bitmap_test.cc \
 	runtime/gc/heap_test.cc \
-	runtime/gc/space/space_test.cc \
+	runtime/gc/space/dlmalloc_space_test.cc \
+	runtime/gc/space/rosalloc_space_test.cc \
+	runtime/gc/space/large_object_space_test.cc \
 	runtime/gtest_test.cc \
 	runtime/indenter_test.cc \
 	runtime/indirect_reference_table_test.cc \
diff --git a/compiler/dex/arena_bit_vector.cc b/compiler/dex/arena_bit_vector.cc
index b567ae8..1b37b71 100644
--- a/compiler/dex/arena_bit_vector.cc
+++ b/compiler/dex/arena_bit_vector.cc
@@ -44,4 +44,14 @@
                                bool expandable, OatBitMapKind kind)
   :  BitVector(start_bits, expandable, new (arena) ArenaBitVectorAllocator(arena)), kind_(kind) {}
 
+BasicBlock* ArenaBitVector::BasicBlockIterator::Next() {
+    int idx = internal_iterator_.Next();
+
+    if (idx == -1) {
+      return nullptr;
+    }
+
+    return mir_graph_->GetBasicBlock(idx);
+}
+
 }  // namespace art
diff --git a/compiler/dex/arena_bit_vector.h b/compiler/dex/arena_bit_vector.h
index e904406..cdd5c68 100644
--- a/compiler/dex/arena_bit_vector.h
+++ b/compiler/dex/arena_bit_vector.h
@@ -20,14 +20,45 @@
 #include "base/bit_vector.h"
 #include "compiler_enums.h"
 #include "utils/arena_allocator.h"
+#include "compiler_ir.h"
 
 namespace art {
 
+// Forward declaration
+class MIRGraph;
+
 /*
  * A BitVector implementation that uses Arena allocation.
  */
 class ArenaBitVector : public BitVector {
   public:
+    /**
+     * @class BasicBlockIterator
+     * @brief Helper class to get the BasicBlocks when iterating through the ArenaBitVector.
+     */
+    class BasicBlockIterator {
+      public:
+        explicit BasicBlockIterator(ArenaBitVector* bv, MIRGraph* mir_graph)
+          : mir_graph_(mir_graph),
+            internal_iterator_(bv) {}
+
+        explicit BasicBlockIterator(ArenaBitVector* bv, CompilationUnit* c_unit)
+          : mir_graph_(c_unit->mir_graph.get()),
+            internal_iterator_(bv) {}
+
+        BasicBlock* Next();
+
+        static void* operator new(size_t size, ArenaAllocator* arena) {
+          return arena->Alloc(sizeof(ArenaBitVector::BasicBlockIterator),
+                              ArenaAllocator::kAllocGrowableArray);
+        };
+        static void operator delete(void* p) {}  // Nop.
+
+      private:
+        MIRGraph* const mir_graph_;
+        Iterator internal_iterator_;
+    };
+
     ArenaBitVector(ArenaAllocator* arena, uint32_t start_bits, bool expandable,
                    OatBitMapKind kind = kBitMapMisc);
     ~ArenaBitVector() {}
diff --git a/compiler/dex/frontend.cc b/compiler/dex/frontend.cc
index 591d92a..6800f7b 100644
--- a/compiler/dex/frontend.cc
+++ b/compiler/dex/frontend.cc
@@ -211,7 +211,7 @@
                               class_loader, dex_file);
 
   cu.NewTimingSplit("MIROpt:CheckFilters");
-  if (cu.mir_graph->SkipCompilation(Runtime::Current()->GetCompilerFilter())) {
+  if (cu.mir_graph->SkipCompilation()) {
     return NULL;
   }
 
diff --git a/compiler/dex/local_value_numbering.cc b/compiler/dex/local_value_numbering.cc
index 9e83210..a3ea034 100644
--- a/compiler/dex/local_value_numbering.cc
+++ b/compiler/dex/local_value_numbering.cc
@@ -16,8 +16,120 @@
 
 #include "local_value_numbering.h"
 
+#include "mir_graph.h"
+
 namespace art {
 
+uint16_t LocalValueNumbering::GetFieldId(const DexFile* dex_file, uint16_t field_idx) {
+  FieldReference key = { dex_file, field_idx };
+  auto it = field_index_map_.find(key);
+  if (it != field_index_map_.end()) {
+    return it->second;
+  }
+  uint16_t id = field_index_map_.size();
+  field_index_map_.Put(key, id);
+  return id;
+}
+
+void LocalValueNumbering::AdvanceGlobalMemory() {
+  // See AdvanceMemoryVersion() for explanation.
+  global_memory_version_ = next_memory_version_;
+  ++next_memory_version_;
+}
+
+uint16_t LocalValueNumbering::GetMemoryVersion(uint16_t base, uint16_t field, uint16_t type) {
+  // See AdvanceMemoryVersion() for explanation.
+  MemoryVersionKey key = { base, field, type };
+  MemoryVersionMap::iterator it = memory_version_map_.find(key);
+  uint16_t memory_version = (it != memory_version_map_.end()) ? it->second : 0u;
+  if (base != NO_VALUE && non_aliasing_refs_.find(base) == non_aliasing_refs_.end()) {
+    // Check modifications by potentially aliased access.
+    MemoryVersionKey aliased_access_key = { NO_VALUE, field, type };
+    auto aa_it = memory_version_map_.find(aliased_access_key);
+    if (aa_it != memory_version_map_.end() && aa_it->second > memory_version) {
+      memory_version = aa_it->second;
+    }
+    memory_version = std::max(memory_version, global_memory_version_);
+  } else if (base != NO_VALUE) {
+    // Ignore global_memory_version_ for access via unique references.
+  } else {
+    memory_version = std::max(memory_version, global_memory_version_);
+  }
+  return memory_version;
+};
+
+uint16_t LocalValueNumbering::AdvanceMemoryVersion(uint16_t base, uint16_t field, uint16_t type) {
+  // When we read the same value from memory, we want to assign the same value name to it.
+  // However, we need to be careful not to assign the same value name if the memory location
+  // may have been written to between the reads. To avoid that we do "memory versioning".
+  //
+  // For each write to a memory location (instance field, static field, array element) we assign
+  // a new memory version number to the location identified by the value name of the base register,
+  // the field id and type, or "{ base, field, type }". For static fields the "base" is NO_VALUE
+  // since they are not accessed via a reference. For arrays the "field" is NO_VALUE since they
+  // don't have a field id.
+  //
+  // To account for the possibility of aliased access to the same memory location via different
+  // "base", we also store the memory version number with the key "{ NO_VALUE, field, type }"
+  // if "base" is an aliasing reference and check it in GetMemoryVersion() on reads via
+  // aliasing references. A global memory version is set for method calls as a method can
+  // potentially write to any memory location accessed via an aliasing reference.
+
+  uint16_t result = next_memory_version_;
+  ++next_memory_version_;
+  MemoryVersionKey key = { base, field, type };
+  memory_version_map_.Overwrite(key, result);
+  if (base != NO_VALUE && non_aliasing_refs_.find(base) == non_aliasing_refs_.end()) {
+    // Advance memory version for aliased access.
+    MemoryVersionKey aliased_access_key = { NO_VALUE, field, type };
+    memory_version_map_.Overwrite(aliased_access_key, result);
+  }
+  return result;
+};
+
+uint16_t LocalValueNumbering::MarkNonAliasingNonNull(MIR* mir) {
+  uint16_t res = GetOperandValue(mir->ssa_rep->defs[0]);
+  SetOperandValue(mir->ssa_rep->defs[0], res);
+  DCHECK(null_checked_.find(res) == null_checked_.end());
+  null_checked_.insert(res);
+  non_aliasing_refs_.insert(res);
+  return res;
+}
+
+void LocalValueNumbering::MakeArgsAliasing(MIR* mir) {
+  for (size_t i = 0u, count = mir->ssa_rep->num_uses; i != count; ++i) {
+    uint16_t reg = GetOperandValue(mir->ssa_rep->uses[i]);
+    non_aliasing_refs_.erase(reg);
+  }
+}
+
+void LocalValueNumbering::HandleNullCheck(MIR* mir, uint16_t reg) {
+  if (null_checked_.find(reg) != null_checked_.end()) {
+    if (cu_->verbose) {
+      LOG(INFO) << "Removing null check for 0x" << std::hex << mir->offset;
+    }
+    mir->optimization_flags |= MIR_IGNORE_NULL_CHECK;
+  } else {
+    null_checked_.insert(reg);
+  }
+}
+
+void LocalValueNumbering::HandleRangeCheck(MIR* mir, uint16_t array, uint16_t index) {
+  if (ValueExists(ARRAY_REF, array, index, NO_VALUE)) {
+    if (cu_->verbose) {
+      LOG(INFO) << "Removing range check for 0x" << std::hex << mir->offset;
+    }
+    mir->optimization_flags |= MIR_IGNORE_RANGE_CHECK;
+  }
+  // Use side effect to note range check completed.
+  (void)LookupValue(ARRAY_REF, array, index, NO_VALUE);
+}
+
+void LocalValueNumbering::HandlePutObject(MIR* mir) {
+  // If we're storing a non-aliasing reference, stop tracking it as non-aliasing now.
+  uint16_t base = GetOperandValue(mir->ssa_rep->uses[0]);
+  non_aliasing_refs_.erase(base);
+}
 
 uint16_t LocalValueNumbering::GetValueNumber(MIR* mir) {
   uint16_t res = NO_VALUE;
@@ -36,8 +148,6 @@
     case Instruction::CHECK_CAST:
     case Instruction::THROW:
     case Instruction::FILL_ARRAY_DATA:
-    case Instruction::FILLED_NEW_ARRAY:
-    case Instruction::FILLED_NEW_ARRAY_RANGE:
     case Instruction::PACKED_SWITCH:
     case Instruction::SPARSE_SWITCH:
     case Instruction::IF_EQ:
@@ -52,16 +162,6 @@
     case Instruction::IF_GEZ:
     case Instruction::IF_GTZ:
     case Instruction::IF_LEZ:
-    case Instruction::INVOKE_STATIC_RANGE:
-    case Instruction::INVOKE_STATIC:
-    case Instruction::INVOKE_DIRECT:
-    case Instruction::INVOKE_DIRECT_RANGE:
-    case Instruction::INVOKE_VIRTUAL:
-    case Instruction::INVOKE_VIRTUAL_RANGE:
-    case Instruction::INVOKE_SUPER:
-    case Instruction::INVOKE_SUPER_RANGE:
-    case Instruction::INVOKE_INTERFACE:
-    case Instruction::INVOKE_INTERFACE_RANGE:
     case kMirOpFusedCmplFloat:
     case kMirOpFusedCmpgFloat:
     case kMirOpFusedCmplDouble:
@@ -70,25 +170,55 @@
       // Nothing defined - take no action.
       break;
 
-    case Instruction::MOVE_EXCEPTION:
+    case Instruction::FILLED_NEW_ARRAY:
+    case Instruction::FILLED_NEW_ARRAY_RANGE:
+      // Nothing defined but the result will be unique and non-null.
+      if (mir->next != nullptr && mir->next->dalvikInsn.opcode == Instruction::MOVE_RESULT_OBJECT) {
+        MarkNonAliasingNonNull(mir->next);
+        // The MOVE_RESULT_OBJECT will be processed next and we'll return the value name then.
+      }
+      MakeArgsAliasing(mir);
+      break;
+
+    case Instruction::INVOKE_DIRECT:
+    case Instruction::INVOKE_DIRECT_RANGE:
+    case Instruction::INVOKE_VIRTUAL:
+    case Instruction::INVOKE_VIRTUAL_RANGE:
+    case Instruction::INVOKE_SUPER:
+    case Instruction::INVOKE_SUPER_RANGE:
+    case Instruction::INVOKE_INTERFACE:
+    case Instruction::INVOKE_INTERFACE_RANGE: {
+        // Nothing defined but handle the null check.
+        uint16_t reg = GetOperandValue(mir->ssa_rep->uses[0]);
+        HandleNullCheck(mir, reg);
+      }
+      // Intentional fall-through.
+    case Instruction::INVOKE_STATIC:
+    case Instruction::INVOKE_STATIC_RANGE:
+      AdvanceGlobalMemory();
+      MakeArgsAliasing(mir);
+      break;
+
     case Instruction::MOVE_RESULT:
     case Instruction::MOVE_RESULT_OBJECT:
     case Instruction::INSTANCE_OF:
+      // 1 result, treat as unique each time, use result s_reg - will be unique.
+      res = GetOperandValue(mir->ssa_rep->defs[0]);
+      SetOperandValue(mir->ssa_rep->defs[0], res);
+      break;
+    case Instruction::MOVE_EXCEPTION:
     case Instruction::NEW_INSTANCE:
     case Instruction::CONST_STRING:
     case Instruction::CONST_STRING_JUMBO:
     case Instruction::CONST_CLASS:
-    case Instruction::NEW_ARRAY: {
-        // 1 result, treat as unique each time, use result s_reg - will be unique.
-        uint16_t res = GetOperandValue(mir->ssa_rep->defs[0]);
-        SetOperandValue(mir->ssa_rep->defs[0], res);
-      }
+    case Instruction::NEW_ARRAY:
+      // 1 result, treat as unique each time, use result s_reg - will be unique.
+      res = MarkNonAliasingNonNull(mir);
       break;
-    case Instruction::MOVE_RESULT_WIDE: {
-        // 1 wide result, treat as unique each time, use result s_reg - will be unique.
-        uint16_t res = GetOperandValueWide(mir->ssa_rep->defs[0]);
-        SetOperandValueWide(mir->ssa_rep->defs[0], res);
-      }
+    case Instruction::MOVE_RESULT_WIDE:
+      // 1 wide result, treat as unique each time, use result s_reg - will be unique.
+      res = GetOperandValueWide(mir->ssa_rep->defs[0]);
+      SetOperandValueWide(mir->ssa_rep->defs[0], res);
       break;
 
     case kMirOpPhi:
@@ -104,35 +234,31 @@
     case Instruction::MOVE_OBJECT_16:
     case Instruction::MOVE_FROM16:
     case Instruction::MOVE_OBJECT_FROM16:
-    case kMirOpCopy: {
-        // Just copy value number of source to value number of resulit.
-        uint16_t res = GetOperandValue(mir->ssa_rep->uses[0]);
-        SetOperandValue(mir->ssa_rep->defs[0], res);
-      }
+    case kMirOpCopy:
+      // Just copy value number of source to value number of result.
+      res = GetOperandValue(mir->ssa_rep->uses[0]);
+      SetOperandValue(mir->ssa_rep->defs[0], res);
       break;
 
     case Instruction::MOVE_WIDE:
     case Instruction::MOVE_WIDE_16:
-    case Instruction::MOVE_WIDE_FROM16: {
-        // Just copy value number of source to value number of result.
-        uint16_t res = GetOperandValueWide(mir->ssa_rep->uses[0]);
-        SetOperandValueWide(mir->ssa_rep->defs[0], res);
-      }
+    case Instruction::MOVE_WIDE_FROM16:
+      // Just copy value number of source to value number of result.
+      res = GetOperandValueWide(mir->ssa_rep->uses[0]);
+      SetOperandValueWide(mir->ssa_rep->defs[0], res);
       break;
 
     case Instruction::CONST:
     case Instruction::CONST_4:
-    case Instruction::CONST_16: {
-        uint16_t res = LookupValue(Instruction::CONST, Low16Bits(mir->dalvikInsn.vB),
-                                   High16Bits(mir->dalvikInsn.vB >> 16), 0);
-        SetOperandValue(mir->ssa_rep->defs[0], res);
-      }
+    case Instruction::CONST_16:
+      res = LookupValue(Instruction::CONST, Low16Bits(mir->dalvikInsn.vB),
+                        High16Bits(mir->dalvikInsn.vB >> 16), 0);
+      SetOperandValue(mir->ssa_rep->defs[0], res);
       break;
 
-    case Instruction::CONST_HIGH16: {
-        uint16_t res = LookupValue(Instruction::CONST, 0, mir->dalvikInsn.vB, 0);
-        SetOperandValue(mir->ssa_rep->defs[0], res);
-      }
+    case Instruction::CONST_HIGH16:
+      res = LookupValue(Instruction::CONST, 0, mir->dalvikInsn.vB, 0);
+      SetOperandValue(mir->ssa_rep->defs[0], res);
       break;
 
     case Instruction::CONST_WIDE_16:
@@ -145,8 +271,8 @@
         } else {
           high_res = LookupValue(Instruction::CONST, 0, 0, 2);
         }
-        uint16_t res = LookupValue(Instruction::CONST, low_res, high_res, 3);
-        SetOperandValue(mir->ssa_rep->defs[0], res);
+        res = LookupValue(Instruction::CONST, low_res, high_res, 3);
+        SetOperandValueWide(mir->ssa_rep->defs[0], res);
       }
       break;
 
@@ -157,7 +283,7 @@
                                        High16Bits(low_word), 1);
         uint16_t high_res = LookupValue(Instruction::CONST, Low16Bits(high_word),
                                        High16Bits(high_word), 2);
-        uint16_t res = LookupValue(Instruction::CONST, low_res, high_res, 3);
+        res = LookupValue(Instruction::CONST, low_res, high_res, 3);
         SetOperandValueWide(mir->ssa_rep->defs[0], res);
       }
       break;
@@ -165,7 +291,7 @@
     case Instruction::CONST_WIDE_HIGH16: {
         uint16_t low_res = LookupValue(Instruction::CONST, 0, 0, 1);
         uint16_t high_res = LookupValue(Instruction::CONST, 0, Low16Bits(mir->dalvikInsn.vB), 2);
-        uint16_t res = LookupValue(Instruction::CONST, low_res, high_res, 3);
+        res = LookupValue(Instruction::CONST, low_res, high_res, 3);
         SetOperandValueWide(mir->ssa_rep->defs[0], res);
       }
       break;
@@ -181,7 +307,7 @@
     case Instruction::FLOAT_TO_INT: {
         // res = op + 1 operand
         uint16_t operand1 = GetOperandValue(mir->ssa_rep->uses[0]);
-        uint16_t res = LookupValue(opcode, operand1, NO_VALUE, NO_VALUE);
+        res = LookupValue(opcode, operand1, NO_VALUE, NO_VALUE);
         SetOperandValue(mir->ssa_rep->defs[0], res);
       }
       break;
@@ -192,7 +318,7 @@
     case Instruction::DOUBLE_TO_INT: {
         // res = op + 1 wide operand
         uint16_t operand1 = GetOperandValue(mir->ssa_rep->uses[0]);
-        uint16_t res = LookupValue(opcode, operand1, NO_VALUE, NO_VALUE);
+        res = LookupValue(opcode, operand1, NO_VALUE, NO_VALUE);
         SetOperandValue(mir->ssa_rep->defs[0], res);
       }
       break;
@@ -205,7 +331,7 @@
     case Instruction::NEG_DOUBLE: {
         // wide res = op + 1 wide operand
         uint16_t operand1 = GetOperandValueWide(mir->ssa_rep->uses[0]);
-        uint16_t res = LookupValue(opcode, operand1, NO_VALUE, NO_VALUE);
+        res = LookupValue(opcode, operand1, NO_VALUE, NO_VALUE);
         SetOperandValueWide(mir->ssa_rep->defs[0], res);
       }
       break;
@@ -216,7 +342,7 @@
     case Instruction::INT_TO_LONG: {
         // wide res = op + 1 operand
         uint16_t operand1 = GetOperandValueWide(mir->ssa_rep->uses[0]);
-        uint16_t res = LookupValue(opcode, operand1, NO_VALUE, NO_VALUE);
+        res = LookupValue(opcode, operand1, NO_VALUE, NO_VALUE);
         SetOperandValueWide(mir->ssa_rep->defs[0], res);
       }
       break;
@@ -227,7 +353,7 @@
         // res = op + 2 wide operands
         uint16_t operand1 = GetOperandValueWide(mir->ssa_rep->uses[0]);
         uint16_t operand2 = GetOperandValueWide(mir->ssa_rep->uses[2]);
-        uint16_t res = LookupValue(opcode, operand1, operand2, NO_VALUE);
+        res = LookupValue(opcode, operand1, operand2, NO_VALUE);
         SetOperandValue(mir->ssa_rep->defs[0], res);
       }
       break;
@@ -259,7 +385,7 @@
         // res = op + 2 operands
         uint16_t operand1 = GetOperandValue(mir->ssa_rep->uses[0]);
         uint16_t operand2 = GetOperandValue(mir->ssa_rep->uses[1]);
-        uint16_t res = LookupValue(opcode, operand1, operand2, NO_VALUE);
+        res = LookupValue(opcode, operand1, operand2, NO_VALUE);
         SetOperandValue(mir->ssa_rep->defs[0], res);
       }
       break;
@@ -293,7 +419,7 @@
         // wide res = op + 2 wide operands
         uint16_t operand1 = GetOperandValueWide(mir->ssa_rep->uses[0]);
         uint16_t operand2 = GetOperandValueWide(mir->ssa_rep->uses[2]);
-        uint16_t res = LookupValue(opcode, operand1, operand2, NO_VALUE);
+        res = LookupValue(opcode, operand1, operand2, NO_VALUE);
         SetOperandValueWide(mir->ssa_rep->defs[0], res);
       }
       break;
@@ -307,7 +433,7 @@
         // wide res = op + 1 wide operand + 1 operand
         uint16_t operand1 = GetOperandValueWide(mir->ssa_rep->uses[0]);
         uint16_t operand2 = GetOperandValueWide(mir->ssa_rep->uses[2]);
-        uint16_t res = LookupValue(opcode, operand1, operand2, NO_VALUE);
+        res = LookupValue(opcode, operand1, operand2, NO_VALUE);
         SetOperandValueWide(mir->ssa_rep->defs[0], res);
       }
       break;
@@ -325,7 +451,7 @@
         // res = op + 2 operands
         uint16_t operand1 = GetOperandValue(mir->ssa_rep->uses[0]);
         uint16_t operand2 = GetOperandValue(mir->ssa_rep->uses[1]);
-        uint16_t res = LookupValue(opcode, operand1, operand2, NO_VALUE);
+        res = LookupValue(opcode, operand1, operand2, NO_VALUE);
         SetOperandValue(mir->ssa_rep->defs[0], res);
       }
       break;
@@ -352,38 +478,25 @@
         // Same as res = op + 2 operands, except use vB as operand 2
         uint16_t operand1 = GetOperandValue(mir->ssa_rep->uses[0]);
         uint16_t operand2 = LookupValue(Instruction::CONST, mir->dalvikInsn.vB, 0, 0);
-        uint16_t res = LookupValue(opcode, operand1, operand2, NO_VALUE);
+        res = LookupValue(opcode, operand1, operand2, NO_VALUE);
         SetOperandValue(mir->ssa_rep->defs[0], res);
       }
       break;
 
-    case Instruction::AGET_WIDE:
-    case Instruction::AGET:
     case Instruction::AGET_OBJECT:
+    case Instruction::AGET:
+    case Instruction::AGET_WIDE:
     case Instruction::AGET_BOOLEAN:
     case Instruction::AGET_BYTE:
     case Instruction::AGET_CHAR:
     case Instruction::AGET_SHORT: {
+        uint16_t type = opcode - Instruction::AGET;
         uint16_t array = GetOperandValue(mir->ssa_rep->uses[0]);
-        if (null_checked_.find(array) != null_checked_.end()) {
-          if (cu_->verbose) {
-            LOG(INFO) << "Removing null check for 0x" << std::hex << mir->offset;
-          }
-          mir->optimization_flags |= MIR_IGNORE_NULL_CHECK;
-        } else {
-          null_checked_.insert(array);
-        }
+        HandleNullCheck(mir, array);
         uint16_t index = GetOperandValue(mir->ssa_rep->uses[1]);
-        if (ValueExists(ARRAY_REF, array, index, NO_VALUE)) {
-          if (cu_->verbose) {
-            LOG(INFO) << "Removing range check for 0x" << std::hex << mir->offset;
-          }
-          mir->optimization_flags |= MIR_IGNORE_RANGE_CHECK;
-        }
-        // Use side effect to note range check completed.
-        (void)LookupValue(ARRAY_REF, array, index, NO_VALUE);
+        HandleRangeCheck(mir, array, index);
         // Establish value number for loaded register. Note use of memory version.
-        uint16_t memory_version = GetMemoryVersion(array, NO_VALUE);
+        uint16_t memory_version = GetMemoryVersion(array, NO_VALUE, type);
         uint16_t res = LookupValue(ARRAY_REF, array, index, memory_version);
         if (opcode == Instruction::AGET_WIDE) {
           SetOperandValueWide(mir->ssa_rep->defs[0], res);
@@ -393,116 +506,113 @@
       }
       break;
 
-    case Instruction::APUT_WIDE:
-    case Instruction::APUT:
     case Instruction::APUT_OBJECT:
-    case Instruction::APUT_SHORT:
-    case Instruction::APUT_CHAR:
+      HandlePutObject(mir);
+      // Intentional fall-through.
+    case Instruction::APUT:
+    case Instruction::APUT_WIDE:
     case Instruction::APUT_BYTE:
-    case Instruction::APUT_BOOLEAN: {
+    case Instruction::APUT_BOOLEAN:
+    case Instruction::APUT_SHORT:
+    case Instruction::APUT_CHAR: {
+        uint16_t type = opcode - Instruction::APUT;
         int array_idx = (opcode == Instruction::APUT_WIDE) ? 2 : 1;
         int index_idx = array_idx + 1;
         uint16_t array = GetOperandValue(mir->ssa_rep->uses[array_idx]);
-        if (null_checked_.find(array) != null_checked_.end()) {
-          if (cu_->verbose) {
-            LOG(INFO) << "Removing range check for 0x" << std::hex << mir->offset;
-          }
-          mir->optimization_flags |= MIR_IGNORE_NULL_CHECK;
-        } else {
-          null_checked_.insert(array);
-        }
+        HandleNullCheck(mir, array);
         uint16_t index = GetOperandValue(mir->ssa_rep->uses[index_idx]);
-        if (ValueExists(ARRAY_REF, array, index, NO_VALUE)) {
-          if (cu_->verbose) {
-            LOG(INFO) << "Removing range check for 0x" << std::hex << mir->offset;
-          }
-          mir->optimization_flags |= MIR_IGNORE_RANGE_CHECK;
-        }
-        // Use side effect to note range check completed.
-        (void)LookupValue(ARRAY_REF, array, index, NO_VALUE);
+        HandleRangeCheck(mir, array, index);
         // Rev the memory version
-        AdvanceMemoryVersion(array, NO_VALUE);
+        AdvanceMemoryVersion(array, NO_VALUE, type);
       }
       break;
 
     case Instruction::IGET_OBJECT:
-    case Instruction::IGET_WIDE:
     case Instruction::IGET:
-    case Instruction::IGET_CHAR:
-    case Instruction::IGET_SHORT:
+    case Instruction::IGET_WIDE:
     case Instruction::IGET_BOOLEAN:
-    case Instruction::IGET_BYTE: {
+    case Instruction::IGET_BYTE:
+    case Instruction::IGET_CHAR:
+    case Instruction::IGET_SHORT: {
         uint16_t base = GetOperandValue(mir->ssa_rep->uses[0]);
-        if (null_checked_.find(base) != null_checked_.end()) {
-          if (cu_->verbose) {
-            LOG(INFO) << "Removing null check for 0x" << std::hex << mir->offset;
-          }
-          mir->optimization_flags |= MIR_IGNORE_NULL_CHECK;
-        } else {
-          null_checked_.insert(base);
-        }
-        uint16_t field_ref = mir->dalvikInsn.vC;
-        uint16_t memory_version = GetMemoryVersion(base, field_ref);
+        HandleNullCheck(mir, base);
+        uint16_t memory_version;
+        uint16_t field_id;
+        // TODO: all gets treated as volatile.
+        // Volatile fields always get a new memory version; field id is irrelevant.
+        // Unresolved fields are always marked as volatile and handled the same way here.
+        field_id = 0u;
+        memory_version = next_memory_version_;
+        ++next_memory_version_;
         if (opcode == Instruction::IGET_WIDE) {
-          uint16_t res = LookupValue(Instruction::IGET_WIDE, base, field_ref, memory_version);
+          res = LookupValue(Instruction::IGET_WIDE, base, field_id, memory_version);
           SetOperandValueWide(mir->ssa_rep->defs[0], res);
         } else {
-          uint16_t res = LookupValue(Instruction::IGET, base, field_ref, memory_version);
+          res = LookupValue(Instruction::IGET, base, field_id, memory_version);
           SetOperandValue(mir->ssa_rep->defs[0], res);
         }
       }
       break;
 
-    case Instruction::IPUT_WIDE:
     case Instruction::IPUT_OBJECT:
+      HandlePutObject(mir);
+      // Intentional fall-through.
     case Instruction::IPUT:
+    case Instruction::IPUT_WIDE:
     case Instruction::IPUT_BOOLEAN:
     case Instruction::IPUT_BYTE:
     case Instruction::IPUT_CHAR:
     case Instruction::IPUT_SHORT: {
+        uint16_t type = opcode - Instruction::IPUT;
         int base_reg = (opcode == Instruction::IPUT_WIDE) ? 2 : 1;
         uint16_t base = GetOperandValue(mir->ssa_rep->uses[base_reg]);
-        if (null_checked_.find(base) != null_checked_.end()) {
-          if (cu_->verbose) {
-            LOG(INFO) << "Removing null check for 0x" << std::hex << mir->offset;
-          }
-          mir->optimization_flags |= MIR_IGNORE_NULL_CHECK;
-        } else {
-          null_checked_.insert(base);
-        }
-        uint16_t field_ref = mir->dalvikInsn.vC;
-        AdvanceMemoryVersion(base, field_ref);
+        HandleNullCheck(mir, base);
+        // TODO: all puts treated as unresolved.
+        // Unresolved fields always alias with everything of the same type.
+        unresolved_ifield_version_[type] = next_memory_version_;
+        ++next_memory_version_;
       }
       break;
 
     case Instruction::SGET_OBJECT:
     case Instruction::SGET:
+    case Instruction::SGET_WIDE:
     case Instruction::SGET_BOOLEAN:
     case Instruction::SGET_BYTE:
     case Instruction::SGET_CHAR:
-    case Instruction::SGET_SHORT:
-    case Instruction::SGET_WIDE: {
-        uint16_t field_ref = mir->dalvikInsn.vB;
-        uint16_t memory_version = GetMemoryVersion(NO_VALUE, field_ref);
+    case Instruction::SGET_SHORT: {
+        uint16_t memory_version;
+        uint16_t field_id;
+        // TODO: all gets treated as volatile.
+        // Volatile fields always get a new memory version; field id is irrelevant.
+        // Unresolved fields are always marked as volatile and handled the same way here.
+        field_id = 0u;
+        memory_version = next_memory_version_;
+        ++next_memory_version_;
         if (opcode == Instruction::SGET_WIDE) {
-          uint16_t res = LookupValue(Instruction::SGET_WIDE, NO_VALUE, field_ref, memory_version);
+          res = LookupValue(Instruction::SGET_WIDE, NO_VALUE, field_id, memory_version);
           SetOperandValueWide(mir->ssa_rep->defs[0], res);
         } else {
-          uint16_t res = LookupValue(Instruction::SGET, NO_VALUE, field_ref, memory_version);
+          res = LookupValue(Instruction::SGET, NO_VALUE, field_id, memory_version);
           SetOperandValue(mir->ssa_rep->defs[0], res);
         }
       }
       break;
 
     case Instruction::SPUT_OBJECT:
+      HandlePutObject(mir);
+      // Intentional fall-through.
     case Instruction::SPUT:
+    case Instruction::SPUT_WIDE:
     case Instruction::SPUT_BOOLEAN:
     case Instruction::SPUT_BYTE:
     case Instruction::SPUT_CHAR:
-    case Instruction::SPUT_SHORT:
-    case Instruction::SPUT_WIDE: {
-        uint16_t field_ref = mir->dalvikInsn.vB;
-        AdvanceMemoryVersion(NO_VALUE, field_ref);
+    case Instruction::SPUT_SHORT: {
+        uint16_t type = opcode - Instruction::SPUT;
+        // TODO: all puts treated as unresolved.
+        // Unresolved fields always alias with everything of the same type.
+        unresolved_sfield_version_[type] = next_memory_version_;
+        ++next_memory_version_;
       }
       break;
   }
diff --git a/compiler/dex/local_value_numbering.h b/compiler/dex/local_value_numbering.h
index 33ca8f1..348bedc 100644
--- a/compiler/dex/local_value_numbering.h
+++ b/compiler/dex/local_value_numbering.h
@@ -24,16 +24,78 @@
 
 namespace art {
 
-// Key is s_reg, value is value name.
-typedef SafeMap<uint16_t, uint16_t> SregValueMap;
-// Key is concatenation of quad, value is value name.
-typedef SafeMap<uint64_t, uint16_t> ValueMap;
-// Key represents a memory address, value is generation.
-typedef SafeMap<uint32_t, uint16_t> MemoryVersionMap;
+class DexFile;
 
 class LocalValueNumbering {
+ private:
+  // Field types correspond to the ordering of GET/PUT instructions; this order is the same
+  // for IGET, IPUT, SGET, SPUT, AGET and APUT:
+  // op         0
+  // op_WIDE    1
+  // op_OBJECT  2
+  // op_BOOLEAN 3
+  // op_BYTE    4
+  // op_CHAR    5
+  // op_SHORT   6
+  static constexpr size_t kFieldTypeCount = 7;
+
+  // FieldReference represents either a unique resolved field or all unresolved fields together.
+  struct FieldReference {
+    const DexFile* dex_file;
+    uint16_t field_idx;
+  };
+
+  struct FieldReferenceComparator {
+    bool operator()(const FieldReference& lhs, const FieldReference& rhs) const {
+      if (lhs.field_idx != rhs.field_idx) {
+        return lhs.field_idx < rhs.field_idx;
+      }
+      return lhs.dex_file < rhs.dex_file;
+    }
+  };
+
+  struct MemoryVersionKey {
+    uint16_t base;
+    uint16_t field_id;
+    uint16_t type;
+  };
+
+  struct MemoryVersionKeyComparator {
+    bool operator()(const MemoryVersionKey& lhs, const MemoryVersionKey& rhs) const {
+      if (lhs.base != rhs.base) {
+        return lhs.base < rhs.base;
+      }
+      if (lhs.field_id != rhs.field_id) {
+        return lhs.field_id < rhs.field_id;
+      }
+      return lhs.type < rhs.type;
+    }
+  };
+
+  // Key is s_reg, value is value name.
+  typedef SafeMap<uint16_t, uint16_t> SregValueMap;
+  // Key is concatenation of opcode, operand1, operand2 and modifier, value is value name.
+  typedef SafeMap<uint64_t, uint16_t> ValueMap;
+  // Key represents a memory address, value is generation.
+  typedef SafeMap<MemoryVersionKey, uint16_t, MemoryVersionKeyComparator> MemoryVersionMap;
+  // Maps field key to field id for resolved fields.
+  typedef SafeMap<FieldReference, uint32_t, FieldReferenceComparator> FieldIndexMap;
+
  public:
-  explicit LocalValueNumbering(CompilationUnit* cu) : cu_(cu) {}
+  explicit LocalValueNumbering(CompilationUnit* cu)
+      : cu_(cu),
+        sreg_value_map_(),
+        sreg_wide_value_map_(),
+        value_map_(),
+        next_memory_version_(1u),
+        global_memory_version_(0u),
+        memory_version_map_(),
+        field_index_map_(),
+        non_aliasing_refs_(),
+        null_checked_() {
+    std::fill_n(unresolved_sfield_version_, kFieldTypeCount, 0u);
+    std::fill_n(unresolved_ifield_version_, kFieldTypeCount, 0u);
+  }
 
   static uint64_t BuildKey(uint16_t op, uint16_t operand1, uint16_t operand2, uint16_t modifier) {
     return (static_cast<uint64_t>(op) << 48 | static_cast<uint64_t>(operand1) << 32 |
@@ -59,29 +121,6 @@
     return (it != value_map_.end());
   };
 
-  uint16_t GetMemoryVersion(uint16_t base, uint16_t field) {
-    uint32_t key = (base << 16) | field;
-    uint16_t res;
-    MemoryVersionMap::iterator it = memory_version_map_.find(key);
-    if (it == memory_version_map_.end()) {
-      res = 0;
-      memory_version_map_.Put(key, res);
-    } else {
-      res = it->second;
-    }
-    return res;
-  };
-
-  void AdvanceMemoryVersion(uint16_t base, uint16_t field) {
-    uint32_t key = (base << 16) | field;
-    MemoryVersionMap::iterator it = memory_version_map_.find(key);
-    if (it == memory_version_map_.end()) {
-      memory_version_map_.Put(key, 0);
-    } else {
-      it->second++;
-    }
-  };
-
   void SetOperandValue(uint16_t s_reg, uint16_t value) {
     SregValueMap::iterator it = sreg_value_map_.find(s_reg);
     if (it != sreg_value_map_.end()) {
@@ -129,11 +168,28 @@
   uint16_t GetValueNumber(MIR* mir);
 
  private:
+  uint16_t GetFieldId(const DexFile* dex_file, uint16_t field_idx);
+  void AdvanceGlobalMemory();
+  uint16_t GetMemoryVersion(uint16_t base, uint16_t field, uint16_t type);
+  uint16_t AdvanceMemoryVersion(uint16_t base, uint16_t field, uint16_t type);
+  uint16_t MarkNonAliasingNonNull(MIR* mir);
+  void MakeArgsAliasing(MIR* mir);
+  void HandleNullCheck(MIR* mir, uint16_t reg);
+  void HandleRangeCheck(MIR* mir, uint16_t array, uint16_t index);
+  void HandlePutObject(MIR* mir);
+
   CompilationUnit* const cu_;
   SregValueMap sreg_value_map_;
   SregValueMap sreg_wide_value_map_;
   ValueMap value_map_;
+  uint16_t next_memory_version_;
+  uint16_t global_memory_version_;
+  uint16_t unresolved_sfield_version_[kFieldTypeCount];
+  uint16_t unresolved_ifield_version_[kFieldTypeCount];
   MemoryVersionMap memory_version_map_;
+  FieldIndexMap field_index_map_;
+  // Value names of references to objects that cannot be reached through a different value name.
+  std::set<uint16_t> non_aliasing_refs_;
   std::set<uint16_t> null_checked_;
 };
 
diff --git a/compiler/dex/local_value_numbering_test.cc b/compiler/dex/local_value_numbering_test.cc
new file mode 100644
index 0000000..6ab6c51
--- /dev/null
+++ b/compiler/dex/local_value_numbering_test.cc
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2014 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 <vector>
+
+#include "local_value_numbering.h"
+#include "compiler_internals.h"
+#include "gtest/gtest.h"
+
+namespace art {
+
+class LocalValueNumberingTest : public testing::Test {
+ protected:
+  struct IFieldDef {
+    uint16_t field_idx;
+    uintptr_t declaring_dex_file;
+    uint16_t declaring_field_idx;
+    bool is_volatile;
+  };
+
+  struct SFieldDef {
+    uint16_t field_idx;
+    uintptr_t declaring_dex_file;
+    uint16_t declaring_field_idx;
+    bool is_volatile;
+  };
+
+  struct MIRDef {
+    static constexpr size_t kMaxSsaDefs = 2;
+    static constexpr size_t kMaxSsaUses = 3;
+
+    Instruction::Code opcode;
+    int64_t value;
+    uint32_t field_annotation;
+    size_t num_uses;
+    int32_t uses[kMaxSsaUses];
+    size_t num_defs;
+    int32_t defs[kMaxSsaDefs];
+  };
+
+#define DEF_CONST(opcode, reg, value) \
+    { opcode, value, 0u, 0, { }, 1, { reg } }
+#define DEF_CONST_WIDE(opcode, reg, value) \
+    { opcode, value, 0u, 0, { }, 2, { reg, reg + 1 } }
+#define DEF_IGET(opcode, reg, obj, field_annotation) \
+    { opcode, 0u, field_annotation, 1, { obj }, 1, { reg } }
+#define DEF_IGET_WIDE(opcode, reg, obj, field_annotation) \
+    { opcode, 0u, field_annotation, 1, { obj }, 2, { reg, reg + 1 } }
+#define DEF_IPUT(opcode, reg, obj, field_annotation) \
+    { opcode, 0u, field_annotation, 2, { reg, obj }, 0, { } }
+#define DEF_IPUT_WIDE(opcode, reg, obj, field_annotation) \
+    { opcode, 0u, field_annotation, 3, { reg, reg + 1, obj }, 0, { } }
+#define DEF_SGET(opcode, reg, field_annotation) \
+    { opcode, 0u, field_annotation, 0, { }, 1, { reg } }
+#define DEF_SGET_WIDE(opcode, reg, field_annotation) \
+    { opcode, 0u, field_annotation, 0, { }, 2, { reg, reg + 1 } }
+#define DEF_SPUT(opcode, reg, field_annotation) \
+    { opcode, 0u, field_annotation, 1, { reg }, 0, { } }
+#define DEF_SPUT_WIDE(opcode, reg, field_annotation) \
+    { opcode, 0u, field_annotation, 2, { reg, reg + 1 }, 0, { } }
+#define DEF_INVOKE1(opcode, reg) \
+    { opcode, 0u, 0u, 1, { reg }, 0, { } }
+#define DEF_UNIQUE_REF(opcode, reg) \
+    { opcode, 0u, 0u, 0, { }, 1, { reg } }  // CONST_CLASS, CONST_STRING, NEW_ARRAY, ...
+
+  void DoPrepareIFields(const IFieldDef* defs, size_t count) {
+  }
+
+  template <size_t count>
+  void PrepareIFields(const IFieldDef (&defs)[count]) {
+    DoPrepareIFields(defs, count);
+  }
+
+  void DoPrepareSFields(const SFieldDef* defs, size_t count) {
+  }
+
+  template <size_t count>
+  void PrepareSFields(const SFieldDef (&defs)[count]) {
+    DoPrepareSFields(defs, count);
+  }
+
+  void DoPrepareMIRs(const MIRDef* defs, size_t count) {
+    mir_count_ = count;
+    mirs_ = reinterpret_cast<MIR*>(cu_.arena.Alloc(sizeof(MIR) * count, ArenaAllocator::kAllocMIR));
+    ssa_reps_.resize(count);
+    for (size_t i = 0u; i != count; ++i) {
+      const MIRDef* def = &defs[i];
+      MIR* mir = &mirs_[i];
+      mir->dalvikInsn.opcode = def->opcode;
+      mir->dalvikInsn.vB = static_cast<int32_t>(def->value);
+      mir->dalvikInsn.vB_wide = def->value;
+      mir->ssa_rep = &ssa_reps_[i];
+      mir->ssa_rep->num_uses = def->num_uses;
+      mir->ssa_rep->uses = const_cast<int32_t*>(def->uses);  // Not modified by LVN.
+      mir->ssa_rep->fp_use = nullptr;  // Not used by LVN.
+      mir->ssa_rep->num_defs = def->num_defs;
+      mir->ssa_rep->defs = const_cast<int32_t*>(def->defs);  // Not modified by LVN.
+      mir->ssa_rep->fp_def = nullptr;  // Not used by LVN.
+      mir->dalvikInsn.opcode = def->opcode;
+      mir->offset = i;  // LVN uses offset only for debug output
+      mir->width = 1u;  // Not used by LVN.
+      mir->optimization_flags = 0u;
+
+      if (i != 0u) {
+        mirs_[i - 1u].next = mir;
+      }
+    }
+    mirs_[count - 1u].next = nullptr;
+  }
+
+  template <size_t count>
+  void PrepareMIRs(const MIRDef (&defs)[count]) {
+    DoPrepareMIRs(defs, count);
+  }
+
+  void PerformLVN() {
+    value_names_.resize(mir_count_);
+    for (size_t i = 0; i != mir_count_; ++i) {
+      value_names_[i] =  lvn_.GetValueNumber(&mirs_[i]);
+    }
+  }
+
+  LocalValueNumberingTest() : pool_(), cu_(&pool_), mir_count_(0u), mirs_(nullptr), lvn_(&cu_) {
+    cu_.mir_graph.reset(new MIRGraph(&cu_, &cu_.arena));
+  }
+
+  ArenaPool pool_;
+  CompilationUnit cu_;
+  size_t mir_count_;
+  MIR* mirs_;
+  std::vector<SSARepresentation> ssa_reps_;
+  std::vector<uint16_t> value_names_;
+  LocalValueNumbering lvn_;
+};
+
+#if 0  // TODO: re-enable when LVN is handling memory igets.
+TEST_F(LocalValueNumberingTest, TestIGetIGetInvokeIGet) {
+  static const IFieldDef ifields[] = {
+      { 1u, 1u, 1u, false }
+  };
+  static const MIRDef mirs[] = {
+      DEF_IGET(Instruction::IGET, 0u, 10u, 0u),
+      DEF_IGET(Instruction::IGET, 1u, 10u, 0u),
+      DEF_INVOKE1(Instruction::INVOKE_VIRTUAL, 11u),
+      DEF_IGET(Instruction::IGET, 2u, 10u, 0u),
+  };
+
+  PrepareIFields(ifields);
+  PrepareMIRs(mirs);
+  PerformLVN();
+  ASSERT_EQ(value_names_.size(), 4u);
+  EXPECT_EQ(value_names_[0], value_names_[1]);
+  EXPECT_NE(value_names_[0], value_names_[3]);
+  EXPECT_EQ(mirs_[0].optimization_flags, 0u);
+  EXPECT_EQ(mirs_[1].optimization_flags, MIR_IGNORE_NULL_CHECK);
+  EXPECT_EQ(mirs_[2].optimization_flags, 0u);
+  EXPECT_EQ(mirs_[3].optimization_flags, MIR_IGNORE_NULL_CHECK);
+}
+#endif
+
+TEST_F(LocalValueNumberingTest, TestIGetIPutIGetIGetIGet) {
+  static const IFieldDef ifields[] = {
+      { 1u, 1u, 1u, false },
+      { 2u, 1u, 2u, false },
+  };
+  static const MIRDef mirs[] = {
+      DEF_IGET(Instruction::IGET, 0u, 10u, 0u),
+      DEF_IPUT(Instruction::IPUT, 1u, 11u, 0u),  // May alias.
+      DEF_IGET(Instruction::IGET, 2u, 10u, 0u),
+      DEF_IGET(Instruction::IGET, 3u,  0u, 1u),
+      DEF_IGET(Instruction::IGET, 4u,  2u, 1u),
+  };
+
+  PrepareIFields(ifields);
+  PrepareMIRs(mirs);
+  PerformLVN();
+  ASSERT_EQ(value_names_.size(), 5u);
+  EXPECT_NE(value_names_[0], value_names_[2]);
+  EXPECT_NE(value_names_[3], value_names_[4]);
+  EXPECT_EQ(mirs_[0].optimization_flags, 0u);
+  EXPECT_EQ(mirs_[1].optimization_flags, 0u);
+  EXPECT_EQ(mirs_[2].optimization_flags, MIR_IGNORE_NULL_CHECK);
+  EXPECT_EQ(mirs_[3].optimization_flags, 0u);
+  EXPECT_EQ(mirs_[4].optimization_flags, 0u);
+}
+
+#if 0  // TODO: re-enable when LVN is handling memory igets.
+TEST_F(LocalValueNumberingTest, TestUniquePreserve1) {
+  static const IFieldDef ifields[] = {
+      { 1u, 1u, 1u, false },
+  };
+  static const MIRDef mirs[] = {
+      DEF_UNIQUE_REF(Instruction::NEW_INSTANCE, 10u),
+      DEF_IGET(Instruction::IGET, 0u, 10u, 0u),
+      DEF_IPUT(Instruction::IPUT, 1u, 11u, 0u),  // No aliasing since 10u is unique.
+      DEF_IGET(Instruction::IGET, 2u, 10u, 0u),
+  };
+
+  PrepareIFields(ifields);
+  PrepareMIRs(mirs);
+  PerformLVN();
+  ASSERT_EQ(value_names_.size(), 4u);
+  EXPECT_EQ(value_names_[1], value_names_[3]);
+  EXPECT_EQ(mirs_[1].optimization_flags, MIR_IGNORE_NULL_CHECK);
+  EXPECT_EQ(mirs_[2].optimization_flags, 0u);
+  EXPECT_EQ(mirs_[3].optimization_flags, MIR_IGNORE_NULL_CHECK);
+}
+#endif
+
+#if 0  // TODO: re-enable when LVN is handling memory igets.
+TEST_F(LocalValueNumberingTest, TestUniquePreserve2) {
+  static const IFieldDef ifields[] = {
+      { 1u, 1u, 1u, false },
+  };
+  static const MIRDef mirs[] = {
+      DEF_UNIQUE_REF(Instruction::NEW_INSTANCE, 11u),
+      DEF_IGET(Instruction::IGET, 0u, 10u, 0u),
+      DEF_IPUT(Instruction::IPUT, 1u, 11u, 0u),  // No aliasing since 11u is unique.
+      DEF_IGET(Instruction::IGET, 2u, 10u, 0u),
+  };
+
+  PrepareIFields(ifields);
+  PrepareMIRs(mirs);
+  PerformLVN();
+  ASSERT_EQ(value_names_.size(), 4u);
+  EXPECT_EQ(value_names_[1], value_names_[3]);
+  EXPECT_EQ(mirs_[1].optimization_flags, 0u);
+  EXPECT_EQ(mirs_[2].optimization_flags, MIR_IGNORE_NULL_CHECK);
+  EXPECT_EQ(mirs_[3].optimization_flags, MIR_IGNORE_NULL_CHECK);
+}
+#endif
+
+#if 0  // TODO: re-enable when LVN is handling memory igets.
+TEST_F(LocalValueNumberingTest, TestUniquePreserveAndEscape) {
+  static const IFieldDef ifields[] = {
+      { 1u, 1u, 1u, false },
+  };
+  static const MIRDef mirs[] = {
+      DEF_UNIQUE_REF(Instruction::NEW_INSTANCE, 10u),
+      DEF_IGET(Instruction::IGET, 0u, 10u, 0u),
+      DEF_INVOKE1(Instruction::INVOKE_VIRTUAL, 11u),  // 10u still unique.
+      DEF_IGET(Instruction::IGET, 2u, 10u, 0u),
+      DEF_INVOKE1(Instruction::INVOKE_VIRTUAL, 10u),  // 10u not unique anymore.
+      DEF_IGET(Instruction::IGET, 3u, 10u, 0u),
+  };
+
+  PrepareIFields(ifields);
+  PrepareMIRs(mirs);
+  PerformLVN();
+  ASSERT_EQ(value_names_.size(), 6u);
+  EXPECT_EQ(value_names_[1], value_names_[3]);
+  EXPECT_NE(value_names_[1], value_names_[5]);
+  EXPECT_EQ(mirs_[1].optimization_flags, MIR_IGNORE_NULL_CHECK);
+  EXPECT_EQ(mirs_[3].optimization_flags, MIR_IGNORE_NULL_CHECK);
+  EXPECT_EQ(mirs_[5].optimization_flags, MIR_IGNORE_NULL_CHECK);
+}
+#endif
+
+TEST_F(LocalValueNumberingTest, TestVolatile) {
+  static const IFieldDef ifields[] = {
+      { 1u, 1u, 1u, false },
+      { 2u, 1u, 2u, true },
+  };
+  static const MIRDef mirs[] = {
+      DEF_IGET(Instruction::IGET, 0u, 10u, 1u),  // Volatile.
+      DEF_IGET(Instruction::IGET, 1u,  0u, 0u),  // Non-volatile.
+      DEF_IGET(Instruction::IGET, 2u, 10u, 1u),  // Volatile.
+      DEF_IGET(Instruction::IGET, 3u,  2u, 1u),  // Non-volatile.
+  };
+
+  PrepareIFields(ifields);
+  PrepareMIRs(mirs);
+  PerformLVN();
+  ASSERT_EQ(value_names_.size(), 4u);
+  EXPECT_NE(value_names_[0], value_names_[2]);  // Volatile has always different value name.
+  EXPECT_NE(value_names_[1], value_names_[3]);  // Used different base because of volatile.
+  EXPECT_EQ(mirs_[0].optimization_flags, 0u);
+  EXPECT_EQ(mirs_[1].optimization_flags, 0u);
+  EXPECT_EQ(mirs_[2].optimization_flags, MIR_IGNORE_NULL_CHECK);
+  EXPECT_EQ(mirs_[3].optimization_flags, 0u);
+}
+
+}  // namespace art
diff --git a/compiler/dex/mir_analysis.cc b/compiler/dex/mir_analysis.cc
index ab55333..7ce8f69 100644
--- a/compiler/dex/mir_analysis.cc
+++ b/compiler/dex/mir_analysis.cc
@@ -18,6 +18,7 @@
 #include "dataflow_iterator-inl.h"
 #include "dex/quick/dex_file_method_inliner.h"
 #include "dex/quick/dex_file_to_method_inliner_map.h"
+#include "driver/compiler_options.h"
 
 namespace art {
 
@@ -958,7 +959,7 @@
   }
 
   // Complex, logic-intensive?
-  if ((GetNumDalvikInsns() > Runtime::Current()->GetSmallMethodThreshold()) &&
+  if (cu_->compiler_driver->GetCompilerOptions().IsSmallMethod(GetNumDalvikInsns()) &&
       stats->branch_ratio > 0.3) {
     return false;
   }
@@ -984,7 +985,7 @@
   }
 
   // If significant in size and high proportion of expensive operations, skip.
-  if ((GetNumDalvikInsns() > Runtime::Current()->GetSmallMethodThreshold()) &&
+  if (cu_->compiler_driver->GetCompilerOptions().IsSmallMethod(GetNumDalvikInsns()) &&
       (stats->heavyweight_ratio > 0.3)) {
     return true;
   }
@@ -996,12 +997,14 @@
   * Will eventually want this to be a bit more sophisticated and happen at verification time.
   * Ultimate goal is to drive with profile data.
   */
-bool MIRGraph::SkipCompilation(Runtime::CompilerFilter compiler_filter) {
-  if (compiler_filter == Runtime::kEverything) {
+bool MIRGraph::SkipCompilation() {
+  const CompilerOptions& compiler_options = cu_->compiler_driver->GetCompilerOptions();
+  CompilerOptions::CompilerFilter compiler_filter = compiler_options.GetCompilerFilter();
+  if (compiler_filter == CompilerOptions::kEverything) {
     return false;
   }
 
-  if (compiler_filter == Runtime::kInterpretOnly) {
+  if (compiler_filter == CompilerOptions::kInterpretOnly) {
     LOG(WARNING) << "InterpretOnly should ideally be filtered out prior to parsing.";
     return true;
   }
@@ -1010,17 +1013,17 @@
   size_t small_cutoff = 0;
   size_t default_cutoff = 0;
   switch (compiler_filter) {
-    case Runtime::kBalanced:
-      small_cutoff = Runtime::Current()->GetSmallMethodThreshold();
-      default_cutoff = Runtime::Current()->GetLargeMethodThreshold();
+    case CompilerOptions::kBalanced:
+      small_cutoff = compiler_options.GetSmallMethodThreshold();
+      default_cutoff = compiler_options.GetLargeMethodThreshold();
       break;
-    case Runtime::kSpace:
-      small_cutoff = Runtime::Current()->GetTinyMethodThreshold();
-      default_cutoff = Runtime::Current()->GetSmallMethodThreshold();
+    case CompilerOptions::kSpace:
+      small_cutoff = compiler_options.GetTinyMethodThreshold();
+      default_cutoff = compiler_options.GetSmallMethodThreshold();
       break;
-    case Runtime::kSpeed:
-      small_cutoff = Runtime::Current()->GetHugeMethodThreshold();
-      default_cutoff = Runtime::Current()->GetHugeMethodThreshold();
+    case CompilerOptions::kSpeed:
+      small_cutoff = compiler_options.GetHugeMethodThreshold();
+      default_cutoff = compiler_options.GetHugeMethodThreshold();
       break;
     default:
       LOG(FATAL) << "Unexpected compiler_filter_: " << compiler_filter;
@@ -1033,17 +1036,17 @@
    * Filter 1: Huge methods are likely to be machine generated, but some aren't.
    * If huge, assume we won't compile, but allow futher analysis to turn it back on.
    */
-  if (GetNumDalvikInsns() > Runtime::Current()->GetHugeMethodThreshold()) {
+  if (compiler_options.IsHugeMethod(GetNumDalvikInsns())) {
     skip_compilation = true;
     // If we're got a huge number of basic blocks, don't bother with further analysis.
-    if (static_cast<size_t>(num_blocks_) > (Runtime::Current()->GetHugeMethodThreshold() / 2)) {
+    if (static_cast<size_t>(num_blocks_) > (compiler_options.GetHugeMethodThreshold() / 2)) {
       return true;
     }
-  } else if (GetNumDalvikInsns() > Runtime::Current()->GetLargeMethodThreshold() &&
+  } else if (compiler_options.IsLargeMethod(GetNumDalvikInsns()) &&
     /* If it's large and contains no branches, it's likely to be machine generated initialization */
       (GetBranchCount() == 0)) {
     return true;
-  } else if (compiler_filter == Runtime::kSpeed) {
+  } else if (compiler_filter == CompilerOptions::kSpeed) {
     // If not huge, compile.
     return false;
   }
diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h
index d304db9..2174f67 100644
--- a/compiler/dex/mir_graph.h
+++ b/compiler/dex/mir_graph.h
@@ -372,7 +372,7 @@
    * Examine the graph to determine whether it's worthwile to spend the time compiling
    * this method.
    */
-  bool SkipCompilation(Runtime::CompilerFilter compiler_filter);
+  bool SkipCompilation();
 
   /*
    * Parse dex method and add MIR at current insert point.  Returns id (which is
@@ -917,6 +917,8 @@
   size_t num_non_special_compiler_temps_;
   size_t max_available_non_special_compiler_temps_;
   size_t max_available_special_compiler_temps_;
+
+  friend class LocalValueNumberingTest;
 };
 
 }  // namespace art
diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc
index b91ef28..67d4769 100644
--- a/compiler/dex/mir_optimization.cc
+++ b/compiler/dex/mir_optimization.cc
@@ -406,7 +406,7 @@
       // Is this the select pattern?
       // TODO: flesh out support for Mips.  NOTE: llvm's select op doesn't quite work here.
       // TUNING: expand to support IF_xx compare & branches
-      if (cu_->compiler_backend->IsPortable() &&
+      if (!cu_->compiler_backend->IsPortable() &&
           (cu_->instruction_set == kThumb2 || cu_->instruction_set == kX86) &&
           ((mir->dalvikInsn.opcode == Instruction::IF_EQZ) ||
           (mir->dalvikInsn.opcode == Instruction::IF_NEZ))) {
diff --git a/compiler/dex/pass_driver.cc b/compiler/dex/pass_driver.cc
index 4f8739a..b60f296 100644
--- a/compiler/dex/pass_driver.cc
+++ b/compiler/dex/pass_driver.cc
@@ -116,7 +116,7 @@
 }
 
 void PassDriver::DispatchPass(CompilationUnit* c_unit, const Pass* curPass) {
-  LOG(DEBUG) << "Dispatching " << curPass->GetName();
+  VLOG(compiler) << "Dispatching " << curPass->GetName();
 
   DataFlowAnalysisMode mode = curPass->GetTraversal();
 
@@ -145,7 +145,7 @@
     case kNoNodes:
       break;
     default:
-      LOG(DEBUG) << "Iterator mode not handled in dispatcher: " << mode;
+      LOG(FATAL) << "Iterator mode not handled in dispatcher: " << mode;
       break;
   }
 }
diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc
index c5dccda..3185449 100644
--- a/compiler/dex/quick/codegen_util.cc
+++ b/compiler/dex/quick/codegen_util.cc
@@ -25,6 +25,7 @@
 #include "dex/verified_method.h"
 #include "verifier/dex_gc_map.h"
 #include "verifier/method_verifier.h"
+#include "vmap_table.h"
 
 namespace art {
 
@@ -1042,30 +1043,32 @@
 }
 
 CompiledMethod* Mir2Lir::GetCompiledMethod() {
-  // Combine vmap tables - core regs, then fp regs - into vmap_table
-  std::vector<uint16_t> raw_vmap_table;
-  // Core regs may have been inserted out of order - sort first
-  std::sort(core_vmap_table_.begin(), core_vmap_table_.end());
-  for (size_t i = 0 ; i < core_vmap_table_.size(); ++i) {
-    // Copy, stripping out the phys register sort key
-    raw_vmap_table.push_back(~(-1 << VREG_NUM_WIDTH) & core_vmap_table_[i]);
-  }
-  // If we have a frame, push a marker to take place of lr
+  // Combine vmap tables - core regs, then fp regs - into vmap_table.
+  Leb128EncodingVector vmap_encoder;
   if (frame_size_ > 0) {
-    raw_vmap_table.push_back(INVALID_VREG);
+    // Prefix the encoded data with its size.
+    size_t size = core_vmap_table_.size() + 1 /* marker */ + fp_vmap_table_.size();
+    vmap_encoder.Reserve(size + 1u);  // All values are likely to be one byte in ULEB128 (<128).
+    vmap_encoder.PushBackUnsigned(size);
+    // Core regs may have been inserted out of order - sort first.
+    std::sort(core_vmap_table_.begin(), core_vmap_table_.end());
+    for (size_t i = 0 ; i < core_vmap_table_.size(); ++i) {
+      // Copy, stripping out the phys register sort key.
+      vmap_encoder.PushBackUnsigned(
+          ~(-1 << VREG_NUM_WIDTH) & (core_vmap_table_[i] + VmapTable::kEntryAdjustment));
+    }
+    // Push a marker to take place of lr.
+    vmap_encoder.PushBackUnsigned(VmapTable::kAdjustedFpMarker);
+    // fp regs already sorted.
+    for (uint32_t i = 0; i < fp_vmap_table_.size(); i++) {
+      vmap_encoder.PushBackUnsigned(fp_vmap_table_[i] + VmapTable::kEntryAdjustment);
+    }
   } else {
     DCHECK_EQ(__builtin_popcount(core_spill_mask_), 0);
     DCHECK_EQ(__builtin_popcount(fp_spill_mask_), 0);
-  }
-  // Combine vmap tables - core regs, then fp regs. fp regs already sorted
-  for (uint32_t i = 0; i < fp_vmap_table_.size(); i++) {
-    raw_vmap_table.push_back(fp_vmap_table_[i]);
-  }
-  Leb128EncodingVector vmap_encoder;
-  // Prefix the encoded data with its size.
-  vmap_encoder.PushBackUnsigned(raw_vmap_table.size());
-  for (uint16_t cur : raw_vmap_table) {
-    vmap_encoder.PushBackUnsigned(cur);
+    DCHECK_EQ(core_vmap_table_.size(), 0u);
+    DCHECK_EQ(fp_vmap_table_.size(), 0u);
+    vmap_encoder.PushBackUnsigned(0u);  // Size is 0.
   }
   CompiledMethod* result =
       new CompiledMethod(*cu_->compiler_driver, cu_->instruction_set, code_buffer_, frame_size_,
diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc
index 5fa4596..35d193c 100644
--- a/compiler/dex/quick/gen_invoke.cc
+++ b/compiler/dex/quick/gen_invoke.cc
@@ -1257,17 +1257,13 @@
   } else {
     LoadValueDirectFixed(rl_start, reg_start);
   }
-  int r_tgt = (cu_->instruction_set != kX86) ? LoadHelper(QUICK_ENTRYPOINT_OFFSET(pIndexOf)) : 0;
+  int r_tgt = LoadHelper(QUICK_ENTRYPOINT_OFFSET(pIndexOf));
   GenNullCheck(rl_obj.s_reg_low, reg_ptr, info->opt_flags);
   LIR* launch_pad = RawLIR(0, kPseudoIntrinsicRetry, WrapPointer(info));
   intrinsic_launchpads_.Insert(launch_pad);
   OpCmpImmBranch(kCondGt, reg_char, 0xFFFF, launch_pad);
   // NOTE: not a safepoint
-  if (cu_->instruction_set != kX86) {
-    OpReg(kOpBlx, r_tgt);
-  } else {
-    OpThreadMem(kOpBlx, QUICK_ENTRYPOINT_OFFSET(pIndexOf));
-  }
+  OpReg(kOpBlx, r_tgt);
   LIR* resume_tgt = NewLIR0(kPseudoTargetLabel);
   launch_pad->operands[2] = WrapPointer(resume_tgt);
   // Record that we've already inlined & null checked
diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h
index c60c394..6e97c53 100644
--- a/compiler/dex/quick/mir_to_lir.h
+++ b/compiler/dex/quick/mir_to_lir.h
@@ -676,7 +676,7 @@
     bool GenInlinedAbsDouble(CallInfo* info);
     bool GenInlinedFloatCvt(CallInfo* info);
     bool GenInlinedDoubleCvt(CallInfo* info);
-    bool GenInlinedIndexOf(CallInfo* info, bool zero_based);
+    virtual bool GenInlinedIndexOf(CallInfo* info, bool zero_based);
     bool GenInlinedStringCompareTo(CallInfo* info);
     bool GenInlinedCurrentThread(CallInfo* info);
     bool GenInlinedUnsafeGet(CallInfo* info, bool is_long, bool is_volatile);
diff --git a/compiler/dex/quick/x86/assemble_x86.cc b/compiler/dex/quick/x86/assemble_x86.cc
index 6481589..538ce0d 100644
--- a/compiler/dex/quick/x86/assemble_x86.cc
+++ b/compiler/dex/quick/x86/assemble_x86.cc
@@ -175,6 +175,8 @@
   { kX86Mov32AI, kArrayImm,  IS_STORE | IS_QUIN_OP     | REG_USE01,      { 0,             0, 0xC7, 0, 0, 0, 0, 4 }, "Mov32AI", "[!0r+!1r<<!2d+!3d],!4d" },
   { kX86Mov32TI, kThreadImm, IS_STORE | IS_BINARY_OP,                    { THREAD_PREFIX, 0, 0xC7, 0, 0, 0, 0, 4 }, "Mov32TI", "fs:[!0d],!1d" },
 
+  { kX86Lea32RM, kRegMem, IS_TERTIARY_OP | IS_LOAD | REG_DEF0_USE12, { 0, 0, 0x8D, 0, 0, 0, 0, 0 }, "Lea32RM", "!0r,[!1r+!2d]" },
+
   { kX86Lea32RA, kRegArray, IS_QUIN_OP | REG_DEF0_USE12, { 0, 0, 0x8D, 0, 0, 0, 0, 0 }, "Lea32RA", "!0r,[!1r+!2r<<!3d+!4d]" },
 
   { kX86Cmov32RRC, kRegRegCond, IS_TERTIARY_OP | REG_DEF0_USE01 | USES_CCODES, {0, 0, 0x0F, 0x40, 0, 0, 0, 0}, "Cmovcc32RR", "!2c !0r,!1r" },
@@ -354,6 +356,7 @@
   { kX86Jmp8,  kJmp,  IS_UNARY_OP  | IS_BRANCH | NEEDS_FIXUP,               { 0,             0, 0xEB, 0,    0, 0, 0, 0 }, "Jmp8",  "!0t" },
   { kX86Jmp32, kJmp,  IS_UNARY_OP  | IS_BRANCH | NEEDS_FIXUP,               { 0,             0, 0xE9, 0,    0, 0, 0, 0 }, "Jmp32", "!0t" },
   { kX86JmpR,  kJmp,  IS_UNARY_OP  | IS_BRANCH | REG_USE0,                  { 0,             0, 0xFF, 0,    0, 4, 0, 0 }, "JmpR",  "!0r" },
+  { kX86Jecxz8, kJmp, NO_OPERAND   | IS_BRANCH | NEEDS_FIXUP | REG_USEC,    { 0,             0, 0xE3, 0,    0, 0, 0, 0 }, "Jecxz", "!0t" },
   { kX86CallR, kCall, IS_UNARY_OP  | IS_BRANCH | REG_USE0,                  { 0,             0, 0xE8, 0,    0, 0, 0, 0 }, "CallR", "!0r" },
   { kX86CallM, kCall, IS_BINARY_OP | IS_BRANCH | IS_LOAD | REG_USE0,        { 0,             0, 0xFF, 0,    0, 2, 0, 0 }, "CallM", "[!0r+!1d]" },
   { kX86CallA, kCall, IS_QUAD_OP   | IS_BRANCH | IS_LOAD | REG_USE01,       { 0,             0, 0xFF, 0,    0, 2, 0, 0 }, "CallA", "[!0r+!1r<<!2d+!3d]" },
@@ -364,6 +367,7 @@
   { kX86StartOfMethod, kMacro,  IS_UNARY_OP | SETS_CCODES,             { 0, 0, 0,    0, 0, 0, 0, 0 }, "StartOfMethod", "!0r" },
   { kX86PcRelLoadRA,   kPcRel,  IS_LOAD | IS_QUIN_OP | REG_DEF0_USE12, { 0, 0, 0x8B, 0, 0, 0, 0, 0 }, "PcRelLoadRA",   "!0r,[!1r+!2r<<!3d+!4p]" },
   { kX86PcRelAdr,      kPcRel,  IS_LOAD | IS_BINARY_OP | REG_DEF0,     { 0, 0, 0xB8, 0, 0, 0, 0, 4 }, "PcRelAdr",      "!0r,!1d" },
+  { kX86RepneScasw, kPrefix2Nullary, NO_OPERAND | SETS_CCODES,         { 0x66, 0xF2, 0xAF, 0, 0, 0, 0, 0 }, "RepNE ScasW", "" },
 };
 
 static size_t ComputeSize(const X86EncodingMap* entry, int base, int displacement, bool has_sib) {
@@ -407,6 +411,8 @@
       return lir->operands[0];  // length of nop is sole operand
     case kNullary:
       return 1;  // 1 byte of opcode
+    case kPrefix2Nullary:
+      return 3;  // 1 byte of opcode + 2 prefixes
     case kRegOpcode:  // lir operands - 0: reg
       return ComputeSize(entry, 0, 0, false) - 1;  // substract 1 for modrm
     case kReg:  // lir operands - 0: reg
@@ -489,7 +495,7 @@
         return 6;  // 2 byte opcode + rel32
       }
     case kJmp:
-      if (lir->opcode == kX86Jmp8) {
+      if (lir->opcode == kX86Jmp8 || lir->opcode == kX86Jecxz8) {
         return 2;  // opcode + rel8
       } else if (lir->opcode == kX86Jmp32) {
         return 5;  // opcode + rel32
@@ -957,6 +963,10 @@
     code_buffer_.push_back((rel >> 8) & 0xFF);
     code_buffer_.push_back((rel >> 16) & 0xFF);
     code_buffer_.push_back((rel >> 24) & 0xFF);
+  } else if (entry->opcode == kX86Jecxz8) {
+    DCHECK(IS_SIMM8(rel));
+    code_buffer_.push_back(0xE3);
+    code_buffer_.push_back(rel & 0xFF);
   } else {
     DCHECK(entry->opcode == kX86JmpR);
     code_buffer_.push_back(entry->skeleton.opcode);
@@ -1148,6 +1158,17 @@
           lir->operands[0] = delta;
           break;
         }
+        case kX86Jecxz8: {
+          LIR *target_lir = lir->target;
+          DCHECK(target_lir != NULL);
+          CodeOffset pc;
+          pc = lir->offset + 2;  // opcode + rel8
+          CodeOffset target = target_lir->offset;
+          int delta = target - pc;
+          lir->operands[0] = delta;
+          DCHECK(IS_SIMM8(delta));
+          break;
+        }
         case kX86Jmp8: {
           LIR *target_lir = lir->target;
           DCHECK(target_lir != NULL);
@@ -1226,6 +1247,14 @@
         DCHECK_EQ(0, entry->skeleton.ax_opcode);
         DCHECK_EQ(0, entry->skeleton.immediate_bytes);
         break;
+      case kPrefix2Nullary:  // 1 byte of opcode + 2 prefixes.
+        DCHECK_NE(0, entry->skeleton.prefix1);
+        DCHECK_NE(0, entry->skeleton.prefix2);
+        EmitPrefixAndOpcode(entry);
+        DCHECK_EQ(0, entry->skeleton.modrm_opcode);
+        DCHECK_EQ(0, entry->skeleton.ax_opcode);
+        DCHECK_EQ(0, entry->skeleton.immediate_bytes);
+        break;
       case kRegOpcode:  // lir operands - 0: reg
         EmitOpRegOpcode(entry, lir->operands[0]);
         break;
diff --git a/compiler/dex/quick/x86/codegen_x86.h b/compiler/dex/quick/x86/codegen_x86.h
index 6100a1d..421d51e 100644
--- a/compiler/dex/quick/x86/codegen_x86.h
+++ b/compiler/dex/quick/x86/codegen_x86.h
@@ -359,6 +359,15 @@
     void GenConstWide(RegLocation rl_dest, int64_t value);
 
     /*
+     * @brief generate inline code for fast case of Strng.indexOf.
+     * @param info Call parameters
+     * @param zero_based 'true' if the index into the string is 0.
+     * @returns 'true' if the call was inlined, 'false' if a regular call needs to be
+     * generated.
+     */
+    bool GenInlinedIndexOf(CallInfo* info, bool zero_based);
+
+    /*
      * @brief Return the correct x86 opcode for the Dex operation
      * @param op Dex opcode for the operation
      * @param loc Register location of the operand
diff --git a/compiler/dex/quick/x86/target_x86.cc b/compiler/dex/quick/x86/target_x86.cc
index fa9a944..eea7191 100644
--- a/compiler/dex/quick/x86/target_x86.cc
+++ b/compiler/dex/quick/x86/target_x86.cc
@@ -20,6 +20,8 @@
 #include "codegen_x86.h"
 #include "dex/compiler_internals.h"
 #include "dex/quick/mir_to_lir-inl.h"
+#include "mirror/array.h"
+#include "mirror/string.h"
 #include "x86_lir.h"
 
 namespace art {
@@ -185,6 +187,14 @@
   if (flags & REG_USEB) {
     SetupRegMask(&lir->u.m.use_mask, rBX);
   }
+
+  // Fixup hard to describe instruction: Uses rAX, rCX, rDI; sets rDI.
+  if (lir->opcode == kX86RepneScasw) {
+    SetupRegMask(&lir->u.m.use_mask, rAX);
+    SetupRegMask(&lir->u.m.use_mask, rCX);
+    SetupRegMask(&lir->u.m.use_mask, rDI);
+    SetupRegMask(&lir->u.m.def_mask, rDI);
+  }
 }
 
 /* For dumping instructions */
@@ -936,4 +946,176 @@
   Mir2Lir::InstallLiteralPools();
 }
 
+/*
+ * Fast string.index_of(I) & (II).  Inline check for simple case of char <= 0xffff,
+ * otherwise bails to standard library code.
+ */
+bool X86Mir2Lir::GenInlinedIndexOf(CallInfo* info, bool zero_based) {
+  ClobberCallerSave();
+  LockCallTemps();  // Using fixed registers
+
+  // EAX: 16 bit character being searched.
+  // ECX: count: number of words to be searched.
+  // EDI: String being searched.
+  // EDX: temporary during execution.
+  // EBX: temporary during execution.
+
+  RegLocation rl_obj = info->args[0];
+  RegLocation rl_char = info->args[1];
+  RegLocation rl_start = info->args[2];
+
+  uint32_t char_value =
+    rl_char.is_const ? mir_graph_->ConstantValue(rl_char.orig_sreg) : 0;
+
+  if (char_value > 0xFFFF) {
+    // We have to punt to the real String.indexOf.
+    return false;
+  }
+
+  // Okay, we are commited to inlining this.
+  RegLocation rl_return = GetReturn(false);
+  RegLocation rl_dest = InlineTarget(info);
+
+  // Is the string non-NULL?
+  LoadValueDirectFixed(rl_obj, rDX);
+  GenNullCheck(rl_obj.s_reg_low, rDX, info->opt_flags);
+
+  // Record that we have inlined & null checked the object.
+  info->opt_flags |= (MIR_INLINED | MIR_IGNORE_NULL_CHECK);
+
+  // Does the character fit in 16 bits?
+  LIR* launch_pad = nullptr;
+  if (rl_char.is_const) {
+    // We need the value in EAX.
+    LoadConstantNoClobber(rAX, char_value);
+  } else {
+    // Character is not a constant; compare at runtime.
+    LoadValueDirectFixed(rl_char, rAX);
+    launch_pad = RawLIR(0, kPseudoIntrinsicRetry, WrapPointer(info));
+    intrinsic_launchpads_.Insert(launch_pad);
+    OpCmpImmBranch(kCondGt, rAX, 0xFFFF, launch_pad);
+  }
+
+  // From here down, we know that we are looking for a char that fits in 16 bits.
+  // Location of reference to data array within the String object.
+  int value_offset = mirror::String::ValueOffset().Int32Value();
+  // Location of count within the String object.
+  int count_offset = mirror::String::CountOffset().Int32Value();
+  // Starting offset within data array.
+  int offset_offset = mirror::String::OffsetOffset().Int32Value();
+  // Start of char data with array_.
+  int data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Int32Value();
+
+  // Character is in EAX.
+  // Object pointer is in EDX.
+
+  // We need to preserve EDI, but have no spare registers, so push it on the stack.
+  // We have to remember that all stack addresses after this are offset by sizeof(EDI).
+  NewLIR1(kX86Push32R, rDI);
+
+  // Compute the number of words to search in to rCX.
+  LoadWordDisp(rDX, count_offset, rCX);
+  LIR *length_compare = nullptr;
+  int start_value = 0;
+  if (zero_based) {
+    // We have to handle an empty string.  Use special instruction JECXZ.
+    length_compare = NewLIR0(kX86Jecxz8);
+  } else {
+    // We have to offset by the start index.
+    if (rl_start.is_const) {
+      start_value = mir_graph_->ConstantValue(rl_start.orig_sreg);
+      start_value = std::max(start_value, 0);
+
+      // Is the start > count?
+      length_compare = OpCmpImmBranch(kCondLe, rCX, start_value, nullptr);
+
+      if (start_value != 0) {
+        OpRegImm(kOpSub, rCX, start_value);
+      }
+    } else {
+      // Runtime start index.
+      rl_start = UpdateLoc(rl_start);
+      if (rl_start.location == kLocPhysReg) {
+        length_compare = OpCmpBranch(kCondLe, rCX, rl_start.low_reg, nullptr);
+        OpRegReg(kOpSub, rCX, rl_start.low_reg);
+      } else {
+        // Compare to memory to avoid a register load.  Handle pushed EDI.
+        int displacement = SRegOffset(rl_start.s_reg_low) + sizeof(uint32_t);
+        OpRegMem(kOpCmp, rDX, rX86_SP, displacement);
+        length_compare = NewLIR2(kX86Jcc8, 0, kX86CondLe);
+        OpRegMem(kOpSub, rCX, rX86_SP, displacement);
+      }
+    }
+  }
+  DCHECK(length_compare != nullptr);
+
+  // ECX now contains the count in words to be searched.
+
+  // Load the address of the string into EBX.
+  // The string starts at VALUE(String) + 2 * OFFSET(String) + DATA_OFFSET.
+  LoadWordDisp(rDX, value_offset, rDI);
+  LoadWordDisp(rDX, offset_offset, rBX);
+  OpLea(rBX, rDI, rBX, 1, data_offset);
+
+  // Now compute into EDI where the search will start.
+  if (zero_based || rl_start.is_const) {
+    if (start_value == 0) {
+      OpRegCopy(rDI, rBX);
+    } else {
+      NewLIR3(kX86Lea32RM, rDI, rBX, 2 * start_value);
+    }
+  } else {
+    if (rl_start.location == kLocPhysReg) {
+      if (rl_start.low_reg == rDI) {
+        // We have a slight problem here.  We are already using RDI!
+        // Grab the value from the stack.
+        LoadWordDisp(rX86_SP, 0, rDX);
+        OpLea(rDI, rBX, rDX, 1, 0);
+      } else {
+        OpLea(rDI, rBX, rl_start.low_reg, 1, 0);
+      }
+    } else {
+      OpRegCopy(rDI, rBX);
+      // Load the start index from stack, remembering that we pushed EDI.
+      int displacement = SRegOffset(rl_start.s_reg_low) + sizeof(uint32_t);
+      LoadWordDisp(rX86_SP, displacement, rDX);
+      OpLea(rDI, rBX, rDX, 1, 0);
+    }
+  }
+
+  // EDI now contains the start of the string to be searched.
+  // We are all prepared to do the search for the character.
+  NewLIR0(kX86RepneScasw);
+
+  // Did we find a match?
+  LIR* failed_branch = OpCondBranch(kCondNe, nullptr);
+
+  // yes, we matched.  Compute the index of the result.
+  // index = ((curr_ptr - orig_ptr) / 2) - 1.
+  OpRegReg(kOpSub, rDI, rBX);
+  OpRegImm(kOpAsr, rDI, 1);
+  NewLIR3(kX86Lea32RM, rl_return.low_reg, rDI, -1);
+  LIR *all_done = NewLIR1(kX86Jmp8, 0);
+
+  // Failed to match; return -1.
+  LIR *not_found = NewLIR0(kPseudoTargetLabel);
+  length_compare->target = not_found;
+  failed_branch->target = not_found;
+  LoadConstantNoClobber(rl_return.low_reg, -1);
+
+  // And join up at the end.
+  all_done->target = NewLIR0(kPseudoTargetLabel);
+  // Restore EDI from the stack.
+  NewLIR1(kX86Pop32R, rDI);
+
+  // Out of line code returns here.
+  if (launch_pad != nullptr) {
+    LIR *return_point = NewLIR0(kPseudoTargetLabel);
+    launch_pad->operands[2] = WrapPointer(return_point);
+  }
+
+  StoreValue(rl_dest, rl_return);
+  return true;
+}
+
 }  // namespace art
diff --git a/compiler/dex/quick/x86/x86_lir.h b/compiler/dex/quick/x86/x86_lir.h
index 480d5f5..4064bd6 100644
--- a/compiler/dex/quick/x86/x86_lir.h
+++ b/compiler/dex/quick/x86/x86_lir.h
@@ -277,6 +277,7 @@
   kX86Mov32MR, kX86Mov32AR, kX86Mov32TR,
   kX86Mov32RR, kX86Mov32RM, kX86Mov32RA, kX86Mov32RT,
   kX86Mov32RI, kX86Mov32MI, kX86Mov32AI, kX86Mov32TI,
+  kX86Lea32RM,
   kX86Lea32RA,
   // RRC - Register Register ConditionCode - cond_opcode reg1, reg2
   //             - lir operands - 0: reg1, 1: reg2, 2: CC
@@ -384,6 +385,7 @@
   kX86Jcc8, kX86Jcc32,  // jCC rel8/32; lir operands - 0: rel, 1: CC, target assigned
   kX86Jmp8, kX86Jmp32,  // jmp rel8/32; lir operands - 0: rel, target assigned
   kX86JmpR,             // jmp reg; lir operands - 0: reg
+  kX86Jecxz8,           // jcexz rel8; jump relative if ECX is zero.
   kX86CallR,            // call reg; lir operands - 0: reg
   kX86CallM,            // call [base + disp]; lir operands - 0: base, 1: disp
   kX86CallA,            // call [base + index * scale + disp]
@@ -396,6 +398,7 @@
   kX86PcRelLoadRA,      // mov reg, [base + index * scale + PC relative displacement]
                         // lir operands - 0: reg, 1: base, 2: index, 3: scale, 4: table
   kX86PcRelAdr,         // mov reg, PC relative displacement; lir operands - 0: reg, 1: table
+  kX86RepneScasw,       // repne scasw
   kX86Last
 };
 
@@ -404,6 +407,7 @@
   kData,                                   // Special case for raw data.
   kNop,                                    // Special case for variable length nop.
   kNullary,                                // Opcode that takes no arguments.
+  kPrefix2Nullary,                         // Opcode that takes no arguments, but 2 prefixes.
   kRegOpcode,                              // Shorter form of R instruction kind (opcode+rd)
   kReg, kMem, kArray,                      // R, M and A instruction kinds.
   kMemReg, kArrayReg, kThreadReg,          // MR, AR and TR instruction kinds.
diff --git a/compiler/dex/ssa_transformation.cc b/compiler/dex/ssa_transformation.cc
index 502df1e..0f79f41 100644
--- a/compiler/dex/ssa_transformation.cc
+++ b/compiler/dex/ssa_transformation.cc
@@ -248,22 +248,11 @@
   }
 
   /* Calculate DF_up */
-  ArenaBitVector::Iterator bv_iterator(bb->i_dominated);
-  while (true) {
-    // TUNING: hot call to BitVectorIteratorNext
-    int dominated_idx = bv_iterator.Next();
-    if (dominated_idx == -1) {
-      break;
-    }
-    BasicBlock* dominated_bb = GetBasicBlock(dominated_idx);
-    ArenaBitVector::Iterator df_iterator(dominated_bb->dom_frontier);
-    while (true) {
-      // TUNING: hot call to BitVectorIteratorNext
-      int df_up_idx = df_iterator.Next();
-      if (df_up_idx == -1) {
-        break;
-      }
-      BasicBlock* df_up_block = GetBasicBlock(df_up_idx);
+  ArenaBitVector::BasicBlockIterator it(bb->i_dominated, cu_);
+  for (BasicBlock *dominated_bb = it.Next(); dominated_bb != nullptr; dominated_bb = it.Next()) {
+    ArenaBitVector::BasicBlockIterator inner_it(dominated_bb->dom_frontier, cu_);
+    for (BasicBlock *df_up_block = inner_it.Next(); df_up_block != nullptr;
+         df_up_block = inner_it.Next()) {
       CheckForDominanceFrontier(bb, df_up_block);
     }
   }
diff --git a/compiler/dex/verification_results.cc b/compiler/dex/verification_results.cc
index edccec5..947c22d 100644
--- a/compiler/dex/verification_results.cc
+++ b/compiler/dex/verification_results.cc
@@ -19,6 +19,8 @@
 #include "base/stl_util.h"
 #include "base/mutex.h"
 #include "base/mutex-inl.h"
+#include "driver/compiler_driver.h"
+#include "driver/compiler_options.h"
 #include "thread.h"
 #include "thread-inl.h"
 #include "verified_method.h"
@@ -27,8 +29,9 @@
 
 namespace art {
 
-VerificationResults::VerificationResults()
-    : verified_methods_lock_("compiler verified methods lock"),
+VerificationResults::VerificationResults(const CompilerOptions* compiler_options)
+    : compiler_options_(compiler_options),
+      verified_methods_lock_("compiler verified methods lock"),
       verified_methods_(),
       rejected_classes_lock_("compiler rejected classes lock"),
       rejected_classes_() {
@@ -43,6 +46,7 @@
 }
 
 bool VerificationResults::ProcessVerifiedMethod(verifier::MethodVerifier* method_verifier) {
+  DCHECK(method_verifier != NULL);
   MethodReference ref = method_verifier->GetMethodReference();
   bool compile = IsCandidateForCompilation(ref, method_verifier->GetAccessFlags());
   // TODO: Check also for virtual/interface invokes when DEX-to-DEX supports devirtualization.
@@ -95,16 +99,18 @@
 bool VerificationResults::IsCandidateForCompilation(MethodReference& method_ref,
                                                     const uint32_t access_flags) {
 #ifdef ART_SEA_IR_MODE
-    bool use_sea = Runtime::Current()->IsSeaIRMode();
-    use_sea = use_sea && (std::string::npos != PrettyMethod(
-                          method_ref.dex_method_index, *(method_ref.dex_file)).find("fibonacci"));
-    if (use_sea) return true;
+  bool use_sea = compiler_options_->GetSeaIrMode();
+  use_sea = use_sea && (std::string::npos != PrettyMethod(
+                        method_ref.dex_method_index, *(method_ref.dex_file)).find("fibonacci"));
+  if (use_sea) {
+    return true;
+  }
 #endif
   // Don't compile class initializers, ever.
   if (((access_flags & kAccConstructor) != 0) && ((access_flags & kAccStatic) != 0)) {
     return false;
   }
-  return (Runtime::Current()->GetCompilerFilter() != Runtime::kInterpretOnly);
+  return (compiler_options_->GetCompilerFilter() != CompilerOptions::kInterpretOnly);
 }
 
 }  // namespace art
diff --git a/compiler/dex/verification_results.h b/compiler/dex/verification_results.h
index 2eb0713..278182f 100644
--- a/compiler/dex/verification_results.h
+++ b/compiler/dex/verification_results.h
@@ -33,11 +33,13 @@
 class MethodVerifier;
 }  // namespace verifier
 
+class CompilerOptions;
 class VerifiedMethod;
 
+// Used by CompilerCallbacks to track verification information from the Runtime.
 class VerificationResults {
   public:
-    VerificationResults();
+    explicit VerificationResults(const CompilerOptions* compiler_options);
     ~VerificationResults();
 
     bool ProcessVerifiedMethod(verifier::MethodVerifier* method_verifier)
@@ -50,15 +52,17 @@
     void AddRejectedClass(ClassReference ref) LOCKS_EXCLUDED(rejected_classes_lock_);
     bool IsClassRejected(ClassReference ref) LOCKS_EXCLUDED(rejected_classes_lock_);
 
-    static bool IsCandidateForCompilation(MethodReference& method_ref,
-                                          const uint32_t access_flags);
+    bool IsCandidateForCompilation(MethodReference& method_ref,
+                                   const uint32_t access_flags);
 
   private:
+    const CompilerOptions* compiler_options_;
+
     // Verified methods.
     typedef SafeMap<MethodReference, const VerifiedMethod*,
         MethodReferenceComparator> VerifiedMethodMap;
     ReaderWriterMutex verified_methods_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
-    VerifiedMethodMap verified_methods_;
+    VerifiedMethodMap verified_methods_ GUARDED_BY(verified_methods_lock_);
 
     // Rejected classes.
     ReaderWriterMutex rejected_classes_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
diff --git a/compiler/driver/compiler_callbacks_impl.h b/compiler/driver/compiler_callbacks_impl.h
new file mode 100644
index 0000000..fd2cd4a
--- /dev/null
+++ b/compiler/driver/compiler_callbacks_impl.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2011 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_DRIVER_COMPILER_CALLBACKS_IMPL_H_
+#define ART_COMPILER_DRIVER_COMPILER_CALLBACKS_IMPL_H_
+
+#include "compiler_callbacks.h"
+#include "dex/quick/dex_file_to_method_inliner_map.h"
+#include "verifier/method_verifier-inl.h"
+
+namespace art {
+
+class CompilerCallbacksImpl : public CompilerCallbacks {
+  public:
+    CompilerCallbacksImpl(VerificationResults* verification_results,
+                          DexFileToMethodInlinerMap* method_inliner_map)
+        : verification_results_(verification_results),
+          method_inliner_map_(method_inliner_map) {
+      CHECK(verification_results != nullptr);
+      CHECK(method_inliner_map != nullptr);
+    }
+
+    virtual ~CompilerCallbacksImpl() { }
+
+    virtual bool MethodVerified(verifier::MethodVerifier* verifier)
+        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+      bool result = verification_results_->ProcessVerifiedMethod(verifier);
+      if (result) {
+        MethodReference ref = verifier->GetMethodReference();
+        method_inliner_map_->GetMethodInliner(ref.dex_file)
+            ->AnalyseMethodCode(verifier);
+      }
+      return result;
+    }
+    virtual void ClassRejected(ClassReference ref) {
+      verification_results_->AddRejectedClass(ref);
+    }
+
+  private:
+    VerificationResults* const verification_results_;
+    DexFileToMethodInlinerMap* const method_inliner_map_;
+};
+
+}  // namespace art
+
+#endif  // ART_COMPILER_DRIVER_COMPILER_CALLBACKS_IMPL_H_
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 5adb792..1b284de 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -293,14 +293,16 @@
                                               jobject class_loader,
                                               const art::DexFile& dex_file);
 
-CompilerDriver::CompilerDriver(VerificationResults* verification_results,
+CompilerDriver::CompilerDriver(const CompilerOptions* compiler_options,
+                               VerificationResults* verification_results,
                                DexFileToMethodInlinerMap* method_inliner_map,
                                CompilerBackend::Kind compiler_backend_kind,
                                InstructionSet instruction_set,
                                InstructionSetFeatures instruction_set_features,
                                bool image, DescriptorSet* image_classes, size_t thread_count,
                                bool dump_stats, bool dump_passes, CumulativeLogger* timer)
-    : verification_results_(verification_results),
+    : compiler_options_(compiler_options),
+      verification_results_(verification_results),
       method_inliner_map_(method_inliner_map),
       compiler_backend_(CompilerBackend::Create(compiler_backend_kind)),
       instruction_set_(instruction_set),
@@ -325,6 +327,9 @@
       dedupe_mapping_table_("dedupe mapping table"),
       dedupe_vmap_table_("dedupe vmap table"),
       dedupe_gc_map_("dedupe gc map") {
+  DCHECK(compiler_options_ != nullptr);
+  DCHECK(verification_results_ != nullptr);
+  DCHECK(method_inliner_map_ != nullptr);
 
   CHECK_PTHREAD_CALL(pthread_key_create, (&tls_key_, NULL), "compiler tls key");
 
@@ -447,13 +452,12 @@
 }
 
 static DexToDexCompilationLevel GetDexToDexCompilationlevel(
-    SirtRef<mirror::ClassLoader>& class_loader, const DexFile& dex_file,
+    Thread* self, SirtRef<mirror::ClassLoader>& class_loader, const DexFile& dex_file,
     const DexFile::ClassDef& class_def) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   const char* descriptor = dex_file.GetClassDescriptor(class_def);
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  mirror::Class* klass = class_linker->FindClass(descriptor, class_loader);
+  mirror::Class* klass = class_linker->FindClass(self, descriptor, class_loader);
   if (klass == NULL) {
-    Thread* self = Thread::Current();
     CHECK(self->IsExceptionPending());
     self->ClearException();
     return kDontDexToDexCompile;
@@ -515,7 +519,8 @@
     const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_idx);
     SirtRef<mirror::ClassLoader> class_loader(soa.Self(),
                                               soa.Decode<mirror::ClassLoader*>(jclass_loader));
-    dex_to_dex_compilation_level = GetDexToDexCompilationlevel(class_loader, *dex_file, class_def);
+    dex_to_dex_compilation_level = GetDexToDexCompilationlevel(self, class_loader, *dex_file,
+                                                               class_def);
   }
   CompileMethod(code_item, access_flags, invoke_type, class_def_idx, method_idx, jclass_loader,
                 *dex_file, dex_to_dex_compilation_level);
@@ -633,7 +638,7 @@
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
   for (auto it = image_classes_->begin(), end = image_classes_->end(); it != end;) {
     const std::string& descriptor(*it);
-    SirtRef<mirror::Class> klass(self, class_linker->FindSystemClass(descriptor.c_str()));
+    SirtRef<mirror::Class> klass(self, class_linker->FindSystemClass(self, descriptor.c_str()));
     if (klass.get() == NULL) {
       VLOG(compiler) << "Failed to find class " << descriptor;
       image_classes_->erase(it++);
@@ -648,7 +653,7 @@
   // Do this here so that exception classes appear to have been specified image classes.
   std::set<std::pair<uint16_t, const DexFile*> > unresolved_exception_types;
   SirtRef<mirror::Class> java_lang_Throwable(self,
-                                     class_linker->FindSystemClass("Ljava/lang/Throwable;"));
+                                     class_linker->FindSystemClass(self, "Ljava/lang/Throwable;"));
   do {
     unresolved_exception_types.clear();
     class_linker->VisitClasses(ResolveCatchBlockExceptionsClassVisitor,
@@ -1661,7 +1666,8 @@
   jobject jclass_loader = manager->GetClassLoader();
   SirtRef<mirror::ClassLoader> class_loader(
       soa.Self(), soa.Decode<mirror::ClassLoader*>(jclass_loader));
-  SirtRef<mirror::Class> klass(soa.Self(), class_linker->FindClass(descriptor, class_loader));
+  SirtRef<mirror::Class> klass(soa.Self(), class_linker->FindClass(soa.Self(), descriptor,
+                                                                   class_loader));
   if (klass.get() == nullptr) {
     CHECK(soa.Self()->IsExceptionPending());
     soa.Self()->ClearException();
@@ -1716,7 +1722,8 @@
   SirtRef<mirror::ClassLoader> class_loader(soa.Self(),
                                             soa.Decode<mirror::ClassLoader*>(jclass_loader));
   SirtRef<mirror::Class> klass(soa.Self(),
-                               manager->GetClassLinker()->FindClass(descriptor, class_loader));
+                               manager->GetClassLinker()->FindClass(soa.Self(), descriptor,
+                                                                    class_loader));
 
   if (klass.get() != nullptr && !SkipClass(jclass_loader, dex_file, klass.get())) {
     // Only try to initialize classes that were successfully verified.
@@ -1861,7 +1868,8 @@
     ScopedObjectAccess soa(Thread::Current());
     SirtRef<mirror::ClassLoader> class_loader(soa.Self(),
                                               soa.Decode<mirror::ClassLoader*>(jclass_loader));
-    dex_to_dex_compilation_level = GetDexToDexCompilationlevel(class_loader, dex_file, class_def);
+    dex_to_dex_compilation_level = GetDexToDexCompilationlevel(soa.Self(), class_loader, dex_file,
+                                                               class_def);
   }
   ClassDataItemIterator it(dex_file, class_data);
   // Skip fields
@@ -1929,7 +1937,7 @@
   } else if ((access_flags & kAccAbstract) != 0) {
   } else {
     MethodReference method_ref(&dex_file, method_idx);
-    bool compile = VerificationResults::IsCandidateForCompilation(method_ref, access_flags);
+    bool compile = verification_results_->IsCandidateForCompilation(method_ref, access_flags);
 
     if (compile) {
       // NOTE: if compiler declines to compile this method, it will return NULL.
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 092fe52..377eb6f 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -45,6 +45,7 @@
 }  // namespace verifier
 
 class AOTCompilationStats;
+class CompilerOptions;
 class DexCompilationUnit;
 class DexFileToMethodInlinerMap;
 class InlineIGetIPutData;
@@ -94,7 +95,8 @@
   // enabled.  "image_classes" lets the compiler know what classes it
   // can assume will be in the image, with NULL implying all available
   // classes.
-  explicit CompilerDriver(VerificationResults* verification_results,
+  explicit CompilerDriver(const CompilerOptions* compiler_options,
+                          VerificationResults* verification_results,
                           DexFileToMethodInlinerMap* method_inliner_map,
                           CompilerBackend::Kind compiler_backend_kind,
                           InstructionSet instruction_set,
@@ -129,6 +131,11 @@
     return instruction_set_features_;
   }
 
+  const CompilerOptions& GetCompilerOptions() const {
+    DCHECK(compiler_options_ != nullptr);
+    return *compiler_options_;
+  }
+
   CompilerBackend* GetCompilerBackend() const {
     return compiler_backend_.get();
   }
@@ -551,8 +558,9 @@
   std::vector<const CallPatchInformation*> methods_to_patch_;
   std::vector<const TypePatchInformation*> classes_to_patch_;
 
-  VerificationResults* verification_results_;
-  DexFileToMethodInlinerMap* method_inliner_map_;
+  const CompilerOptions* const compiler_options_;
+  VerificationResults* const verification_results_;
+  DexFileToMethodInlinerMap* const method_inliner_map_;
 
   UniquePtr<CompilerBackend> compiler_backend_;
 
diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc
index 0d0c204..ec0a8bd 100644
--- a/compiler/driver/compiler_driver_test.cc
+++ b/compiler/driver/compiler_driver_test.cc
@@ -79,9 +79,8 @@
       const DexFile::ClassDef& class_def = dex_file.GetClassDef(i);
       const char* descriptor = dex_file.GetClassDescriptor(class_def);
       ScopedObjectAccess soa(Thread::Current());
-      Thread* self = Thread::Current();
-      SirtRef<mirror::ClassLoader> loader(self, soa.Decode<mirror::ClassLoader*>(class_loader));
-      mirror::Class* c = class_linker->FindClass(descriptor, loader);
+      SirtRef<mirror::ClassLoader> loader(soa.Self(), soa.Decode<mirror::ClassLoader*>(class_loader));
+      mirror::Class* c = class_linker->FindClass(soa.Self(), descriptor, loader);
       CHECK(c != NULL);
       for (size_t i = 0; i < c->NumDirectMethods(); i++) {
         MakeExecutable(c->GetDirectMethod(i));
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
new file mode 100644
index 0000000..9f6745b
--- /dev/null
+++ b/compiler/driver/compiler_options.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2014 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_DRIVER_COMPILER_OPTIONS_H_
+#define ART_COMPILER_DRIVER_COMPILER_OPTIONS_H_
+
+namespace art {
+
+class CompilerOptions {
+ public:
+  enum CompilerFilter {
+    kInterpretOnly,       // Compile nothing.
+    kSpace,               // Maximize space savings.
+    kBalanced,            // Try to get the best performance return on compilation investment.
+    kSpeed,               // Maximize runtime performance.
+    kEverything           // Force compilation (Note: excludes compilaton of class initializers).
+  };
+
+  // Guide heuristics to determine whether to compile method if profile data not available.
+  static const CompilerFilter kDefaultCompilerFilter = kSpeed;
+  static const size_t kDefaultHugeMethodThreshold = 10000;
+  static const size_t kDefaultLargeMethodThreshold = 600;
+  static const size_t kDefaultSmallMethodThreshold = 60;
+  static const size_t kDefaultTinyMethodThreshold = 20;
+  static const size_t kDefaultNumDexMethodsThreshold = 900;
+
+  CompilerOptions() :
+    compiler_filter_(kDefaultCompilerFilter),
+    huge_method_threshold_(kDefaultHugeMethodThreshold),
+    large_method_threshold_(kDefaultLargeMethodThreshold),
+    small_method_threshold_(kDefaultSmallMethodThreshold),
+    tiny_method_threshold_(kDefaultTinyMethodThreshold),
+    num_dex_methods_threshold_(kDefaultNumDexMethodsThreshold)
+#ifdef ART_SEA_IR_MODE
+    , sea_ir_mode_(false)
+#endif
+    {}
+
+  CompilerOptions(CompilerFilter compiler_filter,
+                  size_t huge_method_threshold,
+                  size_t large_method_threshold,
+                  size_t small_method_threshold,
+                  size_t tiny_method_threshold,
+                  size_t num_dex_methods_threshold
+#ifdef ART_SEA_IR_MODE
+                  , bool sea_ir_mode
+#endif
+                  ) :  // NOLINT(whitespace/parens)
+    compiler_filter_(compiler_filter),
+    huge_method_threshold_(huge_method_threshold),
+    large_method_threshold_(large_method_threshold),
+    small_method_threshold_(small_method_threshold),
+    tiny_method_threshold_(tiny_method_threshold),
+    num_dex_methods_threshold_(num_dex_methods_threshold)
+#ifdef ART_SEA_IR_MODE
+    , sea_ir_mode_(sea_ir_mode)
+#endif
+    {}
+
+  CompilerFilter GetCompilerFilter() const {
+    return compiler_filter_;
+  }
+
+  void SetCompilerFilter(CompilerFilter compiler_filter) {
+    compiler_filter_ = compiler_filter;
+  }
+
+  size_t GetHugeMethodThreshold() const {
+    return huge_method_threshold_;
+  }
+
+  size_t GetLargeMethodThreshold() const {
+    return large_method_threshold_;
+  }
+
+  size_t GetSmallMethodThreshold() const {
+    return small_method_threshold_;
+  }
+
+  size_t GetTinyMethodThreshold() const {
+    return tiny_method_threshold_;
+  }
+
+  bool IsHugeMethod(size_t num_dalvik_instructions) const {
+    return num_dalvik_instructions > huge_method_threshold_;
+  }
+
+  bool IsLargeMethod(size_t num_dalvik_instructions) const {
+    return num_dalvik_instructions > large_method_threshold_;
+  }
+
+  bool IsSmallMethod(size_t num_dalvik_instructions) const {
+    return num_dalvik_instructions > small_method_threshold_;
+  }
+
+  bool IsTinyMethod(size_t num_dalvik_instructions) const {
+    return num_dalvik_instructions > tiny_method_threshold_;
+  }
+
+  size_t GetNumDexMethodsThreshold() const {
+    return num_dex_methods_threshold_;
+  }
+
+#ifdef ART_SEA_IR_MODE
+  bool GetSeaIrMode();
+#endif
+
+ private:
+  CompilerFilter compiler_filter_;
+  size_t huge_method_threshold_;
+  size_t large_method_threshold_;
+  size_t small_method_threshold_;
+  size_t tiny_method_threshold_;
+  size_t num_dex_methods_threshold_;
+
+#ifdef ART_SEA_IR_MODE
+  bool sea_ir_mode_;
+#endif
+};
+
+}  // namespace art
+
+#endif  // ART_COMPILER_DRIVER_COMPILER_OPTIONS_H_
diff --git a/compiler/image_test.cc b/compiler/image_test.cc
index 3406fe6..49cabdc 100644
--- a/compiler/image_test.cc
+++ b/compiler/image_test.cc
@@ -151,7 +151,7 @@
   for (size_t i = 0; i < dex->NumClassDefs(); ++i) {
     const DexFile::ClassDef& class_def = dex->GetClassDef(i);
     const char* descriptor = dex->GetClassDescriptor(class_def);
-    mirror::Class* klass = class_linker_->FindSystemClass(descriptor);
+    mirror::Class* klass = class_linker_->FindSystemClass(soa.Self(), descriptor);
     EXPECT_TRUE(klass != nullptr) << descriptor;
     if (image_classes.find(descriptor) != image_classes.end()) {
       // Image classes should be located inside the image.
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 60beebb..c8447be 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -401,7 +401,8 @@
   Runtime* runtime = Runtime::Current();
   ClassLinker* class_linker = runtime->GetClassLinker();
   Thread* self = Thread::Current();
-  SirtRef<Class> object_array_class(self, class_linker->FindSystemClass("[Ljava/lang/Object;"));
+  SirtRef<Class> object_array_class(self, class_linker->FindSystemClass(self,
+                                                                        "[Ljava/lang/Object;"));
 
   // build an Object[] of all the DexCaches used in the source_space_
   ObjectArray<Object>* dex_caches = ObjectArray<Object>::Alloc(self, object_array_class.get(),
@@ -581,14 +582,15 @@
 void ImageWriter::FixupObject(Object* orig, Object* copy) {
   DCHECK(orig != NULL);
   DCHECK(copy != NULL);
-  copy->SetClass(down_cast<Class*>(GetImageAddress(orig->GetClass())));
+  copy->SetClass<kVerifyNone>(down_cast<Class*>(GetImageAddress(orig->GetClass())));
   // TODO: special case init of pointers to malloc data (or removal of these pointers)
-  if (orig->IsClass()) {
-    FixupClass(orig->AsClass(), down_cast<Class*>(copy));
-  } else if (orig->IsObjectArray()) {
-    FixupObjectArray(orig->AsObjectArray<Object>(), down_cast<ObjectArray<Object>*>(copy));
-  } else if (orig->IsArtMethod()) {
-    FixupMethod(orig->AsArtMethod(), down_cast<ArtMethod*>(copy));
+  if (orig->IsClass<kVerifyNone>()) {
+    FixupClass(orig->AsClass<kVerifyNone>(), down_cast<Class*>(copy));
+  } else if (orig->IsObjectArray<kVerifyNone>()) {
+    FixupObjectArray(orig->AsObjectArray<Object, kVerifyNone>(),
+                     down_cast<ObjectArray<Object>*>(copy));
+  } else if (orig->IsArtMethod<kVerifyNone>()) {
+    FixupMethod(orig->AsArtMethod<kVerifyNone>(), down_cast<ArtMethod*>(copy));
   } else {
     FixupInstanceFields(orig, copy);
   }
@@ -607,54 +609,54 @@
 
   // The resolution method has a special trampoline to call.
   if (UNLIKELY(orig == Runtime::Current()->GetResolutionMethod())) {
-    copy->SetEntryPointFromPortableCompiledCode(GetOatAddress(portable_resolution_trampoline_offset_));
-    copy->SetEntryPointFromQuickCompiledCode(GetOatAddress(quick_resolution_trampoline_offset_));
+    copy->SetEntryPointFromPortableCompiledCode<kVerifyNone>(GetOatAddress(portable_resolution_trampoline_offset_));
+    copy->SetEntryPointFromQuickCompiledCode<kVerifyNone>(GetOatAddress(quick_resolution_trampoline_offset_));
   } else if (UNLIKELY(orig == Runtime::Current()->GetImtConflictMethod())) {
-    copy->SetEntryPointFromPortableCompiledCode(GetOatAddress(portable_imt_conflict_trampoline_offset_));
-    copy->SetEntryPointFromQuickCompiledCode(GetOatAddress(quick_imt_conflict_trampoline_offset_));
+    copy->SetEntryPointFromPortableCompiledCode<kVerifyNone>(GetOatAddress(portable_imt_conflict_trampoline_offset_));
+    copy->SetEntryPointFromQuickCompiledCode<kVerifyNone>(GetOatAddress(quick_imt_conflict_trampoline_offset_));
   } else {
     // We assume all methods have code. If they don't currently then we set them to the use the
     // resolution trampoline. Abstract methods never have code and so we need to make sure their
     // use results in an AbstractMethodError. We use the interpreter to achieve this.
     if (UNLIKELY(orig->IsAbstract())) {
-      copy->SetEntryPointFromPortableCompiledCode(GetOatAddress(portable_to_interpreter_bridge_offset_));
-      copy->SetEntryPointFromQuickCompiledCode(GetOatAddress(quick_to_interpreter_bridge_offset_));
-      copy->SetEntryPointFromInterpreter(reinterpret_cast<EntryPointFromInterpreter*>
+      copy->SetEntryPointFromPortableCompiledCode<kVerifyNone>(GetOatAddress(portable_to_interpreter_bridge_offset_));
+      copy->SetEntryPointFromQuickCompiledCode<kVerifyNone>(GetOatAddress(quick_to_interpreter_bridge_offset_));
+      copy->SetEntryPointFromInterpreter<kVerifyNone>(reinterpret_cast<EntryPointFromInterpreter*>
           (const_cast<byte*>(GetOatAddress(interpreter_to_interpreter_bridge_offset_))));
     } else {
-      copy->SetEntryPointFromInterpreter(reinterpret_cast<EntryPointFromInterpreter*>
+      copy->SetEntryPointFromInterpreter<kVerifyNone>(reinterpret_cast<EntryPointFromInterpreter*>
           (const_cast<byte*>(GetOatAddress(interpreter_to_compiled_code_bridge_offset_))));
       // Use original code if it exists. Otherwise, set the code pointer to the resolution
       // trampoline.
       const byte* quick_code = GetOatAddress(orig->GetQuickOatCodeOffset());
       if (quick_code != nullptr) {
-        copy->SetEntryPointFromQuickCompiledCode(quick_code);
+        copy->SetEntryPointFromQuickCompiledCode<kVerifyNone>(quick_code);
       } else {
-        copy->SetEntryPointFromQuickCompiledCode(GetOatAddress(quick_resolution_trampoline_offset_));
+        copy->SetEntryPointFromQuickCompiledCode<kVerifyNone>(GetOatAddress(quick_resolution_trampoline_offset_));
       }
       const byte* portable_code = GetOatAddress(orig->GetPortableOatCodeOffset());
       if (portable_code != nullptr) {
-        copy->SetEntryPointFromPortableCompiledCode(portable_code);
+        copy->SetEntryPointFromPortableCompiledCode<kVerifyNone>(portable_code);
       } else {
-        copy->SetEntryPointFromPortableCompiledCode(GetOatAddress(portable_resolution_trampoline_offset_));
+        copy->SetEntryPointFromPortableCompiledCode<kVerifyNone>(GetOatAddress(portable_resolution_trampoline_offset_));
       }
       if (orig->IsNative()) {
         // The native method's pointer is set to a stub to lookup via dlsym.
         // Note this is not the code_ pointer, that is handled above.
-        copy->SetNativeMethod(GetOatAddress(jni_dlsym_lookup_offset_));
+        copy->SetNativeMethod<kVerifyNone>(GetOatAddress(jni_dlsym_lookup_offset_));
       } else {
         // Normal (non-abstract non-native) methods have various tables to relocate.
         uint32_t mapping_table_off = orig->GetOatMappingTableOffset();
         const byte* mapping_table = GetOatAddress(mapping_table_off);
-        copy->SetMappingTable(mapping_table);
+        copy->SetMappingTable<kVerifyNone>(mapping_table);
 
         uint32_t vmap_table_offset = orig->GetOatVmapTableOffset();
         const byte* vmap_table = GetOatAddress(vmap_table_offset);
-        copy->SetVmapTable(vmap_table);
+        copy->SetVmapTable<kVerifyNone>(vmap_table);
 
         uint32_t native_gc_map_offset = orig->GetOatNativeGcMapOffset();
         const byte* native_gc_map = GetOatAddress(native_gc_map_offset);
-        copy->SetNativeGcMap(reinterpret_cast<const uint8_t*>(native_gc_map));
+        copy->SetNativeGcMap<kVerifyNone>(reinterpret_cast<const uint8_t*>(native_gc_map));
       }
     }
   }
@@ -663,7 +665,7 @@
 void ImageWriter::FixupObjectArray(ObjectArray<Object>* orig, ObjectArray<Object>* copy) {
   for (int32_t i = 0; i < orig->GetLength(); ++i) {
     Object* element = orig->Get(i);
-    copy->SetWithoutChecksAndWriteBarrier<false>(i, GetImageAddress(element));
+    copy->SetWithoutChecksAndWriteBarrier<false, true, kVerifyNone>(i, GetImageAddress(element));
   }
 }
 
@@ -690,10 +692,11 @@
     while (ref_offsets != 0) {
       size_t right_shift = CLZ(ref_offsets);
       MemberOffset byte_offset = CLASS_OFFSET_FROM_CLZ(right_shift);
-      Object* ref = orig->GetFieldObject<Object>(byte_offset, false);
+      Object* ref = orig->GetFieldObject<Object, kVerifyNone>(byte_offset, false);
       // Use SetFieldObjectWithoutWriteBarrier to avoid card marking since we are writing to the
       // image.
-      copy->SetFieldObjectWithoutWriteBarrier<false>(byte_offset, GetImageAddress(ref), false);
+      copy->SetFieldObjectWithoutWriteBarrier<false, true, kVerifyNone>(
+          byte_offset, GetImageAddress(ref), false);
       ref_offsets &= ~(CLASS_HIGH_BIT >> right_shift);
     }
   } else {
@@ -712,10 +715,11 @@
                            ? klass->GetStaticField(i)
                            : klass->GetInstanceField(i));
         MemberOffset field_offset = field->GetOffset();
-        Object* ref = orig->GetFieldObject<Object>(field_offset, false);
+        Object* ref = orig->GetFieldObject<Object, kVerifyNone>(field_offset, false);
         // Use SetFieldObjectWithoutWriteBarrier to avoid card marking since we are writing to the
         // image.
-        copy->SetFieldObjectWithoutWriteBarrier<false>(field_offset, GetImageAddress(ref), false);
+        copy->SetFieldObjectWithoutWriteBarrier<false, true, kVerifyNone>(
+            field_offset, GetImageAddress(ref), false);
       }
     }
   }
@@ -726,7 +730,8 @@
     Object* ref = orig->GetFieldObject<Object>(field_offset, false);
     // Use SetFieldObjectWithoutWriteBarrier to avoid card marking since we are writing to the
     // image.
-    copy->SetFieldObjectWithoutWriteBarrier<false>(field_offset, GetImageAddress(ref), false);
+    copy->SetFieldObjectWithoutWriteBarrier<false, true, kVerifyNone>(
+        field_offset, GetImageAddress(ref), false);
   }
 }
 
diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc
index c77d319..1bdff37 100644
--- a/compiler/jni/jni_compiler_test.cc
+++ b/compiler/jni/jni_compiler_test.cc
@@ -50,7 +50,7 @@
     ScopedObjectAccess soa(Thread::Current());
     SirtRef<mirror::ClassLoader> loader(soa.Self(), soa.Decode<mirror::ClassLoader*>(class_loader));
     // Compile the native method before starting the runtime
-    mirror::Class* c = class_linker_->FindClass("LMyClassNatives;", loader);
+    mirror::Class* c = class_linker_->FindClass(soa.Self(), "LMyClassNatives;", loader);
     mirror::ArtMethod* method;
     if (direct) {
       method = c->FindDirectMethod(method_name, method_sig);
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index 10d2c5c..e91ffcb 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -91,11 +91,14 @@
   InstructionSet insn_set = kIsTargetBuild ? kThumb2 : kX86;
 
   InstructionSetFeatures insn_features;
-  verification_results_.reset(new VerificationResults);
+  compiler_options_.reset(new CompilerOptions);
+  verification_results_.reset(new VerificationResults(compiler_options_.get()));
   method_inliner_map_.reset(new DexFileToMethodInlinerMap);
-  callbacks_.Reset(verification_results_.get(), method_inliner_map_.get());
+  callbacks_.reset(new CompilerCallbacksImpl(verification_results_.get(),
+                                             method_inliner_map_.get()));
   timer_.reset(new CumulativeLogger("Compilation times"));
-  compiler_driver_.reset(new CompilerDriver(verification_results_.get(),
+  compiler_driver_.reset(new CompilerDriver(compiler_options_.get(),
+                                            verification_results_.get(),
                                             method_inliner_map_.get(),
                                             compiler_backend, insn_set,
                                             insn_features, false, NULL, 2, true, true,
@@ -150,8 +153,8 @@
       num_virtual_methods = it.NumVirtualMethods();
     }
     const char* descriptor = dex_file->GetClassDescriptor(class_def);
-    SirtRef<mirror::ClassLoader> loader(Thread::Current(), nullptr);
-    mirror::Class* klass = class_linker->FindClass(descriptor, loader);
+    SirtRef<mirror::ClassLoader> loader(soa.Self(), nullptr);
+    mirror::Class* klass = class_linker->FindClass(soa.Self(), descriptor, loader);
 
     UniquePtr<const OatFile::OatClass> oat_class(oat_dex_file->GetOatClass(i));
     CHECK_EQ(mirror::Class::Status::kStatusNotReady, oat_class->GetStatus()) << descriptor;
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index dab419f..a082e36 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -34,7 +34,9 @@
 #include "compiler_callbacks.h"
 #include "dex_file-inl.h"
 #include "dex/verification_results.h"
+#include "driver/compiler_callbacks_impl.h"
 #include "driver/compiler_driver.h"
+#include "driver/compiler_options.h"
 #include "elf_fixup.h"
 #include "elf_stripper.h"
 #include "gc/space/image_space.h"
@@ -54,15 +56,22 @@
 #include "scoped_thread_state_change.h"
 #include "sirt_ref.h"
 #include "vector_output_stream.h"
-#include "verifier/method_verifier.h"
-#include "verifier/method_verifier-inl.h"
 #include "well_known_classes.h"
 #include "zip_archive.h"
 
-#include "dex/quick/dex_file_to_method_inliner_map.h"
-
 namespace art {
 
+static int original_argc;
+static char** original_argv;
+
+static std::string CommandLine() {
+  std::vector<std::string> command;
+  for (int i = 0; i < original_argc; ++i) {
+    command.push_back(original_argv[i]);
+  }
+  return Join(command, ' ');
+}
+
 static void UsageErrorV(const char* fmt, va_list ap) {
   std::string error;
   StringAppendV(&error, fmt, ap);
@@ -82,6 +91,8 @@
   UsageErrorV(fmt, ap);
   va_end(ap);
 
+  UsageError("Command: %s", CommandLine().c_str());
+
   UsageError("Usage: dex2oat [options]...");
   UsageError("");
   UsageError("  --dex-file=<dex-file>: specifies a .dex file to compile.");
@@ -147,6 +158,46 @@
   UsageError("      Example: --compiler-backend=Portable");
   UsageError("      Default: Quick");
   UsageError("");
+  UsageError("  --compiler-filter=(interpret-only|space|balanced|speed|everything): select");
+  UsageError("      compiler filter.");
+  UsageError("      Example: --compiler-filter=everything");
+#if ART_SMALL_MODE
+  UsageError("      Default: interpret-only");
+#else
+  UsageError("      Default: speed");
+#endif
+  UsageError("");
+  UsageError("  --huge-method-max=<method-instruction-count>: the threshold size for a huge");
+  UsageError("      method for compiler filter tuning.");
+  UsageError("      Example: --huge-method-max=%d", CompilerOptions::kDefaultHugeMethodThreshold);
+  UsageError("      Default: %d", CompilerOptions::kDefaultHugeMethodThreshold);
+  UsageError("");
+  UsageError("  --huge-method-max=<method-instruction-count>: threshold size for a huge");
+  UsageError("      method for compiler filter tuning.");
+  UsageError("      Example: --huge-method-max=%d", CompilerOptions::kDefaultHugeMethodThreshold);
+  UsageError("      Default: %d", CompilerOptions::kDefaultHugeMethodThreshold);
+  UsageError("");
+  UsageError("  --large-method-max=<method-instruction-count>: threshold size for a large");
+  UsageError("      method for compiler filter tuning.");
+  UsageError("      Example: --large-method-max=%d", CompilerOptions::kDefaultLargeMethodThreshold);
+  UsageError("      Default: %d", CompilerOptions::kDefaultLargeMethodThreshold);
+  UsageError("");
+  UsageError("  --small-method-max=<method-instruction-count>: threshold size for a small");
+  UsageError("      method for compiler filter tuning.");
+  UsageError("      Example: --small-method-max=%d", CompilerOptions::kDefaultSmallMethodThreshold);
+  UsageError("      Default: %d", CompilerOptions::kDefaultSmallMethodThreshold);
+  UsageError("");
+  UsageError("  --tiny-method-max=<method-instruction-count>: threshold size for a tiny");
+  UsageError("      method for compiler filter tuning.");
+  UsageError("      Example: --tiny-method-max=%d", CompilerOptions::kDefaultTinyMethodThreshold);
+  UsageError("      Default: %d", CompilerOptions::kDefaultTinyMethodThreshold);
+  UsageError("");
+  UsageError("  --num-dex-methods=<method-count>: threshold size for a small dex file for");
+  UsageError("      compiler filter tuning. If the input has fewer than this many methods");
+  UsageError("      and the filter is not interpret-only, overrides the filter to use speed");
+  UsageError("      Example: --num-dex-method=%d", CompilerOptions::kDefaultNumDexMethodsThreshold);
+  UsageError("      Default: %d", CompilerOptions::kDefaultNumDexMethodsThreshold);
+  UsageError("");
   UsageError("  --host: used with Portable backend to link against host runtime libraries");
   UsageError("");
   UsageError("  --dump-timing: display a breakdown of where time was spent");
@@ -163,15 +214,25 @@
 class Dex2Oat {
  public:
   static bool Create(Dex2Oat** p_dex2oat,
-                     Runtime::Options& options,
+                     const Runtime::Options& runtime_options,
+                     const CompilerOptions& compiler_options,
                      CompilerBackend::Kind compiler_backend,
                      InstructionSet instruction_set,
                      InstructionSetFeatures instruction_set_features,
+                     VerificationResults* verification_results,
+                     DexFileToMethodInlinerMap* method_inliner_map,
                      size_t thread_count)
       SHARED_TRYLOCK_FUNCTION(true, Locks::mutator_lock_) {
-    UniquePtr<Dex2Oat> dex2oat(new Dex2Oat(compiler_backend, instruction_set,
-                                           instruction_set_features, thread_count));
-    if (!dex2oat->CreateRuntime(options, instruction_set)) {
+    CHECK(verification_results != nullptr);
+    CHECK(method_inliner_map != nullptr);
+    UniquePtr<Dex2Oat> dex2oat(new Dex2Oat(&compiler_options,
+                                           compiler_backend,
+                                           instruction_set,
+                                           instruction_set_features,
+                                           verification_results,
+                                           method_inliner_map,
+                                           thread_count));
+    if (!dex2oat->CreateRuntime(runtime_options, instruction_set)) {
       *p_dex2oat = NULL;
       return false;
     }
@@ -275,8 +336,9 @@
       Runtime::Current()->SetCompileTimeClassPath(class_loader, class_path_files);
     }
 
-    UniquePtr<CompilerDriver> driver(new CompilerDriver(verification_results_.get(),
-                                                        method_inliner_map_.get(),
+    UniquePtr<CompilerDriver> driver(new CompilerDriver(compiler_options_,
+                                                        verification_results_,
+                                                        method_inliner_map_,
                                                         compiler_backend_,
                                                         instruction_set_,
                                                         instruction_set_features_,
@@ -353,53 +415,30 @@
   }
 
  private:
-  class Dex2OatCompilerCallbacks : public CompilerCallbacks {
-    public:
-      Dex2OatCompilerCallbacks(VerificationResults* verification_results,
-                               DexFileToMethodInlinerMap* method_inliner_map)
-          : verification_results_(verification_results),
-            method_inliner_map_(method_inliner_map) { }
-      virtual ~Dex2OatCompilerCallbacks() { }
-
-      virtual bool MethodVerified(verifier::MethodVerifier* verifier)
-          SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-        bool result = verification_results_->ProcessVerifiedMethod(verifier);
-        if (result) {
-          MethodReference ref = verifier->GetMethodReference();
-          method_inliner_map_->GetMethodInliner(ref.dex_file)
-              ->AnalyseMethodCode(verifier);
-        }
-        return result;
-      }
-      virtual void ClassRejected(ClassReference ref) {
-        verification_results_->AddRejectedClass(ref);
-      }
-
-    private:
-      VerificationResults* verification_results_;
-      DexFileToMethodInlinerMap* method_inliner_map_;
-  };
-
-  explicit Dex2Oat(CompilerBackend::Kind compiler_backend,
+  explicit Dex2Oat(const CompilerOptions* compiler_options,
+                   CompilerBackend::Kind compiler_backend,
                    InstructionSet instruction_set,
                    InstructionSetFeatures instruction_set_features,
+                   VerificationResults* verification_results,
+                   DexFileToMethodInlinerMap* method_inliner_map,
                    size_t thread_count)
-      : compiler_backend_(compiler_backend),
+      : compiler_options_(compiler_options),
+        compiler_backend_(compiler_backend),
         instruction_set_(instruction_set),
         instruction_set_features_(instruction_set_features),
-        verification_results_(new VerificationResults),
-        method_inliner_map_(new DexFileToMethodInlinerMap),
-        callbacks_(verification_results_.get(), method_inliner_map_.get()),
+        verification_results_(verification_results),
+        method_inliner_map_(method_inliner_map),
         runtime_(nullptr),
         thread_count_(thread_count),
         start_ns_(NanoTime()) {
+    CHECK(compiler_options != nullptr);
+    CHECK(verification_results != nullptr);
+    CHECK(method_inliner_map != nullptr);
   }
 
-  bool CreateRuntime(Runtime::Options& options, InstructionSet instruction_set)
+  bool CreateRuntime(const Runtime::Options& runtime_options, InstructionSet instruction_set)
       SHARED_TRYLOCK_FUNCTION(true, Locks::mutator_lock_) {
-    options.push_back(
-        std::make_pair("compilercallbacks", static_cast<CompilerCallbacks*>(&callbacks_)));
-    if (!Runtime::Create(options, false)) {
+    if (!Runtime::Create(runtime_options, false)) {
       LOG(ERROR) << "Failed to create runtime";
       return false;
     }
@@ -448,14 +487,14 @@
     return false;
   }
 
+  const CompilerOptions* const compiler_options_;
   const CompilerBackend::Kind compiler_backend_;
 
   const InstructionSet instruction_set_;
   const InstructionSetFeatures instruction_set_features_;
 
-  UniquePtr<VerificationResults> verification_results_;
-  UniquePtr<DexFileToMethodInlinerMap> method_inliner_map_;
-  Dex2OatCompilerCallbacks callbacks_;
+  VerificationResults* const verification_results_;
+  DexFileToMethodInlinerMap* const method_inliner_map_;
   Runtime* runtime_;
   size_t thread_count_;
   uint64_t start_ns_;
@@ -579,7 +618,7 @@
     CHECK_GT(kWatchDogTimeoutSeconds, kWatchDogWarningSeconds);
     // TODO: tune the multiplier for GC verification, the following is just to make the timeout
     //       large.
-    int64_t multiplier = gc::kDesiredHeapVerification > gc::kVerifyAllFast ? 100 : 1;
+    int64_t multiplier = kVerifyObjectSupport > kVerifyObjectModeFast ? 100 : 1;
     timespec warning_ts;
     InitTimeSpec(true, CLOCK_REALTIME, multiplier * kWatchDogWarningSeconds * 1000, 0, &warning_ts);
     timespec timeout_ts;
@@ -656,6 +695,9 @@
 }
 
 static int dex2oat(int argc, char** argv) {
+  original_argc = argc;
+  original_argv = argv;
+
   TimingLogger timings("compiler", false, false);
   CumulativeLogger compiler_phases_timings("compilation times");
 
@@ -690,6 +732,12 @@
   CompilerBackend::Kind compiler_backend = kUsePortableCompiler
       ? CompilerBackend::kPortable
       : CompilerBackend::kQuick;
+  const char* compiler_filter_string = NULL;
+  int huge_method_threshold = CompilerOptions::kDefaultHugeMethodThreshold;
+  int large_method_threshold = CompilerOptions::kDefaultLargeMethodThreshold;
+  int small_method_threshold = CompilerOptions::kDefaultSmallMethodThreshold;
+  int tiny_method_threshold = CompilerOptions::kDefaultTinyMethodThreshold;
+  int num_dex_methods_threshold = CompilerOptions::kDefaultNumDexMethodsThreshold;
 
   // Take the default set of instruction features from the build.
   InstructionSetFeatures instruction_set_features =
@@ -713,7 +761,6 @@
   bool dump_slow_timing = kIsDebugBuild;
   bool watch_dog_enabled = !kIsTargetBuild;
 
-
   for (int i = 0; i < argc; i++) {
     const StringPiece option(argv[i]);
     bool log_options = false;
@@ -729,6 +776,9 @@
       if (!ParseInt(zip_fd_str, &zip_fd)) {
         Usage("Failed to parse --zip-fd argument '%s' as an integer", zip_fd_str);
       }
+      if (zip_fd < 0) {
+        Usage("--zip-fd passed a negative value %d", zip_fd);
+      }
     } else if (option.starts_with("--zip-location=")) {
       zip_location = option.substr(strlen("--zip-location=")).data();
     } else if (option.starts_with("--oat-file=")) {
@@ -740,6 +790,9 @@
       if (!ParseInt(oat_fd_str, &oat_fd)) {
         Usage("Failed to parse --oat-fd argument '%s' as an integer", oat_fd_str);
       }
+      if (oat_fd < 0) {
+        Usage("--oat-fd passed a negative value %d", oat_fd);
+      }
     } else if (option == "--watch-dog") {
       watch_dog_enabled = true;
     } else if (option == "--no-watch-dog") {
@@ -793,6 +846,48 @@
       } else if (backend_str == "Portable") {
         compiler_backend = CompilerBackend::kPortable;
       }
+    } else if (option.starts_with("--compiler-filter=")) {
+      compiler_filter_string = option.substr(strlen("--compiler-filter=")).data();
+    } else if (option.starts_with("--huge-method-max=")) {
+      const char* threshold = option.substr(strlen("--huge-method-max=")).data();
+      if (!ParseInt(threshold, &huge_method_threshold)) {
+        Usage("Failed to parse --huge-method-max '%s' as an integer", threshold);
+      }
+      if (huge_method_threshold < 0) {
+        Usage("--huge-method-max passed a negative value %s", huge_method_threshold);
+      }
+    } else if (option.starts_with("--large-method-max=")) {
+      const char* threshold = option.substr(strlen("--large-method-max=")).data();
+      if (!ParseInt(threshold, &large_method_threshold)) {
+        Usage("Failed to parse --large-method-max '%s' as an integer", threshold);
+      }
+      if (large_method_threshold < 0) {
+        Usage("--large-method-max passed a negative value %s", large_method_threshold);
+      }
+    } else if (option.starts_with("--small-method-max=")) {
+      const char* threshold = option.substr(strlen("--small-method-max=")).data();
+      if (!ParseInt(threshold, &small_method_threshold)) {
+        Usage("Failed to parse --small-method-max '%s' as an integer", threshold);
+      }
+      if (small_method_threshold < 0) {
+        Usage("--small-method-max passed a negative value %s", small_method_threshold);
+      }
+    } else if (option.starts_with("--tiny-method-max=")) {
+      const char* threshold = option.substr(strlen("--tiny-method-max=")).data();
+      if (!ParseInt(threshold, &tiny_method_threshold)) {
+        Usage("Failed to parse --tiny-method-max '%s' as an integer", threshold);
+      }
+      if (tiny_method_threshold < 0) {
+        Usage("--tiny-method-max passed a negative value %s", tiny_method_threshold);
+      }
+    } else if (option.starts_with("--num-dex-methods=")) {
+      const char* threshold = option.substr(strlen("--num-dex-methods=")).data();
+      if (!ParseInt(threshold, &num_dex_methods_threshold)) {
+        Usage("Failed to parse --num-dex-methods '%s' as an integer", threshold);
+      }
+      if (num_dex_methods_threshold < 0) {
+        Usage("--num-dex-methods passed a negative value %s", num_dex_methods_threshold);
+      }
     } else if (option == "--host") {
       is_host = true;
     } else if (option == "--runtime-arg") {
@@ -915,6 +1010,44 @@
     oat_unstripped += oat_filename;
   }
 
+  if (compiler_filter_string == NULL) {
+    if (image) {
+      compiler_filter_string = "everything";
+    } else {
+#if ART_SMALL_MODE
+      compiler_filter_string = "interpret-only";
+#else
+      compiler_filter_string = "speed";
+#endif
+    }
+  }
+  CHECK(compiler_filter_string != nullptr);
+  CompilerOptions::CompilerFilter compiler_filter = CompilerOptions::kDefaultCompilerFilter;
+  if (strcmp(compiler_filter_string, "interpret-only") == 0) {
+    compiler_filter = CompilerOptions::kInterpretOnly;
+  } else if (strcmp(compiler_filter_string, "space") == 0) {
+    compiler_filter = CompilerOptions::kSpace;
+  } else if (strcmp(compiler_filter_string, "balanced") == 0) {
+    compiler_filter = CompilerOptions::kBalanced;
+  } else if (strcmp(compiler_filter_string, "speed") == 0) {
+    compiler_filter = CompilerOptions::kSpeed;
+  } else if (strcmp(compiler_filter_string, "everything") == 0) {
+    compiler_filter = CompilerOptions::kEverything;
+  } else {
+    Usage("Unknown --compiler-filter value %s", compiler_filter_string);
+  }
+
+  CompilerOptions compiler_options(compiler_filter,
+                                   huge_method_threshold,
+                                   large_method_threshold,
+                                   small_method_threshold,
+                                   tiny_method_threshold,
+                                   num_dex_methods_threshold
+#ifdef ART_SEA_IR_MODE
+                                   , compiler_options.sea_ir_ = true;
+#endif
+                                   );  // NOLINT(whitespace/parens)
+
   // Done with usage checks, enable watchdog if requested
   WatchDog watch_dog(watch_dog_enabled);
 
@@ -940,22 +1073,9 @@
   }
 
   timings.StartSplit("dex2oat Setup");
-  LOG(INFO) << "dex2oat: " << oat_location;
+  LOG(INFO) << "dex2oat: " << CommandLine();
 
-  if (image) {
-    bool has_compiler_filter = false;
-    for (const char* r : runtime_args) {
-      if (strncmp(r, "-compiler-filter:", 17) == 0) {
-        has_compiler_filter = true;
-        break;
-      }
-    }
-    if (!has_compiler_filter) {
-      runtime_args.push_back("-compiler-filter:everything");
-    }
-  }
-
-  Runtime::Options options;
+  Runtime::Options runtime_options;
   std::vector<const DexFile*> boot_class_path;
   if (boot_image_option.empty()) {
     size_t failure_count = OpenDexFiles(dex_filenames, dex_locations, boot_class_path);
@@ -963,24 +1083,33 @@
       LOG(ERROR) << "Failed to open some dex files: " << failure_count;
       return EXIT_FAILURE;
     }
-    options.push_back(std::make_pair("bootclasspath", &boot_class_path));
+    runtime_options.push_back(std::make_pair("bootclasspath", &boot_class_path));
   } else {
-    options.push_back(std::make_pair(boot_image_option.c_str(), reinterpret_cast<void*>(NULL)));
+    runtime_options.push_back(std::make_pair(boot_image_option.c_str(),
+                                             reinterpret_cast<void*>(NULL)));
   }
   if (host_prefix.get() != NULL) {
-    options.push_back(std::make_pair("host-prefix", host_prefix->c_str()));
+    runtime_options.push_back(std::make_pair("host-prefix", host_prefix->c_str()));
   }
   for (size_t i = 0; i < runtime_args.size(); i++) {
-    options.push_back(std::make_pair(runtime_args[i], reinterpret_cast<void*>(NULL)));
+    runtime_options.push_back(std::make_pair(runtime_args[i], reinterpret_cast<void*>(NULL)));
   }
 
-#ifdef ART_SEA_IR_MODE
-  options.push_back(std::make_pair("-sea_ir", reinterpret_cast<void*>(NULL)));
-#endif
+  VerificationResults verification_results(&compiler_options);
+  DexFileToMethodInlinerMap method_inliner_map;
+  CompilerCallbacksImpl callbacks(&verification_results, &method_inliner_map);
+  runtime_options.push_back(std::make_pair("compilercallbacks", &callbacks));
 
   Dex2Oat* p_dex2oat;
-  if (!Dex2Oat::Create(&p_dex2oat, options, compiler_backend, instruction_set,
-      instruction_set_features, thread_count)) {
+  if (!Dex2Oat::Create(&p_dex2oat,
+                       runtime_options,
+                       compiler_options,
+                       compiler_backend,
+                       instruction_set,
+                       instruction_set_features,
+                       &verification_results,
+                       &method_inliner_map,
+                       thread_count)) {
     LOG(ERROR) << "Failed to create dex2oat";
     return EXIT_FAILURE;
   }
@@ -1043,6 +1172,22 @@
       }
     }
 
+    const bool kSaveDexInput = false;
+    if (kSaveDexInput) {
+      for (size_t i = 0; i < dex_files.size(); ++i) {
+        const DexFile* dex_file = dex_files[i];
+        std::string tmp_file_name(StringPrintf("/data/local/tmp/dex2oat.%d.%zd.dex", getpid(), i));
+        UniquePtr<File> tmp_file(OS::CreateEmptyFile(tmp_file_name.c_str()));
+        if (tmp_file.get() == nullptr) {
+            PLOG(ERROR) << "Failed to open file " << tmp_file_name
+                        << ". Try: adb shell chmod 777 /data/local/tmp";
+            continue;
+        }
+        tmp_file->WriteFully(dex_file->Begin(), dex_file->Size());
+        LOG(INFO) << "Wrote input to " << tmp_file_name;
+      }
+    }
+
     // Ensure opened dex files are writable for dex-to-dex transformations.
     for (const auto& dex_file : dex_files) {
       if (!dex_file->EnableWrite()) {
@@ -1055,15 +1200,15 @@
    * If we're not in interpret-only mode, go ahead and compile small applications. Don't
    * bother to check if we're doing the image.
    */
-  if (!image && (Runtime::Current()->GetCompilerFilter() != Runtime::kInterpretOnly)) {
+  if (!image && (compiler_options.GetCompilerFilter() != CompilerOptions::kInterpretOnly)) {
     size_t num_methods = 0;
     for (size_t i = 0; i != dex_files.size(); ++i) {
       const DexFile* dex_file = dex_files[i];
       CHECK(dex_file != NULL);
       num_methods += dex_file->NumMethodIds();
     }
-    if (num_methods <= Runtime::Current()->GetNumDexMethodsThreshold()) {
-      Runtime::Current()->SetCompilerFilter(Runtime::kSpeed);
+    if (num_methods <= compiler_options.GetNumDexMethodsThreshold()) {
+      compiler_options.SetCompilerFilter(CompilerOptions::kSpeed);
       VLOG(compiler) << "Below method threshold, compiling anyways";
     }
   }
@@ -1207,13 +1352,13 @@
 
   // Everything was successfully written, do an explicit exit here to avoid running Runtime
   // destructors that take time (bug 10645725) unless we're a debug build or running on valgrind.
-  if (!kIsDebugBuild || (RUNNING_ON_VALGRIND == 0)) {
+  if (!kIsDebugBuild && (RUNNING_ON_VALGRIND == 0)) {
     dex2oat->LogCompletionTime();
     exit(EXIT_SUCCESS);
   }
 
   return EXIT_SUCCESS;
-}
+}  // NOLINT(readability/fn_size)
 }  // namespace art
 
 int main(int argc, char** argv) {
diff --git a/disassembler/disassembler_x86.cc b/disassembler/disassembler_x86.cc
index b6ddc95..d86ba7b 100644
--- a/disassembler/disassembler_x86.cc
+++ b/disassembler/disassembler_x86.cc
@@ -633,6 +633,9 @@
   case 0x99:
     opcode << "cdq";
     break;
+  case 0xAF:
+    opcode << (prefix[2] == 0x66 ? "scasw" : "scasl");
+    break;
   case 0xB0: case 0xB1: case 0xB2: case 0xB3: case 0xB4: case 0xB5: case 0xB6: case 0xB7:
     opcode << "mov";
     immediate_bytes = 1;
@@ -693,6 +696,7 @@
     has_modrm = true;
     reg_is_opcode = true;
     break;
+  case 0xE3: opcode << "jecxz"; branch_bytes = 1; break;
   case 0xE8: opcode << "call"; branch_bytes = 4; break;
   case 0xE9: opcode << "jmp"; branch_bytes = 4; break;
   case 0xEB: opcode << "jmp"; branch_bytes = 1; break;
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 53b07f9..330a307 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -49,6 +49,7 @@
 #include "runtime.h"
 #include "safe_map.h"
 #include "scoped_thread_state_change.h"
+#include "thread_list.h"
 #include "verifier/dex_gc_map.h"
 #include "verifier/method_verifier.h"
 #include "vmap_table.h"
@@ -83,6 +84,11 @@
           "  --output=<file> may be used to send the output to a file.\n"
           "      Example: --output=/tmp/oatdump.txt\n"
           "\n");
+  fprintf(stderr,
+          "  --dump:[raw_mapping_table|raw_gc_map]\n"
+          "    Example: --dump:raw_gc_map\n"
+          "    Default: neither\n"
+          "\n");
   exit(EXIT_FAILURE);
 }
 
@@ -100,10 +106,12 @@
 
 class OatDumper {
  public:
-  explicit OatDumper(const std::string& host_prefix, const OatFile& oat_file)
+  explicit OatDumper(const std::string& host_prefix, const OatFile& oat_file, bool dump_raw_mapping_table, bool dump_raw_gc_map)
     : host_prefix_(host_prefix),
       oat_file_(oat_file),
       oat_dex_files_(oat_file.GetOatDexFiles()),
+      dump_raw_mapping_table_(dump_raw_mapping_table),
+      dump_raw_gc_map_(dump_raw_gc_map),
       disassembler_(Disassembler::Create(oat_file_.GetOatHeader().GetInstructionSet())) {
     AddAllOffsets();
   }
@@ -358,16 +366,14 @@
       DumpVmap(indent2_os, oat_method);
       indent2_os << StringPrintf("mapping_table: %p (offset=0x%08x)\n",
                                  oat_method.GetMappingTable(), oat_method.GetMappingTableOffset());
-      const bool kDumpRawMappingTable = false;
-      if (kDumpRawMappingTable) {
+      if (dump_raw_mapping_table_) {
         Indenter indent3_filter(indent2_os.rdbuf(), kIndentChar, kIndentBy1Count);
         std::ostream indent3_os(&indent3_filter);
         DumpMappingTable(indent3_os, oat_method);
       }
       indent2_os << StringPrintf("gc_map: %p (offset=0x%08x)\n",
                                  oat_method.GetNativeGcMap(), oat_method.GetNativeGcMapOffset());
-      const bool kDumpRawGcMap = false;
-      if (kDumpRawGcMap) {
+      if (dump_raw_gc_map_) {
         Indenter indent3_filter(indent2_os.rdbuf(), kIndentChar, kIndentBy1Count);
         std::ostream indent3_os(&indent3_filter);
         DumpGcMap(indent3_os, oat_method, code_item);
@@ -699,6 +705,8 @@
   const std::string host_prefix_;
   const OatFile& oat_file_;
   std::vector<const OatFile::OatDexFile*> oat_dex_files_;
+  bool dump_raw_mapping_table_;
+  bool dump_raw_gc_map_;
   std::set<uintptr_t> offsets_;
   UniquePtr<Disassembler> disassembler_;
 };
@@ -707,9 +715,12 @@
  public:
   explicit ImageDumper(std::ostream* os, const std::string& image_filename,
                        const std::string& host_prefix, gc::space::ImageSpace& image_space,
-                       const ImageHeader& image_header)
+                       const ImageHeader& image_header, bool dump_raw_mapping_table,
+                       bool dump_raw_gc_map)
       : os_(os), image_filename_(image_filename), host_prefix_(host_prefix),
-        image_space_(image_space), image_header_(image_header) {}
+        image_space_(image_space), image_header_(image_header),
+        dump_raw_mapping_table_(dump_raw_mapping_table),
+        dump_raw_gc_map_(dump_raw_gc_map) {}
 
   void Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     std::ostream& os = *os_;
@@ -791,7 +802,8 @@
 
     stats_.oat_file_bytes = oat_file->Size();
 
-    oat_dumper_.reset(new OatDumper(host_prefix_, *oat_file));
+    oat_dumper_.reset(new OatDumper(host_prefix_, *oat_file, dump_raw_mapping_table_,
+        dump_raw_gc_map_));
 
     for (const OatFile::OatDexFile* oat_dex_file : oat_file->GetOatDexFiles()) {
       CHECK(oat_dex_file != NULL);
@@ -806,8 +818,21 @@
     const std::vector<gc::space::ContinuousSpace*>& spaces = heap->GetContinuousSpaces();
     Thread* self = Thread::Current();
     {
-      WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
-      heap->FlushAllocStack();
+      {
+        WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
+        heap->FlushAllocStack();
+      }
+      // Since FlushAllocStack() above resets the (active) allocation
+      // stack. Need to revoke the thread-local allocation stacks that
+      // point into it.
+      {
+        self->TransitionFromRunnableToSuspended(kNative);
+        ThreadList* thread_list = Runtime::Current()->GetThreadList();
+        thread_list->SuspendAll();
+        heap->RevokeAllThreadLocalAllocationStacks(self);
+        thread_list->ResumeAll();
+        self->TransitionFromSuspendedToRunnable();
+      }
     }
     {
       std::ostream* saved_os = os_;
@@ -1402,6 +1427,8 @@
   const std::string host_prefix_;
   gc::space::ImageSpace& image_space_;
   const ImageHeader& image_header_;
+  bool dump_raw_mapping_table_;
+  bool dump_raw_gc_map_;
 
   DISALLOW_COPY_AND_ASSIGN(ImageDumper);
 };
@@ -1425,6 +1452,8 @@
   UniquePtr<std::string> host_prefix;
   std::ostream* os = &std::cout;
   UniquePtr<std::ofstream> out;
+  bool dump_raw_mapping_table = false;
+  bool dump_raw_gc_map = false;
 
   for (int i = 0; i < argc; i++) {
     const StringPiece option(argv[i]);
@@ -1436,6 +1465,15 @@
       boot_image_filename = option.substr(strlen("--boot-image=")).data();
     } else if (option.starts_with("--host-prefix=")) {
       host_prefix.reset(new std::string(option.substr(strlen("--host-prefix=")).data()));
+    } else if (option.starts_with("--dump:")) {
+        if (option == "--dump:raw_mapping_table") {
+          dump_raw_mapping_table = true;
+        } else if (option == "--dump:raw_gc_map") {
+          dump_raw_gc_map = true;
+        } else {
+          fprintf(stderr, "Unknown argument %s\n", option.data());
+          usage();
+        }
     } else if (option.starts_with("--output=")) {
       const char* filename = option.substr(strlen("--output=")).data();
       out.reset(new std::ofstream(filename));
@@ -1477,7 +1515,7 @@
       fprintf(stderr, "Failed to open oat file from '%s': %s\n", oat_filename, error_msg.c_str());
       return EXIT_FAILURE;
     }
-    OatDumper oat_dumper(*host_prefix.get(), *oat_file);
+    OatDumper oat_dumper(*host_prefix.get(), *oat_file, dump_raw_mapping_table, dump_raw_gc_map);
     oat_dumper.Dump(*os);
     return EXIT_SUCCESS;
   }
@@ -1520,7 +1558,6 @@
   // give it away now and then switch to a more managable ScopedObjectAccess.
   Thread::Current()->TransitionFromRunnableToSuspended(kNative);
   ScopedObjectAccess soa(Thread::Current());
-
   gc::Heap* heap = Runtime::Current()->GetHeap();
   gc::space::ImageSpace* image_space = heap->GetImageSpace();
   CHECK(image_space != NULL);
@@ -1529,7 +1566,8 @@
     fprintf(stderr, "Invalid image header %s\n", image_filename);
     return EXIT_FAILURE;
   }
-  ImageDumper image_dumper(os, image_filename, *host_prefix.get(), *image_space, image_header);
+  ImageDumper image_dumper(os, image_filename, *host_prefix.get(), *image_space, image_header,
+    dump_raw_mapping_table, dump_raw_gc_map);
   image_dumper.Dump();
   return EXIT_SUCCESS;
 }
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index cf3f72e..ed8bc13 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -351,31 +351,31 @@
      */
     .extern artLockObjectFromCode
 ENTRY art_quick_lock_object
-    cbz    r0, slow_lock
-retry_lock:
+    cbz    r0, .Lslow_lock
+.Lretry_lock:
     ldr    r2, [r9, #THREAD_ID_OFFSET]
     ldrex  r1, [r0, #LOCK_WORD_OFFSET]
-    cbnz   r1, not_unlocked           @ already thin locked
+    cbnz   r1, .Lnot_unlocked         @ already thin locked
     @ unlocked case - r2 holds thread id with count of 0
     strex  r3, r2, [r0, #LOCK_WORD_OFFSET]
-    cbnz   r3, strex_fail             @ store failed, retry
+    cbnz   r3, .Lstrex_fail           @ store failed, retry
     dmb    ish                        @ full (LoadLoad) memory barrier
     bx lr
-strex_fail:
-    b retry_lock                      @ unlikely forward branch, need to reload and recheck r1/r2
-not_unlocked:
+.Lstrex_fail:
+    b .Lretry_lock                    @ unlikely forward branch, need to reload and recheck r1/r2
+.Lnot_unlocked:
     lsr    r3, r1, 30
-    cbnz   r3, slow_lock              @ if either of the top two bits are set, go slow path
+    cbnz   r3, .Lslow_lock            @ if either of the top two bits are set, go slow path
     eor    r2, r1, r2                 @ lock_word.ThreadId() ^ self->ThreadId()
     uxth   r2, r2                     @ zero top 16 bits
-    cbnz   r2, slow_lock              @ lock word and self thread id's match -> recursive lock
+    cbnz   r2, .Lslow_lock            @ lock word and self thread id's match -> recursive lock
                                       @ else contention, go to slow path
     add    r2, r1, #65536             @ increment count in lock word placing in r2 for storing
     lsr    r1, r2, 30                 @ if either of the top two bits are set, we overflowed.
-    cbnz   r1, slow_lock              @ if we overflow the count go slow path
+    cbnz   r1, .Lslow_lock            @ if we overflow the count go slow path
     str    r2, [r0, #LOCK_WORD_OFFSET] @ no need for strex as we hold the lock
     bx lr
-slow_lock:
+.Lslow_lock:
     SETUP_REF_ONLY_CALLEE_SAVE_FRAME  @ save callee saves in case we block
     mov    r1, r9                     @ pass Thread::Current
     mov    r2, sp                     @ pass SP
@@ -391,25 +391,25 @@
      */
     .extern artUnlockObjectFromCode
 ENTRY art_quick_unlock_object
-    cbz    r0, slow_unlock
+    cbz    r0, .Lslow_unlock
     ldr    r1, [r0, #LOCK_WORD_OFFSET]
     lsr    r2, r1, 30
-    cbnz   r2, slow_unlock            @ if either of the top two bits are set, go slow path
+    cbnz   r2, .Lslow_unlock          @ if either of the top two bits are set, go slow path
     ldr    r2, [r9, #THREAD_ID_OFFSET]
     eor    r3, r1, r2                 @ lock_word.ThreadId() ^ self->ThreadId()
     uxth   r3, r3                     @ zero top 16 bits
-    cbnz   r3, slow_unlock            @ do lock word and self thread id's match?
+    cbnz   r3, .Lslow_unlock          @ do lock word and self thread id's match?
     cmp    r1, #65536
-    bpl    recursive_thin_unlock
+    bpl    .Lrecursive_thin_unlock
     @ transition to unlocked, r3 holds 0
     str    r3, [r0, #LOCK_WORD_OFFSET]
     dmb    ish                        @ full (StoreLoad) memory barrier
     bx     lr
-recursive_thin_unlock:
+.Lrecursive_thin_unlock:
     sub    r1, r1, #65536
     str    r1, [r0, #LOCK_WORD_OFFSET]
     bx     lr
-slow_unlock:
+.Lslow_unlock:
     SETUP_REF_ONLY_CALLEE_SAVE_FRAME  @ save callee saves in case exception allocation triggers GC
     mov    r1, r9                     @ pass Thread::Current
     mov    r2, sp                     @ pass SP
@@ -435,11 +435,11 @@
     .pad #4
     .cfi_adjust_cfa_offset 4
     bl artIsAssignableFromCode
-    cbz    r0, throw_class_cast_exception
+    cbz    r0, .Lthrow_class_cast_exception
     add sp, #4
     .cfi_adjust_cfa_offset -4
     pop {r0-r1, pc}
-throw_class_cast_exception:
+.Lthrow_class_cast_exception:
     add sp, #4
     .cfi_adjust_cfa_offset -4
     pop {r0-r1, lr}
@@ -473,29 +473,29 @@
 END art_quick_aput_obj_with_bound_check
 
 ENTRY art_quick_aput_obj
-    cbz r2, do_aput_null
+    cbz r2, .Ldo_aput_null
     ldr r3, [r0, #CLASS_OFFSET]
     ldr ip, [r2, #CLASS_OFFSET]
     ldr r3, [r3, #CLASS_COMPONENT_TYPE_OFFSET]
     cmp r3, ip  @ value's type == array's component type - trivial assignability
-    bne check_assignability
-do_aput:
+    bne .Lcheck_assignability
+.Ldo_aput:
     add r3, r0, #OBJECT_ARRAY_DATA_OFFSET
     str r2, [r3, r1, lsl #2]
     ldr r3, [r9, #THREAD_CARD_TABLE_OFFSET]
     lsr r0, r0, #7
     strb r3, [r3, r0]
     blx lr
-do_aput_null:
+.Ldo_aput_null:
     add r3, r0, #OBJECT_ARRAY_DATA_OFFSET
     str r2, [r3, r1, lsl #2]
     blx lr
-check_assignability:
-    push {r0-r2, lr}                 @ save arguments
+.Lcheck_assignability:
+    push {r0-r2, lr}             @ save arguments
     mov r1, ip
     mov r0, r3
     bl artIsAssignableFromCode
-    cbz r0, throw_array_store_exception
+    cbz r0, .Lthrow_array_store_exception
     pop {r0-r2, lr}
     add r3, r0, #OBJECT_ARRAY_DATA_OFFSET
     str r2, [r3, r1, lsl #2]
@@ -503,7 +503,7 @@
     lsr r0, r0, #7
     strb r3, [r3, r0]
     blx lr
-throw_array_store_exception:
+.Lthrow_array_store_exception:
     pop {r0-r2, lr}
     SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
     mov r1, r2
@@ -1181,55 +1181,55 @@
      */
 
     subs  r2, #4
-    blt   indexof_remainder
+    blt   .Lindexof_remainder
 
-indexof_loop4:
+.Lindexof_loop4:
     ldrh  r3, [r0, #2]!
     ldrh  r4, [r0, #2]!
     ldrh  r10, [r0, #2]!
     ldrh  r11, [r0, #2]!
     cmp   r3, r1
-    beq   match_0
+    beq   .Lmatch_0
     cmp   r4, r1
-    beq   match_1
+    beq   .Lmatch_1
     cmp   r10, r1
-    beq   match_2
+    beq   .Lmatch_2
     cmp   r11, r1
-    beq   match_3
+    beq   .Lmatch_3
     subs  r2, #4
-    bge   indexof_loop4
+    bge   .Lindexof_loop4
 
-indexof_remainder:
-    adds    r2, #4
-    beq     indexof_nomatch
+.Lindexof_remainder:
+    adds  r2, #4
+    beq   .Lindexof_nomatch
 
-indexof_loop1:
+.Lindexof_loop1:
     ldrh  r3, [r0, #2]!
     cmp   r3, r1
-    beq   match_3
+    beq   .Lmatch_3
     subs  r2, #1
-    bne   indexof_loop1
+    bne   .Lindexof_loop1
 
-indexof_nomatch:
+.Lindexof_nomatch:
     mov   r0, #-1
     pop {r4, r10-r11, pc}
 
-match_0:
+.Lmatch_0:
     sub   r0, #6
     sub   r0, r12
     asr   r0, r0, #1
     pop {r4, r10-r11, pc}
-match_1:
+.Lmatch_1:
     sub   r0, #4
     sub   r0, r12
     asr   r0, r0, #1
     pop {r4, r10-r11, pc}
-match_2:
+.Lmatch_2:
     sub   r0, #2
     sub   r0, r12
     asr   r0, r0, #1
     pop {r4, r10-r11, pc}
-match_3:
+.Lmatch_3:
     sub   r0, r12
     asr   r0, r0, #1
     pop {r4, r10-r11, pc}
@@ -1308,7 +1308,7 @@
       */
 
     subs  r10, #2
-    blt   do_remainder2
+    blt   .Ldo_remainder2
 
       /*
        * Unroll the first two checks so we can quickly catch early mismatch
@@ -1322,13 +1322,13 @@
     subs  r0, r3, r4
     it    eq
     subseq  r0, r7, r8
-    bne   done
+    bne   .Ldone
     cmp   r10, #28
-    bgt   do_memcmp16
+    bgt   .Ldo_memcmp16
     subs  r10, #3
-    blt   do_remainder
+    blt   .Ldo_remainder
 
-loopback_triple:
+.Lloopback_triple:
     ldrh  r3, [r2, #2]!
     ldrh  r4, [r1, #2]!
     ldrh  r7, [r2, #2]!
@@ -1340,34 +1340,34 @@
     subseq  r0, r7, r8
     it    eq
     subseq  r0, r9, r12
-    bne   done
+    bne   .Ldone
     subs  r10, #3
-    bge   loopback_triple
+    bge   .Lloopback_triple
 
-do_remainder:
+.Ldo_remainder:
     adds  r10, #3
-    beq   returnDiff
+    beq   .Lreturn_diff
 
-loopback_single:
+.Lloopback_single:
     ldrh  r3, [r2, #2]!
     ldrh  r4, [r1, #2]!
     subs  r0, r3, r4
-    bne   done
+    bne   .Ldone
     subs  r10, #1
-    bne     loopback_single
+    bne   .Lloopback_single
 
-returnDiff:
+.Lreturn_diff:
     mov   r0, r11
     pop   {r4, r7-r12, pc}
 
-do_remainder2:
+.Ldo_remainder2:
     adds  r10, #2
-    bne   loopback_single
+    bne   .Lloopback_single
     mov   r0, r11
     pop   {r4, r7-r12, pc}
 
     /* Long string case */
-do_memcmp16:
+.Ldo_memcmp16:
     mov   r7, r11
     add   r0, r2, #2
     add   r1, r1, #2
@@ -1376,6 +1376,6 @@
     cmp   r0, #0
     it    eq
     moveq r0, r7
-done:
+.Ldone:
     pop   {r4, r7-r12, pc}
 END art_quick_string_compareto
diff --git a/runtime/arch/mips/jni_entrypoints_mips.S b/runtime/arch/mips/jni_entrypoints_mips.S
index ad7c021..f9ca7df 100644
--- a/runtime/arch/mips/jni_entrypoints_mips.S
+++ b/runtime/arch/mips/jni_entrypoints_mips.S
@@ -44,13 +44,13 @@
     lw    $a2, 8($sp)
     lw    $a3, 12($sp)
     lw    $ra, 16($sp)
-    beq   $v0, $zero, no_native_code_found
+    beq   $v0, $zero, .Lno_native_code_found
     addiu $sp, $sp, 32          # restore the stack
     .cfi_adjust_cfa_offset -32
     move  $t9, $v0              # put method code result in $t9
     jr    $t9                   # leaf call to method's code
     nop
-no_native_code_found:
+.Lno_native_code_found:
     jr    $ra
     nop
 END art_jni_dlsym_lookup_stub
diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S
index f9a200a..d23be47 100644
--- a/runtime/arch/mips/quick_entrypoints_mips.S
+++ b/runtime/arch/mips/quick_entrypoints_mips.S
@@ -336,7 +336,7 @@
     .extern artThrowNullPointerExceptionFromCode
 ENTRY art_quick_throw_null_pointer_exception
     GENERATE_GLOBAL_POINTER
-art_quick_throw_null_pointer_exception_gp_set:
+.Lart_quick_throw_null_pointer_exception_gp_set:
     SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
     move $a0, rSELF                 # pass Thread::Current
     la   $t9, artThrowNullPointerExceptionFromCode
@@ -363,7 +363,7 @@
     .extern artThrowArrayBoundsFromCode
 ENTRY art_quick_throw_array_bounds
     GENERATE_GLOBAL_POINTER
-art_quick_throw_array_bounds_gp_set:
+.Lart_quick_throw_array_bounds_gp_set:
     SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
     move $a2, rSELF                 # pass Thread::Current
     la   $t9, artThrowArrayBoundsFromCode
@@ -537,7 +537,7 @@
     .extern artLockObjectFromCode
 ENTRY art_quick_lock_object
     GENERATE_GLOBAL_POINTER
-    beqz    $a0, art_quick_throw_null_pointer_exception_gp_set
+    beqz    $a0, .Lart_quick_throw_null_pointer_exception_gp_set
     nop
     SETUP_REF_ONLY_CALLEE_SAVE_FRAME      # save callee saves in case we block
     move    $a1, rSELF                    # pass Thread::Current
@@ -552,7 +552,7 @@
     .extern artUnlockObjectFromCode
 ENTRY art_quick_unlock_object
     GENERATE_GLOBAL_POINTER
-    beqz    $a0, art_quick_throw_null_pointer_exception_gp_set
+    beqz    $a0, .Lart_quick_throw_null_pointer_exception_gp_set
     nop
     SETUP_REF_ONLY_CALLEE_SAVE_FRAME  # save callee saves in case exception allocation triggers GC
     move    $a1, rSELF                # pass Thread::Current
@@ -576,12 +576,12 @@
     sw     $a0, 0($sp)
     jal    artIsAssignableFromCode
     nop
-    beqz   $v0, throw_class_cast_exception
+    beqz   $v0, .Lthrow_class_cast_exception
     lw     $ra, 12($sp)
     jr     $ra
     addiu  $sp, $sp, 16
     .cfi_adjust_cfa_offset -16
-throw_class_cast_exception:
+.Lthrow_class_cast_exception:
     lw     $t9, 8($sp)
     lw     $a1, 4($sp)
     lw     $a0, 0($sp)
@@ -601,35 +601,35 @@
      */
 ENTRY art_quick_aput_obj_with_null_and_bound_check
     GENERATE_GLOBAL_POINTER
-    bnez    $a0, art_quick_aput_obj_with_bound_check_gp_set
+    bnez    $a0, .Lart_quick_aput_obj_with_bound_check_gp_set
     nop
-    b art_quick_throw_null_pointer_exception_gp_set
+    b .Lart_quick_throw_null_pointer_exception_gp_set
     nop
 END art_quick_aput_obj_with_null_and_bound_check
 
 ENTRY art_quick_aput_obj_with_bound_check
     GENERATE_GLOBAL_POINTER
-art_quick_aput_obj_with_bound_check_gp_set:
+.Lart_quick_aput_obj_with_bound_check_gp_set:
     lw $t0, ARRAY_LENGTH_OFFSET($a0)
     sltu $t1, $a1, $t0
-    bnez $t1, art_quick_aput_obj_gp_set
+    bnez $t1, .Lart_quick_aput_obj_gp_set
     nop
     move $a0, $a1
-    b art_quick_throw_array_bounds_gp_set
+    b .Lart_quick_throw_array_bounds_gp_set
     move $a1, $t0
 END art_quick_aput_obj_with_bound_check
 
 ENTRY art_quick_aput_obj
     GENERATE_GLOBAL_POINTER
-art_quick_aput_obj_gp_set:
-    beqz $a2, do_aput_null
+.Lart_quick_aput_obj_gp_set:
+    beqz $a2, .Ldo_aput_null
     nop
     lw $t0, CLASS_OFFSET($a0)
     lw $t1, CLASS_OFFSET($a2)
     lw $t0, CLASS_COMPONENT_TYPE_OFFSET($t0)
-    bne $t1, $t0, check_assignability  # value's type == array's component type - trivial assignability
+    bne $t1, $t0, .Lcheck_assignability  # value's type == array's component type - trivial assignability
     nop
-do_aput:
+.Ldo_aput:
     sll $a1, $a1, 2
     add $t0, $a0, $a1
     sw  $a2, OBJECT_ARRAY_DATA_OFFSET($t0)
@@ -639,13 +639,13 @@
     sb  $t0, ($t1)
     jr  $ra
     nop
-do_aput_null:
+.Ldo_aput_null:
     sll $a1, $a1, 2
     add $t0, $a0, $a1
     sw  $a2, OBJECT_ARRAY_DATA_OFFSET($t0)
     jr  $ra
     nop
-check_assignability:
+.Lcheck_assignability:
     addiu  $sp, $sp, -32
     .cfi_adjust_cfa_offset 32
     sw     $ra, 28($sp)
@@ -665,7 +665,7 @@
     lw     $a0, 0($sp)
     add    $sp, 32
     .cfi_adjust_cfa_offset -32
-    bnez   $v0, do_aput
+    bnez   $v0, .Ldo_aput
     nop
     SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
     move $a1, $a2
diff --git a/runtime/arch/x86/entrypoints_init_x86.cc b/runtime/arch/x86/entrypoints_init_x86.cc
index 589c7d9..888310a 100644
--- a/runtime/arch/x86/entrypoints_init_x86.cc
+++ b/runtime/arch/x86/entrypoints_init_x86.cc
@@ -84,7 +84,6 @@
 
 // Intrinsic entrypoints.
 extern "C" int32_t art_quick_memcmp16(void*, void*, int32_t);
-extern "C" int32_t art_quick_indexof(void*, uint32_t, uint32_t, uint32_t);
 extern "C" int32_t art_quick_string_compareto(void*, void*);
 extern "C" void* art_quick_memcpy(void*, const void*, size_t);
 
@@ -193,7 +192,7 @@
   qpoints->pUshrLong = art_quick_lushr;
 
   // Intrinsics
-  qpoints->pIndexOf = art_quick_indexof;
+  // qpoints->pIndexOf = nullptr;  // Not needed on x86
   qpoints->pMemcmp16 = art_quick_memcmp16;
   qpoints->pStringCompareTo = art_quick_string_compareto;
   qpoints->pMemcpy = art_quick_memcpy;
diff --git a/runtime/arch/x86/jni_entrypoints_x86.S b/runtime/arch/x86/jni_entrypoints_x86.S
index 2eb5ada..ebd82b5 100644
--- a/runtime/arch/x86/jni_entrypoints_x86.S
+++ b/runtime/arch/x86/jni_entrypoints_x86.S
@@ -30,8 +30,8 @@
     addl LITERAL(8), %esp         // restore the stack
     CFI_ADJUST_CFA_OFFSET(-12)
     cmpl LITERAL(0), %eax         // check if returned method code is null
-    je no_native_code_found       // if null, jump to return to handle
+    je .Lno_native_code_found     // if null, jump to return to handle
     jmp *%eax                     // otherwise, tail call to intended method
-no_native_code_found:
+.Lno_native_code_found:
     ret
 END_FUNCTION art_jni_dlsym_lookup_stub
diff --git a/runtime/arch/x86/portable_entrypoints_x86.S b/runtime/arch/x86/portable_entrypoints_x86.S
index 4bd6173..5f270f8 100644
--- a/runtime/arch/x86/portable_entrypoints_x86.S
+++ b/runtime/arch/x86/portable_entrypoints_x86.S
@@ -52,16 +52,16 @@
     POP ebp                       // pop ebp
     mov 20(%esp), %ecx            // get result pointer
     cmpl LITERAL(68), 24(%esp)    // test if result type char == 'D'
-    je return_double_portable
+    je .Lreturn_double_portable
     cmpl LITERAL(70), 24(%esp)    // test if result type char == 'F'
-    je return_float_portable
+    je .Lreturn_float_portable
     mov %eax, (%ecx)              // store the result
     mov %edx, 4(%ecx)             // store the other half of the result
     ret
-return_double_portable:
+.Lreturn_double_portable:
     fstpl (%ecx)                  // store the floating point result as double
     ret
-return_float_portable:
+.Lreturn_float_portable:
     fstps (%ecx)                  // store the floating point result as float
     ret
 END_FUNCTION art_portable_invoke_stub
@@ -109,9 +109,9 @@
   CFI_RESTORE(%ebp)
   CFI_DEF_CFA(%esp, 4)
   testl %eax, %eax
-  jz  resolve_fail
+  jz  .Lresolve_fail
   jmp * %eax
-resolve_fail:                     // Resolution failed, return with exception pending.
+.Lresolve_fail:                   // Resolution failed, return with exception pending.
   ret
 END_FUNCTION art_portable_resolution_trampoline
 
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index c76c6b2..b1f2275 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -285,14 +285,14 @@
     mov %edx, 4(%ecx)             // store the other half of the result
     mov 24(%esp), %edx            // get the shorty
     cmpb LITERAL(68), (%edx)      // test if result type char == 'D'
-    je return_double_quick
+    je .Lreturn_double_quick
     cmpb LITERAL(70), (%edx)      // test if result type char == 'F'
-    je return_float_quick
+    je .Lreturn_float_quick
     ret
-return_double_quick:
+.Lreturn_double_quick:
     movsd %xmm0, (%ecx)           // store the floating point result
     ret
-return_float_quick:
+.Lreturn_float_quick:
     movss %xmm0, (%ecx)           // store the floating point result
     ret
 END_FUNCTION art_quick_invoke_stub
@@ -514,32 +514,32 @@
 
 DEFINE_FUNCTION art_quick_lock_object
     testl %eax, %eax                      // null check object/eax
-    jz   slow_lock
-retry_lock:
+    jz   .Lslow_lock
+.Lretry_lock:
     movl LOCK_WORD_OFFSET(%eax), %ecx     // ecx := lock word
     test LITERAL(0xC0000000), %ecx        // test the 2 high bits.
-    jne  slow_lock                        // slow path if either of the two high bits are set.
+    jne  .Lslow_lock                      // slow path if either of the two high bits are set.
     movl %fs:THREAD_ID_OFFSET, %edx       // edx := thread id
     test %ecx, %ecx
-    jnz  already_thin                     // lock word contains a thin lock
+    jnz  .Lalready_thin                   // lock word contains a thin lock
     // unlocked case - %edx holds thread id with count of 0
     movl %eax, %ecx                       // remember object in case of retry
     xor  %eax, %eax                       // eax == 0 for comparison with lock word in cmpxchg
     lock cmpxchg  %edx, LOCK_WORD_OFFSET(%ecx)
-    jnz  cmpxchg_fail                     // cmpxchg failed retry
+    jnz  .Lcmpxchg_fail                   // cmpxchg failed retry
     ret
-cmpxchg_fail:
+.Lcmpxchg_fail:
     movl  %ecx, %eax                       // restore eax
-    jmp  retry_lock
-already_thin:
+    jmp  .Lretry_lock
+.Lalready_thin:
     cmpw %ax, %dx                         // do we hold the lock already?
-    jne  slow_lock
+    jne  .Lslow_lock
     addl LITERAL(65536), %eax             // increment recursion count
     test LITERAL(0xC0000000), %eax        // overflowed if either of top two bits are set
-    jne  slow_lock                        // count overflowed so go slow
+    jne  .Lslow_lock                      // count overflowed so go slow
     movl %eax, LOCK_WORD_OFFSET(%ecx)     // update lockword, cmpxchg not necessary as we hold lock
     ret
-slow_lock:
+.Lslow_lock:
     SETUP_REF_ONLY_CALLEE_SAVE_FRAME  // save ref containing registers for GC
     mov %esp, %edx                // remember SP
     SETUP_GOT_NOSAVE              // clobbers EBX
@@ -558,22 +558,22 @@
 
 DEFINE_FUNCTION art_quick_unlock_object
     testl %eax, %eax                      // null check object/eax
-    jz   slow_unlock
+    jz   .Lslow_unlock
     movl LOCK_WORD_OFFSET(%eax), %ecx     // ecx := lock word
     movl %fs:THREAD_ID_OFFSET, %edx       // edx := thread id
     test %ecx, %ecx
-    jb   slow_unlock                      // lock word contains a monitor
+    jb   .Lslow_unlock                    // lock word contains a monitor
     cmpw %cx, %dx                         // does the thread id match?
-    jne  slow_unlock
+    jne  .Lslow_unlock
     cmpl LITERAL(65536), %ecx
-    jae  recursive_thin_unlock
+    jae  .Lrecursive_thin_unlock
     movl LITERAL(0), LOCK_WORD_OFFSET(%eax)
     ret
-recursive_thin_unlock:
+.Lrecursive_thin_unlock:
     subl LITERAL(65536), %ecx
     mov  %ecx, LOCK_WORD_OFFSET(%eax)
     ret
-slow_unlock:
+.Lslow_unlock:
     SETUP_REF_ONLY_CALLEE_SAVE_FRAME  // save ref containing registers for GC
     mov %esp, %edx                // remember SP
     SETUP_GOT_NOSAVE              // clobbers EBX
@@ -651,21 +651,21 @@
 
 DEFINE_FUNCTION art_quick_aput_obj
     test %edx, %edx              // store of null
-    jz do_aput_null
+    jz .Ldo_aput_null
     movl CLASS_OFFSET(%eax), %ebx
     movl CLASS_COMPONENT_TYPE_OFFSET(%ebx), %ebx
     cmpl CLASS_OFFSET(%edx), %ebx // value's type == array's component type - trivial assignability
-    jne check_assignability
-do_aput:
+    jne .Lcheck_assignability
+.Ldo_aput:
     movl %edx, OBJECT_ARRAY_DATA_OFFSET(%eax, %ecx, 4)
     movl %fs:THREAD_CARD_TABLE_OFFSET, %edx
     shrl LITERAL(7), %eax
     movb %dl, (%edx, %eax)
     ret
-do_aput_null:
+.Ldo_aput_null:
     movl %edx, OBJECT_ARRAY_DATA_OFFSET(%eax, %ecx, 4)
     ret
-check_assignability:
+.Lcheck_assignability:
     PUSH eax                     // save arguments
     PUSH ecx
     PUSH edx
@@ -679,7 +679,7 @@
     addl LITERAL(16), %esp       // pop arguments
     CFI_ADJUST_CFA_OFFSET(-16)
     testl %eax, %eax
-    jz   throw_array_store_exception
+    jz   .Lthrow_array_store_exception
     POP  edx
     POP  ecx
     POP  eax
@@ -688,7 +688,7 @@
     shrl LITERAL(7), %eax
     movb %dl, (%edx, %eax)
     ret
-throw_array_store_exception:
+.Lthrow_array_store_exception:
     POP  edx
     POP  ecx
     POP  eax
@@ -792,14 +792,14 @@
 
 DEFINE_FUNCTION art_quick_idivmod
     cmpl LITERAL(0x80000000), %eax
-    je check_arg2  // special case
-args_ok:
+    je .Lcheck_arg2  // special case
+.Largs_ok:
     cdq         // edx:eax = sign extend eax
     idiv %ecx   // (edx,eax) = (edx:eax % ecx, edx:eax / ecx)
     ret
-check_arg2:
+.Lcheck_arg2:
     cmpl LITERAL(-1), %ecx
-    jne args_ok
+    jne .Largs_ok
     xorl %edx, %edx
     ret         // eax already holds min int
 END_FUNCTION art_quick_idivmod
@@ -1277,56 +1277,6 @@
 END_FUNCTION art_quick_deoptimize
 
     /*
-     * String's indexOf.
-     *
-     * On entry:
-     *    eax:   string object (known non-null)
-     *    ecx:   char to match (known <= 0xFFFF)
-     *    edx:   Starting offset in string data
-     */
-DEFINE_FUNCTION art_quick_indexof
-    PUSH edi                      // push callee save reg
-    mov STRING_COUNT_OFFSET(%eax), %ebx
-    mov STRING_VALUE_OFFSET(%eax), %edi
-    mov STRING_OFFSET_OFFSET(%eax), %eax
-    testl %edx, %edx              // check if start < 0
-    jl   clamp_min
-clamp_done:
-    cmpl %ebx, %edx               // check if start >= count
-    jge  not_found
-    lea  STRING_DATA_OFFSET(%edi, %eax, 2), %edi  // build a pointer to the start of string data
-    mov  %edi, %eax               // save a copy in eax to later compute result
-    lea  (%edi, %edx, 2), %edi    // build pointer to start of data to compare
-    subl  %edx, %ebx              // compute iteration count
-    /*
-     * At this point we have:
-     *   eax: original start of string data
-     *   ecx: char to compare
-     *   ebx: length to compare
-     *   edi: start of data to test
-     */
-    mov  %eax, %edx
-    mov  %ecx, %eax               // put char to match in %eax
-    mov  %ebx, %ecx               // put length to compare in %ecx
-    repne scasw                   // find %ax, starting at [%edi], up to length %ecx
-    jne  not_found
-    subl %edx, %edi
-    sar  LITERAL(1), %edi
-    decl %edi                     // index = ((curr_ptr - orig_ptr) / 2) - 1
-    mov  %edi, %eax
-    POP edi                       // pop callee save reg
-    ret
-    .balign 16
-not_found:
-    mov  LITERAL(-1), %eax        // return -1 (not found)
-    POP edi                       // pop callee save reg
-    ret
-clamp_min:
-    xor  %edx, %edx               // clamp start to 0
-    jmp  clamp_done
-END_FUNCTION art_quick_indexof
-
-    /*
      * String's compareTo.
      *
      * On entry:
@@ -1358,12 +1308,12 @@
      *   edi: pointer to comp string data
      */
     repe cmpsw                    // find nonmatching chars in [%esi] and [%edi], up to length %ecx
-    jne not_equal
+    jne .Lnot_equal
     POP edi                       // pop callee save reg
     POP esi                       // pop callee save reg
     ret
     .balign 16
-not_equal:
+.Lnot_equal:
     movzwl  -2(%esi), %eax        // get last compared char from this string
     movzwl  -2(%edi), %ecx        // get last compared char from comp string
     subl  %ecx, %eax              // return the difference
diff --git a/runtime/arch/x86_64/asm_support_x86_64.h b/runtime/arch/x86_64/asm_support_x86_64.h
index d425ed8..444fa22 100644
--- a/runtime/arch/x86_64/asm_support_x86_64.h
+++ b/runtime/arch/x86_64/asm_support_x86_64.h
@@ -19,6 +19,13 @@
 
 #include "asm_support.h"
 
+// Offset of field Runtime::callee_save_methods_[kSaveAll]
+#define RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET 208
+// Offset of field Runtime::callee_save_methods_[kRefsOnly]
+#define RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET 216
+// Offset of field Runtime::callee_save_methods_[kRefsAndArgs]
+#define RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET 224
+
 // Offset of field Thread::self_ verified in InitCpu
 #define THREAD_SELF_OFFSET 72
 // Offset of field Thread::card_table_ verified in InitCpu
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index 44bc7a2..ac238f0 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -46,12 +46,63 @@
      * Runtime::CreateCalleeSaveMethod(kRefsAndArgs)
      */
 MACRO0(SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME)
-    int3
-    int3
+    // R10 := Runtime::Current()
+    movq _ZN3art7Runtime9instance_E@GOTPCREL(%rip), %r10
+    movq (%r10), %r10
+    // Save callee and GPR args, mixed together to agree with core spills bitmap.
+    PUSH r15  // Callee save.
+    PUSH r14  // Callee save.
+    PUSH r13  // Callee save.
+    PUSH r12  // Callee save.
+    PUSH r9   // Arg.
+    PUSH r8   // Arg.
+    PUSH rsi  // Arg.
+    PUSH rbp  // Callee save.
+    PUSH rbx  // Callee save.
+    PUSH rdx  // Arg.
+    PUSH rcx  // Arg.
+    // Create space for FPR args and create 2 slots, 1 of padding and 1 for the ArtMethod*.
+    subq LITERAL(80), %rsp
+    CFI_ADJUST_CFA_OFFSET(80)
+    // R10 := ArtMethod* for ref and args callee save frame method.
+    movq RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET(%r10), %r10
+    // Save FPRs.
+    movq %xmm0, 16(%rsp)
+    movq %xmm1, 24(%rsp)
+    movq %xmm2, 32(%rsp)
+    movq %xmm3, 40(%rsp)
+    movq %xmm4, 48(%rsp)
+    movq %xmm5, 56(%rsp)
+    movq %xmm6, 64(%rsp)
+    movq %xmm7, 72(%rsp)
+    // Store ArtMethod* to bottom of stack.
+    movq %r10, 0(%rsp)
 END_MACRO
 
 MACRO0(RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME)
-    int3
+    // Restore FPRs.
+    movq 16(%rsp), %xmm0
+    movq 24(%rsp), %xmm1
+    movq 32(%rsp), %xmm2
+    movq 40(%rsp), %xmm3
+    movq 48(%rsp), %xmm4
+    movq 56(%rsp), %xmm5
+    movq 64(%rsp), %xmm6
+    movq 72(%rsp), %xmm7
+    addq LITERAL(80), %rsp
+    CFI_ADJUST_CFA_OFFSET(-80)
+    // Save callee and GPR args, mixed together to agree with core spills bitmap.
+    POP rcx
+    POP rdx
+    POP rbx
+    POP rbp
+    POP rsi
+    POP r8
+    POP r9
+    POP r12
+    POP r13
+    POP r14
+    POP r15
 END_MACRO
 
     /*
@@ -147,20 +198,210 @@
 INVOKE_TRAMPOLINE art_quick_invoke_super_trampoline_with_access_check, artInvokeSuperTrampolineWithAccessCheck
 INVOKE_TRAMPOLINE art_quick_invoke_virtual_trampoline_with_access_check, artInvokeVirtualTrampolineWithAccessCheck
 
+
+    /*
+     * Helper for quick invocation stub to set up XMM registers. Assumes r10 == shorty,
+     * r11 == arg_array. Clobbers r10, r11 and al. Branches to xmm_setup_finished if it encounters
+     * the end of the shorty.
+     */
+MACRO2(LOOP_OVER_SHORTY_LOADING_XMMS, xmm_reg, finished)
+1: // LOOP
+    movb (%r10), %al              // al := *shorty
+    addq LITERAL(1), %r10         // shorty++
+    cmpb LITERAL(0), %al          // if (al == '\0') goto xmm_setup_finished
+    je VAR(finished, 1)
+    cmpb LITERAL(68), %al         // if (al == 'D') goto FOUND_DOUBLE
+    je 2f
+    cmpb LITERAL(70), %al         // if (al == 'F') goto FOUND_FLOAT
+    je 3f
+    addq LITERAL(4), %r11         // arg_array++
+    //  Handle extra space in arg array taken by a long.
+    cmpb LITERAL(74), %al         // if (al != 'J') goto LOOP
+    jne 1b
+    addq LITERAL(4), %r11         // arg_array++
+    jmp 1b                        // goto LOOP
+2:  // FOUND_DOUBLE
+    movsd (%r11), REG_VAR(xmm_reg, 0)
+    addq LITERAL(8), %r11         // arg_array+=2
+    jmp 4f
+3:  // FOUND_FLOAT
+    movss (%r11), REG_VAR(xmm_reg, 0)
+    addq LITERAL(4), %r11         // arg_array++
+4:
+END_MACRO
+
+    /*
+     * Helper for quick invocation stub to set up GPR registers. Assumes r10 == shorty,
+     * r11 == arg_array. Clobbers r10, r11 and al. Branches to gpr_setup_finished if it encounters
+     * the end of the shorty.
+     */
+MACRO3(LOOP_OVER_SHORTY_LOADING_GPRS, gpr_reg64, gpr_reg32, finished)
+1: // LOOP
+    movb (%r10), %al              // al := *shorty
+    addq LITERAL(1), %r10         // shorty++
+    cmpb LITERAL(0), %al          // if (al == '\0') goto gpr_setup_finished
+    je  VAR(finished, 2)
+    cmpb LITERAL(74), %al         // if (al == 'J') goto FOUND_LONG
+    je 2f
+    cmpb LITERAL(70), %al         // if (al == 'F') goto SKIP_FLOAT
+    je 3f
+    cmpb LITERAL(68), %al         // if (al == 'D') goto SKIP_DOUBLE
+    je 4f
+    movl (%r11), REG_VAR(gpr_reg32, 1)
+    addq LITERAL(4), %r11         // arg_array++
+    jmp 5f
+2:  // FOUND_LONG
+    movq (%r11), REG_VAR(gpr_reg64, 0)
+    addq LITERAL(8), %r11         // arg_array+=2
+    jmp 5f
+3:  // SKIP_FLOAT
+    addq LITERAL(4), %r11         // arg_array++
+    jmp 1b
+4:  // SKIP_DOUBLE
+    addq LITERAL(8), %r11         // arg_array+=2
+    jmp 1b
+5:
+END_MACRO
+
     /*
      * Quick invocation stub.
      * On entry:
      *   [sp] = return address
      *   rdi = method pointer
-     *   rsi = argument array or NULL for no argument methods
+     *   rsi = argument array that must at least contain the this pointer.
      *   rdx = size of argument array in bytes
      *   rcx = (managed) thread pointer
      *   r8 = JValue* result
      *   r9 = char* shorty
      */
 DEFINE_FUNCTION art_quick_invoke_stub
-    int3
-    int3
+    // Set up argument XMM registers.
+    leaq 1(%r9), %r10             // R10 := shorty + 1  ; ie skip return arg character.
+    leaq 4(%rsi), %r11            // R11 := arg_array + 4 ; ie skip this pointer.
+    LOOP_OVER_SHORTY_LOADING_XMMS xmm0, .Lxmm_setup_finished
+    LOOP_OVER_SHORTY_LOADING_XMMS xmm1, .Lxmm_setup_finished
+    LOOP_OVER_SHORTY_LOADING_XMMS xmm2, .Lxmm_setup_finished
+    LOOP_OVER_SHORTY_LOADING_XMMS xmm3, .Lxmm_setup_finished
+    LOOP_OVER_SHORTY_LOADING_XMMS xmm4, .Lxmm_setup_finished
+    LOOP_OVER_SHORTY_LOADING_XMMS xmm5, .Lxmm_setup_finished
+    LOOP_OVER_SHORTY_LOADING_XMMS xmm6, .Lxmm_setup_finished
+    LOOP_OVER_SHORTY_LOADING_XMMS xmm7, .Lxmm_setup_finished
+    .balign 16
+.Lxmm_setup_finished:
+    PUSH rbp                      // Save rbp.
+    PUSH r8                       // Save r8/result*.
+    PUSH r9                       // Save r9/shorty*.
+    mov %rsp, %rbp                // Copy value of stack pointer into base pointer.
+    CFI_DEF_CFA_REGISTER(rbp)
+    movl %edx, %r10d
+    addl LITERAL(64), %edx        // Reserve space for return addr, method*, rbp, r8 and r9 in frame.
+    andl LITERAL(0xFFFFFFF0), %edx    // Align frame size to 16 bytes.
+    subl LITERAL(32), %edx        // Remove space for return address, rbp, r8 and r9.
+    subq %rdx, %rsp               // Reserve stack space for argument array.
+    movq LITERAL(0), (%rsp)       // Store NULL for method*
+    movl %r10d, %ecx              // Place size of args in rcx.
+    movq %rdi, %rax               // RAX := method to be called
+    movq %rsi, %r11               // R11 := arg_array
+    leaq 8(%rsp), %rdi            // Rdi is pointing just above the method* in the stack arguments.
+    // Copy arg array into stack.
+    rep movsb                     // while (rcx--) { *rdi++ = *rsi++ }
+    leaq 1(%r9), %r10             // R10 := shorty + 1  ; ie skip return arg character
+    movq %rax, %rdi               // RDI := method to be called
+    movl (%r11), %esi             // RSI := this pointer
+    addq LITERAL(4), %r11         // arg_array++
+    LOOP_OVER_SHORTY_LOADING_GPRS rdx, edx, .Lgpr_setup_finished
+    LOOP_OVER_SHORTY_LOADING_GPRS rcx, ecx, .Lgpr_setup_finished
+    LOOP_OVER_SHORTY_LOADING_GPRS r8, r8d, .Lgpr_setup_finished
+    LOOP_OVER_SHORTY_LOADING_GPRS r9, r9d, .Lgpr_setup_finished
+.Lgpr_setup_finished:
+    call *METHOD_QUICK_CODE_OFFSET(%rdi) // Call the method.
+    movq %rbp, %rsp               // Restore stack pointer.
+    CFI_DEF_CFA_REGISTER(rsp)
+    POP r9                        // Pop r9 - shorty*.
+    POP r8                        // Pop r8 - result*.
+    POP rbp                       // Pop rbp
+    cmpb LITERAL(68), (%r9)       // Test if result type char == 'D'.
+    je .Lreturn_double_quick
+    cmpb LITERAL(70), (%r9)       // Test if result type char == 'F'.
+    je .Lreturn_float_quick
+    movq %rax, (%r8)              // Store the result assuming its a long, int or Object*
+    ret
+.Lreturn_double_quick:
+    movsd %xmm0, (%r8)           // Store the double floating point result.
+    ret
+.Lreturn_float_quick:
+    movss %xmm0, (%r8)           // Store the floating point result.
+    ret
+END_FUNCTION art_quick_invoke_stub
+
+    /*
+     * Quick invocation stub.
+     * On entry:
+     *   [sp] = return address
+     *   rdi = method pointer
+     *   rsi = argument array or NULL if no arguments.
+     *   rdx = size of argument array in bytes
+     *   rcx = (managed) thread pointer
+     *   r8 = JValue* result
+     *   r9 = char* shorty
+     */
+DEFINE_FUNCTION art_quick_invoke_static_stub
+    // Set up argument XMM registers.
+    leaq 1(%r9), %r10             // R10 := shorty + 1  ; ie skip return arg character
+    movq %rsi, %r11               // R11 := arg_array
+    LOOP_OVER_SHORTY_LOADING_XMMS xmm0, .Lxmm_setup_finished2
+    LOOP_OVER_SHORTY_LOADING_XMMS xmm1, .Lxmm_setup_finished2
+    LOOP_OVER_SHORTY_LOADING_XMMS xmm2, .Lxmm_setup_finished2
+    LOOP_OVER_SHORTY_LOADING_XMMS xmm3, .Lxmm_setup_finished2
+    LOOP_OVER_SHORTY_LOADING_XMMS xmm4, .Lxmm_setup_finished2
+    LOOP_OVER_SHORTY_LOADING_XMMS xmm5, .Lxmm_setup_finished2
+    LOOP_OVER_SHORTY_LOADING_XMMS xmm6, .Lxmm_setup_finished2
+    LOOP_OVER_SHORTY_LOADING_XMMS xmm7, .Lxmm_setup_finished2
+    .balign 16
+.Lxmm_setup_finished2:
+    PUSH rbp                      // Save rbp.
+    PUSH r8                       // Save r8/result*.
+    PUSH r9                       // Save r9/shorty*.
+    mov %rsp, %rbp                // Copy value of stack pointer into base pointer.
+    CFI_DEF_CFA_REGISTER(rbp)
+    movl %edx, %r10d
+    addl LITERAL(64), %edx        // Reserve space for return addr, method*, rbp, r8 and r9 in frame.
+    andl LITERAL(0xFFFFFFF0), %edx    // Align frame size to 16 bytes.
+    subl LITERAL(32), %edx        // Remove space for return address, rbp, r8 and r9.
+    subq %rdx, %rsp               // Reserve stack space for argument array.
+    movq LITERAL(0), (%rsp)       // Store NULL for method*
+    movl %r10d, %ecx              // Place size of args in rcx.
+    movq %rdi, %rax               // RAX := method to be called
+    movq %rsi, %r11               // R11 := arg_array
+    leaq 8(%rsp), %rdi            // Rdi is pointing just above the method* in the stack arguments.
+    // Copy arg array into stack.
+    rep movsb                     // while (rcx--) { *rdi++ = *rsi++ }
+    leaq 1(%r9), %r10             // R10 := shorty + 1  ; ie skip return arg character
+    movq %rax, %rdi               // RDI := method to be called
+    LOOP_OVER_SHORTY_LOADING_GPRS rsi, esi, .Lgpr_setup_finished2
+    LOOP_OVER_SHORTY_LOADING_GPRS rdx, edx, .Lgpr_setup_finished2
+    LOOP_OVER_SHORTY_LOADING_GPRS rcx, ecx, .Lgpr_setup_finished2
+    LOOP_OVER_SHORTY_LOADING_GPRS r8, r8d, .Lgpr_setup_finished2
+    LOOP_OVER_SHORTY_LOADING_GPRS r9, r9d, .Lgpr_setup_finished2
+.Lgpr_setup_finished2:
+    call *METHOD_QUICK_CODE_OFFSET(%rdi) // Call the method.
+    movq %rbp, %rsp               // Restore stack pointer.
+    CFI_DEF_CFA_REGISTER(rsp)
+    POP r9                        // Pop r9 - shorty*.
+    POP r8                        // Pop r8 - result*.
+    POP rbp                       // Pop rbp
+    cmpb LITERAL(68), (%r9)       // Test if result type char == 'D'.
+    je .Lreturn_double_quick2
+    cmpb LITERAL(70), (%r9)       // Test if result type char == 'F'.
+    je .Lreturn_float_quick2
+    movq %rax, (%r8)              // Store the result assuming its a long, int or Object*
+    ret
+.Lreturn_double_quick2:
+    movsd %xmm0, (%r8)           // Store the double floating point result.
+    ret
+.Lreturn_float_quick2:
+    movss %xmm0, (%r8)           // Store the floating point result.
+    ret
 END_FUNCTION art_quick_invoke_stub
 
 MACRO3(NO_ARG_DOWNCALL, c_name, cxx_name, return_macro)
@@ -210,8 +451,11 @@
 END_MACRO
 
 MACRO0(RETURN_OR_DELIVER_PENDING_EXCEPTION)
-    int3
-    int3
+    movq %gs:THREAD_EXCEPTION_OFFSET, %rcx // get exception field
+    testq %rcx, %rcx               // rcx == 0 ?
+    jnz 1f                         // if rcx != 0 goto 1
+    ret                            // return
+1:                                 // deliver exception on current thread
     DELIVER_PENDING_EXCEPTION
 END_MACRO
 
@@ -390,7 +634,22 @@
      */
 UNIMPLEMENTED art_quick_imt_conflict_trampoline
 UNIMPLEMENTED art_quick_resolution_trampoline
-UNIMPLEMENTED art_quick_to_interpreter_bridge
+
+    /*
+     * Called to bridge from the quick to interpreter ABI. On entry the arguments match those
+     * of a quick call:
+     * RDI = method being called / to bridge to.
+     * RSI, RDX, RCX, R8, R9 are arguments to that method.
+     */
+DEFINE_FUNCTION art_quick_to_interpreter_bridge
+    SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME   // Set up frame and save arguments.
+    movq %gs:THREAD_SELF_OFFSET, %rsi      // RSI := Thread::Current()
+    movq %rsp, %rdx                        // RDX := sp
+    call PLT_SYMBOL(artQuickToInterpreterBridge)  // (method, Thread*, SP)
+    RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME  // TODO: no need to restore arguments in this case.
+    movq %rax, %xmm0                   // Place return value also into floating point return value.
+    RETURN_OR_DELIVER_PENDING_EXCEPTION    // return or deliver exception
+END_FUNCTION art_quick_to_interpreter_bridge
 
     /*
      * Routine that intercepts method calls and returns.
diff --git a/runtime/arch/x86_64/registers_x86_64.h b/runtime/arch/x86_64/registers_x86_64.h
index 9808d91..c1a9942 100644
--- a/runtime/arch/x86_64/registers_x86_64.h
+++ b/runtime/arch/x86_64/registers_x86_64.h
@@ -48,6 +48,26 @@
 };
 std::ostream& operator<<(std::ostream& os, const Register& rhs);
 
+enum FloatRegister {
+  XMM0 = 0,
+  XMM1 = 1,
+  XMM2 = 2,
+  XMM3 = 3,
+  XMM4 = 4,
+  XMM5 = 5,
+  XMM6 = 6,
+  XMM7 = 7,
+  XMM8 = 8,
+  XMM9 = 9,
+  XMM10 = 10,
+  XMM11 = 11,
+  XMM12 = 12,
+  XMM13 = 13,
+  XMM14 = 14,
+  XMM15 = 15,
+};
+std::ostream& operator<<(std::ostream& os, const FloatRegister& rhs);
+
 }  // namespace x86_64
 }  // namespace art
 
diff --git a/runtime/arch/x86_64/thread_x86_64.cc b/runtime/arch/x86_64/thread_x86_64.cc
index 9e45a72..b74fc5d 100644
--- a/runtime/arch/x86_64/thread_x86_64.cc
+++ b/runtime/arch/x86_64/thread_x86_64.cc
@@ -48,6 +48,12 @@
   CHECK_EQ(self_check, this);
 
   // Sanity check other offsets.
+  CHECK_EQ(static_cast<size_t>(RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET),
+           Runtime::GetCalleeSaveMethodOffset(Runtime::kSaveAll));
+  CHECK_EQ(static_cast<size_t>(RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET),
+           Runtime::GetCalleeSaveMethodOffset(Runtime::kRefsOnly));
+  CHECK_EQ(static_cast<size_t>(RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET),
+           Runtime::GetCalleeSaveMethodOffset(Runtime::kRefsAndArgs));
   CHECK_EQ(THREAD_EXCEPTION_OFFSET, OFFSETOF_MEMBER(Thread, exception_));
   CHECK_EQ(THREAD_CARD_TABLE_OFFSET, OFFSETOF_MEMBER(Thread, card_table_));
   CHECK_EQ(THREAD_ID_OFFSET, OFFSETOF_MEMBER(Thread, thin_lock_thread_id_));
diff --git a/runtime/atomic.h b/runtime/atomic.h
index 2a47e46..795f917 100644
--- a/runtime/atomic.h
+++ b/runtime/atomic.h
@@ -96,7 +96,7 @@
 // quasiatomic operations that are performed on partially-overlapping
 // memory.
 class QuasiAtomic {
-#if !defined(__arm__) && !defined(__i386__)
+#if defined(__mips__) && !defined(__LP64__)
   static constexpr bool kNeedSwapMutexes = true;
 #else
   static constexpr bool kNeedSwapMutexes = false;
@@ -141,7 +141,7 @@
   }
 
   static void MembarLoadStore() {
-  #if defined(__arm__)
+  #if defined(__arm__) || defined(__aarch64__)
     __asm__ __volatile__("dmb ish" : : : "memory");
   #elif defined(__i386__) || defined(__x86_64__)
     __asm__ __volatile__("" : : : "memory");
@@ -153,7 +153,7 @@
   }
 
   static void MembarLoadLoad() {
-  #if defined(__arm__)
+  #if defined(__arm__) || defined(__aarch64__)
     __asm__ __volatile__("dmb ish" : : : "memory");
   #elif defined(__i386__) || defined(__x86_64__)
     __asm__ __volatile__("" : : : "memory");
@@ -165,7 +165,7 @@
   }
 
   static void MembarStoreStore() {
-  #if defined(__arm__)
+  #if defined(__arm__) || defined(__aarch64__)
     __asm__ __volatile__("dmb ishst" : : : "memory");
   #elif defined(__i386__) || defined(__x86_64__)
     __asm__ __volatile__("" : : : "memory");
@@ -177,7 +177,7 @@
   }
 
   static void MembarStoreLoad() {
-  #if defined(__arm__)
+  #if defined(__arm__) || defined(__aarch64__)
     __asm__ __volatile__("dmb ish" : : : "memory");
   #elif defined(__i386__) || defined(__x86_64__)
     __asm__ __volatile__("mfence" : : : "memory");
diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h
index 66c24b5..6ef0082 100644
--- a/runtime/class_linker-inl.h
+++ b/runtime/class_linker-inl.h
@@ -23,10 +23,40 @@
 #include "mirror/dex_cache.h"
 #include "mirror/iftable.h"
 #include "mirror/object_array.h"
+#include "object_utils.h"
 #include "sirt_ref.h"
 
 namespace art {
 
+inline bool ClassLinker::IsInBootClassPath(const char* descriptor) {
+  DexFile::ClassPathEntry pair = DexFile::FindInClassPath(descriptor, boot_class_path_);
+  return pair.second != nullptr;
+}
+
+inline mirror::Class* ClassLinker::FindSystemClass(Thread* self, const char* descriptor) {
+  SirtRef<mirror::ClassLoader> class_loader(self, nullptr);
+  return FindClass(self, descriptor, class_loader);
+}
+
+inline mirror::Class* ClassLinker::FindArrayClass(Thread* self, mirror::Class* element_class) {
+  for (size_t i = 0; i < kFindArrayCacheSize; ++i) {
+    // Read the cached the array class once to avoid races with other threads setting it.
+    mirror::Class* array_class = find_array_class_cache_[i];
+    if (array_class != nullptr && array_class->GetComponentType() == element_class) {
+      return array_class;
+    }
+  }
+  std::string descriptor("[");
+  descriptor += ClassHelper(element_class).GetDescriptor();
+  SirtRef<mirror::ClassLoader> class_loader(self, element_class->GetClassLoader());
+  mirror::Class* array_class = FindClass(self, descriptor.c_str(), class_loader);
+  // Benign races in storing array class and incrementing index.
+  size_t victim_index = find_array_class_cache_next_victim_;
+  find_array_class_cache_[victim_index] = array_class;
+  find_array_class_cache_next_victim_ = (victim_index + 1) % kFindArrayCacheSize;
+  return array_class;
+}
+
 inline mirror::String* ClassLinker::ResolveString(uint32_t string_idx,
                                                   mirror::ArtMethod* referrer) {
   mirror::String* resolved_string = referrer->GetDexCacheStrings()->Get(string_idx);
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index fa50324..69d957f 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -19,8 +19,6 @@
 #include <fcntl.h>
 #include <sys/file.h>
 #include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/wait.h>
 
 #include <deque>
 #include <string>
@@ -176,17 +174,19 @@
     : dex_lock_("ClassLinker dex lock", kDefaultMutexLevel),
       dex_cache_image_class_lookup_required_(false),
       failed_dex_cache_class_lookups_(0),
-      class_roots_(NULL),
-      array_iftable_(NULL),
+      class_roots_(nullptr),
+      array_iftable_(nullptr),
+      find_array_class_cache_next_victim_(0),
       init_done_(false),
       dex_caches_dirty_(false),
       class_table_dirty_(false),
       intern_table_(intern_table),
-      portable_resolution_trampoline_(NULL),
-      quick_resolution_trampoline_(NULL),
-      portable_imt_conflict_trampoline_(NULL),
-      quick_imt_conflict_trampoline_(NULL) {
+      portable_resolution_trampoline_(nullptr),
+      quick_resolution_trampoline_(nullptr),
+      portable_imt_conflict_trampoline_(nullptr),
+      quick_imt_conflict_trampoline_(nullptr) {
   CHECK_EQ(arraysize(class_roots_descriptors_), size_t(kClassRootsMax));
+  memset(find_array_class_cache_, 0, kFindArrayCacheSize * sizeof(mirror::Class*));
 }
 
 void ClassLinker::InitFromCompiler(const std::vector<const DexFile*>& boot_class_path) {
@@ -335,54 +335,54 @@
 
   // Object, String and DexCache need to be rerun through FindSystemClass to finish init
   java_lang_Object->SetStatus(mirror::Class::kStatusNotReady, self);
-  mirror::Class* Object_class = FindSystemClass("Ljava/lang/Object;");
+  mirror::Class* Object_class = FindSystemClass(self, "Ljava/lang/Object;");
   CHECK_EQ(java_lang_Object.get(), Object_class);
   CHECK_EQ(java_lang_Object->GetObjectSize(), sizeof(mirror::Object));
   java_lang_String->SetStatus(mirror::Class::kStatusNotReady, self);
-  mirror::Class* String_class = FindSystemClass("Ljava/lang/String;");
+  mirror::Class* String_class = FindSystemClass(self, "Ljava/lang/String;");
   CHECK_EQ(java_lang_String.get(), String_class);
   CHECK_EQ(java_lang_String->GetObjectSize(), sizeof(mirror::String));
   java_lang_DexCache->SetStatus(mirror::Class::kStatusNotReady, self);
-  mirror::Class* DexCache_class = FindSystemClass("Ljava/lang/DexCache;");
+  mirror::Class* DexCache_class = FindSystemClass(self, "Ljava/lang/DexCache;");
   CHECK_EQ(java_lang_String.get(), String_class);
   CHECK_EQ(java_lang_DexCache.get(), DexCache_class);
   CHECK_EQ(java_lang_DexCache->GetObjectSize(), sizeof(mirror::DexCache));
 
   // Setup the primitive array type classes - can't be done until Object has a vtable.
-  SetClassRoot(kBooleanArrayClass, FindSystemClass("[Z"));
+  SetClassRoot(kBooleanArrayClass, FindSystemClass(self, "[Z"));
   mirror::BooleanArray::SetArrayClass(GetClassRoot(kBooleanArrayClass));
 
-  SetClassRoot(kByteArrayClass, FindSystemClass("[B"));
+  SetClassRoot(kByteArrayClass, FindSystemClass(self, "[B"));
   mirror::ByteArray::SetArrayClass(GetClassRoot(kByteArrayClass));
 
-  mirror::Class* found_char_array_class = FindSystemClass("[C");
+  mirror::Class* found_char_array_class = FindSystemClass(self, "[C");
   CHECK_EQ(char_array_class.get(), found_char_array_class);
 
-  SetClassRoot(kShortArrayClass, FindSystemClass("[S"));
+  SetClassRoot(kShortArrayClass, FindSystemClass(self, "[S"));
   mirror::ShortArray::SetArrayClass(GetClassRoot(kShortArrayClass));
 
-  mirror::Class* found_int_array_class = FindSystemClass("[I");
+  mirror::Class* found_int_array_class = FindSystemClass(self, "[I");
   CHECK_EQ(int_array_class.get(), found_int_array_class);
 
-  SetClassRoot(kLongArrayClass, FindSystemClass("[J"));
+  SetClassRoot(kLongArrayClass, FindSystemClass(self, "[J"));
   mirror::LongArray::SetArrayClass(GetClassRoot(kLongArrayClass));
 
-  SetClassRoot(kFloatArrayClass, FindSystemClass("[F"));
+  SetClassRoot(kFloatArrayClass, FindSystemClass(self, "[F"));
   mirror::FloatArray::SetArrayClass(GetClassRoot(kFloatArrayClass));
 
-  SetClassRoot(kDoubleArrayClass, FindSystemClass("[D"));
+  SetClassRoot(kDoubleArrayClass, FindSystemClass(self, "[D"));
   mirror::DoubleArray::SetArrayClass(GetClassRoot(kDoubleArrayClass));
 
-  mirror::Class* found_class_array_class = FindSystemClass("[Ljava/lang/Class;");
+  mirror::Class* found_class_array_class = FindSystemClass(self, "[Ljava/lang/Class;");
   CHECK_EQ(class_array_class.get(), found_class_array_class);
 
-  mirror::Class* found_object_array_class = FindSystemClass("[Ljava/lang/Object;");
+  mirror::Class* found_object_array_class = FindSystemClass(self, "[Ljava/lang/Object;");
   CHECK_EQ(object_array_class.get(), found_object_array_class);
 
   // Setup the single, global copy of "iftable".
-  mirror::Class* java_lang_Cloneable = FindSystemClass("Ljava/lang/Cloneable;");
+  mirror::Class* java_lang_Cloneable = FindSystemClass(self, "Ljava/lang/Cloneable;");
   CHECK(java_lang_Cloneable != NULL);
-  mirror::Class* java_io_Serializable = FindSystemClass("Ljava/io/Serializable;");
+  mirror::Class* java_io_Serializable = FindSystemClass(self, "Ljava/io/Serializable;");
   CHECK(java_io_Serializable != NULL);
   // We assume that Cloneable/Serializable don't have superinterfaces -- normally we'd have to
   // crawl up and explicitly list all of the supers as well.
@@ -398,73 +398,73 @@
   CHECK_EQ(java_io_Serializable, kh.GetDirectInterface(1));
   // Run Class, ArtField, and ArtMethod through FindSystemClass. This initializes their
   // dex_cache_ fields and register them in class_table_.
-  mirror::Class* Class_class = FindSystemClass("Ljava/lang/Class;");
+  mirror::Class* Class_class = FindSystemClass(self, "Ljava/lang/Class;");
   CHECK_EQ(java_lang_Class.get(), Class_class);
 
   java_lang_reflect_ArtMethod->SetStatus(mirror::Class::kStatusNotReady, self);
-  mirror::Class* Art_method_class = FindSystemClass("Ljava/lang/reflect/ArtMethod;");
+  mirror::Class* Art_method_class = FindSystemClass(self, "Ljava/lang/reflect/ArtMethod;");
   CHECK_EQ(java_lang_reflect_ArtMethod.get(), Art_method_class);
 
   java_lang_reflect_ArtField->SetStatus(mirror::Class::kStatusNotReady, self);
-  mirror::Class* Art_field_class = FindSystemClass("Ljava/lang/reflect/ArtField;");
+  mirror::Class* Art_field_class = FindSystemClass(self, "Ljava/lang/reflect/ArtField;");
   CHECK_EQ(java_lang_reflect_ArtField.get(), Art_field_class);
 
-  mirror::Class* String_array_class = FindSystemClass(class_roots_descriptors_[kJavaLangStringArrayClass]);
+  mirror::Class* String_array_class = FindSystemClass(self, class_roots_descriptors_[kJavaLangStringArrayClass]);
   CHECK_EQ(object_array_string.get(), String_array_class);
 
   mirror::Class* Art_method_array_class =
-      FindSystemClass(class_roots_descriptors_[kJavaLangReflectArtMethodArrayClass]);
+      FindSystemClass(self, class_roots_descriptors_[kJavaLangReflectArtMethodArrayClass]);
   CHECK_EQ(object_array_art_method.get(), Art_method_array_class);
 
   mirror::Class* Art_field_array_class =
-      FindSystemClass(class_roots_descriptors_[kJavaLangReflectArtFieldArrayClass]);
+      FindSystemClass(self, class_roots_descriptors_[kJavaLangReflectArtFieldArrayClass]);
   CHECK_EQ(object_array_art_field.get(), Art_field_array_class);
 
   // End of special init trickery, subsequent classes may be loaded via FindSystemClass.
 
   // Create java.lang.reflect.Proxy root.
-  mirror::Class* java_lang_reflect_Proxy = FindSystemClass("Ljava/lang/reflect/Proxy;");
+  mirror::Class* java_lang_reflect_Proxy = FindSystemClass(self, "Ljava/lang/reflect/Proxy;");
   SetClassRoot(kJavaLangReflectProxy, java_lang_reflect_Proxy);
 
   // java.lang.ref classes need to be specially flagged, but otherwise are normal classes
-  mirror::Class* java_lang_ref_Reference = FindSystemClass("Ljava/lang/ref/Reference;");
+  mirror::Class* java_lang_ref_Reference = FindSystemClass(self, "Ljava/lang/ref/Reference;");
   SetClassRoot(kJavaLangRefReference, java_lang_ref_Reference);
-  mirror::Class* java_lang_ref_FinalizerReference = FindSystemClass("Ljava/lang/ref/FinalizerReference;");
+  mirror::Class* java_lang_ref_FinalizerReference = FindSystemClass(self, "Ljava/lang/ref/FinalizerReference;");
   java_lang_ref_FinalizerReference->SetAccessFlags(
       java_lang_ref_FinalizerReference->GetAccessFlags() |
           kAccClassIsReference | kAccClassIsFinalizerReference);
-  mirror::Class* java_lang_ref_PhantomReference = FindSystemClass("Ljava/lang/ref/PhantomReference;");
+  mirror::Class* java_lang_ref_PhantomReference = FindSystemClass(self, "Ljava/lang/ref/PhantomReference;");
   java_lang_ref_PhantomReference->SetAccessFlags(
       java_lang_ref_PhantomReference->GetAccessFlags() |
           kAccClassIsReference | kAccClassIsPhantomReference);
-  mirror::Class* java_lang_ref_SoftReference = FindSystemClass("Ljava/lang/ref/SoftReference;");
+  mirror::Class* java_lang_ref_SoftReference = FindSystemClass(self, "Ljava/lang/ref/SoftReference;");
   java_lang_ref_SoftReference->SetAccessFlags(
       java_lang_ref_SoftReference->GetAccessFlags() | kAccClassIsReference);
-  mirror::Class* java_lang_ref_WeakReference = FindSystemClass("Ljava/lang/ref/WeakReference;");
+  mirror::Class* java_lang_ref_WeakReference = FindSystemClass(self, "Ljava/lang/ref/WeakReference;");
   java_lang_ref_WeakReference->SetAccessFlags(
       java_lang_ref_WeakReference->GetAccessFlags() |
           kAccClassIsReference | kAccClassIsWeakReference);
 
   // Setup the ClassLoader, verifying the object_size_.
-  mirror::Class* java_lang_ClassLoader = FindSystemClass("Ljava/lang/ClassLoader;");
+  mirror::Class* java_lang_ClassLoader = FindSystemClass(self, "Ljava/lang/ClassLoader;");
   CHECK_EQ(java_lang_ClassLoader->GetObjectSize(), sizeof(mirror::ClassLoader));
   SetClassRoot(kJavaLangClassLoader, java_lang_ClassLoader);
 
   // Set up java.lang.Throwable, java.lang.ClassNotFoundException, and
   // java.lang.StackTraceElement as a convenience.
-  SetClassRoot(kJavaLangThrowable, FindSystemClass("Ljava/lang/Throwable;"));
+  SetClassRoot(kJavaLangThrowable, FindSystemClass(self, "Ljava/lang/Throwable;"));
   mirror::Throwable::SetClass(GetClassRoot(kJavaLangThrowable));
-  SetClassRoot(kJavaLangClassNotFoundException, FindSystemClass("Ljava/lang/ClassNotFoundException;"));
-  SetClassRoot(kJavaLangStackTraceElement, FindSystemClass("Ljava/lang/StackTraceElement;"));
-  SetClassRoot(kJavaLangStackTraceElementArrayClass, FindSystemClass("[Ljava/lang/StackTraceElement;"));
+  SetClassRoot(kJavaLangClassNotFoundException, FindSystemClass(self, "Ljava/lang/ClassNotFoundException;"));
+  SetClassRoot(kJavaLangStackTraceElement, FindSystemClass(self, "Ljava/lang/StackTraceElement;"));
+  SetClassRoot(kJavaLangStackTraceElementArrayClass, FindSystemClass(self, "[Ljava/lang/StackTraceElement;"));
   mirror::StackTraceElement::SetClass(GetClassRoot(kJavaLangStackTraceElement));
 
-  FinishInit();
+  FinishInit(self);
 
   VLOG(startup) << "ClassLinker::InitFromCompiler exiting";
 }
 
-void ClassLinker::FinishInit() {
+void ClassLinker::FinishInit(Thread* self) {
   VLOG(startup) << "ClassLinker::FinishInit entering";
 
   // Let the heap know some key offsets into java.lang.ref instances
@@ -473,7 +473,7 @@
   // fully initialized
   mirror::Class* java_lang_ref_Reference = GetClassRoot(kJavaLangRefReference);
   mirror::Class* java_lang_ref_FinalizerReference =
-      FindSystemClass("Ljava/lang/ref/FinalizerReference;");
+      FindSystemClass(self, "Ljava/lang/ref/FinalizerReference;");
 
   mirror::ArtField* pendingNext = java_lang_ref_Reference->GetInstanceField(0);
   FieldHelper fh(pendingNext);
@@ -543,108 +543,45 @@
                                   const char* oat_cache_filename,
                                   std::string* error_msg) {
   Locks::mutator_lock_->AssertNotHeld(Thread::Current());  // Avoid starving GC.
-  std::string dex2oat_string(GetAndroidRoot());
-  dex2oat_string += (kIsDebugBuild ? "/bin/dex2oatd" : "/bin/dex2oat");
-  const char* dex2oat = dex2oat_string.c_str();
-
-  const char* class_path = Runtime::Current()->GetClassPathString().c_str();
+  std::string dex2oat(GetAndroidRoot());
+  dex2oat += (kIsDebugBuild ? "/bin/dex2oatd" : "/bin/dex2oat");
 
   gc::Heap* heap = Runtime::Current()->GetHeap();
-  std::string boot_image_option_string("--boot-image=");
-  boot_image_option_string += heap->GetImageSpace()->GetImageFilename();
-  const char* boot_image_option = boot_image_option_string.c_str();
+  std::string boot_image_option("--boot-image=");
+  boot_image_option += heap->GetImageSpace()->GetImageFilename();
 
-  std::string dex_file_option_string("--dex-file=");
-  dex_file_option_string += dex_filename;
-  const char* dex_file_option = dex_file_option_string.c_str();
+  std::string dex_file_option("--dex-file=");
+  dex_file_option += dex_filename;
 
-  std::string oat_fd_option_string("--oat-fd=");
-  StringAppendF(&oat_fd_option_string, "%d", oat_fd);
-  const char* oat_fd_option = oat_fd_option_string.c_str();
+  std::string oat_fd_option("--oat-fd=");
+  StringAppendF(&oat_fd_option, "%d", oat_fd);
 
-  std::string oat_location_option_string("--oat-location=");
-  oat_location_option_string += oat_cache_filename;
-  const char* oat_location_option = oat_location_option_string.c_str();
+  std::string oat_location_option("--oat-location=");
+  oat_location_option += oat_cache_filename;
 
-  std::string oat_compiler_filter_string("-compiler-filter:");
-  Runtime::CompilerFilter filter = Runtime::Current()->GetCompilerFilter();
-  switch (filter) {
-    case Runtime::kInterpretOnly:
-      oat_compiler_filter_string += "interpret-only";
-      break;
-    case Runtime::kSpace:
-      oat_compiler_filter_string += "space";
-      break;
-    case Runtime::kBalanced:
-      oat_compiler_filter_string += "balanced";
-      break;
-    case Runtime::kSpeed:
-      oat_compiler_filter_string += "speed";
-      break;
-    case Runtime::kEverything:
-      oat_compiler_filter_string += "everything";
-      break;
-    default:
-      LOG(FATAL) << "Unexpected case: " << filter;
+  std::vector<std::string> argv;
+  argv.push_back(dex2oat);
+  argv.push_back("--runtime-arg");
+  argv.push_back("-Xms64m");
+  argv.push_back("--runtime-arg");
+  argv.push_back("-Xmx64m");
+  argv.push_back("--runtime-arg");
+  argv.push_back("-classpath");
+  argv.push_back("--runtime-arg");
+  argv.push_back(Runtime::Current()->GetClassPathString());
+  if (!kIsTargetBuild) {
+    argv.push_back("--host");
   }
-  const char* oat_compiler_filter_option = oat_compiler_filter_string.c_str();
-
-  // fork and exec dex2oat
-  pid_t pid = fork();
-  if (pid == 0) {
-    // no allocation allowed between fork and exec
-
-    // change process groups, so we don't get reaped by ProcessManager
-    setpgid(0, 0);
-
-    // gLogVerbosity.class_linker = true;
-    VLOG(class_linker) << dex2oat
-                       << " --runtime-arg -Xms64m"
-                       << " --runtime-arg -Xmx64m"
-                       << " --runtime-arg -classpath"
-                       << " --runtime-arg " << class_path
-                       << " --runtime-arg " << oat_compiler_filter_option
-#if !defined(ART_TARGET)
-                       << " --host"
-#endif
-                       << " " << boot_image_option
-                       << " " << dex_file_option
-                       << " " << oat_fd_option
-                       << " " << oat_location_option;
-
-    execl(dex2oat, dex2oat,
-          "--runtime-arg", "-Xms64m",
-          "--runtime-arg", "-Xmx64m",
-          "--runtime-arg", "-classpath",
-          "--runtime-arg", class_path,
-          "--runtime-arg", oat_compiler_filter_option,
-#if !defined(ART_TARGET)
-          "--host",
-#endif
-          boot_image_option,
-          dex_file_option,
-          oat_fd_option,
-          oat_location_option,
-          NULL);
-
-    PLOG(FATAL) << "execl(" << dex2oat << ") failed";
-    return false;
-  } else {
-    // wait for dex2oat to finish
-    int status;
-    pid_t got_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
-    if (got_pid != pid) {
-      *error_msg = StringPrintf("Failed to create oat file. Waitpid failed: wanted %d, got %d",
-                                pid, got_pid);
-      return false;
-    }
-    if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
-      *error_msg = StringPrintf("Failed to create oat file. %s failed with dex-file '%s'",
-                                dex2oat, dex_filename);
-      return false;
-    }
+  argv.push_back(boot_image_option);
+  argv.push_back(dex_file_option);
+  argv.push_back(oat_fd_option);
+  argv.push_back(oat_location_option);
+  const std::vector<std::string>& compiler_options = Runtime::Current()->GetCompilerOptions();
+  for (size_t i = 0; compiler_options.size(); ++i) {
+    argv.push_back(compiler_options[i].c_str());
   }
-  return true;
+
+  return Exec(argv, error_msg);
 }
 
 const OatFile* ClassLinker::RegisterOatFile(const OatFile* oat_file) {
@@ -1111,7 +1048,7 @@
   mirror::Throwable::SetClass(GetClassRoot(kJavaLangThrowable));
   mirror::StackTraceElement::SetClass(GetClassRoot(kJavaLangStackTraceElement));
 
-  FinishInit();
+  FinishInit(self);
 
   VLOG(startup) << "ClassLinker::InitFromImage exiting";
 }
@@ -1150,6 +1087,13 @@
   }
   callback(reinterpret_cast<mirror::Object**>(&array_iftable_), arg, 0, kRootVMInternal);
   DCHECK(array_iftable_ != nullptr);
+  for (size_t i = 0; i < kFindArrayCacheSize; ++i) {
+    if (find_array_class_cache_[i] != nullptr) {
+      callback(reinterpret_cast<mirror::Object**>(&find_array_class_cache_[i]), arg, 0,
+               kRootVMInternal);
+      DCHECK(find_array_class_cache_[i] != nullptr);
+    }
+  }
 }
 
 void ClassLinker::VisitClasses(ClassVisitor* visitor, void* arg) {
@@ -1307,21 +1251,10 @@
   return klass;
 }
 
-bool ClassLinker::IsInBootClassPath(const char* descriptor) {
-  DexFile::ClassPathEntry pair = DexFile::FindInClassPath(descriptor, boot_class_path_);
-  return pair.second != NULL;
-}
-
-mirror::Class* ClassLinker::FindSystemClass(const char* descriptor) {
-  SirtRef<mirror::ClassLoader> class_loader(Thread::Current(), nullptr);
-  return FindClass(descriptor, class_loader);
-}
-
-mirror::Class* ClassLinker::FindClass(const char* descriptor,
+mirror::Class* ClassLinker::FindClass(Thread* self, const char* descriptor,
                                       const SirtRef<mirror::ClassLoader>& class_loader) {
   DCHECK_NE(*descriptor, '\0') << "descriptor is empty string";
-  Thread* self = Thread::Current();
-  DCHECK(self != NULL);
+  DCHECK(self != nullptr);
   self->AssertNoPendingException();
   if (descriptor[1] == '\0') {
     // only the descriptors of primitive types should be 1 character long, also avoid class lookup
@@ -1335,7 +1268,7 @@
   }
   // Class is not yet loaded.
   if (descriptor[0] == '[') {
-    return CreateArrayClass(descriptor, class_loader);
+    return CreateArrayClass(self, descriptor, class_loader);
   } else if (class_loader.get() == nullptr) {
     DexFile::ClassPathEntry pair = DexFile::FindInClassPath(descriptor, boot_class_path_);
     if (pair.second != NULL) {
@@ -1346,7 +1279,7 @@
     // First try the boot class path, we check the descriptor first to avoid an unnecessary
     // throw of a NoClassDefFoundError.
     if (IsInBootClassPath(descriptor)) {
-      mirror::Class* system_class = FindSystemClass(descriptor);
+      mirror::Class* system_class = FindSystemClass(self, descriptor);
       CHECK(system_class != NULL);
       return system_class;
     }
@@ -1365,7 +1298,7 @@
     }
 
   } else {
-    ScopedObjectAccessUnchecked soa(self->GetJniEnv());
+    ScopedObjectAccessUnchecked soa(self);
     ScopedLocalRef<jobject> class_loader_object(soa.Env(),
                                                 soa.AddLocalReference<jobject>(class_loader.get()));
     std::string class_name_string(DescriptorToDot(descriptor));
@@ -1382,7 +1315,7 @@
                                                WellKnownClasses::java_lang_ClassLoader_loadClass,
                                                class_name_object.get()));
     }
-    if (soa.Self()->IsExceptionPending()) {
+    if (self->IsExceptionPending()) {
       // If the ClassLoader threw, pass that exception up.
       return NULL;
     } else if (result.get() == NULL) {
@@ -2133,12 +2066,11 @@
 // array class; that always comes from the base element class.
 //
 // Returns NULL with an exception raised on failure.
-mirror::Class* ClassLinker::CreateArrayClass(const char* descriptor,
+mirror::Class* ClassLinker::CreateArrayClass(Thread* self, const char* descriptor,
                                              const SirtRef<mirror::ClassLoader>& class_loader) {
   // Identify the underlying component type
   CHECK_EQ('[', descriptor[0]);
-  Thread* self = Thread::Current();
-  SirtRef<mirror::Class> component_type(self, FindClass(descriptor + 1, class_loader));
+  SirtRef<mirror::Class> component_type(self, FindClass(self, descriptor + 1, class_loader));
   if (component_type.get() == nullptr) {
     DCHECK(self->IsExceptionPending());
     return nullptr;
@@ -2308,7 +2240,7 @@
       CHECK(klass == existing);
     }
   }
-  Runtime::Current()->GetHeap()->VerifyObject(klass);
+  VerifyObject(klass);
   class_table_.insert(std::make_pair(hash, klass));
   class_table_dirty_ = true;
   return NULL;
@@ -3242,7 +3174,7 @@
     for (int i = super->GetVTable()->GetLength() - 1; i >= 0; --i) {
       mirror::ArtMethod* method = klass->GetVTable()->Get(i);
       if (method != super->GetVTable()->Get(i) &&
-          !IsSameMethodSignatureInDifferentClassContexts(method, super.get(), klass.get())) {
+          !IsSameMethodSignatureInDifferentClassContexts(self, method, super.get(), klass.get())) {
         ThrowLinkageError(klass.get(), "Class %s method %s resolves differently in superclass %s",
                           PrettyDescriptor(klass.get()).c_str(), PrettyMethod(method).c_str(),
                           PrettyDescriptor(super.get()).c_str());
@@ -3255,7 +3187,7 @@
     if (klass->GetClassLoader() != interface->GetClassLoader()) {
       for (size_t j = 0; j < interface->NumVirtualMethods(); ++j) {
         mirror::ArtMethod* method = klass->GetIfTable()->GetMethodArray(i)->Get(j);
-        if (!IsSameMethodSignatureInDifferentClassContexts(method, interface.get(),
+        if (!IsSameMethodSignatureInDifferentClassContexts(self, method, interface.get(),
                                                            method->GetDeclaringClass())) {
           ThrowLinkageError(klass.get(), "Class %s method %s resolves differently in interface %s",
                             PrettyDescriptor(method->GetDeclaringClass()).c_str(),
@@ -3271,13 +3203,13 @@
 
 // Returns true if classes referenced by the signature of the method are the
 // same classes in klass1 as they are in klass2.
-bool ClassLinker::IsSameMethodSignatureInDifferentClassContexts(mirror::ArtMethod* method,
+bool ClassLinker::IsSameMethodSignatureInDifferentClassContexts(Thread* self,
+                                                                mirror::ArtMethod* method,
                                                                 mirror::Class* klass1,
                                                                 mirror::Class* klass2) {
   if (klass1 == klass2) {
     return true;
   }
-  Thread* self = Thread::Current();
   CHECK(klass1 != nullptr);
   CHECK(klass2 != nullptr);
   SirtRef<mirror::ClassLoader> loader1(self, klass1->GetClassLoader());
@@ -3292,7 +3224,7 @@
     }
     if (descriptor[0] == 'L' || descriptor[0] == '[') {
       // Found a non-primitive type.
-      if (!IsSameDescriptorInDifferentClassContexts(descriptor, loader1, loader2)) {
+      if (!IsSameDescriptorInDifferentClassContexts(self, descriptor, loader1, loader2)) {
         return false;
       }
     }
@@ -3300,7 +3232,7 @@
   // Check the return type
   const char* descriptor = dex_file.GetReturnTypeDescriptor(proto_id);
   if (descriptor[0] == 'L' || descriptor[0] == '[') {
-    if (!IsSameDescriptorInDifferentClassContexts(descriptor, loader1, loader2)) {
+    if (!IsSameDescriptorInDifferentClassContexts(self, descriptor, loader1, loader2)) {
       return false;
     }
   }
@@ -3308,16 +3240,15 @@
 }
 
 // Returns true if the descriptor resolves to the same class in the context of loader1 and loader2.
-bool ClassLinker::IsSameDescriptorInDifferentClassContexts(const char* descriptor,
+bool ClassLinker::IsSameDescriptorInDifferentClassContexts(Thread* self, const char* descriptor,
                                                            SirtRef<mirror::ClassLoader>& loader1,
                                                            SirtRef<mirror::ClassLoader>& loader2) {
   CHECK(descriptor != nullptr);
-  Thread* self = Thread::Current();
-  SirtRef<mirror::Class> found1(self, FindClass(descriptor, loader1));
+  SirtRef<mirror::Class> found1(self, FindClass(self, descriptor, loader1));
   if (found1.get() == nullptr) {
     self->ClearException();
   }
-  mirror::Class* found2 = FindClass(descriptor, loader2);
+  mirror::Class* found2 = FindClass(self, descriptor, loader2);
   if (found2 == nullptr) {
     self->ClearException();
   }
@@ -4117,22 +4048,22 @@
   DCHECK(dex_cache.get() != NULL);
   mirror::Class* resolved = dex_cache->GetResolvedType(type_idx);
   if (resolved == NULL) {
+    Thread* self = Thread::Current();
     const char* descriptor = dex_file.StringByTypeIdx(type_idx);
-    resolved = FindClass(descriptor, class_loader);
+    resolved = FindClass(self, descriptor, class_loader);
     if (resolved != NULL) {
       // TODO: we used to throw here if resolved's class loader was not the
       //       boot class loader. This was to permit different classes with the
       //       same name to be loaded simultaneously by different loaders
       dex_cache->SetResolvedType(type_idx, resolved);
     } else {
-      Thread* self = Thread::Current();
       CHECK(self->IsExceptionPending())
           << "Expected pending exception for failed resolution of: " << descriptor;
       // Convert a ClassNotFoundException to a NoClassDefFoundError.
       SirtRef<mirror::Throwable> cause(self, self->GetException(NULL));
       if (cause->InstanceOf(GetClassRoot(kJavaLangClassNotFoundException))) {
         DCHECK(resolved == NULL);  // No SirtRef needed to preserve resolved.
-        Thread::Current()->ClearException();
+        self->ClearException();
         ThrowNoClassDefFoundError("Failed resolution of: %s", descriptor);
         self->GetException(NULL)->SetCause(cause.get());
       }
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 0745ee2..f346102 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -72,10 +72,17 @@
 
   // Finds a class by its descriptor, loading it if necessary.
   // If class_loader is null, searches boot_class_path_.
-  mirror::Class* FindClass(const char* descriptor, const SirtRef<mirror::ClassLoader>& class_loader)
+  mirror::Class* FindClass(Thread* self, const char* descriptor,
+                           const SirtRef<mirror::ClassLoader>& class_loader)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  mirror::Class* FindSystemClass(const char* descriptor)
+  // 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_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  // Finds the array class given for the element class.
+  mirror::Class* FindArrayClass(Thread* self, mirror::Class* element_class)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Reutrns true if the class linker is initialized.
@@ -378,7 +385,7 @@
       LOCKS_EXCLUDED(dex_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  void FinishInit() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void FinishInit(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // For early bootstrapping by Init
   mirror::Class* AllocClass(Thread* self, mirror::Class* java_lang_Class, size_t class_size)
@@ -399,7 +406,7 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
 
-  mirror::Class* CreateArrayClass(const char* descriptor,
+  mirror::Class* CreateArrayClass(Thread* self, const char* descriptor,
                                   const SirtRef<mirror::ClassLoader>& class_loader)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
@@ -449,12 +456,12 @@
   bool ValidateSuperClassDescriptors(const SirtRef<mirror::Class>& klass)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  bool IsSameDescriptorInDifferentClassContexts(const char* descriptor,
+  bool IsSameDescriptorInDifferentClassContexts(Thread* self, const char* descriptor,
                                                 SirtRef<mirror::ClassLoader>& class_loader1,
                                                 SirtRef<mirror::ClassLoader>& class_loader2)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  bool IsSameMethodSignatureInDifferentClassContexts(mirror::ArtMethod* method,
+  bool IsSameMethodSignatureInDifferentClassContexts(Thread* self, mirror::ArtMethod* method,
                                                      mirror::Class* klass1,
                                                      mirror::Class* klass2)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -617,8 +624,15 @@
     return descriptor;
   }
 
+  // The interface table used by all arrays.
   mirror::IfTable* array_iftable_;
 
+  // A cache of the last FindArrayClass results. The cache serves to avoid creating array class
+  // descriptors for the sake of performing FindClass.
+  static constexpr size_t kFindArrayCacheSize = 16;
+  mirror::Class* find_array_class_cache_[kFindArrayCacheSize];
+  size_t find_array_class_cache_next_victim_;
+
   bool init_done_;
   bool dex_caches_dirty_ GUARDED_BY(dex_lock_);
   bool class_table_dirty_ GUARDED_BY(Locks::classlinker_classes_lock_);
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index ebf02fe..d6a67cc 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -41,18 +41,20 @@
  protected:
   void AssertNonExistentClass(const std::string& descriptor)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    EXPECT_TRUE(class_linker_->FindSystemClass(descriptor.c_str()) == NULL);
     Thread* self = Thread::Current();
+    EXPECT_TRUE(class_linker_->FindSystemClass(self, descriptor.c_str()) == NULL);
     EXPECT_TRUE(self->IsExceptionPending());
     mirror::Object* exception = self->GetException(NULL);
     self->ClearException();
-    mirror::Class* exception_class = class_linker_->FindSystemClass("Ljava/lang/NoClassDefFoundError;");
+    mirror::Class* exception_class =
+        class_linker_->FindSystemClass(self, "Ljava/lang/NoClassDefFoundError;");
     EXPECT_TRUE(exception->InstanceOf(exception_class));
   }
 
   void AssertPrimitiveClass(const std::string& descriptor)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    AssertPrimitiveClass(descriptor, class_linker_->FindSystemClass(descriptor.c_str()));
+    Thread* self = Thread::Current();
+    AssertPrimitiveClass(descriptor, class_linker_->FindSystemClass(self, descriptor.c_str()));
   }
 
   void AssertPrimitiveClass(const std::string& descriptor, mirror::Class* primitive)
@@ -98,7 +100,7 @@
     Thread* self = Thread::Current();
     SirtRef<mirror::ClassLoader> loader(self, class_loader);
     SirtRef<mirror::Class> array(self,
-                                 class_linker_->FindClass(array_descriptor.c_str(), loader));
+                                 class_linker_->FindClass(self, array_descriptor.c_str(), loader));
     ClassHelper array_component_ch(array->GetComponentType());
     EXPECT_STREQ(component_type.c_str(), array_component_ch.GetDescriptor());
     EXPECT_EQ(class_loader, array->GetClassLoader());
@@ -115,7 +117,8 @@
     EXPECT_TRUE(array->GetClass()->GetSuperClass() != NULL);
     ASSERT_STREQ(array_descriptor.c_str(), kh.GetDescriptor());
     EXPECT_TRUE(array->GetSuperClass() != NULL);
-    EXPECT_EQ(class_linker_->FindSystemClass("Ljava/lang/Object;"), array->GetSuperClass());
+    Thread* self = Thread::Current();
+    EXPECT_EQ(class_linker_->FindSystemClass(self, "Ljava/lang/Object;"), array->GetSuperClass());
     EXPECT_TRUE(array->HasSuperClass());
     ASSERT_TRUE(array->GetComponentType() != NULL);
     kh.ChangeClass(array->GetComponentType());
@@ -147,6 +150,7 @@
     kh.ChangeClass(array.get());
     kh.ChangeClass(kh.GetDirectInterface(1));
     EXPECT_STREQ(kh.GetDescriptor(), "Ljava/io/Serializable;");
+    EXPECT_EQ(class_linker_->FindArrayClass(self, array->GetComponentType()), array.get());
   }
 
   void AssertMethod(mirror::ArtMethod* method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -300,8 +304,8 @@
   void AssertDexFileClass(mirror::ClassLoader* class_loader, const std::string& descriptor)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     ASSERT_TRUE(descriptor != NULL);
-    SirtRef<mirror::Class> klass(Thread::Current(),
-                                 class_linker_->FindSystemClass(descriptor.c_str()));
+    Thread* self = Thread::Current();
+    SirtRef<mirror::Class> klass(self, class_linker_->FindSystemClass(self, descriptor.c_str()));
     ASSERT_TRUE(klass.get() != nullptr);
     EXPECT_STREQ(descriptor.c_str(), ClassHelper(klass.get()).GetDescriptor());
     EXPECT_EQ(class_loader, klass->GetClassLoader());
@@ -359,7 +363,9 @@
   std::vector<CheckOffset> offsets;
 
   bool Check() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    mirror::Class* klass = Runtime::Current()->GetClassLinker()->FindSystemClass(class_descriptor.c_str());
+    Thread* self = Thread::Current();
+    mirror::Class* klass =
+        Runtime::Current()->GetClassLinker()->FindSystemClass(self, class_descriptor.c_str());
     CHECK(klass != NULL) << class_descriptor;
 
     bool error = false;
@@ -646,12 +652,12 @@
   ScopedObjectAccess soa(Thread::Current());
   SirtRef<mirror::ClassLoader> class_loader(soa.Self(), soa.Decode<mirror::ClassLoader*>(LoadDex("Nested")));
 
-  mirror::Class* outer = class_linker_->FindClass("LNested;", class_loader);
+  mirror::Class* outer = class_linker_->FindClass(soa.Self(), "LNested;", class_loader);
   ASSERT_TRUE(outer != NULL);
   EXPECT_EQ(0U, outer->NumVirtualMethods());
   EXPECT_EQ(1U, outer->NumDirectMethods());
 
-  mirror::Class* inner = class_linker_->FindClass("LNested$Inner;", class_loader);
+  mirror::Class* inner = class_linker_->FindClass(soa.Self(), "LNested$Inner;", class_loader);
   ASSERT_TRUE(inner != NULL);
   EXPECT_EQ(0U, inner->NumVirtualMethods());
   EXPECT_EQ(1U, inner->NumDirectMethods());
@@ -673,7 +679,7 @@
 
 TEST_F(ClassLinkerTest, FindClass) {
   ScopedObjectAccess soa(Thread::Current());
-  mirror::Class* JavaLangObject = class_linker_->FindSystemClass("Ljava/lang/Object;");
+  mirror::Class* JavaLangObject = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;");
   ClassHelper kh(JavaLangObject);
   ASSERT_TRUE(JavaLangObject != NULL);
   ASSERT_TRUE(JavaLangObject->GetClass() != NULL);
@@ -710,7 +716,7 @@
 
   SirtRef<mirror::ClassLoader> class_loader(soa.Self(), soa.Decode<mirror::ClassLoader*>(LoadDex("MyClass")));
   AssertNonExistentClass("LMyClass;");
-  mirror::Class* MyClass = class_linker_->FindClass("LMyClass;", class_loader);
+  mirror::Class* MyClass = class_linker_->FindClass(soa.Self(), "LMyClass;", class_loader);
   kh.ChangeClass(MyClass);
   ASSERT_TRUE(MyClass != NULL);
   ASSERT_TRUE(MyClass->GetClass() != NULL);
@@ -761,7 +767,7 @@
 // start of the object
 TEST_F(ClassLinkerTest, ValidateObjectArrayElementsOffset) {
   ScopedObjectAccess soa(Thread::Current());
-  mirror::Class* array_class = class_linker_->FindSystemClass("[Ljava/lang/String;");
+  mirror::Class* array_class = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/String;");
   mirror::ObjectArray<mirror::String>* array =
       mirror::ObjectArray<mirror::String>::Alloc(soa.Self(), array_class, 0);
   uintptr_t data_offset =
@@ -777,27 +783,27 @@
 TEST_F(ClassLinkerTest, ValidatePrimitiveArrayElementsOffset) {
   ScopedObjectAccess soa(Thread::Current());
   SirtRef<mirror::LongArray> long_array(soa.Self(), mirror::LongArray::Alloc(soa.Self(), 0));
-  EXPECT_EQ(class_linker_->FindSystemClass("[J"), long_array->GetClass());
+  EXPECT_EQ(class_linker_->FindSystemClass(soa.Self(), "[J"), long_array->GetClass());
   uintptr_t data_offset = reinterpret_cast<uintptr_t>(long_array->GetData());
   EXPECT_TRUE(IsAligned<8>(data_offset));  // Longs require 8 byte alignment
 
   SirtRef<mirror::DoubleArray> double_array(soa.Self(), mirror::DoubleArray::Alloc(soa.Self(), 0));
-  EXPECT_EQ(class_linker_->FindSystemClass("[D"), double_array->GetClass());
+  EXPECT_EQ(class_linker_->FindSystemClass(soa.Self(), "[D"), double_array->GetClass());
   data_offset = reinterpret_cast<uintptr_t>(double_array->GetData());
   EXPECT_TRUE(IsAligned<8>(data_offset));  // Doubles require 8 byte alignment
 
   SirtRef<mirror::IntArray> int_array(soa.Self(), mirror::IntArray::Alloc(soa.Self(), 0));
-  EXPECT_EQ(class_linker_->FindSystemClass("[I"), int_array->GetClass());
+  EXPECT_EQ(class_linker_->FindSystemClass(soa.Self(), "[I"), int_array->GetClass());
   data_offset = reinterpret_cast<uintptr_t>(int_array->GetData());
   EXPECT_TRUE(IsAligned<4>(data_offset));  // Ints require 4 byte alignment
 
   SirtRef<mirror::CharArray> char_array(soa.Self(), mirror::CharArray::Alloc(soa.Self(), 0));
-  EXPECT_EQ(class_linker_->FindSystemClass("[C"), char_array->GetClass());
+  EXPECT_EQ(class_linker_->FindSystemClass(soa.Self(), "[C"), char_array->GetClass());
   data_offset = reinterpret_cast<uintptr_t>(char_array->GetData());
   EXPECT_TRUE(IsAligned<2>(data_offset));  // Chars require 2 byte alignment
 
   SirtRef<mirror::ShortArray> short_array(soa.Self(), mirror::ShortArray::Alloc(soa.Self(), 0));
-  EXPECT_EQ(class_linker_->FindSystemClass("[S"), short_array->GetClass());
+  EXPECT_EQ(class_linker_->FindSystemClass(soa.Self(), "[S"), short_array->GetClass());
   data_offset = reinterpret_cast<uintptr_t>(short_array->GetData());
   EXPECT_TRUE(IsAligned<2>(data_offset));  // Shorts require 2 byte alignment
 
@@ -810,28 +816,28 @@
   ScopedObjectAccess soa(Thread::Current());
   SirtRef<mirror::ClassLoader> class_loader(soa.Self(), nullptr);
   mirror::Class* c;
-  c = class_linker_->FindClass("Ljava/lang/Boolean;", class_loader);
+  c = class_linker_->FindClass(soa.Self(), "Ljava/lang/Boolean;", class_loader);
   FieldHelper fh(c->GetIFields()->Get(0));
   EXPECT_STREQ("value", fh.GetName());
-  c = class_linker_->FindClass("Ljava/lang/Byte;", class_loader);
+  c = class_linker_->FindClass(soa.Self(), "Ljava/lang/Byte;", class_loader);
   fh.ChangeField(c->GetIFields()->Get(0));
   EXPECT_STREQ("value", fh.GetName());
-  c = class_linker_->FindClass("Ljava/lang/Character;", class_loader);
+  c = class_linker_->FindClass(soa.Self(), "Ljava/lang/Character;", class_loader);
   fh.ChangeField(c->GetIFields()->Get(0));
   EXPECT_STREQ("value", fh.GetName());
-  c = class_linker_->FindClass("Ljava/lang/Double;", class_loader);
+  c = class_linker_->FindClass(soa.Self(), "Ljava/lang/Double;", class_loader);
   fh.ChangeField(c->GetIFields()->Get(0));
   EXPECT_STREQ("value", fh.GetName());
-  c = class_linker_->FindClass("Ljava/lang/Float;", class_loader);
+  c = class_linker_->FindClass(soa.Self(), "Ljava/lang/Float;", class_loader);
   fh.ChangeField(c->GetIFields()->Get(0));
   EXPECT_STREQ("value", fh.GetName());
-  c = class_linker_->FindClass("Ljava/lang/Integer;", class_loader);
+  c = class_linker_->FindClass(soa.Self(), "Ljava/lang/Integer;", class_loader);
   fh.ChangeField(c->GetIFields()->Get(0));
   EXPECT_STREQ("value", fh.GetName());
-  c = class_linker_->FindClass("Ljava/lang/Long;", class_loader);
+  c = class_linker_->FindClass(soa.Self(), "Ljava/lang/Long;", class_loader);
   fh.ChangeField(c->GetIFields()->Get(0));
   EXPECT_STREQ("value", fh.GetName());
-  c = class_linker_->FindClass("Ljava/lang/Short;", class_loader);
+  c = class_linker_->FindClass(soa.Self(), "Ljava/lang/Short;", class_loader);
   fh.ChangeField(c->GetIFields()->Get(0));
   EXPECT_STREQ("value", fh.GetName());
 }
@@ -840,8 +846,8 @@
   ScopedObjectAccess soa(Thread::Current());
   SirtRef<mirror::ClassLoader> class_loader_1(soa.Self(), soa.Decode<mirror::ClassLoader*>(LoadDex("MyClass")));
   SirtRef<mirror::ClassLoader> class_loader_2(soa.Self(), soa.Decode<mirror::ClassLoader*>(LoadDex("MyClass")));
-  mirror::Class* MyClass_1 = class_linker_->FindClass("LMyClass;", class_loader_1);
-  mirror::Class* MyClass_2 = class_linker_->FindClass("LMyClass;", class_loader_2);
+  mirror::Class* MyClass_1 = class_linker_->FindClass(soa.Self(), "LMyClass;", class_loader_1);
+  mirror::Class* MyClass_2 = class_linker_->FindClass(soa.Self(), "LMyClass;", class_loader_2);
   EXPECT_TRUE(MyClass_1 != NULL);
   EXPECT_TRUE(MyClass_2 != NULL);
   EXPECT_NE(MyClass_1, MyClass_2);
@@ -849,8 +855,10 @@
 
 TEST_F(ClassLinkerTest, StaticFields) {
   ScopedObjectAccess soa(Thread::Current());
-  SirtRef<mirror::ClassLoader> class_loader(soa.Self(), soa.Decode<mirror::ClassLoader*>(LoadDex("Statics")));
-  SirtRef<mirror::Class> statics(soa.Self(), class_linker_->FindClass("LStatics;", class_loader));
+  SirtRef<mirror::ClassLoader> class_loader(soa.Self(),
+                                            soa.Decode<mirror::ClassLoader*>(LoadDex("Statics")));
+  SirtRef<mirror::Class> statics(soa.Self(), class_linker_->FindClass(soa.Self(), "LStatics;",
+                                                                      class_loader));
   class_linker_->EnsureInitialized(statics, true, true);
 
   // Static final primitives that are initialized by a compile-time constant
@@ -933,11 +941,11 @@
 TEST_F(ClassLinkerTest, Interfaces) {
   ScopedObjectAccess soa(Thread::Current());
   SirtRef<mirror::ClassLoader> class_loader(soa.Self(), soa.Decode<mirror::ClassLoader*>(LoadDex("Interfaces")));
-  mirror::Class* I = class_linker_->FindClass("LInterfaces$I;", class_loader);
-  mirror::Class* J = class_linker_->FindClass("LInterfaces$J;", class_loader);
-  mirror::Class* K = class_linker_->FindClass("LInterfaces$K;", class_loader);
-  mirror::Class* A = class_linker_->FindClass("LInterfaces$A;", class_loader);
-  mirror::Class* B = class_linker_->FindClass("LInterfaces$B;", class_loader);
+  mirror::Class* I = class_linker_->FindClass(soa.Self(), "LInterfaces$I;", class_loader);
+  mirror::Class* J = class_linker_->FindClass(soa.Self(), "LInterfaces$J;", class_loader);
+  mirror::Class* K = class_linker_->FindClass(soa.Self(), "LInterfaces$K;", class_loader);
+  mirror::Class* A = class_linker_->FindClass(soa.Self(), "LInterfaces$A;", class_loader);
+  mirror::Class* B = class_linker_->FindClass(soa.Self(), "LInterfaces$B;", class_loader);
   EXPECT_TRUE(I->IsAssignableFrom(A));
   EXPECT_TRUE(J->IsAssignableFrom(A));
   EXPECT_TRUE(J->IsAssignableFrom(K));
@@ -996,7 +1004,7 @@
   SirtRef<mirror::ClassLoader> class_loader(soa.Self(), soa.Decode<mirror::ClassLoader*>(jclass_loader));
   const DexFile* dex_file = Runtime::Current()->GetCompileTimeClassPath(jclass_loader)[0];
   CHECK(dex_file != NULL);
-  mirror::Class* klass = class_linker_->FindClass("LStaticsFromCode;", class_loader);
+  mirror::Class* klass = class_linker_->FindClass(soa.Self(), "LStaticsFromCode;", class_loader);
   mirror::ArtMethod* clinit = klass->FindClassInitializer();
   mirror::ArtMethod* getS0 = klass->FindDirectMethod("getS0", "()Ljava/lang/Object;");
   const DexFile::StringId* string_id = dex_file->FindStringId("LStaticsFromCode;");
@@ -1017,32 +1025,32 @@
   mirror::Class* c;
 
   // Object has a finalize method, but we know it's empty.
-  c = class_linker_->FindSystemClass("Ljava/lang/Object;");
+  c = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;");
   EXPECT_FALSE(c->IsFinalizable());
 
   // Enum has a finalize method to prevent its subclasses from implementing one.
-  c = class_linker_->FindSystemClass("Ljava/lang/Enum;");
+  c = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Enum;");
   EXPECT_FALSE(c->IsFinalizable());
 
   // RoundingMode is an enum.
-  c = class_linker_->FindSystemClass("Ljava/math/RoundingMode;");
+  c = class_linker_->FindSystemClass(soa.Self(), "Ljava/math/RoundingMode;");
   EXPECT_FALSE(c->IsFinalizable());
 
   // RandomAccessFile extends Object and overrides finalize.
-  c = class_linker_->FindSystemClass("Ljava/io/RandomAccessFile;");
+  c = class_linker_->FindSystemClass(soa.Self(), "Ljava/io/RandomAccessFile;");
   EXPECT_TRUE(c->IsFinalizable());
 
   // FileInputStream is finalizable and extends InputStream which isn't.
-  c = class_linker_->FindSystemClass("Ljava/io/InputStream;");
+  c = class_linker_->FindSystemClass(soa.Self(), "Ljava/io/InputStream;");
   EXPECT_FALSE(c->IsFinalizable());
-  c = class_linker_->FindSystemClass("Ljava/io/FileInputStream;");
+  c = class_linker_->FindSystemClass(soa.Self(), "Ljava/io/FileInputStream;");
   EXPECT_TRUE(c->IsFinalizable());
 
   // ScheduledThreadPoolExecutor doesn't have a finalize method but
   // extends ThreadPoolExecutor which does.
-  c = class_linker_->FindSystemClass("Ljava/util/concurrent/ThreadPoolExecutor;");
+  c = class_linker_->FindSystemClass(soa.Self(), "Ljava/util/concurrent/ThreadPoolExecutor;");
   EXPECT_TRUE(c->IsFinalizable());
-  c = class_linker_->FindSystemClass("Ljava/util/concurrent/ScheduledThreadPoolExecutor;");
+  c = class_linker_->FindSystemClass(soa.Self(), "Ljava/util/concurrent/ScheduledThreadPoolExecutor;");
   EXPECT_TRUE(c->IsFinalizable());
 }
 
diff --git a/runtime/common_test.h b/runtime/common_test.h
index 7f9b6b1..9eaec46 100644
--- a/runtime/common_test.h
+++ b/runtime/common_test.h
@@ -28,7 +28,9 @@
 #include "../compiler/compiler_backend.h"
 #include "../compiler/dex/quick/dex_file_to_method_inliner_map.h"
 #include "../compiler/dex/verification_results.h"
+#include "../compiler/driver/compiler_callbacks_impl.h"
 #include "../compiler/driver/compiler_driver.h"
+#include "../compiler/driver/compiler_options.h"
 #include "base/macros.h"
 #include "base/stl_util.h"
 #include "base/stringprintf.h"
@@ -418,8 +420,9 @@
   void MakeExecutable(mirror::ClassLoader* class_loader, const char* class_name)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     std::string class_descriptor(DotToDescriptor(class_name));
-    SirtRef<mirror::ClassLoader> loader(Thread::Current(), class_loader);
-    mirror::Class* klass = class_linker_->FindClass(class_descriptor.c_str(), loader);
+    Thread* self = Thread::Current();
+    SirtRef<mirror::ClassLoader> loader(self, class_loader);
+    mirror::Class* klass = class_linker_->FindClass(self, class_descriptor.c_str(), loader);
     CHECK(klass != NULL) << "Class not found " << class_name;
     for (size_t i = 0; i < klass->NumDirectMethods(); i++) {
       MakeExecutable(klass->GetDirectMethod(i));
@@ -458,11 +461,13 @@
         ? CompilerBackend::kPortable
         : CompilerBackend::kQuick;
 
-    verification_results_.reset(new VerificationResults);
+    compiler_options_.reset(new CompilerOptions);
+    verification_results_.reset(new VerificationResults(compiler_options_.get()));
     method_inliner_map_.reset(new DexFileToMethodInlinerMap);
-    callbacks_.Reset(verification_results_.get(), method_inliner_map_.get());
+    callbacks_.reset(new CompilerCallbacksImpl(verification_results_.get(),
+                                               method_inliner_map_.get()));
     Runtime::Options options;
-    options.push_back(std::make_pair("compilercallbacks", static_cast<CompilerCallbacks*>(&callbacks_)));
+    options.push_back(std::make_pair("compilercallbacks", callbacks_.get()));
     options.push_back(std::make_pair("bootclasspath", &boot_class_path_));
     options.push_back(std::make_pair("-Xcheck:jni", reinterpret_cast<void*>(NULL)));
     options.push_back(std::make_pair(min_heap_string.c_str(), reinterpret_cast<void*>(NULL)));
@@ -472,8 +477,8 @@
       return;
     }
     runtime_.reset(Runtime::Current());
-    // Runtime::Create acquired the mutator_lock_ that is normally given away when we Runtime::Start,
-    // give it away now and then switch to a more managable ScopedObjectAccess.
+    // Runtime::Create acquired the mutator_lock_ that is normally given away when we
+    // Runtime::Start, give it away now and then switch to a more managable ScopedObjectAccess.
     Thread::Current()->TransitionFromRunnableToSuspended(kNative);
     {
       ScopedObjectAccess soa(Thread::Current());
@@ -512,7 +517,8 @@
       }
       class_linker_->FixupDexCaches(runtime_->GetResolutionMethod());
       timer_.reset(new CumulativeLogger("Compilation times"));
-      compiler_driver_.reset(new CompilerDriver(verification_results_.get(),
+      compiler_driver_.reset(new CompilerDriver(compiler_options_.get(),
+                                                verification_results_.get(),
                                                 method_inliner_map_.get(),
                                                 compiler_backend, instruction_set,
                                                 instruction_set_features,
@@ -563,9 +569,10 @@
 
     compiler_driver_.reset();
     timer_.reset();
-    callbacks_.Reset(nullptr, nullptr);
+    callbacks_.reset();
     method_inliner_map_.reset();
     verification_results_.reset();
+    compiler_options_.reset();
     STLDeleteElements(&opened_dex_files_);
 
     Runtime::Current()->GetHeap()->VerifyHeap();  // Check for heap corruption after the test
@@ -632,8 +639,9 @@
   void CompileClass(mirror::ClassLoader* class_loader, const char* class_name)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     std::string class_descriptor(DotToDescriptor(class_name));
-    SirtRef<mirror::ClassLoader> loader(Thread::Current(), class_loader);
-    mirror::Class* klass = class_linker_->FindClass(class_descriptor.c_str(), loader);
+    Thread* self = Thread::Current();
+    SirtRef<mirror::ClassLoader> loader(self, class_loader);
+    mirror::Class* klass = class_linker_->FindClass(self, class_descriptor.c_str(), loader);
     CHECK(klass != NULL) << "Class not found " << class_name;
     for (size_t i = 0; i < klass->NumDirectMethods(); i++) {
       CompileMethod(klass->GetDirectMethod(i));
@@ -656,7 +664,8 @@
                            const char* method_name, const char* signature)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     std::string class_descriptor(DotToDescriptor(class_name));
-    mirror::Class* klass = class_linker_->FindClass(class_descriptor.c_str(), class_loader);
+    Thread* self = Thread::Current();
+    mirror::Class* klass = class_linker_->FindClass(self, class_descriptor.c_str(), class_loader);
     CHECK(klass != NULL) << "Class not found " << class_name;
     mirror::ArtMethod* method = klass->FindDirectMethod(method_name, signature);
     CHECK(method != NULL) << "Direct method not found: "
@@ -668,7 +677,8 @@
                             const char* method_name, const char* signature)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     std::string class_descriptor(DotToDescriptor(class_name));
-    mirror::Class* klass = class_linker_->FindClass(class_descriptor.c_str(), class_loader);
+    Thread* self = Thread::Current();
+    mirror::Class* klass = class_linker_->FindClass(self, class_descriptor.c_str(), class_loader);
     CHECK(klass != NULL) << "Class not found " << class_name;
     mirror::ArtMethod* method = klass->FindVirtualMethod(method_name, signature);
     CHECK(method != NULL) << "Virtual method not found: "
@@ -693,36 +703,6 @@
     image_reservation_.reset();
   }
 
-  class TestCompilerCallbacks : public CompilerCallbacks {
-   public:
-    TestCompilerCallbacks() : verification_results_(nullptr), method_inliner_map_(nullptr) {}
-
-    void Reset(VerificationResults* verification_results,
-               DexFileToMethodInlinerMap* method_inliner_map) {
-        verification_results_ = verification_results;
-        method_inliner_map_ = method_inliner_map;
-    }
-
-    virtual bool MethodVerified(verifier::MethodVerifier* verifier)
-        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-      CHECK(verification_results_);
-      bool result = verification_results_->ProcessVerifiedMethod(verifier);
-      if (result && method_inliner_map_ != nullptr) {
-        MethodReference ref = verifier->GetMethodReference();
-        method_inliner_map_->GetMethodInliner(ref.dex_file)
-            ->AnalyseMethodCode(verifier);
-      }
-      return result;
-    }
-    virtual void ClassRejected(ClassReference ref) {
-      verification_results_->AddRejectedClass(ref);
-    }
-
-   private:
-    VerificationResults* verification_results_;
-    DexFileToMethodInlinerMap* method_inliner_map_;
-  };
-
   std::string android_data_;
   std::string dalvik_cache_;
   const DexFile* java_lang_dex_file_;  // owned by runtime_
@@ -730,9 +710,10 @@
   UniquePtr<Runtime> runtime_;
   // Owned by the runtime
   ClassLinker* class_linker_;
+  UniquePtr<CompilerOptions> compiler_options_;
   UniquePtr<VerificationResults> verification_results_;
   UniquePtr<DexFileToMethodInlinerMap> method_inliner_map_;
-  TestCompilerCallbacks callbacks_;
+  UniquePtr<CompilerCallbacksImpl> callbacks_;
   UniquePtr<CompilerDriver> compiler_driver_;
   UniquePtr<CumulativeLogger> timer_;
 
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 3c238d6..9f09709 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -289,27 +289,39 @@
   return static_cast<JDWP::JdwpTag>(descriptor[0]);
 }
 
-static JDWP::JdwpTag TagFromClass(mirror::Class* c)
+static JDWP::JdwpTag TagFromClass(const ScopedObjectAccessUnchecked& soa, mirror::Class* c)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   CHECK(c != NULL);
   if (c->IsArrayClass()) {
     return JDWP::JT_ARRAY;
   }
-
-  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
   if (c->IsStringClass()) {
     return JDWP::JT_STRING;
-  } else if (c->IsClassClass()) {
-    return JDWP::JT_CLASS_OBJECT;
-  } else if (class_linker->FindSystemClass("Ljava/lang/Thread;")->IsAssignableFrom(c)) {
-    return JDWP::JT_THREAD;
-  } else if (class_linker->FindSystemClass("Ljava/lang/ThreadGroup;")->IsAssignableFrom(c)) {
-    return JDWP::JT_THREAD_GROUP;
-  } else if (class_linker->FindSystemClass("Ljava/lang/ClassLoader;")->IsAssignableFrom(c)) {
-    return JDWP::JT_CLASS_LOADER;
-  } else {
-    return JDWP::JT_OBJECT;
   }
+  if (c->IsClassClass()) {
+    return JDWP::JT_CLASS_OBJECT;
+  }
+  {
+    mirror::Class* thread_class = soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_Thread);
+    if (thread_class->IsAssignableFrom(c)) {
+      return JDWP::JT_THREAD;
+    }
+  }
+  {
+    mirror::Class* thread_group_class =
+        soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_ThreadGroup);
+    if (thread_group_class->IsAssignableFrom(c)) {
+      return JDWP::JT_THREAD_GROUP;
+    }
+  }
+  {
+    mirror::Class* class_loader_class =
+        soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_ClassLoader);
+    if (class_loader_class->IsAssignableFrom(c)) {
+      return JDWP::JT_CLASS_LOADER;
+    }
+  }
+  return JDWP::JT_OBJECT;
 }
 
 /*
@@ -320,9 +332,9 @@
  *
  * Null objects are tagged JT_OBJECT.
  */
-static JDWP::JdwpTag TagFromObject(mirror::Object* o)
+static JDWP::JdwpTag TagFromObject(const ScopedObjectAccessUnchecked& soa, mirror::Object* o)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  return (o == NULL) ? JDWP::JT_OBJECT : TagFromClass(o->GetClass());
+  return (o == NULL) ? JDWP::JT_OBJECT : TagFromClass(soa, o->GetClass());
 }
 
 static bool IsPrimitiveTag(JDWP::JdwpTag tag) {
@@ -587,20 +599,24 @@
   runtime->GetThreadList()->SuspendAll();
   Thread* self = Thread::Current();
   ThreadState old_state = self->SetStateUnsafe(kRunnable);
-  {
-    // Since we're going to disable deoptimization, we clear the deoptimization requests queue.
-    // This prevent 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_);
-    gDeoptimizationRequests.clear();
+
+  // Debugger may not be active at this point.
+  if (gDebuggerActive) {
+    {
+      // 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_);
+      gDeoptimizationRequests.clear();
+    }
+    runtime->GetInstrumentation()->RemoveListener(&gDebugInstrumentationListener,
+                                                  instrumentation::Instrumentation::kMethodEntered |
+                                                  instrumentation::Instrumentation::kMethodExited |
+                                                  instrumentation::Instrumentation::kDexPcMoved |
+                                                  instrumentation::Instrumentation::kExceptionCaught);
+    runtime->GetInstrumentation()->DisableDeoptimization();
+    gDebuggerActive = false;
   }
-  runtime->GetInstrumentation()->DisableDeoptimization();
-  runtime->GetInstrumentation()->RemoveListener(&gDebugInstrumentationListener,
-                                                instrumentation::Instrumentation::kMethodEntered |
-                                                instrumentation::Instrumentation::kMethodExited |
-                                                instrumentation::Instrumentation::kDexPcMoved |
-                                                instrumentation::Instrumentation::kExceptionCaught);
-  gDebuggerActive = false;
   gRegistry->Clear();
   gDebuggerConnected = false;
   CHECK_EQ(self->SetStateUnsafe(old_state), kRunnable);
@@ -1011,11 +1027,12 @@
 }
 
 JDWP::JdwpError Dbg::GetObjectTag(JDWP::ObjectId object_id, uint8_t& tag) {
+  ScopedObjectAccessUnchecked soa(Thread::Current());
   mirror::Object* o = gRegistry->Get<mirror::Object*>(object_id);
   if (o == ObjectRegistry::kInvalidObject) {
     return JDWP::ERR_INVALID_OBJECT;
   }
-  tag = TagFromObject(o);
+  tag = TagFromObject(soa, o);
   return JDWP::ERR_NONE;
 }
 
@@ -1062,7 +1079,7 @@
 JDWP::JdwpError Dbg::OutputArray(JDWP::ObjectId array_id, int offset, int count, JDWP::ExpandBuf* pReply) {
   JDWP::JdwpError status;
   mirror::Array* a = DecodeArray(array_id, status);
-  if (a == NULL) {
+  if (a == nullptr) {
     return status;
   }
 
@@ -1093,10 +1110,12 @@
       memcpy(dst, &src[offset * width], count * width);
     }
   } else {
+    ScopedObjectAccessUnchecked soa(Thread::Current());
     mirror::ObjectArray<mirror::Object>* oa = a->AsObjectArray<mirror::Object>();
     for (int i = 0; i < count; ++i) {
       mirror::Object* element = oa->Get(offset + i);
-      JDWP::JdwpTag specific_tag = (element != NULL) ? TagFromObject(element) : tag;
+      JDWP::JdwpTag specific_tag = (element != nullptr) ? TagFromObject(soa, element)
+                                                        : tag;
       expandBufAdd1(pReply, specific_tag);
       expandBufAddObjectId(pReply, gRegistry->Add(element));
     }
@@ -1639,8 +1658,9 @@
       CHECK_EQ(tag, JDWP::JT_VOID);
     }
   } else {
+    ScopedObjectAccessUnchecked soa(Thread::Current());
     mirror::Object* value = return_value->GetL();
-    expandBufAdd1(pReply, TagFromObject(value));
+    expandBufAdd1(pReply, TagFromObject(soa, value));
     expandBufAddObjectId(pReply, gRegistry->Add(value));
   }
 }
@@ -1672,7 +1692,7 @@
   if (thread_object == ObjectRegistry::kInvalidObject) {
     return JDWP::ERR_INVALID_OBJECT;
   }
-
+  const char* old_cause = soa.Self()->StartAssertNoThreadSuspension("Debugger: GetThreadGroup");
   // Okay, so it's an object, but is it actually a thread?
   MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
   Thread* thread;
@@ -1685,14 +1705,14 @@
   if (error != JDWP::ERR_NONE) {
     return error;
   }
-
-  mirror::Class* c = Runtime::Current()->GetClassLinker()->FindSystemClass("Ljava/lang/Thread;");
-  CHECK(c != NULL);
+  mirror::Class* c = soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_Thread);
+  CHECK(c != nullptr);
   mirror::ArtField* f = c->FindInstanceField("group", "Ljava/lang/ThreadGroup;");
   CHECK(f != NULL);
   mirror::Object* group = f->GetObject(thread_object);
   CHECK(group != NULL);
   JDWP::ObjectId thread_group_id = gRegistry->Add(group);
+  soa.Self()->EndAssertNoThreadSuspension(old_cause);
 
   expandBufAddObjectId(pReply, thread_group_id);
   return JDWP::ERR_NONE;
@@ -1701,25 +1721,28 @@
 std::string Dbg::GetThreadGroupName(JDWP::ObjectId thread_group_id) {
   ScopedObjectAccess soa(Thread::Current());
   mirror::Object* thread_group = gRegistry->Get<mirror::Object*>(thread_group_id);
-  CHECK(thread_group != NULL);
-
-  mirror::Class* c = Runtime::Current()->GetClassLinker()->FindSystemClass("Ljava/lang/ThreadGroup;");
-  CHECK(c != NULL);
+  CHECK(thread_group != nullptr);
+  const char* old_cause = soa.Self()->StartAssertNoThreadSuspension("Debugger: GetThreadGroupName");
+  mirror::Class* c = soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_ThreadGroup);
+  CHECK(c != nullptr);
   mirror::ArtField* f = c->FindInstanceField("name", "Ljava/lang/String;");
   CHECK(f != NULL);
   mirror::String* s = reinterpret_cast<mirror::String*>(f->GetObject(thread_group));
+  soa.Self()->EndAssertNoThreadSuspension(old_cause);
   return s->ToModifiedUtf8();
 }
 
 JDWP::ObjectId Dbg::GetThreadGroupParent(JDWP::ObjectId thread_group_id) {
+  ScopedObjectAccessUnchecked soa(Thread::Current());
   mirror::Object* thread_group = gRegistry->Get<mirror::Object*>(thread_group_id);
-  CHECK(thread_group != NULL);
-
-  mirror::Class* c = Runtime::Current()->GetClassLinker()->FindSystemClass("Ljava/lang/ThreadGroup;");
-  CHECK(c != NULL);
+  CHECK(thread_group != nullptr);
+  const char* old_cause = soa.Self()->StartAssertNoThreadSuspension("Debugger: GetThreadGroupParent");
+  mirror::Class* c = soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_ThreadGroup);
+  CHECK(c != nullptr);
   mirror::ArtField* f = c->FindInstanceField("parent", "Ljava/lang/ThreadGroup;");
   CHECK(f != NULL);
   mirror::Object* parent = f->GetObject(thread_group);
+  soa.Self()->EndAssertNoThreadSuspension(old_cause);
   return gRegistry->Add(parent);
 }
 
@@ -2090,13 +2113,13 @@
   return JDWP::ERR_NONE;
 }
 
-void Dbg::GetLocalValue(JDWP::ObjectId thread_id, JDWP::FrameId frame_id, int slot, JDWP::JdwpTag tag,
-                        uint8_t* buf, size_t width) {
+void Dbg::GetLocalValue(JDWP::ObjectId thread_id, JDWP::FrameId frame_id, int slot,
+                        JDWP::JdwpTag tag, uint8_t* buf, size_t width) {
   struct GetLocalVisitor : public StackVisitor {
-    GetLocalVisitor(Thread* thread, Context* context, JDWP::FrameId frame_id, int slot,
-                    JDWP::JdwpTag tag, uint8_t* buf, size_t width)
+    GetLocalVisitor(const ScopedObjectAccessUnchecked& soa, Thread* thread, Context* context,
+                    JDWP::FrameId frame_id, int slot, JDWP::JdwpTag tag, uint8_t* buf, size_t width)
         SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-        : StackVisitor(thread, context), frame_id_(frame_id), slot_(slot), tag_(tag),
+        : StackVisitor(thread, context), soa_(soa), frame_id_(frame_id), slot_(slot), tag_(tag),
           buf_(buf), width_(width) {}
 
     // TODO: Enable annotalysis. We know lock is held in constructor, but abstraction confuses
@@ -2175,7 +2198,7 @@
           if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(o)) {
             LOG(FATAL) << "Register " << reg << " expected to hold object: " << o;
           }
-          tag_ = TagFromObject(o);
+          tag_ = TagFromObject(soa_, o);
           JDWP::SetObjectId(buf_+1, gRegistry->Add(o));
         }
         break;
@@ -2208,7 +2231,7 @@
       JDWP::Set1(buf_, tag_);
       return false;
     }
-
+    const ScopedObjectAccessUnchecked& soa_;
     const JDWP::FrameId frame_id_;
     const int slot_;
     JDWP::JdwpTag tag_;
@@ -2224,7 +2247,7 @@
     return;
   }
   UniquePtr<Context> context(Context::Create());
-  GetLocalVisitor visitor(thread, context.get(), frame_id, slot, tag, buf, width);
+  GetLocalVisitor visitor(soa, thread, context.get(), frame_id, slot, tag, buf, width);
   visitor.WalkStack();
 }
 
@@ -3037,7 +3060,7 @@
     pReq->result_value.SetJ(0);
   } else if (pReq->result_tag == JDWP::JT_OBJECT) {
     /* if no exception thrown, examine object result more closely */
-    JDWP::JdwpTag new_tag = TagFromObject(pReq->result_value.GetL());
+    JDWP::JdwpTag new_tag = TagFromObject(soa, pReq->result_value.GetL());
     if (new_tag != pReq->result_tag) {
       VLOG(jdwp) << "  JDWP promoted result from " << pReq->result_tag << " to " << new_tag;
       pReq->result_tag = new_tag;
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index 5e2b9ff..a3415d3 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -721,7 +721,8 @@
   }
 
   if (it.HasNext()) {
-    LOG(ERROR) << "invalid stream - problem with parameter iterator in " << GetLocation();
+    LOG(ERROR) << "invalid stream - problem with parameter iterator in " << GetLocation()
+               << " for method " << PrettyMethod(method_idx, *this);
     return;
   }
 
diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h
index 8b94b5a..2c08351 100644
--- a/runtime/entrypoints/entrypoint_utils.h
+++ b/runtime/entrypoints/entrypoint_utils.h
@@ -18,7 +18,7 @@
 #define ART_RUNTIME_ENTRYPOINTS_ENTRYPOINT_UTILS_H_
 
 #include "base/macros.h"
-#include "class_linker.h"
+#include "class_linker-inl.h"
 #include "common_throws.h"
 #include "dex_file.h"
 #include "indirect_reference_table.h"
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index 012dabb..7cbeb29 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -29,16 +29,18 @@
 #include "object_utils.h"
 #include "runtime.h"
 
-
-
 namespace art {
 
 // Visits the arguments as saved to the stack by a Runtime::kRefAndArgs callee save frame.
 class QuickArgumentVisitor {
- public:
-// Offset to first (not the Method*) argument in a Runtime::kRefAndArgs callee save frame.
-// Size of Runtime::kRefAndArgs callee save frame.
-// Size of Method* and register parameters in out stack arguments.
+  // Size of each spilled GPR.
+#ifdef __LP64__
+  static constexpr size_t kBytesPerGprSpillLocation = 8;
+#else
+  static constexpr size_t kBytesPerGprSpillLocation = 4;
+#endif
+  // Number of bytes for each out register in the caller method's frame.
+  static constexpr size_t kBytesStackArgLocation = 4;
 #if defined(__arm__)
   // The callee save frame is pointed to by SP.
   // | argN       |  |
@@ -53,12 +55,19 @@
   // | R3         |    arg3
   // | R2         |    arg2
   // | R1         |    arg1
-  // | R0         |
+  // | R0         |    padding
   // | Method*    |  <- sp
-#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET 8
-#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__LR_OFFSET 44
-#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE 48
-#define QUICK_STACK_ARG_SKIP 16
+  static constexpr bool kSoftFloatAbi = true;  // This is a soft float ABI.
+  static constexpr size_t kNumGprArgs = 3;  // 3 arguments passed in GPRs.
+  static constexpr size_t kNumFprArgs = 0;  // 0 arguments passed in FPRs.
+  static constexpr size_t kBytesPerFprSpillLocation = 4;  // FPR spill size is 4 bytes.
+  static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset = 0;  // Offset of first FPR arg.
+  static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset = 8;  // Offset of first GPR arg.
+  static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_LrOffset = 44;  // Offset of return address.
+  static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_FrameSize = 48;  // Frame size.
+  static size_t GprIndexToGprOffset(uint32_t gpr_index) {
+    return gpr_index * kBytesPerGprSpillLocation;
+  }
 #elif defined(__mips__)
   // The callee save frame is pointed to by SP.
   // | argN       |  |
@@ -74,10 +83,17 @@
   // | A2         |    arg2
   // | A1         |    arg1
   // | A0/Method* |  <- sp
-#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET 4
-#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__LR_OFFSET 60
-#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE 64
-#define QUICK_STACK_ARG_SKIP 16
+  static constexpr bool kSoftFloatAbi = true;  // This is a soft float ABI.
+  static constexpr size_t kNumGprArgs = 3;  // 3 arguments passed in GPRs.
+  static constexpr size_t kNumFprArgs = 0;  // 0 arguments passed in FPRs.
+  static constexpr size_t kBytesPerFprSpillLocation = 4;  // FPR spill size is 4 bytes.
+  static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset = 0;  // Offset of first FPR arg.
+  static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset = 4;  // Offset of first GPR arg.
+  static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_LrOffset = 60;  // Offset of return address.
+  static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_FrameSize = 64;  // Frame size.
+  static size_t GprIndexToGprOffset(uint32_t gpr_index) {
+    return gpr_index * kBytesPerGprSpillLocation;
+  }
 #elif defined(__i386__)
   // The callee save frame is pointed to by SP.
   // | argN        |  |
@@ -93,49 +109,96 @@
   // | EDX         |    arg2
   // | ECX         |    arg1
   // | EAX/Method* |  <- sp
-#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET 4
-#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__LR_OFFSET 28
-#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE 32
-#define QUICK_STACK_ARG_SKIP 16
+  static constexpr bool kSoftFloatAbi = true;  // This is a soft float ABI.
+  static constexpr size_t kNumGprArgs = 3;  // 3 arguments passed in GPRs.
+  static constexpr size_t kNumFprArgs = 0;  // 0 arguments passed in FPRs.
+  static constexpr size_t kBytesPerFprSpillLocation = 8;  // FPR spill size is 8 bytes.
+  static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset = 0;  // Offset of first FPR arg.
+  static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset = 4;  // Offset of first GPR arg.
+  static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_LrOffset = 28;  // Offset of return address.
+  static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_FrameSize = 32;  // Frame size.
+  static size_t GprIndexToGprOffset(uint32_t gpr_index) {
+    return gpr_index * kBytesPerGprSpillLocation;
+  }
 #elif defined(__x86_64__)
-// TODO: implement and check these.
-#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET 8
-#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__LR_OFFSET 56
-#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE 64
-#define QUICK_STACK_ARG_SKIP 32
+  // The callee save frame is pointed to by SP.
+  // | argN            |  |
+  // | ...             |  |
+  // | reg. arg spills |  |  Caller's frame
+  // | Method*         | ---
+  // | Return          |
+  // | R15             |    callee save
+  // | R14             |    callee save
+  // | R13             |    callee save
+  // | R12             |    callee save
+  // | R9              |    arg5
+  // | R8              |    arg4
+  // | RSI/R6          |    arg1
+  // | RBP/R5          |    callee save
+  // | RBX/R3          |    callee save
+  // | RDX/R2          |    arg2
+  // | RCX/R1          |    arg3
+  // | XMM7            |    float arg 8
+  // | XMM6            |    float arg 7
+  // | XMM5            |    float arg 6
+  // | XMM4            |    float arg 5
+  // | XMM3            |    float arg 4
+  // | XMM2            |    float arg 3
+  // | XMM1            |    float arg 2
+  // | XMM0            |    float arg 1
+  // | Padding         |
+  // | RDI/Method*     |  <- sp
+  static constexpr bool kSoftFloatAbi = false;  // This is a hard float ABI.
+  static constexpr size_t kNumGprArgs = 5;  // 3 arguments passed in GPRs.
+  static constexpr size_t kNumFprArgs = 8;  // 0 arguments passed in FPRs.
+  static constexpr size_t kBytesPerFprSpillLocation = 8;  // FPR spill size is 8 bytes.
+  static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset = 16;  // Offset of first FPR arg.
+  static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset = 80;  // Offset of first GPR arg.
+  static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_LrOffset = 168;  // Offset of return address.
+  static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_FrameSize = 176;  // Frame size.
+  static size_t GprIndexToGprOffset(uint32_t gpr_index) {
+    switch (gpr_index) {
+      case 0: return (4 * kBytesPerGprSpillLocation);
+      case 1: return (1 * kBytesPerGprSpillLocation);
+      case 2: return (0 * kBytesPerGprSpillLocation);
+      case 3: return (5 * kBytesPerGprSpillLocation);
+      case 4: return (6 * kBytesPerGprSpillLocation);
+      default:
+        LOG(FATAL) << "Unexpected GPR index: " << gpr_index;
+        return 0;
+    }
+  }
 #else
 #error "Unsupported architecture"
-#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET 0
-#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__LR_OFFSET 0
-#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE 0
-#define QUICK_STACK_ARG_SKIP 0
 #endif
 
-  static mirror::ArtMethod* GetCallingMethod(mirror::ArtMethod** sp) {
-    byte* previous_sp = reinterpret_cast<byte*>(sp) +
-        QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE;
+ public:
+  static mirror::ArtMethod* GetCallingMethod(mirror::ArtMethod** sp)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    DCHECK((*sp)->IsCalleeSaveMethod());
+    byte* previous_sp = reinterpret_cast<byte*>(sp) + kQuickCalleeSaveFrame_RefAndArgs_FrameSize;
     return *reinterpret_cast<mirror::ArtMethod**>(previous_sp);
   }
 
-  static uintptr_t GetCallingPc(mirror::ArtMethod** sp) {
-    byte* lr = reinterpret_cast<byte*>(sp) + QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__LR_OFFSET;
+  // For the given quick ref and args quick frame, return the caller's PC.
+  static uintptr_t GetCallingPc(mirror::ArtMethod** sp)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    DCHECK((*sp)->IsCalleeSaveMethod());
+    byte* lr = reinterpret_cast<byte*>(sp) + kQuickCalleeSaveFrame_RefAndArgs_LrOffset;
     return *reinterpret_cast<uintptr_t*>(lr);
   }
 
   QuickArgumentVisitor(mirror::ArtMethod** sp, bool is_static,
                        const char* shorty, uint32_t shorty_len)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) :
-    is_static_(is_static), shorty_(shorty), shorty_len_(shorty_len),
-    args_in_regs_(ComputeArgsInRegs(is_static, shorty, shorty_len)),
-    num_params_((is_static ? 0 : 1) + shorty_len - 1),  // +1 for this, -1 for return type
-    reg_args_(reinterpret_cast<byte*>(sp) + QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET),
-    stack_args_(reinterpret_cast<byte*>(sp) + QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE
-                + QUICK_STACK_ARG_SKIP),
-    cur_args_(reg_args_),
-    cur_arg_index_(0),
-    param_index_(0),
-    is_split_long_or_double_(false) {
-    DCHECK_EQ(static_cast<size_t>(QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE),
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) :
+      is_static_(is_static), shorty_(shorty), shorty_len_(shorty_len),
+      gpr_args_(reinterpret_cast<byte*>(sp) + kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset),
+      fpr_args_(reinterpret_cast<byte*>(sp) + kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset),
+      stack_args_(reinterpret_cast<byte*>(sp) + kQuickCalleeSaveFrame_RefAndArgs_FrameSize
+                  + StackArgumentStartFromShorty(is_static, shorty, shorty_len)),
+      gpr_index_(0), fpr_index_(0), stack_index_(0), cur_type_(Primitive::kPrimVoid),
+      is_split_long_or_double_(false) {
+    DCHECK_EQ(kQuickCalleeSaveFrame_RefAndArgs_FrameSize,
               Runtime::Current()->GetCalleeSaveMethod(Runtime::kRefsAndArgs)->GetFrameSizeInBytes());
   }
 
@@ -143,30 +206,38 @@
 
   virtual void Visit() = 0;
 
-  Primitive::Type GetParamPrimitiveType() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    size_t index = param_index_;
-    if (is_static_) {
-      index++;  // 0th argument must skip return value at start of the shorty
-    } else if (index == 0) {
-      return Primitive::kPrimNot;
-    }
-    CHECK_LT(index, shorty_len_);
-    return Primitive::GetType(shorty_[index]);
+  Primitive::Type GetParamPrimitiveType() const {
+    return cur_type_;
   }
 
   byte* GetParamAddress() const {
-    return cur_args_ + (cur_arg_index_ * kPointerSize);
+    if (!kSoftFloatAbi) {
+      Primitive::Type type = GetParamPrimitiveType();
+      if (UNLIKELY((type == Primitive::kPrimDouble) || (type == Primitive::kPrimFloat))) {
+        if ((kNumFprArgs != 0) && (fpr_index_ + 1 < kNumFprArgs + 1)) {
+          return fpr_args_ + (fpr_index_ * kBytesPerFprSpillLocation);
+        }
+      }
+    }
+    if (gpr_index_ < kNumGprArgs) {
+      return gpr_args_ + GprIndexToGprOffset(gpr_index_);
+    }
+    return stack_args_ + (stack_index_ * kBytesStackArgLocation);
   }
 
   bool IsSplitLongOrDouble() const {
-    return is_split_long_or_double_;
+    if ((kBytesPerGprSpillLocation == 4) || (kBytesPerFprSpillLocation == 4)) {
+      return is_split_long_or_double_;
+    } else {
+      return false;  // An optimization for when GPR and FPRs are 64bit.
+    }
   }
 
-  bool IsParamAReference() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  bool IsParamAReference() const {
     return GetParamPrimitiveType() == Primitive::kPrimNot;
   }
 
-  bool IsParamALongOrDouble() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  bool IsParamALongOrDouble() const {
     Primitive::Type type = GetParamPrimitiveType();
     return type == Primitive::kPrimLong || type == Primitive::kPrimDouble;
   }
@@ -179,51 +250,179 @@
   }
 
   void VisitArguments() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    for (cur_arg_index_ = 0;  cur_arg_index_ < args_in_regs_ && param_index_ < num_params_; ) {
-      is_split_long_or_double_ = (cur_arg_index_ == 2) && IsParamALongOrDouble();
+    gpr_index_ = 0;
+    fpr_index_ = 0;
+    stack_index_ = 0;
+    if (!is_static_) {  // Handle this.
+      cur_type_ = Primitive::kPrimNot;
+      is_split_long_or_double_ = false;
       Visit();
-      cur_arg_index_ += (IsParamALongOrDouble() ? 2 : 1);
-      param_index_++;
+      if (kNumGprArgs > 0) {
+        gpr_index_++;
+      } else {
+        stack_index_++;
+      }
     }
-    cur_args_ = stack_args_;
-    cur_arg_index_ = is_split_long_or_double_ ? 1 : 0;
-    is_split_long_or_double_ = false;
-    while (param_index_ < num_params_) {
-      Visit();
-      cur_arg_index_ += (IsParamALongOrDouble() ? 2 : 1);
-      param_index_++;
+    for (uint32_t shorty_index = 1; shorty_index < shorty_len_; ++shorty_index) {
+      cur_type_ = Primitive::GetType(shorty_[shorty_index]);
+      switch (cur_type_) {
+        case Primitive::kPrimNot:
+        case Primitive::kPrimBoolean:
+        case Primitive::kPrimByte:
+        case Primitive::kPrimChar:
+        case Primitive::kPrimShort:
+        case Primitive::kPrimInt:
+          is_split_long_or_double_ = false;
+          Visit();
+          if (gpr_index_ < kNumGprArgs) {
+            gpr_index_++;
+          } else {
+            stack_index_++;
+          }
+          break;
+        case Primitive::kPrimFloat:
+          is_split_long_or_double_ = false;
+          Visit();
+          if (kSoftFloatAbi) {
+            if (gpr_index_ < kNumGprArgs) {
+              gpr_index_++;
+            } else {
+              stack_index_++;
+            }
+          } else {
+            if ((kNumFprArgs != 0) && (fpr_index_ + 1 < kNumFprArgs + 1)) {
+              fpr_index_++;
+            } else {
+              stack_index_++;
+            }
+          }
+          break;
+        case Primitive::kPrimDouble:
+        case Primitive::kPrimLong:
+          if (kSoftFloatAbi || (cur_type_ == Primitive::kPrimLong)) {
+            is_split_long_or_double_ = (kBytesPerGprSpillLocation == 4) &&
+                ((gpr_index_ + 1) == kNumGprArgs);
+            Visit();
+            if (gpr_index_ < kNumGprArgs) {
+              gpr_index_++;
+              if (kBytesPerGprSpillLocation == 4) {
+                if (gpr_index_ < kNumGprArgs) {
+                  gpr_index_++;
+                } else {
+                  stack_index_++;
+                }
+              }
+            } else {
+              if (kBytesStackArgLocation == 4) {
+                stack_index_+= 2;
+              } else {
+                CHECK_EQ(kBytesStackArgLocation, 8U);
+                stack_index_++;
+              }
+            }
+          } else {
+            is_split_long_or_double_ = (kBytesPerFprSpillLocation == 4) &&
+                ((fpr_index_ + 1) == kNumFprArgs);
+            Visit();
+            if ((kNumFprArgs != 0) && (fpr_index_ + 1 < kNumFprArgs + 1)) {
+              fpr_index_++;
+              if (kBytesPerFprSpillLocation == 4) {
+                if ((kNumFprArgs != 0) && (fpr_index_ + 1 < kNumFprArgs + 1)) {
+                  fpr_index_++;
+                } else {
+                  stack_index_++;
+                }
+              }
+            } else {
+              if (kBytesStackArgLocation == 4) {
+                stack_index_+= 2;
+              } else {
+                CHECK_EQ(kBytesStackArgLocation, 8U);
+                stack_index_++;
+              }
+            }
+          }
+          break;
+        default:
+          LOG(FATAL) << "Unexpected type: " << cur_type_ << " in " << shorty_;
+      }
     }
   }
 
  private:
-  static size_t ComputeArgsInRegs(bool is_static, const char* shorty, uint32_t shorty_len)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    size_t args_in_regs = (is_static ? 0 : 1);
-    for (size_t i = 0; i < shorty_len; i++) {
-      char s = shorty[i];
-      if (s == 'J' || s == 'D') {
-        args_in_regs += 2;
-      } else {
-        args_in_regs++;
+  static size_t StackArgumentStartFromShorty(bool is_static, const char* shorty,
+                                             uint32_t shorty_len) {
+    if (kSoftFloatAbi) {
+      CHECK_EQ(kNumFprArgs, 0U);
+      return (kNumGprArgs * kBytesPerGprSpillLocation) + kBytesPerGprSpillLocation /* ArtMethod* */;
+    } else {
+      size_t offset = kBytesPerGprSpillLocation;  // Skip Method*.
+      size_t gprs_seen = 0;
+      size_t fprs_seen = 0;
+      if (!is_static && (gprs_seen < kNumGprArgs)) {
+        gprs_seen++;
+        offset += kBytesStackArgLocation;
       }
-      if (args_in_regs > 3) {
-        args_in_regs = 3;
-        break;
+      for (uint32_t i = 1; i < shorty_len; ++i) {
+        switch (shorty[i]) {
+          case 'Z':
+          case 'B':
+          case 'C':
+          case 'S':
+          case 'I':
+          case 'L':
+            if (gprs_seen < kNumGprArgs) {
+              gprs_seen++;
+              offset += kBytesStackArgLocation;
+            }
+            break;
+          case 'J':
+            if (gprs_seen < kNumGprArgs) {
+              gprs_seen++;
+              offset += 2 * kBytesStackArgLocation;
+              if (kBytesPerGprSpillLocation == 4) {
+                if (gprs_seen < kNumGprArgs) {
+                  gprs_seen++;
+                }
+              }
+            }
+            break;
+          case 'F':
+            if ((kNumFprArgs != 0) && (fprs_seen + 1 < kNumFprArgs + 1)) {
+              fprs_seen++;
+              offset += kBytesStackArgLocation;
+            }
+            break;
+          case 'D':
+            if ((kNumFprArgs != 0) && (fprs_seen + 1 < kNumFprArgs + 1)) {
+              fprs_seen++;
+              offset += 2 * kBytesStackArgLocation;
+              if (kBytesPerFprSpillLocation == 4) {
+                if ((kNumFprArgs != 0) && (fprs_seen + 1 < kNumFprArgs + 1)) {
+                  fprs_seen++;
+                }
+              }
+            }
+            break;
+          default:
+            LOG(FATAL) << "Unexpected shorty character: " << shorty[i] << " in " << shorty;
+        }
       }
+      return offset;
     }
-    return args_in_regs;
   }
 
   const bool is_static_;
   const char* const shorty_;
   const uint32_t shorty_len_;
-  const size_t args_in_regs_;
-  const size_t num_params_;
-  byte* const reg_args_;
-  byte* const stack_args_;
-  byte* cur_args_;
-  size_t cur_arg_index_;
-  size_t param_index_;
+  byte* const gpr_args_;  // Address of GPR arguments in callee save frame.
+  byte* const fpr_args_;  // Address of FPR arguments in callee save frame.
+  byte* const stack_args_;  // Address of stack arguments in caller's frame.
+  uint32_t gpr_index_;  // Index into spilled GPRs.
+  uint32_t fpr_index_;  // Index into spilled FPRs.
+  uint32_t stack_index_;  // Index into arguments on the stack.
+  // The current type of argument during VisitArguments.
+  Primitive::Type cur_type_;
   // Does a 64bit parameter straddle the register and stack arguments?
   bool is_split_long_or_double_;
 };
@@ -231,9 +430,8 @@
 // Visits arguments on the stack placing them into the shadow frame.
 class BuildQuickShadowFrameVisitor : public QuickArgumentVisitor {
  public:
-  BuildQuickShadowFrameVisitor(mirror::ArtMethod** sp,
-      bool is_static, const char* shorty,
-       uint32_t shorty_len, ShadowFrame& sf, size_t first_arg_reg) :
+  BuildQuickShadowFrameVisitor(mirror::ArtMethod** sp, bool is_static, const char* shorty,
+                               uint32_t shorty_len, ShadowFrame* sf, size_t first_arg_reg) :
     QuickArgumentVisitor(sp, is_static, shorty, shorty_len), sf_(sf), cur_reg_(first_arg_reg) {}
 
   virtual void Visit() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -242,14 +440,17 @@
       case Primitive::kPrimLong:  // Fall-through.
       case Primitive::kPrimDouble:
         if (IsSplitLongOrDouble()) {
-          sf_.SetVRegLong(cur_reg_, ReadSplitLongParam());
+          sf_->SetVRegLong(cur_reg_, ReadSplitLongParam());
         } else {
-          sf_.SetVRegLong(cur_reg_, *reinterpret_cast<jlong*>(GetParamAddress()));
+          sf_->SetVRegLong(cur_reg_, *reinterpret_cast<jlong*>(GetParamAddress()));
         }
         ++cur_reg_;
         break;
-      case Primitive::kPrimNot:
-        sf_.SetVRegReference(cur_reg_, *reinterpret_cast<mirror::Object**>(GetParamAddress()));
+      case Primitive::kPrimNot: {
+          StackReference<mirror::Object>* stack_ref =
+              reinterpret_cast<StackReference<mirror::Object>*>(GetParamAddress());
+          sf_->SetVRegReference(cur_reg_, stack_ref->AsMirrorPtr());
+        }
         break;
       case Primitive::kPrimBoolean:  // Fall-through.
       case Primitive::kPrimByte:     // Fall-through.
@@ -257,7 +458,7 @@
       case Primitive::kPrimShort:    // Fall-through.
       case Primitive::kPrimInt:      // Fall-through.
       case Primitive::kPrimFloat:
-        sf_.SetVReg(cur_reg_, *reinterpret_cast<jint*>(GetParamAddress()));
+        sf_->SetVReg(cur_reg_, *reinterpret_cast<jint*>(GetParamAddress()));
         break;
       case Primitive::kPrimVoid:
         LOG(FATAL) << "UNREACHABLE";
@@ -267,8 +468,8 @@
   }
 
  private:
-  ShadowFrame& sf_;
-  size_t cur_reg_;
+  ShadowFrame* const sf_;
+  uint32_t cur_reg_;
 
   DISALLOW_COPY_AND_ASSIGN(BuildQuickShadowFrameVisitor);
 };
@@ -293,8 +494,8 @@
                                                   method, 0, memory));
     size_t first_arg_reg = code_item->registers_size_ - code_item->ins_size_;
     BuildQuickShadowFrameVisitor shadow_frame_builder(sp, mh.IsStatic(), mh.GetShorty(),
-                                                 mh.GetShortyLength(),
-                                                 *shadow_frame, first_arg_reg);
+                                                      mh.GetShortyLength(),
+                                                      shadow_frame, first_arg_reg);
     shadow_frame_builder.VisitArguments();
     // Push a transition back into managed code onto the linked list in thread.
     ManagedStack fragment;
@@ -315,6 +516,7 @@
     JValue result = interpreter::EnterInterpreterFromStub(self, mh, code_item, *shadow_frame);
     // Pop transition.
     self->PopManagedStackFragment(fragment);
+    // No need to restore the args since the method has already been run by the interpreter.
     return result.GetJ();
   }
 }
@@ -333,8 +535,10 @@
     Primitive::Type type = GetParamPrimitiveType();
     switch (type) {
       case Primitive::kPrimNot: {
-        mirror::Object* obj = *reinterpret_cast<mirror::Object**>(GetParamAddress());
-        val.l = soa_->AddLocalReference<jobject>(obj);
+        StackReference<mirror::Object>* stack_ref =
+            reinterpret_cast<StackReference<mirror::Object>*>(GetParamAddress());
+        val.l = soa_->AddLocalReference<jobject>(stack_ref->AsMirrorPtr());
+        references_.push_back(std::make_pair(val.l, stack_ref));
         break;
       }
       case Primitive::kPrimLong:  // Fall-through.
@@ -351,7 +555,7 @@
       case Primitive::kPrimShort:    // Fall-through.
       case Primitive::kPrimInt:      // Fall-through.
       case Primitive::kPrimFloat:
-        val.i =  *reinterpret_cast<jint*>(GetParamAddress());
+        val.i = *reinterpret_cast<jint*>(GetParamAddress());
         break;
       case Primitive::kPrimVoid:
         LOG(FATAL) << "UNREACHABLE";
@@ -361,10 +565,18 @@
     args_->push_back(val);
   }
 
+  void FixupReferences() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    // Fixup any references which may have changed.
+    for (const auto& pair : references_) {
+      pair.second->Assign(soa_->Decode<mirror::Object*>(pair.first));
+    }
+  }
+
  private:
   ScopedObjectAccessUnchecked* soa_;
   std::vector<jvalue>* args_;
-
+  // References which we must update when exiting in case the GC moved the objects.
+  std::vector<std::pair<jobject, StackReference<mirror::Object>*> > references_;
   DISALLOW_COPY_AND_ASSIGN(BuildQuickArgumentVisitor);
 };
 
@@ -417,6 +629,8 @@
   self->EndAssertNoThreadSuspension(old_cause);
   JValue result = InvokeProxyInvocationHandler(soa, proxy_mh.GetShorty(),
                                                rcvr_jobj, interface_method_jobj, args);
+  // Restore references which might have moved.
+  local_ref_visitor.FixupReferences();
   return result.GetJ();
 }
 
@@ -430,23 +644,25 @@
 
   virtual void Visit() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     if (IsParamAReference()) {
-      mirror::Object** param_address = reinterpret_cast<mirror::Object**>(GetParamAddress());
+      StackReference<mirror::Object>* stack_ref =
+          reinterpret_cast<StackReference<mirror::Object>*>(GetParamAddress());
       jobject reference =
-          soa_->AddLocalReference<jobject>(*param_address);
-      references_.push_back(std::make_pair(reference, param_address));
+          soa_->AddLocalReference<jobject>(stack_ref->AsMirrorPtr());
+      references_.push_back(std::make_pair(reference, stack_ref));
     }
   }
 
   void FixupReferences() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     // Fixup any references which may have changed.
-    for (std::pair<jobject, mirror::Object**>& it : references_) {
-      *it.second = soa_->Decode<mirror::Object*>(it.first);
+    for (const auto& pair : references_) {
+      pair.second->Assign(soa_->Decode<mirror::Object*>(pair.first));
     }
   }
 
  private:
   ScopedObjectAccessUnchecked* soa_;
-  std::vector<std::pair<jobject, mirror::Object**> > references_;
+  // References which we must update when exiting in case the GC moved the objects.
+  std::vector<std::pair<jobject, StackReference<mirror::Object>*> > references_;
   DISALLOW_COPY_AND_ASSIGN(RememberForGcArgumentVisitor);
 };
 
diff --git a/runtime/exception_test.cc b/runtime/exception_test.cc
index f7b621f..910a817 100644
--- a/runtime/exception_test.cc
+++ b/runtime/exception_test.cc
@@ -28,6 +28,7 @@
 #include "sirt_ref.h"
 #include "thread.h"
 #include "UniquePtr.h"
+#include "vmap_table.h"
 
 namespace art {
 
@@ -39,7 +40,7 @@
     ScopedObjectAccess soa(Thread::Current());
     SirtRef<mirror::ClassLoader> class_loader(
         soa.Self(), soa.Decode<mirror::ClassLoader*>(LoadDex("ExceptionHandle")));
-    my_klass_ = class_linker_->FindClass("LExceptionHandle;", class_loader);
+    my_klass_ = class_linker_->FindClass(soa.Self(), "LExceptionHandle;", class_loader);
     ASSERT_TRUE(my_klass_ != NULL);
     SirtRef<mirror::Class> sirt_klass(soa.Self(), my_klass_);
     class_linker_->EnsureInitialized(sirt_klass, true, true);
@@ -66,7 +67,7 @@
     fake_mapping_data_.PushBackUnsigned(3 - 0);  // offset 3
     fake_mapping_data_.PushBackSigned(3 - 0);    // maps to dex offset 3
 
-    fake_vmap_table_data_.PushBackUnsigned(0);
+    fake_vmap_table_data_.PushBackUnsigned(0 + VmapTable::kEntryAdjustment);
 
     fake_gc_map_.push_back(0);  // 0 bytes to encode references and native pc offsets.
     fake_gc_map_.push_back(0);
diff --git a/runtime/gc/collector/mark_sweep-inl.h b/runtime/gc/collector/mark_sweep-inl.h
index d148ae5..4915532 100644
--- a/runtime/gc/collector/mark_sweep-inl.h
+++ b/runtime/gc/collector/mark_sweep-inl.h
@@ -30,19 +30,19 @@
 
 template <typename MarkVisitor>
 inline void MarkSweep::ScanObjectVisit(mirror::Object* obj, const MarkVisitor& visitor) {
-  DCHECK(obj != NULL);
   if (kIsDebugBuild && !IsMarked(obj)) {
     heap_->DumpSpaces();
     LOG(FATAL) << "Scanning unmarked object " << obj;
   }
+  // The GetClass verifies the object, don't need to reverify after.
   mirror::Class* klass = obj->GetClass();
-  DCHECK(klass != NULL);
+  // IsArrayClass verifies klass.
   if (UNLIKELY(klass->IsArrayClass())) {
     if (kCountScannedTypes) {
       ++array_count_;
     }
-    if (klass->IsObjectArrayClass()) {
-      VisitObjectArrayReferences(obj->AsObjectArray<mirror::Object>(), visitor);
+    if (klass->IsObjectArrayClass<kVerifyNone>()) {
+      VisitObjectArrayReferences(obj->AsObjectArray<mirror::Object, kVerifyNone>(), visitor);
     }
   } else if (UNLIKELY(klass == mirror::Class::GetJavaLangClass())) {
     if (kCountScannedTypes) {
@@ -54,7 +54,7 @@
       ++other_count_;
     }
     VisitOtherReferences(klass, obj, visitor);
-    if (UNLIKELY(klass->IsReferenceClass())) {
+    if (UNLIKELY(klass->IsReferenceClass<kVerifyNone>())) {
       DelayReferenceReferent(klass, obj);
     }
   }
@@ -65,24 +65,19 @@
                                              bool visit_class)
     SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_,
                           Locks::mutator_lock_) {
-  DCHECK(obj != NULL);
-  DCHECK(obj->GetClass() != NULL);
   mirror::Class* klass = obj->GetClass();
-  DCHECK(klass != NULL);
-  if (klass == mirror::Class::GetJavaLangClass()) {
-    DCHECK_EQ(klass->GetClass(), mirror::Class::GetJavaLangClass());
+  if (klass->IsArrayClass()) {
+    if (visit_class) {
+      visitor(obj, klass, mirror::Object::ClassOffset(), false);
+    }
+    if (klass->IsObjectArrayClass<kVerifyNone>()) {
+      VisitObjectArrayReferences(obj->AsObjectArray<mirror::Object, kVerifyNone>(), visitor);
+    }
+  } else if (klass == mirror::Class::GetJavaLangClass()) {
+    DCHECK_EQ(klass->GetClass<kVerifyNone>(), mirror::Class::GetJavaLangClass());
     VisitClassReferences(klass, obj, visitor);
   } else {
-    if (klass->IsArrayClass()) {
-      if (visit_class) {
-        visitor(obj, klass, mirror::Object::ClassOffset(), false);
-      }
-      if (klass->IsObjectArrayClass()) {
-        VisitObjectArrayReferences(obj->AsObjectArray<mirror::Object>(), visitor);
-      }
-    } else {
-      VisitOtherReferences(klass, obj, visitor);
-    }
+    VisitOtherReferences(klass, obj, visitor);
   }
 }
 
@@ -90,9 +85,7 @@
 inline void MarkSweep::VisitInstanceFieldsReferences(mirror::Class* klass, mirror::Object* obj,
                                                      const Visitor& visitor)
     SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_) {
-  DCHECK(obj != NULL);
-  DCHECK(klass != NULL);
-  VisitFieldsReferences(obj, klass->GetReferenceInstanceOffsets(), false, visitor);
+  VisitFieldsReferences(obj, klass->GetReferenceInstanceOffsets<kVerifyNone>(), false, visitor);
 }
 
 template <typename Visitor>
@@ -100,14 +93,13 @@
                                             const Visitor& visitor)
     SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_) {
   VisitInstanceFieldsReferences(klass, obj, visitor);
-  VisitStaticFieldsReferences(obj->AsClass(), visitor);
+  VisitStaticFieldsReferences(obj->AsClass<kVerifyNone>(), visitor);
 }
 
 template <typename Visitor>
 inline void MarkSweep::VisitStaticFieldsReferences(mirror::Class* klass, const Visitor& visitor)
     SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_) {
-  DCHECK(klass != NULL);
-  VisitFieldsReferences(klass, klass->GetReferenceStaticOffsets(), true, visitor);
+  VisitFieldsReferences(klass, klass->GetReferenceStaticOffsets<kVerifyNone>(), true, visitor);
 }
 
 template <typename Visitor>
@@ -118,7 +110,7 @@
     while (ref_offsets != 0) {
       size_t right_shift = CLZ(ref_offsets);
       MemberOffset field_offset = CLASS_OFFSET_FROM_CLZ(right_shift);
-      mirror::Object* ref = obj->GetFieldObject<mirror::Object>(field_offset, false);
+      mirror::Object* ref = obj->GetFieldObject<mirror::Object, kVerifyReads>(field_offset, false);
       visitor(obj, ref, field_offset, is_static);
       ref_offsets &= ~(CLASS_HIGH_BIT >> right_shift);
     }
@@ -127,7 +119,7 @@
     // walk up the class inheritance hierarchy and find reference
     // offsets the hard way. In the static case, just consider this
     // class.
-    for (mirror::Class* klass = is_static ? obj->AsClass() : obj->GetClass();
+    for (mirror::Class* klass = is_static ? obj->AsClass<kVerifyNone>() : obj->GetClass<kVerifyNone>();
          klass != nullptr;
          klass = is_static ? nullptr : klass->GetSuperClass()) {
       size_t num_reference_fields = (is_static
@@ -137,7 +129,7 @@
         mirror::ArtField* field = (is_static ? klass->GetStaticField(i)
                                              : klass->GetInstanceField(i));
         MemberOffset field_offset = field->GetOffset();
-        mirror::Object* ref = obj->GetFieldObject<mirror::Object>(field_offset, false);
+        mirror::Object* ref = obj->GetFieldObject<mirror::Object, kVerifyReads>(field_offset, false);
         visitor(obj, ref, field_offset, is_static);
       }
     }
diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc
index 006c271..8ca3892 100644
--- a/runtime/gc/collector/mark_sweep.cc
+++ b/runtime/gc/collector/mark_sweep.cc
@@ -62,29 +62,30 @@
 namespace collector {
 
 // Performance options.
-constexpr bool kUseRecursiveMark = false;
-constexpr bool kUseMarkStackPrefetch = true;
-constexpr size_t kSweepArrayChunkFreeSize = 1024;
+static constexpr bool kUseRecursiveMark = false;
+static constexpr bool kUseMarkStackPrefetch = true;
+static constexpr size_t kSweepArrayChunkFreeSize = 1024;
+static constexpr bool kPreCleanCards = true;
 
 // Parallelism options.
-constexpr bool kParallelCardScan = true;
-constexpr bool kParallelRecursiveMark = true;
+static constexpr bool kParallelCardScan = true;
+static constexpr bool kParallelRecursiveMark = true;
 // Don't attempt to parallelize mark stack processing unless the mark stack is at least n
 // elements. This is temporary until we reduce the overhead caused by allocating tasks, etc.. Not
 // having this can add overhead in ProcessReferences since we may end up doing many calls of
 // ProcessMarkStack with very small mark stacks.
-constexpr size_t kMinimumParallelMarkStackSize = 128;
-constexpr bool kParallelProcessMarkStack = true;
+static constexpr size_t kMinimumParallelMarkStackSize = 128;
+static constexpr bool kParallelProcessMarkStack = true;
 
 // Profiling and information flags.
-constexpr bool kCountClassesMarked = false;
-constexpr bool kProfileLargeObjects = false;
-constexpr bool kMeasureOverhead = false;
-constexpr bool kCountTasks = false;
-constexpr bool kCountJavaLangRefs = false;
+static constexpr bool kCountClassesMarked = false;
+static constexpr bool kProfileLargeObjects = false;
+static constexpr bool kMeasureOverhead = false;
+static constexpr bool kCountTasks = false;
+static constexpr bool kCountJavaLangRefs = false;
 
 // Turn off kCheckLocks when profiling the GC since it slows the GC down by up to 40%.
-constexpr bool kCheckLocks = kDebugLocking;
+static constexpr bool kCheckLocks = kDebugLocking;
 
 void MarkSweep::ImmuneSpace(space::ContinuousSpace* space) {
   // Bind live to mark bitmap if necessary.
@@ -179,11 +180,11 @@
   TimingLogger::ScopedSplit split("ProcessReferences", &timings_);
   WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
   GetHeap()->ProcessReferences(timings_, clear_soft_references_, &IsMarkedCallback,
-                               &RecursiveMarkObjectCallback, this);
+                               &MarkObjectCallback, &ProcessMarkStackPausedCallback, this);
 }
 
 bool MarkSweep::HandleDirtyObjectsPhase() {
-  TimingLogger::ScopedSplit split("HandleDirtyObjectsPhase", &timings_);
+  TimingLogger::ScopedSplit split("(Paused)HandleDirtyObjectsPhase", &timings_);
   Thread* self = Thread::Current();
   Locks::mutator_lock_->AssertExclusiveHeld(self);
 
@@ -209,7 +210,7 @@
     // Since SweepArray() above resets the (active) allocation
     // stack. Need to revoke the thread-local allocation stacks that
     // point into it.
-    GetHeap()->RevokeAllThreadLocalAllocationStacks(self);
+    RevokeAllThreadLocalAllocationStacks(self);
   }
 
   timings_.StartSplit("PreSweepingGcVerification");
@@ -232,6 +233,43 @@
   return is_concurrent_;
 }
 
+void MarkSweep::PreCleanCards() {
+  // Don't do this for non concurrent GCs since they don't have any dirty cards.
+  if (kPreCleanCards && IsConcurrent()) {
+    Thread* self = Thread::Current();
+    CHECK(!Locks::mutator_lock_->IsExclusiveHeld(self));
+    // Process dirty cards and add dirty cards to mod union tables, also ages cards.
+    heap_->ProcessCards(timings_);
+    // The checkpoint root marking is required to avoid a race condition which occurs if the
+    // following happens during a reference write:
+    // 1. mutator dirties the card (write barrier)
+    // 2. GC ages the card (the above ProcessCards call)
+    // 3. GC scans the object (the RecursiveMarkDirtyObjects call below)
+    // 4. mutator writes the value (corresponding to the write barrier in 1.)
+    // This causes the GC to age the card but not necessarily mark the reference which the mutator
+    // wrote into the object stored in the card.
+    // Having the checkpoint fixes this issue since it ensures that the card mark and the
+    // reference write are visible to the GC before the card is scanned (this is due to locks being
+    // acquired / released in the checkpoint code).
+    // The other roots are also marked to help reduce the pause.
+    MarkThreadRoots(self);
+    // TODO: Only mark the dirty roots.
+    MarkNonThreadRoots();
+    MarkConcurrentRoots();
+    // Process the newly aged cards.
+    RecursiveMarkDirtyObjects(false, accounting::CardTable::kCardDirty - 1);
+    // TODO: Empty allocation stack to reduce the number of objects we need to test / mark as live
+    // in the next GC.
+  }
+}
+
+void MarkSweep::RevokeAllThreadLocalAllocationStacks(Thread* self) {
+  if (kUseThreadLocalAllocationStack) {
+    Locks::mutator_lock_->AssertExclusiveHeld(self);
+    heap_->RevokeAllThreadLocalAllocationStacks(self);
+  }
+}
+
 void MarkSweep::MarkingPhase() {
   TimingLogger::ScopedSplit split("MarkingPhase", &timings_);
   Thread* self = Thread::Current();
@@ -251,9 +289,7 @@
   if (Locks::mutator_lock_->IsExclusiveHeld(self)) {
     // If we exclusively hold the mutator lock, all threads must be suspended.
     MarkRoots();
-    if (kUseThreadLocalAllocationStack) {
-      heap_->RevokeAllThreadLocalAllocationStacks(self);
-    }
+    RevokeAllThreadLocalAllocationStacks(self);
   } else {
     MarkThreadRoots(self);
     // At this point the live stack should no longer have any mutators which push into it.
@@ -263,6 +299,8 @@
   MarkConcurrentRoots();
   UpdateAndMarkModUnion();
   MarkReachableObjects();
+  // Pre-clean dirtied cards to reduce pauses.
+  PreCleanCards();
 }
 
 void MarkSweep::UpdateAndMarkModUnion() {
@@ -313,20 +351,24 @@
     TimingLogger::ScopedSplit split("UnMarkAllocStack", &timings_);
     WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
     accounting::ObjectStack* allocation_stack = GetHeap()->allocation_stack_.get();
-    // The allocation stack contains things allocated since the start of the GC. These may have been
-    // marked during this GC meaning they won't be eligible for reclaiming in the next sticky GC.
-    // Remove these objects from the mark bitmaps so that they will be eligible for sticky
-    // collection.
-    // There is a race here which is safely handled. Another thread such as the hprof could
-    // have flushed the alloc stack after we resumed the threads. This is safe however, since
-    // reseting the allocation stack zeros it out with madvise. This means that we will either
-    // read NULLs or attempt to unmark a newly allocated object which will not be marked in the
-    // first place.
-    mirror::Object** end = allocation_stack->End();
-    for (mirror::Object** it = allocation_stack->Begin(); it != end; ++it) {
-      const Object* obj = *it;
-      if (obj != NULL) {
-        UnMarkObjectNonNull(obj);
+    if (!kPreCleanCards) {
+      // The allocation stack contains things allocated since the start of the GC. These may have
+      // been marked during this GC meaning they won't be eligible for reclaiming in the next
+      // sticky GC. Unmark these objects so that they are eligible for reclaiming in the next
+      // sticky GC.
+      // There is a race here which is safely handled. Another thread such as the hprof could
+      // have flushed the alloc stack after we resumed the threads. This is safe however, since
+      // reseting the allocation stack zeros it out with madvise. This means that we will either
+      // read NULLs or attempt to unmark a newly allocated object which will not be marked in the
+      // first place.
+      // We can't do this if we pre-clean cards since we will unmark objects which are no longer on
+      // a dirty card since we aged cards during the pre-cleaning process.
+      mirror::Object** end = allocation_stack->End();
+      for (mirror::Object** it = allocation_stack->Begin(); it != end; ++it) {
+        const Object* obj = *it;
+        if (obj != nullptr) {
+          UnMarkObjectNonNull(obj);
+        }
       }
     }
   }
@@ -400,10 +442,9 @@
   }
 }
 
-mirror::Object* MarkSweep::RecursiveMarkObjectCallback(mirror::Object* obj, void* arg) {
+mirror::Object* MarkSweep::MarkObjectCallback(mirror::Object* obj, void* arg) {
   MarkSweep* mark_sweep = reinterpret_cast<MarkSweep*>(arg);
   mark_sweep->MarkObject(obj);
-  mark_sweep->ProcessMarkStack(true);
   return obj;
 }
 
@@ -534,25 +575,14 @@
 
 void MarkSweep::MarkRootParallelCallback(mirror::Object** root, void* arg, uint32_t /*thread_id*/,
                                          RootType /*root_type*/) {
-  DCHECK(root != NULL);
-  DCHECK(arg != NULL);
   reinterpret_cast<MarkSweep*>(arg)->MarkObjectNonNullParallel(*root);
 }
 
 void MarkSweep::MarkRootCallback(Object** root, void* arg, uint32_t /*thread_id*/,
                                  RootType /*root_type*/) {
-  DCHECK(root != nullptr);
-  DCHECK(arg != nullptr);
   reinterpret_cast<MarkSweep*>(arg)->MarkObjectNonNull(*root);
 }
 
-mirror::Object* MarkSweep::MarkObjectCallback(mirror::Object* object, void* arg) {
-  DCHECK(object != nullptr);
-  DCHECK(arg != nullptr);
-  reinterpret_cast<MarkSweep*>(arg)->MarkObjectNonNull(object);
-  return object;
-}
-
 void MarkSweep::VerifyRootCallback(const Object* root, void* arg, size_t vreg,
                                    const StackVisitor* visitor) {
   reinterpret_cast<MarkSweep*>(arg)->VerifyRoot(root, vreg, visitor);
@@ -957,7 +987,7 @@
 }
 
 void MarkSweep::ReMarkRoots() {
-  timings_.StartSplit("ReMarkRoots");
+  timings_.StartSplit("(Paused)ReMarkRoots");
   Runtime::Current()->VisitRoots(MarkRootCallback, this, true, true);
   timings_.EndSplit();
 }
@@ -1208,6 +1238,11 @@
   ScanObjectVisit(obj, visitor);
 }
 
+void MarkSweep::ProcessMarkStackPausedCallback(void* arg) {
+  DCHECK(arg != nullptr);
+  reinterpret_cast<MarkSweep*>(arg)->ProcessMarkStack(true);
+}
+
 void MarkSweep::ProcessMarkStackParallel(size_t thread_count) {
   Thread* self = Thread::Current();
   ThreadPool* thread_pool = GetHeap()->GetThreadPool();
@@ -1231,7 +1266,7 @@
 
 // Scan anything that's on the mark stack.
 void MarkSweep::ProcessMarkStack(bool paused) {
-  timings_.StartSplit("ProcessMarkStack");
+  timings_.StartSplit(paused ? "(Paused)ProcessMarkStack" : "ProcessMarkStack");
   size_t thread_count = GetThreadCount(paused);
   if (kParallelProcessMarkStack && thread_count > 1 &&
       mark_stack_->Size() >= kMinimumParallelMarkStackSize) {
diff --git a/runtime/gc/collector/mark_sweep.h b/runtime/gc/collector/mark_sweep.h
index 6a48cf7..29fafd6 100644
--- a/runtime/gc/collector/mark_sweep.h
+++ b/runtime/gc/collector/mark_sweep.h
@@ -135,6 +135,11 @@
   virtual void UpdateAndMarkModUnion()
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  // Pre clean cards to reduce how much work is needed in the pause.
+  void PreCleanCards()
+      EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   // Sweeps unmarked objects to complete the garbage collection.
   virtual void Sweep(bool swap_bitmaps) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
 
@@ -176,7 +181,7 @@
       SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_,
                             Locks::mutator_lock_);
 
-  static mirror::Object* RecursiveMarkObjectCallback(mirror::Object* obj, void* arg)
+  static mirror::Object* MarkObjectCallback(mirror::Object* obj, void* arg)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
       EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
 
@@ -185,9 +190,8 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
       EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
 
-  static mirror::Object* MarkObjectCallback(mirror::Object* object, void* arg)
-        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-        EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
+  static void ProcessMarkStackPausedCallback(void* arg)
+      EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
 
   static void MarkRootParallelCallback(mirror::Object** root, void* arg, uint32_t thread_id,
                                        RootType root_type)
@@ -330,6 +334,10 @@
   void ClearWhiteReferences(mirror::Object** list)
       SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
 
+  // Used to get around thread safety annotations. The call is from MarkingPhase and is guarded by
+  // IsExclusiveHeld.
+  void RevokeAllThreadLocalAllocationStacks(Thread* self) NO_THREAD_SAFETY_ANALYSIS;
+
   // Whether or not we count how many of each type of object were scanned.
   static const bool kCountScannedTypes = false;
 
diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc
index d64ec61..fe8c253 100644
--- a/runtime/gc/collector/semi_space.cc
+++ b/runtime/gc/collector/semi_space.cc
@@ -163,7 +163,7 @@
   TimingLogger::ScopedSplit split("ProcessReferences", &timings_);
   WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
   GetHeap()->ProcessReferences(timings_, clear_soft_references_, &MarkedForwardingAddressCallback,
-                               &RecursiveMarkObjectCallback, this);
+                               &MarkObjectCallback, &ProcessMarkStackCallback, this);
 }
 
 void SemiSpace::MarkingPhase() {
@@ -310,7 +310,7 @@
   }
 
   // Recursively process the mark stack.
-  ProcessMarkStack(true);
+  ProcessMarkStack();
 }
 
 void SemiSpace::ReclaimPhase() {
@@ -571,13 +571,15 @@
   return forward_address;
 }
 
-mirror::Object* SemiSpace::RecursiveMarkObjectCallback(mirror::Object* root, void* arg) {
+void SemiSpace::ProcessMarkStackCallback(void* arg) {
+  DCHECK(arg != nullptr);
+  reinterpret_cast<SemiSpace*>(arg)->ProcessMarkStack();
+}
+
+mirror::Object* SemiSpace::MarkObjectCallback(mirror::Object* root, void* arg) {
   DCHECK(root != nullptr);
   DCHECK(arg != nullptr);
-  SemiSpace* semi_space = reinterpret_cast<SemiSpace*>(arg);
-  mirror::Object* ret = semi_space->MarkObject(root);
-  semi_space->ProcessMarkStack(true);
-  return ret;
+  return reinterpret_cast<SemiSpace*>(arg)->MarkObject(root);
 }
 
 void SemiSpace::MarkRootCallback(Object** root, void* arg, uint32_t /*thread_id*/,
@@ -587,12 +589,6 @@
   *root = reinterpret_cast<SemiSpace*>(arg)->MarkObject(*root);
 }
 
-Object* SemiSpace::MarkObjectCallback(Object* object, void* arg) {
-  DCHECK(object != nullptr);
-  DCHECK(arg != nullptr);
-  return reinterpret_cast<SemiSpace*>(arg)->MarkObject(object);
-}
-
 // Marks all objects in the root set.
 void SemiSpace::MarkRoots() {
   timings_.StartSplit("MarkRoots");
@@ -670,17 +666,17 @@
       // case since it does not dirty cards and use additional memory.
       // Since we do not change the actual object, we can safely use non-transactional mode. Also
       // disable check as we could run inside a transaction.
-      obj->SetFieldObjectWithoutWriteBarrier<false, false>(offset, new_address, false);
+      obj->SetFieldObjectWithoutWriteBarrier<false, false, kVerifyNone>(offset, new_address, false);
     }
   }, kMovingClasses);
-  mirror::Class* klass = obj->GetClass();
-  if (UNLIKELY(klass->IsReferenceClass())) {
+  mirror::Class* klass = obj->GetClass<kVerifyNone>();
+  if (UNLIKELY(klass->IsReferenceClass<kVerifyNone>())) {
     DelayReferenceReferent(klass, obj);
   }
 }
 
 // Scan anything that's on the mark stack.
-void SemiSpace::ProcessMarkStack(bool paused) {
+void SemiSpace::ProcessMarkStack() {
   space::MallocSpace* promo_dest_space = NULL;
   accounting::SpaceBitmap* live_bitmap = NULL;
   if (generational_ && !whole_heap_collection_) {
@@ -694,7 +690,7 @@
     DCHECK(mark_bitmap != nullptr);
     DCHECK_EQ(live_bitmap, mark_bitmap);
   }
-  timings_.StartSplit(paused ? "(paused)ProcessMarkStack" : "ProcessMarkStack");
+  timings_.StartSplit("ProcessMarkStack");
   while (!mark_stack_->IsEmpty()) {
     Object* obj = mark_stack_->PopBack();
     if (generational_ && !whole_heap_collection_ && promo_dest_space->HasAddress(obj)) {
diff --git a/runtime/gc/collector/semi_space.h b/runtime/gc/collector/semi_space.h
index 89fe326..ba97376 100644
--- a/runtime/gc/collector/semi_space.h
+++ b/runtime/gc/collector/semi_space.h
@@ -146,12 +146,12 @@
                                RootType /*root_type*/)
       EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
 
-  static mirror::Object* MarkObjectCallback(mirror::Object* objecgt, void* arg)
-        EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
-
-  static mirror::Object* RecursiveMarkObjectCallback(mirror::Object* root, void* arg)
+  static mirror::Object* MarkObjectCallback(mirror::Object* root, void* arg)
       EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
 
+  static void ProcessMarkStackCallback(void* arg)
+      EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_);
+
   virtual mirror::Object* MarkNonForwardedObject(mirror::Object* obj)
       EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
 
@@ -174,10 +174,6 @@
   // Returns true if we should sweep the space.
   virtual bool ShouldSweepSpace(space::ContinuousSpace* space) const;
 
-  // Returns how many threads we should use for the current GC phase based on if we are paused,
-  // whether or not we care about pauses.
-  size_t GetThreadCount(bool paused) const;
-
   // Returns true if an object is inside of the immune region (assumed to be marked).
   bool IsImmune(const mirror::Object* obj) const ALWAYS_INLINE {
     return obj >= immune_begin_ && obj < immune_end_;
@@ -237,7 +233,7 @@
       SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
 
   // Recursively blackens objects on the mark stack.
-  void ProcessMarkStack(bool paused)
+  void ProcessMarkStack()
       EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_);
 
   void EnqueueFinalizerReferences(mirror::Object** ref)
diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h
index 9c91b0e..3d591f0 100644
--- a/runtime/gc/heap-inl.h
+++ b/runtime/gc/heap-inl.h
@@ -28,6 +28,7 @@
 #include "runtime.h"
 #include "thread.h"
 #include "thread-inl.h"
+#include "verify_object-inl.h"
 
 namespace art {
 namespace gc {
@@ -98,12 +99,8 @@
   if (AllocatorMayHaveConcurrentGC(allocator) && concurrent_gc_) {
     CheckConcurrentGC(self, new_num_bytes_allocated, obj);
   }
-  if (kIsDebugBuild) {
-    if (kDesiredHeapVerification > kNoHeapVerification) {
-      VerifyObject(obj);
-    }
-    self->VerifyStack();
-  }
+  VerifyObject(obj);
+  self->VerifyStack();
   return obj;
 }
 
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 9e5e3ab..58db7a8 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -135,7 +135,7 @@
        * searching.
        */
       max_allocation_stack_size_(kGCALotMode ? kGcAlotInterval
-          : (kDesiredHeapVerification > kVerifyAllFast) ? KB : MB),
+          : (kVerifyObjectSupport > kVerifyObjectModeFast) ? KB : MB),
       current_allocator_(kAllocatorTypeDlMalloc),
       current_non_moving_allocator_(kAllocatorTypeNonMoving),
       bump_pointer_space_(nullptr),
@@ -150,7 +150,7 @@
       target_utilization_(target_utilization),
       total_wait_time_(0),
       total_allocation_time_(0),
-      verify_object_mode_(kHeapVerificationNotPermitted),
+      verify_object_mode_(kVerifyObjectModeDisabled),
       disable_moving_gc_count_(0),
       running_on_valgrind_(RUNNING_ON_VALGRIND),
       use_tlab_(use_tlab) {
@@ -314,9 +314,7 @@
 
 bool Heap::IsCompilingBoot() const {
   for (const auto& space : continuous_spaces_) {
-    if (space->IsImageSpace()) {
-      return false;
-    } else if (space->IsZygoteSpace()) {
+    if (space->IsImageSpace() || space->IsZygoteSpace()) {
       return false;
     }
   }
@@ -610,41 +608,44 @@
 
 struct SoftReferenceArgs {
   IsMarkedCallback* is_marked_callback_;
-  MarkObjectCallback* recursive_mark_callback_;
+  MarkObjectCallback* mark_callback_;
   void* arg_;
 };
 
 mirror::Object* Heap::PreserveSoftReferenceCallback(mirror::Object* obj, void* arg) {
   SoftReferenceArgs* args = reinterpret_cast<SoftReferenceArgs*>(arg);
   // TODO: Not preserve all soft references.
-  return args->recursive_mark_callback_(obj, args->arg_);
+  return args->mark_callback_(obj, args->arg_);
 }
 
 // Process reference class instances and schedule finalizations.
 void Heap::ProcessReferences(TimingLogger& timings, bool clear_soft,
                              IsMarkedCallback* is_marked_callback,
-                             MarkObjectCallback* recursive_mark_object_callback, void* arg) {
+                             MarkObjectCallback* mark_object_callback,
+                             ProcessMarkStackCallback* process_mark_stack_callback, void* arg) {
   // Unless we are in the zygote or required to clear soft references with white references,
   // preserve some white referents.
   if (!clear_soft && !Runtime::Current()->IsZygote()) {
     SoftReferenceArgs soft_reference_args;
     soft_reference_args.is_marked_callback_ = is_marked_callback;
-    soft_reference_args.recursive_mark_callback_ = recursive_mark_object_callback;
+    soft_reference_args.mark_callback_ = mark_object_callback;
     soft_reference_args.arg_ = arg;
     soft_reference_queue_.PreserveSomeSoftReferences(&PreserveSoftReferenceCallback,
                                                      &soft_reference_args);
+    process_mark_stack_callback(arg);
   }
-  timings.StartSplit("ProcessReferences");
+  timings.StartSplit("(Paused)ProcessReferences");
   // Clear all remaining soft and weak references with white referents.
   soft_reference_queue_.ClearWhiteReferences(cleared_references_, is_marked_callback, arg);
   weak_reference_queue_.ClearWhiteReferences(cleared_references_, is_marked_callback, arg);
   timings.EndSplit();
   // Preserve all white objects with finalize methods and schedule them for finalization.
-  timings.StartSplit("EnqueueFinalizerReferences");
+  timings.StartSplit("(Paused)EnqueueFinalizerReferences");
   finalizer_reference_queue_.EnqueueFinalizerReferences(cleared_references_, is_marked_callback,
-                                                        recursive_mark_object_callback, arg);
+                                                        mark_object_callback, arg);
+  process_mark_stack_callback(arg);
   timings.EndSplit();
-  timings.StartSplit("ProcessReferences");
+  timings.StartSplit("(Paused)ProcessReferences");
   // Clear all f-reachable soft and weak references with white referents.
   soft_reference_queue_.ClearWhiteReferences(cleared_references_, is_marked_callback, arg);
   weak_reference_queue_.ClearWhiteReferences(cleared_references_, is_marked_callback, arg);
@@ -820,14 +821,16 @@
     return false;
   }
   if (bump_pointer_space_ != nullptr && bump_pointer_space_->HasAddress(obj)) {
-    mirror::Class* klass = obj->GetClass();
+    mirror::Class* klass = obj->GetClass<kVerifyNone>();
     if (obj == klass) {
       // This case happens for java.lang.Class.
       return true;
     }
     return VerifyClassClass(klass) && IsLiveObjectLocked(klass);
   } else if (temp_space_ != nullptr && temp_space_->HasAddress(obj)) {
-    return false;
+    // If we are in the allocated region of the temp space, then we are probably live (e.g. during
+    // a GC). When a GC isn't running End() - Begin() is 0 which means no objects are contained.
+    return temp_space_->Contains(obj);
   }
   space::ContinuousSpace* c_space = FindContinuousSpaceFromObject(obj, true);
   space::DiscontinuousSpace* d_space = NULL;
@@ -883,25 +886,6 @@
   return false;
 }
 
-void Heap::VerifyObjectImpl(mirror::Object* obj) {
-  if (Thread::Current() == NULL ||
-      Runtime::Current()->GetThreadList()->GetLockOwner() == Thread::Current()->GetTid()) {
-    return;
-  }
-  VerifyObjectBody(obj);
-}
-
-bool Heap::VerifyClassClass(const mirror::Class* c) const {
-  // Note: we don't use the accessors here as they have internal sanity checks that we don't want
-  // to run
-  const byte* raw_addr =
-      reinterpret_cast<const byte*>(c) + mirror::Object::ClassOffset().Int32Value();
-  mirror::Class* c_c = reinterpret_cast<mirror::HeapReference<mirror::Class> const *>(raw_addr)->AsMirrorPtr();
-  raw_addr = reinterpret_cast<const byte*>(c_c) + mirror::Object::ClassOffset().Int32Value();
-  mirror::Class* c_c_c = reinterpret_cast<mirror::HeapReference<mirror::Class> const *>(raw_addr)->AsMirrorPtr();
-  return c_c == c_c_c;
-}
-
 void Heap::DumpSpaces(std::ostream& stream) {
   for (const auto& space : continuous_spaces_) {
     accounting::SpaceBitmap* live_bitmap = space->GetLiveBitmap();
@@ -920,36 +904,30 @@
 }
 
 void Heap::VerifyObjectBody(mirror::Object* obj) {
-  CHECK(IsAligned<kObjectAlignment>(obj)) << "Object isn't aligned: " << obj;
+  if (this == nullptr && verify_object_mode_ == kVerifyObjectModeDisabled) {
+    return;
+  }
   // Ignore early dawn of the universe verifications.
   if (UNLIKELY(static_cast<size_t>(num_bytes_allocated_.Load()) < 10 * KB)) {
     return;
   }
-  const byte* raw_addr = reinterpret_cast<const byte*>(obj) +
-      mirror::Object::ClassOffset().Int32Value();
-  mirror::Class* c = reinterpret_cast<mirror::HeapReference<mirror::Class> const *>(raw_addr)->AsMirrorPtr();
-  if (UNLIKELY(c == NULL)) {
-    LOG(FATAL) << "Null class in object: " << obj;
-  } else if (UNLIKELY(!IsAligned<kObjectAlignment>(c))) {
-    LOG(FATAL) << "Class isn't aligned: " << c << " in object: " << obj;
-  }
+  CHECK(IsAligned<kObjectAlignment>(obj)) << "Object isn't aligned: " << obj;
+  mirror::Class* c = obj->GetFieldObject<mirror::Class, kVerifyNone>(
+      mirror::Object::ClassOffset(), false);
+  CHECK(c != nullptr) << "Null class in object " << obj;
+  CHECK(IsAligned<kObjectAlignment>(c)) << "Class " << c << " not aligned in object " << obj;
   CHECK(VerifyClassClass(c));
 
-  if (verify_object_mode_ > kVerifyAllFast) {
-    // TODO: the bitmap tests below are racy if VerifyObjectBody is called without the
-    //       heap_bitmap_lock_.
+  if (verify_object_mode_ > kVerifyObjectModeFast) {
+    // Note: the bitmap tests below are racy since we don't hold the heap bitmap lock.
     if (!IsLiveObjectLocked(obj)) {
       DumpSpaces();
       LOG(FATAL) << "Object is dead: " << obj;
     }
-    if (!IsLiveObjectLocked(c)) {
-      LOG(FATAL) << "Class of object is dead: " << c << " in object: " << obj;
-    }
   }
 }
 
 void Heap::VerificationCallback(mirror::Object* obj, void* arg) {
-  DCHECK(obj != NULL);
   reinterpret_cast<Heap*>(arg)->VerifyObjectBody(obj);
 }
 
@@ -1787,7 +1765,7 @@
 
       if (bitmap == nullptr) {
         LOG(ERROR) << "Object " << obj << " has no bitmap";
-        if (!heap_->VerifyClassClass(obj->GetClass())) {
+        if (!VerifyClassClass(obj->GetClass())) {
           LOG(ERROR) << "Object " << obj << " failed class verification!";
         }
       } else {
@@ -2032,12 +2010,6 @@
 }
 
 void Heap::RevokeAllThreadLocalAllocationStacks(Thread* self) {
-  if (!Runtime::Current()->IsStarted()) {
-    // There's no thread list if the runtime hasn't started (eg
-    // dex2oat or a test). Just revoke for self.
-    self->RevokeThreadLocalAllocationStack();
-    return;
-  }
   // This must be called only during the pause.
   CHECK(Locks::mutator_lock_->IsExclusiveHeld(self));
   MutexLock mu(self, *Locks::runtime_shutdown_lock_);
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 80a5a1a..5d44ee1 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -37,6 +37,7 @@
 #include "reference_queue.h"
 #include "safe_map.h"
 #include "thread_pool.h"
+#include "verify_object.h"
 
 namespace art {
 
@@ -99,15 +100,6 @@
   kAllocatorTypeLOS,  // Large object space, also doesn't have entrypoints.
 };
 
-// How we want to sanity check the heap's correctness.
-enum HeapVerificationMode {
-  kHeapVerificationNotPermitted,  // Too early in runtime start-up for heap to be verified.
-  kNoHeapVerification,  // Production default.
-  kVerifyAllFast,  // Sanity check all heap accesses with quick(er) tests.
-  kVerifyAll  // Sanity check all heap accesses.
-};
-static constexpr HeapVerificationMode kDesiredHeapVerification = kNoHeapVerification;
-
 // If true, use rosalloc/RosAllocSpace instead of dlmalloc/DlMallocSpace
 static constexpr bool kUseRosAlloc = true;
 
@@ -208,23 +200,16 @@
   void ChangeCollector(CollectorType collector_type);
 
   // The given reference is believed to be to an object in the Java heap, check the soundness of it.
-  void VerifyObjectImpl(mirror::Object* o);
-  void VerifyObject(mirror::Object* o) {
-    if (o != nullptr && this != nullptr && verify_object_mode_ > kNoHeapVerification) {
-      VerifyObjectImpl(o);
-    }
-  }
-  // Check that c.getClass() == c.getClass().getClass().
-  bool VerifyClassClass(const mirror::Class* c) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  // TODO: NO_THREAD_SAFETY_ANALYSIS since we call this everywhere and it is impossible to find a
+  // proper lock ordering for it.
+  void VerifyObjectBody(mirror::Object* o) NO_THREAD_SAFETY_ANALYSIS;
 
   // Check sanity of all live references.
   void VerifyHeap() LOCKS_EXCLUDED(Locks::heap_bitmap_lock_);
   bool VerifyHeapReferences()
-      EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+      EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
   bool VerifyMissingCardMarks()
-      EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+      EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
 
   // A weaker test than IsLiveObject or VerifyObject that doesn't require the heap lock,
   // and doesn't abort on error, allowing the caller to report more
@@ -339,27 +324,28 @@
   static mirror::Object* PreserveSoftReferenceCallback(mirror::Object* obj, void* arg);
   void ProcessReferences(TimingLogger& timings, bool clear_soft,
                          IsMarkedCallback* is_marked_callback,
-                         MarkObjectCallback* recursive_mark_object_callback, void* arg)
+                         MarkObjectCallback* mark_object_callback,
+                         ProcessMarkStackCallback* process_mark_stack_callback,
+                         void* arg)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
       EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
 
   // Enable verification of object references when the runtime is sufficiently initialized.
   void EnableObjectValidation() {
-    verify_object_mode_ = kDesiredHeapVerification;
-    if (verify_object_mode_ > kNoHeapVerification) {
+    verify_object_mode_ = kVerifyObjectSupport;
+    if (verify_object_mode_ > kVerifyObjectModeDisabled) {
       VerifyHeap();
     }
   }
 
   // Disable object reference verification for image writing.
   void DisableObjectValidation() {
-    verify_object_mode_ = kHeapVerificationNotPermitted;
+    verify_object_mode_ = kVerifyObjectModeDisabled;
   }
 
   // Other checks may be performed if we know the heap should be in a sane state.
   bool IsObjectValidationEnabled() const {
-    return kDesiredHeapVerification > kNoHeapVerification &&
-        verify_object_mode_ > kHeapVerificationNotPermitted;
+    return verify_object_mode_ > kVerifyObjectModeDisabled;
   }
 
   // Returns true if low memory mode is enabled.
@@ -474,6 +460,10 @@
   void FlushAllocStack()
       EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
 
+  // Revoke all the thread-local allocation stacks.
+  void RevokeAllThreadLocalAllocationStacks(Thread* self)
+      EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   // Mark all the objects in the allocation stack in the specified bitmap.
   void MarkAllocStack(accounting::SpaceBitmap* bitmap1, accounting::SpaceBitmap* bitmap2,
                       accounting::ObjectSet* large_objects, accounting::ObjectStack* stack)
@@ -660,19 +650,12 @@
       LOCKS_EXCLUDED(Locks::heap_bitmap_lock_);
   void RemoveSpace(space::Space* space) LOCKS_EXCLUDED(Locks::heap_bitmap_lock_);
 
-  // No thread saftey analysis since we call this everywhere and it is impossible to find a proper
-  // lock ordering for it.
-  void VerifyObjectBody(mirror::Object *obj) NO_THREAD_SAFETY_ANALYSIS;
-
   static void VerificationCallback(mirror::Object* obj, void* arg)
       SHARED_LOCKS_REQUIRED(GlobalSychronization::heap_bitmap_lock_);
 
   // Swap the allocation stack with the live stack.
   void SwapStacks(Thread* self);
 
-  // Revoke all the thread-local allocation stacks.
-  void RevokeAllThreadLocalAllocationStacks(Thread* self);
-
   // Clear cards and update the mod union table.
   void ProcessCards(TimingLogger& timings);
 
@@ -914,7 +897,7 @@
   AtomicInteger total_allocation_time_;
 
   // The current state of heap verification, may be enabled or disabled.
-  HeapVerificationMode verify_object_mode_;
+  VerifyObjectMode verify_object_mode_;
 
   // Compacting GC disable count, prevents compacting GC from running iff > 0.
   size_t disable_moving_gc_count_ GUARDED_BY(gc_complete_lock_);
diff --git a/runtime/gc/heap_test.cc b/runtime/gc/heap_test.cc
index b02b8bb..4b86339 100644
--- a/runtime/gc/heap_test.cc
+++ b/runtime/gc/heap_test.cc
@@ -43,7 +43,8 @@
     ScopedObjectAccess soa(Thread::Current());
     // garbage is created during ClassLinker::Init
 
-    SirtRef<mirror::Class> c(soa.Self(), class_linker_->FindSystemClass("[Ljava/lang/Object;"));
+    SirtRef<mirror::Class> c(soa.Self(), class_linker_->FindSystemClass(soa.Self(),
+                                                                        "[Ljava/lang/Object;"));
     for (size_t i = 0; i < 1024; ++i) {
       SirtRef<mirror::ObjectArray<mirror::Object> > array(soa.Self(),
           mirror::ObjectArray<mirror::Object>::Alloc(soa.Self(), c.get(), 2048));
diff --git a/runtime/gc/space/dlmalloc_space.cc b/runtime/gc/space/dlmalloc_space.cc
index 931ed21..1493019 100644
--- a/runtime/gc/space/dlmalloc_space.cc
+++ b/runtime/gc/space/dlmalloc_space.cc
@@ -53,10 +53,10 @@
     return nullptr;
   }
 
-  // Protect memory beyond the initial size.
+  // Protect memory beyond the starting size. morecore will add r/w permissions when necessory
   byte* end = mem_map->Begin() + starting_size;
-  if (capacity - initial_size > 0) {
-    CHECK_MEMORY_CALL(mprotect, (end, capacity - initial_size, PROT_NONE), name);
+  if (capacity - starting_size > 0) {
+    CHECK_MEMORY_CALL(mprotect, (end, capacity - starting_size, PROT_NONE), name);
   }
 
   // Everything is set so record in immutable structure and leave
diff --git a/runtime/gc/space/dlmalloc_space_test.cc b/runtime/gc/space/dlmalloc_space_test.cc
new file mode 100644
index 0000000..964c31b
--- /dev/null
+++ b/runtime/gc/space/dlmalloc_space_test.cc
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2011 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 "space_test.h"
+#include "dlmalloc_space.h"
+
+namespace art {
+namespace gc {
+namespace space {
+
+MallocSpace* CreateDlMallocSpace(const std::string& name, size_t initial_size, size_t growth_limit,
+                                 size_t capacity, byte* requested_begin) {
+  return DlMallocSpace::Create(name, initial_size, growth_limit, capacity, requested_begin);
+}
+
+TEST_SPACE_CREATE_FN(DlMallocSpace, CreateDlMallocSpace)
+
+
+}  // namespace space
+}  // namespace gc
+}  // namespace art
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index ebad8dd..12c5451 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -16,9 +16,6 @@
 
 #include "image_space.h"
 
-#include <sys/types.h>
-#include <sys/wait.h>
-
 #include "base/stl_util.h"
 #include "base/unix_file/fd_file.h"
 #include "gc/accounting/space_bitmap-inl.h"
@@ -89,52 +86,14 @@
     arg_vector.push_back("--host");
   }
 
+  const std::vector<std::string>& compiler_options = Runtime::Current()->GetImageCompilerOptions();
+  for (size_t i = 0; compiler_options.size(); ++i) {
+    arg_vector.push_back(compiler_options[i].c_str());
+  }
+
   std::string command_line(Join(arg_vector, ' '));
   LOG(INFO) << "GenerateImage: " << command_line;
-
-  // Convert the args to char pointers.
-  std::vector<char*> char_args;
-  for (std::vector<std::string>::iterator it = arg_vector.begin(); it != arg_vector.end();
-      ++it) {
-    char_args.push_back(const_cast<char*>(it->c_str()));
-  }
-  char_args.push_back(NULL);
-
-  // fork and exec dex2oat
-  pid_t pid = fork();
-  if (pid == 0) {
-    // no allocation allowed between fork and exec
-
-    // change process groups, so we don't get reaped by ProcessManager
-    setpgid(0, 0);
-
-    execv(dex2oat.c_str(), &char_args[0]);
-
-    PLOG(FATAL) << "execv(" << dex2oat << ") failed";
-    return false;
-  } else {
-    if (pid == -1) {
-      *error_msg = StringPrintf("Failed to generate image '%s' because fork failed: %s",
-                                image_file_name.c_str(), strerror(errno));
-      return false;
-    }
-
-    // wait for dex2oat to finish
-    int status;
-    pid_t got_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
-    if (got_pid != pid) {
-      *error_msg = StringPrintf("Failed to generate image '%s' because waitpid failed: "
-                                "wanted %d, got %d: %s",
-                                image_file_name.c_str(), pid, got_pid, strerror(errno));
-      return false;
-    }
-    if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
-      *error_msg = StringPrintf("Failed to generate image '%s' because dex2oat failed: %s",
-                                image_file_name.c_str(), command_line.c_str());
-      return false;
-    }
-  }
-  return true;
+  return Exec(arg_vector, error_msg);
 }
 
 ImageSpace* ImageSpace::Create(const char* original_image_file_name) {
diff --git a/runtime/gc/space/large_object_space_test.cc b/runtime/gc/space/large_object_space_test.cc
new file mode 100644
index 0000000..845b9e3
--- /dev/null
+++ b/runtime/gc/space/large_object_space_test.cc
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2011 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 "space_test.h"
+#include "large_object_space.h"
+
+namespace art {
+namespace gc {
+namespace space {
+
+class LargeObjectSpaceTest : public SpaceTest {
+ public:
+  void LargeObjectTest();
+};
+
+
+void LargeObjectSpaceTest::LargeObjectTest() {
+  size_t rand_seed = 0;
+  for (size_t i = 0; i < 2; ++i) {
+    LargeObjectSpace* los = nullptr;
+    if (i == 0) {
+      los = space::LargeObjectMapSpace::Create("large object space");
+    } else {
+      los = space::FreeListSpace::Create("large object space", nullptr, 128 * MB);
+    }
+
+    static const size_t num_allocations = 64;
+    static const size_t max_allocation_size = 0x100000;
+    std::vector<std::pair<mirror::Object*, size_t> > requests;
+
+    for (size_t phase = 0; phase < 2; ++phase) {
+      while (requests.size() < num_allocations) {
+        size_t request_size = test_rand(&rand_seed) % max_allocation_size;
+        size_t allocation_size = 0;
+        mirror::Object* obj = los->Alloc(Thread::Current(), request_size, &allocation_size);
+        ASSERT_TRUE(obj != nullptr);
+        ASSERT_EQ(allocation_size, los->AllocationSize(obj));
+        ASSERT_GE(allocation_size, request_size);
+        // Fill in our magic value.
+        byte magic = (request_size & 0xFF) | 1;
+        memset(obj, magic, request_size);
+        requests.push_back(std::make_pair(obj, request_size));
+      }
+
+      // "Randomly" shuffle the requests.
+      for (size_t k = 0; k < 10; ++k) {
+        for (size_t j = 0; j < requests.size(); ++j) {
+          std::swap(requests[j], requests[test_rand(&rand_seed) % requests.size()]);
+        }
+      }
+
+      // Free 1 / 2 the allocations the first phase, and all the second phase.
+      size_t limit = !phase ? requests.size() / 2 : 0;
+      while (requests.size() > limit) {
+        mirror::Object* obj = requests.back().first;
+        size_t request_size = requests.back().second;
+        requests.pop_back();
+        byte magic = (request_size & 0xFF) | 1;
+        for (size_t k = 0; k < request_size; ++k) {
+          ASSERT_EQ(reinterpret_cast<const byte*>(obj)[k], magic);
+        }
+        ASSERT_GE(los->Free(Thread::Current(), obj), request_size);
+      }
+    }
+
+    size_t bytes_allocated = 0;
+    // Checks that the coalescing works.
+    mirror::Object* obj = los->Alloc(Thread::Current(), 100 * MB, &bytes_allocated);
+    EXPECT_TRUE(obj != nullptr);
+    los->Free(Thread::Current(), obj);
+
+    EXPECT_EQ(0U, los->GetBytesAllocated());
+    EXPECT_EQ(0U, los->GetObjectsAllocated());
+    delete los;
+  }
+}
+
+
+TEST_F(LargeObjectSpaceTest, LargeObjectTest) {
+  LargeObjectTest();
+}
+
+}  // namespace space
+}  // namespace gc
+}  // namespace art
diff --git a/runtime/gc/space/malloc_space.cc b/runtime/gc/space/malloc_space.cc
index f90e6c7..ee31112 100644
--- a/runtime/gc/space/malloc_space.cc
+++ b/runtime/gc/space/malloc_space.cc
@@ -111,7 +111,8 @@
 }
 
 void MallocSpace::RegisterRecentFree(mirror::Object* ptr) {
-  recent_freed_objects_[recent_free_pos_] = std::make_pair(ptr, ptr->GetClass());
+  // No verification since the object is dead.
+  recent_freed_objects_[recent_free_pos_] = std::make_pair(ptr, ptr->GetClass<kVerifyNone>());
   recent_free_pos_ = (recent_free_pos_ + 1) & kRecentFreeMask;
 }
 
diff --git a/runtime/gc/space/rosalloc_space.cc b/runtime/gc/space/rosalloc_space.cc
index 86e441e..cc6c1d9 100644
--- a/runtime/gc/space/rosalloc_space.cc
+++ b/runtime/gc/space/rosalloc_space.cc
@@ -56,10 +56,10 @@
     return NULL;
   }
 
-  // Protect memory beyond the initial size.
+  // Protect memory beyond the starting size. MoreCore will add r/w permissions when necessory
   byte* end = mem_map->Begin() + starting_size;
-  if (capacity - initial_size > 0) {
-    CHECK_MEMORY_CALL(mprotect, (end, capacity - initial_size, PROT_NONE), name);
+  if (capacity - starting_size > 0) {
+    CHECK_MEMORY_CALL(mprotect, (end, capacity - starting_size, PROT_NONE), name);
   }
 
   // Everything is set so record in immutable structure and leave
diff --git a/runtime/gc/space/rosalloc_space.h b/runtime/gc/space/rosalloc_space.h
index 2377423..72e84f6 100644
--- a/runtime/gc/space/rosalloc_space.h
+++ b/runtime/gc/space/rosalloc_space.h
@@ -60,7 +60,8 @@
     // TODO: NO_THREAD_SAFETY_ANALYSIS because SizeOf() requires that mutator_lock is held.
     void* obj_ptr = const_cast<void*>(reinterpret_cast<const void*>(obj));
     // obj is a valid object. Use its class in the header to get the size.
-    size_t size = obj->SizeOf();
+    // Don't use verification since the object may be dead if we are sweeping.
+    size_t size = obj->SizeOf<kVerifyNone>();
     size_t size_by_size = rosalloc_->UsableSize(size);
     if (kIsDebugBuild) {
       size_t size_by_ptr = rosalloc_->UsableSize(obj_ptr);
diff --git a/runtime/gc/space/rosalloc_space_test.cc b/runtime/gc/space/rosalloc_space_test.cc
new file mode 100644
index 0000000..3eac795
--- /dev/null
+++ b/runtime/gc/space/rosalloc_space_test.cc
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2011 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 "space_test.h"
+
+namespace art {
+namespace gc {
+namespace space {
+
+MallocSpace* CreateRosAllocSpace(const std::string& name, size_t initial_size, size_t growth_limit,
+                                 size_t capacity, byte* requested_begin) {
+  return RosAllocSpace::Create(name, initial_size, growth_limit, capacity, requested_begin,
+                               Runtime::Current()->GetHeap()->IsLowMemoryMode());
+}
+
+TEST_SPACE_CREATE_FN(RosAllocSpace, CreateRosAllocSpace)
+
+
+}  // namespace space
+}  // namespace gc
+}  // namespace art
diff --git a/runtime/gc/space/space_test.cc b/runtime/gc/space/space_test.cc
deleted file mode 100644
index 6d07a60..0000000
--- a/runtime/gc/space/space_test.cc
+++ /dev/null
@@ -1,631 +0,0 @@
-/*
- * Copyright (C) 2011 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 "dlmalloc_space.h"
-#include "large_object_space.h"
-#include "zygote_space.h"
-
-#include "common_test.h"
-#include "globals.h"
-#include "UniquePtr.h"
-#include "mirror/array-inl.h"
-#include "mirror/object-inl.h"
-
-#include <stdint.h>
-
-namespace art {
-namespace gc {
-namespace space {
-
-class SpaceTest : public CommonTest {
- public:
-  void AddSpace(ContinuousSpace* space) {
-    // For RosAlloc, revoke the thread local runs before moving onto a
-    // new alloc space.
-    Runtime::Current()->GetHeap()->RevokeAllThreadLocalBuffers();
-    Runtime::Current()->GetHeap()->AddSpace(space);
-  }
-  void InstallClass(mirror::Object* o, size_t size) NO_THREAD_SAFETY_ANALYSIS {
-    // Note the minimum size, which is the size of a zero-length byte array.
-    EXPECT_GE(size, SizeOfZeroLengthByteArray());
-    SirtRef<mirror::ClassLoader> null_loader(Thread::Current(), NULL);
-    mirror::Class* byte_array_class = Runtime::Current()->GetClassLinker()->FindClass("[B", null_loader);
-    EXPECT_TRUE(byte_array_class != NULL);
-    o->SetClass(byte_array_class);
-    mirror::Array* arr = o->AsArray();
-    size_t header_size = SizeOfZeroLengthByteArray();
-    int32_t length = size - header_size;
-    arr->SetLength(length);
-    EXPECT_EQ(arr->SizeOf(), size);
-  }
-
-  static size_t SizeOfZeroLengthByteArray() {
-    return mirror::Array::DataOffset(Primitive::ComponentSize(Primitive::kPrimByte)).Uint32Value();
-  }
-
-  static MallocSpace* CreateDlMallocSpace(const std::string& name, size_t initial_size, size_t growth_limit,
-                                          size_t capacity, byte* requested_begin) {
-    return DlMallocSpace::Create(name, initial_size, growth_limit, capacity, requested_begin);
-  }
-  static MallocSpace* CreateRosAllocSpace(const std::string& name, size_t initial_size, size_t growth_limit,
-                                          size_t capacity, byte* requested_begin) {
-    return RosAllocSpace::Create(name, initial_size, growth_limit, capacity, requested_begin,
-                                 Runtime::Current()->GetHeap()->IsLowMemoryMode());
-  }
-
-  typedef MallocSpace* (*CreateSpaceFn)(const std::string& name, size_t initial_size, size_t growth_limit,
-                                        size_t capacity, byte* requested_begin);
-  void InitTestBody(CreateSpaceFn create_space);
-  void ZygoteSpaceTestBody(CreateSpaceFn create_space);
-  void AllocAndFreeTestBody(CreateSpaceFn create_space);
-  void AllocAndFreeListTestBody(CreateSpaceFn create_space);
-
-  void SizeFootPrintGrowthLimitAndTrimBody(MallocSpace* space, intptr_t object_size,
-                                           int round, size_t growth_limit);
-  void SizeFootPrintGrowthLimitAndTrimDriver(size_t object_size, CreateSpaceFn create_space);
-};
-
-static size_t test_rand(size_t* seed) {
-  *seed = *seed * 1103515245 + 12345;
-  return *seed;
-}
-
-void SpaceTest::InitTestBody(CreateSpaceFn create_space) {
-  {
-    // Init < max == growth
-    UniquePtr<Space> space(create_space("test", 16 * MB, 32 * MB, 32 * MB, NULL));
-    EXPECT_TRUE(space.get() != NULL);
-  }
-  {
-    // Init == max == growth
-    UniquePtr<Space> space(create_space("test", 16 * MB, 16 * MB, 16 * MB, NULL));
-    EXPECT_TRUE(space.get() != NULL);
-  }
-  {
-    // Init > max == growth
-    UniquePtr<Space> space(create_space("test", 32 * MB, 16 * MB, 16 * MB, NULL));
-    EXPECT_TRUE(space.get() == NULL);
-  }
-  {
-    // Growth == init < max
-    UniquePtr<Space> space(create_space("test", 16 * MB, 16 * MB, 32 * MB, NULL));
-    EXPECT_TRUE(space.get() != NULL);
-  }
-  {
-    // Growth < init < max
-    UniquePtr<Space> space(create_space("test", 16 * MB, 8 * MB, 32 * MB, NULL));
-    EXPECT_TRUE(space.get() == NULL);
-  }
-  {
-    // Init < growth < max
-    UniquePtr<Space> space(create_space("test", 8 * MB, 16 * MB, 32 * MB, NULL));
-    EXPECT_TRUE(space.get() != NULL);
-  }
-  {
-    // Init < max < growth
-    UniquePtr<Space> space(create_space("test", 8 * MB, 32 * MB, 16 * MB, NULL));
-    EXPECT_TRUE(space.get() == NULL);
-  }
-}
-
-TEST_F(SpaceTest, Init_DlMallocSpace) {
-  InitTestBody(SpaceTest::CreateDlMallocSpace);
-}
-TEST_F(SpaceTest, Init_RosAllocSpace) {
-  InitTestBody(SpaceTest::CreateRosAllocSpace);
-}
-
-// TODO: This test is not very good, we should improve it.
-// The test should do more allocations before the creation of the ZygoteSpace, and then do
-// allocations after the ZygoteSpace is created. The test should also do some GCs to ensure that
-// the GC works with the ZygoteSpace.
-void SpaceTest::ZygoteSpaceTestBody(CreateSpaceFn create_space) {
-  size_t dummy = 0;
-  MallocSpace* space(create_space("test", 4 * MB, 16 * MB, 16 * MB, NULL));
-  ASSERT_TRUE(space != NULL);
-
-  // Make space findable to the heap, will also delete space when runtime is cleaned up
-  AddSpace(space);
-  Thread* self = Thread::Current();
-
-  // Succeeds, fits without adjusting the footprint limit.
-  mirror::Object* ptr1 = space->Alloc(self, 1 * MB, &dummy);
-  EXPECT_TRUE(ptr1 != NULL);
-  InstallClass(ptr1, 1 * MB);
-
-  // Fails, requires a higher footprint limit.
-  mirror::Object* ptr2 = space->Alloc(self, 8 * MB, &dummy);
-  EXPECT_TRUE(ptr2 == NULL);
-
-  // Succeeds, adjusts the footprint.
-  size_t ptr3_bytes_allocated;
-  mirror::Object* ptr3 = space->AllocWithGrowth(self, 8 * MB, &ptr3_bytes_allocated);
-  EXPECT_TRUE(ptr3 != NULL);
-  EXPECT_LE(8U * MB, ptr3_bytes_allocated);
-  InstallClass(ptr3, 8 * MB);
-
-  // Fails, requires a higher footprint limit.
-  mirror::Object* ptr4 = space->Alloc(self, 8 * MB, &dummy);
-  EXPECT_TRUE(ptr4 == NULL);
-
-  // Also fails, requires a higher allowed footprint.
-  mirror::Object* ptr5 = space->AllocWithGrowth(self, 8 * MB, &dummy);
-  EXPECT_TRUE(ptr5 == NULL);
-
-  // Release some memory.
-  ScopedObjectAccess soa(self);
-  size_t free3 = space->AllocationSize(ptr3);
-  EXPECT_EQ(free3, ptr3_bytes_allocated);
-  EXPECT_EQ(free3, space->Free(self, ptr3));
-  EXPECT_LE(8U * MB, free3);
-
-  // Succeeds, now that memory has been freed.
-  mirror::Object* ptr6 = space->AllocWithGrowth(self, 9 * MB, &dummy);
-  EXPECT_TRUE(ptr6 != NULL);
-  InstallClass(ptr6, 9 * MB);
-
-  // Final clean up.
-  size_t free1 = space->AllocationSize(ptr1);
-  space->Free(self, ptr1);
-  EXPECT_LE(1U * MB, free1);
-
-  // Make sure that the zygote space isn't directly at the start of the space.
-  space->Alloc(self, 1U * MB, &dummy);
-
-  gc::Heap* heap = Runtime::Current()->GetHeap();
-  space::Space* old_space = space;
-  heap->RemoveSpace(old_space);
-  space::ZygoteSpace* zygote_space = space->CreateZygoteSpace("alloc space",
-                                                              heap->IsLowMemoryMode(),
-                                                              &space);
-  delete old_space;
-  // Add the zygote space.
-  AddSpace(zygote_space);
-
-  // Make space findable to the heap, will also delete space when runtime is cleaned up
-  AddSpace(space);
-
-  // Succeeds, fits without adjusting the footprint limit.
-  ptr1 = space->Alloc(self, 1 * MB, &dummy);
-  EXPECT_TRUE(ptr1 != NULL);
-  InstallClass(ptr1, 1 * MB);
-
-  // Fails, requires a higher footprint limit.
-  ptr2 = space->Alloc(self, 8 * MB, &dummy);
-  EXPECT_TRUE(ptr2 == NULL);
-
-  // Succeeds, adjusts the footprint.
-  ptr3 = space->AllocWithGrowth(self, 2 * MB, &dummy);
-  EXPECT_TRUE(ptr3 != NULL);
-  InstallClass(ptr3, 2 * MB);
-  space->Free(self, ptr3);
-
-  // Final clean up.
-  free1 = space->AllocationSize(ptr1);
-  space->Free(self, ptr1);
-  EXPECT_LE(1U * MB, free1);
-}
-
-TEST_F(SpaceTest, ZygoteSpace_DlMallocSpace) {
-  ZygoteSpaceTestBody(SpaceTest::CreateDlMallocSpace);
-}
-
-TEST_F(SpaceTest, ZygoteSpace_RosAllocSpace) {
-  ZygoteSpaceTestBody(SpaceTest::CreateRosAllocSpace);
-}
-
-void SpaceTest::AllocAndFreeTestBody(CreateSpaceFn create_space) {
-  size_t dummy = 0;
-  MallocSpace* space(create_space("test", 4 * MB, 16 * MB, 16 * MB, NULL));
-  ASSERT_TRUE(space != NULL);
-  Thread* self = Thread::Current();
-
-  // Make space findable to the heap, will also delete space when runtime is cleaned up
-  AddSpace(space);
-
-  // Succeeds, fits without adjusting the footprint limit.
-  mirror::Object* ptr1 = space->Alloc(self, 1 * MB, &dummy);
-  EXPECT_TRUE(ptr1 != NULL);
-  InstallClass(ptr1, 1 * MB);
-
-  // Fails, requires a higher footprint limit.
-  mirror::Object* ptr2 = space->Alloc(self, 8 * MB, &dummy);
-  EXPECT_TRUE(ptr2 == NULL);
-
-  // Succeeds, adjusts the footprint.
-  size_t ptr3_bytes_allocated;
-  mirror::Object* ptr3 = space->AllocWithGrowth(self, 8 * MB, &ptr3_bytes_allocated);
-  EXPECT_TRUE(ptr3 != NULL);
-  EXPECT_LE(8U * MB, ptr3_bytes_allocated);
-  InstallClass(ptr3, 8 * MB);
-
-  // Fails, requires a higher footprint limit.
-  mirror::Object* ptr4 = space->Alloc(self, 8 * MB, &dummy);
-  EXPECT_TRUE(ptr4 == NULL);
-
-  // Also fails, requires a higher allowed footprint.
-  mirror::Object* ptr5 = space->AllocWithGrowth(self, 8 * MB, &dummy);
-  EXPECT_TRUE(ptr5 == NULL);
-
-  // Release some memory.
-  ScopedObjectAccess soa(self);
-  size_t free3 = space->AllocationSize(ptr3);
-  EXPECT_EQ(free3, ptr3_bytes_allocated);
-  space->Free(self, ptr3);
-  EXPECT_LE(8U * MB, free3);
-
-  // Succeeds, now that memory has been freed.
-  mirror::Object* ptr6 = space->AllocWithGrowth(self, 9 * MB, &dummy);
-  EXPECT_TRUE(ptr6 != NULL);
-  InstallClass(ptr6, 9 * MB);
-
-  // Final clean up.
-  size_t free1 = space->AllocationSize(ptr1);
-  space->Free(self, ptr1);
-  EXPECT_LE(1U * MB, free1);
-}
-
-TEST_F(SpaceTest, AllocAndFree_DlMallocSpace) {
-  AllocAndFreeTestBody(SpaceTest::CreateDlMallocSpace);
-}
-TEST_F(SpaceTest, AllocAndFree_RosAllocSpace) {
-  AllocAndFreeTestBody(SpaceTest::CreateRosAllocSpace);
-}
-
-TEST_F(SpaceTest, LargeObjectTest) {
-  size_t rand_seed = 0;
-  for (size_t i = 0; i < 2; ++i) {
-    LargeObjectSpace* los = NULL;
-    if (i == 0) {
-      los = space::LargeObjectMapSpace::Create("large object space");
-    } else {
-      los = space::FreeListSpace::Create("large object space", NULL, 128 * MB);
-    }
-
-    static const size_t num_allocations = 64;
-    static const size_t max_allocation_size = 0x100000;
-    std::vector<std::pair<mirror::Object*, size_t> > requests;
-
-    for (size_t phase = 0; phase < 2; ++phase) {
-      while (requests.size() < num_allocations) {
-        size_t request_size = test_rand(&rand_seed) % max_allocation_size;
-        size_t allocation_size = 0;
-        mirror::Object* obj = los->Alloc(Thread::Current(), request_size, &allocation_size);
-        ASSERT_TRUE(obj != NULL);
-        ASSERT_EQ(allocation_size, los->AllocationSize(obj));
-        ASSERT_GE(allocation_size, request_size);
-        // Fill in our magic value.
-        byte magic = (request_size & 0xFF) | 1;
-        memset(obj, magic, request_size);
-        requests.push_back(std::make_pair(obj, request_size));
-      }
-
-      // "Randomly" shuffle the requests.
-      for (size_t k = 0; k < 10; ++k) {
-        for (size_t j = 0; j < requests.size(); ++j) {
-          std::swap(requests[j], requests[test_rand(&rand_seed) % requests.size()]);
-        }
-      }
-
-      // Free 1 / 2 the allocations the first phase, and all the second phase.
-      size_t limit = !phase ? requests.size() / 2 : 0;
-      while (requests.size() > limit) {
-        mirror::Object* obj = requests.back().first;
-        size_t request_size = requests.back().second;
-        requests.pop_back();
-        byte magic = (request_size & 0xFF) | 1;
-        for (size_t k = 0; k < request_size; ++k) {
-          ASSERT_EQ(reinterpret_cast<const byte*>(obj)[k], magic);
-        }
-        ASSERT_GE(los->Free(Thread::Current(), obj), request_size);
-      }
-    }
-
-    size_t bytes_allocated = 0;
-    // Checks that the coalescing works.
-    mirror::Object* obj = los->Alloc(Thread::Current(), 100 * MB, &bytes_allocated);
-    EXPECT_TRUE(obj != NULL);
-    los->Free(Thread::Current(), obj);
-
-    EXPECT_EQ(0U, los->GetBytesAllocated());
-    EXPECT_EQ(0U, los->GetObjectsAllocated());
-    delete los;
-  }
-}
-
-void SpaceTest::AllocAndFreeListTestBody(CreateSpaceFn create_space) {
-  MallocSpace* space(create_space("test", 4 * MB, 16 * MB, 16 * MB, NULL));
-  ASSERT_TRUE(space != NULL);
-
-  // Make space findable to the heap, will also delete space when runtime is cleaned up
-  AddSpace(space);
-  Thread* self = Thread::Current();
-
-  // Succeeds, fits without adjusting the max allowed footprint.
-  mirror::Object* lots_of_objects[1024];
-  for (size_t i = 0; i < arraysize(lots_of_objects); i++) {
-    size_t allocation_size = 0;
-    size_t size_of_zero_length_byte_array = SizeOfZeroLengthByteArray();
-    lots_of_objects[i] = space->Alloc(self, size_of_zero_length_byte_array, &allocation_size);
-    EXPECT_TRUE(lots_of_objects[i] != nullptr);
-    InstallClass(lots_of_objects[i], size_of_zero_length_byte_array);
-    EXPECT_EQ(allocation_size, space->AllocationSize(lots_of_objects[i]));
-  }
-
-  // Release memory and check pointers are NULL.
-  {
-    ScopedObjectAccess soa(self);
-    space->FreeList(self, arraysize(lots_of_objects), lots_of_objects);
-    for (size_t i = 0; i < arraysize(lots_of_objects); i++) {
-      EXPECT_TRUE(lots_of_objects[i] == nullptr);
-    }
-  }
-
-  // Succeeds, fits by adjusting the max allowed footprint.
-  for (size_t i = 0; i < arraysize(lots_of_objects); i++) {
-    size_t allocation_size = 0;
-    lots_of_objects[i] = space->AllocWithGrowth(self, 1024, &allocation_size);
-    EXPECT_TRUE(lots_of_objects[i] != nullptr);
-    InstallClass(lots_of_objects[i], 1024);
-    EXPECT_EQ(allocation_size, space->AllocationSize(lots_of_objects[i]));
-  }
-
-  // Release memory and check pointers are NULL
-  {
-    ScopedObjectAccess soa(self);
-    space->FreeList(self, arraysize(lots_of_objects), lots_of_objects);
-    for (size_t i = 0; i < arraysize(lots_of_objects); i++) {
-      EXPECT_TRUE(lots_of_objects[i] == nullptr);
-    }
-  }
-}
-
-TEST_F(SpaceTest, AllocAndFreeList_DlMallocSpace) {
-  AllocAndFreeListTestBody(SpaceTest::CreateDlMallocSpace);
-}
-TEST_F(SpaceTest, AllocAndFreeList_RosAllocSpace) {
-  AllocAndFreeListTestBody(SpaceTest::CreateRosAllocSpace);
-}
-
-void SpaceTest::SizeFootPrintGrowthLimitAndTrimBody(MallocSpace* space, intptr_t object_size,
-                                                    int round, size_t growth_limit) {
-  if (((object_size > 0 && object_size >= static_cast<intptr_t>(growth_limit))) ||
-      ((object_size < 0 && -object_size >= static_cast<intptr_t>(growth_limit)))) {
-    // No allocation can succeed
-    return;
-  }
-
-  // The space's footprint equals amount of resources requested from system
-  size_t footprint = space->GetFootprint();
-
-  // The space must at least have its book keeping allocated
-  EXPECT_GT(footprint, 0u);
-
-  // But it shouldn't exceed the initial size
-  EXPECT_LE(footprint, growth_limit);
-
-  // space's size shouldn't exceed the initial size
-  EXPECT_LE(space->Size(), growth_limit);
-
-  // this invariant should always hold or else the space has grown to be larger than what the
-  // space believes its size is (which will break invariants)
-  EXPECT_GE(space->Size(), footprint);
-
-  // Fill the space with lots of small objects up to the growth limit
-  size_t max_objects = (growth_limit / (object_size > 0 ? object_size : 8)) + 1;
-  UniquePtr<mirror::Object*[]> lots_of_objects(new mirror::Object*[max_objects]);
-  size_t last_object = 0;  // last object for which allocation succeeded
-  size_t amount_allocated = 0;  // amount of space allocated
-  Thread* self = Thread::Current();
-  size_t rand_seed = 123456789;
-  for (size_t i = 0; i < max_objects; i++) {
-    size_t alloc_fails = 0;  // number of failed allocations
-    size_t max_fails = 30;  // number of times we fail allocation before giving up
-    for (; alloc_fails < max_fails; alloc_fails++) {
-      size_t alloc_size;
-      if (object_size > 0) {
-        alloc_size = object_size;
-      } else {
-        alloc_size = test_rand(&rand_seed) % static_cast<size_t>(-object_size);
-        // Note the minimum size, which is the size of a zero-length byte array.
-        size_t size_of_zero_length_byte_array = SizeOfZeroLengthByteArray();
-        if (alloc_size < size_of_zero_length_byte_array) {
-          alloc_size = size_of_zero_length_byte_array;
-        }
-      }
-      mirror::Object* object;
-      size_t bytes_allocated = 0;
-      if (round <= 1) {
-        object = space->Alloc(self, alloc_size, &bytes_allocated);
-      } else {
-        object = space->AllocWithGrowth(self, alloc_size, &bytes_allocated);
-      }
-      footprint = space->GetFootprint();
-      EXPECT_GE(space->Size(), footprint);  // invariant
-      if (object != NULL) {  // allocation succeeded
-        InstallClass(object, alloc_size);
-        lots_of_objects.get()[i] = object;
-        size_t allocation_size = space->AllocationSize(object);
-        EXPECT_EQ(bytes_allocated, allocation_size);
-        if (object_size > 0) {
-          EXPECT_GE(allocation_size, static_cast<size_t>(object_size));
-        } else {
-          EXPECT_GE(allocation_size, 8u);
-        }
-        amount_allocated += allocation_size;
-        break;
-      }
-    }
-    if (alloc_fails == max_fails) {
-      last_object = i;
-      break;
-    }
-  }
-  CHECK_NE(last_object, 0u);  // we should have filled the space
-  EXPECT_GT(amount_allocated, 0u);
-
-  // We shouldn't have gone past the growth_limit
-  EXPECT_LE(amount_allocated, growth_limit);
-  EXPECT_LE(footprint, growth_limit);
-  EXPECT_LE(space->Size(), growth_limit);
-
-  // footprint and size should agree with amount allocated
-  EXPECT_GE(footprint, amount_allocated);
-  EXPECT_GE(space->Size(), amount_allocated);
-
-  // Release storage in a semi-adhoc manner
-  size_t free_increment = 96;
-  while (true) {
-    // Give the space a haircut
-    space->Trim();
-
-    // Bounds sanity
-    footprint = space->GetFootprint();
-    EXPECT_LE(amount_allocated, growth_limit);
-    EXPECT_GE(footprint, amount_allocated);
-    EXPECT_LE(footprint, growth_limit);
-    EXPECT_GE(space->Size(), amount_allocated);
-    EXPECT_LE(space->Size(), growth_limit);
-
-    if (free_increment == 0) {
-      break;
-    }
-
-    {
-      // Free some objects
-      ScopedObjectAccess soa(self);
-      for (size_t i = 0; i < last_object; i += free_increment) {
-        mirror::Object* object = lots_of_objects.get()[i];
-        if (object == NULL) {
-          continue;
-        }
-        size_t allocation_size = space->AllocationSize(object);
-        if (object_size > 0) {
-          EXPECT_GE(allocation_size, static_cast<size_t>(object_size));
-        } else {
-          EXPECT_GE(allocation_size, 8u);
-        }
-        space->Free(self, object);
-        lots_of_objects.get()[i] = NULL;
-        amount_allocated -= allocation_size;
-        footprint = space->GetFootprint();
-        EXPECT_GE(space->Size(), footprint);  // invariant
-      }
-
-      free_increment >>= 1;
-    }
-  }
-  // The space has become empty here before allocating a large object
-  // below. For RosAlloc, revoke thread-local runs, which are kept
-  // even when empty for a performance reason, so that they won't
-  // cause the following large object allocation to fail due to
-  // potential fragmentation. Note they are normally revoked at each
-  // GC (but no GC here.)
-  space->RevokeAllThreadLocalBuffers();
-
-  // All memory was released, try a large allocation to check freed memory is being coalesced
-  mirror::Object* large_object;
-  size_t three_quarters_space = (growth_limit / 2) + (growth_limit / 4);
-  size_t bytes_allocated = 0;
-  if (round <= 1) {
-    large_object = space->Alloc(self, three_quarters_space, &bytes_allocated);
-  } else {
-    large_object = space->AllocWithGrowth(self, three_quarters_space, &bytes_allocated);
-  }
-  EXPECT_TRUE(large_object != NULL);
-  InstallClass(large_object, three_quarters_space);
-
-  // Sanity check footprint
-  footprint = space->GetFootprint();
-  EXPECT_LE(footprint, growth_limit);
-  EXPECT_GE(space->Size(), footprint);
-  EXPECT_LE(space->Size(), growth_limit);
-
-  // Clean up
-  {
-    ScopedObjectAccess soa(self);
-    space->Free(self, large_object);
-  }
-  // Sanity check footprint
-  footprint = space->GetFootprint();
-  EXPECT_LE(footprint, growth_limit);
-  EXPECT_GE(space->Size(), footprint);
-  EXPECT_LE(space->Size(), growth_limit);
-}
-
-void SpaceTest::SizeFootPrintGrowthLimitAndTrimDriver(size_t object_size, CreateSpaceFn create_space) {
-  if (object_size < SizeOfZeroLengthByteArray()) {
-    // Too small for the object layout/model.
-    return;
-  }
-  size_t initial_size = 4 * MB;
-  size_t growth_limit = 8 * MB;
-  size_t capacity = 16 * MB;
-  MallocSpace* space(create_space("test", initial_size, growth_limit, capacity, NULL));
-  ASSERT_TRUE(space != NULL);
-
-  // Basic sanity
-  EXPECT_EQ(space->Capacity(), growth_limit);
-  EXPECT_EQ(space->NonGrowthLimitCapacity(), capacity);
-
-  // Make space findable to the heap, will also delete space when runtime is cleaned up
-  AddSpace(space);
-
-  // In this round we don't allocate with growth and therefore can't grow past the initial size.
-  // This effectively makes the growth_limit the initial_size, so assert this.
-  SizeFootPrintGrowthLimitAndTrimBody(space, object_size, 1, initial_size);
-  SizeFootPrintGrowthLimitAndTrimBody(space, object_size, 2, growth_limit);
-  // Remove growth limit
-  space->ClearGrowthLimit();
-  EXPECT_EQ(space->Capacity(), capacity);
-  SizeFootPrintGrowthLimitAndTrimBody(space, object_size, 3, capacity);
-}
-
-#define TEST_SizeFootPrintGrowthLimitAndTrim(name, size) \
-  TEST_F(SpaceTest, SizeFootPrintGrowthLimitAndTrim_AllocationsOf_##name##_DlMallocSpace) { \
-    SizeFootPrintGrowthLimitAndTrimDriver(size, SpaceTest::CreateDlMallocSpace); \
-  } \
-  TEST_F(SpaceTest, SizeFootPrintGrowthLimitAndTrim_RandomAllocationsWithMax_##name##_DlMallocSpace) { \
-    SizeFootPrintGrowthLimitAndTrimDriver(-size, SpaceTest::CreateDlMallocSpace); \
-  } \
-  TEST_F(SpaceTest, SizeFootPrintGrowthLimitAndTrim_AllocationsOf_##name##_RosAllocSpace) { \
-    SizeFootPrintGrowthLimitAndTrimDriver(size, SpaceTest::CreateRosAllocSpace); \
-  } \
-  TEST_F(SpaceTest, SizeFootPrintGrowthLimitAndTrim_RandomAllocationsWithMax_##name##_RosAllocSpace) { \
-    SizeFootPrintGrowthLimitAndTrimDriver(-size, SpaceTest::CreateRosAllocSpace); \
-  }
-
-// Each size test is its own test so that we get a fresh heap each time
-TEST_F(SpaceTest, SizeFootPrintGrowthLimitAndTrim_AllocationsOf_12B_DlMallocSpace) {
-  SizeFootPrintGrowthLimitAndTrimDriver(12, SpaceTest::CreateDlMallocSpace);
-}
-TEST_F(SpaceTest, SizeFootPrintGrowthLimitAndTrim_AllocationsOf_12B_RosAllocSpace) {
-  SizeFootPrintGrowthLimitAndTrimDriver(12, SpaceTest::CreateRosAllocSpace);
-}
-TEST_SizeFootPrintGrowthLimitAndTrim(16B, 16)
-TEST_SizeFootPrintGrowthLimitAndTrim(24B, 24)
-TEST_SizeFootPrintGrowthLimitAndTrim(32B, 32)
-TEST_SizeFootPrintGrowthLimitAndTrim(64B, 64)
-TEST_SizeFootPrintGrowthLimitAndTrim(128B, 128)
-TEST_SizeFootPrintGrowthLimitAndTrim(1KB, 1 * KB)
-TEST_SizeFootPrintGrowthLimitAndTrim(4KB, 4 * KB)
-TEST_SizeFootPrintGrowthLimitAndTrim(1MB, 1 * MB)
-TEST_SizeFootPrintGrowthLimitAndTrim(4MB, 4 * MB)
-TEST_SizeFootPrintGrowthLimitAndTrim(8MB, 8 * MB)
-
-}  // namespace space
-}  // namespace gc
-}  // namespace art
diff --git a/runtime/gc/space/space_test.h b/runtime/gc/space/space_test.h
new file mode 100644
index 0000000..093967e
--- /dev/null
+++ b/runtime/gc/space/space_test.h
@@ -0,0 +1,543 @@
+/*
+ * Copyright (C) 2011 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_GC_SPACE_SPACE_TEST_H_
+#define ART_RUNTIME_GC_SPACE_SPACE_TEST_H_
+
+#include "zygote_space.h"
+
+#include "common_test.h"
+#include "globals.h"
+#include "UniquePtr.h"
+#include "mirror/array-inl.h"
+#include "mirror/object-inl.h"
+
+#include <stdint.h>
+
+namespace art {
+namespace gc {
+namespace space {
+
+class SpaceTest : public CommonTest {
+ public:
+  void AddSpace(ContinuousSpace* space) {
+    // For RosAlloc, revoke the thread local runs before moving onto a
+    // new alloc space.
+    Runtime::Current()->GetHeap()->RevokeAllThreadLocalBuffers();
+    Runtime::Current()->GetHeap()->AddSpace(space);
+  }
+  void InstallClass(SirtRef<mirror::Object>& o, size_t size)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    // Note the minimum size, which is the size of a zero-length byte array.
+    EXPECT_GE(size, SizeOfZeroLengthByteArray());
+    Thread* self = Thread::Current();
+    SirtRef<mirror::ClassLoader> null_loader(self, nullptr);
+    mirror::Class* byte_array_class = Runtime::Current()->GetClassLinker()->FindClass(self, "[B",
+                                                                                      null_loader);
+    EXPECT_TRUE(byte_array_class != nullptr);
+    o->SetClass(byte_array_class);
+    mirror::Array* arr = o->AsArray<kVerifyNone>();
+    size_t header_size = SizeOfZeroLengthByteArray();
+    int32_t length = size - header_size;
+    arr->SetLength(length);
+    EXPECT_EQ(arr->SizeOf<kVerifyNone>(), size);
+  }
+
+  static size_t SizeOfZeroLengthByteArray() {
+    return mirror::Array::DataOffset(Primitive::ComponentSize(Primitive::kPrimByte)).Uint32Value();
+  }
+
+  typedef MallocSpace* (*CreateSpaceFn)(const std::string& name, size_t initial_size, size_t growth_limit,
+                                        size_t capacity, byte* requested_begin);
+  void InitTestBody(CreateSpaceFn create_space);
+  void ZygoteSpaceTestBody(CreateSpaceFn create_space);
+  void AllocAndFreeTestBody(CreateSpaceFn create_space);
+  void AllocAndFreeListTestBody(CreateSpaceFn create_space);
+
+  void SizeFootPrintGrowthLimitAndTrimBody(MallocSpace* space, intptr_t object_size,
+                                           int round, size_t growth_limit);
+  void SizeFootPrintGrowthLimitAndTrimDriver(size_t object_size, CreateSpaceFn create_space);
+};
+
+static size_t test_rand(size_t* seed) {
+  *seed = *seed * 1103515245 + 12345;
+  return *seed;
+}
+
+void SpaceTest::InitTestBody(CreateSpaceFn create_space) {
+  {
+    // Init < max == growth
+    UniquePtr<Space> space(create_space("test", 16 * MB, 32 * MB, 32 * MB, nullptr));
+    EXPECT_TRUE(space.get() != nullptr);
+  }
+  {
+    // Init == max == growth
+    UniquePtr<Space> space(create_space("test", 16 * MB, 16 * MB, 16 * MB, nullptr));
+    EXPECT_TRUE(space.get() != nullptr);
+  }
+  {
+    // Init > max == growth
+    UniquePtr<Space> space(create_space("test", 32 * MB, 16 * MB, 16 * MB, nullptr));
+    EXPECT_TRUE(space.get() == nullptr);
+  }
+  {
+    // Growth == init < max
+    UniquePtr<Space> space(create_space("test", 16 * MB, 16 * MB, 32 * MB, nullptr));
+    EXPECT_TRUE(space.get() != nullptr);
+  }
+  {
+    // Growth < init < max
+    UniquePtr<Space> space(create_space("test", 16 * MB, 8 * MB, 32 * MB, nullptr));
+    EXPECT_TRUE(space.get() == nullptr);
+  }
+  {
+    // Init < growth < max
+    UniquePtr<Space> space(create_space("test", 8 * MB, 16 * MB, 32 * MB, nullptr));
+    EXPECT_TRUE(space.get() != nullptr);
+  }
+  {
+    // Init < max < growth
+    UniquePtr<Space> space(create_space("test", 8 * MB, 32 * MB, 16 * MB, nullptr));
+    EXPECT_TRUE(space.get() == nullptr);
+  }
+}
+
+// TODO: This test is not very good, we should improve it.
+// The test should do more allocations before the creation of the ZygoteSpace, and then do
+// allocations after the ZygoteSpace is created. The test should also do some GCs to ensure that
+// the GC works with the ZygoteSpace.
+void SpaceTest::ZygoteSpaceTestBody(CreateSpaceFn create_space) {
+  size_t dummy = 0;
+  MallocSpace* space(create_space("test", 4 * MB, 16 * MB, 16 * MB, nullptr));
+  ASSERT_TRUE(space != nullptr);
+
+  // Make space findable to the heap, will also delete space when runtime is cleaned up
+  AddSpace(space);
+  Thread* self = Thread::Current();
+  ScopedObjectAccess soa(self);
+
+  // Succeeds, fits without adjusting the footprint limit.
+  SirtRef<mirror::Object> ptr1(self, space->Alloc(self, 1 * MB, &dummy));
+  EXPECT_TRUE(ptr1.get() != nullptr);
+  InstallClass(ptr1, 1 * MB);
+
+  // Fails, requires a higher footprint limit.
+  mirror::Object* ptr2 = space->Alloc(self, 8 * MB, &dummy);
+  EXPECT_TRUE(ptr2 == nullptr);
+
+  // Succeeds, adjusts the footprint.
+  size_t ptr3_bytes_allocated;
+  SirtRef<mirror::Object> ptr3(self, space->AllocWithGrowth(self, 8 * MB, &ptr3_bytes_allocated));
+  EXPECT_TRUE(ptr3.get() != nullptr);
+  EXPECT_LE(8U * MB, ptr3_bytes_allocated);
+  InstallClass(ptr3, 8 * MB);
+
+  // Fails, requires a higher footprint limit.
+  mirror::Object* ptr4 = space->Alloc(self, 8 * MB, &dummy);
+  EXPECT_TRUE(ptr4 == nullptr);
+
+  // Also fails, requires a higher allowed footprint.
+  mirror::Object* ptr5 = space->AllocWithGrowth(self, 8 * MB, &dummy);
+  EXPECT_TRUE(ptr5 == nullptr);
+
+  // Release some memory.
+  size_t free3 = space->AllocationSize(ptr3.get());
+  EXPECT_EQ(free3, ptr3_bytes_allocated);
+  EXPECT_EQ(free3, space->Free(self, ptr3.reset(nullptr)));
+  EXPECT_LE(8U * MB, free3);
+
+  // Succeeds, now that memory has been freed.
+  SirtRef<mirror::Object> ptr6(self, space->AllocWithGrowth(self, 9 * MB, &dummy));
+  EXPECT_TRUE(ptr6.get() != nullptr);
+  InstallClass(ptr6, 9 * MB);
+
+  // Final clean up.
+  size_t free1 = space->AllocationSize(ptr1.get());
+  space->Free(self, ptr1.reset(nullptr));
+  EXPECT_LE(1U * MB, free1);
+
+  // Make sure that the zygote space isn't directly at the start of the space.
+  space->Alloc(self, 1U * MB, &dummy);
+
+  gc::Heap* heap = Runtime::Current()->GetHeap();
+  space::Space* old_space = space;
+  heap->RemoveSpace(old_space);
+  space::ZygoteSpace* zygote_space = space->CreateZygoteSpace("alloc space",
+                                                              heap->IsLowMemoryMode(),
+                                                              &space);
+  delete old_space;
+  // Add the zygote space.
+  AddSpace(zygote_space);
+
+  // Make space findable to the heap, will also delete space when runtime is cleaned up
+  AddSpace(space);
+
+  // Succeeds, fits without adjusting the footprint limit.
+  ptr1.reset(space->Alloc(self, 1 * MB, &dummy));
+  EXPECT_TRUE(ptr1.get() != nullptr);
+  InstallClass(ptr1, 1 * MB);
+
+  // Fails, requires a higher footprint limit.
+  ptr2 = space->Alloc(self, 8 * MB, &dummy);
+  EXPECT_TRUE(ptr2 == nullptr);
+
+  // Succeeds, adjusts the footprint.
+  ptr3.reset(space->AllocWithGrowth(self, 2 * MB, &dummy));
+  EXPECT_TRUE(ptr3.get() != nullptr);
+  InstallClass(ptr3, 2 * MB);
+  space->Free(self, ptr3.reset(nullptr));
+
+  // Final clean up.
+  free1 = space->AllocationSize(ptr1.get());
+  space->Free(self, ptr1.reset(nullptr));
+  EXPECT_LE(1U * MB, free1);
+}
+
+void SpaceTest::AllocAndFreeTestBody(CreateSpaceFn create_space) {
+  size_t dummy = 0;
+  MallocSpace* space(create_space("test", 4 * MB, 16 * MB, 16 * MB, nullptr));
+  ASSERT_TRUE(space != nullptr);
+  Thread* self = Thread::Current();
+  ScopedObjectAccess soa(self);
+
+  // Make space findable to the heap, will also delete space when runtime is cleaned up
+  AddSpace(space);
+
+  // Succeeds, fits without adjusting the footprint limit.
+  SirtRef<mirror::Object> ptr1(self, space->Alloc(self, 1 * MB, &dummy));
+  EXPECT_TRUE(ptr1.get() != nullptr);
+  InstallClass(ptr1, 1 * MB);
+
+  // Fails, requires a higher footprint limit.
+  mirror::Object* ptr2 = space->Alloc(self, 8 * MB, &dummy);
+  EXPECT_TRUE(ptr2 == nullptr);
+
+  // Succeeds, adjusts the footprint.
+  size_t ptr3_bytes_allocated;
+  SirtRef<mirror::Object> ptr3(self, space->AllocWithGrowth(self, 8 * MB, &ptr3_bytes_allocated));
+  EXPECT_TRUE(ptr3.get() != nullptr);
+  EXPECT_LE(8U * MB, ptr3_bytes_allocated);
+  InstallClass(ptr3, 8 * MB);
+
+  // Fails, requires a higher footprint limit.
+  mirror::Object* ptr4 = space->Alloc(self, 8 * MB, &dummy);
+  EXPECT_TRUE(ptr4 == nullptr);
+
+  // Also fails, requires a higher allowed footprint.
+  mirror::Object* ptr5 = space->AllocWithGrowth(self, 8 * MB, &dummy);
+  EXPECT_TRUE(ptr5 == nullptr);
+
+  // Release some memory.
+  size_t free3 = space->AllocationSize(ptr3.get());
+  EXPECT_EQ(free3, ptr3_bytes_allocated);
+  space->Free(self, ptr3.reset(nullptr));
+  EXPECT_LE(8U * MB, free3);
+
+  // Succeeds, now that memory has been freed.
+  SirtRef<mirror::Object> ptr6(self, space->AllocWithGrowth(self, 9 * MB, &dummy));
+  EXPECT_TRUE(ptr6.get() != nullptr);
+  InstallClass(ptr6, 9 * MB);
+
+  // Final clean up.
+  size_t free1 = space->AllocationSize(ptr1.get());
+  space->Free(self, ptr1.reset(nullptr));
+  EXPECT_LE(1U * MB, free1);
+}
+
+void SpaceTest::AllocAndFreeListTestBody(CreateSpaceFn create_space) {
+  MallocSpace* space(create_space("test", 4 * MB, 16 * MB, 16 * MB, nullptr));
+  ASSERT_TRUE(space != nullptr);
+
+  // Make space findable to the heap, will also delete space when runtime is cleaned up
+  AddSpace(space);
+  Thread* self = Thread::Current();
+  ScopedObjectAccess soa(self);
+
+  // Succeeds, fits without adjusting the max allowed footprint.
+  mirror::Object* lots_of_objects[1024];
+  for (size_t i = 0; i < arraysize(lots_of_objects); i++) {
+    size_t allocation_size = 0;
+    size_t size_of_zero_length_byte_array = SizeOfZeroLengthByteArray();
+    lots_of_objects[i] = space->Alloc(self, size_of_zero_length_byte_array, &allocation_size);
+    EXPECT_TRUE(lots_of_objects[i] != nullptr);
+    SirtRef<mirror::Object> obj(self, lots_of_objects[i]);
+    InstallClass(obj, size_of_zero_length_byte_array);
+    lots_of_objects[i] = obj.get();
+    EXPECT_EQ(allocation_size, space->AllocationSize(lots_of_objects[i]));
+  }
+
+  // Release memory and check pointers are nullptr.
+  space->FreeList(self, arraysize(lots_of_objects), lots_of_objects);
+  for (size_t i = 0; i < arraysize(lots_of_objects); i++) {
+    EXPECT_TRUE(lots_of_objects[i] == nullptr);
+  }
+
+  // Succeeds, fits by adjusting the max allowed footprint.
+  for (size_t i = 0; i < arraysize(lots_of_objects); i++) {
+    size_t allocation_size = 0;
+    lots_of_objects[i] = space->AllocWithGrowth(self, 1024, &allocation_size);
+    EXPECT_TRUE(lots_of_objects[i] != nullptr);
+    SirtRef<mirror::Object> obj(self, lots_of_objects[i]);
+    InstallClass(obj, 1024);
+    lots_of_objects[i] = obj.get();
+    EXPECT_EQ(allocation_size, space->AllocationSize(lots_of_objects[i]));
+  }
+
+  // Release memory and check pointers are nullptr
+  // TODO: This isn't compaction safe, fix.
+  space->FreeList(self, arraysize(lots_of_objects), lots_of_objects);
+  for (size_t i = 0; i < arraysize(lots_of_objects); i++) {
+    EXPECT_TRUE(lots_of_objects[i] == nullptr);
+  }
+}
+
+void SpaceTest::SizeFootPrintGrowthLimitAndTrimBody(MallocSpace* space, intptr_t object_size,
+                                                    int round, size_t growth_limit) {
+  if (((object_size > 0 && object_size >= static_cast<intptr_t>(growth_limit))) ||
+      ((object_size < 0 && -object_size >= static_cast<intptr_t>(growth_limit)))) {
+    // No allocation can succeed
+    return;
+  }
+
+  // The space's footprint equals amount of resources requested from system
+  size_t footprint = space->GetFootprint();
+
+  // The space must at least have its book keeping allocated
+  EXPECT_GT(footprint, 0u);
+
+  // But it shouldn't exceed the initial size
+  EXPECT_LE(footprint, growth_limit);
+
+  // space's size shouldn't exceed the initial size
+  EXPECT_LE(space->Size(), growth_limit);
+
+  // this invariant should always hold or else the space has grown to be larger than what the
+  // space believes its size is (which will break invariants)
+  EXPECT_GE(space->Size(), footprint);
+
+  // Fill the space with lots of small objects up to the growth limit
+  size_t max_objects = (growth_limit / (object_size > 0 ? object_size : 8)) + 1;
+  UniquePtr<mirror::Object*[]> lots_of_objects(new mirror::Object*[max_objects]);
+  size_t last_object = 0;  // last object for which allocation succeeded
+  size_t amount_allocated = 0;  // amount of space allocated
+  Thread* self = Thread::Current();
+  ScopedObjectAccess soa(self);
+  size_t rand_seed = 123456789;
+  for (size_t i = 0; i < max_objects; i++) {
+    size_t alloc_fails = 0;  // number of failed allocations
+    size_t max_fails = 30;  // number of times we fail allocation before giving up
+    for (; alloc_fails < max_fails; alloc_fails++) {
+      size_t alloc_size;
+      if (object_size > 0) {
+        alloc_size = object_size;
+      } else {
+        alloc_size = test_rand(&rand_seed) % static_cast<size_t>(-object_size);
+        // Note the minimum size, which is the size of a zero-length byte array.
+        size_t size_of_zero_length_byte_array = SizeOfZeroLengthByteArray();
+        if (alloc_size < size_of_zero_length_byte_array) {
+          alloc_size = size_of_zero_length_byte_array;
+        }
+      }
+      SirtRef<mirror::Object> object(self, nullptr);
+      size_t bytes_allocated = 0;
+      if (round <= 1) {
+        object.reset(space->Alloc(self, alloc_size, &bytes_allocated));
+      } else {
+        object.reset(space->AllocWithGrowth(self, alloc_size, &bytes_allocated));
+      }
+      footprint = space->GetFootprint();
+      EXPECT_GE(space->Size(), footprint);  // invariant
+      if (object.get() != nullptr) {  // allocation succeeded
+        InstallClass(object, alloc_size);
+        lots_of_objects[i] = object.get();
+        size_t allocation_size = space->AllocationSize(object.get());
+        EXPECT_EQ(bytes_allocated, allocation_size);
+        if (object_size > 0) {
+          EXPECT_GE(allocation_size, static_cast<size_t>(object_size));
+        } else {
+          EXPECT_GE(allocation_size, 8u);
+        }
+        amount_allocated += allocation_size;
+        break;
+      }
+    }
+    if (alloc_fails == max_fails) {
+      last_object = i;
+      break;
+    }
+  }
+  CHECK_NE(last_object, 0u);  // we should have filled the space
+  EXPECT_GT(amount_allocated, 0u);
+
+  // We shouldn't have gone past the growth_limit
+  EXPECT_LE(amount_allocated, growth_limit);
+  EXPECT_LE(footprint, growth_limit);
+  EXPECT_LE(space->Size(), growth_limit);
+
+  // footprint and size should agree with amount allocated
+  EXPECT_GE(footprint, amount_allocated);
+  EXPECT_GE(space->Size(), amount_allocated);
+
+  // Release storage in a semi-adhoc manner
+  size_t free_increment = 96;
+  while (true) {
+    {
+      ScopedThreadStateChange tsc(self, kNative);
+      // Give the space a haircut.
+      space->Trim();
+    }
+
+    // Bounds sanity
+    footprint = space->GetFootprint();
+    EXPECT_LE(amount_allocated, growth_limit);
+    EXPECT_GE(footprint, amount_allocated);
+    EXPECT_LE(footprint, growth_limit);
+    EXPECT_GE(space->Size(), amount_allocated);
+    EXPECT_LE(space->Size(), growth_limit);
+
+    if (free_increment == 0) {
+      break;
+    }
+
+    // Free some objects
+    for (size_t i = 0; i < last_object; i += free_increment) {
+      mirror::Object* object = lots_of_objects.get()[i];
+      if (object == nullptr) {
+        continue;
+      }
+      size_t allocation_size = space->AllocationSize(object);
+      if (object_size > 0) {
+        EXPECT_GE(allocation_size, static_cast<size_t>(object_size));
+      } else {
+        EXPECT_GE(allocation_size, 8u);
+      }
+      space->Free(self, object);
+      lots_of_objects.get()[i] = nullptr;
+      amount_allocated -= allocation_size;
+      footprint = space->GetFootprint();
+      EXPECT_GE(space->Size(), footprint);  // invariant
+    }
+
+    free_increment >>= 1;
+  }
+
+  // The space has become empty here before allocating a large object
+  // below. For RosAlloc, revoke thread-local runs, which are kept
+  // even when empty for a performance reason, so that they won't
+  // cause the following large object allocation to fail due to
+  // potential fragmentation. Note they are normally revoked at each
+  // GC (but no GC here.)
+  space->RevokeAllThreadLocalBuffers();
+
+  // All memory was released, try a large allocation to check freed memory is being coalesced
+  SirtRef<mirror::Object> large_object(self, nullptr);
+  size_t three_quarters_space = (growth_limit / 2) + (growth_limit / 4);
+  size_t bytes_allocated = 0;
+  if (round <= 1) {
+    large_object.reset(space->Alloc(self, three_quarters_space, &bytes_allocated));
+  } else {
+    large_object.reset(space->AllocWithGrowth(self, three_quarters_space, &bytes_allocated));
+  }
+  EXPECT_TRUE(large_object.get() != nullptr);
+  InstallClass(large_object, three_quarters_space);
+
+  // Sanity check footprint
+  footprint = space->GetFootprint();
+  EXPECT_LE(footprint, growth_limit);
+  EXPECT_GE(space->Size(), footprint);
+  EXPECT_LE(space->Size(), growth_limit);
+
+  // Clean up
+  space->Free(self, large_object.reset(nullptr));
+
+  // Sanity check footprint
+  footprint = space->GetFootprint();
+  EXPECT_LE(footprint, growth_limit);
+  EXPECT_GE(space->Size(), footprint);
+  EXPECT_LE(space->Size(), growth_limit);
+}
+
+void SpaceTest::SizeFootPrintGrowthLimitAndTrimDriver(size_t object_size, CreateSpaceFn create_space) {
+  if (object_size < SizeOfZeroLengthByteArray()) {
+    // Too small for the object layout/model.
+    return;
+  }
+  size_t initial_size = 4 * MB;
+  size_t growth_limit = 8 * MB;
+  size_t capacity = 16 * MB;
+  MallocSpace* space(create_space("test", initial_size, growth_limit, capacity, nullptr));
+  ASSERT_TRUE(space != nullptr);
+
+  // Basic sanity
+  EXPECT_EQ(space->Capacity(), growth_limit);
+  EXPECT_EQ(space->NonGrowthLimitCapacity(), capacity);
+
+  // Make space findable to the heap, will also delete space when runtime is cleaned up
+  AddSpace(space);
+
+  // In this round we don't allocate with growth and therefore can't grow past the initial size.
+  // This effectively makes the growth_limit the initial_size, so assert this.
+  SizeFootPrintGrowthLimitAndTrimBody(space, object_size, 1, initial_size);
+  SizeFootPrintGrowthLimitAndTrimBody(space, object_size, 2, growth_limit);
+  // Remove growth limit
+  space->ClearGrowthLimit();
+  EXPECT_EQ(space->Capacity(), capacity);
+  SizeFootPrintGrowthLimitAndTrimBody(space, object_size, 3, capacity);
+}
+
+#define TEST_SizeFootPrintGrowthLimitAndTrim(name, spaceName, spaceFn, size) \
+  TEST_F(spaceName##Test, SizeFootPrintGrowthLimitAndTrim_AllocationsOf_##name) { \
+    SizeFootPrintGrowthLimitAndTrimDriver(size, spaceFn); \
+  } \
+  TEST_F(spaceName##Test, SizeFootPrintGrowthLimitAndTrim_RandomAllocationsWithMax_##name) { \
+    SizeFootPrintGrowthLimitAndTrimDriver(-size, spaceFn); \
+  }
+
+#define TEST_SPACE_CREATE_FN(spaceName, spaceFn) \
+  class spaceName##Test : public SpaceTest { \
+  }; \
+  \
+  TEST_F(spaceName##Test, Init) { \
+    InitTestBody(spaceFn); \
+  } \
+  TEST_F(spaceName##Test, ZygoteSpace) { \
+    ZygoteSpaceTestBody(spaceFn); \
+  } \
+  TEST_F(spaceName##Test, AllocAndFree) { \
+    AllocAndFreeTestBody(spaceFn); \
+  } \
+  TEST_F(spaceName##Test, AllocAndFreeList) { \
+    AllocAndFreeListTestBody(spaceFn); \
+  } \
+  TEST_F(spaceName##Test, SizeFootPrintGrowthLimitAndTrim_AllocationsOf_12B) { \
+    SizeFootPrintGrowthLimitAndTrimDriver(12, spaceFn); \
+  } \
+  TEST_SizeFootPrintGrowthLimitAndTrim(16B, spaceName, spaceFn, 16) \
+  TEST_SizeFootPrintGrowthLimitAndTrim(24B, spaceName, spaceFn, 24) \
+  TEST_SizeFootPrintGrowthLimitAndTrim(32B, spaceName, spaceFn, 32) \
+  TEST_SizeFootPrintGrowthLimitAndTrim(64B, spaceName, spaceFn, 64) \
+  TEST_SizeFootPrintGrowthLimitAndTrim(128B, spaceName, spaceFn, 128) \
+  TEST_SizeFootPrintGrowthLimitAndTrim(1KB, spaceName, spaceFn, 1 * KB) \
+  TEST_SizeFootPrintGrowthLimitAndTrim(4KB, spaceName, spaceFn, 4 * KB) \
+  TEST_SizeFootPrintGrowthLimitAndTrim(1MB, spaceName, spaceFn, 1 * MB) \
+  TEST_SizeFootPrintGrowthLimitAndTrim(4MB, spaceName, spaceFn, 4 * MB) \
+  TEST_SizeFootPrintGrowthLimitAndTrim(8MB, spaceName, spaceFn, 8 * MB)
+
+}  // namespace space
+}  // namespace gc
+}  // namespace art
+
+#endif  // ART_RUNTIME_GC_SPACE_SPACE_TEST_H_
diff --git a/runtime/indirect_reference_table_test.cc b/runtime/indirect_reference_table_test.cc
index b6c6cb4..78e1992 100644
--- a/runtime/indirect_reference_table_test.cc
+++ b/runtime/indirect_reference_table_test.cc
@@ -48,7 +48,7 @@
   static const size_t kTableMax = 20;
   IndirectReferenceTable irt(kTableInitial, kTableMax, kGlobal);
 
-  mirror::Class* c = class_linker_->FindSystemClass("Ljava/lang/Object;");
+  mirror::Class* c = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;");
   ASSERT_TRUE(c != NULL);
   mirror::Object* obj0 = c->AllocObject(soa.Self());
   ASSERT_TRUE(obj0 != NULL);
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index 59ffdc1..9d05169 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -145,12 +145,10 @@
 static void InstrumentationInstallStack(Thread* thread, void* arg)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   struct InstallStackVisitor : public StackVisitor {
-    InstallStackVisitor(Thread* thread, Context* context, uintptr_t instrumentation_exit_pc,
-                        bool is_deoptimization_enabled)
+    InstallStackVisitor(Thread* thread, Context* context, uintptr_t instrumentation_exit_pc)
         : StackVisitor(thread, context),  instrumentation_stack_(thread->GetInstrumentationStack()),
           existing_instrumentation_frames_count_(instrumentation_stack_->size()),
           instrumentation_exit_pc_(instrumentation_exit_pc),
-          is_deoptimization_enabled_(is_deoptimization_enabled),
           reached_existing_instrumentation_frames_(false), instrumentation_stack_depth_(0),
           last_return_pc_(0) {
     }
@@ -218,7 +216,6 @@
     const size_t existing_instrumentation_frames_count_;
     std::vector<uint32_t> dex_pcs_;
     const uintptr_t instrumentation_exit_pc_;
-    const bool is_deoptimization_enabled_;
     bool reached_existing_instrumentation_frames_;
     size_t instrumentation_stack_depth_;
     uintptr_t last_return_pc_;
@@ -232,12 +229,11 @@
   Instrumentation* instrumentation = reinterpret_cast<Instrumentation*>(arg);
   UniquePtr<Context> context(Context::Create());
   uintptr_t instrumentation_exit_pc = GetQuickInstrumentationExitPc();
-  InstallStackVisitor visitor(thread, context.get(), instrumentation_exit_pc,
-                              instrumentation->IsDeoptimizationEnabled());
+  InstallStackVisitor visitor(thread, context.get(), instrumentation_exit_pc);
   visitor.WalkStack(true);
   CHECK_EQ(visitor.dex_pcs_.size(), thread->GetInstrumentationStack()->size());
 
-  if (!instrumentation->IsDeoptimizationEnabled()) {
+  if (!instrumentation->ShouldNotifyMethodEnterExitEvents()) {
     // Create method enter events for all methods currently on the thread's stack. We only do this
     // if no debugger is attached to prevent from posting events twice.
     typedef std::deque<InstrumentationStackFrame>::const_reverse_iterator It;
@@ -295,7 +291,7 @@
             CHECK(m == instrumentation_frame.method_) << PrettyMethod(m);
           }
           SetReturnPc(instrumentation_frame.return_pc_);
-          if (!instrumentation_->IsDeoptimizationEnabled()) {
+          if (!instrumentation_->ShouldNotifyMethodEnterExitEvents()) {
             // Create the method exit events. As the methods didn't really exit the result is 0.
             // We only do this if no debugger is attached to prevent from posting events twice.
             instrumentation_->MethodExitEvent(thread_, instrumentation_frame.this_object_, m,
@@ -586,9 +582,12 @@
 
 void Instrumentation::EnableDeoptimization() {
   CHECK(deoptimized_methods_.empty());
+  CHECK_EQ(deoptimization_enabled_, false);
+  deoptimization_enabled_ = true;
 }
 
 void Instrumentation::DisableDeoptimization() {
+  CHECK_EQ(deoptimization_enabled_, true);
   // If we deoptimized everything, undo it.
   if (interpreter_stubs_installed_) {
     UndeoptimizeEverything();
@@ -599,10 +598,12 @@
     Undeoptimize(*it_begin);
   }
   CHECK(deoptimized_methods_.empty());
+  deoptimization_enabled_ = false;
 }
 
-bool Instrumentation::IsDeoptimizationEnabled() const {
-  return interpreter_stubs_installed_ || !deoptimized_methods_.empty();
+// Indicates if instrumentation should notify method enter/exit events to the listeners.
+bool Instrumentation::ShouldNotifyMethodEnterExitEvents() const {
+  return deoptimization_enabled_ || interpreter_stubs_installed_;
 }
 
 void Instrumentation::DeoptimizeEverything() {
diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h
index f01add1..1ce72bd 100644
--- a/runtime/instrumentation.h
+++ b/runtime/instrumentation.h
@@ -105,6 +105,7 @@
       have_method_entry_listeners_(false), have_method_exit_listeners_(false),
       have_method_unwind_listeners_(false), have_dex_pc_listeners_(false),
       have_exception_caught_listeners_(false),
+      deoptimization_enabled_(false),
       interpreter_handler_table_(kMainHandlerTable),
       quick_alloc_entry_points_instrumentation_counter_(0) {}
 
@@ -124,7 +125,7 @@
   // Deoptimization.
   void EnableDeoptimization() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
   void DisableDeoptimization() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
-  bool IsDeoptimizationEnabled() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  bool ShouldNotifyMethodEnterExitEvents() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Executes everything with interpreter.
   void DeoptimizeEverything()
@@ -345,6 +346,7 @@
   // only.
   // TODO we need to visit these methods as roots.
   std::set<mirror::ArtMethod*> deoptimized_methods_;
+  bool deoptimization_enabled_;
 
   // Current interpreter handler table. This is updated each time the thread state flags are
   // modified.
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index e37fb61..83a1fbc 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -31,7 +31,7 @@
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   // If both register locations contains the same value, the register probably holds a reference.
   int32_t src_value = shadow_frame.GetVReg(src_reg);
-  mirror::Object* o = shadow_frame.GetVRegReference<false>(src_reg);
+  mirror::Object* o = shadow_frame.GetVRegReference<kVerifyNone>(src_reg);
   if (src_value == reinterpret_cast<intptr_t>(o)) {
     new_shadow_frame->SetVRegReference(dest_reg, o);
   } else {
@@ -280,7 +280,7 @@
     std::string descriptor(DotToDescriptor(shadow_frame->GetVRegReference(arg_offset)->AsString()->ToModifiedUtf8().c_str()));
 
     SirtRef<ClassLoader> class_loader(self, nullptr);  // shadow_frame.GetMethod()->GetDeclaringClass()->GetClassLoader();
-    Class* found = Runtime::Current()->GetClassLinker()->FindClass(descriptor.c_str(),
+    Class* found = Runtime::Current()->GetClassLinker()->FindClass(self, descriptor.c_str(),
                                                                    class_loader);
     CHECK(found != NULL) << "Class.forName failed in un-started runtime for class: "
         << PrettyDescriptor(descriptor);
@@ -289,7 +289,7 @@
     SirtRef<ClassLoader> class_loader(self, down_cast<mirror::ClassLoader*>(shadow_frame->GetVRegReference(arg_offset)));
     std::string descriptor(DotToDescriptor(shadow_frame->GetVRegReference(arg_offset + 1)->AsString()->ToModifiedUtf8().c_str()));
 
-    Class* found = Runtime::Current()->GetClassLinker()->FindClass(descriptor.c_str(),
+    Class* found = Runtime::Current()->GetClassLinker()->FindClass(self, descriptor.c_str(),
                                                                    class_loader);
     result->SetL(found);
   } else if (name == "java.lang.Object java.lang.Class.newInstance()") {
diff --git a/runtime/jdwp/jdwp.h b/runtime/jdwp/jdwp.h
index e45cb6e..1ec795f 100644
--- a/runtime/jdwp/jdwp.h
+++ b/runtime/jdwp/jdwp.h
@@ -154,8 +154,9 @@
    * release it in the "clear" call.
    */
   // ObjectId GetWaitForEventThread();
-  void SetWaitForEventThread(ObjectId threadId);
-  void ClearWaitForEventThread();
+  void SetWaitForEventThread(ObjectId threadId)
+      LOCKS_EXCLUDED(event_thread_lock_, process_request_lock_);
+  void ClearWaitForEventThread() LOCKS_EXCLUDED(event_thread_lock);
 
   /*
    * These notify the debug code that something interesting has happened.  This
@@ -346,7 +347,7 @@
 
   // Used to synchronize request processing and event sending (to avoid sending an event before
   // sending the reply of a command being processed).
-  Mutex process_request_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+  Mutex process_request_lock_ ACQUIRED_AFTER(event_thread_lock_);
   ConditionVariable process_request_cond_ GUARDED_BY(process_request_lock_);
   bool processing_request_ GUARDED_BY(process_request_lock_);
 
diff --git a/runtime/jdwp/jdwp_handler.cc b/runtime/jdwp/jdwp_handler.cc
index 0ff78d0..4b170ba 100644
--- a/runtime/jdwp/jdwp_handler.cc
+++ b/runtime/jdwp/jdwp_handler.cc
@@ -1685,6 +1685,12 @@
   SetWaitForEventThread(0);
 
   /*
+   * We do not want events to be sent while we process a request. Indicate the JDWP thread starts
+   * to process a request so other threads wait for it to finish before sending an event.
+   */
+  StartProcessingRequest();
+
+  /*
    * Tell the VM that we're running and shouldn't be interrupted by GC.
    * Do this after anything that can stall indefinitely.
    */
@@ -1779,9 +1785,15 @@
   Thread* self = Thread::Current();
   CHECK_NE(self, GetDebugThread()) << "Events should not be posted by debug thread";
   MutexLock mu(self, process_request_lock_);
+  bool waited = false;
   while (processing_request_) {
+    VLOG(jdwp) << StringPrintf("wait for processing request");
+    waited = true;
     process_request_cond_.Wait(self);
   }
+  if (waited) {
+    VLOG(jdwp) << StringPrintf("finished waiting for processing request");
+  }
   CHECK_EQ(processing_request_, false);
 }
 
diff --git a/runtime/jdwp/jdwp_main.cc b/runtime/jdwp/jdwp_main.cc
index ba49c45..500585d 100644
--- a/runtime/jdwp/jdwp_main.cc
+++ b/runtime/jdwp/jdwp_main.cc
@@ -386,10 +386,14 @@
   JdwpNetStateBase* netStateBase = reinterpret_cast<JdwpNetStateBase*>(netState);
   JDWP::Request request(netStateBase->input_buffer_, netStateBase->input_count_);
 
-  StartProcessingRequest();
   ExpandBuf* pReply = expandBufAlloc();
   ProcessRequest(request, pReply);
   ssize_t cc = netStateBase->WritePacket(pReply);
+
+  /*
+   * We processed this request and sent its reply. Notify other threads waiting for us they can now
+   * send events.
+   */
   EndProcessingRequest();
 
   if (cc != (ssize_t) expandBufGetLength(pReply)) {
diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc
index 362df8c..76aa734 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -27,7 +27,7 @@
 #include "base/mutex.h"
 #include "base/stl_util.h"
 #include "base/stringpiece.h"
-#include "class_linker.h"
+#include "class_linker-inl.h"
 #include "dex_file-inl.h"
 #include "gc/accounting/card_table-inl.h"
 #include "interpreter/interpreter.h"
@@ -284,7 +284,7 @@
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
   if (sig[1] != '\0') {
     SirtRef<mirror::ClassLoader> class_loader(soa.Self(), c->GetClassLoader());
-    field_type = class_linker->FindClass(sig, class_loader);
+    field_type = class_linker->FindClass(soa.Self(), sig, class_loader);
   } else {
     field_type = class_linker->FindPrimitiveClass(*sig);
   }
@@ -651,9 +651,9 @@
     mirror::Class* c = nullptr;
     if (runtime->IsStarted()) {
       SirtRef<mirror::ClassLoader> class_loader(soa.Self(), GetClassLoader(soa));
-      c = class_linker->FindClass(descriptor.c_str(), class_loader);
+      c = class_linker->FindClass(soa.Self(), descriptor.c_str(), class_loader);
     } else {
-      c = class_linker->FindSystemClass(descriptor.c_str());
+      c = class_linker->FindSystemClass(soa.Self(), descriptor.c_str());
     }
     return soa.AddLocalReference<jclass>(c);
   }
@@ -2140,13 +2140,8 @@
                   PrettyDescriptor(element_class).c_str());
         return nullptr;
       }
-      std::string descriptor("[");
-      descriptor += ClassHelper(element_class).GetDescriptor();
-
-      // Find the class.
       ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-      SirtRef<mirror::ClassLoader> class_loader(soa.Self(), element_class->GetClassLoader());
-      array_class = class_linker->FindClass(descriptor.c_str(), class_loader);
+      array_class = class_linker->FindArrayClass(soa.Self(), element_class);
       if (UNLIKELY(array_class == nullptr)) {
         return nullptr;
       }
@@ -2509,8 +2504,7 @@
       JniAbortF("NewDirectByteBuffer", "non-zero capacity for nullptr pointer: %" PRId64, capacity);
     }
 
-    // At the moment, the Java side is limited to 32 bits.
-    CHECK_LE(reinterpret_cast<uintptr_t>(address), 0xffffffff);
+    // At the moment, the capacity is limited to 32 bits.
     CHECK_LE(capacity, 0xffffffff);
     jlong address_arg = reinterpret_cast<jlong>(address);
     jint capacity_arg = static_cast<jint>(capacity);
diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc
index 4c58c84..63bc45c 100644
--- a/runtime/jni_internal_test.cc
+++ b/runtime/jni_internal_test.cc
@@ -107,7 +107,8 @@
                      class_name);
     }
 
-    mirror::Class* c = class_linker_->FindClass(DotToDescriptor(class_name).c_str(), class_loader);
+    mirror::Class* c = class_linker_->FindClass(self, DotToDescriptor(class_name).c_str(),
+                                                class_loader);
     CHECK(c != NULL);
 
     *method = is_static ? c->FindDirectMethod(method_name, method_signature)
@@ -127,7 +128,7 @@
     mirror::Object* receiver;
     JniInternalTestMakeExecutable(&method, &receiver, is_static, "nop", "()V");
 
-    ArgArray arg_array(NULL, 0);
+    ArgArray arg_array("V", 1);
     JValue result;
 
     if (!is_static) {
@@ -143,7 +144,7 @@
     mirror::Object* receiver;
     JniInternalTestMakeExecutable(&method, &receiver, is_static, "identity", "(B)B");
 
-    ArgArray arg_array(NULL, 0);
+    ArgArray arg_array("BB", 2);
     uint32_t* args = arg_array.GetArray();
     JValue result;
 
@@ -179,7 +180,7 @@
     mirror::Object* receiver;
     JniInternalTestMakeExecutable(&method, &receiver, is_static, "identity", "(I)I");
 
-    ArgArray arg_array(NULL, 0);
+    ArgArray arg_array("II", 2);
     uint32_t* args = arg_array.GetArray();
     JValue result;
 
@@ -215,7 +216,7 @@
     mirror::Object* receiver;
     JniInternalTestMakeExecutable(&method, &receiver, is_static, "identity", "(D)D");
 
-    ArgArray arg_array(NULL, 0);
+    ArgArray arg_array("DD", 2);
     uint32_t* args = arg_array.GetArray();
     JValue value;
     JValue result;
@@ -259,7 +260,7 @@
     mirror::Object* receiver;
     JniInternalTestMakeExecutable(&method, &receiver, is_static, "sum", "(II)I");
 
-    ArgArray arg_array(NULL, 0);
+    ArgArray arg_array("III", 3);
     uint32_t* args = arg_array.GetArray();
     JValue result;
 
@@ -305,7 +306,7 @@
     mirror::Object* receiver;
     JniInternalTestMakeExecutable(&method, &receiver, is_static, "sum", "(III)I");
 
-    ArgArray arg_array(NULL, 0);
+    ArgArray arg_array("IIII", 4);
     uint32_t* args = arg_array.GetArray();
     JValue result;
 
@@ -361,7 +362,7 @@
     mirror::Object* receiver;
     JniInternalTestMakeExecutable(&method, &receiver, is_static, "sum", "(IIII)I");
 
-    ArgArray arg_array(NULL, 0);
+    ArgArray arg_array("IIIII", 5);
     uint32_t* args = arg_array.GetArray();
     JValue result;
 
@@ -422,7 +423,7 @@
     mirror::Object* receiver;
     JniInternalTestMakeExecutable(&method, &receiver, is_static, "sum", "(IIIII)I");
 
-    ArgArray arg_array(NULL, 0);
+    ArgArray arg_array("IIIIII", 6);
     uint32_t* args = arg_array.GetArray();
     JValue result;
 
@@ -488,7 +489,7 @@
     mirror::Object* receiver;
     JniInternalTestMakeExecutable(&method, &receiver, is_static, "sum", "(DD)D");
 
-    ArgArray arg_array(NULL, 0);
+    ArgArray arg_array("DDD", 3);
     uint32_t* args = arg_array.GetArray();
     JValue value;
     JValue value2;
@@ -559,7 +560,7 @@
     mirror::Object* receiver;
     JniInternalTestMakeExecutable(&method, &receiver, is_static, "sum", "(DDD)D");
 
-    ArgArray arg_array(NULL, 0);
+    ArgArray arg_array("DDDD", 4);
     uint32_t* args = arg_array.GetArray();
     JValue value;
     JValue value2;
@@ -617,7 +618,7 @@
     mirror::Object* receiver;
     JniInternalTestMakeExecutable(&method, &receiver, is_static, "sum", "(DDDD)D");
 
-    ArgArray arg_array(NULL, 0);
+    ArgArray arg_array("DDDDD", 5);
     uint32_t* args = arg_array.GetArray();
     JValue value;
     JValue value2;
@@ -684,7 +685,7 @@
     mirror::Object* receiver;
     JniInternalTestMakeExecutable(&method, &receiver, is_static, "sum", "(DDDDD)D");
 
-    ArgArray arg_array(NULL, 0);
+    ArgArray arg_array("DDDDDD", 6);
     uint32_t* args = arg_array.GetArray();
     JValue value;
     JValue value2;
@@ -1778,13 +1779,13 @@
       class_loader(soa.Self(), soa.Decode<mirror::ClassLoader*>(jclass_loader));
   CompileDirectMethod(class_loader, "Main", "main", "([Ljava/lang/String;)V");
 
-  mirror::Class* klass = class_linker_->FindClass("LMain;", class_loader);
+  mirror::Class* klass = class_linker_->FindClass(soa.Self(), "LMain;", class_loader);
   ASSERT_TRUE(klass != NULL);
 
   mirror::ArtMethod* method = klass->FindDirectMethod("main", "([Ljava/lang/String;)V");
   ASSERT_TRUE(method != NULL);
 
-  ArgArray arg_array(NULL, 0);
+  ArgArray arg_array("VL", 2);
   arg_array.Append(0U);
   JValue result;
 
diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h
index 7c5de5e..90aaccd 100644
--- a/runtime/mirror/array-inl.h
+++ b/runtime/mirror/array-inl.h
@@ -27,10 +27,13 @@
 namespace art {
 namespace mirror {
 
+template<VerifyObjectFlags kVerifyFlags>
 inline size_t Array::SizeOf() {
   // This is safe from overflow because the array was already allocated, so we know it's sane.
-  size_t component_size = GetClass()->GetComponentSize();
-  int32_t component_count = GetLength();
+  size_t component_size = GetClass<kVerifyFlags>()->GetComponentSize();
+  // Don't need to check this since we already check this in GetClass.
+  int32_t component_count =
+      GetLength<static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis)>();
   size_t header_size = sizeof(Object) + (component_size == sizeof(int64_t) ? 8 : 4);
   size_t data_size = component_count * component_size;
   return header_size + data_size;
diff --git a/runtime/mirror/array.cc b/runtime/mirror/array.cc
index 2180857..715f072 100644
--- a/runtime/mirror/array.cc
+++ b/runtime/mirror/array.cc
@@ -18,6 +18,7 @@
 
 #include "class.h"
 #include "class-inl.h"
+#include "class_linker-inl.h"
 #include "common_throws.h"
 #include "dex_file-inl.h"
 #include "gc/accounting/card_table-inl.h"
@@ -85,20 +86,22 @@
     }
   }
 
-  // Generate the full name of the array class.
-  std::string descriptor(num_dimensions, '[');
-  descriptor += ClassHelper(element_class.get()).GetDescriptor();
-
   // Find/generate the array class.
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  SirtRef<mirror::ClassLoader> class_loader(self, element_class->GetClassLoader());
   SirtRef<mirror::Class> array_class(self,
-                                     class_linker->FindClass(descriptor.c_str(), class_loader));
+                                     class_linker->FindArrayClass(self, element_class.get()));
   if (UNLIKELY(array_class.get() == nullptr)) {
     CHECK(self->IsExceptionPending());
     return nullptr;
   }
-  // create the array
+  for (int32_t i = 1; i < dimensions->GetLength(); ++i) {
+    array_class.reset(class_linker->FindArrayClass(self, array_class.get()));
+    if (UNLIKELY(array_class.get() == nullptr)) {
+      CHECK(self->IsExceptionPending());
+      return nullptr;
+    }
+  }
+  // Create the array.
   Array* new_array = RecursiveCreateMultiArray(self, array_class, 0, dimensions);
   if (UNLIKELY(new_array == nullptr)) {
     CHECK(self->IsExceptionPending());
diff --git a/runtime/mirror/array.h b/runtime/mirror/array.h
index 7555975..c4f9a75 100644
--- a/runtime/mirror/array.h
+++ b/runtime/mirror/array.h
@@ -53,17 +53,18 @@
                                  const SirtRef<IntArray>& dimensions)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   size_t SizeOf() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   int32_t GetLength() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return GetField32(OFFSET_OF_OBJECT_MEMBER(Array, length_), false);
+    return GetField32<kVerifyFlags>(OFFSET_OF_OBJECT_MEMBER(Array, length_), false);
   }
 
   void SetLength(int32_t length) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     CHECK_GE(length, 0);
     // We use non transactional version since we can't undo this write. We also disable checking
     // since it would fail during a transaction.
-    SetField32<false, false>(OFFSET_OF_OBJECT_MEMBER(Array, length_), length, false, false);
+    SetField32<false, false, kVerifyNone>(OFFSET_OF_OBJECT_MEMBER(Array, length_), length, false);
   }
 
   static MemberOffset LengthOffset() {
@@ -94,8 +95,10 @@
 
   // Returns true if the index is valid. If not, throws an ArrayIndexOutOfBoundsException and
   // returns false.
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool CheckIsValidIndex(int32_t index) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    if (UNLIKELY(static_cast<uint32_t>(index) >= static_cast<uint32_t>(GetLength()))) {
+    if (UNLIKELY(static_cast<uint32_t>(index) >=
+                 static_cast<uint32_t>(GetLength<kVerifyFlags>()))) {
       ThrowArrayIndexOutOfBoundsException(index);
       return false;
     }
diff --git a/runtime/mirror/art_method-inl.h b/runtime/mirror/art_method-inl.h
index d347724..d5eccaf 100644
--- a/runtime/mirror/art_method-inl.h
+++ b/runtime/mirror/art_method-inl.h
@@ -215,6 +215,13 @@
   DCHECK(!result || IsRuntimeMethod());
   return result;
 }
+
+template<VerifyObjectFlags kVerifyFlags>
+inline void ArtMethod::SetNativeMethod(const void* native_method) {
+  SetFieldPtr<false, true, kVerifyFlags>(
+      OFFSET_OF_OBJECT_MEMBER(ArtMethod, entry_point_from_jni_), native_method, false);
+}
+
 }  // namespace mirror
 }  // namespace art
 
diff --git a/runtime/mirror/art_method.cc b/runtime/mirror/art_method.cc
index d5f7597..fe27992 100644
--- a/runtime/mirror/art_method.cc
+++ b/runtime/mirror/art_method.cc
@@ -37,6 +37,10 @@
 extern "C" void art_portable_invoke_stub(ArtMethod*, uint32_t*, uint32_t, Thread*, JValue*, char);
 extern "C" void art_quick_invoke_stub(ArtMethod*, uint32_t*, uint32_t, Thread*, JValue*,
                                       const char*);
+#ifdef __x86_64__
+extern "C" void art_quick_invoke_static_stub(ArtMethod*, uint32_t*, uint32_t, Thread*, JValue*,
+                                             const char*);
+#endif
 
 // TODO: get global references for these
 Class* ArtMethod::java_lang_reflect_ArtMethod_ = NULL;
@@ -276,7 +280,15 @@
                                                   : GetEntryPointFromPortableCompiledCode());
       }
       if (!IsPortableCompiled()) {
+#ifdef __x86_64__
+        if (!IsStatic()) {
+          (*art_quick_invoke_stub)(this, args, args_size, self, result, shorty);
+        } else {
+          (*art_quick_invoke_static_stub)(this, args, args_size, self, result, shorty);
+        }
+#else
         (*art_quick_invoke_stub)(this, args, args_size, self, result, shorty);
+#endif
       } else {
         (*art_portable_invoke_stub)(this, args, args_size, self, result, shorty[0]);
       }
@@ -348,10 +360,5 @@
   RegisterNative(self, GetJniDlsymLookupStub(), false);
 }
 
-void ArtMethod::SetNativeMethod(const void* native_method) {
-  SetFieldPtr<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, entry_point_from_jni_),
-                     native_method, false);
-}
-
 }  // namespace mirror
 }  // namespace art
diff --git a/runtime/mirror/art_method.h b/runtime/mirror/art_method.h
index 71cc7af..e17dc5f 100644
--- a/runtime/mirror/art_method.h
+++ b/runtime/mirror/art_method.h
@@ -214,40 +214,48 @@
   void Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* result,
               const char* shorty) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   EntryPointFromInterpreter* GetEntryPointFromInterpreter() {
-    return GetFieldPtr<EntryPointFromInterpreter*>(
+    return GetFieldPtr<EntryPointFromInterpreter*, kVerifyFlags>(
                OFFSET_OF_OBJECT_MEMBER(ArtMethod, entry_point_from_interpreter_), false);
   }
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   void SetEntryPointFromInterpreter(EntryPointFromInterpreter* entry_point_from_interpreter) {
-    SetFieldPtr<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, entry_point_from_interpreter_),
-                       entry_point_from_interpreter, false);
+    SetFieldPtr<false, true, kVerifyFlags>(
+        OFFSET_OF_OBJECT_MEMBER(ArtMethod, entry_point_from_interpreter_),
+        entry_point_from_interpreter, false);
   }
 
   static MemberOffset EntryPointFromPortableCompiledCodeOffset() {
     return MemberOffset(OFFSETOF_MEMBER(ArtMethod, entry_point_from_portable_compiled_code_));
   }
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   const void* GetEntryPointFromPortableCompiledCode() {
-    return GetFieldPtr<const void*>(EntryPointFromPortableCompiledCodeOffset(), false);
+    return GetFieldPtr<const void*, kVerifyFlags>(
+        EntryPointFromPortableCompiledCodeOffset(), false);
   }
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   void SetEntryPointFromPortableCompiledCode(const void* entry_point_from_portable_compiled_code) {
-    SetFieldPtr<false>(EntryPointFromPortableCompiledCodeOffset(),
-                       entry_point_from_portable_compiled_code, false);
+    SetFieldPtr<false, true, kVerifyFlags>(
+        EntryPointFromPortableCompiledCodeOffset(), entry_point_from_portable_compiled_code, false);
   }
 
   static MemberOffset EntryPointFromQuickCompiledCodeOffset() {
     return MemberOffset(OFFSETOF_MEMBER(ArtMethod, entry_point_from_quick_compiled_code_));
   }
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   const void* GetEntryPointFromQuickCompiledCode() {
-    return GetFieldPtr<const void*>(EntryPointFromQuickCompiledCodeOffset(), false);
+    return GetFieldPtr<const void*, kVerifyFlags>(EntryPointFromQuickCompiledCodeOffset(), false);
   }
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   void SetEntryPointFromQuickCompiledCode(const void* entry_point_from_quick_compiled_code) {
-    SetFieldPtr<false>(EntryPointFromQuickCompiledCodeOffset(),
-                       entry_point_from_quick_compiled_code, false);
+    SetFieldPtr<false, true, kVerifyFlags>(
+        EntryPointFromQuickCompiledCodeOffset(), entry_point_from_quick_compiled_code, false);
   }
 
 
@@ -279,9 +287,10 @@
         false);
   }
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   void SetMappingTable(const uint8_t* mapping_table) {
-    SetFieldPtr<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, quick_mapping_table_),
-                       mapping_table, false);
+    SetFieldPtr<false, true, kVerifyFlags>(
+        OFFSET_OF_OBJECT_MEMBER(ArtMethod, quick_mapping_table_), mapping_table, false);
   }
 
   uint32_t GetOatMappingTableOffset();
@@ -294,8 +303,10 @@
         false);
   }
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   void SetVmapTable(const uint8_t* vmap_table) {
-    SetFieldPtr<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, quick_vmap_table_), vmap_table, false);
+    SetFieldPtr<false, true, kVerifyFlags>(
+        OFFSET_OF_OBJECT_MEMBER(ArtMethod, quick_vmap_table_), vmap_table, false);
   }
 
   uint32_t GetOatVmapTableOffset();
@@ -305,17 +316,19 @@
   const uint8_t* GetNativeGcMap() {
     return GetFieldPtr<uint8_t*>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, gc_map_), false);
   }
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   void SetNativeGcMap(const uint8_t* data) {
-    SetFieldPtr<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, gc_map_), data, false);
+    SetFieldPtr<false, true, kVerifyFlags>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, gc_map_), data,
+                                           false);
   }
 
   // When building the oat need a convenient place to stuff the offset of the native GC map.
   void SetOatNativeGcMapOffset(uint32_t gc_map_offset);
   uint32_t GetOatNativeGcMapOffset();
 
-  size_t GetFrameSizeInBytes() {
-    DCHECK_EQ(sizeof(size_t), sizeof(uint32_t));
-    size_t result = GetField32(OFFSET_OF_OBJECT_MEMBER(ArtMethod, quick_frame_size_in_bytes_), false);
+  uint32_t GetFrameSizeInBytes() {
+    uint32_t result = GetField32(OFFSET_OF_OBJECT_MEMBER(ArtMethod, quick_frame_size_in_bytes_),
+                                 false);
     DCHECK_LE(static_cast<size_t>(kStackAlignment), result);
     return result;
   }
@@ -350,6 +363,7 @@
     return reinterpret_cast<const void*>(GetField32(NativeMethodOffset(), false));
   }
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   void SetNativeMethod(const void*);
 
   static MemberOffset GetMethodIndexOffset() {
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index cba221d..e82c393 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -85,6 +85,7 @@
   return (GetDirectMethods() != NULL) ? GetDirectMethods()->GetLength() : 0;
 }
 
+template<VerifyObjectFlags kVerifyFlags>
 inline ObjectArray<ArtMethod>* Class::GetVirtualMethods() {
   DCHECK(IsLoaded() || IsErroneous());
   return GetFieldObject<ObjectArray<ArtMethod> >(
@@ -103,8 +104,9 @@
   return (GetVirtualMethods() != NULL) ? GetVirtualMethods()->GetLength() : 0;
 }
 
+template<VerifyObjectFlags kVerifyFlags>
 inline ArtMethod* Class::GetVirtualMethod(uint32_t i) {
-  DCHECK(IsResolved() || IsErroneous());
+  DCHECK(IsResolved<kVerifyFlags>() || IsErroneous<kVerifyFlags>());
   return GetVirtualMethods()->Get(i);
 }
 
@@ -415,14 +417,16 @@
   }
 }
 
+template<VerifyObjectFlags kVerifyFlags>
 inline uint32_t Class::GetAccessFlags() {
   // Check class is loaded or this is java.lang.String that has a
   // circularity issue during loading the names of its members
-  DCHECK(IsLoaded() || IsErroneous() ||
+  DCHECK(IsLoaded<kVerifyFlags>() ||
+         IsErroneous<static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis)>() ||
          this == String::GetJavaLangString() ||
          this == ArtField::GetJavaLangReflectArtField() ||
          this == ArtMethod::GetJavaLangReflectArtMethod());
-  return GetField32(OFFSET_OF_OBJECT_MEMBER(Class, access_flags_), false);
+  return GetField32<kVerifyFlags>(OFFSET_OF_OBJECT_MEMBER(Class, access_flags_), false);
 }
 
 inline String* Class::GetName() {
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index 3208de9..6dbb29d 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -72,7 +72,7 @@
             << PrettyClass(this) << " " << old_status << " -> " << new_status;
     }
   }
-  if (new_status == kStatusError) {
+  if (UNLIKELY(new_status == kStatusError)) {
     CHECK_NE(GetStatus(), kStatusError)
         << "Attempt to set as erroneous an already erroneous class " << PrettyClass(this);
 
@@ -95,7 +95,8 @@
     // clear exception to call FindSystemClass
     self->ClearException();
     ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-    Class* eiie_class = class_linker->FindSystemClass("Ljava/lang/ExceptionInInitializerError;");
+    Class* eiie_class = class_linker->FindSystemClass(self,
+                                                      "Ljava/lang/ExceptionInInitializerError;");
     CHECK(!self->IsExceptionPending());
 
     // Only verification errors, not initialization problems, should set a verify error.
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index cd8504b..43db996 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -120,9 +120,11 @@
     kStatusMax = 10,
   };
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   Status GetStatus() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    DCHECK_EQ(sizeof(Status), sizeof(uint32_t));
-    return static_cast<Status>(GetField32(OFFSET_OF_OBJECT_MEMBER(Class, status_), true));
+    COMPILE_ASSERT(sizeof(Status) == sizeof(uint32_t), size_of_status_not_uint32);
+    return static_cast<Status>(GetField32<kVerifyFlags>(OFFSET_OF_OBJECT_MEMBER(Class, status_),
+                                                       true));
   }
 
   void SetStatus(Status new_status, Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -132,45 +134,54 @@
   }
 
   // Returns true if the class has failed to link.
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool IsErroneous() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return GetStatus() == kStatusError;
+    return GetStatus<kVerifyFlags>() == kStatusError;
   }
 
   // Returns true if the class has been loaded.
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool IsIdxLoaded() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return GetStatus() >= kStatusIdx;
+    return GetStatus<kVerifyFlags>() >= kStatusIdx;
   }
 
   // Returns true if the class has been loaded.
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool IsLoaded() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return GetStatus() >= kStatusLoaded;
+    return GetStatus<kVerifyFlags>() >= kStatusLoaded;
   }
 
   // Returns true if the class has been linked.
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool IsResolved() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return GetStatus() >= kStatusResolved;
+    return GetStatus<kVerifyFlags>() >= kStatusResolved;
   }
 
   // Returns true if the class was compile-time verified.
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool IsCompileTimeVerified() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return GetStatus() >= kStatusRetryVerificationAtRuntime;
+    return GetStatus<kVerifyFlags>() >= kStatusRetryVerificationAtRuntime;
   }
 
   // Returns true if the class has been verified.
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool IsVerified() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return GetStatus() >= kStatusVerified;
+    return GetStatus<kVerifyFlags>() >= kStatusVerified;
   }
 
   // Returns true if the class is initializing.
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool IsInitializing() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return GetStatus() >= kStatusInitializing;
+    return GetStatus<kVerifyFlags>() >= kStatusInitializing;
   }
 
   // Returns true if the class is initialized.
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool IsInitialized() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return GetStatus() == kStatusInitialized;
+    return GetStatus<kVerifyFlags>() == kStatusInitialized;
   }
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   uint32_t GetAccessFlags() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   void SetAccessFlags(uint32_t new_access_flags) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -217,24 +228,29 @@
     return (GetAccessFlags() & kAccSynthetic) != 0;
   }
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool IsReferenceClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return (GetAccessFlags() & kAccClassIsReference) != 0;
+    return (GetAccessFlags<kVerifyFlags>() & kAccClassIsReference) != 0;
   }
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool IsWeakReferenceClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return (GetAccessFlags() & kAccClassIsWeakReference) != 0;
+    return (GetAccessFlags<kVerifyFlags>() & kAccClassIsWeakReference) != 0;
   }
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool IsSoftReferenceClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return (GetAccessFlags() & kAccReferenceFlagsMask) == kAccClassIsReference;
+    return (GetAccessFlags<kVerifyFlags>() & kAccReferenceFlagsMask) == kAccClassIsReference;
   }
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool IsFinalizerReferenceClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return (GetAccessFlags() & kAccClassIsFinalizerReference) != 0;
+    return (GetAccessFlags<kVerifyFlags>() & kAccClassIsFinalizerReference) != 0;
   }
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool IsPhantomReferenceClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return (GetAccessFlags() & kAccClassIsPhantomReference) != 0;
+    return (GetAccessFlags<kVerifyFlags>() & kAccClassIsPhantomReference) != 0;
   }
 
   // Can references of this type be assigned to by things of another type? For non-array types
@@ -260,7 +276,7 @@
   // Computes the name, then sets the cached value.
   String* ComputeName() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  bool IsProxyClass() {
+  bool IsProxyClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     // Read access flags without using getter as whether something is a proxy can be check in
     // any loaded state
     // TODO: switch to a check if the super class is java.lang.reflect.Proxy?
@@ -268,10 +284,11 @@
     return (access_flags & kAccClassIsProxy) != 0;
   }
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   Primitive::Type GetPrimitiveType() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     DCHECK_EQ(sizeof(Primitive::Type), sizeof(int32_t));
     return static_cast<Primitive::Type>(
-        GetField32(OFFSET_OF_OBJECT_MEMBER(Class, primitive_type_), false));
+        GetField32<kVerifyFlags>(OFFSET_OF_OBJECT_MEMBER(Class, primitive_type_), false));
   }
 
   void SetPrimitiveType(Primitive::Type new_type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -280,48 +297,61 @@
   }
 
   // Returns true if the class is a primitive type.
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool IsPrimitive() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return GetPrimitiveType() != Primitive::kPrimNot;
+    return GetPrimitiveType<kVerifyFlags>() != Primitive::kPrimNot;
   }
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool IsPrimitiveBoolean() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return GetPrimitiveType() == Primitive::kPrimBoolean;
+    return GetPrimitiveType<kVerifyFlags>() == Primitive::kPrimBoolean;
   }
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool IsPrimitiveByte() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return GetPrimitiveType() == Primitive::kPrimByte;
+    return GetPrimitiveType<kVerifyFlags>() == Primitive::kPrimByte;
   }
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool IsPrimitiveChar() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return GetPrimitiveType() == Primitive::kPrimChar;
+    return GetPrimitiveType<kVerifyFlags>() == Primitive::kPrimChar;
   }
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool IsPrimitiveShort() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return GetPrimitiveType() == Primitive::kPrimShort;
+    return GetPrimitiveType<kVerifyFlags>() == Primitive::kPrimShort;
   }
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool IsPrimitiveInt() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     return GetPrimitiveType() == Primitive::kPrimInt;
   }
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool IsPrimitiveLong() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return GetPrimitiveType() == Primitive::kPrimLong;
+    return GetPrimitiveType<kVerifyFlags>() == Primitive::kPrimLong;
   }
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool IsPrimitiveFloat() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return GetPrimitiveType() == Primitive::kPrimFloat;
+    return GetPrimitiveType<kVerifyFlags>() == Primitive::kPrimFloat;
   }
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool IsPrimitiveDouble() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return GetPrimitiveType() == Primitive::kPrimDouble;
+    return GetPrimitiveType<kVerifyFlags>() == Primitive::kPrimDouble;
   }
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool IsPrimitiveVoid() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return GetPrimitiveType() == Primitive::kPrimVoid;
+    return GetPrimitiveType<kVerifyFlags>() == Primitive::kPrimVoid;
   }
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool IsPrimitiveArray() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return IsArrayClass() && GetComponentType()->IsPrimitive();
+    return IsArrayClass<kVerifyFlags>() &&
+        GetComponentType<static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis)>()->
+        IsPrimitive();
   }
 
   // Depth of class from java.lang.Object
@@ -333,8 +363,9 @@
     return depth;
   }
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool IsArrayClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return GetComponentType() != NULL;
+    return GetComponentType<kVerifyFlags>() != NULL;
   }
 
   bool IsClassClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -351,8 +382,9 @@
     return OFFSET_OF_OBJECT_MEMBER(Class, component_type_);
   }
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   Class* GetComponentType() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return GetFieldObject<Class>(ComponentTypeOffset(), false);
+    return GetFieldObject<Class, kVerifyFlags>(ComponentTypeOffset(), false);
   }
 
   void SetComponentType(Class* new_component_type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -376,8 +408,9 @@
     return (!IsPrimitive() && !IsInterface() && !IsAbstract()) || ((IsAbstract()) && IsArrayClass());
   }
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool IsObjectArrayClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return GetComponentType() != NULL && !GetComponentType()->IsPrimitive();
+    return GetComponentType<kVerifyFlags>() != nullptr && !GetComponentType<kVerifyFlags>()->IsPrimitive();
   }
 
   // Creates a raw object instance but does not invoke the default constructor.
@@ -396,12 +429,14 @@
     return IsClassClass() || IsArrayClass();
   }
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   uint32_t SizeOf() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return GetField32(OFFSET_OF_OBJECT_MEMBER(Class, class_size_), false);
+    return GetField32<kVerifyFlags>(OFFSET_OF_OBJECT_MEMBER(Class, class_size_), false);
   }
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   uint32_t GetClassSize() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return GetField32(OFFSET_OF_OBJECT_MEMBER(Class, class_size_), false);
+    return GetField32<kVerifyFlags>(OFFSET_OF_OBJECT_MEMBER(Class, class_size_), false);
   }
 
   void SetClassSize(uint32_t new_class_size)
@@ -549,6 +584,7 @@
   // Returns the number of static, private, and constructor methods.
   uint32_t NumDirectMethods() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   ObjectArray<ArtMethod>* GetVirtualMethods() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   void SetVirtualMethods(ObjectArray<ArtMethod>* new_virtual_methods)
@@ -557,6 +593,7 @@
   // Returns the number of non-inherited virtual methods.
   uint32_t NumVirtualMethods() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   ArtMethod* GetVirtualMethod(uint32_t i) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   ArtMethod* GetVirtualMethodDuringLinking(uint32_t i) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -683,9 +720,11 @@
                       false);
   }
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   uint32_t GetReferenceInstanceOffsets() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    DCHECK(IsResolved() || IsErroneous());
-    return GetField32(OFFSET_OF_OBJECT_MEMBER(Class, reference_instance_offsets_), false);
+    DCHECK(IsResolved<kVerifyFlags>() || IsErroneous<kVerifyFlags>());
+    return GetField32<kVerifyFlags>(OFFSET_OF_OBJECT_MEMBER(Class, reference_instance_offsets_),
+                                   false);
   }
 
   void SetReferenceInstanceOffsets(uint32_t new_reference_offsets)
@@ -725,8 +764,10 @@
   // TODO: uint16_t
   void SetStaticField(uint32_t i, ArtField* f) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   uint32_t GetReferenceStaticOffsets() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return GetField32(OFFSET_OF_OBJECT_MEMBER(Class, reference_static_offsets_), false);
+    return GetField32<kVerifyFlags>(OFFSET_OF_OBJECT_MEMBER(Class, reference_static_offsets_),
+                                   false);
   }
 
   void SetReferenceStaticOffsets(uint32_t new_reference_offsets)
diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h
index 70291c1..df8104d 100644
--- a/runtime/mirror/object-inl.h
+++ b/runtime/mirror/object-inl.h
@@ -32,18 +32,21 @@
 namespace art {
 namespace mirror {
 
+template<VerifyObjectFlags kVerifyFlags>
 inline Class* Object::GetClass() {
-  return GetFieldObject<Class>(OFFSET_OF_OBJECT_MEMBER(Object, klass_), false);
+  return GetFieldObject<Class, kVerifyFlags>(OFFSET_OF_OBJECT_MEMBER(Object, klass_), false);
 }
 
+template<VerifyObjectFlags kVerifyFlags>
 inline void Object::SetClass(Class* new_klass) {
   // new_klass may be NULL prior to class linker initialization.
   // We don't mark the card as this occurs as part of object allocation. Not all objects have
   // backing cards, such as large objects.
   // We use non transactional version since we can't undo this write. We also disable checking as
   // we may run in transaction mode here.
-  SetFieldObjectWithoutWriteBarrier<false, false>(OFFSET_OF_OBJECT_MEMBER(Object, klass_),
-                                                  new_klass, false, false);
+  SetFieldObjectWithoutWriteBarrier<false, false,
+      static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis)>(
+      OFFSET_OF_OBJECT_MEMBER(Object, klass_), new_klass, false);
 }
 
 inline LockWord Object::GetLockWord() {
@@ -89,176 +92,222 @@
   Monitor::Wait(self, this, ms, ns, true, kTimedWaiting);
 }
 
+template<VerifyObjectFlags kVerifyFlags>
 inline bool Object::VerifierInstanceOf(Class* klass) {
   DCHECK(klass != NULL);
-  DCHECK(GetClass() != NULL);
+  DCHECK(GetClass<kVerifyFlags>() != NULL);
   return klass->IsInterface() || InstanceOf(klass);
 }
 
+template<VerifyObjectFlags kVerifyFlags>
 inline bool Object::InstanceOf(Class* klass) {
   DCHECK(klass != NULL);
-  DCHECK(GetClass() != NULL);
-  return klass->IsAssignableFrom(GetClass());
+  DCHECK(GetClass<kVerifyNone>() != NULL);
+  return klass->IsAssignableFrom(GetClass<kVerifyFlags>());
 }
 
+template<VerifyObjectFlags kVerifyFlags>
 inline bool Object::IsClass() {
-  Class* java_lang_Class = GetClass()->GetClass();
-  return GetClass() == java_lang_Class;
+  Class* java_lang_Class = GetClass<kVerifyFlags>()->GetClass();
+  return GetClass<static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis)>() ==
+      java_lang_Class;
 }
 
+template<VerifyObjectFlags kVerifyFlags>
 inline Class* Object::AsClass() {
-  DCHECK(IsClass());
+  DCHECK(IsClass<kVerifyFlags>());
   return down_cast<Class*>(this);
 }
 
+template<VerifyObjectFlags kVerifyFlags>
 inline bool Object::IsObjectArray() {
-  return IsArrayInstance() && !GetClass()->GetComponentType()->IsPrimitive();
+  constexpr auto kNewFlags = static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis);
+  return IsArrayInstance<kVerifyFlags>() &&
+      !GetClass<kNewFlags>()->template GetComponentType<kNewFlags>()->IsPrimitive();
 }
 
-template<class T>
+template<class T, VerifyObjectFlags kVerifyFlags>
 inline ObjectArray<T>* Object::AsObjectArray() {
-  DCHECK(IsObjectArray());
+  DCHECK(IsObjectArray<kVerifyFlags>());
   return down_cast<ObjectArray<T>*>(this);
 }
 
+template<VerifyObjectFlags kVerifyFlags>
 inline bool Object::IsArrayInstance() {
-  return GetClass()->IsArrayClass();
+  return GetClass<kVerifyFlags>()->IsArrayClass();
 }
 
+template<VerifyObjectFlags kVerifyFlags>
 inline bool Object::IsArtField() {
-  return GetClass()->IsArtFieldClass();
+  return GetClass<kVerifyFlags>()->IsArtFieldClass();
 }
 
-inline ArtField* Object::AsArtField() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  DCHECK(IsArtField());
+template<VerifyObjectFlags kVerifyFlags>
+inline ArtField* Object::AsArtField() {
+  DCHECK(IsArtField<kVerifyFlags>());
   return down_cast<ArtField*>(this);
 }
 
+template<VerifyObjectFlags kVerifyFlags>
 inline bool Object::IsArtMethod() {
-  return GetClass()->IsArtMethodClass();
+  return GetClass<kVerifyFlags>()->IsArtMethodClass();
 }
 
+template<VerifyObjectFlags kVerifyFlags>
 inline ArtMethod* Object::AsArtMethod() {
-  DCHECK(IsArtMethod());
+  DCHECK(IsArtMethod<kVerifyFlags>());
   return down_cast<ArtMethod*>(this);
 }
 
+template<VerifyObjectFlags kVerifyFlags>
 inline bool Object::IsReferenceInstance() {
-  return GetClass()->IsReferenceClass();
+  return GetClass<kVerifyFlags>()->IsReferenceClass();
 }
 
+template<VerifyObjectFlags kVerifyFlags>
 inline Array* Object::AsArray() {
-  DCHECK(IsArrayInstance());
+  DCHECK(IsArrayInstance<kVerifyFlags>());
   return down_cast<Array*>(this);
 }
 
+template<VerifyObjectFlags kVerifyFlags>
 inline BooleanArray* Object::AsBooleanArray() {
-  DCHECK(GetClass()->IsArrayClass());
-  DCHECK(GetClass()->GetComponentType()->IsPrimitiveBoolean());
+  constexpr auto kNewFlags = static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis);
+  DCHECK(GetClass<kVerifyFlags>()->IsArrayClass());
+  DCHECK(GetClass<kNewFlags>()->GetComponentType()->IsPrimitiveBoolean());
   return down_cast<BooleanArray*>(this);
 }
 
+template<VerifyObjectFlags kVerifyFlags>
 inline ByteArray* Object::AsByteArray() {
-  DCHECK(GetClass()->IsArrayClass());
-  DCHECK(GetClass()->GetComponentType()->IsPrimitiveByte());
+  static const VerifyObjectFlags kNewFlags = static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis);
+  DCHECK(GetClass<kVerifyFlags>()->IsArrayClass());
+  DCHECK(GetClass<kNewFlags>()->template GetComponentType<kNewFlags>()->IsPrimitiveByte());
   return down_cast<ByteArray*>(this);
 }
 
+template<VerifyObjectFlags kVerifyFlags>
 inline ByteArray* Object::AsByteSizedArray() {
-  DCHECK(GetClass()->IsArrayClass());
-  DCHECK(GetClass()->GetComponentType()->IsPrimitiveByte() ||
-         GetClass()->GetComponentType()->IsPrimitiveBoolean());
+  constexpr VerifyObjectFlags kNewFlags = static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis);
+  DCHECK(GetClass<kVerifyFlags>()->IsArrayClass());
+  DCHECK(GetClass<kNewFlags>()->template GetComponentType<kNewFlags>()->IsPrimitiveByte() ||
+         GetClass<kNewFlags>()->template GetComponentType<kNewFlags>()->IsPrimitiveBoolean());
   return down_cast<ByteArray*>(this);
 }
 
+template<VerifyObjectFlags kVerifyFlags>
 inline CharArray* Object::AsCharArray() {
-  DCHECK(GetClass()->IsArrayClass());
-  DCHECK(GetClass()->GetComponentType()->IsPrimitiveChar());
+  constexpr auto kNewFlags = static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis);
+  DCHECK(GetClass<kVerifyFlags>()->IsArrayClass());
+  DCHECK(GetClass<kNewFlags>()->template GetComponentType<kNewFlags>()->IsPrimitiveChar());
   return down_cast<CharArray*>(this);
 }
 
+template<VerifyObjectFlags kVerifyFlags>
 inline ShortArray* Object::AsShortArray() {
-  DCHECK(GetClass()->IsArrayClass());
-  DCHECK(GetClass()->GetComponentType()->IsPrimitiveShort());
+  constexpr auto kNewFlags = static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis);
+  DCHECK(GetClass<kVerifyFlags>()->IsArrayClass());
+  DCHECK(GetClass<kNewFlags>()->template GetComponentType<kNewFlags>()->IsPrimitiveShort());
   return down_cast<ShortArray*>(this);
 }
 
+template<VerifyObjectFlags kVerifyFlags>
 inline ShortArray* Object::AsShortSizedArray() {
-  DCHECK(GetClass()->IsArrayClass());
-  DCHECK(GetClass()->GetComponentType()->IsPrimitiveShort() ||
-         GetClass()->GetComponentType()->IsPrimitiveChar());
+  constexpr auto kNewFlags = static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis);
+  DCHECK(GetClass<kVerifyFlags>()->IsArrayClass());
+  DCHECK(GetClass<kNewFlags>()->template GetComponentType<kNewFlags>()->IsPrimitiveShort() ||
+         GetClass<kNewFlags>()->template GetComponentType<kNewFlags>()->IsPrimitiveChar());
   return down_cast<ShortArray*>(this);
 }
 
+template<VerifyObjectFlags kVerifyFlags>
 inline IntArray* Object::AsIntArray() {
-  DCHECK(GetClass()->IsArrayClass());
-  DCHECK(GetClass()->GetComponentType()->IsPrimitiveInt() ||
-         GetClass()->GetComponentType()->IsPrimitiveFloat());
+  constexpr auto kNewFlags = static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis);
+  DCHECK(GetClass<kVerifyFlags>()->IsArrayClass());
+  DCHECK(GetClass<kNewFlags>()->template GetComponentType<kNewFlags>()->IsPrimitiveInt() ||
+         GetClass<kNewFlags>()->template GetComponentType<kNewFlags>()->IsPrimitiveFloat());
   return down_cast<IntArray*>(this);
 }
 
+template<VerifyObjectFlags kVerifyFlags>
 inline LongArray* Object::AsLongArray() {
-  DCHECK(GetClass()->IsArrayClass());
-  DCHECK(GetClass()->GetComponentType()->IsPrimitiveLong() ||
-         GetClass()->GetComponentType()->IsPrimitiveDouble());
+  constexpr auto kNewFlags = static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis);
+  DCHECK(GetClass<kVerifyFlags>()->IsArrayClass());
+  DCHECK(GetClass<kNewFlags>()->template GetComponentType<kNewFlags>()->IsPrimitiveLong() ||
+         GetClass<kNewFlags>()->template GetComponentType<kNewFlags>()->IsPrimitiveDouble());
   return down_cast<LongArray*>(this);
 }
 
+template<VerifyObjectFlags kVerifyFlags>
 inline FloatArray* Object::AsFloatArray() {
-  DCHECK(GetClass()->IsArrayClass());
-  DCHECK(GetClass()->GetComponentType()->IsPrimitiveFloat());
+  constexpr auto kNewFlags = static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis);
+  DCHECK(GetClass<kVerifyFlags>()->IsArrayClass());
+  DCHECK(GetClass<kNewFlags>()->template GetComponentType<kNewFlags>()->IsPrimitiveFloat());
   return down_cast<FloatArray*>(this);
 }
 
+template<VerifyObjectFlags kVerifyFlags>
 inline DoubleArray* Object::AsDoubleArray() {
-  DCHECK(GetClass()->IsArrayClass());
-  DCHECK(GetClass()->GetComponentType()->IsPrimitiveDouble());
+  constexpr auto kNewFlags = static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis);
+  DCHECK(GetClass<kVerifyFlags>()->IsArrayClass());
+  DCHECK(GetClass<kNewFlags>()->template GetComponentType<kNewFlags>()->IsPrimitiveDouble());
   return down_cast<DoubleArray*>(this);
 }
 
+template<VerifyObjectFlags kVerifyFlags>
 inline String* Object::AsString() {
-  DCHECK(GetClass()->IsStringClass());
+  DCHECK(GetClass<kVerifyFlags>()->IsStringClass());
   return down_cast<String*>(this);
 }
 
+template<VerifyObjectFlags kVerifyFlags>
 inline Throwable* Object::AsThrowable() {
-  DCHECK(GetClass()->IsThrowableClass());
+  DCHECK(GetClass<kVerifyFlags>()->IsThrowableClass());
   return down_cast<Throwable*>(this);
 }
 
+template<VerifyObjectFlags kVerifyFlags>
 inline bool Object::IsWeakReferenceInstance() {
-  return GetClass()->IsWeakReferenceClass();
+  return GetClass<kVerifyFlags>()->IsWeakReferenceClass();
 }
 
+template<VerifyObjectFlags kVerifyFlags>
 inline bool Object::IsSoftReferenceInstance() {
-  return GetClass()->IsSoftReferenceClass();
+  return GetClass<kVerifyFlags>()->IsSoftReferenceClass();
 }
 
+template<VerifyObjectFlags kVerifyFlags>
 inline bool Object::IsFinalizerReferenceInstance() {
-  return GetClass()->IsFinalizerReferenceClass();
+  return GetClass<kVerifyFlags>()->IsFinalizerReferenceClass();
 }
 
+template<VerifyObjectFlags kVerifyFlags>
 inline bool Object::IsPhantomReferenceInstance() {
-  return GetClass()->IsPhantomReferenceClass();
+  return GetClass<kVerifyFlags>()->IsPhantomReferenceClass();
 }
 
+template<VerifyObjectFlags kVerifyFlags>
 inline size_t Object::SizeOf() {
   size_t result;
-  if (IsArrayInstance()) {
-    result = AsArray()->SizeOf();
-  } else if (IsClass()) {
-    result = AsClass()->SizeOf();
+  constexpr auto kNewFlags = static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis);
+  if (IsArrayInstance<kVerifyFlags>()) {
+    result = AsArray<kNewFlags>()->SizeOf<>();
+  } else if (IsClass<kNewFlags>()) {
+    result = AsClass<kNewFlags>()->SizeOf<kNewFlags>();
   } else {
-    result = GetClass()->GetObjectSize();
+    result = GetClass<kNewFlags>()->GetObjectSize();
   }
-  DCHECK_GE(result, sizeof(Object)) << " class=" << PrettyTypeOf(GetClass());
-  DCHECK(!IsArtField()  || result == sizeof(ArtField));
-  DCHECK(!IsArtMethod() || result == sizeof(ArtMethod));
+  DCHECK_GE(result, sizeof(Object)) << " class=" << PrettyTypeOf(GetClass<kNewFlags>());
+  DCHECK(!IsArtField<kNewFlags>()  || result == sizeof(ArtField));
+  DCHECK(!IsArtMethod<kNewFlags>() || result == sizeof(ArtMethod));
   return result;
 }
 
+template<VerifyObjectFlags kVerifyFlags>
 inline int32_t Object::GetField32(MemberOffset field_offset, bool is_volatile) {
-  VerifyObject(this);
+  if (kVerifyFlags & kVerifyThis) {
+    VerifyObject(this);
+  }
   const byte* raw_addr = reinterpret_cast<const byte*>(this) + field_offset.Int32Value();
   const int32_t* word_addr = reinterpret_cast<const int32_t*>(raw_addr);
   if (UNLIKELY(is_volatile)) {
@@ -270,9 +319,8 @@
   }
 }
 
-template<bool kTransactionActive, bool kCheckTransaction>
-inline void Object::SetField32(MemberOffset field_offset, int32_t new_value, bool is_volatile,
-                               bool this_is_valid) {
+template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
+inline void Object::SetField32(MemberOffset field_offset, int32_t new_value, bool is_volatile) {
   if (kCheckTransaction) {
     DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
   }
@@ -280,7 +328,7 @@
     Runtime::Current()->RecordWriteField32(this, field_offset, GetField32(field_offset, is_volatile),
                                            is_volatile);
   }
-  if (this_is_valid) {
+  if (kVerifyFlags & kVerifyThis) {
     VerifyObject(this);
   }
   byte* raw_addr = reinterpret_cast<byte*>(this) + field_offset.Int32Value();
@@ -294,7 +342,7 @@
   }
 }
 
-template<bool kTransactionActive, bool kCheckTransaction>
+template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
 inline bool Object::CasField32(MemberOffset field_offset, int32_t old_value, int32_t new_value) {
   if (kCheckTransaction) {
     DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
@@ -302,14 +350,19 @@
   if (kTransactionActive) {
     Runtime::Current()->RecordWriteField32(this, field_offset, old_value, true);
   }
-  VerifyObject(this);
+  if (kVerifyFlags & kVerifyThis) {
+    VerifyObject(this);
+  }
   byte* raw_addr = reinterpret_cast<byte*>(this) + field_offset.Int32Value();
   volatile int32_t* addr = reinterpret_cast<volatile int32_t*>(raw_addr);
   return __sync_bool_compare_and_swap(addr, old_value, new_value);
 }
 
+template<VerifyObjectFlags kVerifyFlags>
 inline int64_t Object::GetField64(MemberOffset field_offset, bool is_volatile) {
-  VerifyObject(this);
+  if (kVerifyFlags & kVerifyThis) {
+    VerifyObject(this);
+  }
   const byte* raw_addr = reinterpret_cast<const byte*>(this) + field_offset.Int32Value();
   const int64_t* addr = reinterpret_cast<const int64_t*>(raw_addr);
   if (UNLIKELY(is_volatile)) {
@@ -321,9 +374,8 @@
   }
 }
 
-template<bool kTransactionActive, bool kCheckTransaction>
-inline void Object::SetField64(MemberOffset field_offset, int64_t new_value, bool is_volatile,
-                               bool this_is_valid) {
+template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
+inline void Object::SetField64(MemberOffset field_offset, int64_t new_value, bool is_volatile) {
   if (kCheckTransaction) {
     DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
   }
@@ -331,7 +383,7 @@
     Runtime::Current()->RecordWriteField64(this, field_offset, GetField64(field_offset, is_volatile),
                                            is_volatile);
   }
-  if (this_is_valid) {
+  if (kVerifyFlags & kVerifyThis) {
     VerifyObject(this);
   }
   byte* raw_addr = reinterpret_cast<byte*>(this) + field_offset.Int32Value();
@@ -349,7 +401,7 @@
   }
 }
 
-template<bool kTransactionActive, bool kCheckTransaction>
+template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
 inline bool Object::CasField64(MemberOffset field_offset, int64_t old_value, int64_t new_value) {
   if (kCheckTransaction) {
     DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
@@ -357,15 +409,19 @@
   if (kTransactionActive) {
     Runtime::Current()->RecordWriteField64(this, field_offset, old_value, true);
   }
-  VerifyObject(this);
+  if (kVerifyFlags & kVerifyThis) {
+    VerifyObject(this);
+  }
   byte* raw_addr = reinterpret_cast<byte*>(this) + field_offset.Int32Value();
   volatile int64_t* addr = reinterpret_cast<volatile int64_t*>(raw_addr);
   return QuasiAtomic::Cas64(old_value, new_value, addr);
 }
 
-template<class T>
+template<class T, VerifyObjectFlags kVerifyFlags>
 inline T* Object::GetFieldObject(MemberOffset field_offset, bool is_volatile) {
-  VerifyObject(this);
+  if (kVerifyFlags & kVerifyThis) {
+    VerifyObject(this);
+  }
   byte* raw_addr = reinterpret_cast<byte*>(this) + field_offset.Int32Value();
   HeapReference<T>* objref_addr = reinterpret_cast<HeapReference<T>*>(raw_addr);
   HeapReference<T> objref = *objref_addr;
@@ -374,13 +430,15 @@
     QuasiAtomic::MembarLoadLoad();  // Ensure loads don't re-order.
   }
   T* result = objref.AsMirrorPtr();
-  VerifyObject(result);
+  if (kVerifyFlags & kVerifyReads) {
+    VerifyObject(result);
+  }
   return result;
 }
 
-template<bool kTransactionActive, bool kCheckTransaction>
+template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
 inline void Object::SetFieldObjectWithoutWriteBarrier(MemberOffset field_offset, Object* new_value,
-                                                      bool is_volatile, bool this_is_valid) {
+                                                      bool is_volatile) {
   if (kCheckTransaction) {
     DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
   }
@@ -389,10 +447,12 @@
                                                   GetFieldObject<Object>(field_offset, is_volatile),
                                                   true);
   }
-  if (this_is_valid) {
+  if (kVerifyFlags & kVerifyThis) {
     VerifyObject(this);
   }
-  VerifyObject(new_value);
+  if (kVerifyFlags & kVerifyWrites) {
+    VerifyObject(new_value);
+  }
   HeapReference<Object> objref(HeapReference<Object>::FromMirrorPtr(new_value));
   byte* raw_addr = reinterpret_cast<byte*>(this) + field_offset.Int32Value();
   HeapReference<Object>* objref_addr = reinterpret_cast<HeapReference<Object>*>(raw_addr);
@@ -405,27 +465,43 @@
   }
 }
 
-template<bool kTransactionActive, bool kCheckTransaction>
-inline void Object::SetFieldObject(MemberOffset field_offset, Object* new_value, bool is_volatile,
-                                   bool this_is_valid) {
-  SetFieldObjectWithoutWriteBarrier<kTransactionActive, kCheckTransaction>(field_offset, new_value,
-                                                                           is_volatile,
-                                                                           this_is_valid);
+template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
+inline void Object::SetFieldObject(MemberOffset field_offset, Object* new_value, bool is_volatile) {
+  SetFieldObjectWithoutWriteBarrier<kTransactionActive, kCheckTransaction, kVerifyFlags>(
+      field_offset, new_value, is_volatile);
   if (new_value != nullptr) {
     CheckFieldAssignment(field_offset, new_value);
     Runtime::Current()->GetHeap()->WriteBarrierField(this, field_offset, new_value);
   }
 }
 
-template<bool kTransactionActive, bool kCheckTransaction>
-inline bool Object::CasFieldObject(MemberOffset field_offset, Object* old_value, Object* new_value) {
+template <VerifyObjectFlags kVerifyFlags>
+inline HeapReference<Object>* Object::GetFieldObjectReferenceAddr(MemberOffset field_offset) {
+  if (kVerifyFlags & kVerifyThis) {
+    VerifyObject(this);
+  }
+  return reinterpret_cast<HeapReference<Object>*>(reinterpret_cast<byte*>(this) +
+      field_offset.Int32Value());
+}
+
+template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
+inline bool Object::CasFieldObject(MemberOffset field_offset, Object* old_value,
+                                   Object* new_value) {
   if (kCheckTransaction) {
     DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
   }
+  if (kVerifyFlags & kVerifyThis) {
+    VerifyObject(this);
+  }
+  if (kVerifyFlags & kVerifyWrites) {
+    VerifyObject(new_value);
+  }
+  if (kVerifyFlags & kVerifyReads) {
+    VerifyObject(old_value);
+  }
   if (kTransactionActive) {
     Runtime::Current()->RecordWriteFieldReference(this, field_offset, old_value, true);
   }
-  VerifyObject(this);
   byte* raw_addr = reinterpret_cast<byte*>(this) + field_offset.Int32Value();
   volatile int32_t* addr = reinterpret_cast<volatile int32_t*>(raw_addr);
   HeapReference<Object> old_ref(HeapReference<Object>::FromMirrorPtr(old_value));
@@ -437,12 +513,6 @@
   return success;
 }
 
-inline void Object::VerifyObject(Object* obj) {
-  if (kIsDebugBuild) {
-    Runtime::Current()->GetHeap()->VerifyObject(obj);
-  }
-}
-
 }  // namespace mirror
 }  // namespace art
 
diff --git a/runtime/mirror/object.cc b/runtime/mirror/object.cc
index 1251852..f1485e5 100644
--- a/runtime/mirror/object.cc
+++ b/runtime/mirror/object.cc
@@ -141,10 +141,9 @@
 
 void Object::CheckFieldAssignmentImpl(MemberOffset field_offset, Object* new_value) {
   Class* c = GetClass();
-  if (Runtime::Current()->GetClassLinker() == NULL ||
-      !Runtime::Current()->IsStarted() ||
-      !Runtime::Current()->GetHeap()->IsObjectValidationEnabled() ||
-      !c->IsResolved()) {
+  Runtime* runtime = Runtime::Current();
+  if (runtime->GetClassLinker() == nullptr || !runtime->IsStarted() ||
+      !runtime->GetHeap()->IsObjectValidationEnabled() || !c->IsResolved()) {
     return;
   }
   for (Class* cur = c; cur != NULL; cur = cur->GetSuperClass()) {
diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h
index eb118c7..7487dd2 100644
--- a/runtime/mirror/object.h
+++ b/runtime/mirror/object.h
@@ -24,6 +24,7 @@
 #include "object_reference.h"
 #include "offsets.h"
 #include "runtime.h"
+#include "verify_object.h"
 
 namespace art {
 
@@ -59,7 +60,8 @@
 #define OFFSET_OF_OBJECT_MEMBER(type, field) \
     MemberOffset(OFFSETOF_MEMBER(type, field))
 
-constexpr bool kCheckFieldAssignments = false;
+// Checks that we don't do field assignments which violate the typing system.
+static constexpr bool kCheckFieldAssignments = false;
 
 // C++ mirror of java.lang.Object
 class MANAGED Object {
@@ -68,16 +70,20 @@
     return OFFSET_OF_OBJECT_MEMBER(Object, klass_);
   }
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   Class* GetClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   void SetClass(Class* new_klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // The verifier treats all interfaces as java.lang.Object and relies on runtime checks in
   // invoke-interface to detect incompatible interface types.
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool VerifierInstanceOf(Class* klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool InstanceOf(Class* klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   size_t SizeOf() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   Object* Clone(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -95,135 +101,147 @@
 
   mirror::Object* MonitorEnter(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
       EXCLUSIVE_LOCK_FUNCTION(monitor_lock_);
-
   bool MonitorExit(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
       UNLOCK_FUNCTION(monitor_lock_);
-
   void Notify(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
   void NotifyAll(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
   void Wait(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
   void Wait(Thread* self, int64_t timeout, int32_t nanos) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool IsClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   Class* AsClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool IsObjectArray() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
-  template<class T>
+  template<class T, VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   ObjectArray<T>* AsObjectArray() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool IsArrayInstance() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   Array* AsArray() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   BooleanArray* AsBooleanArray() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   ByteArray* AsByteArray() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   ByteArray* AsByteSizedArray() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   CharArray* AsCharArray() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   ShortArray* AsShortArray() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   ShortArray* AsShortSizedArray() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   IntArray* AsIntArray() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   LongArray* AsLongArray() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   FloatArray* AsFloatArray() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   DoubleArray* AsDoubleArray() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   String* AsString() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   Throwable* AsThrowable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool IsArtMethod() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   ArtMethod* AsArtMethod() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool IsArtField() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   ArtField* AsArtField() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool IsReferenceInstance() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool IsWeakReferenceInstance() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool IsSoftReferenceInstance() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool IsFinalizerReferenceInstance() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool IsPhantomReferenceInstance() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Accessor for Java type fields.
-  template<class T> T* GetFieldObject(MemberOffset field_offset, bool is_volatile)
+  template<class T, VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+  T* GetFieldObject(MemberOffset field_offset, bool is_volatile)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  template<bool kTransactionActive, bool kCheckTransaction = true>
+  template<bool kTransactionActive, bool kCheckTransaction = true,
+      VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   void SetFieldObjectWithoutWriteBarrier(MemberOffset field_offset, Object* new_value,
-                                         bool is_volatile, bool this_is_valid = true)
+                                         bool is_volatile)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  template<bool kTransactionActive, bool kCheckTransaction = true>
-  void SetFieldObject(MemberOffset field_offset, Object* new_value, bool is_volatile,
-                      bool this_is_valid = true)
+  template<bool kTransactionActive, bool kCheckTransaction = true,
+      VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+  void SetFieldObject(MemberOffset field_offset, Object* new_value, bool is_volatile)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  template<bool kTransactionActive, bool kCheckTransaction = true>
+  template<bool kTransactionActive, bool kCheckTransaction = true,
+      VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool CasFieldObject(MemberOffset field_offset, Object* old_value, Object* new_value)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  HeapReference<Object>* GetFieldObjectReferenceAddr(MemberOffset field_offset) ALWAYS_INLINE {
-    VerifyObject(this);
-    return reinterpret_cast<HeapReference<Object>*>(reinterpret_cast<byte*>(this) +
-        field_offset.Int32Value());
-  }
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+  HeapReference<Object>* GetFieldObjectReferenceAddr(MemberOffset field_offset);
 
-  int32_t GetField32(MemberOffset field_offset, bool is_volatile);
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+  int32_t GetField32(MemberOffset field_offset, bool is_volatile)
+      NO_THREAD_SAFETY_ANALYSIS;
 
-  template<bool kTransactionActive, bool kCheckTransaction = true>
-  void SetField32(MemberOffset field_offset, int32_t new_value, bool is_volatile,
-                  bool this_is_valid = true);
-
-  template<bool kTransactionActive, bool kCheckTransaction = true>
+  template<bool kTransactionActive, bool kCheckTransaction = true,
+      VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+  void SetField32(MemberOffset field_offset, int32_t new_value, bool is_volatile);
+  template<bool kTransactionActive, bool kCheckTransaction = true,
+      VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool CasField32(MemberOffset field_offset, int32_t old_value, int32_t new_value)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   int64_t GetField64(MemberOffset field_offset, bool is_volatile);
+  template<bool kTransactionActive, bool kCheckTransaction = true,
+      VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+  void SetField64(MemberOffset field_offset, int64_t new_value, bool is_volatile);
 
-  template<bool kTransactionActive, bool kCheckTransaction = true>
-  void SetField64(MemberOffset field_offset, int64_t new_value, bool is_volatile,
-                  bool this_is_valid = true);
-
-  template<bool kTransactionActive, bool kCheckTransaction = true>
+  template<bool kTransactionActive, bool kCheckTransaction = true,
+      VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool CasField64(MemberOffset field_offset, int64_t old_value, int64_t new_value)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  template<bool kTransactionActive, bool kCheckTransaction = true, typename T>
-  void SetFieldPtr(MemberOffset field_offset, T new_value, bool is_volatile,
-                   bool this_is_valid = true) {
+  template<bool kTransactionActive, bool kCheckTransaction = true,
+      VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, typename T>
+  void SetFieldPtr(MemberOffset field_offset, T new_value, bool is_volatile) {
 #ifndef __LP64__
-    SetField32<kTransactionActive, kCheckTransaction>(field_offset,
-                                                      reinterpret_cast<int32_t>(new_value),
-                                                      is_volatile, this_is_valid);
+    SetField32<kTransactionActive, kCheckTransaction, kVerifyFlags>(
+        field_offset, reinterpret_cast<int32_t>(new_value), is_volatile);
 #else
-    SetField64<kTransactionActive, kCheckTransaction>(field_offset,
-                                                      reinterpret_cast<int64_t>(new_value),
-                                                      is_volatile, this_is_valid);
+    SetField64<kTransactionActive, kCheckTransaction, kVerifyFlags>(
+        field_offset, reinterpret_cast<int64_t>(new_value), is_volatile);
 #endif
   }
 
  protected:
   // Accessors for non-Java type fields
-  template<class T>
-  T GetFieldPtr(MemberOffset field_offset, bool is_volatile) {
+  template<class T, VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+  T GetFieldPtr(MemberOffset field_offset, bool is_volatile) NO_THREAD_SAFETY_ANALYSIS {
 #ifndef __LP64__
-    return reinterpret_cast<T>(GetField32(field_offset, is_volatile));
+    return reinterpret_cast<T>(GetField32<kVerifyFlags>(field_offset, is_volatile));
 #else
-    return reinterpret_cast<T>(GetField64(field_offset, is_volatile));
+    return reinterpret_cast<T>(GetField64<kVerifyFlags>(field_offset, is_volatile));
 #endif
   }
 
  private:
-  static void VerifyObject(Object* obj) ALWAYS_INLINE;
   // Verify the type correctness of stores to fields.
   void CheckFieldAssignmentImpl(MemberOffset field_offset, Object* new_value)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/mirror/object_array-inl.h b/runtime/mirror/object_array-inl.h
index 521b6ce..a427957 100644
--- a/runtime/mirror/object_array-inl.h
+++ b/runtime/mirror/object_array-inl.h
@@ -58,10 +58,10 @@
   return GetFieldObject<T>(OffsetOfElement(i), false);
 }
 
-template<class T>
+template<class T> template<VerifyObjectFlags kVerifyFlags>
 inline bool ObjectArray<T>::CheckAssignable(T* object) {
   if (object != NULL) {
-    Class* element_class = GetClass()->GetComponentType();
+    Class* element_class = GetClass<kVerifyFlags>()->GetComponentType();
     if (UNLIKELY(!object->InstanceOf(element_class))) {
       ThrowArrayStoreException(object);
       return false;
@@ -80,31 +80,33 @@
 }
 
 template<class T>
-template<bool kTransactionActive, bool kCheckTransaction>
+template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
 inline void ObjectArray<T>::Set(int32_t i, T* object) {
-  if (LIKELY(CheckIsValidIndex(i) && CheckAssignable(object))) {
-    SetFieldObject<kTransactionActive, kCheckTransaction>(OffsetOfElement(i), object, false);
+  if (LIKELY(CheckIsValidIndex(i) && CheckAssignable<kVerifyFlags>(object))) {
+    SetFieldObject<kTransactionActive, kCheckTransaction, kVerifyFlags>(OffsetOfElement(i), object,
+                                                                        false);
   } else {
     DCHECK(Thread::Current()->IsExceptionPending());
   }
 }
 
 template<class T>
-template<bool kTransactionActive, bool kCheckTransaction>
+template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
 inline void ObjectArray<T>::SetWithoutChecks(int32_t i, T* object) {
-  DCHECK(CheckIsValidIndex(i));
-  DCHECK(CheckAssignable(object));
-  SetFieldObject<kTransactionActive, kCheckTransaction>(OffsetOfElement(i), object, false);
+  DCHECK(CheckIsValidIndex<kVerifyFlags>(i));
+  DCHECK(CheckAssignable<static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis)>(object));
+  SetFieldObject<kTransactionActive, kCheckTransaction, kVerifyFlags>(OffsetOfElement(i), object,
+                                                                      false);
 }
 
 template<class T>
-template<bool kTransactionActive, bool kCheckTransaction>
+template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
 inline void ObjectArray<T>::SetWithoutChecksAndWriteBarrier(int32_t i, T* object) {
-  DCHECK(CheckIsValidIndex(i));
+  DCHECK(CheckIsValidIndex<kVerifyFlags>(i));
   // TODO:  enable this check. It fails when writing the image in ImageWriter::FixupObjectArray.
   // DCHECK(CheckAssignable(object));
-  SetFieldObjectWithoutWriteBarrier<kTransactionActive, kCheckTransaction>(OffsetOfElement(i),
-                                                                           object, false);
+  SetFieldObjectWithoutWriteBarrier<kTransactionActive, kCheckTransaction, kVerifyFlags>(
+      OffsetOfElement(i), object, false);
 }
 
 template<class T>
diff --git a/runtime/mirror/object_array.h b/runtime/mirror/object_array.h
index 668b276..7f9e716 100644
--- a/runtime/mirror/object_array.h
+++ b/runtime/mirror/object_array.h
@@ -37,22 +37,27 @@
 
   // Returns true if the object can be stored into the array. If not, throws
   // an ArrayStoreException and returns false.
-  bool CheckAssignable(T* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  // TODO fix thread safety analysis: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_).
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+  bool CheckAssignable(T* object) NO_THREAD_SAFETY_ANALYSIS;
 
   void Set(int32_t i, T* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   // TODO fix thread safety analysis: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_).
-  template<bool kTransactionActive, bool kCheckTransaction = true>
+  template<bool kTransactionActive, bool kCheckTransaction = true,
+      VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   void Set(int32_t i, T* object) NO_THREAD_SAFETY_ANALYSIS;
 
   // Set element without bound and element type checks, to be used in limited
   // circumstances, such as during boot image writing.
   // TODO fix thread safety analysis broken by the use of template. This should be
   // SHARED_LOCKS_REQUIRED(Locks::mutator_lock_).
-  template<bool kTransactionActive, bool kCheckTransaction = true>
+  template<bool kTransactionActive, bool kCheckTransaction = true,
+      VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   void SetWithoutChecks(int32_t i, T* object) NO_THREAD_SAFETY_ANALYSIS;
   // TODO fix thread safety analysis broken by the use of template. This should be
   // SHARED_LOCKS_REQUIRED(Locks::mutator_lock_).
-  template<bool kTransactionActive, bool kCheckTransaction = true>
+  template<bool kTransactionActive, bool kCheckTransaction = true,
+      VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   void SetWithoutChecksAndWriteBarrier(int32_t i, T* object) NO_THREAD_SAFETY_ANALYSIS;
 
   T* GetWithoutChecks(int32_t i) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc
index 40c3748..34fb15e 100644
--- a/runtime/mirror/object_test.cc
+++ b/runtime/mirror/object_test.cc
@@ -123,7 +123,8 @@
   EXPECT_TRUE(oa->Get(0) == oa.get());
   EXPECT_TRUE(oa->Get(1) == oa.get());
 
-  Class* aioobe = class_linker_->FindSystemClass("Ljava/lang/ArrayIndexOutOfBoundsException;");
+  Class* aioobe = class_linker_->FindSystemClass(soa.Self(),
+                                                 "Ljava/lang/ArrayIndexOutOfBoundsException;");
 
   EXPECT_TRUE(oa->Get(-1) == NULL);
   EXPECT_TRUE(soa.Self()->IsExceptionPending());
@@ -138,21 +139,23 @@
   ASSERT_TRUE(oa->GetClass() != NULL);
   ClassHelper oa_ch(oa->GetClass());
   ASSERT_EQ(2U, oa_ch.NumDirectInterfaces());
-  EXPECT_EQ(class_linker_->FindSystemClass("Ljava/lang/Cloneable;"), oa_ch.GetDirectInterface(0));
-  EXPECT_EQ(class_linker_->FindSystemClass("Ljava/io/Serializable;"), oa_ch.GetDirectInterface(1));
+  EXPECT_EQ(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Cloneable;"),
+            oa_ch.GetDirectInterface(0));
+  EXPECT_EQ(class_linker_->FindSystemClass(soa.Self(), "Ljava/io/Serializable;"),
+            oa_ch.GetDirectInterface(1));
 }
 
 TEST_F(ObjectTest, AllocArray) {
   ScopedObjectAccess soa(Thread::Current());
-  Class* c = class_linker_->FindSystemClass("[I");
+  Class* c = class_linker_->FindSystemClass(soa.Self(), "[I");
   SirtRef<Array> a(soa.Self(), Array::Alloc<true>(soa.Self(), c, 1));
   ASSERT_TRUE(c == a->GetClass());
 
-  c = class_linker_->FindSystemClass("[Ljava/lang/Object;");
+  c = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/Object;");
   a.reset(Array::Alloc<true>(soa.Self(), c, 1));
   ASSERT_TRUE(c == a->GetClass());
 
-  c = class_linker_->FindSystemClass("[[Ljava/lang/Object;");
+  c = class_linker_->FindSystemClass(soa.Self(), "[[Ljava/lang/Object;");
   a.reset(Array::Alloc<true>(soa.Self(), c, 1));
   ASSERT_TRUE(c == a->GetClass());
 }
@@ -173,7 +176,7 @@
   EXPECT_EQ(T(123), a->Get(0));
   EXPECT_EQ(T(321), a->Get(1));
 
-  Class* aioobe = cl->FindSystemClass("Ljava/lang/ArrayIndexOutOfBoundsException;");
+  Class* aioobe = cl->FindSystemClass(soa.Self(), "Ljava/lang/ArrayIndexOutOfBoundsException;");
 
   EXPECT_EQ(0, a->Get(-1));
   EXPECT_TRUE(soa.Self()->IsExceptionPending());
@@ -214,7 +217,7 @@
 TEST_F(ObjectTest, CheckAndAllocArrayFromCode) {
   // pretend we are trying to call 'new char[3]' from String.toCharArray
   ScopedObjectAccess soa(Thread::Current());
-  Class* java_util_Arrays = class_linker_->FindSystemClass("Ljava/util/Arrays;");
+  Class* java_util_Arrays = class_linker_->FindSystemClass(soa.Self(), "Ljava/util/Arrays;");
   ArtMethod* sort = java_util_Arrays->FindDirectMethod("sort", "([I)V");
   const DexFile::StringId* string_id = java_lang_dex_file_->FindStringId("[I");
   ASSERT_TRUE(string_id != NULL);
@@ -233,11 +236,11 @@
 TEST_F(ObjectTest, CreateMultiArray) {
   ScopedObjectAccess soa(Thread::Current());
 
-  SirtRef<Class> c(soa.Self(), class_linker_->FindSystemClass("I"));
+  SirtRef<Class> c(soa.Self(), class_linker_->FindSystemClass(soa.Self(), "I"));
   SirtRef<IntArray> dims(soa.Self(), IntArray::Alloc(soa.Self(), 1));
   dims->Set<false>(0, 1);
   Array* multi = Array::CreateMultiArray(soa.Self(), c, dims);
-  EXPECT_TRUE(multi->GetClass() == class_linker_->FindSystemClass("[I"));
+  EXPECT_TRUE(multi->GetClass() == class_linker_->FindSystemClass(soa.Self(), "[I"));
   EXPECT_EQ(1, multi->GetLength());
 
   dims->Set<false>(0, -1);
@@ -253,11 +256,11 @@
       dims->Set<false>(0, i);
       dims->Set<false>(1, j);
       multi = Array::CreateMultiArray(soa.Self(), c, dims);
-      EXPECT_TRUE(multi->GetClass() == class_linker_->FindSystemClass("[[I"));
+      EXPECT_TRUE(multi->GetClass() == class_linker_->FindSystemClass(soa.Self(), "[[I"));
       EXPECT_EQ(i, multi->GetLength());
       for (int k = 0; k < i; ++k) {
         Array* outer = multi->AsObjectArray<Array>()->Get(k);
-        EXPECT_TRUE(outer->GetClass() == class_linker_->FindSystemClass("[I"));
+        EXPECT_TRUE(outer->GetClass() == class_linker_->FindSystemClass(soa.Self(), "[I"));
         EXPECT_EQ(j, outer->GetLength());
       }
     }
@@ -272,8 +275,7 @@
   CHECK(dex_file != NULL);
 
   SirtRef<mirror::ClassLoader> loader(soa.Self(), soa.Decode<ClassLoader*>(class_loader));
-  Class* klass =
-      class_linker_->FindClass("LStaticsFromCode;", loader);
+  Class* klass = class_linker_->FindClass(soa.Self(), "LStaticsFromCode;", loader);
   ArtMethod* clinit = klass->FindClassInitializer();
   const DexFile::StringId* klass_string_id = dex_file->FindStringId("LStaticsFromCode;");
   ASSERT_TRUE(klass_string_id != NULL);
@@ -404,9 +406,9 @@
   SirtRef<ClassLoader> class_loader_1(soa.Self(), soa.Decode<ClassLoader*>(jclass_loader_1));
   SirtRef<ClassLoader> class_loader_2(soa.Self(), soa.Decode<ClassLoader*>(jclass_loader_2));
 
-  Class* klass1 = linker->FindClass("LProtoCompare;", class_loader_1);
+  Class* klass1 = linker->FindClass(soa.Self(), "LProtoCompare;", class_loader_1);
   ASSERT_TRUE(klass1 != NULL);
-  Class* klass2 = linker->FindClass("LProtoCompare2;", class_loader_2);
+  Class* klass2 = linker->FindClass(soa.Self(), "LProtoCompare2;", class_loader_2);
   ASSERT_TRUE(klass2 != NULL);
 
   ArtMethod* m1_1 = klass1->GetVirtualMethod(0);
@@ -472,8 +474,8 @@
   jobject jclass_loader = LoadDex("XandY");
   SirtRef<ClassLoader> class_loader(soa.Self(), soa.Decode<ClassLoader*>(jclass_loader));
 
-  Class* X = class_linker_->FindClass("LX;", class_loader);
-  Class* Y = class_linker_->FindClass("LY;", class_loader);
+  Class* X = class_linker_->FindClass(soa.Self(), "LX;", class_loader);
+  Class* Y = class_linker_->FindClass(soa.Self(), "LY;", class_loader);
   ASSERT_TRUE(X != NULL);
   ASSERT_TRUE(Y != NULL);
 
@@ -487,16 +489,16 @@
   EXPECT_TRUE(y->InstanceOf(X));
   EXPECT_TRUE(y->InstanceOf(Y));
 
-  Class* java_lang_Class = class_linker_->FindSystemClass("Ljava/lang/Class;");
-  Class* Object_array_class = class_linker_->FindSystemClass("[Ljava/lang/Object;");
+  Class* java_lang_Class = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Class;");
+  Class* Object_array_class = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/Object;");
 
   EXPECT_FALSE(java_lang_Class->InstanceOf(Object_array_class));
   EXPECT_TRUE(Object_array_class->InstanceOf(java_lang_Class));
 
   // All array classes implement Cloneable and Serializable.
   Object* array = ObjectArray<Object>::Alloc(soa.Self(), Object_array_class, 1);
-  Class* java_lang_Cloneable = class_linker_->FindSystemClass("Ljava/lang/Cloneable;");
-  Class* java_io_Serializable = class_linker_->FindSystemClass("Ljava/io/Serializable;");
+  Class* java_lang_Cloneable = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Cloneable;");
+  Class* java_io_Serializable = class_linker_->FindSystemClass(soa.Self(), "Ljava/io/Serializable;");
   EXPECT_TRUE(array->InstanceOf(java_lang_Cloneable));
   EXPECT_TRUE(array->InstanceOf(java_io_Serializable));
 }
@@ -505,8 +507,8 @@
   ScopedObjectAccess soa(Thread::Current());
   jobject jclass_loader = LoadDex("XandY");
   SirtRef<ClassLoader> class_loader(soa.Self(), soa.Decode<ClassLoader*>(jclass_loader));
-  Class* X = class_linker_->FindClass("LX;", class_loader);
-  Class* Y = class_linker_->FindClass("LY;", class_loader);
+  Class* X = class_linker_->FindClass(soa.Self(), "LX;", class_loader);
+  Class* Y = class_linker_->FindClass(soa.Self(), "LY;", class_loader);
 
   EXPECT_TRUE(X->IsAssignableFrom(X));
   EXPECT_TRUE(X->IsAssignableFrom(Y));
@@ -514,8 +516,8 @@
   EXPECT_TRUE(Y->IsAssignableFrom(Y));
 
   // class final String implements CharSequence, ..
-  Class* string = class_linker_->FindSystemClass("Ljava/lang/String;");
-  Class* charseq = class_linker_->FindSystemClass("Ljava/lang/CharSequence;");
+  Class* string = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/String;");
+  Class* charseq = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/CharSequence;");
   // Can String be assigned to CharSequence without a cast?
   EXPECT_TRUE(charseq->IsAssignableFrom(string));
   // Can CharSequence be assigned to String without a cast?
@@ -542,36 +544,36 @@
   ScopedObjectAccess soa(Thread::Current());
   jobject jclass_loader = LoadDex("XandY");
   SirtRef<ClassLoader> class_loader(soa.Self(), soa.Decode<ClassLoader*>(jclass_loader));
-  Class* X = class_linker_->FindClass("LX;", class_loader);
-  Class* Y = class_linker_->FindClass("LY;", class_loader);
+  Class* X = class_linker_->FindClass(soa.Self(), "LX;", class_loader);
+  Class* Y = class_linker_->FindClass(soa.Self(), "LY;", class_loader);
   ASSERT_TRUE(X != NULL);
   ASSERT_TRUE(Y != NULL);
 
-  Class* YA = class_linker_->FindClass("[LY;", class_loader);
-  Class* YAA = class_linker_->FindClass("[[LY;", class_loader);
+  Class* YA = class_linker_->FindClass(soa.Self(), "[LY;", class_loader);
+  Class* YAA = class_linker_->FindClass(soa.Self(), "[[LY;", class_loader);
   ASSERT_TRUE(YA != NULL);
   ASSERT_TRUE(YAA != NULL);
 
-  Class* XAA = class_linker_->FindClass("[[LX;", class_loader);
+  Class* XAA = class_linker_->FindClass(soa.Self(), "[[LX;", class_loader);
   ASSERT_TRUE(XAA != NULL);
 
-  Class* O = class_linker_->FindSystemClass("Ljava/lang/Object;");
-  Class* OA = class_linker_->FindSystemClass("[Ljava/lang/Object;");
-  Class* OAA = class_linker_->FindSystemClass("[[Ljava/lang/Object;");
-  Class* OAAA = class_linker_->FindSystemClass("[[[Ljava/lang/Object;");
+  Class* O = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;");
+  Class* OA = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/Object;");
+  Class* OAA = class_linker_->FindSystemClass(soa.Self(), "[[Ljava/lang/Object;");
+  Class* OAAA = class_linker_->FindSystemClass(soa.Self(), "[[[Ljava/lang/Object;");
   ASSERT_TRUE(O != NULL);
   ASSERT_TRUE(OA != NULL);
   ASSERT_TRUE(OAA != NULL);
   ASSERT_TRUE(OAAA != NULL);
 
-  Class* S = class_linker_->FindSystemClass("Ljava/io/Serializable;");
-  Class* SA = class_linker_->FindSystemClass("[Ljava/io/Serializable;");
-  Class* SAA = class_linker_->FindSystemClass("[[Ljava/io/Serializable;");
+  Class* S = class_linker_->FindSystemClass(soa.Self(), "Ljava/io/Serializable;");
+  Class* SA = class_linker_->FindSystemClass(soa.Self(), "[Ljava/io/Serializable;");
+  Class* SAA = class_linker_->FindSystemClass(soa.Self(), "[[Ljava/io/Serializable;");
   ASSERT_TRUE(S != NULL);
   ASSERT_TRUE(SA != NULL);
   ASSERT_TRUE(SAA != NULL);
 
-  Class* IA = class_linker_->FindSystemClass("[I");
+  Class* IA = class_linker_->FindSystemClass(soa.Self(), "[I");
   ASSERT_TRUE(IA != NULL);
 
   EXPECT_TRUE(YAA->IsAssignableFrom(YAA));  // identity
@@ -616,7 +618,7 @@
   // TODO: check that s.count == 3.
 
   // Ensure that we handle superclass fields correctly...
-  c = class_linker_->FindSystemClass("Ljava/lang/StringBuilder;");
+  c = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/StringBuilder;");
   ASSERT_TRUE(c != NULL);
   // No StringBuilder.count...
   EXPECT_TRUE(c->FindDeclaredInstanceField("count", "I") == NULL);
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index def3292..5779442 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -16,7 +16,7 @@
 
 #include <limits.h>
 
-#include "class_linker.h"
+#include "class_linker-inl.h"
 #include "common_throws.h"
 #include "debugger.h"
 #include "dex_file-inl.h"
@@ -57,22 +57,22 @@
                                             jint length) {
   ScopedFastNativeObjectAccess soa(env);
   mirror::Class* element_class = soa.Decode<mirror::Class*>(javaElementClass);
-  if (element_class == NULL) {
+  if (UNLIKELY(element_class == nullptr)) {
     ThrowNullPointerException(NULL, "element class == null");
-    return NULL;
+    return nullptr;
   }
-  if (length < 0) {
+  if (UNLIKELY(length < 0)) {
     ThrowNegativeArraySizeException(length);
-    return NULL;
+    return nullptr;
   }
-  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  std::string descriptor;
-  descriptor += "[";
-  descriptor += ClassHelper(element_class).GetDescriptor();
-  SirtRef<mirror::ClassLoader> class_loader(soa.Self(), nullptr);
-  mirror::Class* array_class = class_linker->FindClass(descriptor.c_str(), class_loader);
+  Runtime* runtime = Runtime::Current();
+  mirror::Class* array_class = runtime->GetClassLinker()->FindArrayClass(soa.Self(), element_class);
+  if (UNLIKELY(array_class == nullptr)) {
+    return nullptr;
+  }
+  gc::AllocatorType allocator = runtime->GetHeap()->GetCurrentNonMovingAllocator();
   mirror::Array* result = mirror::Array::Alloc<true>(soa.Self(), array_class, length,
-                                                     Runtime::Current()->GetHeap()->GetCurrentNonMovingAllocator());
+                                                     allocator);
   return soa.AddLocalReference<jobject>(result);
 }
 
diff --git a/runtime/native/dalvik_system_Zygote.cc b/runtime/native/dalvik_system_Zygote.cc
index 29c0bc0..4d009db 100644
--- a/runtime/native/dalvik_system_Zygote.cc
+++ b/runtime/native/dalvik_system_Zygote.cc
@@ -217,10 +217,6 @@
 
 static void DropCapabilitiesBoundingSet() {
   for (int i = 0; prctl(PR_CAPBSET_READ, i, 0, 0, 0) >= 0; i++) {
-    if (i == CAP_NET_RAW) {
-      // Don't break /system/bin/ping
-      continue;
-    }
     int rc = prctl(PR_CAPBSET_DROP, i, 0, 0, 0);
     if (rc == -1) {
       if (errno == EINVAL) {
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index 3e3f608..8bf36e7 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -43,7 +43,8 @@
 }
 
 // "name" is in "binary name" format, e.g. "dalvik.system.Debug$1".
-static jclass Class_classForName(JNIEnv* env, jclass, jstring javaName, jboolean initialize, jobject javaLoader) {
+static jclass Class_classForName(JNIEnv* env, jclass, jstring javaName, jboolean initialize,
+                                 jobject javaLoader) {
   ScopedObjectAccess soa(env);
   ScopedUtfChars name(env, javaName);
   if (name.c_str() == nullptr) {
@@ -64,7 +65,8 @@
   SirtRef<mirror::ClassLoader> class_loader(soa.Self(),
                                             soa.Decode<mirror::ClassLoader*>(javaLoader));
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  SirtRef<mirror::Class> c(soa.Self(), class_linker->FindClass(descriptor.c_str(), class_loader));
+  SirtRef<mirror::Class> c(soa.Self(), class_linker->FindClass(soa.Self(), descriptor.c_str(),
+                                                               class_loader));
   if (c.get() == nullptr) {
     ScopedLocalRef<jthrowable> cause(env, env->ExceptionOccurred());
     env->ExceptionClear();
diff --git a/runtime/native/java_lang_String.cc b/runtime/native/java_lang_String.cc
index c401d50..f95664b 100644
--- a/runtime/native/java_lang_String.cc
+++ b/runtime/native/java_lang_String.cc
@@ -20,6 +20,7 @@
 #include "scoped_fast_native_object_access.h"
 #include "scoped_thread_state_change.h"
 #include "ScopedLocalRef.h"
+#include "verify_object-inl.h"
 
 namespace art {
 
diff --git a/runtime/native/java_lang_Thread.cc b/runtime/native/java_lang_Thread.cc
index 011e165..2665a08 100644
--- a/runtime/native/java_lang_Thread.cc
+++ b/runtime/native/java_lang_Thread.cc
@@ -24,6 +24,7 @@
 #include "ScopedUtfChars.h"
 #include "thread.h"
 #include "thread_list.h"
+#include "verify_object-inl.h"
 
 namespace art {
 
diff --git a/runtime/native/java_lang_reflect_Array.cc b/runtime/native/java_lang_reflect_Array.cc
index 2197597..fc30aa6 100644
--- a/runtime/native/java_lang_reflect_Array.cc
+++ b/runtime/native/java_lang_reflect_Array.cc
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "class_linker.h"
+#include "class_linker-inl.h"
 #include "common_throws.h"
 #include "dex_file-inl.h"
 #include "jni_internal.h"
@@ -50,12 +50,8 @@
     ThrowNegativeArraySizeException(length);
     return NULL;
   }
-  std::string descriptor("[");
-  descriptor += ClassHelper(element_class).GetDescriptor();
-
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  SirtRef<mirror::ClassLoader> class_loader(soa.Self(), element_class->GetClassLoader());
-  mirror::Class* array_class = class_linker->FindClass(descriptor.c_str(), class_loader);
+  mirror::Class* array_class = class_linker->FindArrayClass(soa.Self(), element_class);
   if (UNLIKELY(array_class == NULL)) {
     CHECK(soa.Self()->IsExceptionPending());
     return NULL;
diff --git a/runtime/native/java_lang_reflect_Proxy.cc b/runtime/native/java_lang_reflect_Proxy.cc
index 809369a..1266c41 100644
--- a/runtime/native/java_lang_reflect_Proxy.cc
+++ b/runtime/native/java_lang_reflect_Proxy.cc
@@ -20,6 +20,7 @@
 #include "mirror/object_array.h"
 #include "mirror/string.h"
 #include "scoped_thread_state_change.h"
+#include "verify_object-inl.h"
 
 namespace art {
 
diff --git a/runtime/native/scoped_fast_native_object_access.h b/runtime/native/scoped_fast_native_object_access.h
index 1658d96..b5ee748 100644
--- a/runtime/native/scoped_fast_native_object_access.h
+++ b/runtime/native/scoped_fast_native_object_access.h
@@ -21,6 +21,7 @@
 #include "jni_internal.h"
 #include "thread-inl.h"
 #include "mirror/art_method.h"
+#include "verify_object.h"
 
 namespace art {
 
@@ -79,9 +80,7 @@
       return NULL;
     }
 
-    if (kIsDebugBuild) {
-      Runtime::Current()->GetHeap()->VerifyObject(obj);
-    }
+    VerifyObject(obj);
 
     DCHECK_NE((reinterpret_cast<uintptr_t>(obj) & 0xffff0000), 0xebad0000);
 
diff --git a/runtime/oat.cc b/runtime/oat.cc
index 81d4540..945cd77 100644
--- a/runtime/oat.cc
+++ b/runtime/oat.cc
@@ -22,7 +22,7 @@
 namespace art {
 
 const uint8_t OatHeader::kOatMagic[] = { 'o', 'a', 't', '\n' };
-const uint8_t OatHeader::kOatVersion[] = { '0', '1', '4', '\0' };
+const uint8_t OatHeader::kOatVersion[] = { '0', '1', '5', '\0' };
 
 OatHeader::OatHeader() {
   memset(this, 0, sizeof(*this));
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 00a8506..61f023c 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -84,6 +84,7 @@
   // This won't work for portable runtime execution because it doesn't process relocations.
   UniquePtr<File> file(OS::OpenFileForReading(filename.c_str()));
   if (file.get() == NULL) {
+    *error_msg = StringPrintf("Failed to open oat filename for reading: %s", strerror(errno));
     return NULL;
   }
   return OpenElfFile(file.get(), location, requested_base, false, executable, error_msg);
diff --git a/runtime/object_callbacks.h b/runtime/object_callbacks.h
index 6af338b..468ba08 100644
--- a/runtime/object_callbacks.h
+++ b/runtime/object_callbacks.h
@@ -60,6 +60,7 @@
 // address the object (if the object didn't move, returns the object input parameter).
 typedef mirror::Object* (IsMarkedCallback)(mirror::Object* object, void* arg)
     __attribute__((warn_unused_result));
+typedef void (ProcessMarkStackCallback)(void* arg);
 
 }  // namespace art
 
diff --git a/runtime/object_utils.h b/runtime/object_utils.h
index a981fab..4eac291 100644
--- a/runtime/object_utils.h
+++ b/runtime/object_utils.h
@@ -17,7 +17,7 @@
 #ifndef ART_RUNTIME_OBJECT_UTILS_H_
 #define ART_RUNTIME_OBJECT_UTILS_H_
 
-#include "class_linker-inl.h"
+#include "class_linker.h"
 #include "dex_file.h"
 #include "monitor.h"
 #include "mirror/art_field.h"
@@ -158,10 +158,10 @@
     DCHECK(!klass_->IsPrimitive());
     if (klass_->IsArrayClass()) {
       if (idx == 0) {
-        return GetClassLinker()->FindSystemClass("Ljava/lang/Cloneable;");
+        return GetClassLinker()->FindSystemClass(Thread::Current(), "Ljava/lang/Cloneable;");
       } else {
         DCHECK_EQ(1U, idx);
-        return GetClassLinker()->FindSystemClass("Ljava/io/Serializable;");
+        return GetClassLinker()->FindSystemClass(Thread::Current(), "Ljava/io/Serializable;");
       }
     } else if (klass_->IsProxyClass()) {
       return klass_->GetIfTable()->GetInterface(idx);
@@ -251,7 +251,7 @@
   mirror::Class* GetType(bool resolve = true) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     uint32_t field_index = field_->GetDexFieldIndex();
     if (UNLIKELY(field_->GetDeclaringClass()->IsProxyClass())) {
-      return GetClassLinker()->FindSystemClass(GetTypeDescriptor());
+      return GetClassLinker()->FindSystemClass(Thread::Current(), GetTypeDescriptor());
     }
     const DexFile& dex_file = GetDexFile();
     const DexFile::FieldId& field_id = dex_file.GetFieldId(field_index);
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index e66e5af..1ef15f7 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -77,13 +77,6 @@
       is_zygote_(false),
       is_concurrent_gc_enabled_(true),
       is_explicit_gc_disabled_(false),
-      compiler_filter_(kSpeed),
-      huge_method_threshold_(0),
-      large_method_threshold_(0),
-      small_method_threshold_(0),
-      tiny_method_threshold_(0),
-      num_dex_methods_threshold_(0),
-      sea_ir_mode_(false),
       default_stack_size_(0),
       heap_(nullptr),
       max_spins_before_thin_lock_inflation_(Monitor::kDefaultMaxSpinsBeforeThinLockInflation),
@@ -452,14 +445,6 @@
   parsed->hook_exit_ = exit;
   parsed->hook_abort_ = NULL;  // We don't call abort(3) by default; see Runtime::Abort.
 
-  parsed->compiler_filter_ = Runtime::kDefaultCompilerFilter;
-  parsed->huge_method_threshold_ = Runtime::kDefaultHugeMethodThreshold;
-  parsed->large_method_threshold_ = Runtime::kDefaultLargeMethodThreshold;
-  parsed->small_method_threshold_ = Runtime::kDefaultSmallMethodThreshold;
-  parsed->tiny_method_threshold_ = Runtime::kDefaultTinyMethodThreshold;
-  parsed->num_dex_methods_threshold_ = Runtime::kDefaultNumDexMethodsThreshold;
-
-  parsed->sea_ir_mode_ = false;
 //  gLogVerbosity.class_linker = true;  // TODO: don't check this in!
 //  gLogVerbosity.compiler = true;  // TODO: don't check this in!
 //  gLogVerbosity.verifier = true;  // TODO: don't check this in!
@@ -721,28 +706,22 @@
     } else if (StartsWith(option, "-Xprofile-backoff:")) {
       parsed->profile_backoff_coefficient_ = ParseDoubleOrDie(
           option, ':', 1.0, 10.0, ignore_unrecognized, parsed->profile_backoff_coefficient_);
-    } else if (option == "-compiler-filter:interpret-only") {
-      parsed->compiler_filter_ = kInterpretOnly;
-    } else if (option == "-compiler-filter:space") {
-      parsed->compiler_filter_ = kSpace;
-    } else if (option == "-compiler-filter:balanced") {
-      parsed->compiler_filter_ = kBalanced;
-    } else if (option == "-compiler-filter:speed") {
-      parsed->compiler_filter_ = kSpeed;
-    } else if (option == "-compiler-filter:everything") {
-      parsed->compiler_filter_ = kEverything;
-    } else if (option == "-sea_ir") {
-      parsed->sea_ir_mode_ = true;
-    } else if (StartsWith(option, "-huge-method-max:")) {
-      parsed->huge_method_threshold_ = ParseIntegerOrDie(option, ':');
-    } else if (StartsWith(option, "-large-method-max:")) {
-      parsed->large_method_threshold_ = ParseIntegerOrDie(option, ':');
-    } else if (StartsWith(option, "-small-method-max:")) {
-      parsed->small_method_threshold_ = ParseIntegerOrDie(option, ':');
-    } else if (StartsWith(option, "-tiny-method-max:")) {
-      parsed->tiny_method_threshold_ = ParseIntegerOrDie(option, ':');
-    } else if (StartsWith(option, "-num-dex-methods-max:")) {
-      parsed->num_dex_methods_threshold_ = ParseIntegerOrDie(option, ':');
+    } else if (option == "-Xcompiler-option") {
+      i++;
+      if (i == options.size()) {
+        // TODO: usage
+        LOG(FATAL) << "Missing required compiler option for " << option;
+        return NULL;
+      }
+      parsed->compiler_options_.push_back(options[i].first);
+    } else if (option == "-Ximage-compiler-option") {
+      i++;
+      if (i == options.size()) {
+        // TODO: usage
+        LOG(FATAL) << "Missing required compiler option for " << option;
+        return NULL;
+      }
+      parsed->image_compiler_options_.push_back(options[i].first);
     } else {
       if (!ignore_unrecognized) {
         // TODO: print usage via vfprintf
@@ -988,14 +967,6 @@
   is_zygote_ = options->is_zygote_;
   is_explicit_gc_disabled_ = options->is_explicit_gc_disabled_;
 
-  compiler_filter_ = options->compiler_filter_;
-  huge_method_threshold_ = options->huge_method_threshold_;
-  large_method_threshold_ = options->large_method_threshold_;
-  small_method_threshold_ = options->small_method_threshold_;
-  tiny_method_threshold_ = options->tiny_method_threshold_;
-  num_dex_methods_threshold_ = options->num_dex_methods_threshold_;
-
-  sea_ir_mode_ = options->sea_ir_mode_;
   vfprintf_ = options->hook_vfprintf_;
   exit_ = options->hook_exit_;
   abort_ = options->hook_abort_;
@@ -1486,12 +1457,18 @@
         (1 << art::x86_64::RSI) | (1 << art::x86_64::RDX) | (1 << art::x86_64::RCX) |
         (1 << art::x86_64::R8) | (1 << art::x86_64::R9);
     uint32_t core_spills = ref_spills | (type == kRefsAndArgs ? arg_spills : 0) |
-                         (1 << art::x86::kNumberOfCpuRegisters);  // fake return address callee save
+                         (1 << art::x86_64::kNumberOfCpuRegisters);  // fake return address callee save
+    uint32_t fp_arg_spills =
+        (1 << art::x86_64::XMM0) | (1 << art::x86_64::XMM1) | (1 << art::x86_64::XMM2) |
+        (1 << art::x86_64::XMM3) | (1 << art::x86_64::XMM4) | (1 << art::x86_64::XMM5) |
+        (1 << art::x86_64::XMM6) | (1 << art::x86_64::XMM7);
+    uint32_t fp_spills = (type == kRefsAndArgs ? fp_arg_spills : 0);
     size_t frame_size = RoundUp((__builtin_popcount(core_spills) /* gprs */ +
+                                 __builtin_popcount(fp_spills) /* fprs */ +
                                  1 /* Method* */) * kPointerSize, kStackAlignment);
     method->SetFrameSizeInBytes(frame_size);
     method->SetCoreSpillMask(core_spills);
-    method->SetFpSpillMask(0);
+    method->SetFpSpillMask(fp_spills);
   } else {
     UNIMPLEMENTED(FATAL) << instruction_set;
   }
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 159de2e..8924921 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -72,26 +72,6 @@
  public:
   typedef std::vector<std::pair<std::string, const void*> > Options;
 
-  enum CompilerFilter {
-    kInterpretOnly,       // Compile nothing.
-    kSpace,               // Maximize space savings.
-    kBalanced,            // Try to get the best performance return on compilation investment.
-    kSpeed,               // Maximize runtime performance.
-    kEverything           // Force compilation (Note: excludes compilaton of class initializers).
-  };
-
-  // Guide heuristics to determine whether to compile method if profile data not available.
-#if ART_SMALL_MODE
-  static const CompilerFilter kDefaultCompilerFilter = kInterpretOnly;
-#else
-  static const CompilerFilter kDefaultCompilerFilter = kSpeed;
-#endif
-  static const size_t kDefaultHugeMethodThreshold = 10000;
-  static const size_t kDefaultLargeMethodThreshold = 600;
-  static const size_t kDefaultSmallMethodThreshold = 60;
-  static const size_t kDefaultTinyMethodThreshold = 20;
-  static const size_t kDefaultNumDexMethodsThreshold = 900;
-
   class ParsedOptions {
    public:
     // returns null if problem parsing and ignore_unrecognized is false
@@ -140,13 +120,8 @@
     void (*hook_exit_)(jint status);
     void (*hook_abort_)();
     std::vector<std::string> properties_;
-    CompilerFilter compiler_filter_;
-    size_t huge_method_threshold_;
-    size_t large_method_threshold_;
-    size_t small_method_threshold_;
-    size_t tiny_method_threshold_;
-    size_t num_dex_methods_threshold_;
-    bool sea_ir_mode_;
+    std::vector<std::string> compiler_options_;
+    std::vector<std::string> image_compiler_options_;
     bool profile_;
     std::string profile_output_filename_;
     int profile_period_s_;
@@ -178,42 +153,12 @@
     return is_explicit_gc_disabled_;
   }
 
-#ifdef ART_SEA_IR_MODE
-  bool IsSeaIRMode() const {
-    return sea_ir_mode_;
-  }
-#endif
-
-  void SetSeaIRMode(bool sea_ir_mode) {
-    sea_ir_mode_ = sea_ir_mode;
+  const std::vector<std::string>& GetCompilerOptions() const {
+    return compiler_options_;
   }
 
-  CompilerFilter GetCompilerFilter() const {
-    return compiler_filter_;
-  }
-
-  void SetCompilerFilter(CompilerFilter compiler_filter) {
-    compiler_filter_ = compiler_filter;
-  }
-
-  size_t GetHugeMethodThreshold() const {
-    return huge_method_threshold_;
-  }
-
-  size_t GetLargeMethodThreshold() const {
-    return large_method_threshold_;
-  }
-
-  size_t GetSmallMethodThreshold() const {
-    return small_method_threshold_;
-  }
-
-  size_t GetTinyMethodThreshold() const {
-    return tiny_method_threshold_;
-  }
-
-  size_t GetNumDexMethodsThreshold() const {
-      return num_dex_methods_threshold_;
+  const std::vector<std::string>& GetImageCompilerOptions() const {
+    return image_compiler_options_;
   }
 
   const std::string& GetHostPrefix() const {
@@ -429,6 +374,10 @@
     return callee_save_methods_[type];
   }
 
+  static size_t GetCalleeSaveMethodOffset(CalleeSaveType type) {
+    return OFFSETOF_MEMBER(Runtime, callee_save_methods_[type]);
+  }
+
   void SetCalleeSaveMethod(mirror::ArtMethod* method, CalleeSaveType type);
 
   mirror::ArtMethod* CreateCalleeSaveMethod(InstructionSet instruction_set,
@@ -521,14 +470,8 @@
   bool is_concurrent_gc_enabled_;
   bool is_explicit_gc_disabled_;
 
-  CompilerFilter compiler_filter_;
-  size_t huge_method_threshold_;
-  size_t large_method_threshold_;
-  size_t small_method_threshold_;
-  size_t tiny_method_threshold_;
-  size_t num_dex_methods_threshold_;
-
-  bool sea_ir_mode_;
+  std::vector<std::string> compiler_options_;
+  std::vector<std::string> image_compiler_options_;
 
   // The host prefix is used during cross compilation. It is removed
   // from the start of host paths such as:
diff --git a/runtime/scoped_thread_state_change.h b/runtime/scoped_thread_state_change.h
index 1ca6c4e..f0f5ed2 100644
--- a/runtime/scoped_thread_state_change.h
+++ b/runtime/scoped_thread_state_change.h
@@ -19,6 +19,7 @@
 
 #include "base/casts.h"
 #include "thread-inl.h"
+#include "verify_object.h"
 
 namespace art {
 
@@ -121,6 +122,7 @@
       : ScopedThreadStateChange(ThreadForEnv(env), kRunnable),
         env_(down_cast<JNIEnvExt*>(env)), vm_(env_->vm) {
     self_->VerifyStack();
+    Locks::mutator_lock_->AssertSharedHeld(self_);
   }
 
   explicit ScopedObjectAccessUnchecked(Thread* self)
@@ -129,6 +131,7 @@
         env_(down_cast<JNIEnvExt*>(self->GetJniEnv())),
         vm_(env_ != NULL ? env_->vm : NULL) {
     self_->VerifyStack();
+    Locks::mutator_lock_->AssertSharedHeld(self_);
   }
 
   // Used when we want a scoped JNI thread state but have no thread/JNIEnv. Consequently doesn't
@@ -138,6 +141,7 @@
 
   // Here purely to force inlining.
   ~ScopedObjectAccessUnchecked() ALWAYS_INLINE {
+    Locks::mutator_lock_->AssertSharedHeld(self_);
   }
 
   JNIEnvExt* Env() const {
@@ -165,9 +169,7 @@
       return NULL;
     }
 
-    if (kIsDebugBuild) {
-      Runtime::Current()->GetHeap()->VerifyObject(obj);
-    }
+    VerifyObject(obj);
 
     DCHECK_NE((reinterpret_cast<uintptr_t>(obj) & 0xffff0000), 0xebad0000);
 
@@ -251,14 +253,12 @@
       LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_)
       SHARED_LOCK_FUNCTION(Locks::mutator_lock_) ALWAYS_INLINE
       : ScopedObjectAccessUnchecked(env) {
-    Locks::mutator_lock_->AssertSharedHeld(Self());
   }
 
   explicit ScopedObjectAccess(Thread* self)
       LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_)
       SHARED_LOCK_FUNCTION(Locks::mutator_lock_)
       : ScopedObjectAccessUnchecked(self) {
-    Locks::mutator_lock_->AssertSharedHeld(Self());
   }
 
   ~ScopedObjectAccess() UNLOCK_FUNCTION(Locks::mutator_lock_) ALWAYS_INLINE {
diff --git a/runtime/sirt_ref.h b/runtime/sirt_ref.h
index 3c5e4f8..b22e816 100644
--- a/runtime/sirt_ref.h
+++ b/runtime/sirt_ref.h
@@ -45,8 +45,11 @@
     return down_cast<T*>(sirt_.GetReference(0));
   }
 
-  void reset(T* object = nullptr) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  // Returns the old reference.
+  T* reset(T* object = nullptr) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    T* old_ref = get();
     sirt_.SetReference(0, object);
+    return old_ref;
   }
 
  private:
diff --git a/runtime/stack.cc b/runtime/stack.cc
index 864b86a..a6a0b29 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -25,14 +25,11 @@
 #include "runtime.h"
 #include "thread_list.h"
 #include "throw_location.h"
+#include "verify_object-inl.h"
 #include "vmap_table.h"
 
 namespace art {
 
-bool ShadowFrame::VerifyReference(const mirror::Object* val) const {
-  return !Runtime::Current()->GetHeap()->IsInTempSpace(val);
-}
-
 mirror::Object* ShadowFrame::GetThisObject() const {
   mirror::ArtMethod* m = GetMethod();
   if (m->IsStatic()) {
diff --git a/runtime/stack.h b/runtime/stack.h
index 7e9889e..6a62922 100644
--- a/runtime/stack.h
+++ b/runtime/stack.h
@@ -22,7 +22,9 @@
 #include "base/casts.h"
 #include "base/macros.h"
 #include "arch/context.h"
+#include "mirror/object.h"
 #include "mirror/object_reference.h"
+#include "verify_object.h"
 
 #include <stdint.h>
 #include <string>
@@ -213,26 +215,20 @@
     return *reinterpret_cast<unaligned_double*>(vreg);
   }
 
-  template <bool kChecked = false>
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   mirror::Object* GetVRegReference(size_t i) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     DCHECK_LT(i, NumberOfVRegs());
+    mirror::Object* ref;
     if (HasReferenceArray()) {
-      mirror::Object* ref = References()[i].AsMirrorPtr();
-      if (kChecked) {
-        CHECK(VerifyReference(ref)) << "VReg " << i << "(" << ref
-                                    << ") is in protected space, reference array " << true;
-      }
-      return ref;
+      ref = References()[i].AsMirrorPtr();
     } else {
       const uint32_t* vreg_ptr = &vregs_[i];
-      mirror::Object* ref =
-          reinterpret_cast<const StackReference<mirror::Object>*>(vreg_ptr)->AsMirrorPtr();
-      if (kChecked) {
-        CHECK(VerifyReference(ref)) << "VReg " << i
-            << "(" << ref << ") is in protected space, reference array " << false;
-      }
-      return ref;
+      ref = reinterpret_cast<const StackReference<mirror::Object>*>(vreg_ptr)->AsMirrorPtr();
     }
+    if (kVerifyFlags & kVerifyReads) {
+      VerifyObject(ref);
+    }
+    return ref;
   }
 
   // Get view of vregs as range of consecutive arguments starting at i.
@@ -290,10 +286,12 @@
     }
   }
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   void SetVRegReference(size_t i, mirror::Object* val) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     DCHECK_LT(i, NumberOfVRegs());
-    DCHECK(!kMovingCollector || VerifyReference(val))
-        << "VReg " << i << "(" << val << ") is in protected space";
+    if (kVerifyFlags & kVerifyWrites) {
+      VerifyObject(val);
+    }
     uint32_t* vreg = &vregs_[i];
     reinterpret_cast<StackReference<mirror::Object>*>(vreg)->Assign(val);
     if (HasReferenceArray()) {
@@ -374,8 +372,6 @@
     return reinterpret_cast<const StackReference<mirror::Object>*>(vreg_end);
   }
 
-  bool VerifyReference(const mirror::Object* val) const;
-
   StackReference<mirror::Object>* References() {
     return const_cast<StackReference<mirror::Object>*>(const_cast<const ShadowFrame*>(this)->References());
   }
diff --git a/runtime/thread-inl.h b/runtime/thread-inl.h
index c0bf377..f7e88cc 100644
--- a/runtime/thread-inl.h
+++ b/runtime/thread-inl.h
@@ -152,9 +152,10 @@
 }
 
 inline void Thread::VerifyStack() {
-  gc::Heap* heap = Runtime::Current()->GetHeap();
-  if (heap->IsObjectValidationEnabled()) {
-    VerifyStackImpl();
+  if (kVerifyStack) {
+    if (Runtime::Current()->GetHeap()->IsObjectValidationEnabled()) {
+      VerifyStackImpl();
+    }
   }
 }
 
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 16655fb..4fe9169 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -66,6 +66,7 @@
 #include "thread_list.h"
 #include "utils.h"
 #include "verifier/dex_gc_map.h"
+#include "verify_object-inl.h"
 #include "vmap_table.h"
 #include "well_known_classes.h"
 
@@ -1004,6 +1005,15 @@
   }
 }
 
+void Thread::AssertNoPendingExceptionForNewException(const char* msg) const {
+  if (UNLIKELY(IsExceptionPending())) {
+    ScopedObjectAccess soa(Thread::Current());
+    mirror::Throwable* exception = GetException(nullptr);
+    LOG(FATAL) << "Throwing new exception " << msg << " with unexpected pending exception: "
+        << exception->Dump();
+  }
+}
+
 static void MonitorExitVisitor(mirror::Object** object, void* arg, uint32_t /*thread_id*/,
                                RootType /*root_type*/)
     NO_THREAD_SAFETY_ANALYSIS {
@@ -1217,8 +1227,8 @@
   if (UNLIKELY(result == nullptr)) {
     JniAbortF(nullptr, "use of deleted %s %p", ToStr<IndirectRefKind>(kind).c_str(), obj);
   } else {
-    if (kIsDebugBuild && (result != kInvalidIndirectRefObject)) {
-      Runtime::Current()->GetHeap()->VerifyObject(result);
+    if (result != kInvalidIndirectRefObject) {
+      VerifyObject(result);
     }
   }
   return result;
@@ -1506,7 +1516,8 @@
 
 void Thread::ThrowNewException(const ThrowLocation& throw_location, const char* exception_class_descriptor,
                                const char* msg) {
-  AssertNoPendingException();  // Callers should either clear or call ThrowNewWrappedException.
+  // Callers should either clear or call ThrowNewWrappedException.
+  AssertNoPendingExceptionForNewException(msg);
   ThrowNewWrappedException(throw_location, exception_class_descriptor, msg);
 }
 
@@ -1528,7 +1539,7 @@
   }
   SirtRef<mirror::ClassLoader> class_loader(this, cl);
   SirtRef<mirror::Class>
-      exception_class(this, runtime->GetClassLinker()->FindClass(exception_class_descriptor,
+      exception_class(this, runtime->GetClassLinker()->FindClass(this, exception_class_descriptor,
                                                                  class_loader));
   if (UNLIKELY(exception_class.get() == nullptr)) {
     CHECK(IsExceptionPending());
@@ -2000,9 +2011,7 @@
 };
 
 void Thread::SetClassLoaderOverride(mirror::ClassLoader* class_loader_override) {
-  if (kIsDebugBuild) {
-    Runtime::Current()->GetHeap()->VerifyObject(class_loader_override);
-  }
+  VerifyObject(class_loader_override);
   class_loader_override_ = class_loader_override;
 }
 
@@ -2037,11 +2046,9 @@
   }
 }
 
-static void VerifyRoot(mirror::Object** root, void* arg, uint32_t /*thread_id*/,
-                       RootType /*root_type*/) {
-  DCHECK(root != nullptr);
-  DCHECK(arg != nullptr);
-  reinterpret_cast<gc::Heap*>(arg)->VerifyObject(*root);
+static void VerifyRoot(mirror::Object** root, void* /*arg*/, uint32_t /*thread_id*/,
+                       RootType /*root_type*/) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  VerifyObject(*root);
 }
 
 void Thread::VerifyStackImpl() {
diff --git a/runtime/thread.h b/runtime/thread.h
index 48912d1..f9d31af 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -285,6 +285,7 @@
   }
 
   void AssertNoPendingException() const;
+  void AssertNoPendingExceptionForNewException(const char* msg) const;
 
   void SetException(const ThrowLocation& throw_location, mirror::Throwable* new_exception)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -375,7 +376,8 @@
     return class_loader_override_;
   }
 
-  void SetClassLoaderOverride(mirror::ClassLoader* class_loader_override);
+  void SetClassLoaderOverride(mirror::ClassLoader* class_loader_override)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Create the internal representation of a stack trace, that is more time
   // and space efficient to compute than the StackTraceElement[]
@@ -391,7 +393,7 @@
 
   void VisitRoots(RootCallback* visitor, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  void VerifyStack() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  ALWAYS_INLINE void VerifyStack() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   //
   // Offsets of various members of native Thread class, used by compiled code.
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index 74e6f1c..d311945 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -269,6 +269,7 @@
 
 void ThreadList::SuspendAll() {
   Thread* self = Thread::Current();
+  DCHECK(self != nullptr);
 
   VLOG(threads) << *self << " SuspendAll starting...";
 
diff --git a/runtime/transaction.cc b/runtime/transaction.cc
index 019a322..fcda6c9 100644
--- a/runtime/transaction.cc
+++ b/runtime/transaction.cc
@@ -157,7 +157,6 @@
 }
 
 void Transaction::VisitRoots(RootCallback* callback, void* arg) {
-  LOG(INFO) << "Transaction::VisitRoots";
   MutexLock mu(Thread::Current(), log_lock_);
   VisitObjectLogs(callback, arg);
   VisitArrayLogs(callback, arg);
@@ -310,8 +309,10 @@
     if (field_value.kind == ObjectLog::kReference) {
       mirror::Object* obj =
           reinterpret_cast<mirror::Object*>(static_cast<uintptr_t>(field_value.value));
-      callback(&obj, arg, 0, kRootUnknown);
-      field_value.value = reinterpret_cast<uintptr_t>(obj);
+      if (obj != nullptr) {
+        callback(&obj, arg, 0, kRootUnknown);
+        field_value.value = reinterpret_cast<uintptr_t>(obj);
+      }
     }
   }
 }
diff --git a/runtime/transaction_test.cc b/runtime/transaction_test.cc
index dcfa24b..9dc7b44 100644
--- a/runtime/transaction_test.cc
+++ b/runtime/transaction_test.cc
@@ -29,7 +29,7 @@
 TEST_F(TransactionTest, Object_class) {
   ScopedObjectAccess soa(Thread::Current());
   SirtRef<mirror::Class> sirt_klass(soa.Self(),
-                                    class_linker_->FindSystemClass("Ljava/lang/Object;"));
+                                    class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;"));
   ASSERT_TRUE(sirt_klass.get() != nullptr);
 
   Transaction transaction;
@@ -47,7 +47,8 @@
 TEST_F(TransactionTest, Object_monitor) {
   ScopedObjectAccess soa(Thread::Current());
   SirtRef<mirror::Class> sirt_klass(soa.Self(),
-                                    class_linker_->FindSystemClass("Ljava/lang/Object;"));
+                                    class_linker_->FindSystemClass(soa.Self(),
+                                                                   "Ljava/lang/Object;"));
   ASSERT_TRUE(sirt_klass.get() != nullptr);
   SirtRef<mirror::Object> sirt_obj(soa.Self(), sirt_klass->AllocObject(soa.Self()));
   ASSERT_TRUE(sirt_obj.get() != nullptr);
@@ -74,7 +75,8 @@
 TEST_F(TransactionTest, Array_length) {
   ScopedObjectAccess soa(Thread::Current());
   SirtRef<mirror::Class> sirt_klass(soa.Self(),
-                                    class_linker_->FindSystemClass("[Ljava/lang/Object;"));
+                                    class_linker_->FindSystemClass(soa.Self(),
+                                                                   "[Ljava/lang/Object;"));
   ASSERT_TRUE(sirt_klass.get() != nullptr);
 
   constexpr int32_t kArraySize = 2;
@@ -101,7 +103,8 @@
   ASSERT_TRUE(class_loader.get() != nullptr);
 
   SirtRef<mirror::Class> sirt_klass(soa.Self(),
-                                    class_linker_->FindClass("LStaticFieldsTest;", class_loader));
+                                    class_linker_->FindClass(soa.Self(), "LStaticFieldsTest;",
+                                                             class_loader));
   ASSERT_TRUE(sirt_klass.get() != nullptr);
   class_linker_->EnsureInitialized(sirt_klass, true, true);
   ASSERT_TRUE(sirt_klass->IsInitialized());
@@ -147,13 +150,16 @@
   ASSERT_EQ(FieldHelper(doubleField).GetTypeAsPrimitiveType(), Primitive::kPrimDouble);
   ASSERT_EQ(doubleField->GetDouble(sirt_klass.get()), static_cast<double>(0.0));
 
-  mirror::ArtField* objectField = sirt_klass->FindDeclaredStaticField("objectField", "Ljava/lang/Object;");
+  mirror::ArtField* objectField = sirt_klass->FindDeclaredStaticField("objectField",
+                                                                      "Ljava/lang/Object;");
   ASSERT_TRUE(objectField != nullptr);
   ASSERT_EQ(FieldHelper(objectField).GetTypeAsPrimitiveType(), Primitive::kPrimNot);
   ASSERT_EQ(objectField->GetObject(sirt_klass.get()), nullptr);
 
   // Create a java.lang.Object instance to set objectField.
-  SirtRef<mirror::Class> object_klass(soa.Self(), class_linker_->FindSystemClass("Ljava/lang/Object;"));
+  SirtRef<mirror::Class> object_klass(soa.Self(),
+                                      class_linker_->FindSystemClass(soa.Self(),
+                                                                     "Ljava/lang/Object;"));
   ASSERT_TRUE(object_klass.get() != nullptr);
   SirtRef<mirror::Object> sirt_obj(soa.Self(), sirt_klass->AllocObject(soa.Self()));
   ASSERT_TRUE(sirt_obj.get() != nullptr);
@@ -193,7 +199,8 @@
   ASSERT_TRUE(class_loader.get() != nullptr);
 
   SirtRef<mirror::Class> sirt_klass(soa.Self(),
-                                    class_linker_->FindClass("LInstanceFieldsTest;", class_loader));
+                                    class_linker_->FindClass(soa.Self(), "LInstanceFieldsTest;",
+                                                             class_loader));
   ASSERT_TRUE(sirt_klass.get() != nullptr);
   class_linker_->EnsureInitialized(sirt_klass, true, true);
   ASSERT_TRUE(sirt_klass->IsInitialized());
@@ -243,13 +250,16 @@
   ASSERT_EQ(FieldHelper(doubleField).GetTypeAsPrimitiveType(), Primitive::kPrimDouble);
   ASSERT_EQ(doubleField->GetDouble(sirt_instance.get()), static_cast<double>(0.0));
 
-  mirror::ArtField* objectField = sirt_klass->FindDeclaredInstanceField("objectField", "Ljava/lang/Object;");
+  mirror::ArtField* objectField = sirt_klass->FindDeclaredInstanceField("objectField",
+                                                                        "Ljava/lang/Object;");
   ASSERT_TRUE(objectField != nullptr);
   ASSERT_EQ(FieldHelper(objectField).GetTypeAsPrimitiveType(), Primitive::kPrimNot);
   ASSERT_EQ(objectField->GetObject(sirt_instance.get()), nullptr);
 
   // Create a java.lang.Object instance to set objectField.
-  SirtRef<mirror::Class> object_klass(soa.Self(), class_linker_->FindSystemClass("Ljava/lang/Object;"));
+  SirtRef<mirror::Class> object_klass(soa.Self(),
+                                      class_linker_->FindSystemClass(soa.Self(),
+                                                                     "Ljava/lang/Object;"));
   ASSERT_TRUE(object_klass.get() != nullptr);
   SirtRef<mirror::Object> sirt_obj(soa.Self(), sirt_klass->AllocObject(soa.Self()));
   ASSERT_TRUE(sirt_obj.get() != nullptr);
@@ -290,7 +300,8 @@
   ASSERT_TRUE(class_loader.get() != nullptr);
 
   SirtRef<mirror::Class> sirt_klass(soa.Self(),
-                                    class_linker_->FindClass("LStaticArrayFieldsTest;", class_loader));
+                                    class_linker_->FindClass(soa.Self(), "LStaticArrayFieldsTest;",
+                                                             class_loader));
   ASSERT_TRUE(sirt_klass.get() != nullptr);
   class_linker_->EnsureInitialized(sirt_klass, true, true);
   ASSERT_TRUE(sirt_klass->IsInitialized());
@@ -352,15 +363,19 @@
   ASSERT_EQ(doubleArray->GetLength(), 1);
   ASSERT_EQ(doubleArray->GetWithoutChecks(0), static_cast<double>(0.0f));
 
-  mirror::ArtField* objectArrayField = sirt_klass->FindDeclaredStaticField("objectArrayField", "[Ljava/lang/Object;");
+  mirror::ArtField* objectArrayField = sirt_klass->FindDeclaredStaticField("objectArrayField",
+                                                                           "[Ljava/lang/Object;");
   ASSERT_TRUE(objectArrayField != nullptr);
-  mirror::ObjectArray<mirror::Object>* objectArray = objectArrayField->GetObject(sirt_klass.get())->AsObjectArray<mirror::Object>();
+  mirror::ObjectArray<mirror::Object>* objectArray =
+      objectArrayField->GetObject(sirt_klass.get())->AsObjectArray<mirror::Object>();
   ASSERT_TRUE(objectArray != nullptr);
   ASSERT_EQ(objectArray->GetLength(), 1);
   ASSERT_EQ(objectArray->GetWithoutChecks(0), nullptr);
 
   // Create a java.lang.Object instance to set objectField.
-  SirtRef<mirror::Class> object_klass(soa.Self(), class_linker_->FindSystemClass("Ljava/lang/Object;"));
+  SirtRef<mirror::Class> object_klass(soa.Self(),
+                                      class_linker_->FindSystemClass(soa.Self(),
+                                                                     "Ljava/lang/Object;"));
   ASSERT_TRUE(object_klass.get() != nullptr);
   SirtRef<mirror::Object> sirt_obj(soa.Self(), sirt_klass->AllocObject(soa.Self()));
   ASSERT_TRUE(sirt_obj.get() != nullptr);
@@ -400,7 +415,9 @@
   ASSERT_TRUE(class_loader.get() != nullptr);
 
   SirtRef<mirror::Class> sirt_klass(soa.Self(),
-                                    class_linker_->FindClass("LTransaction$EmptyStatic;", class_loader));
+                                    class_linker_->FindClass(soa.Self(),
+                                                             "LTransaction$EmptyStatic;",
+                                                             class_loader));
   ASSERT_TRUE(sirt_klass.get() != nullptr);
   class_linker_->VerifyClass(sirt_klass);
   ASSERT_TRUE(sirt_klass->IsVerified());
@@ -419,7 +436,9 @@
   ASSERT_TRUE(class_loader.get() != nullptr);
 
   SirtRef<mirror::Class> sirt_klass(soa.Self(),
-                                    class_linker_->FindClass("LTransaction$StaticFieldClass;", class_loader));
+                                    class_linker_->FindClass(soa.Self(),
+                                                             "LTransaction$StaticFieldClass;",
+                                                             class_loader));
   ASSERT_TRUE(sirt_klass.get() != nullptr);
   class_linker_->VerifyClass(sirt_klass);
   ASSERT_TRUE(sirt_klass->IsVerified());
@@ -441,22 +460,25 @@
   // Load and verify java.lang.ExceptionInInitializerError and java.lang.InternalError which will
   // be thrown by class initialization due to native call.
   SirtRef<mirror::Class> sirt_klass(soa.Self(),
-                                    class_linker_->FindSystemClass("Ljava/lang/ExceptionInInitializerError;"));
+                                    class_linker_->FindSystemClass(soa.Self(),
+                                                                   "Ljava/lang/ExceptionInInitializerError;"));
   ASSERT_TRUE(sirt_klass.get() != nullptr);
   class_linker_->VerifyClass(sirt_klass);
   ASSERT_TRUE(sirt_klass->IsVerified());
-  sirt_klass.reset(class_linker_->FindSystemClass("Ljava/lang/InternalError;"));
+  sirt_klass.reset(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/InternalError;"));
   ASSERT_TRUE(sirt_klass.get() != nullptr);
   class_linker_->VerifyClass(sirt_klass);
   ASSERT_TRUE(sirt_klass->IsVerified());
 
   // Load and verify Transaction$NativeSupport used in class initialization.
-  sirt_klass.reset(class_linker_->FindClass("LTransaction$NativeSupport;", class_loader));
+  sirt_klass.reset(class_linker_->FindClass(soa.Self(), "LTransaction$NativeSupport;",
+                                            class_loader));
   ASSERT_TRUE(sirt_klass.get() != nullptr);
   class_linker_->VerifyClass(sirt_klass);
   ASSERT_TRUE(sirt_klass->IsVerified());
 
-  sirt_klass.reset(class_linker_->FindClass("LTransaction$BlacklistedClass;", class_loader));
+  sirt_klass.reset(class_linker_->FindClass(soa.Self(), "LTransaction$BlacklistedClass;",
+                                            class_loader));
   ASSERT_TRUE(sirt_klass.get() != nullptr);
   class_linker_->VerifyClass(sirt_klass);
   ASSERT_TRUE(sirt_klass->IsVerified());
diff --git a/runtime/utils.cc b/runtime/utils.cc
index aad21bc..68d8417 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -21,9 +21,12 @@
 #include <sys/stat.h>
 #include <sys/syscall.h>
 #include <sys/types.h>
+#include <sys/wait.h>
+
 #include <unistd.h>
 
 #include "UniquePtr.h"
+#include "base/stl_util.h"
 #include "base/unix_file/fd_file.h"
 #include "dex_file-inl.h"
 #include "mirror/art_field-inl.h"
@@ -1203,4 +1206,57 @@
                  sizeof(OatHeader::kOatMagic)) == 0);
 }
 
+bool Exec(std::vector<std::string>& arg_vector, std::string* error_msg) {
+  const std::string command_line(Join(arg_vector, ' '));
+
+  CHECK_GE(arg_vector.size(), 1U) << command_line;
+
+  // Convert the args to char pointers.
+  const char* program = arg_vector[0].c_str();
+  std::vector<char*> args;
+  for (size_t i = 0; i < arg_vector.size(); ++i) {
+    const std::string& arg = arg_vector[i];
+    char* arg_str = const_cast<char*>(arg.c_str());
+    CHECK(arg_str != nullptr) << i;
+    args.push_back(arg_str);
+  }
+  args.push_back(NULL);
+
+  // fork and exec
+  pid_t pid = fork();
+  if (pid == 0) {
+    // no allocation allowed between fork and exec
+
+    // change process groups, so we don't get reaped by ProcessManager
+    setpgid(0, 0);
+
+    execv(program, &args[0]);
+
+    *error_msg = StringPrintf("Failed to execv(%s): %s", command_line.c_str(), strerror(errno));
+    return false;
+  } else {
+    if (pid == -1) {
+      *error_msg = StringPrintf("Failed to execv(%s) because fork failed: %s",
+                                command_line.c_str(), strerror(errno));
+      return false;
+    }
+
+    // wait for subprocess to finish
+    int status;
+    pid_t got_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
+    if (got_pid != pid) {
+      *error_msg = StringPrintf("Failed after fork for execv(%s) because waitpid failed: "
+                                "wanted %d, got %d: %s",
+                                command_line.c_str(), pid, got_pid, strerror(errno));
+      return false;
+    }
+    if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+      *error_msg = StringPrintf("Failed execv(%s) because non-0 exit status",
+                                command_line.c_str());
+      return false;
+    }
+  }
+  return true;
+}
+
 }  // namespace art
diff --git a/runtime/utils.h b/runtime/utils.h
index e2d8966..0bb06de 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -396,6 +396,9 @@
 bool IsDexMagic(uint32_t magic);
 bool IsOatMagic(uint32_t magic);
 
+// Wrapper on fork/execv to run a command in a subprocess.
+bool Exec(std::vector<std::string>& arg_vector, std::string* error_msg);
+
 class VoidFunctor {
  public:
   template <typename A>
diff --git a/runtime/utils_test.cc b/runtime/utils_test.cc
index b43177b..0d237e2 100644
--- a/runtime/utils_test.cc
+++ b/runtime/utils_test.cc
@@ -99,7 +99,7 @@
   SirtRef<mirror::ShortArray> a(soa.Self(), mirror::ShortArray::Alloc(soa.Self(), 2));
   EXPECT_EQ("short[]", PrettyTypeOf(a.get()));
 
-  mirror::Class* c = class_linker_->FindSystemClass("[Ljava/lang/String;");
+  mirror::Class* c = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/String;");
   ASSERT_TRUE(c != NULL);
   mirror::Object* o = mirror::ObjectArray<mirror::String>::Alloc(soa.Self(), c, 0);
   EXPECT_EQ("java.lang.String[]", PrettyTypeOf(o));
@@ -109,7 +109,7 @@
 TEST_F(UtilsTest, PrettyClass) {
   ScopedObjectAccess soa(Thread::Current());
   EXPECT_EQ("null", PrettyClass(NULL));
-  mirror::Class* c = class_linker_->FindSystemClass("[Ljava/lang/String;");
+  mirror::Class* c = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/String;");
   ASSERT_TRUE(c != NULL);
   mirror::Object* o = mirror::ObjectArray<mirror::String>::Alloc(soa.Self(), c, 0);
   EXPECT_EQ("java.lang.Class<java.lang.String[]>", PrettyClass(o->GetClass()));
@@ -118,7 +118,7 @@
 TEST_F(UtilsTest, PrettyClassAndClassLoader) {
   ScopedObjectAccess soa(Thread::Current());
   EXPECT_EQ("null", PrettyClassAndClassLoader(NULL));
-  mirror::Class* c = class_linker_->FindSystemClass("[Ljava/lang/String;");
+  mirror::Class* c = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/String;");
   ASSERT_TRUE(c != NULL);
   mirror::Object* o = mirror::ObjectArray<mirror::String>::Alloc(soa.Self(), c, 0);
   EXPECT_EQ("java.lang.Class<java.lang.String[],null>", PrettyClassAndClassLoader(o->GetClass()));
@@ -128,7 +128,8 @@
   ScopedObjectAccess soa(Thread::Current());
   EXPECT_EQ("null", PrettyField(NULL));
 
-  mirror::Class* java_lang_String = class_linker_->FindSystemClass("Ljava/lang/String;");
+  mirror::Class* java_lang_String = class_linker_->FindSystemClass(soa.Self(),
+                                                                   "Ljava/lang/String;");
 
   mirror::ArtField* f;
   f = java_lang_String->FindDeclaredInstanceField("count", "I");
@@ -197,7 +198,7 @@
 
 TEST_F(UtilsTest, JniShortName_JniLongName) {
   ScopedObjectAccess soa(Thread::Current());
-  mirror::Class* c = class_linker_->FindSystemClass("Ljava/lang/String;");
+  mirror::Class* c = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/String;");
   ASSERT_TRUE(c != NULL);
   mirror::ArtMethod* m;
 
@@ -349,4 +350,26 @@
   CheckGetDalvikCacheFilenameOrDie("/system/framework/boot.art", "system@framework@boot.art");
 }
 
+TEST_F(UtilsTest, ExecSuccess) {
+  std::vector<std::string> command;
+  if (kIsTargetBuild) {
+    command.push_back("/system/bin/id");
+  } else {
+    command.push_back("/usr/bin/id");
+  }
+  std::string error_msg;
+  EXPECT_TRUE(Exec(command, &error_msg));
+  EXPECT_EQ(0U, error_msg.size()) << error_msg;
+}
+
+// TODO: Disabled due to hang tearing down CommonTest.
+// Renable after splitting into RuntimeTest and CompilerTest.
+TEST_F(UtilsTest, DISABLED_ExecError) {
+  std::vector<std::string> command;
+  command.push_back("bogus");
+  std::string error_msg;
+  EXPECT_FALSE(Exec(command, &error_msg));
+  EXPECT_NE(0U, error_msg.size());
+}
+
 }  // namespace art
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index ab943a6..ffa8b9e 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -3129,14 +3129,15 @@
     this_class = actual_arg_type.GetClass();
   } else {
     const std::string& descriptor(actual_arg_type.GetDescriptor());
+    Thread* self = Thread::Current();
     ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-    this_class = class_linker->FindClass(descriptor.c_str(), *class_loader_);
+    this_class = class_linker->FindClass(self, descriptor.c_str(), *class_loader_);
     if (this_class == NULL) {
       Thread* self = Thread::Current();
       self->ClearException();
       // Look for a system class
       SirtRef<mirror::ClassLoader> null_class_loader(self, nullptr);
-      this_class = class_linker->FindClass(descriptor.c_str(), null_class_loader);
+      this_class = class_linker->FindClass(self, descriptor.c_str(), null_class_loader);
     }
   }
   if (this_class == NULL) {
@@ -3638,7 +3639,7 @@
 // Returns the access field of a quick field access (iget/iput-quick) or NULL
 // if it cannot be found.
 mirror::ArtField* MethodVerifier::GetQuickFieldAccess(const Instruction* inst,
-                                                   RegisterLine* reg_line) {
+                                                      RegisterLine* reg_line) {
   DCHECK(inst->Opcode() == Instruction::IGET_QUICK ||
          inst->Opcode() == Instruction::IGET_WIDE_QUICK ||
          inst->Opcode() == Instruction::IGET_OBJECT_QUICK ||
@@ -3654,12 +3655,12 @@
     const std::string& descriptor(object_type.GetDescriptor());
     ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
     Thread* self = Thread::Current();
-    object_class = class_linker->FindClass(descriptor.c_str(), *class_loader_);
+    object_class = class_linker->FindClass(self, descriptor.c_str(), *class_loader_);
     if (object_class == NULL) {
       self->ClearException();
       // Look for a system class
       SirtRef<mirror::ClassLoader> null_class_loader(self, nullptr);
-      object_class = class_linker->FindClass(descriptor.c_str(), null_class_loader);
+      object_class = class_linker->FindClass(self, descriptor.c_str(), null_class_loader);
     }
   }
   if (object_class == NULL) {
diff --git a/runtime/verifier/method_verifier_test.cc b/runtime/verifier/method_verifier_test.cc
index a56abba..ffa2455 100644
--- a/runtime/verifier/method_verifier_test.cc
+++ b/runtime/verifier/method_verifier_test.cc
@@ -30,7 +30,7 @@
   void VerifyClass(const std::string& descriptor)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     ASSERT_TRUE(descriptor != NULL);
-    mirror::Class* klass = class_linker_->FindSystemClass(descriptor.c_str());
+    mirror::Class* klass = class_linker_->FindSystemClass(Thread::Current(), descriptor.c_str());
 
     // Verify the class
     std::string error_msg;
diff --git a/runtime/verifier/reg_type.cc b/runtime/verifier/reg_type.cc
index 630ef8a..63f0ff4 100644
--- a/runtime/verifier/reg_type.cc
+++ b/runtime/verifier/reg_type.cc
@@ -18,6 +18,7 @@
 
 
 #include "base/casts.h"
+#include "class_linker-inl.h"
 #include "dex_file-inl.h"
 #include "mirror/class.h"
 #include "mirror/class-inl.h"
@@ -928,11 +929,7 @@
     }
     mirror::Class* common_elem = ClassJoin(s_ct, t_ct);
     ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-    Thread* self = Thread::Current();
-    SirtRef<mirror::ClassLoader> class_loader(self, s->GetClassLoader());
-    std::string descriptor("[");
-    descriptor += ClassHelper(common_elem).GetDescriptor();
-    mirror::Class* array_class = class_linker->FindClass(descriptor.c_str(), class_loader);
+    mirror::Class* array_class = class_linker->FindArrayClass(Thread::Current(), common_elem);
     DCHECK(array_class != NULL);
     return array_class;
   } else {
diff --git a/runtime/verifier/reg_type_cache.cc b/runtime/verifier/reg_type_cache.cc
index 5e894ed..9dd57b8 100644
--- a/runtime/verifier/reg_type_cache.cc
+++ b/runtime/verifier/reg_type_cache.cc
@@ -17,6 +17,7 @@
 #include "reg_type_cache-inl.h"
 
 #include "base/casts.h"
+#include "class_linker-inl.h"
 #include "dex_file-inl.h"
 #include "mirror/class-inl.h"
 #include "mirror/object-inl.h"
@@ -140,10 +141,11 @@
   // Class was not found, must create new type.
   // Try resolving class
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  SirtRef<mirror::ClassLoader> class_loader(Thread::Current(), loader);
+  Thread* self = Thread::Current();
+  SirtRef<mirror::ClassLoader> class_loader(self, loader);
   mirror::Class* klass = NULL;
   if (can_load_classes_) {
-    klass = class_linker->FindClass(descriptor, class_loader);
+    klass = class_linker->FindClass(self, descriptor, class_loader);
   } else {
     klass = class_linker->LookupClass(descriptor, loader);
     if (klass != NULL && !klass->IsLoaded()) {
@@ -277,7 +279,8 @@
   mirror::Class* klass = NULL;
   // Try loading the class from linker.
   if (!descriptor.empty()) {
-    klass = art::Runtime::Current()->GetClassLinker()->FindSystemClass(descriptor.c_str());
+    klass = art::Runtime::Current()->GetClassLinker()->FindSystemClass(Thread::Current(),
+                                                                       descriptor.c_str());
   }
   Type* entry = Type::CreateInstance(klass, descriptor, RegTypeCache::primitive_count_);
   RegTypeCache::primitive_count_++;
diff --git a/runtime/verify_object-inl.h b/runtime/verify_object-inl.h
new file mode 100644
index 0000000..e211c83
--- /dev/null
+++ b/runtime/verify_object-inl.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2014 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_VERIFY_OBJECT_INL_H_
+#define ART_RUNTIME_VERIFY_OBJECT_INL_H_
+
+#include "verify_object.h"
+
+#include "gc/heap.h"
+#include "mirror/class-inl.h"
+#include "mirror/object-inl.h"
+
+namespace art {
+
+inline void VerifyObject(mirror::Object* obj) {
+  if (kVerifyObjectSupport > kVerifyObjectModeDisabled && obj != nullptr) {
+    if (kVerifyObjectSupport > kVerifyObjectModeFast) {
+      // Slow object verification, try the heap right away.
+      Runtime::Current()->GetHeap()->VerifyObjectBody(obj);
+    } else {
+      // Fast object verification, only call the heap if our quick sanity tests fail. The heap will
+      // print the diagnostic message.
+      bool failed = !IsAligned<kObjectAlignment>(obj);
+      if (!failed) {
+        mirror::Class* c = obj->GetClass<kVerifyNone>();
+        failed = failed || c == nullptr;
+        failed = failed ||!IsAligned<kObjectAlignment>(c);
+        failed = failed ||!VerifyClassClass(c);
+      }
+      if (UNLIKELY(failed)) {
+        Runtime::Current()->GetHeap()->VerifyObjectBody(obj);
+      }
+    }
+  }
+}
+
+inline bool VerifyClassClass(mirror::Class* c) {
+  if (UNLIKELY(c == nullptr)) {
+    return false;
+  }
+  // Note: We pass in flags to ensure that the accessors don't call VerifyObject.
+  mirror::Class* c_c = c->GetClass<kVerifyNone>();
+  return c_c != nullptr && c_c == c_c->GetClass<kVerifyNone>();
+}
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_VERIFY_OBJECT_INL_H_
diff --git a/runtime/verify_object.h b/runtime/verify_object.h
new file mode 100644
index 0000000..b39df4a
--- /dev/null
+++ b/runtime/verify_object.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2014 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_VERIFY_OBJECT_H_
+#define ART_RUNTIME_VERIFY_OBJECT_H_
+
+#include "locks.h"
+
+#include <stdint.h>
+
+namespace art {
+
+namespace mirror {
+  class Class;
+  class Object;
+}  // namespace mirror
+
+// How we want to sanity check the heap's correctness.
+enum VerifyObjectMode {
+  kVerifyObjectModeDisabled,  // Heap verification is disabled.
+  kVerifyObjectModeFast,  // Sanity heap accesses quickly by using VerifyClassClass.
+  kVerifyObjectModeAll  // Sanity heap accesses thoroughly.
+};
+
+enum VerifyObjectFlags {
+  kVerifyNone = 0x0,
+  // Verify self when we are doing an operation.
+  kVerifyThis = 0x1,
+  // Verify reads from objects.
+  kVerifyReads = 0x2,
+  // Verify writes to objects.
+  kVerifyWrites = 0x4,
+  // Verify all things.
+  kVerifyAll = kVerifyThis | kVerifyReads | kVerifyWrites,
+};
+
+static constexpr bool kVerifyStack = kIsDebugBuild;
+static constexpr VerifyObjectFlags kDefaultVerifyFlags = kVerifyNone;
+static constexpr VerifyObjectMode kVerifyObjectSupport =
+    kDefaultVerifyFlags != 0 ? kVerifyObjectModeFast : kVerifyObjectModeDisabled;
+
+ALWAYS_INLINE inline void VerifyObject(mirror::Object* obj) NO_THREAD_SAFETY_ANALYSIS;
+
+// Check that c.getClass() == c.getClass().getClass().
+ALWAYS_INLINE inline bool VerifyClassClass(mirror::Class* c) NO_THREAD_SAFETY_ANALYSIS;
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_VERIFY_OBJECT_H_
diff --git a/runtime/vmap_table.h b/runtime/vmap_table.h
index abc50b9..2fbaebe 100644
--- a/runtime/vmap_table.h
+++ b/runtime/vmap_table.h
@@ -25,6 +25,10 @@
 
 class VmapTable {
  public:
+  // For efficient encoding of special values, entries are adjusted by 2.
+  static constexpr uint16_t kEntryAdjustment = 2u;
+  static constexpr uint16_t kAdjustedFpMarker = static_cast<uint16_t>(0xffffu + kEntryAdjustment);
+
   explicit VmapTable(const uint8_t* table) : table_(table) {
   }
 
@@ -33,11 +37,11 @@
     const uint8_t* table = table_;
     size_t size = DecodeUnsignedLeb128(&table);
     CHECK_LT(n, size);
-    uint16_t entry = DecodeUnsignedLeb128(&table);
+    uint16_t adjusted_entry = DecodeUnsignedLeb128(&table);
     for (size_t i = 0; i < n; ++i) {
-      entry = DecodeUnsignedLeb128(&table);
+      adjusted_entry = DecodeUnsignedLeb128(&table);
     }
-    return entry;
+    return adjusted_entry - kEntryAdjustment;
   }
 
   size_t Size() const {
@@ -58,16 +62,17 @@
     bool is_float = (kind == kFloatVReg) || (kind == kDoubleLoVReg) || (kind == kDoubleHiVReg);
     bool in_floats = false;
     const uint8_t* table = table_;
+    uint16_t adjusted_vreg = vreg + kEntryAdjustment;
     size_t end = DecodeUnsignedLeb128(&table);
     for (size_t i = 0; i < end; ++i) {
       // Stop if we find what we are are looking for.
-      uint16_t entry = DecodeUnsignedLeb128(&table);
-      if ((entry == vreg) && (in_floats == is_float)) {
+      uint16_t adjusted_entry = DecodeUnsignedLeb128(&table);
+      if ((adjusted_entry == adjusted_vreg) && (in_floats == is_float)) {
         *vmap_offset = i;
         return true;
       }
       // 0xffff is the marker for LR (return PC on x86), following it are spilled float registers.
-      if (entry == 0xffff) {
+      if (adjusted_entry == kAdjustedFpMarker) {
         in_floats = true;
       }
     }
@@ -89,7 +94,7 @@
     if (UNLIKELY(is_float)) {
       const uint8_t* table = table_;
       DecodeUnsignedLeb128(&table);  // Skip size.
-      while (DecodeUnsignedLeb128(&table) != 0xffff) {
+      while (DecodeUnsignedLeb128(&table) != kAdjustedFpMarker) {
         matches++;
       }
       matches++;
diff --git a/test/030-bad-finalizer/expected.txt b/test/030-bad-finalizer/expected.txt
index 88b1896..ee9cfff 100644
--- a/test/030-bad-finalizer/expected.txt
+++ b/test/030-bad-finalizer/expected.txt
@@ -1,7 +1,4 @@
-Constructed object.
-Nulled. Requestion gc.
+About to null reference and request GC.
 Finalizer started and spinning...
 Finalizer done spinning.
 Finalizer sleeping forever now.
-Requesting another GC.
-Requesting another GC.
diff --git a/test/030-bad-finalizer/src/BadFinalizer.java b/test/030-bad-finalizer/src/BadFinalizer.java
deleted file mode 100644
index 6911a02..0000000
--- a/test/030-bad-finalizer/src/BadFinalizer.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * Class with a bad finalizer.
- */
-public class BadFinalizer {
-    public static void snooze(int ms) {
-        try {
-            Thread.sleep(ms);
-        } catch (InterruptedException ie) {
-            System.out.println("Snooze: " + ie.getMessage());
-        }
-    }
-
-    protected void finalize() {
-        System.out.println("Finalizer started and spinning...");
-        int j = 0;
-
-        /* spin for a bit */
-        long start, end;
-        start = System.nanoTime();
-        for (int i = 0; i < 1000000; i++)
-            j++;
-        end = System.nanoTime();
-        System.out.println("Finalizer done spinning.");
-
-        System.out.println("Finalizer sleeping forever now.");
-        while (true) {
-            snooze(10000);
-        }
-    }
-}
diff --git a/test/030-bad-finalizer/src/Main.java b/test/030-bad-finalizer/src/Main.java
index 330e344..942ee25 100644
--- a/test/030-bad-finalizer/src/Main.java
+++ b/test/030-bad-finalizer/src/Main.java
@@ -21,19 +21,47 @@
     public static void main(String[] args) {
         BadFinalizer bf = new BadFinalizer();
 
-        System.out.println("Constructed object.");
+        System.out.println("About to null reference and request GC.");
         bf = null;
-
-        System.out.println("Nulled. Requestion gc.");
         Runtime.getRuntime().gc();
 
         for (int i = 0; i < 8; i++) {
-            BadFinalizer.snooze(4000);
-            System.out.println("Requesting another GC.");
+            snooze(4000);
             Runtime.getRuntime().gc();
         }
 
-        System.out.println("Done waiting.");
+        System.out.println("UNREACHABLE");
         System.exit(0);
     }
+
+    public static void snooze(int ms) {
+        try {
+            Thread.sleep(ms);
+        } catch (InterruptedException ie) {
+        }
+    }
+
+    /**
+     * Class with a bad finalizer.
+     */
+    public static class BadFinalizer {
+        protected void finalize() {
+            System.out.println("Finalizer started and spinning...");
+            int j = 0;
+
+            /* spin for a bit */
+            long start, end;
+            start = System.nanoTime();
+            for (int i = 0; i < 1000000; i++) {
+                j++;
+            }
+            end = System.nanoTime();
+            System.out.println("Finalizer done spinning.");
+
+            System.out.println("Finalizer sleeping forever now.");
+            while (true) {
+                snooze(10000);
+            }
+        }
+    }
 }
diff --git a/test/036-finalizer/expected.txt b/test/036-finalizer/expected.txt
index f9b29b0..a2a74fc 100644
--- a/test/036-finalizer/expected.txt
+++ b/test/036-finalizer/expected.txt
@@ -1,14 +1,13 @@
-wimp: wahoo
+wimp: [FinalizerTest message=wahoo, finalized=false]
 gc
-finalizer executed: wahoo
 wimp: null
 finalize
 wimp: null
 sleep
-reborn: wahoo
+reborn: [FinalizerTest message=wahoo, finalized=true]
 wimp: null
 reset reborn
 gc + finalize
 sleep
-reborn: nothing
+reborn: [FinalizerTest message=nothing, finalized=false]
 wimp: null
diff --git a/test/036-finalizer/src/FinalizerTest.java b/test/036-finalizer/src/FinalizerTest.java
deleted file mode 100644
index b0d014d..0000000
--- a/test/036-finalizer/src/FinalizerTest.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2008 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.ref.WeakReference;
-
-public class FinalizerTest {
-    public static FinalizerTest mNothing = new FinalizerTest("nothing");
-    public static FinalizerTest mReborn = mNothing;
-
-    public String mMsg = "default";
-
-    public FinalizerTest(String msg) {
-        mMsg = msg;
-    }
-
-    public String toString() {
-        return mMsg;
-    }
-
-    protected void finalize() {
-        System.out.println("finalizer executed: " + mMsg);
-        mReborn = this;
-    }
-}
diff --git a/test/036-finalizer/src/Main.java b/test/036-finalizer/src/Main.java
index 6195aff..328425f 100644
--- a/test/036-finalizer/src/Main.java
+++ b/test/036-finalizer/src/Main.java
@@ -15,6 +15,8 @@
  */
 
 import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Some finalizer tests.
@@ -31,18 +33,19 @@
         }
     }
 
-    public static WeakReference makeRef() {
+    public static WeakReference<FinalizerTest> makeRef() {
         /*
          * Make ft in another thread, so there is no danger of
          * a conservative reference leaking onto the main thread's
          * stack.
          */
 
-        final WeakReference[] wimp = new WeakReference[1];
+        final List<WeakReference<FinalizerTest>> wimp =
+                new ArrayList<WeakReference<FinalizerTest>>();
         Thread t = new Thread() {
                 public void run() {
                     FinalizerTest ft = new FinalizerTest("wahoo");
-                    wimp[0] = new WeakReference(ft);
+                    wimp.add(new WeakReference<FinalizerTest>(ft));
                     ft = null;
                 }
             };
@@ -55,10 +58,10 @@
             throw new RuntimeException(ie);
         }
 
-        return wimp[0];
+        return wimp.get(0);
     }
 
-    public static String wimpString(final WeakReference wimp) {
+    public static String wimpString(final WeakReference<FinalizerTest> wimp) {
         /*
          * Do the work in another thread, so there is no danger of a
          * conservative reference to ft leaking onto the main thread's
@@ -68,7 +71,7 @@
         final String[] s = new String[1];
         Thread t = new Thread() {
                 public void run() {
-                    Object ref = wimp.get();
+                    FinalizerTest ref = wimp.get();
                     if (ref != null) {
                         s[0] = ref.toString();
                     }
@@ -87,7 +90,7 @@
     }
 
     public static void main(String[] args) {
-        WeakReference wimp = makeRef();
+        WeakReference<FinalizerTest> wimp = makeRef();
 
         System.out.println("wimp: " + wimpString(wimp));
 
@@ -118,4 +121,26 @@
         System.out.println("reborn: " + FinalizerTest.mReborn);
         System.out.println("wimp: " + wimpString(wimp));
     }
+
+    public static class FinalizerTest {
+        public static FinalizerTest mNothing = new FinalizerTest("nothing");
+        public static FinalizerTest mReborn = mNothing;
+
+        private final String message;
+        private boolean finalized = false;
+
+        public FinalizerTest(String message) {
+            this.message = message;
+        }
+
+        public String toString() {
+            return "[FinalizerTest message=" + message +
+                    ", finalized=" + finalized + "]";
+        }
+
+        protected void finalize() {
+            finalized = true;
+            mReborn = this;
+        }
+    }
 }
diff --git a/test/048-server-socket/expected.txt b/test/048-server-socket/expected.txt
deleted file mode 100644
index 23c3e84..0000000
--- a/test/048-server-socket/expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-opened!
-closed!
-reopened!
-done
diff --git a/test/048-server-socket/info.txt b/test/048-server-socket/info.txt
deleted file mode 100644
index 08127da..0000000
--- a/test/048-server-socket/info.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-This is a miscellaneous test that was imported into the new-at-the-time
-runtime test framework. The test is intended to exercise basic features,
-and as such cannot be build on top of junit, since failure of such basic
-features might disrupt junit.
-
-TODO: Real description goes here.
diff --git a/test/048-server-socket/src/Main.java b/test/048-server-socket/src/Main.java
deleted file mode 100644
index 5b287ca..0000000
--- a/test/048-server-socket/src/Main.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2007 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.net.ServerSocket;
-import java.io.IOException;
-
-
-/**
- * Quick server socket test.
- */
-public class Main {
-    private static void snooze(int sec) {
-        try {
-            Thread.sleep(sec * 1000);
-        } catch (InterruptedException ie) {
-            ie.printStackTrace();
-        }
-    }
-
-    public static void main(String[] args) {
-        ServerSocket socket;
-
-        try {
-            socket = new ServerSocket(7890);
-        } catch (IOException ioe) {
-            System.out.println("couldn't open socket " + ioe.getMessage());
-            return;
-        }
-
-        System.out.println("opened!");
-        snooze(1);
-
-        try {
-            socket.close();
-        } catch (IOException ioe) {
-            System.out.println("couldn't close socket " + ioe.getMessage());
-            return;
-        }
-
-        System.out.println("closed!");
-        snooze(1);
-
-        try {
-            socket = new ServerSocket(7890);
-        } catch (IOException ioe) {
-            System.out.println("couldn't reopen socket " + ioe.getMessage());
-            return;
-        }
-
-        System.out.println("reopened!");
-        System.out.println("done");
-    }
-}
diff --git a/test/083-compiler-regressions/expected.txt b/test/083-compiler-regressions/expected.txt
index e2846dd..57065ef 100644
--- a/test/083-compiler-regressions/expected.txt
+++ b/test/083-compiler-regressions/expected.txt
@@ -9,5 +9,7 @@
 wideGetterSetterTest passes
 wideIdentityTest passes
 returnConstantTest passes
+LVNTests.testNPE1 passes
+LVNTests.testNPE2 passes
 longDivTest passes
 longModTest passes
diff --git a/test/083-compiler-regressions/src/Main.java b/test/083-compiler-regressions/src/Main.java
index 3307e50..6829388 100644
--- a/test/083-compiler-regressions/src/Main.java
+++ b/test/083-compiler-regressions/src/Main.java
@@ -40,6 +40,8 @@
         wideGetterSetterTest();
         wideIdentityTest();
         returnConstantTest();
+        LVNTests.testNPE1();
+        LVNTests.testNPE2();
         ZeroTests.longDivTest();
         ZeroTests.longModTest();
     }
@@ -8440,6 +8442,52 @@
 
     public long wideIdent5(int a6, int a5, int a4, int a3, int a2, long a1) {
         return a1;
-  }
+    }
+}
 
+class LVNTests {
+    private LVNTests link = null;
+    private int value = 0;
+
+    private void setLink(LVNTests l) {
+        link = l;
+    }
+
+    private static void causeNPE1(LVNTests lhs, LVNTests rhs) {
+        LVNTests link1 = lhs.link;
+        rhs.link = null;
+        LVNTests link2 = lhs.link;
+        int value1 = link1.value;
+        int value2 = link2.value;
+        System.out.println("LVNTests.testNPE1 fails with " + value1 + " and " + value2);
+    }
+
+    public static void testNPE1() {
+        LVNTests t = new LVNTests();
+        t.link = new LVNTests();
+        try {
+          causeNPE1(t, t);
+        } catch (NullPointerException e) {
+          System.out.println("LVNTests.testNPE1 passes");
+        }
+    }
+
+    private static void causeNPE2(LVNTests lhs, LVNTests rhs) {
+      LVNTests link1 = lhs.link;
+      rhs.setLink(null);
+      LVNTests link2 = lhs.link;
+      int value1 = link1.value;
+      int value2 = link2.value;
+      System.out.println("LVNTests.testNPE2 fails with " + value1 + " and " + value2);
+    }
+
+    public static void testNPE2() {
+        LVNTests t = new LVNTests();
+        t.link = new LVNTests();
+        try {
+          causeNPE2(t, t);
+        } catch (NullPointerException e) {
+          System.out.println("LVNTests.testNPE2 passes");
+        }
+    }
 }