Merge "Revert "Switch to using ELF-64 for 64-bit architectures.""
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 730e61d..bfc8956 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -30,6 +30,7 @@
   Interfaces \
   Main \
   MultiDex \
+  MultiDexModifiedSecondary \
   MyClass \
   MyClassNatives \
   Nested \
@@ -68,7 +69,7 @@
 ART_GTEST_instrumentation_test_DEX_DEPS := Instrumentation
 ART_GTEST_jni_compiler_test_DEX_DEPS := MyClassNatives
 ART_GTEST_jni_internal_test_DEX_DEPS := AllFields StaticLeafMethods
-ART_GTEST_oat_file_assistant_test_DEX_DEPS := Main MainStripped MultiDex Nested
+ART_GTEST_oat_file_assistant_test_DEX_DEPS := Main MainStripped MultiDex MultiDexModifiedSecondary Nested
 ART_GTEST_oat_file_test_DEX_DEPS := Main MultiDex
 ART_GTEST_object_test_DEX_DEPS := ProtoCompare ProtoCompare2 StaticsFromCode XandY
 ART_GTEST_proxy_test_DEX_DEPS := Interfaces
@@ -162,6 +163,7 @@
   runtime/instrumentation_test.cc \
   runtime/intern_table_test.cc \
   runtime/interpreter/safe_math_test.cc \
+  runtime/interpreter/unstarted_runtime_test.cc \
   runtime/java_vm_ext_test.cc \
   runtime/jit/jit_code_cache_test.cc \
   runtime/leb128_test.cc \
@@ -416,7 +418,7 @@
   LOCAL_CPP_EXTENSION := $$(ART_CPP_EXTENSION)
   LOCAL_SRC_FILES := $$(art_gtest_filename)
   LOCAL_C_INCLUDES += $$(ART_C_INCLUDES) art/runtime $$(art_gtest_extra_c_includes)
-  LOCAL_SHARED_LIBRARIES += libartd $$(art_gtest_extra_shared_libraries) libart-gtest libart-disassembler
+  LOCAL_SHARED_LIBRARIES += libartd $$(art_gtest_extra_shared_libraries) libart-gtest libartd-disassembler
   LOCAL_WHOLE_STATIC_LIBRARIES += libsigchain
 
   LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 47288b5..d352f39 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -1241,7 +1241,7 @@
   mirror::Class* referrer_class;
   mirror::DexCache* dex_cache;
   {
-    StackHandleScope<3> hs(soa.Self());
+    StackHandleScope<2> hs(soa.Self());
     Handle<mirror::DexCache> dex_cache_handle(
         hs.NewHandle(mUnit->GetClassLinker()->FindDexCache(*mUnit->GetDexFile())));
     Handle<mirror::ClassLoader> class_loader_handle(
diff --git a/compiler/elf_writer_debug.cc b/compiler/elf_writer_debug.cc
index a1aabc3..f4df6c1 100644
--- a/compiler/elf_writer_debug.cc
+++ b/compiler/elf_writer_debug.cc
@@ -243,6 +243,7 @@
                         std::vector<uintptr_t>* debug_line_patches) {
   const std::vector<OatWriter::DebugInfo>& method_infos = oat_writer->GetMethodDebugInfo();
   const InstructionSet isa = compiler->GetInstructionSet();
+  const bool is64bit = Is64BitInstructionSet(isa);
 
   // Find all addresses (low_pc) which contain deduped methods.
   // The first instance of method is not marked deduped_, but the rest is.
@@ -280,7 +281,7 @@
     }
 
     size_t debug_abbrev_offset = debug_abbrev->size();
-    DebugInfoEntryWriter<> info(false /* 32 bit */, debug_abbrev);
+    DebugInfoEntryWriter<> info(is64bit, debug_abbrev);
     info.StartTag(DW_TAG_compile_unit, DW_CHILDREN_yes);
     info.WriteStrp(DW_AT_producer, "Android dex2oat", debug_str);
     info.WriteData1(DW_AT_language, DW_LANG_Java);
@@ -325,7 +326,7 @@
       case kX86_64:
         break;
     }
-    DebugLineOpCodeWriter<> opcodes(false /* 32bit */, code_factor_bits_);
+    DebugLineOpCodeWriter<> opcodes(is64bit, code_factor_bits_);
     opcodes.SetAddress(cunit_low_pc);
     if (dwarf_isa != -1) {
       opcodes.SetISA(dwarf_isa);
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 4805cee..d71266d 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -702,8 +702,10 @@
   if (environment->GetParent() != nullptr) {
     // We emit the parent environment first.
     EmitEnvironment(environment->GetParent(), slow_path);
-    stack_map_stream_.BeginInlineInfoEntry(
-        environment->GetMethodIdx(), environment->GetDexPc(), environment->Size());
+    stack_map_stream_.BeginInlineInfoEntry(environment->GetMethodIdx(),
+                                           environment->GetDexPc(),
+                                           environment->GetInvokeType(),
+                                           environment->Size());
   }
 
   // Walk over the environment, and record the location of dex registers.
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 7b37f74..09ed9c7 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -391,7 +391,7 @@
       location_builder_(graph, this),
       instruction_visitor_(graph, this),
       move_resolver_(graph->GetArena(), this),
-      assembler_(true),
+      assembler_(false /* can_relocate_branches */),
       isa_features_(isa_features) {
   // Save the PC register to mimic Quick.
   AddAllocatedRegister(Location::RegisterLocation(PC));
@@ -2883,7 +2883,7 @@
   Location left = locations->InAt(0);
   Location right = locations->InAt(1);
 
-  Label less, greater, done;
+  NearLabel less, greater, done;
   Primitive::Type type = compare->InputAt(0)->GetType();
   switch (type) {
     case Primitive::kPrimLong: {
@@ -2979,7 +2979,7 @@
                                                           Register temp1,
                                                           Register temp2,
                                                           HInstruction* instruction) {
-  Label fail;
+  NearLabel fail;
   if (offset != 0) {
     __ LoadImmediate(temp1, offset);
     __ add(IP, addr, ShifterOperand(temp1));
@@ -3659,7 +3659,7 @@
                                   Register object,
                                   Register value,
                                   bool can_be_null) {
-  Label is_null;
+  NearLabel is_null;
   if (can_be_null) {
     __ CompareAndBranchIfZero(value, &is_null);
   }
@@ -4081,14 +4081,13 @@
   Register cls = locations->InAt(1).AsRegister<Register>();
   Register out = locations->Out().AsRegister<Register>();
   uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
-  Label done, zero;
+  NearLabel done, zero;
   SlowPathCodeARM* slow_path = nullptr;
 
   // Return 0 if `obj` is null.
   // avoid null check if we know obj is not null.
   if (instruction->MustDoNullCheck()) {
-    __ cmp(obj, ShifterOperand(0));
-    __ b(&zero, EQ);
+    __ CompareAndBranchIfZero(obj, &zero);
   }
   // Compare the class of `obj` with `cls`.
   __ LoadFromOffset(kLoadWord, out, obj, class_offset);
@@ -4139,16 +4138,19 @@
       instruction, locations->InAt(1), locations->GetTemp(0), instruction->GetDexPc());
   codegen_->AddSlowPath(slow_path);
 
+  NearLabel done;
   // avoid null check if we know obj is not null.
   if (instruction->MustDoNullCheck()) {
-    __ cmp(obj, ShifterOperand(0));
-    __ b(slow_path->GetExitLabel(), EQ);
+    __ CompareAndBranchIfZero(obj, &done);
   }
   // Compare the class of `obj` with `cls`.
   __ LoadFromOffset(kLoadWord, temp, obj, class_offset);
   __ cmp(temp, ShifterOperand(cls));
   __ b(slow_path->GetEntryLabel(), NE);
   __ Bind(slow_path->GetExitLabel());
+  if (instruction->MustDoNullCheck()) {
+    __ Bind(&done);
+  }
 }
 
 void LocationsBuilderARM::VisitMonitorOperation(HMonitorOperation* instruction) {
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index be28755..7da4f2d 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -265,6 +265,21 @@
     StartAttributeStream("kind") << barrier->GetBarrierKind();
   }
 
+  void VisitLoadClass(HLoadClass* load_cass) OVERRIDE {
+    StartAttributeStream("gen_clinit_check") << std::boolalpha
+        << load_cass->MustGenerateClinitCheck() << std::noboolalpha;
+  }
+
+  void VisitCheckCast(HCheckCast* check_cast) OVERRIDE {
+    StartAttributeStream("must_do_null_check") << std::boolalpha
+        << check_cast->MustDoNullCheck() << std::noboolalpha;
+  }
+
+  void VisitInstanceOf(HInstanceOf* instance_of) OVERRIDE {
+    StartAttributeStream("must_do_null_check") << std::boolalpha
+        << instance_of->MustDoNullCheck() << std::noboolalpha;
+  }
+
   bool IsPass(const char* name) {
     return strcmp(pass_name_, name) == 0;
   }
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 56d868f..47c6318 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -193,6 +193,7 @@
       caller_dex_file,
       method_index,
       requires_ctor_barrier,
+      invoke_instruction->GetOriginalInvokeType(),
       graph_->IsDebuggable(),
       graph_->GetCurrentInstructionId());
 
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index 0adb931..fcb3471 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -65,6 +65,7 @@
   void VisitUShr(HUShr* instruction) OVERRIDE;
   void VisitXor(HXor* instruction) OVERRIDE;
   void VisitInstanceOf(HInstanceOf* instruction) OVERRIDE;
+  bool IsDominatedByInputNullCheck(HInstruction* instr);
 
   OptimizingCompilerStats* stats_;
   bool simplification_occurred_ = false;
@@ -174,9 +175,20 @@
   }
 }
 
+bool InstructionSimplifierVisitor::IsDominatedByInputNullCheck(HInstruction* instr) {
+  HInstruction* input = instr->InputAt(0);
+  for (HUseIterator<HInstruction*> it(input->GetUses()); !it.Done(); it.Advance()) {
+    HInstruction* use = it.Current()->GetUser();
+    if (use->IsNullCheck() && use->StrictlyDominates(instr)) {
+      return true;
+    }
+  }
+  return false;
+}
+
 void InstructionSimplifierVisitor::VisitCheckCast(HCheckCast* check_cast) {
   HLoadClass* load_class = check_cast->InputAt(1)->AsLoadClass();
-  if (!check_cast->InputAt(0)->CanBeNull()) {
+  if (!check_cast->InputAt(0)->CanBeNull() || IsDominatedByInputNullCheck(check_cast)) {
     check_cast->ClearMustDoNullCheck();
   }
 
@@ -198,7 +210,7 @@
 }
 
 void InstructionSimplifierVisitor::VisitInstanceOf(HInstanceOf* instruction) {
-  if (!instruction->InputAt(0)->CanBeNull()) {
+  if (!instruction->InputAt(0)->CanBeNull() || IsDominatedByInputNullCheck(instruction)) {
     instruction->ClearMustDoNullCheck();
   }
 }
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 2850368..12ace41 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -60,6 +60,8 @@
 static constexpr uint32_t kMaxIntShiftValue = 0x1f;
 static constexpr uint64_t kMaxLongShiftValue = 0x3f;
 
+static constexpr InvokeType kInvalidInvokeType = static_cast<InvokeType>(-1);
+
 enum IfCondition {
   kCondEQ,
   kCondNE,
@@ -121,6 +123,7 @@
          const DexFile& dex_file,
          uint32_t method_idx,
          bool should_generate_constructor_barrier,
+         InvokeType invoke_type = kInvalidInvokeType,
          bool debuggable = false,
          int start_instruction_id = 0)
       : arena_(arena),
@@ -138,6 +141,7 @@
         current_instruction_id_(start_instruction_id),
         dex_file_(dex_file),
         method_idx_(method_idx),
+        invoke_type_(invoke_type),
         should_generate_constructor_barrier_(should_generate_constructor_barrier),
         cached_null_constant_(nullptr),
         cached_int_constants_(std::less<int32_t>(), arena->Adapter()),
@@ -283,6 +287,10 @@
     return method_idx_;
   }
 
+  InvokeType GetInvokeType() const {
+    return invoke_type_;
+  }
+
  private:
   void VisitBlockForDominatorTree(HBasicBlock* block,
                                   HBasicBlock* predecessor,
@@ -365,6 +373,9 @@
   // The method index in the dex file.
   const uint32_t method_idx_;
 
+  // If inlined, this encodes how the callee is being invoked.
+  const InvokeType invoke_type_;
+
   const bool should_generate_constructor_barrier_;
 
   // Cached constants.
@@ -1101,13 +1112,15 @@
                size_t number_of_vregs,
                const DexFile& dex_file,
                uint32_t method_idx,
-               uint32_t dex_pc)
+               uint32_t dex_pc,
+               InvokeType invoke_type)
      : vregs_(arena, number_of_vregs),
        locations_(arena, number_of_vregs),
        parent_(nullptr),
        dex_file_(dex_file),
        method_idx_(method_idx),
-       dex_pc_(dex_pc) {
+       dex_pc_(dex_pc),
+       invoke_type_(invoke_type) {
     vregs_.SetSize(number_of_vregs);
     for (size_t i = 0; i < number_of_vregs; i++) {
       vregs_.Put(i, HUserRecord<HEnvironment*>());
@@ -1119,16 +1132,20 @@
     }
   }
 
+  HEnvironment(ArenaAllocator* arena, const HEnvironment& to_copy)
+      : HEnvironment(arena,
+                     to_copy.Size(),
+                     to_copy.GetDexFile(),
+                     to_copy.GetMethodIdx(),
+                     to_copy.GetDexPc(),
+                     to_copy.GetInvokeType()) {}
+
   void SetAndCopyParentChain(ArenaAllocator* allocator, HEnvironment* parent) {
-    parent_ = new (allocator) HEnvironment(allocator,
-                                           parent->Size(),
-                                           parent->GetDexFile(),
-                                           parent->GetMethodIdx(),
-                                           parent->GetDexPc());
+    parent_ = new (allocator) HEnvironment(allocator, *parent);
+    parent_->CopyFrom(parent);
     if (parent->GetParent() != nullptr) {
       parent_->SetAndCopyParentChain(allocator, parent->GetParent());
     }
-    parent_->CopyFrom(parent);
   }
 
   void CopyFrom(const GrowableArray<HInstruction*>& locals);
@@ -1169,6 +1186,10 @@
     return method_idx_;
   }
 
+  InvokeType GetInvokeType() const {
+    return invoke_type_;
+  }
+
   const DexFile& GetDexFile() const {
     return dex_file_;
   }
@@ -1188,6 +1209,7 @@
   const DexFile& dex_file_;
   const uint32_t method_idx_;
   const uint32_t dex_pc_;
+  const InvokeType invoke_type_;
 
   friend class HInstruction;
 
@@ -1401,12 +1423,7 @@
   // copying, the uses lists are being updated.
   void CopyEnvironmentFrom(HEnvironment* environment) {
     ArenaAllocator* allocator = GetBlock()->GetGraph()->GetArena();
-    environment_ = new (allocator) HEnvironment(
-        allocator,
-        environment->Size(),
-        environment->GetDexFile(),
-        environment->GetMethodIdx(),
-        environment->GetDexPc());
+    environment_ = new (allocator) HEnvironment(allocator, *environment);
     environment_->CopyFrom(environment);
     if (environment->GetParent() != nullptr) {
       environment_->SetAndCopyParentChain(allocator, environment->GetParent());
@@ -1416,16 +1433,11 @@
   void CopyEnvironmentFromWithLoopPhiAdjustment(HEnvironment* environment,
                                                 HBasicBlock* block) {
     ArenaAllocator* allocator = GetBlock()->GetGraph()->GetArena();
-    environment_ = new (allocator) HEnvironment(
-        allocator,
-        environment->Size(),
-        environment->GetDexFile(),
-        environment->GetMethodIdx(),
-        environment->GetDexPc());
+    environment_ = new (allocator) HEnvironment(allocator, *environment);
+    environment_->CopyFromWithLoopPhiAdjustment(environment, block);
     if (environment->GetParent() != nullptr) {
       environment_->SetAndCopyParentChain(allocator, environment->GetParent());
     }
-    environment_->CopyFromWithLoopPhiAdjustment(environment, block);
   }
 
   // Returns the number of entries in the environment. Typically, that is the
@@ -2376,6 +2388,8 @@
 
   uint32_t GetDexMethodIndex() const { return dex_method_index_; }
 
+  InvokeType GetOriginalInvokeType() const { return original_invoke_type_; }
+
   Intrinsics GetIntrinsic() const {
     return intrinsic_;
   }
@@ -2392,13 +2406,15 @@
           uint32_t number_of_other_inputs,
           Primitive::Type return_type,
           uint32_t dex_pc,
-          uint32_t dex_method_index)
+          uint32_t dex_method_index,
+          InvokeType original_invoke_type)
     : HInstruction(SideEffects::All()),
       number_of_arguments_(number_of_arguments),
       inputs_(arena, number_of_arguments),
       return_type_(return_type),
       dex_pc_(dex_pc),
       dex_method_index_(dex_method_index),
+      original_invoke_type_(original_invoke_type),
       intrinsic_(Intrinsics::kNone) {
     uint32_t number_of_inputs = number_of_arguments + number_of_other_inputs;
     inputs_.SetSize(number_of_inputs);
@@ -2414,6 +2430,7 @@
   const Primitive::Type return_type_;
   const uint32_t dex_pc_;
   const uint32_t dex_method_index_;
+  const InvokeType original_invoke_type_;
   Intrinsics intrinsic_;
 
  private:
@@ -2445,8 +2462,8 @@
                 clinit_check_requirement == ClinitCheckRequirement::kExplicit ? 1u : 0u,
                 return_type,
                 dex_pc,
-                dex_method_index),
-        original_invoke_type_(original_invoke_type),
+                dex_method_index,
+                original_invoke_type),
         invoke_type_(invoke_type),
         is_recursive_(is_recursive),
         clinit_check_requirement_(clinit_check_requirement),
@@ -2459,7 +2476,6 @@
     return false;
   }
 
-  InvokeType GetOriginalInvokeType() const { return original_invoke_type_; }
   InvokeType GetInvokeType() const { return invoke_type_; }
   bool IsRecursive() const { return is_recursive_; }
   bool NeedsDexCache() const OVERRIDE { return !IsRecursive(); }
@@ -2517,7 +2533,6 @@
   }
 
  private:
-  const InvokeType original_invoke_type_;
   const InvokeType invoke_type_;
   const bool is_recursive_;
   ClinitCheckRequirement clinit_check_requirement_;
@@ -2536,7 +2551,7 @@
                  uint32_t dex_pc,
                  uint32_t dex_method_index,
                  uint32_t vtable_index)
-      : HInvoke(arena, number_of_arguments, 0u, return_type, dex_pc, dex_method_index),
+      : HInvoke(arena, number_of_arguments, 0u, return_type, dex_pc, dex_method_index, kVirtual),
         vtable_index_(vtable_index) {}
 
   bool CanDoImplicitNullCheckOn(HInstruction* obj) const OVERRIDE {
@@ -2562,7 +2577,7 @@
                    uint32_t dex_pc,
                    uint32_t dex_method_index,
                    uint32_t imt_index)
-      : HInvoke(arena, number_of_arguments, 0u, return_type, dex_pc, dex_method_index),
+      : HInvoke(arena, number_of_arguments, 0u, return_type, dex_pc, dex_method_index, kInterface),
         imt_index_(imt_index) {}
 
   bool CanDoImplicitNullCheckOn(HInstruction* obj) const OVERRIDE {
@@ -3444,8 +3459,8 @@
     return generate_clinit_check_;
   }
 
-  void SetMustGenerateClinitCheck() {
-    generate_clinit_check_ = true;
+  void SetMustGenerateClinitCheck(bool generate_clinit_check) {
+    generate_clinit_check_ = generate_clinit_check;
   }
 
   bool CanCallRuntime() const {
@@ -3920,7 +3935,9 @@
       }
       for (size_t i = 0, e = moves_.Size(); i < e; ++i) {
         DCHECK(!destination.OverlapsWith(moves_.Get(i).GetDestination()))
-            << "Overlapped destination for two moves in a parallel move.";
+            << "Overlapped destination for two moves in a parallel move: "
+            << moves_.Get(i).GetSource() << " ==> " << moves_.Get(i).GetDestination() << " and "
+            << source << " ==> " << destination;
       }
     }
     moves_.Add(MoveOperands(source, destination, type, instruction));
diff --git a/compiler/optimizing/nodes_test.cc b/compiler/optimizing/nodes_test.cc
index 2736453..782cde4 100644
--- a/compiler/optimizing/nodes_test.cc
+++ b/compiler/optimizing/nodes_test.cc
@@ -51,7 +51,7 @@
   exit_block->AddInstruction(new (&allocator) HExit());
 
   HEnvironment* environment = new (&allocator) HEnvironment(
-      &allocator, 1, graph->GetDexFile(), graph->GetMethodIdx(), 0);
+      &allocator, 1, graph->GetDexFile(), graph->GetMethodIdx(), 0, kStatic);
   null_check->SetRawEnvironment(environment);
   environment->SetRawEnvAt(0, parameter);
   parameter->AddEnvUseAt(null_check->GetEnvironment(), 0);
@@ -132,7 +132,7 @@
   ASSERT_TRUE(parameter1->GetUses().HasOnlyOneUse());
 
   HEnvironment* environment = new (&allocator) HEnvironment(
-      &allocator, 1, graph->GetDexFile(), graph->GetMethodIdx(), 0);
+      &allocator, 1, graph->GetDexFile(), graph->GetMethodIdx(), 0, kStatic);
   GrowableArray<HInstruction*> array(&allocator, 1);
   array.Add(parameter1);
 
@@ -143,13 +143,13 @@
   ASSERT_TRUE(parameter1->GetEnvUses().HasOnlyOneUse());
 
   HEnvironment* parent1 = new (&allocator) HEnvironment(
-      &allocator, 1, graph->GetDexFile(), graph->GetMethodIdx(), 0);
+      &allocator, 1, graph->GetDexFile(), graph->GetMethodIdx(), 0, kStatic);
   parent1->CopyFrom(array);
 
   ASSERT_EQ(parameter1->GetEnvUses().SizeSlow(), 2u);
 
   HEnvironment* parent2 = new (&allocator) HEnvironment(
-      &allocator, 1, graph->GetDexFile(), graph->GetMethodIdx(), 0);
+      &allocator, 1, graph->GetDexFile(), graph->GetMethodIdx(), 0, kStatic);
   parent2->CopyFrom(array);
   parent1->SetAndCopyParentChain(&allocator, parent2);
 
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index be9a424..b2e8ecd 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -518,7 +518,7 @@
                                                      dex_compilation_unit.GetClassDefIndex());
   ArenaAllocator arena(Runtime::Current()->GetArenaPool());
   HGraph* graph = new (&arena) HGraph(
-      &arena, dex_file, method_idx, requires_barrier,
+      &arena, dex_file, method_idx, requires_barrier, kInvalidInvokeType,
       compiler_driver->GetCompilerOptions().GetDebuggable());
 
   // For testing purposes, we put a special marker on method names that should be compiled
diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc
index 78d1185..538736b 100644
--- a/compiler/optimizing/prepare_for_register_allocation.cc
+++ b/compiler/optimizing/prepare_for_register_allocation.cc
@@ -53,7 +53,7 @@
   if (check->GetPrevious() == cls) {
     // Pass the initialization duty to the `HLoadClass` instruction,
     // and remove the instruction from the graph.
-    cls->SetMustGenerateClinitCheck();
+    cls->SetMustGenerateClinitCheck(true);
     check->GetBlock()->RemoveInstruction(check);
   }
 }
@@ -82,8 +82,15 @@
 void PrepareForRegisterAllocation::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
   if (invoke->IsStaticWithExplicitClinitCheck()) {
     size_t last_input_index = invoke->InputCount() - 1;
-    HInstruction* last_input = invoke->InputAt(last_input_index);
-    DCHECK(last_input->IsLoadClass()) << last_input->DebugName();
+    HLoadClass* last_input = invoke->InputAt(last_input_index)->AsLoadClass();
+    DCHECK(last_input != nullptr)
+        << "Last input is not HLoadClass. It is " << last_input->DebugName();
+
+    // The static call will initialize the class so there's no need for a clinit check if
+    // it's the first user.
+    if (last_input == invoke->GetPrevious()) {
+      last_input->SetMustGenerateClinitCheck(false);
+    }
 
     // Remove a load class instruction as last input of a static
     // invoke, which has been added (along with a clinit check,
diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc
index 59a2852..c51d248 100644
--- a/compiler/optimizing/ssa_builder.cc
+++ b/compiler/optimizing/ssa_builder.cc
@@ -504,7 +504,7 @@
     // typed and the value in a dex register will not be used for both floating point and
     // non-floating point operations. So the only reason an instruction would want a floating
     // point equivalent is for an unused phi that will be removed by the dead phi elimination phase.
-    DCHECK(user->IsPhi());
+    DCHECK(user->IsPhi()) << "is actually " << user->DebugName() << " (" << user->GetId() << ")";
     return value;
   }
 }
@@ -547,7 +547,8 @@
       current_locals_->Size(),
       GetGraph()->GetDexFile(),
       GetGraph()->GetMethodIdx(),
-      instruction->GetDexPc());
+      instruction->GetDexPc(),
+      GetGraph()->GetInvokeType());
   environment->CopyFrom(*current_locals_);
   instruction->SetRawEnvironment(environment);
 }
diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc
index 89035a3..b446815 100644
--- a/compiler/optimizing/stack_map_stream.cc
+++ b/compiler/optimizing/stack_map_stream.cc
@@ -101,11 +101,13 @@
 
 void StackMapStream::BeginInlineInfoEntry(uint32_t method_index,
                                           uint32_t dex_pc,
+                                          InvokeType invoke_type,
                                           uint32_t num_dex_registers) {
   DCHECK(!in_inline_frame_);
   in_inline_frame_ = true;
   current_inline_info_.method_index = method_index;
   current_inline_info_.dex_pc = dex_pc;
+  current_inline_info_.invoke_type = invoke_type;
   current_inline_info_.num_dex_registers = num_dex_registers;
   current_inline_info_.dex_register_locations_start_index = dex_register_locations_.Size();
   if (num_dex_registers != 0) {
@@ -313,6 +315,7 @@
         InlineInfoEntry inline_entry = inline_infos_.Get(depth + entry.inline_infos_start_index);
         inline_info.SetMethodIndexAtDepth(depth, inline_entry.method_index);
         inline_info.SetDexPcAtDepth(depth, inline_entry.dex_pc);
+        inline_info.SetInvokeTypeAtDepth(depth, inline_entry.invoke_type);
         if (inline_entry.num_dex_registers == 0) {
           // No dex map available.
           inline_info.SetDexRegisterMapOffsetAtDepth(depth, StackMap::kNoDexRegisterMap);
diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h
index 4c03f9f..0af983b 100644
--- a/compiler/optimizing/stack_map_stream.h
+++ b/compiler/optimizing/stack_map_stream.h
@@ -104,6 +104,7 @@
   struct InlineInfoEntry {
     uint32_t dex_pc;
     uint32_t method_index;
+    InvokeType invoke_type;
     uint32_t num_dex_registers;
     BitVector* live_dex_registers_mask;
     size_t dex_register_locations_start_index;
@@ -121,6 +122,7 @@
 
   void BeginInlineInfoEntry(uint32_t method_index,
                             uint32_t dex_pc,
+                            InvokeType invoke_type,
                             uint32_t num_dex_registers);
   void EndInlineInfoEntry();
 
diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc
index e04fa98..98e14ea 100644
--- a/compiler/optimizing/stack_map_test.cc
+++ b/compiler/optimizing/stack_map_test.cc
@@ -128,9 +128,9 @@
   stream.BeginStackMapEntry(0, 64, 0x3, &sp_mask1, number_of_dex_registers, 2);
   stream.AddDexRegisterEntry(Kind::kInStack, 0);         // Short location.
   stream.AddDexRegisterEntry(Kind::kConstant, -2);       // Large location.
-  stream.BeginInlineInfoEntry(82, 3, number_of_dex_registers_in_inline_info);
+  stream.BeginInlineInfoEntry(82, 3, kDirect, number_of_dex_registers_in_inline_info);
   stream.EndInlineInfoEntry();
-  stream.BeginInlineInfoEntry(42, 2, number_of_dex_registers_in_inline_info);
+  stream.BeginInlineInfoEntry(42, 2, kStatic, number_of_dex_registers_in_inline_info);
   stream.EndInlineInfoEntry();
   stream.EndStackMapEntry();
 
@@ -218,6 +218,8 @@
     ASSERT_EQ(42u, inline_info.GetMethodIndexAtDepth(1));
     ASSERT_EQ(3u, inline_info.GetDexPcAtDepth(0));
     ASSERT_EQ(2u, inline_info.GetDexPcAtDepth(1));
+    ASSERT_EQ(kDirect, inline_info.GetInvokeTypeAtDepth(0));
+    ASSERT_EQ(kStatic, inline_info.GetInvokeTypeAtDepth(1));
   }
 
   // Second stack map.
@@ -519,10 +521,10 @@
   stream.AddDexRegisterEntry(Kind::kInStack, 0);
   stream.AddDexRegisterEntry(Kind::kConstant, 4);
 
-  stream.BeginInlineInfoEntry(42, 2, 1);
+  stream.BeginInlineInfoEntry(42, 2, kStatic, 1);
   stream.AddDexRegisterEntry(Kind::kInStack, 8);
   stream.EndInlineInfoEntry();
-  stream.BeginInlineInfoEntry(82, 3, 3);
+  stream.BeginInlineInfoEntry(82, 3, kStatic, 3);
   stream.AddDexRegisterEntry(Kind::kInStack, 16);
   stream.AddDexRegisterEntry(Kind::kConstant, 20);
   stream.AddDexRegisterEntry(Kind::kInRegister, 15);
@@ -535,15 +537,15 @@
   stream.AddDexRegisterEntry(Kind::kInStack, 56);
   stream.AddDexRegisterEntry(Kind::kConstant, 0);
 
-  stream.BeginInlineInfoEntry(42, 2, 1);
+  stream.BeginInlineInfoEntry(42, 2, kDirect, 1);
   stream.AddDexRegisterEntry(Kind::kInStack, 12);
   stream.EndInlineInfoEntry();
-  stream.BeginInlineInfoEntry(82, 3, 3);
+  stream.BeginInlineInfoEntry(82, 3, kStatic, 3);
   stream.AddDexRegisterEntry(Kind::kInStack, 80);
   stream.AddDexRegisterEntry(Kind::kConstant, 10);
   stream.AddDexRegisterEntry(Kind::kInRegister, 5);
   stream.EndInlineInfoEntry();
-  stream.BeginInlineInfoEntry(52, 5, 0);
+  stream.BeginInlineInfoEntry(52, 5, kVirtual, 0);
   stream.EndInlineInfoEntry();
 
   stream.EndStackMapEntry();
@@ -559,12 +561,12 @@
   stream.AddDexRegisterEntry(Kind::kInStack, 56);
   stream.AddDexRegisterEntry(Kind::kConstant, 0);
 
-  stream.BeginInlineInfoEntry(42, 2, 0);
+  stream.BeginInlineInfoEntry(42, 2, kVirtual, 0);
   stream.EndInlineInfoEntry();
-  stream.BeginInlineInfoEntry(52, 5, 1);
+  stream.BeginInlineInfoEntry(52, 5, kInterface, 1);
   stream.AddDexRegisterEntry(Kind::kInRegister, 2);
   stream.EndInlineInfoEntry();
-  stream.BeginInlineInfoEntry(52, 10, 2);
+  stream.BeginInlineInfoEntry(52, 10, kStatic, 2);
   stream.AddDexRegisterEntry(Kind::kNone, 0);
   stream.AddDexRegisterEntry(Kind::kInRegister, 3);
   stream.EndInlineInfoEntry();
@@ -590,8 +592,10 @@
     ASSERT_EQ(2u, if0.GetDepth());
     ASSERT_EQ(2u, if0.GetDexPcAtDepth(0));
     ASSERT_EQ(42u, if0.GetMethodIndexAtDepth(0));
+    ASSERT_EQ(kStatic, if0.GetInvokeTypeAtDepth(0));
     ASSERT_EQ(3u, if0.GetDexPcAtDepth(1));
     ASSERT_EQ(82u, if0.GetMethodIndexAtDepth(1));
+    ASSERT_EQ(kStatic, if0.GetInvokeTypeAtDepth(1));
 
     DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(0, if0, 1);
     ASSERT_EQ(8, dex_registers1.GetStackOffsetInBytes(0, 1, ci));
@@ -614,10 +618,13 @@
     ASSERT_EQ(3u, if1.GetDepth());
     ASSERT_EQ(2u, if1.GetDexPcAtDepth(0));
     ASSERT_EQ(42u, if1.GetMethodIndexAtDepth(0));
+    ASSERT_EQ(kDirect, if1.GetInvokeTypeAtDepth(0));
     ASSERT_EQ(3u, if1.GetDexPcAtDepth(1));
     ASSERT_EQ(82u, if1.GetMethodIndexAtDepth(1));
+    ASSERT_EQ(kStatic, if1.GetInvokeTypeAtDepth(1));
     ASSERT_EQ(5u, if1.GetDexPcAtDepth(2));
     ASSERT_EQ(52u, if1.GetMethodIndexAtDepth(2));
+    ASSERT_EQ(kVirtual, if1.GetInvokeTypeAtDepth(2));
 
     DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(0, if1, 1);
     ASSERT_EQ(12, dex_registers1.GetStackOffsetInBytes(0, 1, ci));
@@ -652,10 +659,13 @@
     ASSERT_EQ(3u, if2.GetDepth());
     ASSERT_EQ(2u, if2.GetDexPcAtDepth(0));
     ASSERT_EQ(42u, if2.GetMethodIndexAtDepth(0));
+    ASSERT_EQ(kVirtual, if2.GetInvokeTypeAtDepth(0));
     ASSERT_EQ(5u, if2.GetDexPcAtDepth(1));
     ASSERT_EQ(52u, if2.GetMethodIndexAtDepth(1));
+    ASSERT_EQ(kInterface, if2.GetInvokeTypeAtDepth(1));
     ASSERT_EQ(10u, if2.GetDexPcAtDepth(2));
     ASSERT_EQ(52u, if2.GetMethodIndexAtDepth(2));
+    ASSERT_EQ(kStatic, if2.GetInvokeTypeAtDepth(2));
 
     ASSERT_FALSE(if2.HasDexRegisterMapAtDepth(0));
 
diff --git a/compiler/utils/arm/assembler_arm.h b/compiler/utils/arm/assembler_arm.h
index dee8287..52a69ca 100644
--- a/compiler/utils/arm/assembler_arm.h
+++ b/compiler/utils/arm/assembler_arm.h
@@ -33,6 +33,16 @@
 class Arm32Assembler;
 class Thumb2Assembler;
 
+// This class indicates that the label and its uses
+// will fall into a range that is encodable in 16bits on thumb2.
+class NearLabel : public Label {
+ public:
+  NearLabel() {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(NearLabel);
+};
+
 class ShifterOperand {
  public:
   ShifterOperand() : type_(kUnknown), rm_(kNoRegister), rs_(kNoRegister),
@@ -519,6 +529,9 @@
 
   // Branch instructions.
   virtual void b(Label* label, Condition cond = AL) = 0;
+  virtual void b(NearLabel* label, Condition cond = AL) {
+    b(static_cast<Label*>(label), cond);
+  }
   virtual void bl(Label* label, Condition cond = AL) = 0;
   virtual void blx(Register rm, Condition cond = AL) = 0;
   virtual void bx(Register rm, Condition cond = AL) = 0;
@@ -654,6 +667,9 @@
   virtual void Bind(Label* label) = 0;
 
   virtual void CompareAndBranchIfZero(Register r, Label* label) = 0;
+  virtual void CompareAndBranchIfZero(Register r, NearLabel* label) {
+    CompareAndBranchIfZero(r, static_cast<Label*>(label));
+  }
   virtual void CompareAndBranchIfNonZero(Register r, Label* label) = 0;
 
   //
diff --git a/compiler/utils/arm/assembler_arm32.h b/compiler/utils/arm/assembler_arm32.h
index 55ec7b4..4564767 100644
--- a/compiler/utils/arm/assembler_arm32.h
+++ b/compiler/utils/arm/assembler_arm32.h
@@ -201,8 +201,8 @@
   void vpopd(DRegister reg, int nregs, Condition cond = AL) OVERRIDE;
 
   // Branch instructions.
-  void b(Label* label, Condition cond = AL);
-  void bl(Label* label, Condition cond = AL);
+  void b(Label* label, Condition cond = AL) OVERRIDE;
+  void bl(Label* label, Condition cond = AL) OVERRIDE;
   void blx(Register rm, Condition cond = AL) OVERRIDE;
   void bx(Register rm, Condition cond = AL) OVERRIDE;
   void Lsl(Register rd, Register rm, uint32_t shift_imm, bool setcc = false,
diff --git a/compiler/utils/arm/assembler_thumb2.cc b/compiler/utils/arm/assembler_thumb2.cc
index e7cf26e..4ff3aeb 100644
--- a/compiler/utils/arm/assembler_thumb2.cc
+++ b/compiler/utils/arm/assembler_thumb2.cc
@@ -671,11 +671,17 @@
   EmitVFPddd(cond, B23 | B21 | B20 | B18 | B16 | B6, dd, D0, D0);
 }
 
+
 void Thumb2Assembler::b(Label* label, Condition cond) {
   EmitBranch(cond, label, false, false);
 }
 
 
+void Thumb2Assembler::b(NearLabel* label, Condition cond) {
+  EmitBranch(cond, label, false, false, /* is_near */ true);
+}
+
+
 void Thumb2Assembler::bl(Label* label, Condition cond) {
   CheckCondition(cond);
   EmitBranch(cond, label, true, false);
@@ -1369,6 +1375,7 @@
 
 
 uint16_t Thumb2Assembler::EmitCompareAndBranch(Register rn, uint16_t prev, bool n) {
+  CHECK(IsLowRegister(rn));
   uint32_t location = buffer_.Size();
 
   // This is always unresolved as it must be a forward branch.
@@ -1613,7 +1620,7 @@
 }
 
 
-void Thumb2Assembler::EmitBranch(Condition cond, Label* label, bool link, bool x) {
+void Thumb2Assembler::EmitBranch(Condition cond, Label* label, bool link, bool x, bool is_near) {
   uint32_t pc = buffer_.Size();
   Branch::Type branch_type;
   if (cond == AL) {
@@ -1638,15 +1645,14 @@
     // branch the size may change if it so happens that other branches change size that change
     // the distance to the target and that distance puts this branch over the limit for 16 bits.
     if (size == Branch::k16Bit) {
-      DCHECK(!force_32bit_branches_);
       Emit16(0);          // Space for a 16 bit branch.
     } else {
       Emit32(0);            // Space for a 32 bit branch.
     }
   } else {
     // Branch is to an unbound label.  Emit space for it.
-    uint16_t branch_id = AddBranch(branch_type, pc, cond);    // Unresolved branch.
-    if (force_32bit_branches_ || force_32bit_) {
+    uint16_t branch_id = AddBranch(branch_type, pc, cond, is_near);    // Unresolved branch.
+    if (force_32bit_ || (!CanRelocateBranches() && !is_near)) {
       Emit16(static_cast<uint16_t>(label->position_));    // Emit current label link.
       Emit16(0);                   // another 16 bits.
     } else {
@@ -2200,6 +2206,9 @@
   if (label->IsBound()) {
     LOG(FATAL) << "cbz can only be used to branch forwards";
     UNREACHABLE();
+  } else if (IsHighRegister(rn)) {
+    LOG(FATAL) << "cbz can only be used with low registers";
+    UNREACHABLE();
   } else {
     uint16_t branchid = EmitCompareAndBranch(rn, static_cast<uint16_t>(label->position_), false);
     label->LinkTo(branchid);
@@ -2212,6 +2221,9 @@
   if (label->IsBound()) {
     LOG(FATAL) << "cbnz can only be used to branch forwards";
     UNREACHABLE();
+  } else if (IsHighRegister(rn)) {
+    LOG(FATAL) << "cbnz can only be used with low registers";
+    UNREACHABLE();
   } else {
     uint16_t branchid = EmitCompareAndBranch(rn, static_cast<uint16_t>(label->position_), true);
     label->LinkTo(branchid);
@@ -2282,7 +2294,7 @@
     uint32_t branch_location = branch->GetLocation();
     uint16_t next = buffer_.Load<uint16_t>(branch_location);       // Get next in chain.
     if (changed) {
-      DCHECK(!force_32bit_branches_);
+      DCHECK(CanRelocateBranches());
       MakeHoleForBranch(branch->GetLocation(), 2);
       if (branch->IsCompareAndBranch()) {
         // A cbz/cbnz instruction has changed size.  There is no valid encoding for
@@ -2742,21 +2754,31 @@
 
 
 void Thumb2Assembler::CompareAndBranchIfZero(Register r, Label* label) {
-  if (force_32bit_branches_) {
+  if (CanRelocateBranches() && IsLowRegister(r)) {
+    cbz(r, label);
+  } else {
     cmp(r, ShifterOperand(0));
     b(label, EQ);
-  } else {
+  }
+}
+
+
+void Thumb2Assembler::CompareAndBranchIfZero(Register r, NearLabel* label) {
+  if (IsLowRegister(r)) {
     cbz(r, label);
+  } else {
+    cmp(r, ShifterOperand(0));
+    b(label, EQ);
   }
 }
 
 
 void Thumb2Assembler::CompareAndBranchIfNonZero(Register r, Label* label) {
-  if (force_32bit_branches_) {
+  if (CanRelocateBranches() && IsLowRegister(r)) {
+    cbnz(r, label);
+  } else {
     cmp(r, ShifterOperand(0));
     b(label, NE);
-  } else {
-    cbnz(r, label);
   }
 }
 }  // namespace arm
diff --git a/compiler/utils/arm/assembler_thumb2.h b/compiler/utils/arm/assembler_thumb2.h
index 17eae8b..9f02e56 100644
--- a/compiler/utils/arm/assembler_thumb2.h
+++ b/compiler/utils/arm/assembler_thumb2.h
@@ -31,8 +31,8 @@
 
 class Thumb2Assembler FINAL : public ArmAssembler {
  public:
-  explicit Thumb2Assembler(bool force_32bit_branches = false)
-      : force_32bit_branches_(force_32bit_branches),
+  explicit Thumb2Assembler(bool can_relocate_branches = true)
+      : can_relocate_branches_(can_relocate_branches),
         force_32bit_(false),
         it_cond_index_(kNoItCondition),
         next_condition_(AL) {
@@ -52,8 +52,8 @@
     return force_32bit_;
   }
 
-  bool IsForced32BitBranches() const {
-    return force_32bit_branches_;
+  bool CanRelocateBranches() const {
+    return can_relocate_branches_;
   }
 
   void FinalizeInstructions(const MemoryRegion& region) OVERRIDE {
@@ -239,6 +239,7 @@
 
   // Branch instructions.
   void b(Label* label, Condition cond = AL);
+  void b(NearLabel* label, Condition cond = AL);
   void bl(Label* label, Condition cond = AL);
   void blx(Label* label);
   void blx(Register rm, Condition cond = AL) OVERRIDE;
@@ -273,6 +274,7 @@
   void Mov(Register rd, Register rm, Condition cond = AL) OVERRIDE;
 
   void CompareAndBranchIfZero(Register r, Label* label) OVERRIDE;
+  void CompareAndBranchIfZero(Register r, NearLabel* label) OVERRIDE;
   void CompareAndBranchIfNonZero(Register r, Label* label) OVERRIDE;
 
   // Memory barriers.
@@ -431,7 +433,7 @@
 
   void EmitVPushPop(uint32_t reg, int nregs, bool push, bool dbl, Condition cond);
 
-  void EmitBranch(Condition cond, Label* label, bool link, bool x);
+  void EmitBranch(Condition cond, Label* label, bool link, bool x, bool is_near = false);
   static int32_t EncodeBranchOffset(int32_t offset, int32_t inst);
   static int DecodeBranchOffset(int32_t inst);
   int32_t EncodeTstOffset(int offset, int32_t inst);
@@ -439,8 +441,12 @@
   void EmitShift(Register rd, Register rm, Shift shift, uint8_t amount, bool setcc = false);
   void EmitShift(Register rd, Register rn, Shift shift, Register rm, bool setcc = false);
 
-  bool force_32bit_branches_;  // Force the assembler to use 32 bit branch instructions.
-  bool force_32bit_;           // Force the assembler to use 32 bit thumb2 instructions.
+  // Whether the assembler can relocate branches. If false, unresolved branches will be
+  // emitted on 32bits.
+  bool can_relocate_branches_;
+
+  // Force the assembler to use 32 bit thumb2 instructions.
+  bool force_32bit_;
 
   // IfThen conditions.  Used to check that conditional instructions match the preceding IT.
   Condition it_conditions_[4];
@@ -555,13 +561,26 @@
     // Resolve a branch when the target is known.  If this causes the
     // size of the branch to change return true.  Otherwise return false.
     bool Resolve(uint32_t target) {
+      uint32_t old_target = target_;
       target_ = target;
-      Size newsize = CalculateSize();
-      if (size_ != newsize) {
-        size_ = newsize;
-        return true;
+      if (assembler_->CanRelocateBranches()) {
+        Size new_size = CalculateSize();
+        if (size_ != new_size) {
+          size_ = new_size;
+          return true;
+        }
+        return false;
+      } else {
+        if (kIsDebugBuild) {
+          if (old_target == kUnresolved) {
+            // Check that the size has not increased.
+            DCHECK(!(CalculateSize() == k32Bit && size_ == k16Bit));
+          } else {
+            DCHECK(CalculateSize() == size_);
+          }
+        }
+        return false;
       }
-      return false;
     }
 
     // Move a cbz/cbnz branch.  This is always forward.
@@ -577,6 +596,7 @@
     // size of the branch instruction.  It returns true if the branch
     // has changed size.
     bool Relocate(uint32_t oldlocation, int32_t delta) {
+      DCHECK(assembler_->CanRelocateBranches());
       if (location_ > oldlocation) {
         location_ += delta;
       }
@@ -589,9 +609,9 @@
       }
 
       // Calculate the new size.
-      Size newsize = CalculateSize();
-      if (size_ != newsize) {
-        size_ = newsize;
+      Size new_size = CalculateSize();
+      if (size_ != new_size) {
+        size_ = new_size;
         return true;
       }
       return false;
@@ -633,15 +653,17 @@
    private:
     // Calculate the size of the branch instruction based on its type and offset.
     Size CalculateSize() const {
-      if (assembler_->IsForced32BitBranches()) {
-        return k32Bit;
-      }
       if (target_ == kUnresolved) {
         if (assembler_->IsForced32Bit() && (type_ == kUnconditional || type_ == kConditional)) {
           return k32Bit;
         }
-        return k16Bit;
+        if (IsCompareAndBranch()) {
+          // Compare and branch instructions can only be encoded on 16 bits.
+          return k16Bit;
+        }
+        return assembler_->CanRelocateBranches() ? k16Bit : k32Bit;
       }
+      // When the target is resolved, we know the best encoding for it.
       int32_t delta = target_ - location_ - 4;
       if (delta < 0) {
         delta = -delta;
@@ -702,8 +724,15 @@
   }
 
   // Add an unresolved branch and return its id.
-  uint16_t AddBranch(Branch::Type type, uint32_t location, Condition cond = AL) {
-    branches_.push_back(new Branch(this, type, location, cond));
+  uint16_t AddBranch(Branch::Type type,
+                     uint32_t location,
+                     Condition cond = AL,
+                     bool is_near = false) {
+    Branch* branch = new Branch(this, type, location, cond);
+    if (is_near) {
+      branch->ResetSize(Branch::k16Bit);
+    }
+    branches_.push_back(branch);
     return branches_.size() - 1;
   }
 
diff --git a/compiler/utils/assembler_test_base.h b/compiler/utils/assembler_test_base.h
index 3341151..40eb15b 100644
--- a/compiler/utils/assembler_test_base.h
+++ b/compiler/utils/assembler_test_base.h
@@ -215,9 +215,9 @@
 
     bool success = Exec(args, error_msg);
     if (!success) {
-      LOG(INFO) << "Assembler command line:";
+      LOG(ERROR) << "Assembler command line:";
       for (std::string arg : args) {
-        LOG(INFO) << arg;
+        LOG(ERROR) << arg;
       }
     }
     return success;
diff --git a/compiler/utils/assembler_thumb_test.cc b/compiler/utils/assembler_thumb_test.cc
index 7738627..1a2c9a9 100644
--- a/compiler/utils/assembler_thumb_test.cc
+++ b/compiler/utils/assembler_thumb_test.cc
@@ -1338,6 +1338,24 @@
   delete assembler;
 }
 
+TEST(Thumb2AssemblerTest, CompareAndBranch) {
+  arm::Thumb2Assembler* assembler = static_cast<arm::Thumb2Assembler*>(Assembler::Create(kThumb2));
+
+  arm::NearLabel label;
+  __ CompareAndBranchIfZero(arm::R0, &label);
+  __ CompareAndBranchIfZero(arm::R11, &label);
+  __ CompareAndBranchIfNonZero(arm::R0, &label);
+  __ CompareAndBranchIfNonZero(arm::R11, &label);
+  __ Bind(&label);
+
+  size_t cs = __ CodeSize();
+  std::vector<uint8_t> managed_code(cs);
+  MemoryRegion code(&managed_code[0], managed_code.size());
+  __ FinalizeInstructions(code);
+  dump(managed_code, "CompareAndBranch");
+  delete assembler;
+}
+
 #undef __
 }  // namespace arm
 }  // namespace art
diff --git a/compiler/utils/assembler_thumb_test_expected.cc.inc b/compiler/utils/assembler_thumb_test_expected.cc.inc
index 3d03234..841d6a0 100644
--- a/compiler/utils/assembler_thumb_test_expected.cc.inc
+++ b/compiler/utils/assembler_thumb_test_expected.cc.inc
@@ -4822,6 +4822,16 @@
   "  30:   f8a4 0040       strh.w  r0, [r4, #64]   ; 0x40\n",
   nullptr
 };
+const char* CompareAndBranchResults[] = {
+  "  0: b130        cbz r0, 10 <CompareAndBranch+0x10>\n",
+  "  2: f1bb 0f00   cmp.w fp, #0\n",
+  "  6: d003        beq.n 10 <CompareAndBranch+0x10>\n",
+  "  8: b910        cbnz r0, 10 <CompareAndBranch+0x10>\n",
+  "  a: f1bb 0f00   cmp.w fp, #0\n",
+  "  e: d1ff        bne.n 10 <CompareAndBranch+0x10>\n",
+  nullptr
+};
+
 std::map<std::string, const char**> test_results;
 void setup_results() {
     test_results["SimpleMov"] = SimpleMovResults;
@@ -4869,4 +4879,5 @@
     test_results["LoadStoreRegOffset"] = LoadStoreRegOffsetResults;
     test_results["LoadStoreLiteral"] = LoadStoreLiteralResults;
     test_results["LoadStoreLimits"] = LoadStoreLimitsResults;
+    test_results["CompareAndBranch"] = CompareAndBranchResults;
 }
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 3cf458a..43bec37 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -1594,7 +1594,7 @@
 
     // Initialize maps for unstarted runtime. This needs to be here, as running clinits needs this
     // set up.
-    interpreter::UnstartedRuntimeInitialize();
+    interpreter::UnstartedRuntime::Initialize();
 
     runtime->GetClassLinker()->RunRootClinits();
     runtime_ = runtime;
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index 7488578..3c145d7 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -313,8 +313,7 @@
     /*
      * All generated callsites for interface invokes and invocation slow paths will load arguments
      * as usual - except instead of loading arg0/r0 with the target Method*, arg0/r0 will contain
-     * the method_idx.  This wrapper will save arg1-arg3, load the caller's Method*, align the
-     * stack and call the appropriate C helper.
+     * the method_idx.  This wrapper will save arg1-arg3, and call the appropriate C helper.
      * NOTE: "this" is first visible argument of the target, and so can be found in arg1/r1.
      *
      * The helper will attempt to locate the target and return a 64-bit result in r0/r1 consisting
@@ -330,13 +329,10 @@
     .extern \cxx_name
 ENTRY \c_name
     SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME r2, r3  @ save callee saves in case allocation triggers GC
-    ldr    r2, [sp, #FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE]  @ pass caller Method*
-    mov    r3, r9                         @ pass Thread::Current
-    mov    r12, sp
-    str    r12, [sp, #-16]!               @ expand the frame and pass SP
+    mov    r2, r9                         @ pass Thread::Current
+    mov    r3, sp
     .cfi_adjust_cfa_offset 16
     bl     \cxx_name                      @ (method_idx, this, caller, Thread*, SP)
-    add    sp, #16                        @ strip the extra frame
     .cfi_adjust_cfa_offset -16
     mov    r12, r1                        @ save Method*->code_
     RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index f8b0734..6b16a2e5 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -459,8 +459,7 @@
     /*
      * All generated callsites for interface invokes and invocation slow paths will load arguments
      * as usual - except instead of loading arg0/x0 with the target Method*, arg0/x0 will contain
-     * the method_idx.  This wrapper will save arg1-arg3, load the caller's Method*, align the
-     * stack and call the appropriate C helper.
+     * the method_idx.  This wrapper will save arg1-arg3, and call the appropriate C helper.
      * NOTE: "this" is first visible argument of the target, and so can be found in arg1/x1.
      *
      * The helper will attempt to locate the target and return a 128-bit result in x0/x1 consisting
@@ -483,10 +482,9 @@
     // Helper signature is always
     // (method_idx, *this_object, *caller_method, *self, sp)
 
-    ldr    w2, [sp, #FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE]  // pass caller Method*
-    mov    x3, xSELF                      // pass Thread::Current
-    mov    x4, sp
-    bl     \cxx_name                      // (method_idx, this, caller, Thread*, SP)
+    mov    x2, xSELF                      // pass Thread::Current
+    mov    x3, sp
+    bl     \cxx_name                      // (method_idx, this, Thread*, SP)
     mov    xIP0, x1                       // save Method*->code_
     RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
     cbz    x0, 1f                         // did we find the target? if not go to exception delivery
diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S
index ee5c59f..92b180e 100644
--- a/runtime/arch/mips/quick_entrypoints_mips.S
+++ b/runtime/arch/mips/quick_entrypoints_mips.S
@@ -439,8 +439,7 @@
     /*
      * All generated callsites for interface invokes and invocation slow paths will load arguments
      * as usual - except instead of loading arg0/$a0 with the target Method*, arg0/$a0 will contain
-     * the method_idx.  This wrapper will save arg1-arg3, load the caller's Method*, align the
-     * stack and call the appropriate C helper.
+     * the method_idx.  This wrapper will save arg1-arg3, and call the appropriate C helper.
      * NOTE: "this" is first visable argument of the target, and so can be found in arg1/$a1.
      *
      * The helper will attempt to locate the target and return a 64-bit result in $v0/$v1 consisting
@@ -456,15 +455,13 @@
     .extern \cxx_name
 ENTRY \c_name
     SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME  # save callee saves in case allocation triggers GC
-    lw    $a2, FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE+ARG_SLOT_SIZE($sp)    # pass caller Method*
-    addiu $t0, $sp, ARG_SLOT_SIZE         # save $sp (remove arg slots)
-    move  $a3, rSELF                      # pass Thread::Current
-    jal   \cxx_name                       # (method_idx, this, caller, Thread*, $sp)
-    sw    $t0, 16($sp)                    # pass $sp
-    move  $a0, $v0                        # save target Method*
+    move  $a2, rSELF                       # pass Thread::Current
+    jal   \cxx_name                        # (method_idx, this, Thread*, $sp)
+    addiu $a3, $sp, ARG_SLOT_SIZE          # pass $sp (remove arg slots)
+    move  $a0, $v0                         # save target Method*
     RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
     beqz  $v0, 1f
-    move  $t9, $v1                        # save $v0->code_
+    move  $t9, $v1                         # save $v0->code_
     jalr  $zero, $t9
     nop
 1:
diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S
index ff79b5d..b7320a6 100644
--- a/runtime/arch/mips64/quick_entrypoints_mips64.S
+++ b/runtime/arch/mips64/quick_entrypoints_mips64.S
@@ -529,10 +529,9 @@
     .extern \cxx_name
 ENTRY \c_name
     SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME  # save callee saves in case allocation triggers GC
-    lwu   $a2, FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE($sp)  # pass caller Method*
-    move  $a3, rSELF                       # pass Thread::Current
-    jal   \cxx_name                        # (method_idx, this, caller, Thread*, $sp)
-    move  $a4, $sp                         # pass $sp
+    move  $a2, rSELF                       # pass Thread::Current
+    jal   \cxx_name                        # (method_idx, this, Thread*, $sp)
+    move  $a3, $sp                         # pass $sp
     move  $a0, $v0                         # save target Method*
     move  $t9, $v1                         # save $v0->code_
     RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index 6ebeba3..d62c1bc 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -278,8 +278,7 @@
     /*
      * All generated callsites for interface invokes and invocation slow paths will load arguments
      * as usual - except instead of loading arg0/r0 with the target Method*, arg0/r0 will contain
-     * the method_idx.  This wrapper will save arg1-arg3, load the caller's Method*, align the
-     * stack and call the appropriate C helper.
+     * the method_idx.  This wrapper will save arg1-arg3 and call the appropriate C helper.
      * NOTE: "this" is first visible argument of the target, and so can be found in arg1/r1.
      *
      * The helper will attempt to locate the target and return a 64-bit result in r0/r1 consisting
@@ -297,19 +296,15 @@
     movl %esp, %edx  // remember SP
 
     // Outgoing argument set up
-    subl MACRO_LITERAL(12), %esp  // alignment padding
-    CFI_ADJUST_CFA_OFFSET(12)
     PUSH edx                      // pass SP
     pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
     CFI_ADJUST_CFA_OFFSET(4)
-    pushl 32+32(%edx)             // pass caller Method*
-    CFI_ADJUST_CFA_OFFSET(4)
     PUSH ecx                      // pass arg2
     PUSH eax                      // pass arg1
     call VAR(cxx_name, 1)         // cxx_name(arg1, arg2, arg3, Thread*, SP)
     movl %edx, %edi               // save code pointer in EDI
-    addl MACRO_LITERAL(36), %esp  // Pop arguments skip eax
-    CFI_ADJUST_CFA_OFFSET(-36)
+    addl MACRO_LITERAL(20), %esp  // Pop arguments skip eax
+    CFI_ADJUST_CFA_OFFSET(-20)
 
     // Restore FPRs.
     movsd 0(%esp), %xmm0
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index da4d92b..ddeb5b8 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -341,8 +341,7 @@
     /*
      * All generated callsites for interface invokes and invocation slow paths will load arguments
      * as usual - except instead of loading arg0/rdi with the target Method*, arg0/rdi will contain
-     * the method_idx.  This wrapper will save arg1-arg3, load the caller's Method*, align the
-     * stack and call the appropriate C helper.
+     * the method_idx.  This wrapper will save arg1-arg3, and call the appropriate C helper.
      * NOTE: "this" is first visible argument of the target, and so can be found in arg1/rsi.
      *
      * The helper will attempt to locate the target and return a 128-bit result in rax/rdx consisting
@@ -362,11 +361,10 @@
     // Helper signature is always
     // (method_idx, *this_object, *caller_method, *self, sp)
 
-    movl FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE(%rsp), %edx  // pass caller Method*
-    movq %gs:THREAD_SELF_OFFSET, %rcx                      // pass Thread
-    movq %rsp, %r8                                         // pass SP
+    movq %gs:THREAD_SELF_OFFSET, %rdx                      // pass Thread
+    movq %rsp, %rcx                                        // pass SP
 
-    call VAR(cxx_name, 1)                   // cxx_name(arg1, arg2, caller method*, Thread*, SP)
+    call VAR(cxx_name, 1)                   // cxx_name(arg1, arg2, Thread*, SP)
                                                            // save the code pointer
     movq %rax, %rdi
     movq %rdx, %rax
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index 7a711cc..de3a29b 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -327,7 +327,7 @@
   // Initialize maps for unstarted runtime. This needs to be here, as running clinits needs this
   // set up.
   if (!unstarted_initialized_) {
-    interpreter::UnstartedRuntimeInitialize();
+    interpreter::UnstartedRuntime::Initialize();
     unstarted_initialized_ = true;
   }
 
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 852ba49..0eb7f2b 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -3974,7 +3974,8 @@
 
   CHECK_EQ(sizeof(jvalue), sizeof(uint64_t));
 
-  JValue result = InvokeWithJValues(soa, pReq->receiver.Read(), soa.EncodeMethod(m.Get()),
+  ScopedLocalRef<jobject> ref(soa.Env(), soa.AddLocalReference<jobject>(pReq->receiver.Read()));
+  JValue result = InvokeWithJValues(soa, ref.get(), soa.EncodeMethod(m.Get()),
                                     reinterpret_cast<jvalue*>(pReq->arg_values));
 
   pReq->result_tag = BasicTagFromDescriptor(m.Get()->GetShorty());
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index 9292cff..625e695 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -38,25 +38,34 @@
 
 namespace art {
 
-inline mirror::ArtMethod* GetCalleeSaveMethodCaller(Thread* self, Runtime::CalleeSaveType type)
+inline mirror::ArtMethod* GetCalleeSaveMethodCaller(StackReference<mirror::ArtMethod>* sp,
+                                                    Runtime::CalleeSaveType type,
+                                                    bool do_caller_check = false)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  auto* refs_only_sp = self->GetManagedStack()->GetTopQuickFrame();
-  DCHECK_EQ(refs_only_sp->AsMirrorPtr(), Runtime::Current()->GetCalleeSaveMethod(type));
+  DCHECK_EQ(sp->AsMirrorPtr(), Runtime::Current()->GetCalleeSaveMethod(type));
 
   const size_t callee_frame_size = GetCalleeSaveFrameSize(kRuntimeISA, type);
   auto* caller_sp = reinterpret_cast<StackReference<mirror::ArtMethod>*>(
-          reinterpret_cast<uintptr_t>(refs_only_sp) + callee_frame_size);
+          reinterpret_cast<uintptr_t>(sp) + callee_frame_size);
   auto* caller = caller_sp->AsMirrorPtr();
 
-  if (kIsDebugBuild) {
-    NthCallerVisitor visitor(self, 1, true);
+  if (kIsDebugBuild && do_caller_check) {
+    // Note that do_caller_check is optional, as this method can be called by
+    // stubs, and tests without a proper call stack.
+    NthCallerVisitor visitor(Thread::Current(), 1, true);
     visitor.WalkStack();
-    CHECK(caller == visitor.caller);
+    CHECK_EQ(caller, visitor.caller);
   }
 
   return caller;
 }
 
+inline mirror::ArtMethod* GetCalleeSaveMethodCaller(Thread* self, Runtime::CalleeSaveType type)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  return GetCalleeSaveMethodCaller(
+      self->GetManagedStack()->GetTopQuickFrame(), type, true /* do_caller_check */);
+}
+
 template <const bool kAccessCheck>
 ALWAYS_INLINE
 inline mirror::Class* CheckObjectAlloc(uint32_t type_idx,
diff --git a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
index 46629f5..9148878 100644
--- a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
@@ -25,8 +25,7 @@
 
 namespace art {
 
-extern "C" mirror::Class* artInitializeStaticStorageFromCode(uint32_t type_idx,
-                                                             Thread* self)
+extern "C" mirror::Class* artInitializeStaticStorageFromCode(uint32_t type_idx, Thread* self)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   // Called to ensure static storage base is initialized for direct static field reads and writes.
   // A class may be accessing another class' fields when it doesn't have access, as access has been
@@ -36,8 +35,7 @@
   return ResolveVerifyAndClinit(type_idx, caller, self, true, false);
 }
 
-extern "C" mirror::Class* artInitializeTypeFromCode(uint32_t type_idx,
-                                                    Thread* self)
+extern "C" mirror::Class* artInitializeTypeFromCode(uint32_t type_idx, Thread* self)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   // Called when method->dex_cache_resolved_types_[] misses.
   ScopedQuickEntrypointChecks sqec(self);
@@ -45,8 +43,7 @@
   return ResolveVerifyAndClinit(type_idx, caller, self, false, false);
 }
 
-extern "C" mirror::Class* artInitializeTypeAndVerifyAccessFromCode(uint32_t type_idx,
-                                                                   Thread* self)
+extern "C" mirror::Class* artInitializeTypeAndVerifyAccessFromCode(uint32_t type_idx, Thread* self)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   // Called when caller isn't guaranteed to have access to a type and the dex cache may be
   // unpopulated.
@@ -55,8 +52,7 @@
   return ResolveVerifyAndClinit(type_idx, caller, self, false, true);
 }
 
-extern "C" mirror::String* artResolveStringFromCode(int32_t string_idx,
-                                                    Thread* self)
+extern "C" mirror::String* artResolveStringFromCode(int32_t string_idx, Thread* self)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   ScopedQuickEntrypointChecks sqec(self);
   auto* caller = GetCalleeSaveMethodCaller(self, Runtime::kRefsOnly);
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index 2e7e2df..345b0ad 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -294,8 +294,13 @@
   static mirror::ArtMethod* GetCallingMethod(StackReference<mirror::ArtMethod>* sp)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     DCHECK(sp->AsMirrorPtr()->IsCalleeSaveMethod());
-    uint8_t* previous_sp = reinterpret_cast<uint8_t*>(sp) + kQuickCalleeSaveFrame_RefAndArgs_FrameSize;
-    return reinterpret_cast<StackReference<mirror::ArtMethod>*>(previous_sp)->AsMirrorPtr();
+    return GetCalleeSaveMethodCaller(sp, Runtime::kRefsAndArgs);
+  }
+
+  static uint32_t GetCallingDexPc(StackReference<mirror::ArtMethod>* sp)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    DCHECK(sp->AsMirrorPtr()->IsCalleeSaveMethod());
+    return GetCallingMethod(sp)->ToDexPc(QuickArgumentVisitor::GetCallingPc(sp));
   }
 
   // For the given quick ref and args quick frame, return the caller's PC.
@@ -827,12 +832,13 @@
 
   // Compute details about the called method (avoid GCs)
   ClassLinker* linker = Runtime::Current()->GetClassLinker();
-  mirror::ArtMethod* caller = QuickArgumentVisitor::GetCallingMethod(sp);
   InvokeType invoke_type;
   MethodReference called_method(nullptr, 0);
   const bool called_method_known_on_entry = !called->IsRuntimeMethod();
+  mirror::ArtMethod* caller = nullptr;
   if (!called_method_known_on_entry) {
-    uint32_t dex_pc = caller->ToDexPc(QuickArgumentVisitor::GetCallingPc(sp));
+    caller = QuickArgumentVisitor::GetCallingMethod(sp);
+    uint32_t dex_pc = QuickArgumentVisitor::GetCallingDexPc(sp);
     const DexFile::CodeItem* code;
     called_method.dex_file = caller->GetDexFile();
     code = caller->GetCodeItem();
@@ -1946,16 +1952,13 @@
 // to hold the mutator lock (see SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) annotations).
 
 template<InvokeType type, bool access_check>
-static TwoWordReturn artInvokeCommon(uint32_t method_idx, mirror::Object* this_object,
-                                     mirror::ArtMethod* caller_method,
-                                     Thread* self, StackReference<mirror::ArtMethod>* sp);
-
-template<InvokeType type, bool access_check>
-static TwoWordReturn artInvokeCommon(uint32_t method_idx, mirror::Object* this_object,
-                                     mirror::ArtMethod* caller_method,
-                                     Thread* self, StackReference<mirror::ArtMethod>* sp) {
+static TwoWordReturn artInvokeCommon(uint32_t method_idx,
+                                     mirror::Object* this_object,
+                                     Thread* self,
+                                     StackReference<mirror::ArtMethod>* sp) {
   ScopedQuickEntrypointChecks sqec(self);
   DCHECK_EQ(sp->AsMirrorPtr(), Runtime::Current()->GetCalleeSaveMethod(Runtime::kRefsAndArgs));
+  mirror::ArtMethod* caller_method = QuickArgumentVisitor::GetCallingMethod(sp);
   mirror::ArtMethod* method = FindMethodFast(method_idx, this_object, caller_method, access_check,
                                              type);
   if (UNLIKELY(method == nullptr)) {
@@ -1994,7 +1997,6 @@
   template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)                                          \
   TwoWordReturn artInvokeCommon<type, access_check>(uint32_t method_idx,                        \
                                                     mirror::Object* this_object,                \
-                                                    mirror::ArtMethod* caller_method,           \
                                                     Thread* self,                               \
                                                     StackReference<mirror::ArtMethod>* sp)      \
 
@@ -2012,58 +2014,58 @@
 
 // See comments in runtime_support_asm.S
 extern "C" TwoWordReturn artInvokeInterfaceTrampolineWithAccessCheck(
-    uint32_t method_idx, mirror::Object* this_object,
-    mirror::ArtMethod* caller_method, Thread* self,
+    uint32_t method_idx,
+    mirror::Object* this_object,
+    Thread* self,
     StackReference<mirror::ArtMethod>* sp)
         SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  return artInvokeCommon<kInterface, true>(method_idx, this_object,
-                                           caller_method, self, sp);
+  return artInvokeCommon<kInterface, true>(method_idx, this_object, self, sp);
 }
 
 extern "C" TwoWordReturn artInvokeDirectTrampolineWithAccessCheck(
-    uint32_t method_idx, mirror::Object* this_object,
-    mirror::ArtMethod* caller_method, Thread* self,
+    uint32_t method_idx,
+    mirror::Object* this_object,
+    Thread* self,
     StackReference<mirror::ArtMethod>* sp)
         SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  return artInvokeCommon<kDirect, true>(method_idx, this_object, caller_method,
-                                        self, sp);
+  return artInvokeCommon<kDirect, true>(method_idx, this_object, self, sp);
 }
 
 extern "C" TwoWordReturn artInvokeStaticTrampolineWithAccessCheck(
-    uint32_t method_idx, mirror::Object* this_object,
-    mirror::ArtMethod* caller_method, Thread* self,
+    uint32_t method_idx,
+    mirror::Object* this_object,
+    Thread* self,
     StackReference<mirror::ArtMethod>* sp)
         SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  return artInvokeCommon<kStatic, true>(method_idx, this_object, caller_method,
-                                        self, sp);
+  return artInvokeCommon<kStatic, true>(method_idx, this_object, self, sp);
 }
 
 extern "C" TwoWordReturn artInvokeSuperTrampolineWithAccessCheck(
-    uint32_t method_idx, mirror::Object* this_object,
-    mirror::ArtMethod* caller_method, Thread* self,
+    uint32_t method_idx,
+    mirror::Object* this_object,
+    Thread* self,
     StackReference<mirror::ArtMethod>* sp)
         SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  return artInvokeCommon<kSuper, true>(method_idx, this_object, caller_method,
-                                       self, sp);
+  return artInvokeCommon<kSuper, true>(method_idx, this_object, self, sp);
 }
 
 extern "C" TwoWordReturn artInvokeVirtualTrampolineWithAccessCheck(
-    uint32_t method_idx, mirror::Object* this_object,
-    mirror::ArtMethod* caller_method, Thread* self,
+    uint32_t method_idx,
+    mirror::Object* this_object,
+    Thread* self,
     StackReference<mirror::ArtMethod>* sp)
         SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  return artInvokeCommon<kVirtual, true>(method_idx, this_object, caller_method,
-                                         self, sp);
+  return artInvokeCommon<kVirtual, true>(method_idx, this_object, self, sp);
 }
 
 // Determine target of interface dispatch. This object is known non-null.
 extern "C" TwoWordReturn artInvokeInterfaceTrampoline(mirror::ArtMethod* interface_method,
                                                       mirror::Object* this_object,
-                                                      mirror::ArtMethod* caller_method,
                                                       Thread* self,
                                                       StackReference<mirror::ArtMethod>* sp)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   ScopedQuickEntrypointChecks sqec(self);
+  mirror::ArtMethod* caller_method = QuickArgumentVisitor::GetCallingMethod(sp);
   mirror::ArtMethod* method;
   if (LIKELY(interface_method->GetDexMethodIndex() != DexFile::kDexNoIndex)) {
     method = this_object->GetClass()->FindVirtualMethodForInterface(interface_method);
@@ -2075,12 +2077,7 @@
   } else {
     DCHECK(interface_method == Runtime::Current()->GetResolutionMethod());
 
-    // Find the caller PC.
-    constexpr size_t pc_offset = GetCalleeSaveReturnPcOffset(kRuntimeISA, Runtime::kRefsAndArgs);
-    uintptr_t caller_pc = *reinterpret_cast<uintptr_t*>(reinterpret_cast<uint8_t*>(sp) + pc_offset);
-
-    // Map the caller PC to a dex PC.
-    uint32_t dex_pc = caller_method->ToDexPc(caller_pc);
+    uint32_t dex_pc = QuickArgumentVisitor::GetCallingDexPc(sp);
     const DexFile::CodeItem* code = caller_method->GetCodeItem();
     CHECK_LT(dex_pc, code->insns_size_in_code_units_);
     const Instruction* instr = Instruction::At(&code->insns_[dex_pc]);
diff --git a/runtime/indirect_reference_table-inl.h b/runtime/indirect_reference_table-inl.h
index 639be51..39d850f 100644
--- a/runtime/indirect_reference_table-inl.h
+++ b/runtime/indirect_reference_table-inl.h
@@ -82,6 +82,15 @@
   return obj;
 }
 
+inline void IndirectReferenceTable::Update(IndirectRef iref, mirror::Object* obj) {
+  if (!GetChecked(iref)) {
+    LOG(WARNING) << "IndirectReferenceTable Update failed to find reference " << iref;
+    return;
+  }
+  uint32_t idx = ExtractIndex(iref);
+  table_[idx].SetReference(obj);
+}
+
 }  // namespace art
 
 #endif  // ART_RUNTIME_INDIRECT_REFERENCE_TABLE_INL_H_
diff --git a/runtime/indirect_reference_table.h b/runtime/indirect_reference_table.h
index a0e53af..dea5dfd 100644
--- a/runtime/indirect_reference_table.h
+++ b/runtime/indirect_reference_table.h
@@ -213,6 +213,10 @@
   uint32_t GetSerial() const {
     return serial_;
   }
+  void SetReference(mirror::Object* obj) {
+    DCHECK_LT(serial_, kIRTPrevCount);
+    references_[serial_] = GcRoot<mirror::Object>(obj);
+  }
 
  private:
   uint32_t serial_;
@@ -294,6 +298,13 @@
   }
 
   /*
+   * Update an existing entry.
+   *
+   * Updates an existing indirect reference to point to a new object.
+   */
+  void Update(IndirectRef iref, mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  /*
    * Remove an existing entry.
    *
    * If the entry is not between the current top index and the bottom index
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index a37aee5..26860e7 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -386,7 +386,7 @@
     // references pointers due to moving GC.
     args = shadow_frame->GetVRegArgs(method->IsStatic() ? 0 : 1);
     if (!Runtime::Current()->IsStarted()) {
-      UnstartedRuntimeJni(self, method, receiver, args, result);
+      UnstartedRuntime::Jni(self, method, receiver, args, result);
     } else {
       InterpreterJni(self, method, shorty, receiver, args, result);
     }
@@ -474,7 +474,7 @@
     CHECK(!Runtime::Current()->IsStarted());
     Object* receiver = is_static ? nullptr : shadow_frame->GetVRegReference(0);
     uint32_t* args = shadow_frame->GetVRegArgs(is_static ? 0 : 1);
-    UnstartedRuntimeJni(self, shadow_frame->GetMethod(), receiver, args, result);
+    UnstartedRuntime::Jni(self, shadow_frame->GetMethod(), receiver, args, result);
   }
 
   self->PopShadowFrame();
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 59d3008..363c65a 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -659,7 +659,7 @@
     }
     entry(self, code_item, new_shadow_frame, result);
   } else {
-    UnstartedRuntimeInvoke(self, code_item, new_shadow_frame, result, first_dest_reg);
+    UnstartedRuntime::Invoke(self, code_item, new_shadow_frame, result, first_dest_reg);
   }
 
   if (string_init && !self->IsExceptionPending()) {
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index 317106b..738e52b 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -120,7 +120,7 @@
   return param->AsString();
 }
 
-static void UnstartedClassForName(
+void UnstartedRuntime::UnstartedClassForName(
     Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   mirror::String* class_name = GetClassName(self, shadow_frame, arg_offset);
@@ -134,7 +134,7 @@
   CheckExceptionGenerateClassNotFound(self);
 }
 
-static void UnstartedClassForNameLong(
+void UnstartedRuntime::UnstartedClassForNameLong(
     Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   mirror::String* class_name = GetClassName(self, shadow_frame, arg_offset);
@@ -152,7 +152,7 @@
   CheckExceptionGenerateClassNotFound(self);
 }
 
-static void UnstartedClassClassForName(
+void UnstartedRuntime::UnstartedClassClassForName(
     Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   mirror::String* class_name = GetClassName(self, shadow_frame, arg_offset);
@@ -170,7 +170,7 @@
   CheckExceptionGenerateClassNotFound(self);
 }
 
-static void UnstartedClassNewInstance(
+void UnstartedRuntime::UnstartedClassNewInstance(
     Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   StackHandleScope<3> hs(self);  // Class, constructor, object.
@@ -226,7 +226,7 @@
   }
 }
 
-static void UnstartedClassGetDeclaredField(
+void UnstartedRuntime::UnstartedClassGetDeclaredField(
     Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   // Special managed code cut-out to allow field lookup in a un-started runtime that'd fail
@@ -265,7 +265,7 @@
   }
 }
 
-static void UnstartedVmClassLoaderFindLoadedClass(
+void UnstartedRuntime::UnstartedVmClassLoaderFindLoadedClass(
     Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   mirror::String* class_name = shadow_frame->GetVRegReference(arg_offset + 1)->AsString();
@@ -286,7 +286,7 @@
   }
 }
 
-static void UnstartedVoidLookupType(Thread* self ATTRIBUTE_UNUSED,
+void UnstartedRuntime::UnstartedVoidLookupType(Thread* self ATTRIBUTE_UNUSED,
                                     ShadowFrame* shadow_frame ATTRIBUTE_UNUSED,
                                     JValue* result,
                                     size_t arg_offset ATTRIBUTE_UNUSED)
@@ -323,7 +323,7 @@
   }
 }
 
-static void UnstartedSystemArraycopy(
+void UnstartedRuntime::UnstartedSystemArraycopy(
     Thread* self, ShadowFrame* shadow_frame, JValue* result ATTRIBUTE_UNUSED, size_t arg_offset)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   // Special case array copying without initializing System.
@@ -409,7 +409,21 @@
   }
 }
 
-static void UnstartedThreadLocalGet(
+void UnstartedRuntime::UnstartedSystemArraycopyChar(
+    Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  // Just forward.
+  UnstartedRuntime::UnstartedSystemArraycopy(self, shadow_frame, result, arg_offset);
+}
+
+void UnstartedRuntime::UnstartedSystemArraycopyInt(
+    Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  // Just forward.
+  UnstartedRuntime::UnstartedSystemArraycopy(self, shadow_frame, result, arg_offset);
+}
+
+void UnstartedRuntime::UnstartedThreadLocalGet(
     Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset ATTRIBUTE_UNUSED)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   std::string caller(PrettyMethod(shadow_frame->GetLink()->GetMethod()));
@@ -459,7 +473,7 @@
   }
 }
 
-static void UnstartedMathCeil(
+void UnstartedRuntime::UnstartedMathCeil(
     Thread* self ATTRIBUTE_UNUSED, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
   double in = shadow_frame->GetVRegDouble(arg_offset);
   double out;
@@ -474,21 +488,21 @@
   result->SetD(out);
 }
 
-static void UnstartedArtMethodGetMethodName(
+void UnstartedRuntime::UnstartedArtMethodGetMethodName(
     Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   mirror::ArtMethod* method = shadow_frame->GetVRegReference(arg_offset)->AsArtMethod();
   result->SetL(method->GetNameAsString(self));
 }
 
-static void UnstartedObjectHashCode(
+void UnstartedRuntime::UnstartedObjectHashCode(
     Thread* self ATTRIBUTE_UNUSED, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   mirror::Object* obj = shadow_frame->GetVRegReference(arg_offset);
   result->SetI(obj->IdentityHashCode());
 }
 
-static void UnstartedDoubleDoubleToRawLongBits(
+void UnstartedRuntime::UnstartedDoubleDoubleToRawLongBits(
     Thread* self ATTRIBUTE_UNUSED, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
   double in = shadow_frame->GetVRegDouble(arg_offset);
   result->SetJ(bit_cast<int64_t, double>(in));
@@ -522,7 +536,7 @@
   return self->DecodeJObject(dex.get());
 }
 
-static void UnstartedDexCacheGetDexNative(
+void UnstartedRuntime::UnstartedDexCacheGetDexNative(
     Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   // We will create the Dex object, but the image writer will release it before creating the
@@ -555,17 +569,20 @@
     }
 
     case Primitive::kPrimShort: {
-      result->SetS(*reinterpret_cast<int16_t*>(static_cast<intptr_t>(address)));
+      typedef int16_t unaligned_short __attribute__ ((aligned (1)));
+      result->SetS(*reinterpret_cast<unaligned_short*>(static_cast<intptr_t>(address)));
       return;
     }
 
     case Primitive::kPrimInt: {
-      result->SetI(*reinterpret_cast<int32_t*>(static_cast<intptr_t>(address)));
+      typedef int32_t unaligned_int __attribute__ ((aligned (1)));
+      result->SetI(*reinterpret_cast<unaligned_int*>(static_cast<intptr_t>(address)));
       return;
     }
 
     case Primitive::kPrimLong: {
-      result->SetJ(*reinterpret_cast<int64_t*>(static_cast<intptr_t>(address)));
+      typedef int64_t unaligned_long __attribute__ ((aligned (1)));
+      result->SetJ(*reinterpret_cast<unaligned_long*>(static_cast<intptr_t>(address)));
       return;
     }
 
@@ -582,22 +599,28 @@
   UNREACHABLE();
 }
 
-static void UnstartedMemoryPeekEntry(
+void UnstartedRuntime::UnstartedMemoryPeekByte(
     Thread* self ATTRIBUTE_UNUSED, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  std::string name(PrettyMethod(shadow_frame->GetMethod()));
-  if (name == "byte libcore.io.Memory.peekByte(long)") {
-    UnstartedMemoryPeek(Primitive::kPrimByte, shadow_frame, result, arg_offset);
-  } else if (name == "short libcore.io.Memory.peekShortNative(long)") {
-    UnstartedMemoryPeek(Primitive::kPrimShort, shadow_frame, result, arg_offset);
-  } else if (name == "int libcore.io.Memory.peekIntNative(long)") {
-    UnstartedMemoryPeek(Primitive::kPrimInt, shadow_frame, result, arg_offset);
-  } else if (name == "long libcore.io.Memory.peekLongNative(long)") {
-    UnstartedMemoryPeek(Primitive::kPrimLong, shadow_frame, result, arg_offset);
-  } else {
-    LOG(FATAL) << "Unsupported Memory.peek entry: " << name;
-    UNREACHABLE();
-  }
+  UnstartedMemoryPeek(Primitive::kPrimByte, shadow_frame, result, arg_offset);
+}
+
+void UnstartedRuntime::UnstartedMemoryPeekShort(
+    Thread* self ATTRIBUTE_UNUSED, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  UnstartedMemoryPeek(Primitive::kPrimShort, shadow_frame, result, arg_offset);
+}
+
+void UnstartedRuntime::UnstartedMemoryPeekInt(
+    Thread* self ATTRIBUTE_UNUSED, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  UnstartedMemoryPeek(Primitive::kPrimInt, shadow_frame, result, arg_offset);
+}
+
+void UnstartedRuntime::UnstartedMemoryPeekLong(
+    Thread* self ATTRIBUTE_UNUSED, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  UnstartedMemoryPeek(Primitive::kPrimLong, shadow_frame, result, arg_offset);
 }
 
 static void UnstartedMemoryPeekArray(
@@ -649,20 +672,14 @@
   UNREACHABLE();
 }
 
-static void UnstartedMemoryPeekArrayEntry(
+void UnstartedRuntime::UnstartedMemoryPeekByteArray(
     Thread* self, ShadowFrame* shadow_frame, JValue* result ATTRIBUTE_UNUSED, size_t arg_offset)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  std::string name(PrettyMethod(shadow_frame->GetMethod()));
-  if (name == "void libcore.io.Memory.peekByteArray(long, byte[], int, int)") {
-    UnstartedMemoryPeekArray(Primitive::kPrimByte, self, shadow_frame, arg_offset);
-  } else {
-    LOG(FATAL) << "Unsupported Memory.peekArray entry: " << name;
-    UNREACHABLE();
-  }
+  UnstartedMemoryPeekArray(Primitive::kPrimByte, self, shadow_frame, arg_offset);
 }
 
 // This allows reading security.properties in an unstarted runtime and initialize Security.
-static void UnstartedSecurityGetSecurityPropertiesReader(
+void UnstartedRuntime::UnstartedSecurityGetSecurityPropertiesReader(
     Thread* self,
     ShadowFrame* shadow_frame ATTRIBUTE_UNUSED,
     JValue* result,
@@ -756,7 +773,7 @@
 }
 
 // This allows reading the new style of String objects during compilation.
-static void UnstartedStringGetCharsNoCheck(
+void UnstartedRuntime::UnstartedStringGetCharsNoCheck(
     Thread* self, ShadowFrame* shadow_frame, JValue* result ATTRIBUTE_UNUSED, size_t arg_offset)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   jint start = shadow_frame->GetVReg(arg_offset + 1);
@@ -777,7 +794,7 @@
 }
 
 // This allows reading chars from the new style of String objects during compilation.
-static void UnstartedStringCharAt(
+void UnstartedRuntime::UnstartedStringCharAt(
     Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   jint index = shadow_frame->GetVReg(arg_offset + 1);
@@ -790,7 +807,7 @@
 }
 
 // This allows setting chars from the new style of String objects during compilation.
-static void UnstartedStringSetCharAt(
+void UnstartedRuntime::UnstartedStringSetCharAt(
     Thread* self, ShadowFrame* shadow_frame, JValue* result ATTRIBUTE_UNUSED, size_t arg_offset)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   jint index = shadow_frame->GetVReg(arg_offset + 1);
@@ -804,7 +821,7 @@
 }
 
 // This allows creating the new style of String objects during compilation.
-static void UnstartedStringFactoryNewStringFromChars(
+void UnstartedRuntime::UnstartedStringFactoryNewStringFromChars(
     Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   jint offset = shadow_frame->GetVReg(arg_offset);
@@ -818,7 +835,7 @@
 }
 
 // This allows creating the new style of String objects during compilation.
-static void UnstartedStringFactoryNewStringFromString(
+void UnstartedRuntime::UnstartedStringFactoryNewStringFromString(
     Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   mirror::String* to_copy = shadow_frame->GetVRegReference(arg_offset)->AsString();
@@ -834,8 +851,7 @@
                                                      allocator));
 }
 
-// This allows creating the new style of String objects during compilation.
-static void UnstartedStringFastSubstring(
+void UnstartedRuntime::UnstartedStringFastSubstring(
     Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   jint start = shadow_frame->GetVReg(arg_offset + 1);
@@ -852,7 +868,7 @@
 }
 
 // This allows getting the char array for new style of String objects during compilation.
-static void UnstartedStringToCharArray(
+void UnstartedRuntime::UnstartedStringToCharArray(
     Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   mirror::String* string = shadow_frame->GetVRegReference(arg_offset)->AsString();
@@ -863,7 +879,7 @@
   result->SetL(string->ToCharArray(self));
 }
 
-static void UnstartedJNIVMRuntimeNewUnpaddedArray(Thread* self,
+void UnstartedRuntime::UnstartedJNIVMRuntimeNewUnpaddedArray(Thread* self,
                                                   mirror::ArtMethod* method ATTRIBUTE_UNUSED,
                                                   mirror::Object* receiver ATTRIBUTE_UNUSED,
                                                   uint32_t* args,
@@ -880,7 +896,7 @@
                                                 array_class->GetComponentSizeShift(), allocator));
 }
 
-static void UnstartedJNIVMStackGetCallingClassLoader(Thread* self ATTRIBUTE_UNUSED,
+void UnstartedRuntime::UnstartedJNIVMStackGetCallingClassLoader(Thread* self ATTRIBUTE_UNUSED,
                                                      mirror::ArtMethod* method ATTRIBUTE_UNUSED,
                                                      mirror::Object* receiver ATTRIBUTE_UNUSED,
                                                      uint32_t* args ATTRIBUTE_UNUSED,
@@ -888,7 +904,7 @@
   result->SetL(nullptr);
 }
 
-static void UnstartedJNIVMStackGetStackClass2(Thread* self,
+void UnstartedRuntime::UnstartedJNIVMStackGetStackClass2(Thread* self,
                                               mirror::ArtMethod* method ATTRIBUTE_UNUSED,
                                               mirror::Object* receiver ATTRIBUTE_UNUSED,
                                               uint32_t* args ATTRIBUTE_UNUSED,
@@ -901,7 +917,7 @@
   }
 }
 
-static void UnstartedJNIMathLog(Thread* self ATTRIBUTE_UNUSED,
+void UnstartedRuntime::UnstartedJNIMathLog(Thread* self ATTRIBUTE_UNUSED,
                                 mirror::ArtMethod* method ATTRIBUTE_UNUSED,
                                 mirror::Object* receiver ATTRIBUTE_UNUSED,
                                 uint32_t* args,
@@ -911,7 +927,7 @@
   result->SetD(log(value.GetD()));
 }
 
-static void UnstartedJNIMathExp(Thread* self ATTRIBUTE_UNUSED,
+void UnstartedRuntime::UnstartedJNIMathExp(Thread* self ATTRIBUTE_UNUSED,
                                 mirror::ArtMethod* method ATTRIBUTE_UNUSED,
                                 mirror::Object* receiver ATTRIBUTE_UNUSED,
                                 uint32_t* args,
@@ -921,7 +937,7 @@
   result->SetD(exp(value.GetD()));
 }
 
-static void UnstartedJNIClassGetNameNative(Thread* self,
+void UnstartedRuntime::UnstartedJNIClassGetNameNative(Thread* self,
                                            mirror::ArtMethod* method ATTRIBUTE_UNUSED,
                                            mirror::Object* receiver,
                                            uint32_t* args ATTRIBUTE_UNUSED,
@@ -931,7 +947,7 @@
   result->SetL(mirror::Class::ComputeName(hs.NewHandle(receiver->AsClass())));
 }
 
-static void UnstartedJNIFloatFloatToRawIntBits(Thread* self ATTRIBUTE_UNUSED,
+void UnstartedRuntime::UnstartedJNIFloatFloatToRawIntBits(Thread* self ATTRIBUTE_UNUSED,
                                                mirror::ArtMethod* method ATTRIBUTE_UNUSED,
                                                mirror::Object* receiver ATTRIBUTE_UNUSED,
                                                uint32_t* args,
@@ -939,7 +955,7 @@
   result->SetI(args[0]);
 }
 
-static void UnstartedJNIFloatIntBitsToFloat(Thread* self ATTRIBUTE_UNUSED,
+void UnstartedRuntime::UnstartedJNIFloatIntBitsToFloat(Thread* self ATTRIBUTE_UNUSED,
                                             mirror::ArtMethod* method ATTRIBUTE_UNUSED,
                                             mirror::Object* receiver ATTRIBUTE_UNUSED,
                                             uint32_t* args,
@@ -947,7 +963,7 @@
   result->SetI(args[0]);
 }
 
-static void UnstartedJNIObjectInternalClone(Thread* self,
+void UnstartedRuntime::UnstartedJNIObjectInternalClone(Thread* self,
                                             mirror::ArtMethod* method ATTRIBUTE_UNUSED,
                                             mirror::Object* receiver,
                                             uint32_t* args ATTRIBUTE_UNUSED,
@@ -956,7 +972,7 @@
   result->SetL(receiver->Clone(self));
 }
 
-static void UnstartedJNIObjectNotifyAll(Thread* self,
+void UnstartedRuntime::UnstartedJNIObjectNotifyAll(Thread* self,
                                         mirror::ArtMethod* method ATTRIBUTE_UNUSED,
                                         mirror::Object* receiver,
                                         uint32_t* args ATTRIBUTE_UNUSED,
@@ -965,7 +981,7 @@
   receiver->NotifyAll(self);
 }
 
-static void UnstartedJNIStringCompareTo(Thread* self,
+void UnstartedRuntime::UnstartedJNIStringCompareTo(Thread* self,
                                         mirror::ArtMethod* method ATTRIBUTE_UNUSED,
                                         mirror::Object* receiver,
                                         uint32_t* args,
@@ -978,7 +994,7 @@
   result->SetI(receiver->AsString()->CompareTo(rhs));
 }
 
-static void UnstartedJNIStringIntern(Thread* self ATTRIBUTE_UNUSED,
+void UnstartedRuntime::UnstartedJNIStringIntern(Thread* self ATTRIBUTE_UNUSED,
                                      mirror::ArtMethod* method ATTRIBUTE_UNUSED,
                                      mirror::Object* receiver,
                                      uint32_t* args ATTRIBUTE_UNUSED,
@@ -987,7 +1003,7 @@
   result->SetL(receiver->AsString()->Intern());
 }
 
-static void UnstartedJNIStringFastIndexOf(Thread* self ATTRIBUTE_UNUSED,
+void UnstartedRuntime::UnstartedJNIStringFastIndexOf(Thread* self ATTRIBUTE_UNUSED,
                                           mirror::ArtMethod* method ATTRIBUTE_UNUSED,
                                           mirror::Object* receiver,
                                           uint32_t* args,
@@ -996,7 +1012,7 @@
   result->SetI(receiver->AsString()->FastIndexOf(args[0], args[1]));
 }
 
-static void UnstartedJNIArrayCreateMultiArray(Thread* self,
+void UnstartedRuntime::UnstartedJNIArrayCreateMultiArray(Thread* self,
                                               mirror::ArtMethod* method ATTRIBUTE_UNUSED,
                                               mirror::Object* receiver ATTRIBUTE_UNUSED,
                                               uint32_t* args,
@@ -1008,7 +1024,7 @@
   result->SetL(mirror::Array::CreateMultiArray(self, h_class, h_dimensions));
 }
 
-static void UnstartedJNIArrayCreateObjectArray(Thread* self,
+void UnstartedRuntime::UnstartedJNIArrayCreateObjectArray(Thread* self,
                                                mirror::ArtMethod* method ATTRIBUTE_UNUSED,
                                                mirror::Object* receiver ATTRIBUTE_UNUSED,
                                                uint32_t* args,
@@ -1033,7 +1049,7 @@
   result->SetL(new_array);
 }
 
-static void UnstartedJNIThrowableNativeFillInStackTrace(Thread* self,
+void UnstartedRuntime::UnstartedJNIThrowableNativeFillInStackTrace(Thread* self,
                                                         mirror::ArtMethod* method ATTRIBUTE_UNUSED,
                                                         mirror::Object* receiver ATTRIBUTE_UNUSED,
                                                         uint32_t* args ATTRIBUTE_UNUSED,
@@ -1047,7 +1063,7 @@
   }
 }
 
-static void UnstartedJNISystemIdentityHashCode(Thread* self ATTRIBUTE_UNUSED,
+void UnstartedRuntime::UnstartedJNISystemIdentityHashCode(Thread* self ATTRIBUTE_UNUSED,
                                                mirror::ArtMethod* method ATTRIBUTE_UNUSED,
                                                mirror::Object* receiver ATTRIBUTE_UNUSED,
                                                uint32_t* args,
@@ -1057,7 +1073,7 @@
   result->SetI((obj != nullptr) ? obj->IdentityHashCode() : 0);
 }
 
-static void UnstartedJNIByteOrderIsLittleEndian(Thread* self ATTRIBUTE_UNUSED,
+void UnstartedRuntime::UnstartedJNIByteOrderIsLittleEndian(Thread* self ATTRIBUTE_UNUSED,
                                                 mirror::ArtMethod* method ATTRIBUTE_UNUSED,
                                                 mirror::Object* receiver ATTRIBUTE_UNUSED,
                                                 uint32_t* args ATTRIBUTE_UNUSED,
@@ -1065,7 +1081,7 @@
   result->SetZ(JNI_TRUE);
 }
 
-static void UnstartedJNIUnsafeCompareAndSwapInt(Thread* self ATTRIBUTE_UNUSED,
+void UnstartedRuntime::UnstartedJNIUnsafeCompareAndSwapInt(Thread* self ATTRIBUTE_UNUSED,
                                                 mirror::ArtMethod* method ATTRIBUTE_UNUSED,
                                                 mirror::Object* receiver ATTRIBUTE_UNUSED,
                                                 uint32_t* args,
@@ -1086,7 +1102,7 @@
   result->SetZ(success ? JNI_TRUE : JNI_FALSE);
 }
 
-static void UnstartedJNIUnsafePutObject(Thread* self ATTRIBUTE_UNUSED,
+void UnstartedRuntime::UnstartedJNIUnsafePutObject(Thread* self ATTRIBUTE_UNUSED,
                                         mirror::ArtMethod* method ATTRIBUTE_UNUSED,
                                         mirror::Object* receiver ATTRIBUTE_UNUSED,
                                         uint32_t* args,
@@ -1102,7 +1118,7 @@
   }
 }
 
-static void UnstartedJNIUnsafeGetArrayBaseOffsetForComponentType(
+void UnstartedRuntime::UnstartedJNIUnsafeGetArrayBaseOffsetForComponentType(
     Thread* self ATTRIBUTE_UNUSED,
     mirror::ArtMethod* method ATTRIBUTE_UNUSED,
     mirror::Object* receiver ATTRIBUTE_UNUSED,
@@ -1114,7 +1130,7 @@
   result->SetI(mirror::Array::DataOffset(Primitive::ComponentSize(primitive_type)).Int32Value());
 }
 
-static void UnstartedJNIUnsafeGetArrayIndexScaleForComponentType(
+void UnstartedRuntime::UnstartedJNIUnsafeGetArrayIndexScaleForComponentType(
     Thread* self ATTRIBUTE_UNUSED,
     mirror::ArtMethod* method ATTRIBUTE_UNUSED,
     mirror::Object* receiver ATTRIBUTE_UNUSED,
@@ -1136,147 +1152,37 @@
 static std::unordered_map<std::string, InvokeHandler> invoke_handlers_;
 static std::unordered_map<std::string, JNIHandler> jni_handlers_;
 
-static void UnstartedRuntimeInitializeInvokeHandlers() {
-  struct InvokeHandlerDef {
-    std::string name;
-    InvokeHandler function;
-  };
-
-  InvokeHandlerDef defs[] {
-      { "java.lang.Class java.lang.Class.forName(java.lang.String)",
-          &UnstartedClassForName },
-      { "java.lang.Class java.lang.Class.forName(java.lang.String, boolean, java.lang.ClassLoader)",
-          &UnstartedClassForNameLong },
-      { "java.lang.Class java.lang.Class.classForName(java.lang.String, boolean, java.lang.ClassLoader)",
-          &UnstartedClassClassForName },
-      { "java.lang.Class java.lang.VMClassLoader.findLoadedClass(java.lang.ClassLoader, java.lang.String)",
-          &UnstartedVmClassLoaderFindLoadedClass },
-      { "java.lang.Class java.lang.Void.lookupType()",
-          &UnstartedVoidLookupType },
-      { "java.lang.Object java.lang.Class.newInstance()",
-          &UnstartedClassNewInstance },
-      { "java.lang.reflect.Field java.lang.Class.getDeclaredField(java.lang.String)",
-          &UnstartedClassGetDeclaredField },
-      { "int java.lang.Object.hashCode()",
-          &UnstartedObjectHashCode },
-      { "java.lang.String java.lang.reflect.ArtMethod.getMethodName(java.lang.reflect.ArtMethod)",
-          &UnstartedArtMethodGetMethodName },
-      { "void java.lang.System.arraycopy(java.lang.Object, int, java.lang.Object, int, int)",
-          &UnstartedSystemArraycopy},
-      { "void java.lang.System.arraycopy(char[], int, char[], int, int)",
-          &UnstartedSystemArraycopy },
-      { "void java.lang.System.arraycopy(int[], int, int[], int, int)",
-          &UnstartedSystemArraycopy },
-      { "long java.lang.Double.doubleToRawLongBits(double)",
-          &UnstartedDoubleDoubleToRawLongBits },
-      { "double java.lang.Math.ceil(double)",
-          &UnstartedMathCeil },
-      { "java.lang.Object java.lang.ThreadLocal.get()",
-          &UnstartedThreadLocalGet },
-      { "com.android.dex.Dex java.lang.DexCache.getDexNative()",
-          &UnstartedDexCacheGetDexNative },
-      { "byte libcore.io.Memory.peekByte(long)",
-          &UnstartedMemoryPeekEntry },
-      { "short libcore.io.Memory.peekShortNative(long)",
-          &UnstartedMemoryPeekEntry },
-      { "int libcore.io.Memory.peekIntNative(long)",
-          &UnstartedMemoryPeekEntry },
-      { "long libcore.io.Memory.peekLongNative(long)",
-          &UnstartedMemoryPeekEntry },
-      { "void libcore.io.Memory.peekByteArray(long, byte[], int, int)",
-          &UnstartedMemoryPeekArrayEntry },
-      { "java.io.Reader java.security.Security.getSecurityPropertiesReader()",
-          &UnstartedSecurityGetSecurityPropertiesReader },
-      { "void java.lang.String.getCharsNoCheck(int, int, char[], int)",
-          &UnstartedStringGetCharsNoCheck },
-      { "char java.lang.String.charAt(int)",
-          &UnstartedStringCharAt },
-      { "void java.lang.String.setCharAt(int, char)",
-          &UnstartedStringSetCharAt },
-      { "java.lang.String java.lang.StringFactory.newStringFromChars(int, int, char[])",
-          &UnstartedStringFactoryNewStringFromChars },
-      { "java.lang.String java.lang.StringFactory.newStringFromString(java.lang.String)",
-          &UnstartedStringFactoryNewStringFromString },
-      { "java.lang.String java.lang.String.fastSubstring(int, int)",
-          &UnstartedStringFastSubstring },
-      { "char[] java.lang.String.toCharArray()",
-          &UnstartedStringToCharArray },
-  };
-
-  for (auto& def : defs) {
-    invoke_handlers_.insert(std::make_pair(def.name, def.function));
-  }
+void UnstartedRuntime::InitializeInvokeHandlers() {
+#define UNSTARTED_DIRECT(ShortName, Sig) \
+  invoke_handlers_.insert(std::make_pair(Sig, & UnstartedRuntime::Unstarted ## ShortName));
+#include "unstarted_runtime_list.h"
+  UNSTARTED_RUNTIME_DIRECT_LIST(UNSTARTED_DIRECT)
+#undef UNSTARTED_RUNTIME_DIRECT_LIST
+#undef UNSTARTED_RUNTIME_JNI_LIST
+#undef UNSTARTED_DIRECT
 }
 
-static void UnstartedRuntimeInitializeJNIHandlers() {
-  struct JNIHandlerDef {
-    std::string name;
-    JNIHandler function;
-  };
-
-  JNIHandlerDef defs[] {
-      { "java.lang.Object dalvik.system.VMRuntime.newUnpaddedArray(java.lang.Class, int)",
-          &UnstartedJNIVMRuntimeNewUnpaddedArray },
-      { "java.lang.ClassLoader dalvik.system.VMStack.getCallingClassLoader()",
-          &UnstartedJNIVMStackGetCallingClassLoader },
-      { "java.lang.Class dalvik.system.VMStack.getStackClass2()",
-          &UnstartedJNIVMStackGetStackClass2 },
-      { "double java.lang.Math.log(double)",
-          &UnstartedJNIMathLog },
-      { "java.lang.String java.lang.Class.getNameNative()",
-          &UnstartedJNIClassGetNameNative },
-      { "int java.lang.Float.floatToRawIntBits(float)",
-          &UnstartedJNIFloatFloatToRawIntBits },
-      { "float java.lang.Float.intBitsToFloat(int)",
-          &UnstartedJNIFloatIntBitsToFloat },
-      { "double java.lang.Math.exp(double)",
-          &UnstartedJNIMathExp },
-      { "java.lang.Object java.lang.Object.internalClone()",
-          &UnstartedJNIObjectInternalClone },
-      { "void java.lang.Object.notifyAll()",
-          &UnstartedJNIObjectNotifyAll},
-      { "int java.lang.String.compareTo(java.lang.String)",
-          &UnstartedJNIStringCompareTo },
-      { "java.lang.String java.lang.String.intern()",
-          &UnstartedJNIStringIntern },
-      { "int java.lang.String.fastIndexOf(int, int)",
-          &UnstartedJNIStringFastIndexOf },
-      { "java.lang.Object java.lang.reflect.Array.createMultiArray(java.lang.Class, int[])",
-          &UnstartedJNIArrayCreateMultiArray },
-      { "java.lang.Object java.lang.reflect.Array.createObjectArray(java.lang.Class, int)",
-          &UnstartedJNIArrayCreateObjectArray },
-      { "java.lang.Object java.lang.Throwable.nativeFillInStackTrace()",
-          &UnstartedJNIThrowableNativeFillInStackTrace },
-      { "int java.lang.System.identityHashCode(java.lang.Object)",
-          &UnstartedJNISystemIdentityHashCode },
-      { "boolean java.nio.ByteOrder.isLittleEndian()",
-          &UnstartedJNIByteOrderIsLittleEndian },
-      { "boolean sun.misc.Unsafe.compareAndSwapInt(java.lang.Object, long, int, int)",
-          &UnstartedJNIUnsafeCompareAndSwapInt },
-      { "void sun.misc.Unsafe.putObject(java.lang.Object, long, java.lang.Object)",
-          &UnstartedJNIUnsafePutObject },
-      { "int sun.misc.Unsafe.getArrayBaseOffsetForComponentType(java.lang.Class)",
-          &UnstartedJNIUnsafeGetArrayBaseOffsetForComponentType },
-      { "int sun.misc.Unsafe.getArrayIndexScaleForComponentType(java.lang.Class)",
-          &UnstartedJNIUnsafeGetArrayIndexScaleForComponentType },
-  };
-
-  for (auto& def : defs) {
-    jni_handlers_.insert(std::make_pair(def.name, def.function));
-  }
+void UnstartedRuntime::InitializeJNIHandlers() {
+#define UNSTARTED_JNI(ShortName, Sig) \
+  jni_handlers_.insert(std::make_pair(Sig, & UnstartedRuntime::UnstartedJNI ## ShortName));
+#include "unstarted_runtime_list.h"
+  UNSTARTED_RUNTIME_JNI_LIST(UNSTARTED_JNI)
+#undef UNSTARTED_RUNTIME_DIRECT_LIST
+#undef UNSTARTED_RUNTIME_JNI_LIST
+#undef UNSTARTED_JNI
 }
 
-void UnstartedRuntimeInitialize() {
+void UnstartedRuntime::Initialize() {
   CHECK(!tables_initialized_);
 
-  UnstartedRuntimeInitializeInvokeHandlers();
-  UnstartedRuntimeInitializeJNIHandlers();
+  InitializeInvokeHandlers();
+  InitializeJNIHandlers();
 
   tables_initialized_ = true;
 }
 
-void UnstartedRuntimeInvoke(Thread* self, const DexFile::CodeItem* code_item,
-                            ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
+void UnstartedRuntime::Invoke(Thread* self, const DexFile::CodeItem* code_item,
+                              ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
   // In a runtime that's not started we intercept certain methods to avoid complicated dependency
   // problems in core libraries.
   CHECK(tables_initialized_);
@@ -1294,8 +1200,8 @@
 }
 
 // Hand select a number of methods to be run in a not yet started runtime without using JNI.
-void UnstartedRuntimeJni(Thread* self, mirror::ArtMethod* method, mirror::Object* receiver,
-                         uint32_t* args, JValue* result) {
+void UnstartedRuntime::Jni(Thread* self, mirror::ArtMethod* method, mirror::Object* receiver,
+                           uint32_t* args, JValue* result) {
   std::string name(PrettyMethod(method));
   const auto& iter = jni_handlers_.find(name);
   if (iter != jni_handlers_.end()) {
diff --git a/runtime/interpreter/unstarted_runtime.h b/runtime/interpreter/unstarted_runtime.h
index 2d7d380..a361af0 100644
--- a/runtime/interpreter/unstarted_runtime.h
+++ b/runtime/interpreter/unstarted_runtime.h
@@ -36,16 +36,69 @@
 
 namespace interpreter {
 
-void UnstartedRuntimeInitialize();
+// Support for an unstarted runtime. These are special handwritten implementations for select
+// libcore native and non-native methods so we can compile-time initialize classes in the boot
+// image.
+//
+// While it would technically be OK to only expose the public functions, a class was chosen to
+// wrap this so the actual implementations are exposed for testing. This is also why the private
+// methods are not documented here - they are not intended to be used directly except in
+// testing.
 
-void UnstartedRuntimeInvoke(Thread* self, const DexFile::CodeItem* code_item,
-                            ShadowFrame* shadow_frame,
-                            JValue* result, size_t arg_offset)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+class UnstartedRuntime {
+ public:
+  static void Initialize();
 
-void UnstartedRuntimeJni(Thread* self, mirror::ArtMethod* method, mirror::Object* receiver,
-                         uint32_t* args, JValue* result)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  static void Invoke(Thread* self,
+                     const DexFile::CodeItem* code_item,
+                     ShadowFrame* shadow_frame,
+                     JValue* result,
+                     size_t arg_offset)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  static void Jni(Thread* self,
+                  mirror::ArtMethod* method,
+                  mirror::Object* receiver,
+                  uint32_t* args,
+                  JValue* result)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ private:
+  // Methods that intercept available libcore implementations.
+#define UNSTARTED_DIRECT(ShortName, SigIgnored)                 \
+  static void Unstarted ## ShortName(Thread* self,              \
+                                     ShadowFrame* shadow_frame, \
+                                     JValue* result,            \
+                                     size_t arg_offset)         \
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+#include "unstarted_runtime_list.h"
+  UNSTARTED_RUNTIME_DIRECT_LIST(UNSTARTED_DIRECT)
+#undef UNSTARTED_RUNTIME_DIRECT_LIST
+#undef UNSTARTED_RUNTIME_JNI_LIST
+#undef UNSTARTED_DIRECT
+
+  // Methods that are native.
+#define UNSTARTED_JNI(ShortName, SigIgnored)                       \
+  static void UnstartedJNI ## ShortName(Thread* self,              \
+                                        mirror::ArtMethod* method, \
+                                        mirror::Object* receiver,  \
+                                        uint32_t* args,            \
+                                        JValue* result)            \
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+#include "unstarted_runtime_list.h"
+  UNSTARTED_RUNTIME_JNI_LIST(UNSTARTED_JNI)
+#undef UNSTARTED_RUNTIME_DIRECT_LIST
+#undef UNSTARTED_RUNTIME_JNI_LIST
+#undef UNSTARTED_JNI
+
+  static void InitializeInvokeHandlers();
+  static void InitializeJNIHandlers();
+
+  friend class UnstartedRuntimeTest;
+
+  DISALLOW_ALLOCATION();
+  DISALLOW_COPY_AND_ASSIGN(UnstartedRuntime);
+};
 
 }  // namespace interpreter
 }  // namespace art
diff --git a/runtime/interpreter/unstarted_runtime_list.h b/runtime/interpreter/unstarted_runtime_list.h
new file mode 100644
index 0000000..8f6014c
--- /dev/null
+++ b/runtime/interpreter/unstarted_runtime_list.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_INTERPRETER_UNSTARTED_RUNTIME_LIST_H_
+#define ART_RUNTIME_INTERPRETER_UNSTARTED_RUNTIME_LIST_H_
+
+// Methods that intercept available libcore implementations.
+#define UNSTARTED_RUNTIME_DIRECT_LIST(V)    \
+  V(ClassForName, "java.lang.Class java.lang.Class.forName(java.lang.String)") \
+  V(ClassForNameLong, "java.lang.Class java.lang.Class.forName(java.lang.String, boolean, java.lang.ClassLoader)") \
+  V(ClassClassForName, "java.lang.Class java.lang.Class.classForName(java.lang.String, boolean, java.lang.ClassLoader)") \
+  V(ClassNewInstance, "java.lang.Object java.lang.Class.newInstance()") \
+  V(ClassGetDeclaredField, "java.lang.reflect.Field java.lang.Class.getDeclaredField(java.lang.String)") \
+  V(VmClassLoaderFindLoadedClass, "java.lang.Class java.lang.VMClassLoader.findLoadedClass(java.lang.ClassLoader, java.lang.String)") \
+  V(VoidLookupType, "java.lang.Class java.lang.Void.lookupType()") \
+  V(SystemArraycopy, "void java.lang.System.arraycopy(java.lang.Object, int, java.lang.Object, int, int)") \
+  V(SystemArraycopyChar, "void java.lang.System.arraycopy(char[], int, char[], int, int)") \
+  V(SystemArraycopyInt, "void java.lang.System.arraycopy(int[], int, int[], int, int)") \
+  V(ThreadLocalGet, "java.lang.Object java.lang.ThreadLocal.get()") \
+  V(MathCeil, "double java.lang.Math.ceil(double)") \
+  V(ArtMethodGetMethodName, "java.lang.String java.lang.reflect.ArtMethod.getMethodName(java.lang.reflect.ArtMethod)") \
+  V(ObjectHashCode, "int java.lang.Object.hashCode()") \
+  V(DoubleDoubleToRawLongBits, "long java.lang.Double.doubleToRawLongBits(double)") \
+  V(DexCacheGetDexNative, "com.android.dex.Dex java.lang.DexCache.getDexNative()") \
+  V(MemoryPeekByte, "byte libcore.io.Memory.peekByte(long)") \
+  V(MemoryPeekShort, "short libcore.io.Memory.peekShortNative(long)") \
+  V(MemoryPeekInt, "int libcore.io.Memory.peekIntNative(long)") \
+  V(MemoryPeekLong, "long libcore.io.Memory.peekLongNative(long)") \
+  V(MemoryPeekByteArray, "void libcore.io.Memory.peekByteArray(long, byte[], int, int)") \
+  V(SecurityGetSecurityPropertiesReader, "java.io.Reader java.security.Security.getSecurityPropertiesReader()") \
+  V(StringGetCharsNoCheck, "void java.lang.String.getCharsNoCheck(int, int, char[], int)") \
+  V(StringCharAt, "char java.lang.String.charAt(int)") \
+  V(StringSetCharAt, "void java.lang.String.setCharAt(int, char)") \
+  V(StringFactoryNewStringFromChars, "java.lang.String java.lang.StringFactory.newStringFromChars(int, int, char[])") \
+  V(StringFactoryNewStringFromString, "java.lang.String java.lang.StringFactory.newStringFromString(java.lang.String)") \
+  V(StringFastSubstring, "java.lang.String java.lang.String.fastSubstring(int, int)") \
+  V(StringToCharArray, "char[] java.lang.String.toCharArray()")
+
+// Methods that are native.
+#define UNSTARTED_RUNTIME_JNI_LIST(V)           \
+  V(VMRuntimeNewUnpaddedArray, "java.lang.Object dalvik.system.VMRuntime.newUnpaddedArray(java.lang.Class, int)") \
+  V(VMStackGetCallingClassLoader, "java.lang.ClassLoader dalvik.system.VMStack.getCallingClassLoader()") \
+  V(VMStackGetStackClass2, "java.lang.Class dalvik.system.VMStack.getStackClass2()") \
+  V(MathLog, "double java.lang.Math.log(double)") \
+  V(MathExp, "double java.lang.Math.exp(double)") \
+  V(ClassGetNameNative, "java.lang.String java.lang.Class.getNameNative()") \
+  V(FloatFloatToRawIntBits, "int java.lang.Float.floatToRawIntBits(float)") \
+  V(FloatIntBitsToFloat, "float java.lang.Float.intBitsToFloat(int)") \
+  V(ObjectInternalClone, "java.lang.Object java.lang.Object.internalClone()") \
+  V(ObjectNotifyAll, "void java.lang.Object.notifyAll()") \
+  V(StringCompareTo, "int java.lang.String.compareTo(java.lang.String)") \
+  V(StringIntern, "java.lang.String java.lang.String.intern()") \
+  V(StringFastIndexOf, "int java.lang.String.fastIndexOf(int, int)") \
+  V(ArrayCreateMultiArray, "java.lang.Object java.lang.reflect.Array.createMultiArray(java.lang.Class, int[])") \
+  V(ArrayCreateObjectArray, "java.lang.Object java.lang.reflect.Array.createObjectArray(java.lang.Class, int)") \
+  V(ThrowableNativeFillInStackTrace, "java.lang.Object java.lang.Throwable.nativeFillInStackTrace()") \
+  V(SystemIdentityHashCode, "int java.lang.System.identityHashCode(java.lang.Object)") \
+  V(ByteOrderIsLittleEndian, "boolean java.nio.ByteOrder.isLittleEndian()") \
+  V(UnsafeCompareAndSwapInt, "boolean sun.misc.Unsafe.compareAndSwapInt(java.lang.Object, long, int, int)") \
+  V(UnsafePutObject, "void sun.misc.Unsafe.putObject(java.lang.Object, long, java.lang.Object)") \
+  V(UnsafeGetArrayBaseOffsetForComponentType, "int sun.misc.Unsafe.getArrayBaseOffsetForComponentType(java.lang.Class)") \
+  V(UnsafeGetArrayIndexScaleForComponentType, "int sun.misc.Unsafe.getArrayIndexScaleForComponentType(java.lang.Class)")
+
+#endif  // ART_RUNTIME_INTERPRETER_UNSTARTED_RUNTIME_LIST_H_
+// the guard in this file is just for cpplint
+#undef ART_RUNTIME_INTERPRETER_UNSTARTED_RUNTIME_LIST_H_
diff --git a/runtime/interpreter/unstarted_runtime_test.cc b/runtime/interpreter/unstarted_runtime_test.cc
new file mode 100644
index 0000000..34ab277
--- /dev/null
+++ b/runtime/interpreter/unstarted_runtime_test.cc
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "unstarted_runtime.h"
+
+#include "class_linker.h"
+#include "common_runtime_test.h"
+#include "handle.h"
+#include "handle_scope-inl.h"
+#include "mirror/class_loader.h"
+#include "mirror/string-inl.h"
+#include "runtime.h"
+#include "scoped_thread_state_change.h"
+#include "thread.h"
+
+namespace art {
+namespace interpreter {
+
+class UnstartedRuntimeTest : public CommonRuntimeTest {
+ protected:
+  // Re-expose all UnstartedRuntime implementations so we don't need to declare a million
+  // test friends.
+
+  // Methods that intercept available libcore implementations.
+#define UNSTARTED_DIRECT(Name, SigIgnored)                 \
+  static void Unstarted ## Name(Thread* self,              \
+                                ShadowFrame* shadow_frame, \
+                                JValue* result,            \
+                                size_t arg_offset)         \
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {        \
+    interpreter::UnstartedRuntime::Unstarted ## Name(self, shadow_frame, result, arg_offset); \
+  }
+#include "unstarted_runtime_list.h"
+  UNSTARTED_RUNTIME_DIRECT_LIST(UNSTARTED_DIRECT)
+#undef UNSTARTED_RUNTIME_DIRECT_LIST
+#undef UNSTARTED_RUNTIME_JNI_LIST
+#undef UNSTARTED_DIRECT
+
+  // Methods that are native.
+#define UNSTARTED_JNI(Name, SigIgnored)                                   \
+  static void UnstartedJNI ## Name(Thread* self,              \
+                                   mirror::ArtMethod* method, \
+                                   mirror::Object* receiver,  \
+                                   uint32_t* args,            \
+                                   JValue* result)            \
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {           \
+    interpreter::UnstartedRuntime::UnstartedJNI ## Name(self, method, receiver, args, result); \
+  }
+#include "unstarted_runtime_list.h"
+  UNSTARTED_RUNTIME_JNI_LIST(UNSTARTED_JNI)
+#undef UNSTARTED_RUNTIME_DIRECT_LIST
+#undef UNSTARTED_RUNTIME_JNI_LIST
+#undef UNSTARTED_JNI
+};
+
+TEST_F(UnstartedRuntimeTest, MemoryPeekByte) {
+  Thread* self = Thread::Current();
+
+  ScopedObjectAccess soa(self);
+  constexpr const uint8_t base_array[] = "abcdefghijklmnop";
+  constexpr int32_t kBaseLen = sizeof(base_array) / sizeof(uint8_t);
+  const uint8_t* base_ptr = base_array;
+
+  JValue result;
+  ShadowFrame* tmp = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0);
+
+  for (int32_t i = 0; i < kBaseLen; ++i) {
+    tmp->SetVRegLong(0, static_cast<int64_t>(reinterpret_cast<intptr_t>(base_ptr + i)));
+
+    UnstartedMemoryPeekByte(self, tmp, &result, 0);
+
+    EXPECT_EQ(result.GetB(), static_cast<int8_t>(base_array[i]));
+  }
+
+  ShadowFrame::DeleteDeoptimizedFrame(tmp);
+}
+
+TEST_F(UnstartedRuntimeTest, MemoryPeekShort) {
+  Thread* self = Thread::Current();
+
+  ScopedObjectAccess soa(self);
+  constexpr const uint8_t base_array[] = "abcdefghijklmnop";
+  constexpr int32_t kBaseLen = sizeof(base_array) / sizeof(uint8_t);
+  const uint8_t* base_ptr = base_array;
+
+  JValue result;
+  ShadowFrame* tmp = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0);
+
+  int32_t adjusted_length = kBaseLen - sizeof(int16_t);
+  for (int32_t i = 0; i < adjusted_length; ++i) {
+    tmp->SetVRegLong(0, static_cast<int64_t>(reinterpret_cast<intptr_t>(base_ptr + i)));
+
+    UnstartedMemoryPeekShort(self, tmp, &result, 0);
+
+    typedef int16_t unaligned_short __attribute__ ((aligned (1)));
+    const unaligned_short* short_ptr = reinterpret_cast<const unaligned_short*>(base_ptr + i);
+    EXPECT_EQ(result.GetS(), *short_ptr);
+  }
+
+  ShadowFrame::DeleteDeoptimizedFrame(tmp);
+}
+
+TEST_F(UnstartedRuntimeTest, MemoryPeekInt) {
+  Thread* self = Thread::Current();
+
+  ScopedObjectAccess soa(self);
+  constexpr const uint8_t base_array[] = "abcdefghijklmnop";
+  constexpr int32_t kBaseLen = sizeof(base_array) / sizeof(uint8_t);
+  const uint8_t* base_ptr = base_array;
+
+  JValue result;
+  ShadowFrame* tmp = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0);
+
+  int32_t adjusted_length = kBaseLen - sizeof(int32_t);
+  for (int32_t i = 0; i < adjusted_length; ++i) {
+    tmp->SetVRegLong(0, static_cast<int64_t>(reinterpret_cast<intptr_t>(base_ptr + i)));
+
+    UnstartedMemoryPeekInt(self, tmp, &result, 0);
+
+    typedef int32_t unaligned_int __attribute__ ((aligned (1)));
+    const unaligned_int* int_ptr = reinterpret_cast<const unaligned_int*>(base_ptr + i);
+    EXPECT_EQ(result.GetI(), *int_ptr);
+  }
+
+  ShadowFrame::DeleteDeoptimizedFrame(tmp);
+}
+
+TEST_F(UnstartedRuntimeTest, MemoryPeekLong) {
+  Thread* self = Thread::Current();
+
+  ScopedObjectAccess soa(self);
+  constexpr const uint8_t base_array[] = "abcdefghijklmnop";
+  constexpr int32_t kBaseLen = sizeof(base_array) / sizeof(uint8_t);
+  const uint8_t* base_ptr = base_array;
+
+  JValue result;
+  ShadowFrame* tmp = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0);
+
+  int32_t adjusted_length = kBaseLen - sizeof(int64_t);
+  for (int32_t i = 0; i < adjusted_length; ++i) {
+    tmp->SetVRegLong(0, static_cast<int64_t>(reinterpret_cast<intptr_t>(base_ptr + i)));
+
+    UnstartedMemoryPeekLong(self, tmp, &result, 0);
+
+    typedef int64_t unaligned_long __attribute__ ((aligned (1)));
+    const unaligned_long* long_ptr = reinterpret_cast<const unaligned_long*>(base_ptr + i);
+    EXPECT_EQ(result.GetJ(), *long_ptr);
+  }
+
+  ShadowFrame::DeleteDeoptimizedFrame(tmp);
+}
+
+TEST_F(UnstartedRuntimeTest, StringGetCharsNoCheck) {
+  Thread* self = Thread::Current();
+
+  ScopedObjectAccess soa(self);
+  StackHandleScope<2> hs(self);
+  // TODO: Actual UTF.
+  constexpr const char base_string[] = "abcdefghijklmnop";
+  Handle<mirror::String> h_test_string(hs.NewHandle(
+      mirror::String::AllocFromModifiedUtf8(self, base_string)));
+  constexpr int32_t kBaseLen = sizeof(base_string) / sizeof(char) - 1;
+  Handle<mirror::CharArray> h_char_array(hs.NewHandle(
+      mirror::CharArray::Alloc(self, kBaseLen)));
+  // A buffer so we can make sure we only modify the elements targetted.
+  uint16_t buf[kBaseLen];
+
+  JValue result;
+  ShadowFrame* tmp = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0);
+
+  for (int32_t start_index = 0; start_index < kBaseLen; ++start_index) {
+    for (int32_t count = 0; count <= kBaseLen; ++count) {
+      for (int32_t trg_offset = 0; trg_offset < kBaseLen; ++trg_offset) {
+        // Only do it when in bounds.
+        if (start_index + count <= kBaseLen && trg_offset + count <= kBaseLen) {
+          tmp->SetVRegReference(0, h_test_string.Get());
+          tmp->SetVReg(1, start_index);
+          tmp->SetVReg(2, count);
+          tmp->SetVRegReference(3, h_char_array.Get());
+          tmp->SetVReg(3, trg_offset);
+
+          // Copy the char_array into buf.
+          memcpy(buf, h_char_array->GetData(), kBaseLen * sizeof(uint16_t));
+
+          UnstartedStringCharAt(self, tmp, &result, 0);
+
+          uint16_t* data = h_char_array->GetData();
+
+          bool success = true;
+
+          // First segment should be unchanged.
+          for (int32_t i = 0; i < trg_offset; ++i) {
+            success = success && (data[i] == buf[i]);
+          }
+          // Second segment should be a copy.
+          for (int32_t i = trg_offset; i < trg_offset + count; ++i) {
+            success = success && (data[i] == buf[i - trg_offset + start_index]);
+          }
+          // Third segment should be unchanged.
+          for (int32_t i = trg_offset + count; i < kBaseLen; ++i) {
+            success = success && (data[i] == buf[i]);
+          }
+
+          EXPECT_TRUE(success);
+        }
+      }
+    }
+  }
+
+  ShadowFrame::DeleteDeoptimizedFrame(tmp);
+}
+
+TEST_F(UnstartedRuntimeTest, StringCharAt) {
+  Thread* self = Thread::Current();
+
+  ScopedObjectAccess soa(self);
+  // TODO: Actual UTF.
+  constexpr const char* base_string = "abcdefghijklmnop";
+  int32_t base_len = static_cast<int32_t>(strlen(base_string));
+  mirror::String* test_string = mirror::String::AllocFromModifiedUtf8(self, base_string);
+
+  JValue result;
+  ShadowFrame* tmp = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0);
+
+  for (int32_t i = 0; i < base_len; ++i) {
+    tmp->SetVRegReference(0, test_string);
+    tmp->SetVReg(1, i);
+
+    UnstartedStringCharAt(self, tmp, &result, 0);
+
+    EXPECT_EQ(result.GetI(), base_string[i]);
+  }
+
+  ShadowFrame::DeleteDeoptimizedFrame(tmp);
+}
+
+}  // namespace interpreter
+}  // namespace art
diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc
index fd386d7..f435467 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -311,6 +311,30 @@
     return; \
   }
 
+template <bool kNative>
+static mirror::ArtMethod* FindMethod(mirror::Class* c,
+                                     const StringPiece& name,
+                                     const StringPiece& sig)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  for (size_t i = 0; i < c->NumDirectMethods(); ++i) {
+    mirror::ArtMethod* method = c->GetDirectMethod(i);
+    if (kNative == method->IsNative() &&
+        name == method->GetName() && method->GetSignature() == sig) {
+      return method;
+    }
+  }
+
+  for (size_t i = 0; i < c->NumVirtualMethods(); ++i) {
+    mirror::ArtMethod* method = c->GetVirtualMethod(i);
+    if (kNative == method->IsNative() &&
+        name == method->GetName() && method->GetSignature() == sig) {
+      return method;
+    }
+  }
+
+  return nullptr;
+}
+
 class JNI {
  public:
   static jint GetVersion(JNIEnv*) {
@@ -682,8 +706,7 @@
     CHECK_NON_NULL_ARGUMENT(obj);
     CHECK_NON_NULL_ARGUMENT(mid);
     ScopedObjectAccess soa(env);
-    JValue result(InvokeVirtualOrInterfaceWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid,
-                                                      args));
+    JValue result(InvokeVirtualOrInterfaceWithJValues(soa, obj, mid, args));
     return soa.AddLocalReference<jobject>(result.GetL());
   }
 
@@ -709,8 +732,7 @@
     CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj);
     CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid);
     ScopedObjectAccess soa(env);
-    return InvokeVirtualOrInterfaceWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid,
-                                               args).GetZ();
+    return InvokeVirtualOrInterfaceWithJValues(soa, obj, mid, args).GetZ();
   }
 
   static jbyte CallByteMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) {
@@ -735,8 +757,7 @@
     CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj);
     CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid);
     ScopedObjectAccess soa(env);
-    return InvokeVirtualOrInterfaceWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid,
-                                               args).GetB();
+    return InvokeVirtualOrInterfaceWithJValues(soa, obj, mid, args).GetB();
   }
 
   static jchar CallCharMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) {
@@ -761,8 +782,7 @@
     CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj);
     CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid);
     ScopedObjectAccess soa(env);
-    return InvokeVirtualOrInterfaceWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid,
-                                               args).GetC();
+    return InvokeVirtualOrInterfaceWithJValues(soa, obj, mid, args).GetC();
   }
 
   static jdouble CallDoubleMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) {
@@ -787,8 +807,7 @@
     CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj);
     CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid);
     ScopedObjectAccess soa(env);
-    return InvokeVirtualOrInterfaceWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid,
-                                               args).GetD();
+    return InvokeVirtualOrInterfaceWithJValues(soa, obj, mid, args).GetD();
   }
 
   static jfloat CallFloatMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) {
@@ -813,8 +832,7 @@
     CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj);
     CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid);
     ScopedObjectAccess soa(env);
-    return InvokeVirtualOrInterfaceWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid,
-                                               args).GetF();
+    return InvokeVirtualOrInterfaceWithJValues(soa, obj, mid, args).GetF();
   }
 
   static jint CallIntMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) {
@@ -839,8 +857,7 @@
     CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj);
     CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid);
     ScopedObjectAccess soa(env);
-    return InvokeVirtualOrInterfaceWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid,
-                                               args).GetI();
+    return InvokeVirtualOrInterfaceWithJValues(soa, obj, mid, args).GetI();
   }
 
   static jlong CallLongMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) {
@@ -865,8 +882,7 @@
     CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj);
     CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid);
     ScopedObjectAccess soa(env);
-    return InvokeVirtualOrInterfaceWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid,
-                                               args).GetJ();
+    return InvokeVirtualOrInterfaceWithJValues(soa, obj, mid, args).GetJ();
   }
 
   static jshort CallShortMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) {
@@ -891,8 +907,7 @@
     CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj);
     CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid);
     ScopedObjectAccess soa(env);
-    return InvokeVirtualOrInterfaceWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid,
-                                               args).GetS();
+    return InvokeVirtualOrInterfaceWithJValues(soa, obj, mid, args).GetS();
   }
 
   static void CallVoidMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) {
@@ -916,7 +931,7 @@
     CHECK_NON_NULL_ARGUMENT_RETURN_VOID(obj);
     CHECK_NON_NULL_ARGUMENT_RETURN_VOID(mid);
     ScopedObjectAccess soa(env);
-    InvokeVirtualOrInterfaceWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args);
+    InvokeVirtualOrInterfaceWithJValues(soa, obj, mid, args);
   }
 
   static jobject CallNonvirtualObjectMethod(JNIEnv* env, jobject obj, jclass, jmethodID mid, ...) {
@@ -945,7 +960,7 @@
     CHECK_NON_NULL_ARGUMENT(obj);
     CHECK_NON_NULL_ARGUMENT(mid);
     ScopedObjectAccess soa(env);
-    JValue result(InvokeWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args));
+    JValue result(InvokeWithJValues(soa, obj, mid, args));
     return soa.AddLocalReference<jobject>(result.GetL());
   }
 
@@ -974,7 +989,7 @@
     CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj);
     CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid);
     ScopedObjectAccess soa(env);
-    return InvokeWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args).GetZ();
+    return InvokeWithJValues(soa, obj, mid, args).GetZ();
   }
 
   static jbyte CallNonvirtualByteMethod(JNIEnv* env, jobject obj, jclass, jmethodID mid, ...) {
@@ -1001,7 +1016,7 @@
     CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj);
     CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid);
     ScopedObjectAccess soa(env);
-    return InvokeWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args).GetB();
+    return InvokeWithJValues(soa, obj, mid, args).GetB();
   }
 
   static jchar CallNonvirtualCharMethod(JNIEnv* env, jobject obj, jclass, jmethodID mid, ...) {
@@ -1028,7 +1043,7 @@
     CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj);
     CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid);
     ScopedObjectAccess soa(env);
-    return InvokeWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args).GetC();
+    return InvokeWithJValues(soa, obj, mid, args).GetC();
   }
 
   static jshort CallNonvirtualShortMethod(JNIEnv* env, jobject obj, jclass, jmethodID mid, ...) {
@@ -1055,7 +1070,7 @@
     CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj);
     CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid);
     ScopedObjectAccess soa(env);
-    return InvokeWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args).GetS();
+    return InvokeWithJValues(soa, obj, mid, args).GetS();
   }
 
   static jint CallNonvirtualIntMethod(JNIEnv* env, jobject obj, jclass, jmethodID mid, ...) {
@@ -1082,7 +1097,7 @@
     CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj);
     CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid);
     ScopedObjectAccess soa(env);
-    return InvokeWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args).GetI();
+    return InvokeWithJValues(soa, obj, mid, args).GetI();
   }
 
   static jlong CallNonvirtualLongMethod(JNIEnv* env, jobject obj, jclass, jmethodID mid, ...) {
@@ -1109,7 +1124,7 @@
     CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj);
     CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid);
     ScopedObjectAccess soa(env);
-    return InvokeWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args).GetJ();
+    return InvokeWithJValues(soa, obj, mid, args).GetJ();
   }
 
   static jfloat CallNonvirtualFloatMethod(JNIEnv* env, jobject obj, jclass, jmethodID mid, ...) {
@@ -1136,7 +1151,7 @@
     CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj);
     CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid);
     ScopedObjectAccess soa(env);
-    return InvokeWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args).GetF();
+    return InvokeWithJValues(soa, obj, mid, args).GetF();
   }
 
   static jdouble CallNonvirtualDoubleMethod(JNIEnv* env, jobject obj, jclass, jmethodID mid, ...) {
@@ -1163,7 +1178,7 @@
     CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj);
     CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid);
     ScopedObjectAccess soa(env);
-    return InvokeWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args).GetD();
+    return InvokeWithJValues(soa, obj, mid, args).GetD();
   }
 
   static void CallNonvirtualVoidMethod(JNIEnv* env, jobject obj, jclass, jmethodID mid, ...) {
@@ -1189,7 +1204,7 @@
     CHECK_NON_NULL_ARGUMENT_RETURN_VOID(obj);
     CHECK_NON_NULL_ARGUMENT_RETURN_VOID(mid);
     ScopedObjectAccess soa(env);
-    InvokeWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args);
+    InvokeWithJValues(soa, obj, mid, args);
   }
 
   static jfieldID GetFieldID(JNIEnv* env, jclass java_class, const char* name, const char* sig) {
@@ -2133,10 +2148,33 @@
         ++sig;
       }
 
-      mirror::ArtMethod* m = c->FindDirectMethod(name, sig);
-      if (m == nullptr) {
-        m = c->FindVirtualMethod(name, sig);
+      // Note: the right order is to try to find the method locally
+      // first, either as a direct or a virtual method. Then move to
+      // the parent.
+      mirror::ArtMethod* m = nullptr;
+      bool warn_on_going_to_parent = down_cast<JNIEnvExt*>(env)->vm->IsCheckJniEnabled();
+      for (mirror::Class* current_class = c;
+           current_class != nullptr;
+           current_class = current_class->GetSuperClass()) {
+        // Search first only comparing methods which are native.
+        m = FindMethod<true>(current_class, name, sig);
+        if (m != nullptr) {
+          break;
+        }
+
+        // Search again comparing to all methods, to find non-native methods that match.
+        m = FindMethod<false>(current_class, name, sig);
+        if (m != nullptr) {
+          break;
+        }
+
+        if (warn_on_going_to_parent) {
+          LOG(WARNING) << "CheckJNI: method to register \"" << name << "\" not in the given class. "
+                       << "This is slow, consider changing your RegisterNatives calls.";
+          warn_on_going_to_parent = false;
+        }
       }
+
       if (m == nullptr) {
         LOG(return_errors ? ERROR : INTERNAL_FATAL) << "Failed to register native method "
             << PrettyDescriptor(c) << "." << name << sig << " in "
diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc
index 3d14a4e..581ef0e 100644
--- a/runtime/jni_internal_test.cc
+++ b/runtime/jni_internal_test.cc
@@ -858,8 +858,7 @@
   jstring s = reinterpret_cast<jstring>(env_->AllocObject(c));
   ASSERT_NE(s, nullptr);
   env_->CallVoidMethod(s, mid2);
-  // With the string change, this should now throw an UnsupportedOperationException.
-  ASSERT_EQ(JNI_TRUE, env_->ExceptionCheck());
+  ASSERT_EQ(JNI_FALSE, env_->ExceptionCheck());
   env_->ExceptionClear();
 
   mid = env_->GetMethodID(c, "length", "()I");
diff --git a/runtime/mirror/art_method.cc b/runtime/mirror/art_method.cc
index 9518c9d..079a231 100644
--- a/runtime/mirror/art_method.cc
+++ b/runtime/mirror/art_method.cc
@@ -201,29 +201,32 @@
   uint32_t sought_offset = pc - reinterpret_cast<uintptr_t>(entry_point);
   if (IsOptimized(sizeof(void*))) {
     CodeInfo code_info = GetOptimizedCodeInfo();
-    return code_info.GetStackMapForNativePcOffset(sought_offset).GetDexPc(code_info);
-  }
-
-  MappingTable table(entry_point != nullptr ?
-      GetMappingTable(EntryPointToCodePointer(entry_point), sizeof(void*)) : nullptr);
-  if (table.TotalSize() == 0) {
-    // NOTE: Special methods (see Mir2Lir::GenSpecialCase()) have an empty mapping
-    // but they have no suspend checks and, consequently, we never call ToDexPc() for them.
-    DCHECK(IsNative() || IsCalleeSaveMethod() || IsProxyMethod()) << PrettyMethod(this);
-    return DexFile::kDexNoIndex;   // Special no mapping case
-  }
-  // Assume the caller wants a pc-to-dex mapping so check here first.
-  typedef MappingTable::PcToDexIterator It;
-  for (It cur = table.PcToDexBegin(), end = table.PcToDexEnd(); cur != end; ++cur) {
-    if (cur.NativePcOffset() == sought_offset) {
-      return cur.DexPc();
+    StackMap stack_map = code_info.GetStackMapForNativePcOffset(sought_offset);
+    if (stack_map.IsValid()) {
+      return stack_map.GetDexPc(code_info);
     }
-  }
-  // Now check dex-to-pc mappings.
-  typedef MappingTable::DexToPcIterator It2;
-  for (It2 cur = table.DexToPcBegin(), end = table.DexToPcEnd(); cur != end; ++cur) {
-    if (cur.NativePcOffset() == sought_offset) {
-      return cur.DexPc();
+  } else {
+    MappingTable table(entry_point != nullptr ?
+        GetMappingTable(EntryPointToCodePointer(entry_point), sizeof(void*)) : nullptr);
+    if (table.TotalSize() == 0) {
+      // NOTE: Special methods (see Mir2Lir::GenSpecialCase()) have an empty mapping
+      // but they have no suspend checks and, consequently, we never call ToDexPc() for them.
+      DCHECK(IsNative() || IsCalleeSaveMethod() || IsProxyMethod()) << PrettyMethod(this);
+      return DexFile::kDexNoIndex;   // Special no mapping case
+    }
+    // Assume the caller wants a pc-to-dex mapping so check here first.
+    typedef MappingTable::PcToDexIterator It;
+    for (It cur = table.PcToDexBegin(), end = table.PcToDexEnd(); cur != end; ++cur) {
+      if (cur.NativePcOffset() == sought_offset) {
+        return cur.DexPc();
+      }
+    }
+    // Now check dex-to-pc mappings.
+    typedef MappingTable::DexToPcIterator It2;
+    for (It2 cur = table.DexToPcBegin(), end = table.DexToPcEnd(); cur != end; ++cur) {
+      if (cur.NativePcOffset() == sought_offset) {
+        return cur.DexPc();
+      }
     }
   }
   if (abort_on_failure) {
diff --git a/runtime/mirror/field-inl.h b/runtime/mirror/field-inl.h
index 9820db7..388921b 100644
--- a/runtime/mirror/field-inl.h
+++ b/runtime/mirror/field-inl.h
@@ -30,10 +30,11 @@
 template <bool kTransactionActive>
 inline mirror::Field* Field::CreateFromArtField(Thread* self, ArtField* field,
                                                 bool force_resolve) {
+  StackHandleScope<2> hs(self);
   // Try to resolve type before allocating since this is a thread suspension point.
-  mirror::Class* type = field->GetType<true>();
+  Handle<mirror::Class> type = hs.NewHandle(field->GetType<true>());
 
-  if (type == nullptr) {
+  if (type.Get() == nullptr) {
     if (force_resolve) {
       if (kIsDebugBuild) {
         self->AssertPendingException();
@@ -48,7 +49,6 @@
       self->ClearException();
     }
   }
-  StackHandleScope<1> hs(self);
   auto ret = hs.NewHandle(static_cast<Field*>(StaticClass()->AllocObject(self)));
   if (ret.Get() == nullptr) {
     if (kIsDebugBuild) {
@@ -58,14 +58,22 @@
   }
   auto dex_field_index = field->GetDexFieldIndex();
   auto* resolved_field = field->GetDexCache()->GetResolvedField(dex_field_index, sizeof(void*));
-  if (resolved_field != nullptr) {
-    DCHECK_EQ(resolved_field, field);
+  if (field->GetDeclaringClass()->IsProxyClass()) {
+    DCHECK(field->IsStatic());
+    DCHECK_LT(dex_field_index, 2U);
+    // The two static fields (interfaces, throws) of all proxy classes
+    // share the same dex file indices 0 and 1. So, we can't resolve
+    // them in the dex cache.
   } else {
-    // We rely on the field being resolved so that we can back to the ArtField
-    // (i.e. FromReflectedMethod).
-    field->GetDexCache()->SetResolvedField(dex_field_index, field, sizeof(void*));
+    if (resolved_field != nullptr) {
+      DCHECK_EQ(resolved_field, field);
+    } else {
+      // We rely on the field being resolved so that we can back to the ArtField
+      // (i.e. FromReflectedMethod).
+      field->GetDexCache()->SetResolvedField(dex_field_index, field, sizeof(void*));
+    }
   }
-  ret->SetType<kTransactionActive>(type);
+  ret->SetType<kTransactionActive>(type.Get());
   ret->SetDeclaringClass<kTransactionActive>(field->GetDeclaringClass());
   ret->SetAccessFlags<kTransactionActive>(field->GetAccessFlags());
   ret->SetDexFieldIndex<kTransactionActive>(dex_field_index);
diff --git a/runtime/mirror/field.cc b/runtime/mirror/field.cc
index 70311bb..933784e 100644
--- a/runtime/mirror/field.cc
+++ b/runtime/mirror/field.cc
@@ -54,7 +54,19 @@
 }
 
 ArtField* Field::GetArtField() {
-  mirror::DexCache* const dex_cache = GetDeclaringClass()->GetDexCache();
+  mirror::Class* declaring_class = GetDeclaringClass();
+  if (UNLIKELY(declaring_class->IsProxyClass())) {
+    DCHECK(IsStatic());
+    DCHECK_EQ(declaring_class->NumStaticFields(), 2U);
+    // 0 == Class[] interfaces; 1 == Class[][] throws;
+    if (GetDexFieldIndex() == 0) {
+      return &declaring_class->GetSFields()[0];
+    } else {
+      DCHECK_EQ(GetDexFieldIndex(), 1U);
+      return &declaring_class->GetSFields()[1];
+    }
+  }
+  mirror::DexCache* const dex_cache = declaring_class->GetDexCache();
   ArtField* const art_field = dex_cache->GetResolvedField(GetDexFieldIndex(), sizeof(void*));
   CHECK(art_field != nullptr);
   return art_field;
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index d0bfe6e..094d8b7 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -417,7 +417,7 @@
           << secondary_dex_location
           << ". Expected: " << expected_secondary_checksum
           << ", Actual: " << actual_secondary_checksum;
-        return false;
+        return true;
       }
     } else {
       // If we can't get the checksum for the secondary location, we assume
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index 865fcb0..d8e3797 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -67,10 +67,23 @@
       << "Expected stripped dex file to be at: " << GetStrippedDexSrc1();
     ASSERT_FALSE(DexFile::GetChecksum(GetStrippedDexSrc1().c_str(), &checksum, &error_msg))
       << "Expected stripped dex file to be stripped: " << GetStrippedDexSrc1();
-    ASSERT_TRUE(OS::FileExists(GetMultiDexSrc1().c_str()))
-      << "Expected multidex file to be at: " << GetMultiDexSrc1();
     ASSERT_TRUE(OS::FileExists(GetDexSrc2().c_str()))
       << "Expected dex file to be at: " << GetDexSrc2();
+
+    // GetMultiDexSrc2 should have the same primary dex checksum as
+    // GetMultiDexSrc1, but a different secondary dex checksum.
+    std::vector<std::unique_ptr<const DexFile>> multi1;
+    ASSERT_TRUE(DexFile::Open(GetMultiDexSrc1().c_str(),
+          GetMultiDexSrc1().c_str(), &error_msg, &multi1)) << error_msg;
+    ASSERT_GT(multi1.size(), 1u);
+
+    std::vector<std::unique_ptr<const DexFile>> multi2;
+    ASSERT_TRUE(DexFile::Open(GetMultiDexSrc2().c_str(),
+          GetMultiDexSrc2().c_str(), &error_msg, &multi2)) << error_msg;
+    ASSERT_GT(multi2.size(), 1u);
+
+    ASSERT_EQ(multi1[0]->GetLocationChecksum(), multi2[0]->GetLocationChecksum());
+    ASSERT_NE(multi1[1]->GetLocationChecksum(), multi2[1]->GetLocationChecksum());
   }
 
   virtual void SetUpRuntimeOptions(RuntimeOptions* options) {
@@ -149,6 +162,12 @@
     return GetTestDexFileName("MultiDex");
   }
 
+  // Returns the path to a multidex file equivalent to GetMultiDexSrc2, but
+  // with the contents of the secondary dex file changed.
+  std::string GetMultiDexSrc2() {
+    return GetTestDexFileName("MultiDexModifiedSecondary");
+  }
+
   std::string GetDexSrc2() {
     return GetTestDexFileName("Nested");
   }
@@ -344,6 +363,23 @@
   EXPECT_EQ(2u, dex_files.size());
 }
 
+// Case: We have a MultiDEX file where the secondary dex file is out of date.
+// Expect: The status is kDex2OatNeeded.
+TEST_F(OatFileAssistantTest, MultiDexSecondaryOutOfDate) {
+  std::string dex_location = GetScratchDir() + "/MultiDexSecondaryOutOfDate.jar";
+
+  // Compile code for GetMultiDexSrc1.
+  Copy(GetMultiDexSrc1(), dex_location);
+  GenerateOatForTest(dex_location.c_str());
+
+  // Now overwrite the dex file with GetMultiDexSrc2 so the secondary checksum
+  // is out of date.
+  Copy(GetMultiDexSrc2(), dex_location);
+
+  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded());
+}
+
 // Case: We have a MultiDEX file and up-to-date OAT file for it with relative
 // encoded dex locations.
 // Expect: The oat file status is kNoDexOptNeeded.
@@ -1001,9 +1037,6 @@
 // TODO: More Tests:
 //  * Test class linker falls back to unquickened dex for DexNoOat
 //  * Test class linker falls back to unquickened dex for MultiDexNoOat
-//  * Test multidex files:
-//     - Multidex with only classes2.dex out of date should have status
-//       kOutOfDate
 //  * Test using secondary isa
 //  * Test with profiling info?
 //  * Test for status of oat while oat is being generated (how?)
diff --git a/runtime/proxy_test.cc b/runtime/proxy_test.cc
index b471293..93d1f66 100644
--- a/runtime/proxy_test.cc
+++ b/runtime/proxy_test.cc
@@ -20,6 +20,7 @@
 #include "art_field-inl.h"
 #include "class_linker-inl.h"
 #include "common_compiler_test.h"
+#include "mirror/field-inl.h"
 #include "mirror/method.h"
 #include "scoped_thread_state_change.h"
 
@@ -191,4 +192,53 @@
   EXPECT_FALSE(field->IsPrimitiveType());
 }
 
+// Creates two proxy classes and check the art/mirror fields of their static fields.
+TEST_F(ProxyTest, CheckArtMirrorFieldsOfProxyStaticFields) {
+  ScopedObjectAccess soa(Thread::Current());
+  jobject jclass_loader = LoadDex("Interfaces");
+  StackHandleScope<7> hs(soa.Self());
+  Handle<mirror::ClassLoader> class_loader(
+      hs.NewHandle(soa.Decode<mirror::ClassLoader*>(jclass_loader)));
+
+  Handle<mirror::Class> proxyClass0;
+  Handle<mirror::Class> proxyClass1;
+  {
+    std::vector<mirror::Class*> interfaces;
+    proxyClass0 = hs.NewHandle(GenerateProxyClass(soa, jclass_loader, "$Proxy0", interfaces));
+    proxyClass1 = hs.NewHandle(GenerateProxyClass(soa, jclass_loader, "$Proxy1", interfaces));
+  }
+
+  ASSERT_TRUE(proxyClass0.Get() != nullptr);
+  ASSERT_TRUE(proxyClass0->IsProxyClass());
+  ASSERT_TRUE(proxyClass0->IsInitialized());
+  ASSERT_TRUE(proxyClass1.Get() != nullptr);
+  ASSERT_TRUE(proxyClass1->IsProxyClass());
+  ASSERT_TRUE(proxyClass1->IsInitialized());
+
+  ArtField* static_fields0 = proxyClass0->GetSFields();
+  ASSERT_TRUE(static_fields0 != nullptr);
+  ASSERT_EQ(2u, proxyClass0->NumStaticFields());
+  ArtField* static_fields1 = proxyClass1->GetSFields();
+  ASSERT_TRUE(static_fields1 != nullptr);
+  ASSERT_EQ(2u, proxyClass1->NumStaticFields());
+
+  EXPECT_EQ(static_fields0[0].GetDeclaringClass(), proxyClass0.Get());
+  EXPECT_EQ(static_fields0[1].GetDeclaringClass(), proxyClass0.Get());
+  EXPECT_EQ(static_fields1[0].GetDeclaringClass(), proxyClass1.Get());
+  EXPECT_EQ(static_fields1[1].GetDeclaringClass(), proxyClass1.Get());
+
+  Handle<mirror::Field> field00 =
+      hs.NewHandle(mirror::Field::CreateFromArtField(soa.Self(), &static_fields0[0], true));
+  Handle<mirror::Field> field01 =
+      hs.NewHandle(mirror::Field::CreateFromArtField(soa.Self(), &static_fields0[1], true));
+  Handle<mirror::Field> field10 =
+      hs.NewHandle(mirror::Field::CreateFromArtField(soa.Self(), &static_fields1[0], true));
+  Handle<mirror::Field> field11 =
+      hs.NewHandle(mirror::Field::CreateFromArtField(soa.Self(), &static_fields1[1], true));
+  EXPECT_EQ(field00->GetArtField(), &static_fields0[0]);
+  EXPECT_EQ(field01->GetArtField(), &static_fields0[1]);
+  EXPECT_EQ(field10->GetArtField(), &static_fields1[0]);
+  EXPECT_EQ(field11->GetArtField(), &static_fields1[1]);
+}
+
 }  // namespace art
diff --git a/runtime/reflection.cc b/runtime/reflection.cc
index 49e1b8e..d321d27 100644
--- a/runtime/reflection.cc
+++ b/runtime/reflection.cc
@@ -21,6 +21,7 @@
 #include "common_throws.h"
 #include "dex_file-inl.h"
 #include "entrypoints/entrypoint_utils.h"
+#include "indirect_reference_table-inl.h"
 #include "jni_internal.h"
 #include "mirror/abstract_method.h"
 #include "mirror/art_method-inl.h"
@@ -449,6 +450,11 @@
   }
 
   mirror::ArtMethod* method = soa.DecodeMethod(mid);
+  bool is_string_init = method->GetDeclaringClass()->IsStringClass() && method->IsConstructor();
+  if (is_string_init) {
+    // Replace calls to String.<init> with equivalent StringFactory call.
+    method = soa.DecodeMethod(WellKnownClasses::StringInitToStringFactoryMethodID(mid));
+  }
   mirror::Object* receiver = method->IsStatic() ? nullptr : soa.Decode<mirror::Object*>(obj);
   uint32_t shorty_len = 0;
   const char* shorty = method->GetShorty(&shorty_len);
@@ -456,11 +462,15 @@
   ArgArray arg_array(shorty, shorty_len);
   arg_array.BuildArgArrayFromVarArgs(soa, receiver, args);
   InvokeWithArgArray(soa, method, &arg_array, &result, shorty);
+  if (is_string_init) {
+    // For string init, remap original receiver to StringFactory result.
+    soa.Self()->GetJniEnv()->locals.Update(obj, result.GetL());
+  }
   return result;
 }
 
-JValue InvokeWithJValues(const ScopedObjectAccessAlreadyRunnable& soa, mirror::Object* receiver,
-                         jmethodID mid, jvalue* args) {
+JValue InvokeWithJValues(const ScopedObjectAccessAlreadyRunnable& soa, jobject obj, jmethodID mid,
+                         jvalue* args) {
   // We want to make sure that the stack is not within a small distance from the
   // protected region in case we are calling into a leaf function whose stack
   // check has been elided.
@@ -470,17 +480,27 @@
   }
 
   mirror::ArtMethod* method = soa.DecodeMethod(mid);
+  bool is_string_init = method->GetDeclaringClass()->IsStringClass() && method->IsConstructor();
+  if (is_string_init) {
+    // Replace calls to String.<init> with equivalent StringFactory call.
+    method = soa.DecodeMethod(WellKnownClasses::StringInitToStringFactoryMethodID(mid));
+  }
+  mirror::Object* receiver = method->IsStatic() ? nullptr : soa.Decode<mirror::Object*>(obj);
   uint32_t shorty_len = 0;
   const char* shorty = method->GetShorty(&shorty_len);
   JValue result;
   ArgArray arg_array(shorty, shorty_len);
   arg_array.BuildArgArrayFromJValues(soa, receiver, args);
   InvokeWithArgArray(soa, method, &arg_array, &result, shorty);
+  if (is_string_init) {
+    // For string init, remap original receiver to StringFactory result.
+    soa.Self()->GetJniEnv()->locals.Update(obj, result.GetL());
+  }
   return result;
 }
 
 JValue InvokeVirtualOrInterfaceWithJValues(const ScopedObjectAccessAlreadyRunnable& soa,
-                                           mirror::Object* receiver, jmethodID mid, jvalue* args) {
+                                           jobject obj, jmethodID mid, jvalue* args) {
   // We want to make sure that the stack is not within a small distance from the
   // protected region in case we are calling into a leaf function whose stack
   // check has been elided.
@@ -489,13 +509,24 @@
     return JValue();
   }
 
+  mirror::Object* receiver = soa.Decode<mirror::Object*>(obj);
   mirror::ArtMethod* method = FindVirtualMethod(receiver, soa.DecodeMethod(mid));
+  bool is_string_init = method->GetDeclaringClass()->IsStringClass() && method->IsConstructor();
+  if (is_string_init) {
+    // Replace calls to String.<init> with equivalent StringFactory call.
+    method = soa.DecodeMethod(WellKnownClasses::StringInitToStringFactoryMethodID(mid));
+    receiver = nullptr;
+  }
   uint32_t shorty_len = 0;
   const char* shorty = method->GetShorty(&shorty_len);
   JValue result;
   ArgArray arg_array(shorty, shorty_len);
   arg_array.BuildArgArrayFromJValues(soa, receiver, args);
   InvokeWithArgArray(soa, method, &arg_array, &result, shorty);
+  if (is_string_init) {
+    // For string init, remap original receiver to StringFactory result.
+    soa.Self()->GetJniEnv()->locals.Update(obj, result.GetL());
+  }
   return result;
 }
 
@@ -511,12 +542,22 @@
 
   mirror::Object* receiver = soa.Decode<mirror::Object*>(obj);
   mirror::ArtMethod* method = FindVirtualMethod(receiver, soa.DecodeMethod(mid));
+  bool is_string_init = method->GetDeclaringClass()->IsStringClass() && method->IsConstructor();
+  if (is_string_init) {
+    // Replace calls to String.<init> with equivalent StringFactory call.
+    method = soa.DecodeMethod(WellKnownClasses::StringInitToStringFactoryMethodID(mid));
+    receiver = nullptr;
+  }
   uint32_t shorty_len = 0;
   const char* shorty = method->GetShorty(&shorty_len);
   JValue result;
   ArgArray arg_array(shorty, shorty_len);
   arg_array.BuildArgArrayFromVarArgs(soa, receiver, args);
   InvokeWithArgArray(soa, method, &arg_array, &result, shorty);
+  if (is_string_init) {
+    // For string init, remap original receiver to StringFactory result.
+    soa.Self()->GetJniEnv()->locals.Update(obj, result.GetL());
+  }
   return result;
 }
 
diff --git a/runtime/reflection.h b/runtime/reflection.h
index 37f8a6a..6b5ffc7 100644
--- a/runtime/reflection.h
+++ b/runtime/reflection.h
@@ -49,12 +49,12 @@
                          va_list args)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-JValue InvokeWithJValues(const ScopedObjectAccessAlreadyRunnable& soa, mirror::Object* receiver,
-                         jmethodID mid, jvalue* args)
+JValue InvokeWithJValues(const ScopedObjectAccessAlreadyRunnable& soa, jobject obj, jmethodID mid,
+                         jvalue* args)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
 JValue InvokeVirtualOrInterfaceWithJValues(const ScopedObjectAccessAlreadyRunnable& soa,
-                                           mirror::Object* receiver, jmethodID mid, jvalue* args)
+                                           jobject obj, jmethodID mid, jvalue* args)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
 JValue InvokeVirtualOrInterfaceWithVarArgs(const ScopedObjectAccessAlreadyRunnable& soa,
diff --git a/runtime/reflection_test.cc b/runtime/reflection_test.cc
index a62bc5e..36e444a 100644
--- a/runtime/reflection_test.cc
+++ b/runtime/reflection_test.cc
@@ -133,7 +133,8 @@
     mirror::ArtMethod* method;
     mirror::Object* receiver;
     ReflectionTestMakeExecutable(&method, &receiver, is_static, "nop", "()V");
-    InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), nullptr);
+    ScopedLocalRef<jobject> receiver_ref(soa.Env(), soa.AddLocalReference<jobject>(receiver));
+    InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), nullptr);
   }
 
   void InvokeIdentityByteMethod(bool is_static) {
@@ -141,22 +142,23 @@
     mirror::ArtMethod* method;
     mirror::Object* receiver;
     ReflectionTestMakeExecutable(&method, &receiver, is_static, "identity", "(B)B");
+    ScopedLocalRef<jobject> receiver_ref(soa.Env(), soa.AddLocalReference<jobject>(receiver));
     jvalue args[1];
 
     args[0].b = 0;
-    JValue result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    JValue result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
     EXPECT_EQ(0, result.GetB());
 
     args[0].b = -1;
-    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
     EXPECT_EQ(-1, result.GetB());
 
     args[0].b = SCHAR_MAX;
-    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
     EXPECT_EQ(SCHAR_MAX, result.GetB());
 
     args[0].b = (SCHAR_MIN << 24) >> 24;
-    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
     EXPECT_EQ(SCHAR_MIN, result.GetB());
   }
 
@@ -165,22 +167,23 @@
     mirror::ArtMethod* method;
     mirror::Object* receiver;
     ReflectionTestMakeExecutable(&method, &receiver, is_static, "identity", "(I)I");
+    ScopedLocalRef<jobject> receiver_ref(soa.Env(), soa.AddLocalReference<jobject>(receiver));
     jvalue args[1];
 
     args[0].i = 0;
-    JValue result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    JValue result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
     EXPECT_EQ(0, result.GetI());
 
     args[0].i = -1;
-    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
     EXPECT_EQ(-1, result.GetI());
 
     args[0].i = INT_MAX;
-    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
     EXPECT_EQ(INT_MAX, result.GetI());
 
     args[0].i = INT_MIN;
-    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
     EXPECT_EQ(INT_MIN, result.GetI());
   }
 
@@ -189,22 +192,23 @@
     mirror::ArtMethod* method;
     mirror::Object* receiver;
     ReflectionTestMakeExecutable(&method, &receiver, is_static, "identity", "(D)D");
+    ScopedLocalRef<jobject> receiver_ref(soa.Env(), soa.AddLocalReference<jobject>(receiver));
     jvalue args[1];
 
     args[0].d = 0.0;
-    JValue result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    JValue result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
     EXPECT_DOUBLE_EQ(0.0, result.GetD());
 
     args[0].d = -1.0;
-    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
     EXPECT_DOUBLE_EQ(-1.0, result.GetD());
 
     args[0].d = DBL_MAX;
-    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
     EXPECT_DOUBLE_EQ(DBL_MAX, result.GetD());
 
     args[0].d = DBL_MIN;
-    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
     EXPECT_DOUBLE_EQ(DBL_MIN, result.GetD());
   }
 
@@ -213,26 +217,27 @@
     mirror::ArtMethod* method;
     mirror::Object* receiver;
     ReflectionTestMakeExecutable(&method, &receiver, is_static, "sum", "(II)I");
+    ScopedLocalRef<jobject> receiver_ref(soa.Env(), soa.AddLocalReference<jobject>(receiver));
     jvalue args[2];
 
     args[0].i = 1;
     args[1].i = 2;
-    JValue result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    JValue result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
     EXPECT_EQ(3, result.GetI());
 
     args[0].i = -2;
     args[1].i = 5;
-    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
     EXPECT_EQ(3, result.GetI());
 
     args[0].i = INT_MAX;
     args[1].i = INT_MIN;
-    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
     EXPECT_EQ(-1, result.GetI());
 
     args[0].i = INT_MAX;
     args[1].i = INT_MAX;
-    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
     EXPECT_EQ(-2, result.GetI());
   }
 
@@ -241,36 +246,37 @@
     mirror::ArtMethod* method;
     mirror::Object* receiver;
     ReflectionTestMakeExecutable(&method, &receiver, is_static, "sum", "(III)I");
+    ScopedLocalRef<jobject> receiver_ref(soa.Env(), soa.AddLocalReference<jobject>(receiver));
     jvalue args[3];
 
     args[0].i = 0;
     args[1].i = 0;
     args[2].i = 0;
-    JValue result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    JValue result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
     EXPECT_EQ(0, result.GetI());
 
     args[0].i = 1;
     args[1].i = 2;
     args[2].i = 3;
-    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
     EXPECT_EQ(6, result.GetI());
 
     args[0].i = -1;
     args[1].i = 2;
     args[2].i = -3;
-    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
     EXPECT_EQ(-2, result.GetI());
 
     args[0].i = INT_MAX;
     args[1].i = INT_MIN;
     args[2].i = INT_MAX;
-    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
     EXPECT_EQ(2147483646, result.GetI());
 
     args[0].i = INT_MAX;
     args[1].i = INT_MAX;
     args[2].i = INT_MAX;
-    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
     EXPECT_EQ(2147483645, result.GetI());
   }
 
@@ -279,41 +285,42 @@
     mirror::ArtMethod* method;
     mirror::Object* receiver;
     ReflectionTestMakeExecutable(&method, &receiver, is_static, "sum", "(IIII)I");
+    ScopedLocalRef<jobject> receiver_ref(soa.Env(), soa.AddLocalReference<jobject>(receiver));
     jvalue args[4];
 
     args[0].i = 0;
     args[1].i = 0;
     args[2].i = 0;
     args[3].i = 0;
-    JValue result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    JValue result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
     EXPECT_EQ(0, result.GetI());
 
     args[0].i = 1;
     args[1].i = 2;
     args[2].i = 3;
     args[3].i = 4;
-    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
     EXPECT_EQ(10, result.GetI());
 
     args[0].i = -1;
     args[1].i = 2;
     args[2].i = -3;
     args[3].i = 4;
-    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
     EXPECT_EQ(2, result.GetI());
 
     args[0].i = INT_MAX;
     args[1].i = INT_MIN;
     args[2].i = INT_MAX;
     args[3].i = INT_MIN;
-    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
     EXPECT_EQ(-2, result.GetI());
 
     args[0].i = INT_MAX;
     args[1].i = INT_MAX;
     args[2].i = INT_MAX;
     args[3].i = INT_MAX;
-    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
     EXPECT_EQ(-4, result.GetI());
   }
 
@@ -322,6 +329,7 @@
     mirror::ArtMethod* method;
     mirror::Object* receiver;
     ReflectionTestMakeExecutable(&method, &receiver, is_static, "sum", "(IIIII)I");
+    ScopedLocalRef<jobject> receiver_ref(soa.Env(), soa.AddLocalReference<jobject>(receiver));
     jvalue args[5];
 
     args[0].i = 0;
@@ -329,7 +337,7 @@
     args[2].i = 0;
     args[3].i = 0;
     args[4].i = 0;
-    JValue result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    JValue result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
     EXPECT_EQ(0, result.GetI());
 
     args[0].i = 1;
@@ -337,7 +345,7 @@
     args[2].i = 3;
     args[3].i = 4;
     args[4].i = 5;
-    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
     EXPECT_EQ(15, result.GetI());
 
     args[0].i = -1;
@@ -345,7 +353,7 @@
     args[2].i = -3;
     args[3].i = 4;
     args[4].i = -5;
-    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
     EXPECT_EQ(-3, result.GetI());
 
     args[0].i = INT_MAX;
@@ -353,7 +361,7 @@
     args[2].i = INT_MAX;
     args[3].i = INT_MIN;
     args[4].i = INT_MAX;
-    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
     EXPECT_EQ(2147483645, result.GetI());
 
     args[0].i = INT_MAX;
@@ -361,7 +369,7 @@
     args[2].i = INT_MAX;
     args[3].i = INT_MAX;
     args[4].i = INT_MAX;
-    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
     EXPECT_EQ(2147483643, result.GetI());
   }
 
@@ -370,31 +378,32 @@
     mirror::ArtMethod* method;
     mirror::Object* receiver;
     ReflectionTestMakeExecutable(&method, &receiver, is_static, "sum", "(DD)D");
+    ScopedLocalRef<jobject> receiver_ref(soa.Env(), soa.AddLocalReference<jobject>(receiver));
     jvalue args[2];
 
     args[0].d = 0.0;
     args[1].d = 0.0;
-    JValue result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    JValue result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
     EXPECT_DOUBLE_EQ(0.0, result.GetD());
 
     args[0].d = 1.0;
     args[1].d = 2.0;
-    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
     EXPECT_DOUBLE_EQ(3.0, result.GetD());
 
     args[0].d = 1.0;
     args[1].d = -2.0;
-    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
     EXPECT_DOUBLE_EQ(-1.0, result.GetD());
 
     args[0].d = DBL_MAX;
     args[1].d = DBL_MIN;
-    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
     EXPECT_DOUBLE_EQ(1.7976931348623157e308, result.GetD());
 
     args[0].d = DBL_MAX;
     args[1].d = DBL_MAX;
-    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
     EXPECT_DOUBLE_EQ(INFINITY, result.GetD());
   }
 
@@ -403,24 +412,25 @@
     mirror::ArtMethod* method;
     mirror::Object* receiver;
     ReflectionTestMakeExecutable(&method, &receiver, is_static, "sum", "(DDD)D");
+    ScopedLocalRef<jobject> receiver_ref(soa.Env(), soa.AddLocalReference<jobject>(receiver));
     jvalue args[3];
 
     args[0].d = 0.0;
     args[1].d = 0.0;
     args[2].d = 0.0;
-    JValue result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    JValue result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
     EXPECT_DOUBLE_EQ(0.0, result.GetD());
 
     args[0].d = 1.0;
     args[1].d = 2.0;
     args[2].d = 3.0;
-    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
     EXPECT_DOUBLE_EQ(6.0, result.GetD());
 
     args[0].d = 1.0;
     args[1].d = -2.0;
     args[2].d = 3.0;
-    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
     EXPECT_DOUBLE_EQ(2.0, result.GetD());
   }
 
@@ -429,27 +439,28 @@
     mirror::ArtMethod* method;
     mirror::Object* receiver;
     ReflectionTestMakeExecutable(&method, &receiver, is_static, "sum", "(DDDD)D");
+    ScopedLocalRef<jobject> receiver_ref(soa.Env(), soa.AddLocalReference<jobject>(receiver));
     jvalue args[4];
 
     args[0].d = 0.0;
     args[1].d = 0.0;
     args[2].d = 0.0;
     args[3].d = 0.0;
-    JValue result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    JValue result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
     EXPECT_DOUBLE_EQ(0.0, result.GetD());
 
     args[0].d = 1.0;
     args[1].d = 2.0;
     args[2].d = 3.0;
     args[3].d = 4.0;
-    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
     EXPECT_DOUBLE_EQ(10.0, result.GetD());
 
     args[0].d = 1.0;
     args[1].d = -2.0;
     args[2].d = 3.0;
     args[3].d = -4.0;
-    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
     EXPECT_DOUBLE_EQ(-2.0, result.GetD());
   }
 
@@ -458,6 +469,7 @@
     mirror::ArtMethod* method;
     mirror::Object* receiver;
     ReflectionTestMakeExecutable(&method, &receiver, is_static, "sum", "(DDDDD)D");
+    ScopedLocalRef<jobject> receiver_ref(soa.Env(), soa.AddLocalReference<jobject>(receiver));
     jvalue args[5];
 
     args[0].d = 0.0;
@@ -465,7 +477,7 @@
     args[2].d = 0.0;
     args[3].d = 0.0;
     args[4].d = 0.0;
-    JValue result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    JValue result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
     EXPECT_DOUBLE_EQ(0.0, result.GetD());
 
     args[0].d = 1.0;
@@ -473,7 +485,7 @@
     args[2].d = 3.0;
     args[3].d = 4.0;
     args[4].d = 5.0;
-    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
     EXPECT_DOUBLE_EQ(15.0, result.GetD());
 
     args[0].d = 1.0;
@@ -481,7 +493,7 @@
     args[2].d = 3.0;
     args[3].d = -4.0;
     args[4].d = 5.0;
-    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
     EXPECT_DOUBLE_EQ(3.0, result.GetD());
   }
 
diff --git a/runtime/stack.cc b/runtime/stack.cc
index 6795516..f7b96ea 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -109,6 +109,7 @@
   uint32_t native_pc_offset = outer_method->NativeQuickPcOffset(cur_quick_frame_pc_);
   CodeInfo code_info = outer_method->GetOptimizedCodeInfo();
   StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset);
+  DCHECK(stack_map.IsValid());
   return code_info.GetInlineInfoOf(stack_map);
 }
 
@@ -269,6 +270,7 @@
 
   uint32_t native_pc_offset = outer_method->NativeQuickPcOffset(cur_quick_frame_pc_);
   StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset);
+  DCHECK(stack_map.IsValid());
   size_t depth_in_stack_map = current_inlining_depth_ - 1;
 
   DexRegisterMap dex_register_map = IsInInlinedFrame()
@@ -749,7 +751,7 @@
           CodeInfo code_info = method->GetOptimizedCodeInfo();
           uint32_t native_pc_offset = method->NativeQuickPcOffset(cur_quick_frame_pc_);
           StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset);
-          if (stack_map.HasInlineInfo(code_info)) {
+          if (stack_map.IsValid() && stack_map.HasInlineInfo(code_info)) {
             InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map);
             DCHECK_EQ(current_inlining_depth_, 0u);
             for (current_inlining_depth_ = inline_info.GetDepth();
diff --git a/runtime/stack_map.h b/runtime/stack_map.h
index 16ae772..f710460 100644
--- a/runtime/stack_map.h
+++ b/runtime/stack_map.h
@@ -641,6 +641,9 @@
 class StackMap {
  public:
   explicit StackMap(MemoryRegion region) : region_(region) {}
+  StackMap() {}
+
+  bool IsValid() const { return region_.pointer() != nullptr; }
 
   uint32_t GetDexPc(const CodeInfo& info) const;
 
@@ -735,31 +738,43 @@
   }
 
   uint32_t GetMethodIndexAtDepth(uint8_t depth) const {
-    return region_.LoadUnaligned<uint32_t>(kFixedSize + depth * SingleEntrySize());
+    return region_.LoadUnaligned<uint32_t>(
+        kFixedSize + depth * SingleEntrySize() + kMethodIndexOffset);
   }
 
   void SetMethodIndexAtDepth(uint8_t depth, uint32_t index) {
-    region_.StoreUnaligned<uint32_t>(kFixedSize + depth * SingleEntrySize(), index);
+    region_.StoreUnaligned<uint32_t>(
+        kFixedSize + depth * SingleEntrySize() + kMethodIndexOffset, index);
   }
 
   uint32_t GetDexPcAtDepth(uint8_t depth) const {
     return region_.LoadUnaligned<uint32_t>(
-        kFixedSize + depth * SingleEntrySize() + sizeof(uint32_t));
+        kFixedSize + depth * SingleEntrySize() + kDexPcOffset);
   }
 
   void SetDexPcAtDepth(uint8_t depth, uint32_t dex_pc) {
     region_.StoreUnaligned<uint32_t>(
-        kFixedSize + depth * SingleEntrySize() + sizeof(uint32_t), dex_pc);
+        kFixedSize + depth * SingleEntrySize() + kDexPcOffset, dex_pc);
+  }
+
+  uint8_t GetInvokeTypeAtDepth(uint8_t depth) const {
+    return region_.LoadUnaligned<uint8_t>(
+        kFixedSize + depth * SingleEntrySize() + kInvokeTypeOffset);
+  }
+
+  void SetInvokeTypeAtDepth(uint8_t depth, uint8_t invoke_type) {
+    region_.StoreUnaligned<uint8_t>(
+        kFixedSize + depth * SingleEntrySize() + kInvokeTypeOffset, invoke_type);
   }
 
   uint32_t GetDexRegisterMapOffsetAtDepth(uint8_t depth) const {
     return region_.LoadUnaligned<uint32_t>(
-        kFixedSize + depth * SingleEntrySize() + sizeof(uint32_t) + sizeof(uint32_t));
+        kFixedSize + depth * SingleEntrySize() + kDexRegisterMapOffset);
   }
 
   void SetDexRegisterMapOffsetAtDepth(uint8_t depth, uint32_t offset) {
     region_.StoreUnaligned<uint32_t>(
-        kFixedSize + depth * SingleEntrySize() + sizeof(uint32_t) + sizeof(uint32_t), offset);
+        kFixedSize + depth * SingleEntrySize() + kDexRegisterMapOffset, offset);
   }
 
   bool HasDexRegisterMapAtDepth(uint8_t depth) const {
@@ -767,7 +782,7 @@
   }
 
   static size_t SingleEntrySize() {
-    return sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint32_t);
+    return kFixedEntrySize;
   }
 
   void Dump(std::ostream& os, const CodeInfo& info, uint16_t* number_of_dex_registers) const;
@@ -778,6 +793,12 @@
   static constexpr int kDepthOffset = 0;
   static constexpr int kFixedSize = kDepthOffset + sizeof(uint8_t);
 
+  static constexpr int kMethodIndexOffset = 0;
+  static constexpr int kDexPcOffset = kMethodIndexOffset + sizeof(uint32_t);
+  static constexpr int kInvokeTypeOffset = kDexPcOffset + sizeof(uint32_t);
+  static constexpr int kDexRegisterMapOffset = kInvokeTypeOffset + sizeof(uint8_t);
+  static constexpr int kFixedEntrySize = kDexRegisterMapOffset + sizeof(uint32_t);
+
   MemoryRegion region_;
 
   friend class CodeInfo;
@@ -1011,8 +1032,7 @@
         return stack_map;
       }
     }
-    LOG(FATAL) << "Unreachable";
-    UNREACHABLE();
+    return StackMap();
   }
 
   StackMap GetStackMapForNativePcOffset(uint32_t native_pc_offset) const {
@@ -1023,8 +1043,7 @@
         return stack_map;
       }
     }
-    LOG(FATAL) << "Unreachable";
-    UNREACHABLE();
+    return StackMap();
   }
 
   void Dump(std::ostream& os, uint16_t number_of_dex_registers) const;
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 148bb6d..9b1600f 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -216,7 +216,8 @@
     // Invoke the 'run' method of our java.lang.Thread.
     mirror::Object* receiver = self->tlsPtr_.opeer;
     jmethodID mid = WellKnownClasses::java_lang_Thread_run;
-    InvokeVirtualOrInterfaceWithJValues(soa, receiver, mid, nullptr);
+    ScopedLocalRef<jobject> ref(soa.Env(), soa.AddLocalReference<jobject>(receiver));
+    InvokeVirtualOrInterfaceWithJValues(soa, ref.get(), mid, nullptr);
   }
   // Detach and delete self.
   Runtime::Current()->GetThreadList()->Unregister(self);
@@ -1886,7 +1887,8 @@
       jv_args[i].l = cause.get();
       ++i;
     }
-    InvokeWithJValues(soa, exception.Get(), soa.EncodeMethod(exception_init_method), jv_args);
+    ScopedLocalRef<jobject> ref(soa.Env(), soa.AddLocalReference<jobject>(exception.Get()));
+    InvokeWithJValues(soa, ref.get(), soa.EncodeMethod(exception_init_method), jv_args);
     if (LIKELY(!IsExceptionPending())) {
       SetException(exception.Get());
     }
@@ -2246,6 +2248,7 @@
         uintptr_t native_pc_offset = m->NativeQuickPcOffset(GetCurrentQuickFramePc(), entry_point);
         CodeInfo code_info = m->GetOptimizedCodeInfo();
         StackMap map = code_info.GetStackMapForNativePcOffset(native_pc_offset);
+        DCHECK(map.IsValid());
         MemoryRegion mask = map.GetStackMask(code_info);
         // Visit stack entries that hold pointers.
         for (size_t i = 0; i < mask.size_in_bits(); ++i) {
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 1b1bc54..6c58d55 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -287,6 +287,10 @@
 }
 
 static bool IsLargeMethod(const DexFile::CodeItem* const code_item) {
+  if (code_item == nullptr) {
+    return false;
+  }
+
   uint16_t registers_size = code_item->registers_size_;
   uint32_t insns_size = code_item->insns_size_in_code_units_;
 
diff --git a/test/004-JniTest/jni_test.cc b/test/004-JniTest/jni_test.cc
index cdc5461..1ec0cf2 100644
--- a/test/004-JniTest/jni_test.cc
+++ b/test/004-JniTest/jni_test.cc
@@ -550,21 +550,58 @@
 }
 
 extern "C" JNIEXPORT void JNICALL Java_Main_testNewStringObject(JNIEnv* env, jclass) {
-  const char* string = "Test";
-  int length = strlen(string);
   jclass c = env->FindClass("java/lang/String");
-  assert(c != NULL);
-  jmethodID method = env->GetMethodID(c, "<init>", "([B)V");
-  assert(method != NULL);
+  assert(c != nullptr);
+
+  jmethodID mid1 = env->GetMethodID(c, "<init>", "()V");
+  assert(mid1 != nullptr);
   assert(!env->ExceptionCheck());
-  jbyteArray array = env->NewByteArray(length);
-  env->SetByteArrayRegion(array, 0, length, reinterpret_cast<const jbyte*>(string));
-  jobject o = env->NewObject(c, method, array);
-  assert(o != NULL);
-  jstring s = reinterpret_cast<jstring>(o);
-  assert(env->GetStringLength(s) == length);
-  assert(env->GetStringUTFLength(s) == length);
+  jmethodID mid2 = env->GetMethodID(c, "<init>", "([B)V");
+  assert(mid2 != nullptr);
+  assert(!env->ExceptionCheck());
+  jmethodID mid3 = env->GetMethodID(c, "<init>", "([C)V");
+  assert(mid3 != nullptr);
+  assert(!env->ExceptionCheck());
+  jmethodID mid4 = env->GetMethodID(c, "<init>", "(Ljava/lang/String;)V");
+  assert(mid4 != nullptr);
+  assert(!env->ExceptionCheck());
+
+  const char* test_array = "Test";
+  int byte_array_length = strlen(test_array);
+  jbyteArray byte_array = env->NewByteArray(byte_array_length);
+  env->SetByteArrayRegion(byte_array, 0, byte_array_length, reinterpret_cast<const jbyte*>(test_array));
+
+  // Test NewObject
+  jstring s = reinterpret_cast<jstring>(env->NewObject(c, mid2, byte_array));
+  assert(s != nullptr);
+  assert(env->GetStringLength(s) == byte_array_length);
+  assert(env->GetStringUTFLength(s) == byte_array_length);
   const char* chars = env->GetStringUTFChars(s, nullptr);
-  assert(strcmp(string, chars) == 0);
+  assert(strcmp(test_array, chars) == 0);
   env->ReleaseStringUTFChars(s, chars);
+
+  // Test AllocObject and Call(Nonvirtual)VoidMethod
+  jstring s1 = reinterpret_cast<jstring>(env->AllocObject(c));
+  assert(s1 != nullptr);
+  jstring s2 = reinterpret_cast<jstring>(env->AllocObject(c));
+  assert(s2 != nullptr);
+  jstring s3 = reinterpret_cast<jstring>(env->AllocObject(c));
+  assert(s3 != nullptr);
+  jstring s4 = reinterpret_cast<jstring>(env->AllocObject(c));
+  assert(s4 != nullptr);
+
+  jcharArray char_array = env->NewCharArray(5);
+  jstring string_arg = env->NewStringUTF("helloworld");
+
+  // With Var Args
+  env->CallVoidMethod(s1, mid1);
+  env->CallNonvirtualVoidMethod(s2, c, mid2, byte_array);
+
+  // With JValues
+  jvalue args3[1];
+  args3[0].l = char_array;
+  jvalue args4[1];
+  args4[0].l = string_arg;
+  env->CallVoidMethodA(s3, mid3, args3);
+  env->CallNonvirtualVoidMethodA(s4, c, mid4, args4);
 }
diff --git a/test/020-string/src/Main.java b/test/020-string/src/Main.java
index bb8ce1f..b876e6a 100644
--- a/test/020-string/src/Main.java
+++ b/test/020-string/src/Main.java
@@ -14,6 +14,9 @@
  * limitations under the License.
  */
 
+import java.nio.charset.Charset;
+import java.io.UnsupportedEncodingException;
+
 /**
  * Simple string test.
  */
@@ -21,6 +24,7 @@
     public static void main(String args[]) {
         basicTest();
         indexTest();
+        constructorTest();
     }
 
     public static void basicTest() {
@@ -81,4 +85,36 @@
             subStr.indexOf('&') + ":" +
             baseStr.indexOf(0x12341234));
     }
+
+    public static void constructorTest() {
+        byte[] byteArray = "byteArray".getBytes();
+        char[] charArray = new char[] { 'c', 'h', 'a', 'r', 'A', 'r', 'r', 'a', 'y' };
+        String charsetName = "US-ASCII";
+        Charset charset = Charset.forName("UTF-8");
+        String string = "string";
+        StringBuffer stringBuffer = new StringBuffer("stringBuffer");
+        int [] codePoints = new int[] { 65, 66, 67, 68, 69 };
+        StringBuilder stringBuilder = new StringBuilder("stringBuilder");
+
+        String s1 = new String();
+        String s2 = new String(byteArray);
+        String s3 = new String(byteArray, 1);
+        String s4 = new String(byteArray, 0, 4);
+        String s5 = new String(byteArray, 2, 4, 5);
+
+        try {
+            String s6 = new String(byteArray, 2, 4, charsetName);
+            String s7 = new String(byteArray, charsetName);
+        } catch (UnsupportedEncodingException e) {
+            System.out.println("Got unexpected UnsupportedEncodingException");
+        }
+        String s8 = new String(byteArray, 3, 3, charset);
+        String s9 = new String(byteArray, charset);
+        String s10 = new String(charArray);
+        String s11 = new String(charArray, 0, 4);
+        String s12 = new String(string);
+        String s13 = new String(stringBuffer);
+        String s14 = new String(codePoints, 1, 3);
+        String s15 = new String(stringBuilder);
+    }
 }
diff --git a/test/115-native-bridge/nativebridge.cc b/test/115-native-bridge/nativebridge.cc
index 24e9600..db2fc9b 100644
--- a/test/115-native-bridge/nativebridge.cc
+++ b/test/115-native-bridge/nativebridge.cc
@@ -327,5 +327,7 @@
   .loadLibrary = &native_bridge_loadLibrary,
   .getTrampoline = &native_bridge_getTrampoline,
   .isSupported = &native_bridge_isSupported,
-  .getAppEnv = &native_bridge_getAppEnv
+  .getAppEnv = &native_bridge_getAppEnv,
+  .isCompatibleWith = nullptr,
+  .getSignalHandler = nullptr
 };
diff --git a/test/137-cfi/cfi.cc b/test/137-cfi/cfi.cc
new file mode 100644
index 0000000..b2d7e55
--- /dev/null
+++ b/test/137-cfi/cfi.cc
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#if __linux__
+#include <errno.h>
+#include <signal.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ptrace.h>
+#include <sys/wait.h>
+#endif
+
+#include "jni.h"
+
+#include <backtrace/Backtrace.h>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "utils.h"
+
+namespace art {
+
+// For testing debuggerd. We do not have expected-death tests, so can't test this by default.
+// Code for this is copied from SignalTest.
+static constexpr bool kCauseSegfault = false;
+char* go_away_compiler_cfi = nullptr;
+
+static void CauseSegfault() {
+#if defined(__arm__) || defined(__i386__) || defined(__x86_64__) || defined(__aarch64__)
+  // On supported architectures we cause a real SEGV.
+  *go_away_compiler_cfi = 'a';
+#else
+  // On other architectures we simulate SEGV.
+  kill(getpid(), SIGSEGV);
+#endif
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_sleep(JNIEnv*, jobject, jint, jboolean, jdouble) {
+  // Keep pausing.
+  for (;;) {
+    pause();
+  }
+}
+
+// Helper to look for a sequence in the stack trace.
+#if __linux__
+static bool CheckStack(Backtrace* bt, const std::vector<std::string>& seq) {
+  size_t cur_search_index = 0;  // The currently active index in seq.
+  CHECK_GT(seq.size(), 0U);
+
+  for (Backtrace::const_iterator it = bt->begin(); it != bt->end(); ++it) {
+    if (BacktraceMap::IsValid(it->map)) {
+      LOG(INFO) << "Got " << it->func_name << ", looking for " << seq[cur_search_index];
+      if (it->func_name == seq[cur_search_index]) {
+        cur_search_index++;
+        if (cur_search_index == seq.size()) {
+          return true;
+        }
+      }
+    }
+  }
+
+  return false;
+}
+#endif
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindInProcess(JNIEnv*, jobject, jint, jboolean) {
+#if __linux__
+  // TODO: What to do on Valgrind?
+
+  std::unique_ptr<Backtrace> bt(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, GetTid()));
+  if (!bt->Unwind(0, nullptr)) {
+    return JNI_FALSE;
+  } else if (bt->NumFrames() == 0) {
+    return JNI_FALSE;
+  }
+
+  // We cannot really parse an exact stack, as the optimizing compiler may inline some functions.
+  // This is also risky, as deduping might play a trick on us, so the test needs to make sure that
+  // only unique functions are being expected.
+  std::vector<std::string> seq = {
+      "Java_Main_unwindInProcess",                   // This function.
+      "boolean Main.unwindInProcess(int, boolean)",  // The corresponding Java native method frame.
+      "void Main.main(java.lang.String[])"           // The Java entry method.
+  };
+
+  bool result = CheckStack(bt.get(), seq);
+  if (!kCauseSegfault) {
+    return result ? JNI_TRUE : JNI_FALSE;
+  } else {
+    LOG(INFO) << "Result of check-stack: " << result;
+  }
+#endif
+
+  if (kCauseSegfault) {
+    CauseSegfault();
+  }
+
+  return JNI_FALSE;
+}
+
+#if __linux__
+static constexpr int kSleepTimeMicroseconds = 50000;            // 0.05 seconds
+static constexpr int kMaxTotalSleepTimeMicroseconds = 1000000;  // 1 second
+
+// Wait for a sigstop. This code is copied from libbacktrace.
+int wait_for_sigstop(pid_t tid, int* total_sleep_time_usec, bool* detach_failed ATTRIBUTE_UNUSED) {
+  for (;;) {
+    int status;
+    pid_t n = TEMP_FAILURE_RETRY(waitpid(tid, &status, __WALL | WNOHANG));
+    if (n == -1) {
+      PLOG(WARNING) << "waitpid failed: tid " << tid;
+      break;
+    } else if (n == tid) {
+      if (WIFSTOPPED(status)) {
+        return WSTOPSIG(status);
+      } else {
+        PLOG(ERROR) << "unexpected waitpid response: n=" << n << ", status=" << std::hex << status;
+        break;
+      }
+    }
+
+    if (*total_sleep_time_usec > kMaxTotalSleepTimeMicroseconds) {
+      PLOG(WARNING) << "timed out waiting for stop signal: tid=" << tid;
+      break;
+    }
+
+    usleep(kSleepTimeMicroseconds);
+    *total_sleep_time_usec += kSleepTimeMicroseconds;
+  }
+
+  return -1;
+}
+#endif
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindOtherProcess(JNIEnv*, jobject, jint pid_int) {
+#if __linux__
+  // TODO: What to do on Valgrind?
+  pid_t pid = static_cast<pid_t>(pid_int);
+
+  // OK, this is painful. debuggerd uses ptrace to unwind other processes.
+
+  if (ptrace(PTRACE_ATTACH, pid, 0, 0)) {
+    // Were not able to attach, bad.
+    PLOG(ERROR) << "Failed to attach.";
+    kill(pid, SIGCONT);
+    return JNI_FALSE;
+  }
+
+  kill(pid, SIGSTOP);
+
+  bool detach_failed = false;
+  int total_sleep_time_usec = 0;
+  int signal = wait_for_sigstop(pid, &total_sleep_time_usec, &detach_failed);
+  if (signal == -1) {
+    LOG(WARNING) << "wait_for_sigstop failed.";
+  }
+
+  std::unique_ptr<Backtrace> bt(Backtrace::Create(pid, BACKTRACE_CURRENT_THREAD));
+  bool result = true;
+  if (!bt->Unwind(0, nullptr)) {
+    result = false;
+  } else if (bt->NumFrames() == 0) {
+    result = false;
+  }
+
+  if (result) {
+    // See comment in unwindInProcess for non-exact stack matching.
+    std::vector<std::string> seq = {
+        // "Java_Main_sleep",                        // The sleep function being executed in the
+                                                     // other runtime.
+                                                     // Note: For some reason, the name isn't
+                                                     // resolved, so don't look for it right now.
+        "boolean Main.sleep(int, boolean, double)",  // The corresponding Java native method frame.
+        "void Main.main(java.lang.String[])"         // The Java entry method.
+    };
+
+    result = CheckStack(bt.get(), seq);
+  }
+
+  if (ptrace(PTRACE_DETACH, pid, 0, 0) != 0) {
+    PLOG(ERROR) << "Detach failed";
+  }
+
+  // Continue the process so we can kill it on the Java side.
+  kill(pid, SIGCONT);
+
+  return result ? JNI_TRUE : JNI_FALSE;
+#else
+  return JNI_FALSE;
+#endif
+}
+
+}  // namespace art
diff --git a/test/137-cfi/expected.txt b/test/137-cfi/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/137-cfi/expected.txt
diff --git a/test/137-cfi/info.txt b/test/137-cfi/info.txt
new file mode 100644
index 0000000..7d59605
--- /dev/null
+++ b/test/137-cfi/info.txt
@@ -0,0 +1 @@
+Test that unwinding with CFI info works.
diff --git a/test/137-cfi/src/Main.java b/test/137-cfi/src/Main.java
new file mode 100644
index 0000000..e184e66
--- /dev/null
+++ b/test/137-cfi/src/Main.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+public class Main {
+  // Whether to test local unwinding. Libunwind uses linker info to find executables. As we do
+  // not dlopen at the moment, this doesn't work, so keep it off for now.
+  public final static boolean TEST_LOCAL_UNWINDING = false;
+
+  // Unwinding another process, modelling debuggerd. This doesn't use the linker, so should work
+  // no matter whether we're using dlopen or not.
+  public final static boolean TEST_REMOTE_UNWINDING = true;
+
+  private boolean secondary;
+
+  public Main(boolean secondary) {
+      this.secondary = secondary;
+  }
+
+  public static void main(String[] args) throws Exception {
+      boolean secondary = false;
+      if (args.length > 0 && args[args.length - 1].equals("--secondary")) {
+          secondary = true;
+      }
+      new Main(secondary).run();
+  }
+
+  static {
+      System.loadLibrary("arttest");
+  }
+
+  private void run() {
+      if (secondary) {
+          if (!TEST_REMOTE_UNWINDING) {
+              throw new RuntimeException("Should not be running secondary!");
+          }
+          runSecondary();
+      } else {
+          runPrimary();
+      }
+  }
+
+  private void runSecondary() {
+      foo(true);
+      throw new RuntimeException("Didn't expect to get back...");
+  }
+
+  private void runPrimary() {
+      // First do the in-process unwinding.
+      if (TEST_LOCAL_UNWINDING && !foo(false)) {
+          System.out.println("Unwinding self failed.");
+      }
+
+      if (!TEST_REMOTE_UNWINDING) {
+          // Skip the remote step.
+          return;
+      }
+
+      // Fork the secondary.
+      String[] cmdline = getCmdLine();
+      String[] secCmdLine = new String[cmdline.length + 1];
+      System.arraycopy(cmdline, 0, secCmdLine, 0, cmdline.length);
+      secCmdLine[secCmdLine.length - 1] = "--secondary";
+      Process p = exec(secCmdLine);
+
+      try {
+          int pid = getPid(p);
+          if (pid <= 0) {
+              throw new RuntimeException("Couldn't parse process");
+          }
+
+          // Wait a bit, so the forked process has time to run until its sleep phase.
+          try {
+              Thread.sleep(5000);
+          } catch (Exception e) {
+              throw new RuntimeException(e);
+          }
+
+          if (!unwindOtherProcess(pid)) {
+              System.out.println("Unwinding other process failed.");
+          }
+      } finally {
+          // Kill the forked process.
+          p.destroy();
+      }
+  }
+
+  private static Process exec(String[] args) {
+      try {
+          return Runtime.getRuntime().exec(args);
+      } catch (Exception exc) {
+          throw new RuntimeException(exc);
+      }
+  }
+
+  private static int getPid(Process p) {
+      // Could do reflection for the private pid field, but String parsing is easier.
+      String s = p.toString();
+      if (s.startsWith("Process[pid=")) {
+          return Integer.parseInt(s.substring("Process[pid=".length(), s.length() - 1));
+      } else {
+          return -1;
+      }
+  }
+
+  // Read /proc/self/cmdline to find the invocation command line (so we can fork another runtime).
+  private static String[] getCmdLine() {
+      try {
+          BufferedReader in = new BufferedReader(new FileReader("/proc/self/cmdline"));
+          String s = in.readLine();
+          in.close();
+          return s.split("\0");
+      } catch (Exception exc) {
+          throw new RuntimeException(exc);
+      }
+  }
+
+  public boolean foo(boolean b) {
+      return bar(b);
+  }
+
+  public boolean bar(boolean b) {
+      if (b) {
+          return sleep(2, b, 1.0);
+      } else {
+          return unwindInProcess(1, b);
+      }
+  }
+
+  // Native functions. Note: to avoid deduping, they must all have different signatures.
+
+  public native boolean sleep(int i, boolean b, double dummy);
+
+  public native boolean unwindInProcess(int i, boolean b);
+  public native boolean unwindOtherProcess(int pid);
+}
diff --git a/test/139-register-natives/check b/test/139-register-natives/check
new file mode 100755
index 0000000..265ad85
--- /dev/null
+++ b/test/139-register-natives/check
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Strip any JNI registration error messages
+sed -e '/java_vm_ext/d' -e '/jni_internal.cc/d' "$2" > "$2.tmp"
+
+diff --strip-trailing-cr -q "$1" "$2.tmp" >/dev/null
diff --git a/test/139-register-natives/expected.txt b/test/139-register-natives/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/139-register-natives/expected.txt
diff --git a/test/139-register-natives/info.txt b/test/139-register-natives/info.txt
new file mode 100644
index 0000000..48e08a4
--- /dev/null
+++ b/test/139-register-natives/info.txt
@@ -0,0 +1 @@
+Tests the correct order of RegisterNatives.
diff --git a/test/139-register-natives/regnative.cc b/test/139-register-natives/regnative.cc
new file mode 100644
index 0000000..d9c8b31
--- /dev/null
+++ b/test/139-register-natives/regnative.cc
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "jni.h"
+
+namespace art {
+
+// Simple empty method. We will check for correct registration with UnsatisfiedLinkError.
+static void foo(JNIEnv*, jclass) {
+}
+
+static JNINativeMethod gMethods[] = {
+    { "foo", "()V", reinterpret_cast<void*>(foo) }
+};
+
+extern "C" JNIEXPORT jint JNICALL Java_Main_registerNatives(JNIEnv* env, jclass, jclass trg) {
+  return env->RegisterNatives(trg, gMethods, 1);
+}
+
+}  // namespace art
diff --git a/test/139-register-natives/src/Main.java b/test/139-register-natives/src/Main.java
new file mode 100644
index 0000000..35b2f9c
--- /dev/null
+++ b/test/139-register-natives/src/Main.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+  public static void main(String[] args) {
+    testRegistration1();
+    testRegistration2();
+    testRegistration3();
+  }
+
+  static {
+    System.loadLibrary("arttest");
+  }
+
+  // Test that a subclass' method is registered instead of a superclass' method.
+  private static void testRegistration1() {
+    registerNatives(TestSub.class);
+
+    expectNotThrows(new TestSub());
+    expectThrows(new TestSuper());
+  }
+
+  // Test that a superclass' method is registered if the subclass doesn't have a matching method.
+  private static void testRegistration2() {
+    registerNatives(TestSub2.class);
+
+    expectNotThrows(new TestSub2());
+    expectNotThrows(new TestSuper2());
+  }
+
+  // Test that registration fails if the subclass has a matching non-native method.
+  private static void testRegistration3() {
+    try {
+      registerNatives(TestSub3.class);
+      System.out.println("Expected exception for registerNatives(TestSub3.class)");
+    } catch (NoSuchMethodError ignored) {
+    }
+  }
+
+  private native static int registerNatives(Class c);
+
+  private static void expectThrows(Base b) {
+    try {
+      b.callMyFoo();
+      System.out.println("Expected exception for " + b.getClass().getName());
+    } catch (Throwable ignored) {
+    }
+  }
+
+  private static void expectNotThrows(Base b) {
+    try {
+      b.callMyFoo();
+    } catch (Throwable t) {
+      System.out.println("Did not expect an exception for " + b.getClass().getName());
+      t.printStackTrace(System.out);
+    }
+  }
+}
+
+abstract class Base {
+  public abstract void callMyFoo();
+}
+
+class TestSuper extends Base {
+  private native void foo();
+
+  @Override
+  public void callMyFoo() {
+    foo();
+  }
+}
+
+class TestSub extends TestSuper {
+  public native void foo();
+
+  @Override
+  public void callMyFoo() {
+    foo();
+  }
+}
+
+class TestSuper2 extends Base{
+  public native void foo();
+
+  @Override
+  public void callMyFoo() {
+    foo();
+  }
+}
+
+class TestSub2 extends TestSuper2 {
+}
+
+class TestSuper3 extends Base {
+  public native void foo();
+
+  @Override
+  public void callMyFoo() {
+    foo();
+  }
+}
+
+class TestSub3 extends TestSuper3 {
+  public void foo() {
+    System.out.println("TestSub3.foo()");
+  }
+}
diff --git a/test/476-checker-ctor-memory-barrier/src/Main.java b/test/476-checker-ctor-memory-barrier/src/Main.java
index 9badc0f..f24dc4a 100644
--- a/test/476-checker-ctor-memory-barrier/src/Main.java
+++ b/test/476-checker-ctor-memory-barrier/src/Main.java
@@ -27,9 +27,8 @@
   public ClassWithFinals obj;
 
   // CHECK-START: void ClassWithFinals.<init>(boolean) register (after)
-  // CHECK:     MemoryBarrier kind:StoreStore
-  // CHECK-NOT: {{.*}}
-  // CHECK:     ReturnVoid
+  // CHECK:      MemoryBarrier kind:StoreStore
+  // CHECK-NEXT: ReturnVoid
   public ClassWithFinals(boolean cond) {
     x = 0;
     if (cond) {
@@ -39,18 +38,16 @@
   }
 
   // CHECK-START: void ClassWithFinals.<init>() register (after)
-  // CHECK:     MemoryBarrier kind:StoreStore
-  // CHECK-NOT: {{.*}}
-  // CHECK:     ReturnVoid
+  // CHECK:      MemoryBarrier kind:StoreStore
+  // CHECK-NEXT: ReturnVoid
   public ClassWithFinals() {
     x = 0;
   }
 
   // CHECK-START: void ClassWithFinals.<init>(int) register (after)
-  // CHECK:     MemoryBarrier kind:StoreStore
-  // CHECK:     MemoryBarrier kind:StoreStore
-  // CHECK-NOT: {{.*}}
-  // CHECK:     ReturnVoid
+  // CHECK:      MemoryBarrier kind:StoreStore
+  // CHECK:      MemoryBarrier kind:StoreStore
+  // CHECK-NEXT: ReturnVoid
   public ClassWithFinals(int x) {
     // This should have two barriers:
     //   - one for the constructor
@@ -62,33 +59,32 @@
 
 class InheritFromClassWithFinals extends ClassWithFinals {
   // CHECK-START: void InheritFromClassWithFinals.<init>() register (after)
-  // CHECK:     MemoryBarrier kind:StoreStore
-  // CHECK-NOT: {{.*}}
-  // CHECK:     ReturnVoid
+  // CHECK:      MemoryBarrier kind:StoreStore
+  // CHECK-NEXT: ReturnVoid
 
   // CHECK-START: void InheritFromClassWithFinals.<init>() register (after)
-  // CHECK-NOT: InvokeStaticOrDirect
+  // CHECK-NOT:  InvokeStaticOrDirect
   public InheritFromClassWithFinals() {
     // Should inline the super constructor.
   }
 
   // CHECK-START: void InheritFromClassWithFinals.<init>(boolean) register (after)
-  // CHECK:     InvokeStaticOrDirect
+  // CHECK:      InvokeStaticOrDirect
 
   // CHECK-START: void InheritFromClassWithFinals.<init>(boolean) register (after)
-  // CHECK-NOT: MemoryBarrier kind:StoreStore
+  // CHECK-NOT:  MemoryBarrier kind:StoreStore
   public InheritFromClassWithFinals(boolean cond) {
     super(cond);
     // should not inline the super constructor
   }
 
   // CHECK-START: void InheritFromClassWithFinals.<init>(int) register (after)
-  // CHECK:     MemoryBarrier kind:StoreStore
-  // CHECK:     MemoryBarrier kind:StoreStore
-  // CHECK:     ReturnVoid
+  // CHECK:      MemoryBarrier kind:StoreStore
+  // CHECK:      MemoryBarrier kind:StoreStore
+  // CHECK:      ReturnVoid
 
   // CHECK-START: void InheritFromClassWithFinals.<init>(int) register (after)
-  // CHECK-NOT: InvokeStaticOrDirect
+  // CHECK-NOT:  InvokeStaticOrDirect
   public InheritFromClassWithFinals(int unused) {
     // Should inline the super constructor and insert a memory barrier.
 
@@ -101,9 +97,8 @@
   final int y;
 
   // CHECK-START: void HaveFinalsAndInheritFromClassWithFinals.<init>() register (after)
-  // CHECK:     MemoryBarrier kind:StoreStore
-  // CHECK-NOT: {{.*}}
-  // CHECK:     ReturnVoid
+  // CHECK:      MemoryBarrier kind:StoreStore
+  // CHECK-NEXT: ReturnVoid
 
   // CHECK-START: void HaveFinalsAndInheritFromClassWithFinals.<init>() register (after)
   // CHECK-NOT: InvokeStaticOrDirect
@@ -113,10 +108,9 @@
   }
 
   // CHECK-START: void HaveFinalsAndInheritFromClassWithFinals.<init>(boolean) register (after)
-  // CHECK:     InvokeStaticOrDirect
-  // CHECK:     MemoryBarrier kind:StoreStore
-  // CHECK-NOT: {{.*}}
-  // CHECK:     ReturnVoid
+  // CHECK:      InvokeStaticOrDirect
+  // CHECK:      MemoryBarrier kind:StoreStore
+  // CHECK-NEXT: ReturnVoid
   public HaveFinalsAndInheritFromClassWithFinals(boolean cond) {
     super(cond);
     // should not inline the super constructor
@@ -124,14 +118,13 @@
   }
 
   // CHECK-START: void HaveFinalsAndInheritFromClassWithFinals.<init>(int) register (after)
-  // CHECK:     MemoryBarrier kind:StoreStore
-  // CHECK:     MemoryBarrier kind:StoreStore
-  // CHECK:     MemoryBarrier kind:StoreStore
-  // CHECK-NOT: {{.*}}
-  // CHECK:     ReturnVoid
+  // CHECK:      MemoryBarrier kind:StoreStore
+  // CHECK:      MemoryBarrier kind:StoreStore
+  // CHECK:      MemoryBarrier kind:StoreStore
+  // CHECK-NEXT: ReturnVoid
 
   // CHECK-START: void HaveFinalsAndInheritFromClassWithFinals.<init>(int) register (after)
-  // CHECK-NOT: InvokeStaticOrDirect
+  // CHECK-NOT:  InvokeStaticOrDirect
   public HaveFinalsAndInheritFromClassWithFinals(int unused) {
     // Should inline the super constructor and keep just one memory barrier.
     y = 0;
@@ -146,55 +139,51 @@
 public class Main {
 
   // CHECK-START: ClassWithFinals Main.noInlineNoConstructorBarrier() register (after)
-  // CHECK:     InvokeStaticOrDirect
+  // CHECK:      InvokeStaticOrDirect
 
   // CHECK-START: ClassWithFinals Main.noInlineNoConstructorBarrier() register (after)
-  // CHECK-NOT: MemoryBarrier kind:StoreStore
+  // CHECK-NOT:  MemoryBarrier kind:StoreStore
   public static ClassWithFinals noInlineNoConstructorBarrier() {
     return new ClassWithFinals(false);
   }
 
   // CHECK-START: void Main.inlineNew() register (after)
-  // CHECK:     MemoryBarrier kind:StoreStore
-  // CHECK-NOT: {{.*}}
-  // CHECK:     Return
+  // CHECK:      MemoryBarrier kind:StoreStore
+  // CHECK-NEXT: ReturnVoid
 
   // CHECK-START: void Main.inlineNew() register (after)
-  // CHECK-NOT: InvokeStaticOrDirect
+  // CHECK-NOT:  InvokeStaticOrDirect
   public static void inlineNew() {
     new ClassWithFinals();
   }
 
   // CHECK-START: void Main.inlineNew1() register (after)
-  // CHECK:     MemoryBarrier kind:StoreStore
-  // CHECK-NOT: {{.*}}
-  // CHECK:     Return
+  // CHECK:      MemoryBarrier kind:StoreStore
+  // CHECK-NEXT: ReturnVoid
 
   // CHECK-START: void Main.inlineNew1() register (after)
-  // CHECK-NOT: InvokeStaticOrDirect
+  // CHECK-NOT:  InvokeStaticOrDirect
   public static void inlineNew1() {
     new InheritFromClassWithFinals();
   }
 
   // CHECK-START: void Main.inlineNew2() register (after)
-  // CHECK:     MemoryBarrier kind:StoreStore
-  // CHECK-NOT: {{.*}}
-  // CHECK:     Return
+  // CHECK:      MemoryBarrier kind:StoreStore
+  // CHECK-NEXT: ReturnVoid
 
   // CHECK-START: void Main.inlineNew2() register (after)
-  // CHECK-NOT: InvokeStaticOrDirect
+  // CHECK-NOT:  InvokeStaticOrDirect
   public static void inlineNew2() {
     new HaveFinalsAndInheritFromClassWithFinals();
   }
 
   // CHECK-START: void Main.inlineNew3() register (after)
-  // CHECK:     MemoryBarrier kind:StoreStore
-  // CHECK:     MemoryBarrier kind:StoreStore
-  // CHECK-NOT: {{.*}}
-  // CHECK:     Return
+  // CHECK:      MemoryBarrier kind:StoreStore
+  // CHECK:      MemoryBarrier kind:StoreStore
+  // CHECK-NEXT: ReturnVoid
 
   // CHECK-START: void Main.inlineNew3() register (after)
-  // CHECK-NOT: InvokeStaticOrDirect
+  // CHECK-NOT:  InvokeStaticOrDirect
   public static void inlineNew3() {
     new HaveFinalsAndInheritFromClassWithFinals();
     new HaveFinalsAndInheritFromClassWithFinals();
diff --git a/test/478-checker-clinit-check-pruning/src/Main.java b/test/478-checker-clinit-check-pruning/src/Main.java
index 61199a7..e8739b8 100644
--- a/test/478-checker-clinit-check-pruning/src/Main.java
+++ b/test/478-checker-clinit-check-pruning/src/Main.java
@@ -24,12 +24,12 @@
    */
 
   // CHECK-START: void Main.invokeStaticInlined() builder (after)
-  // CHECK-DAG:     <<LoadClass:l\d+>>    LoadClass
+  // CHECK-DAG:     <<LoadClass:l\d+>>    LoadClass gen_clinit_check:false
   // CHECK-DAG:     <<ClinitCheck:l\d+>>  ClinitCheck [<<LoadClass>>]
   // CHECK-DAG:                           InvokeStaticOrDirect [<<ClinitCheck>>]
 
   // CHECK-START: void Main.invokeStaticInlined() inliner (after)
-  // CHECK-DAG:     <<LoadClass:l\d+>>    LoadClass
+  // CHECK-DAG:     <<LoadClass:l\d+>>    LoadClass gen_clinit_check:false
   // CHECK-DAG:     <<ClinitCheck:l\d+>>  ClinitCheck [<<LoadClass>>]
 
   // CHECK-START: void Main.invokeStaticInlined() inliner (after)
@@ -42,7 +42,7 @@
   // CFG as it is before the next pass (liveness analysis) instead.
 
   // CHECK-START: void Main.invokeStaticInlined() liveness (before)
-  // CHECK-DAG:                           LoadClass
+  // CHECK-DAG:                           LoadClass gen_clinit_check:true
 
   // CHECK-START: void Main.invokeStaticInlined() liveness (before)
   // CHECK-NOT:                           ClinitCheck
@@ -67,12 +67,12 @@
    */
 
   // CHECK-START: void Main.invokeStaticNotInlined() builder (after)
-  // CHECK-DAG:     <<LoadClass:l\d+>>    LoadClass
+  // CHECK-DAG:     <<LoadClass:l\d+>>    LoadClass gen_clinit_check:false
   // CHECK-DAG:     <<ClinitCheck:l\d+>>  ClinitCheck [<<LoadClass>>]
   // CHECK-DAG:                           InvokeStaticOrDirect [<<ClinitCheck>>]
 
   // CHECK-START: void Main.invokeStaticNotInlined() inliner (after)
-  // CHECK-DAG:     <<LoadClass:l\d+>>    LoadClass
+  // CHECK-DAG:     <<LoadClass:l\d+>>    LoadClass gen_clinit_check:false
   // CHECK-DAG:     <<ClinitCheck:l\d+>>  ClinitCheck [<<LoadClass>>]
   // CHECK-DAG:                           InvokeStaticOrDirect [<<ClinitCheck>>]
 
@@ -260,6 +260,44 @@
     }
   }
 
+
+  /*
+   * Verify that if we have a static call immediately after the load class
+   * we don't do generate a clinit check.
+   */
+
+  // CHECK-START: void Main.noClinitBecauseOfInvokeStatic() liveness (before)
+  // CHECK-DAG:     <<IntConstant:i\d+>>  IntConstant 0
+  // CHECK-DAG:     <<LoadClass:l\d+>>    LoadClass gen_clinit_check:false
+  // CHECK-DAG:                           InvokeStaticOrDirect
+  // CHECK-DAG:                           StaticFieldSet [<<LoadClass>>,<<IntConstant>>]
+
+  // CHECK-START: void Main.noClinitBecauseOfInvokeStatic() liveness (before)
+  // CHECK-NOT:                           ClinitCheck
+
+  static void noClinitBecauseOfInvokeStatic() {
+    ClassWithClinit2.staticMethod();
+    ClassWithClinit2.doThrow = false;
+  }
+
+  /*
+   * Verify that if the static call is after a field access, the load class
+   * will generate a clinit check.
+   */
+
+  // CHECK-START: void Main.clinitBecauseOfFieldAccess() liveness (before)
+  // CHECK-DAG:     <<IntConstant:i\d+>>  IntConstant 0
+  // CHECK-DAG:     <<LoadClass:l\d+>>    LoadClass gen_clinit_check:true
+  // CHECK-DAG:                           StaticFieldSet [<<LoadClass>>,<<IntConstant>>]
+  // CHECK-DAG:                           InvokeStaticOrDirect
+
+  // CHECK-START: void Main.clinitBecauseOfFieldAccess() liveness (before)
+  // CHECK-NOT:                           ClinitCheck
+  static void clinitBecauseOfFieldAccess() {
+    ClassWithClinit2.doThrow = false;
+    ClassWithClinit2.staticMethod();
+  }
+
   // TODO: Add a test for the case of a static method whose declaring
   // class type index is not available (i.e. when `storage_index`
   // equals `DexFile::kDexNoIndex` in
diff --git a/test/486-checker-must-do-null-check/expected.txt b/test/486-checker-must-do-null-check/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/486-checker-must-do-null-check/expected.txt
diff --git a/test/486-checker-must-do-null-check/info.txt b/test/486-checker-must-do-null-check/info.txt
new file mode 100644
index 0000000..494ff1c
--- /dev/null
+++ b/test/486-checker-must-do-null-check/info.txt
@@ -0,0 +1 @@
+Verifies MustDoNullCheck() on InstanceOf and CheckCast
diff --git a/test/486-checker-must-do-null-check/src/Main.java b/test/486-checker-must-do-null-check/src/Main.java
new file mode 100644
index 0000000..f285566
--- /dev/null
+++ b/test/486-checker-must-do-null-check/src/Main.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+  // CHECK-START: void Main.InstanceOfPreChecked(java.lang.Object) instruction_simplifier (after)
+  // CHECK:       InstanceOf must_do_null_check:false
+  public void InstanceOfPreChecked(Object o) throws Exception {
+    o.toString();
+    if (o instanceof Main) {
+      throw new Exception();
+    }
+  }
+
+  // CHECK-START: void Main.InstanceOf(java.lang.Object) instruction_simplifier (after)
+  // CHECK:       InstanceOf must_do_null_check:true
+  public void InstanceOf(Object o) throws Exception {
+    if (o instanceof Main) {
+      throw new Exception();
+    }
+  }
+
+  // CHECK-START: void Main.CheckCastPreChecked(java.lang.Object) instruction_simplifier (after)
+  // CHECK:       CheckCast must_do_null_check:false
+  public void CheckCastPreChecked(Object o) {
+    o.toString();
+    ((Main)o).Bar();
+  }
+
+  // CHECK-START: void Main.CheckCast(java.lang.Object) instruction_simplifier (after)
+  // CHECK:       CheckCast must_do_null_check:true
+  public void CheckCast(Object o) {
+    ((Main)o).Bar();
+  }
+
+  void Bar() {throw new RuntimeException();}
+
+  public static void main(String[] sa) {
+    Main t = new Main();
+  }
+}
diff --git a/test/Android.libarttest.mk b/test/Android.libarttest.mk
index 5e768ee..57d06c4 100644
--- a/test/Android.libarttest.mk
+++ b/test/Android.libarttest.mk
@@ -28,6 +28,8 @@
   116-nodex2oat/nodex2oat.cc \
   117-nopatchoat/nopatchoat.cc \
   118-noimage-dex2oat/noimage-dex2oat.cc \
+  137-cfi/cfi.cc \
+  139-register-natives/regnative.cc \
   454-get-vreg/get_vreg_jni.cc \
   455-set-vreg/set_vreg_jni.cc \
   457-regs/regs_jni.cc \
@@ -56,7 +58,7 @@
     LOCAL_MODULE_TAGS := tests
   endif
   LOCAL_SRC_FILES := $(LIBARTTEST_COMMON_SRC_FILES)
-  LOCAL_SHARED_LIBRARIES += libartd
+  LOCAL_SHARED_LIBRARIES += libartd libbacktrace
   LOCAL_C_INCLUDES += $(ART_C_INCLUDES) art/runtime
   LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk
   LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.libarttest.mk
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 07e7620..8b074ae 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -264,10 +264,12 @@
   117-nopatchoat \
   118-noimage-dex2oat \
   119-noimage-patchoat \
+  137-cfi \
   138-duplicate-classes-check2
 
 # This test fails without an image.
 TEST_ART_BROKEN_NO_IMAGE_RUN_TESTS := \
+  137-cfi \
   138-duplicate-classes-check
 
 ifneq (,$(filter no-dex2oat,$(PREBUILD_TYPES)))
@@ -294,9 +296,13 @@
 
 TEST_ART_BROKEN_FALLBACK_RUN_TESTS :=
 
+# 137:
+# This test unrolls and expects managed frames, but tracing means we run the interpreter.
+# 802:
 # This test dynamically enables tracing to force a deoptimization. This makes the test meaningless
 # when already tracing, and writes an error message that we do not want to check for.
 TEST_ART_BROKEN_TRACING_RUN_TESTS := \
+  137-cfi \
   802-deoptimization
 
 ifneq (,$(filter trace,$(TRACE_TYPES)))
@@ -323,6 +329,8 @@
   118-noimage-dex2oat \
   119-noimage-patchoat \
   131-structural-change \
+  137-cfi \
+  139-register-natives \
   454-get-vreg \
   455-set-vreg \
   457-regs \
@@ -337,6 +345,33 @@
 
 TEST_ART_BROKEN_NDEBUG_TESTS :=
 
+# Known broken tests for the interpreter.
+# CFI unwinding expects managed frames.
+TEST_ART_BROKEN_INTERPRETER_RUN_TESTS := \
+  137-cfi
+
+ifneq (,$(filter interpreter,$(COMPILER_TYPES)))
+  ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
+      interpreter,$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
+      $(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES),$(TEST_ART_BROKEN_INTERPRETER_RUN_TESTS),$(ALL_ADDRESS_SIZES))
+endif
+
+TEST_ART_BROKEN_INTERPRETER_RUN_TESTS :=
+
+# Known broken tests for the JIT.
+# CFI unwinding expects managed frames, and the test does not iterate enough to even compile. JIT
+# also uses Generic JNI instead of the JNI compiler.
+TEST_ART_BROKEN_JIT_RUN_TESTS := \
+  137-cfi
+
+ifneq (,$(filter jit,$(COMPILER_TYPES)))
+  ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
+      jit,$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
+      $(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES),$(TEST_ART_BROKEN_JIT_RUN_TESTS),$(ALL_ADDRESS_SIZES))
+endif
+
+TEST_ART_BROKEN_JIT_RUN_TESTS :=
+
 # Known broken tests for the default compiler (Quick).
 TEST_ART_BROKEN_DEFAULT_RUN_TESTS := \
   457-regs
@@ -428,7 +463,9 @@
 TEST_ART_BROKEN_OPTIMIZING_DEBUGGABLE_RUN_TESTS :=
 
 # Tests that should fail in the read barrier configuration.
-TEST_ART_BROKEN_READ_BARRIER_RUN_TESTS :=
+# 137: Read barrier forces interpreter. Cannot run this with the interpreter.
+TEST_ART_BROKEN_READ_BARRIER_RUN_TESTS := \
+  137-cfi
 
 ifeq ($(ART_USE_READ_BARRIER),true)
   ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
@@ -438,6 +475,24 @@
 
 TEST_ART_BROKEN_READ_BARRIER_RUN_TESTS :=
 
+# Tests that should fail in the heap poisoning configuration.
+# 137: Heap poisoning forces interpreter. Cannot run this with the interpreter.
+TEST_ART_BROKEN_HEAP_POISONING_RUN_TESTS := \
+  137-cfi
+
+ifeq ($(ART_HEAP_POISONING),true)
+  ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
+      $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
+      $(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES),$(TEST_ART_BROKEN_HEAP_POISONING_RUN_TESTS),$(ALL_ADDRESS_SIZES))
+endif
+
+TEST_ART_BROKEN_HEAP_POISONING_RUN_TESTS :=
+
+# Test 137-cfi works in 32-bit only until we enable 64-bit ELF files.
+ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
+    $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
+    $(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES),137-cfi,64)
+
 # Clear variables ahead of appending to them when defining tests.
 $(foreach target, $(TARGET_TYPES), $(eval ART_RUN_TEST_$(call name-to-var,$(target))_RULES :=))
 $(foreach target, $(TARGET_TYPES), \
diff --git a/test/MultiDexModifiedSecondary/Main.java b/test/MultiDexModifiedSecondary/Main.java
new file mode 100644
index 0000000..659dba9
--- /dev/null
+++ b/test/MultiDexModifiedSecondary/Main.java
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+class Main {
+    public static void main(String args[]) {
+      Second second = new Second();
+      System.out.println(second.getSecond());
+    }
+}
diff --git a/test/MultiDexModifiedSecondary/README.txt b/test/MultiDexModifiedSecondary/README.txt
new file mode 100644
index 0000000..4cf3a56
--- /dev/null
+++ b/test/MultiDexModifiedSecondary/README.txt
@@ -0,0 +1,4 @@
+MultiDexModifiedSecondary is designed to result in a multidex file that has
+the same classes.dex file as MultiDex, but a different classes2.dex.
+
+This is used in the OatFileAssistantTest.MultiDexSecondaryOutOfDate gtest.
diff --git a/test/MultiDexModifiedSecondary/Second.java b/test/MultiDexModifiedSecondary/Second.java
new file mode 100644
index 0000000..3555a7f
--- /dev/null
+++ b/test/MultiDexModifiedSecondary/Second.java
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+class Second {
+  public String getThird() {
+    return "I Third That.";
+  }
+
+  public String getSecond() {
+    return "I Second That.";
+  }
+}
diff --git a/test/MultiDexModifiedSecondary/main.jpp b/test/MultiDexModifiedSecondary/main.jpp
new file mode 100644
index 0000000..a5d7a6c
--- /dev/null
+++ b/test/MultiDexModifiedSecondary/main.jpp
@@ -0,0 +1,3 @@
+main:
+  @@com.android.jack.annotations.ForceInMainDex
+  class Main
diff --git a/test/MultiDexModifiedSecondary/main.list b/test/MultiDexModifiedSecondary/main.list
new file mode 100644
index 0000000..44ba78e
--- /dev/null
+++ b/test/MultiDexModifiedSecondary/main.list
@@ -0,0 +1 @@
+Main.class
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index 1c44958..240ed41 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -226,7 +226,11 @@
 
 if [ "$USE_JVM" = "y" ]; then
   # Xmx is necessary since we don't pass down the ART flags to JVM.
-  ${JAVA} ${DEBUGGER_OPTS} ${JVM_VERIFY_ARG} -Xmx256m -classpath classes $MAIN "$@"
+  cmdline="${JAVA} ${DEBUGGER_OPTS} ${JVM_VERIFY_ARG} -Xmx256m -classpath classes ${FLAGS} $MAIN $@"
+  if [ "$DEV_MODE" = "y" ]; then
+    echo $cmdline
+  fi
+  $cmdline
   exit
 fi
 
diff --git a/test/run-test b/test/run-test
index 54c6bbd..ed33099 100755
--- a/test/run-test
+++ b/test/run-test
@@ -75,7 +75,7 @@
 check_cmd="check"
 output="output.txt"
 build_output="build-output.txt"
-cfg_output="cfg-output.txt"
+cfg_output="graph.cfg"
 lib="libartd.so"
 run_args="--quiet"
 build_args=""
@@ -368,6 +368,9 @@
     else
       run_args="${run_args} --no-relocate"
     fi
+elif [ "$runtime" = "jvm" ]; then
+    # TODO: Detect whether the host is 32-bit or 64-bit.
+    run_args="${run_args} --runtime-option -Djava.library.path=${ANDROID_HOST_OUT}/lib64"
 fi
 
 if [ "$have_image" = "no" ]; then
diff --git a/tools/checker/common/immutables.py b/tools/checker/common/immutables.py
new file mode 100644
index 0000000..e016867
--- /dev/null
+++ b/tools/checker/common/immutables.py
@@ -0,0 +1,25 @@
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+class ImmutableDict(dict):
+  def __setitem__(self, key, value):
+    raise RuntimeError("Cannot modify ImmutableDict")
+
+  def __delitem__(self, key):
+    raise RuntimeError("Cannot modify ImmutableDict")
+
+  def copyWith(self, key, value):
+    newDict = ImmutableDict(self)
+    dict.__setitem__(newDict, key, value)
+    return newDict
diff --git a/tools/checker/file_format/checker/parser.py b/tools/checker/file_format/checker/parser.py
index d7a38da..4eed391 100644
--- a/tools/checker/file_format/checker/parser.py
+++ b/tools/checker/file_format/checker/parser.py
@@ -54,6 +54,11 @@
   if plainLine is not None:
     return (plainLine, TestAssertion.Variant.InOrder, lineNo), None
 
+  # 'CHECK-NEXT' lines are in-order but must match the very next line.
+  nextLine = __extractLine(prefix + "-NEXT", line)
+  if nextLine is not None:
+    return (nextLine, TestAssertion.Variant.NextLine, lineNo), None
+
   # 'CHECK-DAG' lines are no-order assertions.
   dagLine = __extractLine(prefix + "-DAG", line)
   if dagLine is not None:
diff --git a/tools/checker/file_format/checker/struct.py b/tools/checker/file_format/checker/struct.py
index 381c92b..6a54142 100644
--- a/tools/checker/file_format/checker/struct.py
+++ b/tools/checker/file_format/checker/struct.py
@@ -42,7 +42,7 @@
     self.startLineNo = startLineNo
 
     if not self.name:
-      Logger.fail("Test case does not have a name", self.parent.fileName, self.startLineNo)
+      Logger.fail("Test case does not have a name", self.fileName, self.startLineNo)
 
     self.parent.addTestCase(self)
 
@@ -51,6 +51,13 @@
     return self.parent.fileName
 
   def addAssertion(self, new_assertion):
+    if new_assertion.variant == TestAssertion.Variant.NextLine:
+      if not self.assertions or \
+         (self.assertions[-1].variant != TestAssertion.Variant.InOrder and \
+          self.assertions[-1].variant != TestAssertion.Variant.NextLine):
+        Logger.fail("A next-line assertion can only be placed after an "
+                    "in-order assertion or another next-line assertion.",
+                    new_assertion.fileName, new_assertion.lineNo)
     self.assertions.append(new_assertion)
 
   def __eq__(self, other):
@@ -63,7 +70,7 @@
 
   class Variant(object):
     """Supported types of assertions."""
-    InOrder, DAG, Not = range(3)
+    InOrder, NextLine, DAG, Not = range(4)
 
   def __init__(self, parent, variant, originalText, lineNo):
     assert isinstance(parent, TestCase)
diff --git a/tools/checker/file_format/checker/test.py b/tools/checker/file_format/checker/test.py
index 475e8c3..453deed 100644
--- a/tools/checker/file_format/checker/test.py
+++ b/tools/checker/file_format/checker/test.py
@@ -192,9 +192,12 @@
 
   def assertParsesTo(self, checkerText, expectedData):
     expectedFile = self.createFile(expectedData)
-    actualFile = ParseCheckerStream("<test_file>", "CHECK", io.StringIO(ToUnicode(checkerText)))
+    actualFile = self.parse(checkerText)
     return self.assertEqual(expectedFile, actualFile)
 
+  def parse(self, checkerText):
+    return ParseCheckerStream("<test_file>", "CHECK", io.StringIO(ToUnicode(checkerText)))
+
   def test_EmptyFile(self):
     self.assertParsesTo("", [])
 
@@ -227,12 +230,40 @@
     self.assertParsesTo(
       """
         // CHECK-START: Example Group
-        // CHECK:     foo
-        // CHECK-NOT: bar
-        // CHECK-DAG: abc
-        // CHECK-DAG: def
+        // CHECK:      foo1
+        // CHECK:      foo2
+        // CHECK-NEXT: foo3
+        // CHECK-NEXT: foo4
+        // CHECK-NOT:  bar
+        // CHECK-DAG:  abc
+        // CHECK-DAG:  def
       """,
-      [ ( "Example Group", [ ("foo", TestAssertion.Variant.InOrder),
+      [ ( "Example Group", [ ("foo1", TestAssertion.Variant.InOrder),
+                             ("foo2", TestAssertion.Variant.InOrder),
+                             ("foo3", TestAssertion.Variant.NextLine),
+                             ("foo4", TestAssertion.Variant.NextLine),
                              ("bar", TestAssertion.Variant.Not),
                              ("abc", TestAssertion.Variant.DAG),
                              ("def", TestAssertion.Variant.DAG) ] ) ])
+
+  def test_MisplacedNext(self):
+    with self.assertRaises(CheckerException):
+      self.parse(
+        """
+          // CHECK-START: Example Group
+          // CHECK-DAG:  foo
+          // CHECK-NEXT: bar
+        """)
+    with self.assertRaises(CheckerException):
+      self.parse(
+        """
+          // CHECK-START: Example Group
+          // CHECK-NOT:  foo
+          // CHECK-NEXT: bar
+        """)
+    with self.assertRaises(CheckerException):
+      self.parse(
+        """
+          // CHECK-START: Example Group
+          // CHECK-NEXT: bar
+        """)
diff --git a/tools/checker/match/file.py b/tools/checker/match/file.py
index 2ed4aa7..b22211a 100644
--- a/tools/checker/match/file.py
+++ b/tools/checker/match/file.py
@@ -12,126 +12,143 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+from collections                      import namedtuple
+from common.immutables                import ImmutableDict
 from common.logger                    import Logger
 from file_format.c1visualizer.struct  import C1visualizerFile, C1visualizerPass
 from file_format.checker.struct       import CheckerFile, TestCase, TestAssertion
 from match.line                       import MatchLines
 
-def __headAndTail(list):
-  return list[0], list[1:]
+MatchScope = namedtuple("MatchScope", ["start", "end"])
+MatchInfo = namedtuple("MatchInfo", ["scope", "variables"])
 
-def __splitByVariant(lines, variant):
-  """ Splits a list of check lines at index 'i' such that lines[i] is the first
-      element whose variant is not equal to the given parameter.
+class MatchFailedException(Exception):
+  def __init__(self, assertion, lineNo):
+    self.assertion = assertion
+    self.lineNo = lineNo
+
+def splitIntoGroups(assertions):
+  """ Breaks up a list of assertions, grouping instructions which should be
+      tested in the same scope (consecutive DAG and NOT instructions).
+   """
+  splitAssertions = []
+  lastVariant = None
+  for assertion in assertions:
+    if (assertion.variant == lastVariant and
+        assertion.variant in [TestAssertion.Variant.DAG, TestAssertion.Variant.Not]):
+      splitAssertions[-1].append(assertion)
+    else:
+      splitAssertions.append([assertion])
+      lastVariant = assertion.variant
+  return splitAssertions
+
+def findMatchingLine(assertion, c1Pass, scope, variables, excludeLines=[]):
+  """ Finds the first line in `c1Pass` which matches `assertion`.
+
+  Scan only lines numbered between `scope.start` and `scope.end` and not on the
+  `excludeLines` list.
+
+  Returns the index of the `c1Pass` line matching the assertion and variables
+  values after the match.
+
+  Raises MatchFailedException if no such `c1Pass` line can be found.
   """
-  i = 0
-  while i < len(lines) and lines[i].variant == variant:
-    i += 1
-  return lines[:i], lines[i:]
+  for i in range(scope.start, scope.end):
+    if i in excludeLines: continue
+    newVariables = MatchLines(assertion, c1Pass.body[i], variables)
+    if newVariables is not None:
+      return MatchInfo(MatchScope(i, i), newVariables)
+  raise MatchFailedException(assertion, scope.start)
 
-def __nextIndependentChecks(checkLines):
-  """ Extracts the first sequence of check lines which are independent of each
-      other's match location, i.e. either consecutive DAG lines or a single
-      InOrder line. Any Not lines preceeding this sequence are also extracted.
+def matchDagGroup(assertions, c1Pass, scope, variables):
+  """ Attempts to find matching `c1Pass` lines for a group of DAG assertions.
+
+  Assertions are matched in the list order and variable values propagated. Only
+  lines in `scope` are scanned and each line can only match one assertion.
+
+  Returns the range of `c1Pass` lines covered by this group (min/max of matching
+  line numbers) and the variable values after the match of the last assertion.
+
+  Raises MatchFailedException when an assertion cannot be satisfied.
   """
-  notChecks, checkLines = __splitByVariant(checkLines, TestAssertion.Variant.Not)
-  if not checkLines:
-    return notChecks, [], []
-
-  head, tail = __headAndTail(checkLines)
-  if head.variant == TestAssertion.Variant.InOrder:
-    return notChecks, [head], tail
-  else:
-    assert head.variant == TestAssertion.Variant.DAG
-    independentChecks, checkLines = __splitByVariant(checkLines, TestAssertion.Variant.DAG)
-    return notChecks, independentChecks, checkLines
-
-def __findFirstMatch(checkLine, outputLines, startLineNo, lineFilter, varState):
-  """ If successful, returns the line number of the first output line matching
-      the check line and the updated variable state. Otherwise returns -1 and
-      None, respectively. The 'lineFilter' parameter can be used to supply a
-      list of line numbers (counting from 1) which should be skipped.
-  """
-  matchLineNo = startLineNo
-  for outputLine in outputLines:
-    if matchLineNo not in lineFilter:
-      newVarState = MatchLines(checkLine, outputLine, varState)
-      if newVarState is not None:
-        return matchLineNo, newVarState
-    matchLineNo += 1
-  return -1, None
-
-def __matchIndependentChecks(checkLines, outputLines, startLineNo, varState):
-  """ Matches the given positive check lines against the output in order of
-      appearance. Variable state is propagated but the scope of the search
-      remains the same for all checks. Each output line can only be matched
-      once. If all check lines are matched, the resulting variable state is
-      returned together with the remaining output. The function also returns
-      output lines which appear before either of the matched lines so they can
-      be tested against Not checks.
-  """
-  # If no checks are provided, skip over the entire output.
-  if not checkLines:
-    return outputLines, [], startLineNo + len(outputLines), varState
-
-  # Keep track of which lines have been matched.
   matchedLines = []
+  for assertion in assertions:
+    assert assertion.variant == TestAssertion.Variant.DAG
+    match = findMatchingLine(assertion, c1Pass, scope, variables, matchedLines)
+    variables = match.variables
+    assert match.scope.start == match.scope.end
+    assert match.scope.start not in matchedLines
+    matchedLines.append(match.scope.start)
+  return MatchInfo(MatchScope(min(matchedLines), max(matchedLines)), variables)
 
-  # Find first unused output line which matches each check line.
-  for checkLine in checkLines:
-    matchLineNo, varState = \
-      __findFirstMatch(checkLine, outputLines, startLineNo, matchedLines, varState)
-    if varState is None:
-      Logger.testFailed("Could not match check line \"" + checkLine.originalText + "\" " +
-                        "starting from output line " + str(startLineNo),
-                        checkLine.fileName, checkLine.lineNo)
-    matchedLines.append(matchLineNo)
+def testNotGroup(assertions, c1Pass, scope, variables):
+  """ Verifies that none of the given NOT assertions matches a line inside
+      the given `scope` of `c1Pass` lines.
 
-  # Return new variable state and the output lines which lie outside the
-  # match locations of this independent group.
-  minMatchLineNo = min(matchedLines)
-  maxMatchLineNo = max(matchedLines)
-  preceedingLines = outputLines[:minMatchLineNo - startLineNo]
-  remainingLines = outputLines[maxMatchLineNo - startLineNo + 1:]
-  return preceedingLines, remainingLines, maxMatchLineNo + 1, varState
-
-def __matchNotLines(checkLines, outputLines, startLineNo, varState):
-  """ Makes sure that the given check lines do not match any of the given output
-      lines. Variable state does not change.
+  Raises MatchFailedException if an assertion matches a line in the scope.
   """
-  for checkLine in checkLines:
-    assert checkLine.variant == TestAssertion.Variant.Not
-    matchLineNo, matchVarState = \
-      __findFirstMatch(checkLine, outputLines, startLineNo, [], varState)
-    if matchVarState is not None:
-      Logger.testFailed("CHECK-NOT line \"" + checkLine.originalText + "\" matches output line " + \
-                        str(matchLineNo), checkLine.fileName, checkLine.lineNo)
+  for i in range(scope.start, scope.end):
+    line = c1Pass.body[i]
+    for assertion in assertions:
+      assert assertion.variant == TestAssertion.Variant.Not
+      if MatchLines(assertion, line, variables) is not None:
+        raise MatchFailedException(assertion, i)
 
-def __matchGroups(checkGroup, outputGroup):
-  """ Matches the check lines in this group against an output group. It is
-      responsible for running the checks in the right order and scope, and
-      for propagating the variable state between the check lines.
+def MatchTestCase(testCase, c1Pass):
+  """ Runs a test case against a C1visualizer graph dump.
+
+  Raises MatchFailedException when an assertion cannot be satisfied.
   """
-  varState = {}
-  checkLines = checkGroup.assertions
-  outputLines = outputGroup.body
-  startLineNo = outputGroup.startLineNo
+  assert testCase.name == c1Pass.name
 
-  while checkLines:
-    # Extract the next sequence of location-independent checks to be matched.
-    notChecks, independentChecks, checkLines = __nextIndependentChecks(checkLines)
+  matchFrom = 0
+  variables = ImmutableDict()
+  c1Length = len(c1Pass.body)
 
-    # Match the independent checks.
-    notOutput, outputLines, newStartLineNo, newVarState = \
-      __matchIndependentChecks(independentChecks, outputLines, startLineNo, varState)
+  # NOT assertions are verified retrospectively, once the scope is known.
+  pendingNotAssertions = None
 
-    # Run the Not checks against the output lines which lie between the last
-    # two independent groups or the bounds of the output.
-    __matchNotLines(notChecks, notOutput, startLineNo, varState)
+  # Prepare assertions by grouping those that are verified in the same scope.
+  # We also add None as an EOF assertion that will set scope for NOTs.
+  assertionGroups = splitIntoGroups(testCase.assertions)
+  assertionGroups.append(None)
 
-    # Update variable state.
-    startLineNo = newStartLineNo
-    varState = newVarState
+  for assertionGroup in assertionGroups:
+    if assertionGroup is None:
+      # EOF marker always matches the last+1 line of c1Pass.
+      match = MatchInfo(MatchScope(c1Length, c1Length), None)
+    elif assertionGroup[0].variant == TestAssertion.Variant.Not:
+      # NOT assertions will be tested together with the next group.
+      assert not pendingNotAssertions
+      pendingNotAssertions = assertionGroup
+      continue
+    elif assertionGroup[0].variant == TestAssertion.Variant.InOrder:
+      # Single in-order assertion. Find the first line that matches.
+      assert len(assertionGroup) == 1
+      scope = MatchScope(matchFrom, c1Length)
+      match = findMatchingLine(assertionGroup[0], c1Pass, scope, variables)
+    elif assertionGroup[0].variant == TestAssertion.Variant.NextLine:
+      # Single next-line assertion. Test if the current line matches.
+      assert len(assertionGroup) == 1
+      scope = MatchScope(matchFrom, matchFrom + 1)
+      match = findMatchingLine(assertionGroup[0], c1Pass, scope, variables)
+    else:
+      # A group of DAG assertions. Match them all starting from the same point.
+      assert assertionGroup[0].variant == TestAssertion.Variant.DAG
+      scope = MatchScope(matchFrom, c1Length)
+      match = matchDagGroup(assertionGroup, c1Pass, scope, variables)
+
+    if pendingNotAssertions:
+      # Previous group were NOT assertions. Make sure they don't match any lines
+      # in the [matchFrom, match.start) scope.
+      scope = MatchScope(matchFrom, match.scope.start)
+      testNotGroup(pendingNotAssertions, c1Pass, scope, variables)
+      pendingNotAssertions = None
+
+    # Update state.
+    assert matchFrom <= match.scope.end
+    matchFrom = match.scope.end + 1
+    variables = match.variables
 
 def MatchFiles(checkerFile, c1File):
   for testCase in checkerFile.testCases:
@@ -140,8 +157,18 @@
     # match a check group against the first output group of the same name.
     c1Pass = c1File.findPass(testCase.name)
     if c1Pass is None:
-      Logger.fail("Test case \"" + testCase.name + "\" not found in the C1visualizer output",
+      Logger.fail("Test case \"{}\" not found in the CFG file".format(testCase.name),
                   testCase.fileName, testCase.startLineNo)
+
     Logger.startTest(testCase.name)
-    __matchGroups(testCase, c1Pass)
-    Logger.testPassed()
+    try:
+      MatchTestCase(testCase, c1Pass)
+      Logger.testPassed()
+    except MatchFailedException as e:
+      lineNo = c1Pass.startLineNo + e.lineNo
+      if e.assertion.variant == TestAssertion.Variant.Not:
+        Logger.testFailed("NOT assertion matched line {}".format(lineNo),
+                          e.assertion.fileName, e.assertion.lineNo)
+      else:
+        Logger.testFailed("Assertion could not be matched starting from line {}".format(lineNo),
+                          e.assertion.fileName, e.assertion.lineNo)
diff --git a/tools/checker/match/line.py b/tools/checker/match/line.py
index f0253c3..711d814 100644
--- a/tools/checker/match/line.py
+++ b/tools/checker/match/line.py
@@ -13,77 +13,84 @@
 # limitations under the License.
 
 from common.logger              import Logger
-from file_format.checker.struct import TestAssertion, RegexExpression
+from file_format.checker.struct import RegexExpression
 
 import re
 
-def __isMatchAtStart(match):
-  """ Tests if the given Match occurred at the beginning of the line. """
-  return (match is not None) and (match.start() == 0)
+def headAndTail(list):
+  return list[0], list[1:]
 
-def __generatePattern(checkLine, linePart, varState):
-  """ Returns the regex pattern to be matched in the output line. Variable
-      references are substituted with their current values provided in the
-      'varState' argument.
+def splitAtSeparators(expressions):
+  """ Splits a list of RegexExpressions at separators. """
+  splitExpressions = []
+  wordStart = 0
+  for index, expression in enumerate(expressions):
+    if expression.variant == RegexExpression.Variant.Separator:
+      splitExpressions.append(expressions[wordStart:index])
+      wordStart = index + 1
+  splitExpressions.append(expressions[wordStart:])
+  return splitExpressions
 
-  An exception is raised if a referenced variable is undefined.
+def matchWords(checkerWord, stringWord, variables, pos):
+  """ Attempts to match a list of RegexExpressions against a string.
+      Returns updated variable dictionary if successful and None otherwise.
   """
-  if linePart.variant == RegexExpression.Variant.VarRef:
-    try:
-      return re.escape(varState[linePart.name])
-    except KeyError:
-      Logger.testFailed("Use of undefined variable \"" + linePart.name + "\"",
-                        checkLine.fileName, checkLine.lineNo)
-  else:
-    return linePart.pattern
-
-def __isSeparated(outputLine, matchStart):
-  return (matchStart == 0) or (outputLine[matchStart - 1:matchStart].isspace())
-
-def MatchLines(checkLine, outputLine, initialVarState):
-  """ Attempts to match the check line against a line from the output file with
-      the given initial variable values. It returns the new variable state if
-      successful and None otherwise.
-  """
-  # Do the full matching on a shadow copy of the variable state. If the
-  # matching fails half-way, we will not need to revert the state.
-  varState = dict(initialVarState)
-
-  matchStart = 0
-  isAfterSeparator = True
-
-  # Now try to parse all of the parts of the check line in the right order.
-  # Variable values are updated on-the-fly, meaning that a variable can
-  # be referenced immediately after its definition.
-  for part in checkLine.expressions:
-    if part.variant == RegexExpression.Variant.Separator:
-      isAfterSeparator = True
-      continue
-
-    # Find the earliest match for this line part.
-    pattern = __generatePattern(checkLine, part, varState)
-    while True:
-      match = re.search(pattern, outputLine[matchStart:])
-      if (match is None) or (not isAfterSeparator and not __isMatchAtStart(match)):
-        return None
-      matchEnd = matchStart + match.end()
-      matchStart += match.start()
-
-      # Check if this is a valid match if we expect a whitespace separator
-      # before the matched text. Otherwise loop and look for another match.
-      if not isAfterSeparator or __isSeparated(outputLine, matchStart):
-        break
+  for expression in checkerWord:
+    # If `expression` is a variable reference, replace it with the value.
+    if expression.variant == RegexExpression.Variant.VarRef:
+      if expression.name in variables:
+        pattern = re.escape(variables[expression.name])
       else:
-        matchStart += 1
+        Logger.testFailed("Multiple definitions of variable \"{}\"".format(expression.name),
+                          pos.fileName, pos.lineNo)
+    else:
+      pattern = expression.pattern
 
-    if part.variant == RegexExpression.Variant.VarDef:
-      if part.name in varState:
-        Logger.testFailed("Multiple definitions of variable \"" + part.name + "\"",
-                          checkLine.fileName, checkLine.lineNo)
-      varState[part.name] = outputLine[matchStart:matchEnd]
+    # Match the expression's regex pattern against the remainder of the word.
+    # Note: re.match will succeed only if matched from the beginning.
+    match = re.match(pattern, stringWord)
+    if not match:
+      return None
 
-    matchStart = matchEnd
-    isAfterSeparator = False
+    # If `expression` was a variable definition, set the variable's value.
+    if expression.variant == RegexExpression.Variant.VarDef:
+      if expression.name not in variables:
+        variables = variables.copyWith(expression.name, stringWord[:match.end()])
+      else:
+        Logger.testFailed("Multiple definitions of variable \"{}\"".format(expression.name),
+                          pos.fileName, pos.lineNo)
 
-  # All parts were successfully matched. Return the new variable state.
-  return varState
+    # Move cursor by deleting the matched characters.
+    stringWord = stringWord[match.end():]
+
+  # Make sure the entire word matched, i.e. `stringWord` is empty.
+  if stringWord:
+    return None
+
+  return variables
+
+def MatchLines(checkerLine, stringLine, variables):
+  """ Attempts to match a CHECK line against a string. Returns variable state
+      after the match if successful and None otherwise.
+  """
+  checkerWords = splitAtSeparators(checkerLine.expressions)
+  stringWords = stringLine.split()
+
+  while checkerWords:
+    # Get the next run of RegexExpressions which must match one string word.
+    checkerWord, checkerWords = headAndTail(checkerWords)
+
+    # Keep reading words until a match is found.
+    wordMatched = False
+    while stringWords:
+      stringWord, stringWords = headAndTail(stringWords)
+      newVariables = matchWords(checkerWord, stringWord, variables, checkerLine)
+      if newVariables is not None:
+        wordMatched = True
+        variables = newVariables
+        break
+    if not wordMatched:
+      return None
+
+  # All RegexExpressions matched. Return new variable state.
+  return variables
diff --git a/tools/checker/match/test.py b/tools/checker/match/test.py
index bb3b1af..348c1d2 100644
--- a/tools/checker/match/test.py
+++ b/tools/checker/match/test.py
@@ -12,12 +12,13 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+from common.immutables               import ImmutableDict
 from common.testing                  import ToUnicode
 from file_format.c1visualizer.parser import ParseC1visualizerStream
 from file_format.c1visualizer.struct import C1visualizerFile, C1visualizerPass
 from file_format.checker.parser      import ParseCheckerStream, ParseCheckerAssertion
 from file_format.checker.struct      import CheckerFile, TestCase, TestAssertion, RegexExpression
-from match.file                      import MatchFiles
+from match.file                      import MatchTestCase, MatchFailedException
 from match.line                      import MatchLines
 
 import io
@@ -33,70 +34,75 @@
     return ParseCheckerAssertion(testCase, checkerString, TestAssertion.Variant.InOrder, 0)
 
   def tryMatch(self, checkerString, c1String, varState={}):
-    return MatchLines(self.createTestAssertion(checkerString), ToUnicode(c1String), varState)
+    return MatchLines(self.createTestAssertion(checkerString),
+                      ToUnicode(c1String),
+                      ImmutableDict(varState))
 
-  def matches(self, checkerString, c1String, varState={}):
-    return self.tryMatch(checkerString, c1String, varState) is not None
+  def assertMatches(self, checkerString, c1String, varState={}):
+    self.assertIsNotNone(self.tryMatch(checkerString, c1String, varState))
+
+  def assertDoesNotMatch(self, checkerString, c1String, varState={}):
+    self.assertIsNone(self.tryMatch(checkerString, c1String, varState))
 
   def test_TextAndWhitespace(self):
-    self.assertTrue(self.matches("foo", "foo"))
-    self.assertTrue(self.matches("foo", "  foo  "))
-    self.assertTrue(self.matches("foo", "foo bar"))
-    self.assertFalse(self.matches("foo", "XfooX"))
-    self.assertFalse(self.matches("foo", "zoo"))
+    self.assertMatches("foo", "foo")
+    self.assertMatches("foo", "  foo  ")
+    self.assertMatches("foo", "foo bar")
+    self.assertDoesNotMatch("foo", "XfooX")
+    self.assertDoesNotMatch("foo", "zoo")
 
-    self.assertTrue(self.matches("foo bar", "foo   bar"))
-    self.assertTrue(self.matches("foo bar", "abc foo bar def"))
-    self.assertTrue(self.matches("foo bar", "foo foo bar bar"))
+    self.assertMatches("foo bar", "foo   bar")
+    self.assertMatches("foo bar", "abc foo bar def")
+    self.assertMatches("foo bar", "foo foo bar bar")
 
-    self.assertTrue(self.matches("foo bar", "foo X bar"))
-    self.assertFalse(self.matches("foo bar", "foo Xbar"))
+    self.assertMatches("foo bar", "foo X bar")
+    self.assertDoesNotMatch("foo bar", "foo Xbar")
 
   def test_Pattern(self):
-    self.assertTrue(self.matches("foo{{A|B}}bar", "fooAbar"))
-    self.assertTrue(self.matches("foo{{A|B}}bar", "fooBbar"))
-    self.assertFalse(self.matches("foo{{A|B}}bar", "fooCbar"))
+    self.assertMatches("foo{{A|B}}bar", "fooAbar")
+    self.assertMatches("foo{{A|B}}bar", "fooBbar")
+    self.assertDoesNotMatch("foo{{A|B}}bar", "fooCbar")
 
   def test_VariableReference(self):
-    self.assertTrue(self.matches("foo<<X>>bar", "foobar", {"X": ""}))
-    self.assertTrue(self.matches("foo<<X>>bar", "fooAbar", {"X": "A"}))
-    self.assertTrue(self.matches("foo<<X>>bar", "fooBbar", {"X": "B"}))
-    self.assertFalse(self.matches("foo<<X>>bar", "foobar", {"X": "A"}))
-    self.assertFalse(self.matches("foo<<X>>bar", "foo bar", {"X": "A"}))
+    self.assertMatches("foo<<X>>bar", "foobar", {"X": ""})
+    self.assertMatches("foo<<X>>bar", "fooAbar", {"X": "A"})
+    self.assertMatches("foo<<X>>bar", "fooBbar", {"X": "B"})
+    self.assertDoesNotMatch("foo<<X>>bar", "foobar", {"X": "A"})
+    self.assertDoesNotMatch("foo<<X>>bar", "foo bar", {"X": "A"})
     with self.assertRaises(CheckerException):
-      self.assertTrue(self.matches("foo<<X>>bar", "foobar", {}))
+      self.tryMatch("foo<<X>>bar", "foobar", {})
 
   def test_VariableDefinition(self):
-    self.assertTrue(self.matches("foo<<X:A|B>>bar", "fooAbar"))
-    self.assertTrue(self.matches("foo<<X:A|B>>bar", "fooBbar"))
-    self.assertFalse(self.matches("foo<<X:A|B>>bar", "fooCbar"))
+    self.assertMatches("foo<<X:A|B>>bar", "fooAbar")
+    self.assertMatches("foo<<X:A|B>>bar", "fooBbar")
+    self.assertDoesNotMatch("foo<<X:A|B>>bar", "fooCbar")
 
     env = self.tryMatch("foo<<X:A.*B>>bar", "fooABbar", {})
     self.assertEqual(env, {"X": "AB"})
     env = self.tryMatch("foo<<X:A.*B>>bar", "fooAxxBbar", {})
     self.assertEqual(env, {"X": "AxxB"})
 
-    self.assertTrue(self.matches("foo<<X:A|B>>bar<<X>>baz", "fooAbarAbaz"))
-    self.assertTrue(self.matches("foo<<X:A|B>>bar<<X>>baz", "fooBbarBbaz"))
-    self.assertFalse(self.matches("foo<<X:A|B>>bar<<X>>baz", "fooAbarBbaz"))
+    self.assertMatches("foo<<X:A|B>>bar<<X>>baz", "fooAbarAbaz")
+    self.assertMatches("foo<<X:A|B>>bar<<X>>baz", "fooBbarBbaz")
+    self.assertDoesNotMatch("foo<<X:A|B>>bar<<X>>baz", "fooAbarBbaz")
 
   def test_NoVariableRedefinition(self):
     with self.assertRaises(CheckerException):
-      self.matches("<<X:...>><<X>><<X:...>><<X>>", "foofoobarbar")
+      self.tryMatch("<<X:...>><<X>><<X:...>><<X>>", "foofoobarbar")
 
   def test_EnvNotChangedOnPartialMatch(self):
     env = {"Y": "foo"}
-    self.assertFalse(self.matches("<<X:A>>bar", "Abaz", env))
+    self.assertDoesNotMatch("<<X:A>>bar", "Abaz", env)
     self.assertFalse("X" in env.keys())
 
   def test_VariableContentEscaped(self):
-    self.assertTrue(self.matches("<<X:..>>foo<<X>>", ".*foo.*"))
-    self.assertFalse(self.matches("<<X:..>>foo<<X>>", ".*fooAAAA"))
+    self.assertMatches("<<X:..>>foo<<X>>", ".*foo.*")
+    self.assertDoesNotMatch("<<X:..>>foo<<X>>", ".*fooAAAA")
 
 
 class MatchFiles_Test(unittest.TestCase):
 
-  def matches(self, checkerString, c1String):
+  def assertMatches(self, checkerString, c1String):
     checkerString = \
       """
         // CHECK-START: MyMethod MyPass
@@ -116,31 +122,33 @@
       """
     checkerFile = ParseCheckerStream("<test-file>", "CHECK", io.StringIO(ToUnicode(checkerString)))
     c1File = ParseC1visualizerStream("<c1-file>", io.StringIO(ToUnicode(c1String)))
-    try:
-      MatchFiles(checkerFile, c1File)
-      return True
-    except CheckerException:
-      return False
+    assert len(checkerFile.testCases) == 1
+    assert len(c1File.passes) == 1
+    MatchTestCase(checkerFile.testCases[0], c1File.passes[0])
+
+  def assertDoesNotMatch(self, checkerString, c1String):
+    with self.assertRaises(MatchFailedException):
+      self.assertMatches(checkerString, c1String)
 
   def test_Text(self):
-    self.assertTrue(self.matches( "// CHECK: foo bar", "foo bar"))
-    self.assertFalse(self.matches("// CHECK: foo bar", "abc def"))
+    self.assertMatches("// CHECK: foo bar", "foo bar")
+    self.assertDoesNotMatch("// CHECK: foo bar", "abc def")
 
   def test_Pattern(self):
-    self.assertTrue(self.matches( "// CHECK: abc {{de.}}", "abc de#"))
-    self.assertFalse(self.matches("// CHECK: abc {{de.}}", "abc d#f"))
+    self.assertMatches("// CHECK: abc {{de.}}", "abc de#")
+    self.assertDoesNotMatch("// CHECK: abc {{de.}}", "abc d#f")
 
   def test_Variables(self):
-    self.assertTrue(self.matches(
+    self.assertMatches(
     """
       // CHECK: foo<<X:.>>bar
       // CHECK: abc<<X>>def
     """,
     """
-      foo bar
-      abc def
-    """))
-    self.assertTrue(self.matches(
+      foo0bar
+      abc0def
+    """)
+    self.assertMatches(
     """
       // CHECK: foo<<X:([0-9]+)>>bar
       // CHECK: abc<<X>>def
@@ -150,8 +158,8 @@
       foo1234bar
       abc1234def
       ### 1234 ###
-    """))
-    self.assertFalse(self.matches(
+    """)
+    self.assertDoesNotMatch(
     """
       // CHECK: foo<<X:([0-9]+)>>bar
       // CHECK: abc<<X>>def
@@ -159,10 +167,16 @@
     """
       foo1234bar
       abc1235def
-    """))
+    """)
+
+  def test_WholeWordMustMatch(self):
+    self.assertMatches("// CHECK: b{{.}}r", "abc bar def")
+    self.assertDoesNotMatch("// CHECK: b{{.}}r", "abc Xbar def")
+    self.assertDoesNotMatch("// CHECK: b{{.}}r", "abc barX def")
+    self.assertDoesNotMatch("// CHECK: b{{.}}r", "abc b r def")
 
   def test_InOrderAssertions(self):
-    self.assertTrue(self.matches(
+    self.assertMatches(
     """
       // CHECK: foo
       // CHECK: bar
@@ -170,8 +184,8 @@
     """
       foo
       bar
-    """))
-    self.assertFalse(self.matches(
+    """)
+    self.assertDoesNotMatch(
     """
       // CHECK: foo
       // CHECK: bar
@@ -179,10 +193,58 @@
     """
       bar
       foo
-    """))
+    """)
+
+  def test_NextLineAssertions(self):
+    self.assertMatches(
+    """
+      // CHECK:      foo
+      // CHECK-NEXT: bar
+      // CHECK-NEXT: abc
+      // CHECK:      def
+    """,
+    """
+      foo
+      bar
+      abc
+      def
+    """)
+    self.assertMatches(
+    """
+      // CHECK:      foo
+      // CHECK-NEXT: bar
+      // CHECK:      def
+    """,
+    """
+      foo
+      bar
+      abc
+      def
+    """)
+    self.assertDoesNotMatch(
+    """
+      // CHECK:      foo
+      // CHECK-NEXT: bar
+    """,
+    """
+      foo
+      abc
+      bar
+    """)
+
+    self.assertDoesNotMatch(
+    """
+      // CHECK:      foo
+      // CHECK-NEXT: bar
+    """,
+    """
+      bar
+      foo
+      abc
+    """)
 
   def test_DagAssertions(self):
-    self.assertTrue(self.matches(
+    self.assertMatches(
     """
       // CHECK-DAG: foo
       // CHECK-DAG: bar
@@ -190,8 +252,8 @@
     """
       foo
       bar
-    """))
-    self.assertTrue(self.matches(
+    """)
+    self.assertMatches(
     """
       // CHECK-DAG: foo
       // CHECK-DAG: bar
@@ -199,10 +261,10 @@
     """
       bar
       foo
-    """))
+    """)
 
   def test_DagAssertionsScope(self):
-    self.assertTrue(self.matches(
+    self.assertMatches(
     """
       // CHECK:     foo
       // CHECK-DAG: abc
@@ -214,8 +276,8 @@
       def
       abc
       bar
-    """))
-    self.assertFalse(self.matches(
+    """)
+    self.assertDoesNotMatch(
     """
       // CHECK:     foo
       // CHECK-DAG: abc
@@ -227,8 +289,8 @@
       abc
       bar
       def
-    """))
-    self.assertFalse(self.matches(
+    """)
+    self.assertDoesNotMatch(
     """
       // CHECK:     foo
       // CHECK-DAG: abc
@@ -240,26 +302,26 @@
       def
       bar
       abc
-    """))
+    """)
 
   def test_NotAssertions(self):
-    self.assertTrue(self.matches(
+    self.assertMatches(
     """
       // CHECK-NOT: foo
     """,
     """
       abc
       def
-    """))
-    self.assertFalse(self.matches(
+    """)
+    self.assertDoesNotMatch(
     """
       // CHECK-NOT: foo
     """,
     """
       abc foo
       def
-    """))
-    self.assertFalse(self.matches(
+    """)
+    self.assertDoesNotMatch(
     """
       // CHECK-NOT: foo
       // CHECK-NOT: bar
@@ -267,10 +329,10 @@
     """
       abc
       def bar
-    """))
+    """)
 
   def test_NotAssertionsScope(self):
-    self.assertTrue(self.matches(
+    self.assertMatches(
     """
       // CHECK:     abc
       // CHECK-NOT: foo
@@ -279,8 +341,8 @@
     """
       abc
       def
-    """))
-    self.assertTrue(self.matches(
+    """)
+    self.assertMatches(
     """
       // CHECK:     abc
       // CHECK-NOT: foo
@@ -290,8 +352,8 @@
       abc
       def
       foo
-    """))
-    self.assertFalse(self.matches(
+    """)
+    self.assertDoesNotMatch(
     """
       // CHECK:     abc
       // CHECK-NOT: foo
@@ -301,10 +363,10 @@
       abc
       foo
       def
-    """))
+    """)
 
   def test_LineOnlyMatchesOnce(self):
-    self.assertTrue(self.matches(
+    self.assertMatches(
     """
       // CHECK-DAG: foo
       // CHECK-DAG: foo
@@ -313,8 +375,8 @@
       foo
       abc
       foo
-    """))
-    self.assertFalse(self.matches(
+    """)
+    self.assertDoesNotMatch(
     """
       // CHECK-DAG: foo
       // CHECK-DAG: foo
@@ -323,4 +385,4 @@
       foo
       abc
       bar
-    """))
+    """)
diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt
index a8bc4e1..064abc1 100644
--- a/tools/libcore_failures.txt
+++ b/tools/libcore_failures.txt
@@ -21,12 +21,19 @@
   name: "libcore.java.lang.SystemTest#testSystemProperties_mutable"
 },
 {
-  description: "Differences between vogar and cts",
+  description: "Differences between vogar and cts. Passes with --mode activity",
   result: EXEC_FAILED,
   modes: [device],
-  names: ["libcore.java.lang.OldSystemTest#test_getProperties",
-          "org.apache.harmony.tests.java.lang.Process2Test#test_getErrorStream",
-          "org.apache.harmony.tests.java.lang.ProcessTest#test_exitValue"]
+  names: ["libcore.java.lang.OldSystemTest#test_getProperties"]
+},
+{
+  description: "Differences between vogar and cts. EACCESS when run with vogar.
+                Passes on host, passes with cts. Passes with vogar with su
+                (--invoke-with \"su root\"). Does not pass after setting chmod
+                777 all directories on path to socket (on device without su).",
+  result: EXEC_FAILED,
+  modes: [device],
+  names: ["libcore.io.OsTest#testUnixDomainSockets_in_file_system"]
 },
 {
   description: "Failures needing investigation",
@@ -34,15 +41,9 @@
   modes: [device],
   names: ["libcore.java.util.TimeZoneTest#testDisplayNames",
           "libcore.java.util.TimeZoneTest#test_useDaylightTime_Taiwan",
-          "libcore.java.util.TimeZoneTest#testAllDisplayNames",
-          "libcore.io.OsTest#testUnixDomainSockets_in_file_system",
-          "org.apache.harmony.luni.tests.java.net.URLConnectionTest#test_setReadTimeoutI",
           "org.apache.harmony.tests.java.util.DateTest#test_Constructor",
           "org.apache.harmony.tests.java.util.ScannerTest#test_Constructor_LReadableByteChannel",
-          "org.apache.harmony.tests.java.util.TimeZoneTest#test_hasSameRules_Ljava_util_TimeZone",
-          "org.apache.harmony.tests.java.text.ChoiceFormatTest#testEscapedPatternWithConsecutiveQuotes",
-          "org.apache.harmony.tests.java.text.ChoiceFormatTest#testToPatternWithInfinities",
-          "org.apache.harmony.tests.java.text.MessageFormatTest#test19011159"]
+          "org.apache.harmony.tests.java.util.TimeZoneTest#test_hasSameRules_Ljava_util_TimeZone"]
 },
 {
   description: "Failing due to a locale problem on hammerhead.",