Merge "Better inline instrumentation callbacks in interpreter." into dalvik-dev
diff --git a/build/Android.common.mk b/build/Android.common.mk
index 43f7ff5..ba5fe9f 100644
--- a/build/Android.common.mk
+++ b/build/Android.common.mk
@@ -172,6 +172,8 @@
 	src/compiled_method.cc \
 	src/compiler/driver/compiler_driver.cc \
 	src/compiler/llvm/runtime_support_llvm.cc \
+        src/compiler/stubs/portable/stubs.cc \
+        src/compiler/stubs/quick/stubs.cc \
 	src/debugger.cc \
 	src/dex_file.cc \
 	src/dex_file_verifier.cc \
diff --git a/src/class_linker.cc b/src/class_linker.cc
index 9faf3de..0c5c9da 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -74,8 +74,9 @@
 
 namespace art {
 
-void artInterpreterToQuickEntry(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item,
-                                ShadowFrame* shadow_frame, JValue* result);
+extern "C" void artInterpreterToQuickEntry(Thread* self, MethodHelper& mh,
+                                           const DexFile::CodeItem* code_item,
+                                           ShadowFrame* shadow_frame, JValue* result);
 
 static void ThrowNoClassDefFoundError(const char* fmt, ...)
     __attribute__((__format__(__printf__, 1, 2)))
@@ -201,7 +202,9 @@
       array_iftable_(NULL),
       init_done_(false),
       is_dirty_(false),
-      intern_table_(intern_table) {
+      intern_table_(intern_table),
+      portable_resolution_trampoline_(NULL),
+      quick_resolution_trampoline_(NULL) {
   CHECK_EQ(arraysize(class_roots_descriptors_), size_t(kClassRootsMax));
 }
 
@@ -952,6 +955,8 @@
   CHECK_EQ(oat_file->GetOatHeader().GetImageFileLocationOatChecksum(), 0U);
   CHECK_EQ(oat_file->GetOatHeader().GetImageFileLocationOatDataBegin(), 0U);
   CHECK(oat_file->GetOatHeader().GetImageFileLocation().empty());
+  portable_resolution_trampoline_ = oat_file->GetOatHeader().GetPortableResolutionTrampoline();
+  quick_resolution_trampoline_ = oat_file->GetOatHeader().GetQuickResolutionTrampoline();
   mirror::Object* dex_caches_object = space->GetImageHeader().GetImageRoot(ImageHeader::kDexCaches);
   mirror::ObjectArray<mirror::DexCache>* dex_caches =
       dex_caches_object->AsObjectArray<mirror::DexCache>();
@@ -1038,19 +1043,14 @@
     return;
   }
 
-  // Check if object is a method without its code set and point it to the resolution trampoline.
+  // Set entry points to interpreter for methods in interpreter only mode.
   if (obj->IsMethod()) {
     mirror::AbstractMethod* method = obj->AsMethod();
-    // Install entry point from interpreter.
-    if (!method->IsNative() && !method->IsProxyMethod() &&
-        (method->GetEntryPointFromCompiledCode() == NULL ||
-         Runtime::Current()->GetInstrumentation()->InterpretOnly())) {
-      method->SetEntryPointFromInterpreter(interpreter::EnterInterpreterFromInterpreter);
-    } else {
-      method->SetEntryPointFromInterpreter(artInterpreterToQuickEntry);
-    }
-    if (method->GetEntryPointFromCompiledCode() == NULL) {
-      method->SetEntryPointFromCompiledCode(GetResolutionTrampoline());
+    if (Runtime::Current()->GetInstrumentation()->InterpretOnly() && !method->IsNative()) {
+      method->SetEntryPointFromInterpreter(interpreter::artInterpreterToInterpreterEntry);
+      if (method != Runtime::Current()->GetResolutionMethod()) {
+        method->SetEntryPointFromCompiledCode(GetInterpreterEntryPoint());
+      }
     }
   }
 }
@@ -1612,10 +1612,11 @@
 
   // Install entry point from interpreter.
   Runtime* runtime = Runtime::Current();
-  if (!method->IsNative() && !method->IsProxyMethod() &&
-      (method->GetEntryPointFromCompiledCode() == NULL ||
-       runtime->GetInstrumentation()->InterpretOnly())) {
-    method->SetEntryPointFromInterpreter(interpreter::EnterInterpreterFromInterpreter);
+  bool enter_interpreter = method->GetEntryPointFromCompiledCode() == NULL ||
+                           (runtime->GetInstrumentation()->InterpretOnly() &&
+                           !method->IsNative() && !method->IsProxyMethod());
+  if (enter_interpreter) {
+    method->SetEntryPointFromInterpreter(interpreter::artInterpreterToInterpreterEntry);
   } else {
     method->SetEntryPointFromInterpreter(artInterpreterToQuickEntry);
   }
@@ -1627,7 +1628,7 @@
 
   if (method->IsStatic() && !method->IsConstructor()) {
     // For static methods excluding the class initializer, install the trampoline.
-    method->SetEntryPointFromCompiledCode(GetResolutionTrampoline());
+    method->SetEntryPointFromCompiledCode(GetResolutionTrampoline(runtime->GetClassLinker()));
   }
 
   if (method->IsNative()) {
@@ -1635,10 +1636,8 @@
     method->UnregisterNative(Thread::Current());
   }
 
-  if (method->GetEntryPointFromCompiledCode() == NULL ||
-      (runtime->GetInstrumentation()->InterpretOnly() && !method->IsNative() &&
-       !method->IsProxyMethod())) {
-    // No code? You must mean to go into the interpreter.
+  if (enter_interpreter) {
+    // Set entry point from compiled code if there's no code or in interpreter only mode.
     method->SetEntryPointFromCompiledCode(GetInterpreterEntryPoint());
   }
 
diff --git a/src/class_linker.h b/src/class_linker.h
index 79fa8ba..eab1fcc 100644
--- a/src/class_linker.h
+++ b/src/class_linker.h
@@ -334,6 +334,14 @@
     is_dirty_ = true;
   }
 
+  const void* GetPortableResolutionTrampoline() const {
+    return portable_resolution_trampoline_;
+  }
+
+  const void* GetQuickResolutionTrampoline() const {
+    return quick_resolution_trampoline_;
+  }
+
  private:
   explicit ClassLinker(InternTable*);
 
@@ -593,6 +601,9 @@
 
   InternTable* intern_table_;
 
+  const void* portable_resolution_trampoline_;
+  const void* quick_resolution_trampoline_;
+
   friend class CommonTest;
   friend class ImageWriter;  // for GetClassRoots
   friend class ObjectTest;
diff --git a/src/compiler/dex/quick/gen_common.cc b/src/compiler/dex/quick/gen_common.cc
index 055b67c..7aa71cf 100644
--- a/src/compiler/dex/quick/gen_common.cc
+++ b/src/compiler/dex/quick/gen_common.cc
@@ -883,23 +883,81 @@
   CallRuntimeHelperRegLocation(ENTRYPOINT_OFFSET(pDeliverException), rl_src, true);
 }
 
-void Mir2Lir::GenInstanceof(uint32_t type_idx, RegLocation rl_dest,
-                            RegLocation rl_src)
-{
+// For final classes there are no sub-classes to check and so we can answer the instance-of
+// question with simple comparisons.
+void Mir2Lir::GenInstanceofFinal(bool use_declaring_class, uint32_t type_idx, RegLocation rl_dest,
+                                 RegLocation rl_src) {
+  RegLocation object = LoadValue(rl_src, kCoreReg);
+  RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+  int result_reg = rl_result.low_reg;
+  if (result_reg == object.low_reg) {
+    result_reg = AllocTypedTemp(false, kCoreReg);
+  }
+  LoadConstant(result_reg, 0);     // assume false
+  LIR* null_branchover = OpCmpImmBranch(kCondEq, object.low_reg, 0, NULL);
+
+  int check_class = AllocTypedTemp(false, kCoreReg);
+  int object_class = AllocTypedTemp(false, kCoreReg);
+
+  LoadCurrMethodDirect(check_class);
+  if (use_declaring_class) {
+    LoadWordDisp(check_class, mirror::AbstractMethod::DeclaringClassOffset().Int32Value(),
+                 check_class);
+    LoadWordDisp(object.low_reg,  mirror::Object::ClassOffset().Int32Value(), object_class);
+  } else {
+    LoadWordDisp(check_class, mirror::AbstractMethod::DexCacheResolvedTypesOffset().Int32Value(),
+                 check_class);
+    LoadWordDisp(object.low_reg,  mirror::Object::ClassOffset().Int32Value(), object_class);
+    int32_t offset_of_type =
+      mirror::Array::DataOffset(sizeof(mirror::Class*)).Int32Value() +
+      (sizeof(mirror::Class*) * type_idx);
+    LoadWordDisp(check_class, offset_of_type, check_class);
+  }
+
+  LIR* ne_branchover = NULL;
+  if (cu_->instruction_set == kThumb2) {
+    OpRegReg(kOpCmp, check_class, object_class);  // Same?
+    OpIT(kCondEq, "");   // if-convert the test
+    LoadConstant(result_reg, 1);     // .eq case - load true
+  } else {
+    ne_branchover = OpCmpBranch(kCondNe, check_class, object_class, NULL);
+    LoadConstant(result_reg, 1);     // eq case - load true
+  }
+  LIR* target = NewLIR0(kPseudoTargetLabel);
+  null_branchover->target = target;
+  if (ne_branchover != NULL) {
+    ne_branchover->target = target;
+  }
+  FreeTemp(object_class);
+  FreeTemp(check_class);
+  if (IsTemp(result_reg)) {
+    OpRegCopy(rl_result.low_reg, result_reg);
+    FreeTemp(result_reg);
+  }
+  StoreValue(rl_dest, rl_result);
+}
+
+void Mir2Lir::GenInstanceofCallingHelper(bool needs_access_check, bool type_known_final,
+                                         bool type_known_abstract, bool use_declaring_class,
+                                         bool can_assume_type_is_in_dex_cache,
+                                         uint32_t type_idx, RegLocation rl_dest,
+                                         RegLocation rl_src) {
   FlushAllRegs();
   // May generate a call - use explicit registers
   LockCallTemps();
   LoadCurrMethodDirect(TargetReg(kArg1));  // kArg1 <= current Method*
   int class_reg = TargetReg(kArg2);  // kArg2 will hold the Class*
-  if (!cu_->compiler_driver->CanAccessTypeWithoutChecks(cu_->method_idx,
-                                                   *cu_->dex_file,
-                                                   type_idx)) {
+  if (needs_access_check) {
     // Check we have access to type_idx and if not throw IllegalAccessError,
     // returns Class* in kArg0
     CallRuntimeHelperImm(ENTRYPOINT_OFFSET(pInitializeTypeAndVerifyAccessFromCode),
                          type_idx, true);
     OpRegCopy(class_reg, TargetReg(kRet0));  // Align usage with fast path
     LoadValueDirectFixed(rl_src, TargetReg(kArg0));  // kArg0 <= ref
+  } else if (use_declaring_class) {
+    LoadValueDirectFixed(rl_src, TargetReg(kArg0));  // kArg0 <= ref
+    LoadWordDisp(TargetReg(kArg1),
+                 mirror::AbstractMethod::DeclaringClassOffset().Int32Value(), class_reg);
   } else {
     // Load dex cache entry into class_reg (kArg2)
     LoadValueDirectFixed(rl_src, TargetReg(kArg0));  // kArg0 <= ref
@@ -909,8 +967,7 @@
         mirror::Array::DataOffset(sizeof(mirror::Class*)).Int32Value() + (sizeof(mirror::Class*)
         * type_idx);
     LoadWordDisp(class_reg, offset_of_type, class_reg);
-    if (!cu_->compiler_driver->CanAssumeTypeIsPresentInDexCache(
-        *cu_->dex_file, type_idx)) {
+    if (!can_assume_type_is_in_dex_cache) {
       // Need to test presence of type in dex cache at runtime
       LIR* hop_branch = OpCmpImmBranch(kCondNe, class_reg, 0, NULL);
       // Not resolved
@@ -926,56 +983,101 @@
   /* kArg0 is ref, kArg2 is class. If ref==null, use directly as bool result */
   RegLocation rl_result = GetReturn(false);
   if (cu_->instruction_set == kMips) {
-    LoadConstant(rl_result.low_reg, 0);  // store false result for if branch is taken
+    // On MIPS rArg0 != rl_result, place false in result if branch is taken.
+    LoadConstant(rl_result.low_reg, 0);
   }
   LIR* branch1 = OpCmpImmBranch(kCondEq, TargetReg(kArg0), 0, NULL);
+
   /* load object->klass_ */
   DCHECK_EQ(mirror::Object::ClassOffset().Int32Value(), 0);
   LoadWordDisp(TargetReg(kArg0),  mirror::Object::ClassOffset().Int32Value(), TargetReg(kArg1));
   /* kArg0 is ref, kArg1 is ref->klass_, kArg2 is class */
   LIR* branchover = NULL;
-  if (cu_->instruction_set == kThumb2) {
-    /* Uses conditional nullification */
-    int r_tgt = LoadHelper(ENTRYPOINT_OFFSET(pInstanceofNonTrivialFromCode));
-    OpRegReg(kOpCmp, TargetReg(kArg1), TargetReg(kArg2));  // Same?
-    OpIT(kCondEq, "EE");   // if-convert the test
-    LoadConstant(TargetReg(kArg0), 1);     // .eq case - load true
-    OpRegCopy(TargetReg(kArg0), TargetReg(kArg2));    // .ne case - arg0 <= class
-    OpReg(kOpBlx, r_tgt);    // .ne case: helper(class, ref->class)
-    FreeTemp(r_tgt);
+  if (type_known_final) {
+    // rl_result == ref == null == 0.
+    if (cu_->instruction_set == kThumb2) {
+      OpRegReg(kOpCmp, TargetReg(kArg1), TargetReg(kArg2));  // Same?
+      OpIT(kCondEq, "E");   // if-convert the test
+      LoadConstant(rl_result.low_reg, 1);     // .eq case - load true
+      LoadConstant(rl_result.low_reg, 0);     // .ne case - load false
+    } else {
+      LoadConstant(rl_result.low_reg, 0);     // ne case - load false
+      branchover = OpCmpBranch(kCondNe, TargetReg(kArg1), TargetReg(kArg2), NULL);
+      LoadConstant(rl_result.low_reg, 1);     // eq case - load true
+    }
   } else {
-    /* Uses branchovers */
-    LoadConstant(rl_result.low_reg, 1);     // assume true
-    branchover = OpCmpBranch(kCondEq, TargetReg(kArg1), TargetReg(kArg2), NULL);
-    if (cu_->instruction_set != kX86) {
+    if (cu_->instruction_set == kThumb2) {
       int r_tgt = LoadHelper(ENTRYPOINT_OFFSET(pInstanceofNonTrivialFromCode));
+      if (!type_known_abstract) {
+      /* Uses conditional nullification */
+        OpRegReg(kOpCmp, TargetReg(kArg1), TargetReg(kArg2));  // Same?
+        OpIT(kCondEq, "EE");   // if-convert the test
+        LoadConstant(TargetReg(kArg0), 1);     // .eq case - load true
+      }
       OpRegCopy(TargetReg(kArg0), TargetReg(kArg2));    // .ne case - arg0 <= class
       OpReg(kOpBlx, r_tgt);    // .ne case: helper(class, ref->class)
       FreeTemp(r_tgt);
     } else {
-      OpRegCopy(TargetReg(kArg0), TargetReg(kArg2));
-      OpThreadMem(kOpBlx, ENTRYPOINT_OFFSET(pInstanceofNonTrivialFromCode));
+      if (!type_known_abstract) {
+        /* Uses branchovers */
+        LoadConstant(rl_result.low_reg, 1);     // assume true
+        branchover = OpCmpBranch(kCondEq, TargetReg(kArg1), TargetReg(kArg2), NULL);
+      }
+      if (cu_->instruction_set != kX86) {
+        int r_tgt = LoadHelper(ENTRYPOINT_OFFSET(pInstanceofNonTrivialFromCode));
+        OpRegCopy(TargetReg(kArg0), TargetReg(kArg2));    // .ne case - arg0 <= class
+        OpReg(kOpBlx, r_tgt);    // .ne case: helper(class, ref->class)
+        FreeTemp(r_tgt);
+      } else {
+        OpRegCopy(TargetReg(kArg0), TargetReg(kArg2));
+        OpThreadMem(kOpBlx, ENTRYPOINT_OFFSET(pInstanceofNonTrivialFromCode));
+      }
     }
   }
+  // TODO: only clobber when type isn't final?
   ClobberCalleeSave();
   /* branch targets here */
   LIR* target = NewLIR0(kPseudoTargetLabel);
   StoreValue(rl_dest, rl_result);
   branch1->target = target;
-  if (cu_->instruction_set != kThumb2) {
+  if (branchover != NULL) {
     branchover->target = target;
   }
 }
 
+void Mir2Lir::GenInstanceof(uint32_t type_idx, RegLocation rl_dest, RegLocation rl_src) {
+  bool type_known_final, type_known_abstract, use_declaring_class;
+  bool needs_access_check = !cu_->compiler_driver->CanAccessTypeWithoutChecks(cu_->method_idx,
+                                                                              *cu_->dex_file,
+                                                                              type_idx,
+                                                                              &type_known_final,
+                                                                              &type_known_abstract,
+                                                                              &use_declaring_class);
+  bool can_assume_type_is_in_dex_cache = !needs_access_check &&
+      cu_->compiler_driver->CanAssumeTypeIsPresentInDexCache(*cu_->dex_file, type_idx);
+
+  if ((use_declaring_class || can_assume_type_is_in_dex_cache) && type_known_final) {
+    GenInstanceofFinal(use_declaring_class, type_idx, rl_dest, rl_src);
+  } else {
+    GenInstanceofCallingHelper(needs_access_check, type_known_final, type_known_abstract,
+                               use_declaring_class, can_assume_type_is_in_dex_cache,
+                               type_idx, rl_dest, rl_src);
+  }
+}
+
 void Mir2Lir::GenCheckCast(uint32_t insn_idx, uint32_t type_idx, RegLocation rl_src)
 {
-  DexCompilationUnit* cu = mir_graph_->GetCurrentDexCompilationUnit();
-  const CompilerDriver::MethodReference mr(
-        cu->GetDexFile(),
-        cu->GetDexMethodIndex());
+  bool type_known_final, type_known_abstract, use_declaring_class;
   bool needs_access_check = !cu_->compiler_driver->CanAccessTypeWithoutChecks(cu_->method_idx,
                                                                               *cu_->dex_file,
-                                                                              type_idx);
+                                                                              type_idx,
+                                                                              &type_known_final,
+                                                                              &type_known_abstract,
+                                                                              &use_declaring_class);
+  // Note: currently type_known_final is unused, as optimizing will only improve the performance
+  // of the exception throw path.
+  DexCompilationUnit* cu = mir_graph_->GetCurrentDexCompilationUnit();
+  const CompilerDriver::MethodReference mr(cu->GetDexFile(), cu->GetDexMethodIndex());
   if (!needs_access_check && cu_->compiler_driver->IsSafeCast(mr, insn_idx)) {
     // Verifier type analysis proved this check cast would never cause an exception.
     return;
@@ -992,6 +1094,9 @@
     CallRuntimeHelperImmReg(ENTRYPOINT_OFFSET(pInitializeTypeAndVerifyAccessFromCode),
                             type_idx, TargetReg(kArg1), true);
     OpRegCopy(class_reg, TargetReg(kRet0));  // Align usage with fast path
+  } else if (use_declaring_class) {
+    LoadWordDisp(TargetReg(kArg1),
+                 mirror::AbstractMethod::DeclaringClassOffset().Int32Value(), class_reg);
   } else {
     // Load dex cache entry into class_reg (kArg2)
     LoadWordDisp(TargetReg(kArg1),
@@ -1000,8 +1105,7 @@
         mirror::Array::DataOffset(sizeof(mirror::Class*)).Int32Value() +
         (sizeof(mirror::Class*) * type_idx);
     LoadWordDisp(class_reg, offset_of_type, class_reg);
-    if (!cu_->compiler_driver->CanAssumeTypeIsPresentInDexCache(
-        *cu_->dex_file, type_idx)) {
+    if (!cu_->compiler_driver->CanAssumeTypeIsPresentInDexCache(*cu_->dex_file, type_idx)) {
       // Need to test presence of type in dex cache at runtime
       LIR* hop_branch = OpCmpImmBranch(kCondNe, class_reg, 0, NULL);
       // Not resolved
@@ -1023,25 +1127,18 @@
   DCHECK_EQ(mirror::Object::ClassOffset().Int32Value(), 0);
   LoadWordDisp(TargetReg(kArg0), mirror::Object::ClassOffset().Int32Value(), TargetReg(kArg1));
   /* kArg1 now contains object->klass_ */
-  LIR* branch2;
-  if (cu_->instruction_set == kThumb2) {
-    int r_tgt = LoadHelper(ENTRYPOINT_OFFSET(pCheckCastFromCode));
-    OpRegReg(kOpCmp, TargetReg(kArg1), class_reg);
-    branch2 = OpCondBranch(kCondEq, NULL); /* If eq, trivial yes */
-    OpRegCopy(TargetReg(kArg0), TargetReg(kArg1));
-    OpRegCopy(TargetReg(kArg1), TargetReg(kArg2));
-    ClobberCalleeSave();
-    LIR* call_inst = OpReg(kOpBlx, r_tgt);
-    MarkSafepointPC(call_inst);
-    FreeTemp(r_tgt);
-  } else {
+  LIR* branch2 = NULL;
+  if (!type_known_abstract) {
     branch2 = OpCmpBranch(kCondEq, TargetReg(kArg1), class_reg, NULL);
-    CallRuntimeHelperRegReg(ENTRYPOINT_OFFSET(pCheckCastFromCode), TargetReg(kArg1), TargetReg(kArg2), true);
   }
+  CallRuntimeHelperRegReg(ENTRYPOINT_OFFSET(pCheckCastFromCode), TargetReg(kArg1), TargetReg(kArg2),
+                          true);
   /* branch target here */
   LIR* target = NewLIR0(kPseudoTargetLabel);
   branch1->target = target;
-  branch2->target = target;
+  if (branch2 != NULL) {
+    branch2->target = target;
+  }
 }
 
 void Mir2Lir::GenLong3Addr(OpKind first_op, OpKind second_op, RegLocation rl_dest,
diff --git a/src/compiler/dex/quick/mir_to_lir.h b/src/compiler/dex/quick/mir_to_lir.h
index 1df78cf..9eb4524 100644
--- a/src/compiler/dex/quick/mir_to_lir.h
+++ b/src/compiler/dex/quick/mir_to_lir.h
@@ -699,6 +699,14 @@
     }
 
   private:
+    void GenInstanceofFinal(bool use_declaring_class, uint32_t type_idx, RegLocation rl_dest,
+                            RegLocation rl_src);
+    void GenInstanceofCallingHelper(bool needs_access_check, bool type_known_final,
+                                    bool type_known_abstract, bool use_declaring_class,
+                                    bool can_assume_type_is_in_dex_cache,
+                                    uint32_t type_idx, RegLocation rl_dest,
+                                    RegLocation rl_src);
+
     void ClobberBody(RegisterInfo* p);
     void ResetDefBody(RegisterInfo* p) {
       p->def_start = NULL;
diff --git a/src/compiler/driver/compiler_driver.cc b/src/compiler/driver/compiler_driver.cc
index 3dddc75..40cc483 100644
--- a/src/compiler/driver/compiler_driver.cc
+++ b/src/compiler/driver/compiler_driver.cc
@@ -24,6 +24,7 @@
 #include "base/stl_util.h"
 #include "base/timing_logger.h"
 #include "class_linker.h"
+#include "compiler/stubs/stubs.h"
 #include "dex_compilation_unit.h"
 #include "dex_file-inl.h"
 #include "jni_internal.h"
@@ -448,6 +449,66 @@
   return res;
 }
 
+const std::vector<uint8_t>* CompilerDriver::CreatePortableResolutionTrampoline() const {
+  switch (instruction_set_) {
+    case kArm:
+    case kThumb2:
+      return arm::CreatePortableResolutionTrampoline();
+    case kMips:
+      return mips::CreatePortableResolutionTrampoline();
+    case kX86:
+      return x86::CreatePortableResolutionTrampoline();
+    default:
+      LOG(FATAL) << "Unknown InstructionSet: " << instruction_set_;
+      return NULL;
+  }
+}
+
+const std::vector<uint8_t>* CompilerDriver::CreateQuickResolutionTrampoline() const {
+  switch (instruction_set_) {
+    case kArm:
+    case kThumb2:
+      return arm::CreateQuickResolutionTrampoline();
+    case kMips:
+      return mips::CreateQuickResolutionTrampoline();
+    case kX86:
+      return x86::CreateQuickResolutionTrampoline();
+    default:
+      LOG(FATAL) << "Unknown InstructionSet: " << instruction_set_;
+      return NULL;
+  }
+}
+
+const std::vector<uint8_t>* CompilerDriver::CreateInterpreterToInterpreterEntry() const {
+  switch (instruction_set_) {
+    case kArm:
+    case kThumb2:
+      return arm::CreateInterpreterToInterpreterEntry();
+    case kMips:
+      return mips::CreateInterpreterToInterpreterEntry();
+    case kX86:
+      return x86::CreateInterpreterToInterpreterEntry();
+    default:
+      LOG(FATAL) << "Unknown InstructionSet: " << instruction_set_;
+      return NULL;
+  }
+}
+
+const std::vector<uint8_t>* CompilerDriver::CreateInterpreterToQuickEntry() const {
+  switch (instruction_set_) {
+    case kArm:
+    case kThumb2:
+      return arm::CreateInterpreterToQuickEntry();
+    case kMips:
+      return mips::CreateInterpreterToQuickEntry();
+    case kX86:
+      return x86::CreateInterpreterToQuickEntry();
+    default:
+      LOG(FATAL) << "Unknown InstructionSet: " << instruction_set_;
+      return NULL;
+  }
+}
+
 void CompilerDriver::CompileAll(jobject class_loader,
                                 const std::vector<const DexFile*>& dex_files) {
   DCHECK(!Runtime::Current()->IsStarted());
@@ -536,24 +597,19 @@
 
 bool CompilerDriver::CanAssumeTypeIsPresentInDexCache(const DexFile& dex_file,
                                                       uint32_t type_idx) {
-  ScopedObjectAccess soa(Thread::Current());
-  mirror::DexCache* dex_cache = Runtime::Current()->GetClassLinker()->FindDexCache(dex_file);
-  if (!IsImage()) {
-    stats_->TypeNotInDexCache();
-    return false;
-  }
-  mirror::Class* resolved_class = dex_cache->GetResolvedType(type_idx);
-  if (resolved_class == NULL) {
-    stats_->TypeNotInDexCache();
-    return false;
-  }
-  bool result = IsImageClass(ClassHelper(resolved_class).GetDescriptor());
-  if (result) {
+  if (IsImage() && IsImageClass(dex_file.GetTypeDescriptor(dex_file.GetTypeId(type_idx)))) {
+    if (kIsDebugBuild) {
+      ScopedObjectAccess soa(Thread::Current());
+      mirror::DexCache* dex_cache = Runtime::Current()->GetClassLinker()->FindDexCache(dex_file);
+      mirror::Class* resolved_class = dex_cache->GetResolvedType(type_idx);
+      CHECK(resolved_class != NULL);
+    }
     stats_->TypeInDexCache();
+    return true;
   } else {
     stats_->TypeNotInDexCache();
+    return false;
   }
-  return result;
 }
 
 bool CompilerDriver::CanAssumeStringIsPresentInDexCache(const DexFile& dex_file,
@@ -577,7 +633,18 @@
 }
 
 bool CompilerDriver::CanAccessTypeWithoutChecks(uint32_t referrer_idx, const DexFile& dex_file,
-                                                uint32_t type_idx) {
+                                                uint32_t type_idx,
+                                                bool* type_known_final, bool* type_known_abstract,
+                                                bool* equals_referrers_class) {
+  if (type_known_final != NULL) {
+    *type_known_final = false;
+  }
+  if (type_known_abstract != NULL) {
+    *type_known_abstract = false;
+  }
+  if (equals_referrers_class != NULL) {
+    *equals_referrers_class = false;
+  }
   ScopedObjectAccess soa(Thread::Current());
   mirror::DexCache* dex_cache = Runtime::Current()->GetClassLinker()->FindDexCache(dex_file);
   // Get type from dex cache assuming it was populated by the verifier
@@ -587,6 +654,9 @@
     return false;  // Unknown class needs access checks.
   }
   const DexFile::MethodId& method_id = dex_file.GetMethodId(referrer_idx);
+  if (equals_referrers_class != NULL) {
+    *equals_referrers_class = (method_id.class_idx_ == type_idx);
+  }
   mirror::Class* referrer_class = dex_cache->GetResolvedType(method_id.class_idx_);
   if (referrer_class == NULL) {
     stats_->TypeNeedsAccessCheck();
@@ -597,6 +667,12 @@
   bool result = referrer_class->CanAccess(resolved_class);
   if (result) {
     stats_->TypeDoesntNeedAccessCheck();
+    if (type_known_final != NULL) {
+      *type_known_final = resolved_class->IsFinal() && !resolved_class->IsArrayClass();
+    }
+    if (type_known_abstract != NULL) {
+      *type_known_abstract = resolved_class->IsAbstract();
+    }
   } else {
     stats_->TypeNeedsAccessCheck();
   }
diff --git a/src/compiler/driver/compiler_driver.h b/src/compiler/driver/compiler_driver.h
index 1b5bd0d..4f77bdb 100644
--- a/src/compiler/driver/compiler_driver.h
+++ b/src/compiler/driver/compiler_driver.h
@@ -98,6 +98,16 @@
 
   CompilerTls* GetTls();
 
+  // Generate the trampolines that are invoked by unresolved direct methods.
+  const std::vector<uint8_t>* CreatePortableResolutionTrampoline() const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  const std::vector<uint8_t>* CreateQuickResolutionTrampoline() const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  const std::vector<uint8_t>* CreateInterpreterToInterpreterEntry() const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  const std::vector<uint8_t>* CreateInterpreterToQuickEntry() const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   // A class is uniquely located by its DexFile and the class_defs_ table index into that DexFile
   typedef std::pair<const DexFile*, uint32_t> ClassReference;
 
@@ -138,7 +148,9 @@
 
   // Are runtime access checks necessary in the compiled code?
   bool CanAccessTypeWithoutChecks(uint32_t referrer_idx, const DexFile& dex_file,
-                                  uint32_t type_idx)
+                                  uint32_t type_idx, bool* type_known_final = NULL,
+                                  bool* type_known_abstract = NULL,
+                                  bool* equals_referrers_class = NULL)
       LOCKS_EXCLUDED(Locks::mutator_lock_);
 
   // Are runtime access and instantiable checks necessary in the code?
diff --git a/src/compiler/stubs/portable/stubs.cc b/src/compiler/stubs/portable/stubs.cc
new file mode 100644
index 0000000..db551bf
--- /dev/null
+++ b/src/compiler/stubs/portable/stubs.cc
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "compiler/stubs/stubs.h"
+#include "jni_internal.h"
+#include "oat/utils/arm/assembler_arm.h"
+#include "oat/utils/mips/assembler_mips.h"
+#include "oat/utils/x86/assembler_x86.h"
+#include "oat/runtime/oat_support_entrypoints.h"
+#include "stack_indirect_reference_table.h"
+#include "sirt_ref.h"
+
+#define __ assembler->
+
+namespace art {
+
+namespace arm {
+const std::vector<uint8_t>* CreatePortableResolutionTrampoline() {
+  UniquePtr<ArmAssembler> assembler(static_cast<ArmAssembler*>(Assembler::Create(kArm)));
+  RegList save = (1 << R0) | (1 << R1) | (1 << R2) | (1 << R3) | (1 << LR);
+
+  __ PushList(save);
+  __ LoadFromOffset(kLoadWord, R12, TR, ENTRYPOINT_OFFSET(pPortableResolutionTrampolineFromCode));
+  __ mov(R3, ShifterOperand(TR));  // Pass Thread::Current() in R3
+  __ mov(R2, ShifterOperand(SP));  // Pass sp for Method** callee_addr
+  __ IncreaseFrameSize(12);         // 3 words of space for alignment
+  // Call to resolution trampoline (callee, receiver, callee_addr, Thread*)
+  __ blx(R12);
+  __ mov(R12, ShifterOperand(R0));  // Save code address returned into R12
+  __ DecreaseFrameSize(12);
+  __ PopList(save);
+  __ cmp(R12, ShifterOperand(0));
+  __ bx(R12, NE);                   // If R12 != 0 tail call method's code
+  __ bx(LR);                        // Return to caller to handle exception
+
+  assembler->EmitSlowPaths();
+  size_t cs = assembler->CodeSize();
+  UniquePtr<std::vector<uint8_t> > resolution_trampoline(new std::vector<uint8_t>(cs));
+  MemoryRegion code(&(*resolution_trampoline)[0], resolution_trampoline->size());
+  assembler->FinalizeInstructions(code);
+
+  return resolution_trampoline.release();
+}
+} // namespace arm
+
+namespace mips {
+const std::vector<uint8_t>* CreatePortableResolutionTrampoline() {
+  UniquePtr<MipsAssembler> assembler(static_cast<MipsAssembler*>(Assembler::Create(kMips)));
+  // Build frame and save argument registers and RA.
+  __ AddConstant(SP, SP, -32);
+  __ StoreToOffset(kStoreWord, RA, SP, 28);
+  __ StoreToOffset(kStoreWord, A3, SP, 12);
+  __ StoreToOffset(kStoreWord, A2, SP, 8);
+  __ StoreToOffset(kStoreWord, A1, SP, 4);
+  __ StoreToOffset(kStoreWord, A0, SP, 0);
+
+  __ LoadFromOffset(kLoadWord, T9, S1,
+                    ENTRYPOINT_OFFSET(pPortableResolutionTrampolineFromCode));
+  __ Move(A3, S1);  // Pass Thread::Current() in A3
+  __ Move(A2, SP);  // Pass SP for Method** callee_addr
+  __ Jalr(T9); // Call to resolution trampoline (callee, receiver, callee_addr, Thread*)
+
+  // Restore frame, argument registers, and RA.
+  __ LoadFromOffset(kLoadWord, A0, SP, 0);
+  __ LoadFromOffset(kLoadWord, A1, SP, 4);
+  __ LoadFromOffset(kLoadWord, A2, SP, 8);
+  __ LoadFromOffset(kLoadWord, A3, SP, 12);
+  __ LoadFromOffset(kLoadWord, RA, SP, 28);
+  __ AddConstant(SP, SP, 32);
+
+  Label resolve_fail;
+  __ EmitBranch(V0, ZERO, &resolve_fail, true);
+  __ Jr(V0); // If V0 != 0 tail call method's code
+  __ Bind(&resolve_fail, false);
+  __ Jr(RA); // Return to caller to handle exception
+
+  assembler->EmitSlowPaths();
+  size_t cs = assembler->CodeSize();
+  UniquePtr<std::vector<uint8_t> > resolution_trampoline(new std::vector<uint8_t>(cs));
+  MemoryRegion code(&(*resolution_trampoline)[0], resolution_trampoline->size());
+  assembler->FinalizeInstructions(code);
+
+  return resolution_trampoline.release();
+}
+} // namespace mips
+
+namespace x86 {
+const std::vector<uint8_t>* CreatePortableResolutionTrampoline() {
+  UniquePtr<X86Assembler> assembler(static_cast<X86Assembler*>(Assembler::Create(kX86)));
+
+  __ pushl(EBP);
+  __ movl(EBP, ESP);          // save ESP
+  __ subl(ESP, Immediate(8));  // Align stack
+  __ movl(EAX, Address(EBP, 8));  // Method* called
+  __ leal(EDX, Address(EBP, 8));  // Method** called_addr
+  __ fs()->pushl(Address::Absolute(Thread::SelfOffset()));  // pass thread
+  __ pushl(EDX);  // pass called_addr
+  __ pushl(ECX);  // pass receiver
+  __ pushl(EAX);  // pass called
+  // Call to resolve method.
+  __ Call(ThreadOffset(ENTRYPOINT_OFFSET(pPortableResolutionTrampolineFromCode)),
+          X86ManagedRegister::FromCpuRegister(ECX));
+  __ leave();
+
+  Label resolve_fail;  // forward declaration
+  __ cmpl(EAX, Immediate(0));
+  __ j(kEqual, &resolve_fail);
+  __ jmp(EAX);
+  // Tail call to intended method.
+  __ Bind(&resolve_fail);
+  __ ret();
+
+  assembler->EmitSlowPaths();
+  size_t cs = assembler->CodeSize();
+  UniquePtr<std::vector<uint8_t> > resolution_trampoline(new std::vector<uint8_t>(cs));
+  MemoryRegion code(&(*resolution_trampoline)[0], resolution_trampoline->size());
+  assembler->FinalizeInstructions(code);
+
+  return resolution_trampoline.release();
+}
+} // namespace x86
+
+} // namespace art
diff --git a/src/compiler/stubs/quick/stubs.cc b/src/compiler/stubs/quick/stubs.cc
new file mode 100644
index 0000000..a8e691f
--- /dev/null
+++ b/src/compiler/stubs/quick/stubs.cc
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "compiler/stubs/stubs.h"
+#include "jni_internal.h"
+#include "oat/utils/arm/assembler_arm.h"
+#include "oat/utils/mips/assembler_mips.h"
+#include "oat/utils/x86/assembler_x86.h"
+#include "oat/runtime/oat_support_entrypoints.h"
+#include "stack_indirect_reference_table.h"
+#include "sirt_ref.h"
+
+#define __ assembler->
+
+namespace art {
+
+namespace arm {
+const std::vector<uint8_t>* CreateQuickResolutionTrampoline() {
+  UniquePtr<ArmAssembler> assembler(static_cast<ArmAssembler*>(Assembler::Create(kArm)));
+  // | Out args |
+  // | Method*  | <- SP on entry
+  // | LR       |    return address into caller
+  // | ...      |    callee saves
+  // | R3       |    possible argument
+  // | R2       |    possible argument
+  // | R1       |    possible argument
+  // | R0       |    junk on call to QuickResolutionTrampolineFromCode, holds result Method*
+  // | Method*  |    Callee save Method* set up by QuickResoltuionTrampolineFromCode
+  // Save callee saves and ready frame for exception delivery
+  RegList save = (1 << R1) | (1 << R2) | (1 << R3) | (1 << R5) | (1 << R6) | (1 << R7) | (1 << R8) |
+                 (1 << R10) | (1 << R11) | (1 << LR);
+  // TODO: enable when GetCalleeSaveMethod is available at stub generation time
+  // DCHECK_EQ(save, Runtime::Current()->GetCalleeSaveMethod(Runtime::kRefsAndArgs)->GetCoreSpillMask());
+  __ PushList(save);
+  __ LoadFromOffset(kLoadWord, R12, TR, ENTRYPOINT_OFFSET(pQuickResolutionTrampolineFromCode));
+  __ mov(R3, ShifterOperand(TR));  // Pass Thread::Current() in R3
+  __ IncreaseFrameSize(8);         // 2 words of space for alignment
+  __ mov(R2, ShifterOperand(SP));  // Pass SP
+  // Call to resolution trampoline (method_idx, receiver, sp, Thread*)
+  __ blx(R12);
+  __ mov(R12, ShifterOperand(R0));  // Save code address returned into R12
+  // Restore registers which may have been modified by GC, "R0" will hold the Method*
+  __ DecreaseFrameSize(4);
+  __ PopList((1 << R0) | save);
+  __ bx(R12);  // Leaf call to method's code
+  __ bkpt(0);
+
+  assembler->EmitSlowPaths();
+  size_t cs = assembler->CodeSize();
+  UniquePtr<std::vector<uint8_t> > resolution_trampoline(new std::vector<uint8_t>(cs));
+  MemoryRegion code(&(*resolution_trampoline)[0], resolution_trampoline->size());
+  assembler->FinalizeInstructions(code);
+
+  return resolution_trampoline.release();
+}
+
+const std::vector<uint8_t>* CreateInterpreterToInterpreterEntry() {
+  UniquePtr<ArmAssembler> assembler(static_cast<ArmAssembler*>(Assembler::Create(kArm)));
+
+  __ LoadFromOffset(kLoadWord, PC, R0, ENTRYPOINT_OFFSET(pInterpreterToInterpreterEntry));
+  __ bkpt(0);
+
+  size_t cs = assembler->CodeSize();
+  UniquePtr<std::vector<uint8_t> > entry_stub(new std::vector<uint8_t>(cs));
+  MemoryRegion code(&(*entry_stub)[0], entry_stub->size());
+  assembler->FinalizeInstructions(code);
+
+  return entry_stub.release();
+}
+
+const std::vector<uint8_t>* CreateInterpreterToQuickEntry() {
+  UniquePtr<ArmAssembler> assembler(static_cast<ArmAssembler*>(Assembler::Create(kArm)));
+
+  __ LoadFromOffset(kLoadWord, PC, R0, ENTRYPOINT_OFFSET(pInterpreterToQuickEntry));
+  __ bkpt(0);
+
+  size_t cs = assembler->CodeSize();
+  UniquePtr<std::vector<uint8_t> > entry_stub(new std::vector<uint8_t>(cs));
+  MemoryRegion code(&(*entry_stub)[0], entry_stub->size());
+  assembler->FinalizeInstructions(code);
+
+  return entry_stub.release();
+}
+} // namespace arm
+
+namespace mips {
+const std::vector<uint8_t>* CreateQuickResolutionTrampoline() {
+  UniquePtr<MipsAssembler> assembler(static_cast<MipsAssembler*>(Assembler::Create(kMips)));
+  // | Out args   |
+  // | Method*    | <- SP on entry
+  // | RA         |    return address into caller
+  // | ...        |    callee saves
+  // | A3         |    possible argument
+  // | A2         |    possible argument
+  // | A1         |    possible argument
+  // | A0/Method* |    Callee save Method* set up by UnresolvedDirectMethodTrampolineFromCode
+  // Save callee saves and ready frame for exception delivery
+  __ AddConstant(SP, SP, -64);
+  __ StoreToOffset(kStoreWord, RA, SP, 60);
+  __ StoreToOffset(kStoreWord, FP, SP, 56);
+  __ StoreToOffset(kStoreWord, GP, SP, 52);
+  __ StoreToOffset(kStoreWord, S7, SP, 48);
+  __ StoreToOffset(kStoreWord, S6, SP, 44);
+  __ StoreToOffset(kStoreWord, S5, SP, 40);
+  __ StoreToOffset(kStoreWord, S4, SP, 36);
+  __ StoreToOffset(kStoreWord, S3, SP, 32);
+  __ StoreToOffset(kStoreWord, S2, SP, 28);
+  __ StoreToOffset(kStoreWord, A3, SP, 12);
+  __ StoreToOffset(kStoreWord, A2, SP, 8);
+  __ StoreToOffset(kStoreWord, A1, SP, 4);
+
+  __ LoadFromOffset(kLoadWord, T9, S1, ENTRYPOINT_OFFSET(pQuickResolutionTrampolineFromCode));
+  __ Move(A3, S1);  // Pass Thread::Current() in A3
+  __ Move(A2, SP);  // Pass SP for Method** callee_addr
+  __ Jalr(T9); // Call to resolution trampoline (method_idx, receiver, sp, Thread*)
+
+  // Restore registers which may have been modified by GC
+  __ LoadFromOffset(kLoadWord, A0, SP, 0);
+  __ LoadFromOffset(kLoadWord, A1, SP, 4);
+  __ LoadFromOffset(kLoadWord, A2, SP, 8);
+  __ LoadFromOffset(kLoadWord, A3, SP, 12);
+  __ LoadFromOffset(kLoadWord, S2, SP, 28);
+  __ LoadFromOffset(kLoadWord, S3, SP, 32);
+  __ LoadFromOffset(kLoadWord, S4, SP, 36);
+  __ LoadFromOffset(kLoadWord, S5, SP, 40);
+  __ LoadFromOffset(kLoadWord, S6, SP, 44);
+  __ LoadFromOffset(kLoadWord, S7, SP, 48);
+  __ LoadFromOffset(kLoadWord, GP, SP, 52);
+  __ LoadFromOffset(kLoadWord, FP, SP, 56);
+  __ LoadFromOffset(kLoadWord, RA, SP, 60);
+  __ AddConstant(SP, SP, 64);
+
+  __ Move(T9, V0); // Put method's code in T9
+  __ Jr(T9);  // Leaf call to method's code
+
+  __ Break();
+
+  assembler->EmitSlowPaths();
+  size_t cs = assembler->CodeSize();
+  UniquePtr<std::vector<uint8_t> > resolution_trampoline(new std::vector<uint8_t>(cs));
+  MemoryRegion code(&(*resolution_trampoline)[0], resolution_trampoline->size());
+  assembler->FinalizeInstructions(code);
+
+  return resolution_trampoline.release();
+}
+
+const std::vector<uint8_t>* CreateInterpreterToInterpreterEntry() {
+  UniquePtr<MipsAssembler> assembler(static_cast<MipsAssembler*>(Assembler::Create(kMips)));
+
+  __ LoadFromOffset(kLoadWord, T9, A0, ENTRYPOINT_OFFSET(pInterpreterToInterpreterEntry));
+  __ Jr(T9);
+  __ Break();
+
+  size_t cs = assembler->CodeSize();
+  UniquePtr<std::vector<uint8_t> > entry_stub(new std::vector<uint8_t>(cs));
+  MemoryRegion code(&(*entry_stub)[0], entry_stub->size());
+  assembler->FinalizeInstructions(code);
+
+  return entry_stub.release();
+}
+
+const std::vector<uint8_t>* CreateInterpreterToQuickEntry() {
+  UniquePtr<MipsAssembler> assembler(static_cast<MipsAssembler*>(Assembler::Create(kMips)));
+
+  __ LoadFromOffset(kLoadWord, T9, A0, ENTRYPOINT_OFFSET(pInterpreterToInterpreterEntry));
+  __ Jr(T9);
+  __ Break();
+
+  size_t cs = assembler->CodeSize();
+  UniquePtr<std::vector<uint8_t> > entry_stub(new std::vector<uint8_t>(cs));
+  MemoryRegion code(&(*entry_stub)[0], entry_stub->size());
+  assembler->FinalizeInstructions(code);
+
+  return entry_stub.release();
+}
+} // namespace mips
+
+namespace x86 {
+const std::vector<uint8_t>* CreateQuickResolutionTrampoline() {
+  UniquePtr<X86Assembler> assembler(static_cast<X86Assembler*>(Assembler::Create(kX86)));
+  // Set up the callee save frame to conform with Runtime::CreateCalleeSaveMethod(kRefsAndArgs)
+  // return address
+  __ pushl(EDI);
+  __ pushl(ESI);
+  __ pushl(EBP);
+  __ pushl(EBX);
+  __ pushl(EDX);
+  __ pushl(ECX);
+  __ pushl(EAX);  // <-- callee save Method* to go here
+  __ movl(EDX, ESP);          // save ESP
+  __ fs()->pushl(Address::Absolute(Thread::SelfOffset()));  // pass Thread*
+  __ pushl(EDX);              // pass ESP for Method*
+  __ pushl(ECX);              // pass receiver
+  __ pushl(EAX);              // pass Method*
+
+  // Call to resolve method.
+  __ Call(ThreadOffset(ENTRYPOINT_OFFSET(pQuickResolutionTrampolineFromCode)),
+          X86ManagedRegister::FromCpuRegister(ECX));
+
+  __ movl(EDI, EAX);  // save code pointer in EDI
+  __ addl(ESP, Immediate(16));  // Pop arguments
+  __ popl(EAX);  // Restore args.
+  __ popl(ECX);
+  __ popl(EDX);
+  __ popl(EBX);
+  __ popl(EBP);  // Restore callee saves.
+  __ popl(ESI);
+  // Swap EDI callee save with code pointer
+  __ xchgl(EDI, Address(ESP, 0));
+  // Tail call to intended method.
+  __ ret();
+
+  assembler->EmitSlowPaths();
+  size_t cs = assembler->CodeSize();
+  UniquePtr<std::vector<uint8_t> > resolution_trampoline(new std::vector<uint8_t>(cs));
+  MemoryRegion code(&(*resolution_trampoline)[0], resolution_trampoline->size());
+  assembler->FinalizeInstructions(code);
+
+  return resolution_trampoline.release();
+}
+
+const std::vector<uint8_t>* CreateInterpreterToInterpreterEntry() {
+  UniquePtr<X86Assembler> assembler(static_cast<X86Assembler*>(Assembler::Create(kX86)));
+
+  __ fs()->jmp(Address::Absolute(ThreadOffset(ENTRYPOINT_OFFSET(pInterpreterToInterpreterEntry))));
+
+  size_t cs = assembler->CodeSize();
+  UniquePtr<std::vector<uint8_t> > entry_stub(new std::vector<uint8_t>(cs));
+  MemoryRegion code(&(*entry_stub)[0], entry_stub->size());
+  assembler->FinalizeInstructions(code);
+
+  return entry_stub.release();
+}
+
+const std::vector<uint8_t>* CreateInterpreterToQuickEntry() {
+  UniquePtr<X86Assembler> assembler(static_cast<X86Assembler*>(Assembler::Create(kX86)));
+
+  __ fs()->jmp(Address::Absolute(ThreadOffset(ENTRYPOINT_OFFSET(pInterpreterToQuickEntry))));
+
+  size_t cs = assembler->CodeSize();
+  UniquePtr<std::vector<uint8_t> > entry_stub(new std::vector<uint8_t>(cs));
+  MemoryRegion code(&(*entry_stub)[0], entry_stub->size());
+  assembler->FinalizeInstructions(code);
+
+  return entry_stub.release();
+}
+} // namespace x86
+
+} // namespace art
diff --git a/src/compiler/stubs/stubs.h b/src/compiler/stubs/stubs.h
new file mode 100644
index 0000000..ebe761d
--- /dev/null
+++ b/src/compiler/stubs/stubs.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2012 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_SRC_COMPILER_STUBS_STUBS_H_
+#define ART_SRC_COMPILER_STUBS_STUBS_H_
+
+#include "runtime.h"
+
+namespace art {
+
+namespace arm {
+const std::vector<uint8_t>* CreatePortableResolutionTrampoline()
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+const std::vector<uint8_t>* CreateQuickResolutionTrampoline()
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+const std::vector<uint8_t>* CreateInterpreterToInterpreterEntry()
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+const std::vector<uint8_t>* CreateInterpreterToQuickEntry()
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+}
+
+namespace mips {
+const std::vector<uint8_t>* CreatePortableResolutionTrampoline()
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+const std::vector<uint8_t>* CreateQuickResolutionTrampoline()
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+const std::vector<uint8_t>* CreateInterpreterToInterpreterEntry()
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+const std::vector<uint8_t>* CreateInterpreterToQuickEntry()
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+}
+
+namespace x86 {
+const std::vector<uint8_t>* CreatePortableResolutionTrampoline()
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+const std::vector<uint8_t>* CreateQuickResolutionTrampoline()
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+const std::vector<uint8_t>* CreateInterpreterToInterpreterEntry()
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+const std::vector<uint8_t>* CreateInterpreterToQuickEntry()
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+}
+
+}  // namespace art
+
+#endif  // ART_SRC_COMPILER_STUBS_STUBS_H_
diff --git a/src/dex2oat.cc b/src/dex2oat.cc
index 6b69278..33c1ad4 100644
--- a/src/dex2oat.cc
+++ b/src/dex2oat.cc
@@ -162,7 +162,7 @@
 
 
   // Make a list of descriptors for classes to include in the image
-  const std::set<std::string>* GetImageClassDescriptors(const char* image_classes_filename)
+  std::set<std::string>* GetImageClassDescriptors(const char* image_classes_filename)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     UniquePtr<std::ifstream> image_classes_file(new std::ifstream(image_classes_filename, std::ifstream::in));
     if (image_classes_file.get() == NULL) {
@@ -222,7 +222,7 @@
     // We walk the roots looking for classes so that we'll pick up the
     // above classes plus any classes them depend on such super
     // classes, interfaces, and the required ClassLinker roots.
-    UniquePtr<std::set<std::string> > image_classes(new std::set<std::string>());
+    UniquePtr<ImageWriter::DescriptorSet> image_classes(new ImageWriter::DescriptorSet);
     class_linker->VisitClasses(RecordImageClassesVisitor, image_classes.get());
     CHECK_NE(image_classes->size(), 0U);
     return image_classes.release();
@@ -236,7 +236,7 @@
                                       File* oat_file,
                                       const std::string& bitcode_filename,
                                       bool image,
-                                      const std::set<std::string>* image_classes,
+                                      const ImageWriter::DescriptorSet* image_classes,
                                       bool dump_stats,
                                       bool dump_timings)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -280,9 +280,8 @@
     std::string image_file_location;
     uint32_t image_file_location_oat_checksum = 0;
     uint32_t image_file_location_oat_data_begin = 0;
-    Heap* heap = Runtime::Current()->GetHeap();
-    if (heap->GetSpaces().size() > 1) {
-      ImageSpace* image_space = heap->GetImageSpace();
+    if (!driver->IsImage()) {
+      ImageSpace* image_space = Runtime::Current()->GetHeap()->GetImageSpace();
       image_file_location_oat_checksum = image_space->GetImageHeader().GetOatChecksum();
       image_file_location_oat_data_begin =
           reinterpret_cast<uint32_t>(image_space->GetImageHeader().GetOatDataBegin());
@@ -321,7 +320,7 @@
 
   bool CreateImageFile(const std::string& image_filename,
                        uintptr_t image_base,
-                       const std::set<std::string>* image_classes,
+                       ImageWriter::DescriptorSet* image_classes,
                        const std::string& oat_filename,
                        const std::string& oat_location,
                        const CompilerDriver& compiler)
@@ -439,7 +438,7 @@
 
   static bool RecordImageClassesVisitor(mirror::Class* klass, void* arg)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    std::set<std::string>* image_classes = reinterpret_cast<std::set<std::string>*>(arg);
+    ImageWriter::DescriptorSet* image_classes = reinterpret_cast<ImageWriter::DescriptorSet*>(arg);
     if (klass->IsArrayClass() || klass->IsPrimitive()) {
       return true;
     }
@@ -942,7 +941,7 @@
   ScopedObjectAccess soa(Thread::Current());
 
   // If --image-classes was specified, calculate the full list of classes to include in the image
-  UniquePtr<const std::set<std::string> > image_classes(NULL);
+  UniquePtr<ImageWriter::DescriptorSet> image_classes(NULL);
   if (image_classes_filename != NULL) {
     image_classes.reset(dex2oat->GetImageClassDescriptors(image_classes_filename));
     if (image_classes.get() == NULL) {
diff --git a/src/image_test.cc b/src/image_test.cc
index 8066a90..cd1a34f 100644
--- a/src/image_test.cc
+++ b/src/image_test.cc
@@ -48,7 +48,8 @@
       dex_files.push_back(java_lang_dex_file_);
       dex_files.push_back(conscrypt_file_);
       VectorOutputStream output_stream(tmp_elf.GetFilename(), oat_contents);
-      bool success_oat = OatWriter::Create(output_stream, dex_files, 0, 0, "", *compiler_driver_.get());
+      bool success_oat = OatWriter::Create(output_stream, dex_files, 0, 0, "",
+                                           *compiler_driver_.get());
       ASSERT_TRUE(success_oat);
 
       // Force all system classes into memory
diff --git a/src/image_writer.cc b/src/image_writer.cc
index faa13f7..4ba99fe 100644
--- a/src/image_writer.cc
+++ b/src/image_writer.cc
@@ -81,6 +81,10 @@
   }
   oat_file_ = OatFile::OpenWritable(oat_file.get(), oat_location);
   class_linker->RegisterOatFile(*oat_file_);
+  interpreter_to_interpreter_entry_offset_ = oat_file_->GetOatHeader().GetInterpreterToInterpreterEntryOffset();
+  interpreter_to_quick_entry_offset_ = oat_file_->GetOatHeader().GetInterpreterToQuickEntryOffset();
+  portable_resolution_trampoline_offset_ = oat_file_->GetOatHeader().GetPortableResolutionTrampolineOffset();
+  quick_resolution_trampoline_offset_ = oat_file_->GetOatHeader().GetQuickResolutionTrampolineOffset();
 
   {
     Thread::Current()->TransitionFromSuspendedToRunnable();
@@ -221,17 +225,30 @@
   Runtime* runtime = Runtime::Current();
   ClassLinker* class_linker = runtime->GetClassLinker();
 
+  // Update image_classes_ with classes for objects created by <clinit> methods.
+  Thread* self = Thread::Current();
+  const char* old_cause = self->StartAssertNoThreadSuspension("ImageWriter");
+  Heap* heap = Runtime::Current()->GetHeap();
+  // TODO: Image spaces only?
+  WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
+  heap->FlushAllocStack();
+  heap->GetLiveBitmap()->Walk(FindClinitImageClassesCallback, this);
+  self->EndAssertNoThreadSuspension(old_cause);
+
+  // Make a list of classes we would like to prune.
   std::set<std::string> non_image_classes;
   NonImageClasses context;
   context.image_writer = this;
   context.non_image_classes = &non_image_classes;
   class_linker->VisitClasses(NonImageClassesVisitor, &context);
 
+  // Remove the undesired classes from the class roots.
   typedef std::set<std::string>::const_iterator ClassIt;  // TODO: C++0x auto
   for (ClassIt it = non_image_classes.begin(), end = non_image_classes.end(); it != end; ++it) {
     class_linker->RemoveClass((*it).c_str(), NULL);
   }
 
+  // Clear references to removed classes from the DexCaches.
   AbstractMethod* resolution_method = runtime->GetResolutionMethod();
   typedef Set::const_iterator CacheIt;  // TODO: C++0x auto
   for (CacheIt it = dex_caches_.begin(), end = dex_caches_.end(); it != end; ++it) {
@@ -258,6 +275,28 @@
   }
 }
 
+void ImageWriter::FindClinitImageClassesCallback(Object* object, void* arg) {
+  DCHECK(object != NULL);
+  DCHECK(arg != NULL);
+  ImageWriter* image_writer = reinterpret_cast<ImageWriter*>(arg);
+  Class* klass = object->GetClass();
+  while (klass->IsArrayClass()) {
+    klass = klass->GetComponentType();
+  }
+  if (klass->IsPrimitive()) {
+    return;
+  }
+  while (!klass->IsObjectClass()) {
+    ClassHelper kh(klass);
+    const char* descriptor = kh.GetDescriptor();
+    std::pair<DescriptorSet::iterator, bool> result = image_writer->image_classes_->insert(descriptor);
+    if (result.second) {
+      LOG(INFO) << "Adding " << descriptor << " to image classes";
+    }
+    klass = klass->GetSuperClass();
+  }
+}
+
 bool ImageWriter::NonImageClassesVisitor(Class* klass, void* arg) {
   NonImageClasses* context = reinterpret_cast<NonImageClasses*>(arg);
   if (!context->image_writer->IsImageClass(klass)) {
@@ -477,17 +516,34 @@
   if (orig->IsAbstract()) {
     // Code for abstract methods is set to the abstract method error stub when we load the image.
     copy->SetEntryPointFromCompiledCode(NULL);
+    copy->SetEntryPointFromInterpreter(reinterpret_cast<EntryPointFromInterpreter*>
+                                       (GetOatAddress(interpreter_to_interpreter_entry_offset_)));
     return;
+  } else {
+    copy->SetEntryPointFromInterpreter(reinterpret_cast<EntryPointFromInterpreter*>
+                                       (GetOatAddress(interpreter_to_quick_entry_offset_)));
   }
 
   if (orig == Runtime::Current()->GetResolutionMethod()) {
-    // The resolution method's code is set to the resolution trampoline when we load the image.
-    copy->SetEntryPointFromCompiledCode(NULL);
+#if defined(ART_USE_PORTABLE_COMPILER)
+    copy->SetEntryPointFromCompiledCode(GetOatAddress(portable_resolution_trampoline_offset_));
+#else
+    copy->SetEntryPointFromCompiledCode(GetOatAddress(quick_resolution_trampoline_offset_));
+#endif
     return;
   }
 
-  // Non-abstract methods have code
-  copy->SetEntryPointFromCompiledCode(GetOatAddress(orig->GetOatCodeOffset()));
+  // Use original code if it exists. Otherwise, set the code pointer to the resolution trampoline.
+  const byte* code = GetOatAddress(orig->GetOatCodeOffset());
+  if (code != NULL) {
+    copy->SetEntryPointFromCompiledCode(code);
+  } else {
+#if defined(ART_USE_PORTABLE_COMPILER)
+    copy->SetEntryPointFromCompiledCode(GetOatAddress(portable_resolution_trampoline_offset_));
+#else
+    copy->SetEntryPointFromCompiledCode(GetOatAddress(quick_resolution_trampoline_offset_));
+#endif
+  }
 
   if (orig->IsNative()) {
     // The native method's pointer is set to a stub to lookup via dlsym when we load the image.
diff --git a/src/image_writer.h b/src/image_writer.h
index 46d134f..4628e5a 100644
--- a/src/image_writer.h
+++ b/src/image_writer.h
@@ -37,9 +37,12 @@
 // Write a Space built during compilation for use during execution.
 class ImageWriter {
  public:
-  explicit ImageWriter(const std::set<std::string>* image_classes)
+  typedef std::set<std::string> DescriptorSet;
+  explicit ImageWriter(DescriptorSet* image_classes)
       : oat_file_(NULL), image_end_(0), image_begin_(NULL), image_classes_(image_classes),
-        oat_data_begin_(NULL) {}
+        oat_data_begin_(NULL), interpreter_to_interpreter_entry_offset_(0),
+        interpreter_to_quick_entry_offset_(0), portable_resolution_trampoline_offset_(0),
+        quick_resolution_trampoline_offset_(0) {}
 
   ~ImageWriter() {}
 
@@ -110,27 +113,36 @@
     return oat_data_begin_ + offset;
   }
 
+  // Returns true if the class was in the original requested image classes list.
   bool IsImageClass(const mirror::Class* klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  // Debug aid that list of requested image classes.
   void DumpImageClasses();
 
+  // Preinitializes some otherwise lazy fields (such as Class name) to avoid runtime image dirtying.
   void ComputeLazyFieldsForImageClasses()
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   static bool ComputeLazyFieldsForClassesVisitor(mirror::Class* klass, void* arg)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  // Wire dex cache resolved strings to strings in the image to avoid runtime resolution
+  // Wire dex cache resolved strings to strings in the image to avoid runtime resolution.
   void ComputeEagerResolvedStrings();
   static void ComputeEagerResolvedStringsCallback(mirror::Object* obj, void* arg)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  // Remove unwanted classes from various roots.
   void PruneNonImageClasses() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  static void FindClinitImageClassesCallback(mirror::Object* object, void* arg)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   static bool NonImageClassesVisitor(mirror::Class* c, void* arg)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  // Verify unwanted classes removed.
   void CheckNonImageClassesRemoved();
   static void CheckNonImageClassesRemovedCallback(mirror::Object* obj, void* arg)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  // Lays out where the image objects will be at runtime.
   void CalculateNewObjectOffsets(size_t oat_loaded_size, size_t oat_data_offset)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   mirror::ObjectArray<mirror::Object>* CreateImageRoots() const
@@ -138,6 +150,7 @@
   static void CalculateNewObjectOffsetsCallback(mirror::Object* obj, void* arg)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  // Creates the contiguous image in memory and adjusts pointers.
   void CopyAndFixupObjects();
   static void CopyAndFixupObjectsCallback(mirror::Object* obj, void* arg)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -158,31 +171,40 @@
                    bool is_static)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  // Patches references in OatFile to expect runtime addresses.
   void PatchOatCodeAndMethods(const CompilerDriver& compiler)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   void SetPatchLocation(const CompilerDriver::PatchInformation* patch, uint32_t value)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+
+  // Map of Object to where it will be at runtime.
   SafeMap<const mirror::Object*, size_t> offsets_;
 
   // oat file with code for this image
   OatFile* oat_file_;
 
-  // memory mapped for generating the image
+  // Memory mapped for generating the image.
   UniquePtr<MemMap> image_;
 
-  // Offset to the free space in image_
+  // Offset to the free space in image_.
   size_t image_end_;
 
-  // Beginning target image address for the output image
+  // Beginning target image address for the output image.
   byte* image_begin_;
 
   // Set of classes to be include in the image, or NULL for all.
-  const std::set<std::string>* image_classes_;
+  DescriptorSet* image_classes_;
 
-  // Beginning target oat address for the pointers from the output image to its oat file
+  // Beginning target oat address for the pointers from the output image to its oat file.
   const byte* oat_data_begin_;
 
+  // Offset from oat_data_begin_ to the stubs.
+  uint32_t interpreter_to_interpreter_entry_offset_;
+  uint32_t interpreter_to_quick_entry_offset_;
+  uint32_t portable_resolution_trampoline_offset_;
+  uint32_t quick_resolution_trampoline_offset_;
+
   // DexCaches seen while scanning for fixing up CodeAndDirectMethods
   typedef std::set<mirror::DexCache*> Set;
   Set dex_caches_;
diff --git a/src/instrumentation.cc b/src/instrumentation.cc
index 39fd377..8af0885 100644
--- a/src/instrumentation.cc
+++ b/src/instrumentation.cc
@@ -62,7 +62,7 @@
         if (is_initialized || !method->IsStatic() || method->IsConstructor()) {
           new_code = class_linker->GetOatCodeFor(method);
         } else {
-          new_code = GetResolutionTrampoline();
+          new_code = GetResolutionTrampoline(class_linker);
         }
       } else {  // !uninstall
         if (!interpreter_stubs_installed_ || method->IsNative()) {
@@ -380,7 +380,7 @@
   if (LIKELY(!instrumentation_stubs_installed_)) {
     const void* code = method->GetEntryPointFromCompiledCode();
     DCHECK(code != NULL);
-    if (LIKELY(code != GetResolutionTrampoline())) {
+    if (LIKELY(code != GetResolutionTrampoline(runtime->GetClassLinker()))) {
       return code;
     }
   }
diff --git a/src/interpreter/interpreter.cc b/src/interpreter/interpreter.cc
index b185cf5..0e3ed28 100644
--- a/src/interpreter/interpreter.cc
+++ b/src/interpreter/interpreter.cc
@@ -133,7 +133,7 @@
     }
   } else {
     // Not special, continue with regular interpreter execution.
-    EnterInterpreterFromInterpreter(self, mh, code_item, shadow_frame, result);
+    artInterpreterToInterpreterEntry(self, mh, code_item, shadow_frame, result);
   }
 }
 
@@ -1239,7 +1239,7 @@
         uint16_t size = switch_data[1];
         DCHECK_GT(size, 0);
         const int32_t* keys = reinterpret_cast<const int32_t*>(&switch_data[2]);
-        CHECK(IsAligned<4>(keys));
+        DCHECK(IsAligned<4>(keys));
         int32_t first_key = keys[0];
         const int32_t* targets = reinterpret_cast<const int32_t*>(&switch_data[4]);
         DCHECK(IsAligned<4>(targets));
@@ -1255,13 +1255,13 @@
         PREAMBLE();
         const uint16_t* switch_data = reinterpret_cast<const uint16_t*>(inst) + inst->VRegB_31t();
         int32_t test_val = shadow_frame.GetVReg(inst->VRegA_31t());
-        CHECK_EQ(switch_data[0], static_cast<uint16_t>(Instruction::kSparseSwitchSignature));
+        DCHECK_EQ(switch_data[0], static_cast<uint16_t>(Instruction::kSparseSwitchSignature));
         uint16_t size = switch_data[1];
-        CHECK_GT(size, 0);
+        DCHECK_GT(size, 0);
         const int32_t* keys = reinterpret_cast<const int32_t*>(&switch_data[2]);
-        CHECK(IsAligned<4>(keys));
+        DCHECK(IsAligned<4>(keys));
         const int32_t* entries = keys + size;
-        CHECK(IsAligned<4>(entries));
+        DCHECK(IsAligned<4>(entries));
         int lo = 0;
         int hi = size - 1;
         const Instruction* current_inst = inst;
@@ -2832,9 +2832,9 @@
   return Execute(self, mh, code_item, shadow_frame, JValue());
 }
 
-void EnterInterpreterFromInterpreter(Thread* self, MethodHelper& mh,
-                                     const DexFile::CodeItem* code_item, ShadowFrame* shadow_frame,
-                                     JValue* result)
+void artInterpreterToInterpreterEntry(Thread* self, MethodHelper& mh,
+                                      const DexFile::CodeItem* code_item,
+                                      ShadowFrame* shadow_frame, JValue* result)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   if (UNLIKELY(__builtin_frame_address(0) < self->GetStackEnd())) {
     ThrowStackOverflowError(self);
diff --git a/src/interpreter/interpreter.h b/src/interpreter/interpreter.h
index bae3b65..20166ac 100644
--- a/src/interpreter/interpreter.h
+++ b/src/interpreter/interpreter.h
@@ -47,9 +47,9 @@
                                        ShadowFrame& shadow_frame)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-extern void EnterInterpreterFromInterpreter(Thread* self, MethodHelper& mh,
-                                            const DexFile::CodeItem* code_item,
-                                            ShadowFrame* shadow_frame, JValue* result)
+extern "C" void artInterpreterToInterpreterEntry(Thread* self, MethodHelper& mh,
+                                                const DexFile::CodeItem* code_item,
+                                                ShadowFrame* shadow_frame, JValue* result)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
 }  // namespace interpreter
diff --git a/src/mirror/abstract_method-inl.h b/src/mirror/abstract_method-inl.h
index d4f0f2c..a823886 100644
--- a/src/mirror/abstract_method-inl.h
+++ b/src/mirror/abstract_method-inl.h
@@ -117,7 +117,8 @@
   if (GetEntryPointFromCompiledCode() == GetInterpreterEntryPoint()) {
     return;
   }
-  if (GetEntryPointFromCompiledCode() == GetResolutionTrampoline()) {
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  if (GetEntryPointFromCompiledCode() == GetResolutionTrampoline(class_linker)) {
       return;
   }
   DCHECK(IsWithinCode(pc))
diff --git a/src/oat.cc b/src/oat.cc
index 4eb97f5..e606953 100644
--- a/src/oat.cc
+++ b/src/oat.cc
@@ -22,7 +22,7 @@
 namespace art {
 
 const uint8_t OatHeader::kOatMagic[] = { 'o', 'a', 't', '\n' };
-const uint8_t OatHeader::kOatVersion[] = { '0', '0', '5', '\0' };
+const uint8_t OatHeader::kOatVersion[] = { '0', '0', '6', '\0' };
 
 OatHeader::OatHeader() {
   memset(this, 0, sizeof(*this));
@@ -57,6 +57,10 @@
   UpdateChecksum(image_file_location.data(), image_file_location_size_);
 
   executable_offset_ = 0;
+  interpreter_to_interpreter_entry_offset_ = 0;
+  interpreter_to_quick_entry_offset_ = 0;
+  portable_resolution_trampoline_offset_ = 0;
+  quick_resolution_trampoline_offset_ = 0;
 }
 
 bool OatHeader::IsValid() const {
@@ -97,6 +101,92 @@
   return executable_offset_;
 }
 
+void OatHeader::SetExecutableOffset(uint32_t executable_offset) {
+  DCHECK_ALIGNED(executable_offset, kPageSize);
+  CHECK_GT(executable_offset, sizeof(OatHeader));
+  DCHECK(IsValid());
+  DCHECK_EQ(executable_offset_, 0U);
+
+  executable_offset_ = executable_offset;
+  UpdateChecksum(&executable_offset_, sizeof(executable_offset));
+}
+
+const void* OatHeader::GetInterpreterToInterpreterEntry() const {
+  return reinterpret_cast<const uint8_t*>(this) + GetInterpreterToInterpreterEntryOffset();
+}
+
+uint32_t OatHeader::GetInterpreterToInterpreterEntryOffset() const {
+  DCHECK(IsValid());
+  CHECK_GE(interpreter_to_interpreter_entry_offset_, executable_offset_);
+  return interpreter_to_interpreter_entry_offset_;
+}
+
+void OatHeader::SetInterpreterToInterpreterEntryOffset(uint32_t offset) {
+  CHECK(offset == 0 || offset >= executable_offset_);
+  DCHECK(IsValid());
+  DCHECK_EQ(interpreter_to_interpreter_entry_offset_, 0U) << offset;
+
+  interpreter_to_interpreter_entry_offset_ = offset;
+  UpdateChecksum(&interpreter_to_interpreter_entry_offset_, sizeof(offset));
+}
+
+const void* OatHeader::GetInterpreterToQuickEntry() const {
+  return reinterpret_cast<const uint8_t*>(this) + GetInterpreterToQuickEntryOffset();
+}
+
+uint32_t OatHeader::GetInterpreterToQuickEntryOffset() const {
+  DCHECK(IsValid());
+  CHECK_GE(interpreter_to_quick_entry_offset_, interpreter_to_interpreter_entry_offset_);
+  return interpreter_to_quick_entry_offset_;
+}
+
+void OatHeader::SetInterpreterToQuickEntryOffset(uint32_t offset) {
+  CHECK(offset == 0 || offset >= interpreter_to_interpreter_entry_offset_);
+  DCHECK(IsValid());
+  DCHECK_EQ(interpreter_to_quick_entry_offset_, 0U) << offset;
+
+  interpreter_to_quick_entry_offset_ = offset;
+  UpdateChecksum(&interpreter_to_quick_entry_offset_, sizeof(offset));
+}
+
+const void* OatHeader::GetPortableResolutionTrampoline() const {
+  return reinterpret_cast<const uint8_t*>(this) + GetPortableResolutionTrampolineOffset();
+}
+
+uint32_t OatHeader::GetPortableResolutionTrampolineOffset() const {
+  DCHECK(IsValid());
+  CHECK_GE(portable_resolution_trampoline_offset_, interpreter_to_quick_entry_offset_);
+  return portable_resolution_trampoline_offset_;
+}
+
+void OatHeader::SetPortableResolutionTrampolineOffset(uint32_t offset) {
+  CHECK(offset == 0 || offset >= interpreter_to_quick_entry_offset_);
+  DCHECK(IsValid());
+  DCHECK_EQ(portable_resolution_trampoline_offset_, 0U) << offset;
+
+  portable_resolution_trampoline_offset_ = offset;
+  UpdateChecksum(&portable_resolution_trampoline_offset_, sizeof(offset));
+}
+
+const void* OatHeader::GetQuickResolutionTrampoline() const {
+  return reinterpret_cast<const uint8_t*>(this) + GetQuickResolutionTrampolineOffset();
+}
+
+uint32_t OatHeader::GetQuickResolutionTrampolineOffset() const {
+  DCHECK(IsValid());
+  CHECK_GE(quick_resolution_trampoline_offset_, portable_resolution_trampoline_offset_);
+  return quick_resolution_trampoline_offset_;
+}
+
+void OatHeader::SetQuickResolutionTrampolineOffset(uint32_t offset) {
+  CHECK(offset == 0 || offset >= portable_resolution_trampoline_offset_);
+  DCHECK(IsValid());
+  DCHECK_EQ(quick_resolution_trampoline_offset_, 0U) << offset;
+
+  quick_resolution_trampoline_offset_ = offset;
+  UpdateChecksum(&quick_resolution_trampoline_offset_, sizeof(offset));
+}
+
 uint32_t OatHeader::GetImageFileLocationOatChecksum() const {
   CHECK(IsValid());
   return image_file_location_oat_checksum_;
@@ -123,16 +213,6 @@
                      GetImageFileLocationSize());
 }
 
-void OatHeader::SetExecutableOffset(uint32_t executable_offset) {
-  DCHECK_ALIGNED(executable_offset, kPageSize);
-  CHECK_GT(executable_offset, sizeof(OatHeader));
-  DCHECK(IsValid());
-  DCHECK_EQ(executable_offset_, 0U);
-
-  executable_offset_ = executable_offset;
-  UpdateChecksum(&executable_offset_, sizeof(executable_offset));
-}
-
 OatMethodOffsets::OatMethodOffsets()
   : code_offset_(0),
     frame_size_in_bytes_(0),
diff --git a/src/oat.h b/src/oat.h
index cf98891..c67a1a6 100644
--- a/src/oat.h
+++ b/src/oat.h
@@ -43,8 +43,20 @@
     return dex_file_count_;
   }
   uint32_t GetExecutableOffset() const;
-  InstructionSet GetInstructionSet() const;
   void SetExecutableOffset(uint32_t executable_offset);
+  const void* GetInterpreterToInterpreterEntry() const;
+  uint32_t GetInterpreterToInterpreterEntryOffset() const;
+  void SetInterpreterToInterpreterEntryOffset(uint32_t offset);
+  const void* GetInterpreterToQuickEntry() const;
+  uint32_t GetInterpreterToQuickEntryOffset() const;
+  void SetInterpreterToQuickEntryOffset(uint32_t offset);
+  const void* GetPortableResolutionTrampoline() const;
+  uint32_t GetPortableResolutionTrampolineOffset() const;
+  void SetPortableResolutionTrampolineOffset(uint32_t offset);
+  const void* GetQuickResolutionTrampoline() const;
+  uint32_t GetQuickResolutionTrampolineOffset() const;
+  void SetQuickResolutionTrampolineOffset(uint32_t offset);
+  InstructionSet GetInstructionSet() const;
   uint32_t GetImageFileLocationOatChecksum() const;
   uint32_t GetImageFileLocationOatDataBegin() const;
   uint32_t GetImageFileLocationSize() const;
@@ -62,6 +74,10 @@
   InstructionSet instruction_set_;
   uint32_t dex_file_count_;
   uint32_t executable_offset_;
+  uint32_t interpreter_to_interpreter_entry_offset_;
+  uint32_t interpreter_to_quick_entry_offset_;
+  uint32_t portable_resolution_trampoline_offset_;
+  uint32_t quick_resolution_trampoline_offset_;
 
   uint32_t image_file_location_oat_checksum_;
   uint32_t image_file_location_oat_data_begin_;
diff --git a/src/oat/runtime/arm/oat_support_entrypoints_arm.cc b/src/oat/runtime/arm/oat_support_entrypoints_arm.cc
index 1a5fe47..2e9453c 100644
--- a/src/oat/runtime/arm/oat_support_entrypoints_arm.cc
+++ b/src/oat/runtime/arm/oat_support_entrypoints_arm.cc
@@ -91,12 +91,26 @@
 extern "C" uint64_t art_quick_shr_long(uint64_t, uint32_t);
 extern "C" uint64_t art_quick_ushr_long(uint64_t, uint32_t);
 
+// Interpreter entrypoints.
+extern "C" void artInterpreterToInterpreterEntry(Thread* self, MethodHelper& mh,
+                                                 const DexFile::CodeItem* code_item,
+                                                 ShadowFrame* shadow_frame, JValue* result);
+extern "C" void artInterpreterToQuickEntry(Thread* self, MethodHelper& mh,
+                                           const DexFile::CodeItem* code_item,
+                                           ShadowFrame* shadow_frame, JValue* result);
+
 // Intrinsic entrypoints.
 extern "C" int32_t __memcmp16(void*, void*, int32_t);
 extern "C" int32_t art_quick_indexof(void*, uint32_t, uint32_t, uint32_t);
 extern "C" int32_t art_quick_string_compareto(void*, void*);
 
 // Invoke entrypoints.
+extern "C" const void* artPortableResolutionTrampoline(mirror::AbstractMethod* called,
+                                                       mirror::Object* receiver,
+                                                       mirror::AbstractMethod** sp, Thread* thread);
+extern "C" const void* artQuickResolutionTrampoline(mirror::AbstractMethod* called,
+                                                    mirror::Object* receiver,
+                                                    mirror::AbstractMethod** sp, Thread* thread);
 extern "C" void art_quick_invoke_direct_trampoline_with_access_check(uint32_t, void*);
 extern "C" void art_quick_invoke_interface_trampoline(uint32_t, void*);
 extern "C" void art_quick_invoke_interface_trampoline_with_access_check(uint32_t, void*);
@@ -187,6 +201,10 @@
   points->pShrLong = art_quick_shr_long;
   points->pUshrLong = art_quick_ushr_long;
 
+  // Interpreter
+  points->pInterpreterToInterpreterEntry = artInterpreterToInterpreterEntry;
+  points->pInterpreterToQuickEntry = artInterpreterToQuickEntry;
+
   // Intrinsics
   points->pIndexOf = art_quick_indexof;
   points->pMemcmp16 = __memcmp16;
@@ -194,6 +212,8 @@
   points->pMemcpy = memcpy;
 
   // Invocation
+  points->pPortableResolutionTrampolineFromCode = artPortableResolutionTrampoline;
+  points->pQuickResolutionTrampolineFromCode = artQuickResolutionTrampoline;
   points->pInvokeDirectTrampolineWithAccessCheck = art_quick_invoke_direct_trampoline_with_access_check;
   points->pInvokeInterfaceTrampoline = art_quick_invoke_interface_trampoline;
   points->pInvokeInterfaceTrampolineWithAccessCheck = art_quick_invoke_interface_trampoline_with_access_check;
diff --git a/src/oat/runtime/arm/runtime_support_arm.S b/src/oat/runtime/arm/runtime_support_arm.S
index b8d2265..f19e8ba 100644
--- a/src/oat/runtime/arm/runtime_support_arm.S
+++ b/src/oat/runtime/arm/runtime_support_arm.S
@@ -246,48 +246,6 @@
 INVOKE_TRAMPOLINE art_quick_invoke_super_trampoline_with_access_check, artInvokeSuperTrampolineWithAccessCheck
 INVOKE_TRAMPOLINE art_quick_invoke_virtual_trampoline_with_access_check, artInvokeVirtualTrampolineWithAccessCheck
 
-     /*
-     * Portable resolution trampoline.
-     */
-     .extern artPortableResolutionTrampoline
-ENTRY art_portable_resolution_trampoline
-    push   {r0, r1, r2, r3, lr}           @ spill regs
-    .save  {r0, r1, r2, r3, lr}
-    .pad #20
-    .cfi_adjust_cfa_offset 20
-    sub    sp, #12                        @ pad stack pointer to align frame
-    .pad #12
-    .cfi_adjust_cfa_offset 12
-    mov    r3, r9                         @ pass Thread::Current
-    mov    r2, sp                         @ pass stack pointer
-    blx    artPortableResolutionTrampoline   @ (method, receiver, sp, Thread*)
-    mov    r12, r0                        @ save method code pointer result
-    add    sp, #12                        @ remove padding from stack pointer
-    .cfi_adjust_cfa_offset -12
-    pop    {r0, r1, r2, r3, lr}           @ restore regs
-    .cfi_adjust_cfa_offset -20
-    cmp    r12, #0                        @ is method code null?
-    bxne   r12                            @ if non-null, tail call to method's code
-    bx     lr                             @ otherwise, return to caller to handle exception
-END art_portable_resolution_trampoline
-
-    /*
-     * Quick resolution trampoline.
-     */
-     .extern artQuickResolutionTrampoline
-ENTRY art_quick_resolution_trampoline
-    SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME  @ save callee saves in case allocation triggers GC
-    mov    r3, r9                         @ pass Thread::Current
-    mov    r2, sp                         @ pass stack pointer
-    blx    artQuickResolutionTrampoline   @ (called method, receiver, sp, Thread*)
-    mov    r12, r0                        @ save method code pointer result
-    add    sp, #4                         @ set up stack pointer
-    .cfi_adjust_cfa_offset -4
-    pop    {r0-r3, r5-r8, r10-r11, lr}    @ 11 words, r0 will hold method*
-    .cfi_adjust_cfa_offset -44
-    bx     r12                            @ leaf call to method code
-END art_quick_resolution_trampoline
-
     /*
      * Portable invocation stub.
      * On entry:
diff --git a/src/oat/runtime/mips/oat_support_entrypoints_mips.cc b/src/oat/runtime/mips/oat_support_entrypoints_mips.cc
index eb82c42..8e06611 100644
--- a/src/oat/runtime/mips/oat_support_entrypoints_mips.cc
+++ b/src/oat/runtime/mips/oat_support_entrypoints_mips.cc
@@ -93,12 +93,26 @@
 extern "C" uint64_t art_quick_shr_long(uint64_t, uint32_t);
 extern "C" uint64_t art_quick_ushr_long(uint64_t, uint32_t);
 
+// Interpreter entrypoints.
+extern "C" void artInterpreterToInterpreterEntry(Thread* self, MethodHelper& mh,
+                                                 const DexFile::CodeItem* code_item,
+                                                 ShadowFrame* shadow_frame, JValue* result);
+extern "C" void artInterpreterToQuickEntry(Thread* self, MethodHelper& mh,
+                                           const DexFile::CodeItem* code_item,
+                                           ShadowFrame* shadow_frame, JValue* result);
+
 // Intrinsic entrypoints.
 extern "C" int32_t __memcmp16(void*, void*, int32_t);
 extern "C" int32_t art_quick_indexof(void*, uint32_t, uint32_t, uint32_t);
 extern "C" int32_t art_quick_string_compareto(void*, void*);
 
 // Invoke entrypoints.
+extern "C" const void* artPortableResolutionTrampoline(mirror::AbstractMethod* called,
+                                                       mirror::Object* receiver,
+                                                       mirror::AbstractMethod** sp, Thread* thread);
+extern "C" const void* artQuickResolutionTrampoline(mirror::AbstractMethod* called,
+                                                    mirror::Object* receiver,
+                                                    mirror::AbstractMethod** sp, Thread* thread);
 extern "C" void art_quick_invoke_direct_trampoline_with_access_check(uint32_t, void*);
 extern "C" void art_quick_invoke_interface_trampoline(uint32_t, void*);
 extern "C" void art_quick_invoke_interface_trampoline_with_access_check(uint32_t, void*);
@@ -188,6 +202,10 @@
   points->pShrLong = art_quick_shr_long;
   points->pUshrLong = art_quick_ushr_long;
 
+  // Interpreter
+  points->pInterpreterToInterpreterEntry = artInterpreterToInterpreterEntry;
+  points->pInterpreterToQuickEntry = artInterpreterToQuickEntry;
+
   // Intrinsics
   points->pIndexOf = art_quick_indexof;
   points->pMemcmp16 = __memcmp16;
@@ -195,6 +213,8 @@
   points->pMemcpy = memcpy;
 
   // Invocation
+  points->pPortableResolutionTrampolineFromCode = artPortableResolutionTrampoline;
+  points->pQuickResolutionTrampolineFromCode = artQuickResolutionTrampoline;
   points->pInvokeDirectTrampolineWithAccessCheck = art_quick_invoke_direct_trampoline_with_access_check;
   points->pInvokeInterfaceTrampoline = art_quick_invoke_interface_trampoline;
   points->pInvokeInterfaceTrampolineWithAccessCheck = art_quick_invoke_interface_trampoline_with_access_check;
diff --git a/src/oat/runtime/mips/runtime_support_mips.S b/src/oat/runtime/mips/runtime_support_mips.S
index 2eeb662..45d583e 100644
--- a/src/oat/runtime/mips/runtime_support_mips.S
+++ b/src/oat/runtime/mips/runtime_support_mips.S
@@ -413,71 +413,6 @@
 INVOKE_TRAMPOLINE art_quick_invoke_virtual_trampoline_with_access_check, artInvokeVirtualTrampolineWithAccessCheck
 
     /*
-     * Portable resolution trampoline.
-     */
-    .extern artPortableResolutionTrampoline
-ENTRY art_portable_resolution_trampoline
-    GENERATE_GLOBAL_POINTER
-    addiu $sp, $sp, -32          # leave room for $a0, $a1, $a2, $a3, and $ra
-    .cfi_adjust_cfa_offset 32
-    sw     $ra, 16($sp)
-    .cfi_rel_offset 31, 16
-    sw     $a3, 12($sp)
-    .cfi_rel_offset 7, 12
-    sw     $a2, 8($sp)
-    .cfi_rel_offset 6, 8
-    sw     $a1, 4($sp)
-    .cfi_rel_offset 5, 4
-    sw     $a0, 0($sp)
-    .cfi_rel_offset 4, 0
-    move  $a3, $s1              # pass Thread::Current()
-    jal   artPortableResolutionTrampoline  # (method, receiver, sp, Thread*)
-    move  $a2, $sp              # pass stack pointer
-    lw    $a0, 0($sp)           # restore registers from stack
-    lw    $a1, 4($sp)
-    lw    $a2, 8($sp)
-    lw    $a3, 12($sp)
-    lw    $ra, 16($sp)
-    beq   $v0, $zero, resolve_fail
-    addiu $sp, $sp, 32          # restore the stack
-    .cfi_adjust_cfa_offset -32
-    jr    $t9                   # leaf call to method's code
-    move  $t9, $v0              # put method code result in $t9
-resolve_fail:
-    jr    $ra
-    nop
-END art_portable_resolution_trampoline
-
-    /*
-     * Quick resolution trampoline.
-     */
-    .extern artQuickResolutionTrampoline
-ENTRY art_quick_resolution_trampoline
-    GENERATE_GLOBAL_POINTER
-    SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
-    move  $a3, $s1              # pass Thread::Current()
-    jal   artQuickResolutionTrampoline  # (method, receiver, sp, Thread*)
-    move  $a2, $sp              # pass stack pointer
-    move  $t9, $v0              # put method code result in $t9
-    lw    $a0, 0($sp)           # restore registers from stack
-    lw    $a1, 4($sp)
-    lw    $a2, 8($sp)
-    lw    $a3, 12($sp)
-    lw    $s2, 28($sp)
-    lw    $s3, 32($sp)
-    lw    $s4, 36($sp)
-    lw    $s5, 40($sp)
-    lw    $s6, 44($sp)
-    lw    $s7, 48($sp)
-    lw    $gp, 52($sp)
-    lw    $fp, 56($sp)
-    lw    $ra, 60($sp)
-    jr    $t9                   # leaf call to method's code
-    addiu $sp, $sp, 64          # restore the stack
-    .cfi_adjust_cfa_offset -64
-END art_quick_resolution_trampoline
-
-    /*
      * Common invocation stub for portable and quick.
      * On entry:
      *   a0 = method pointer
diff --git a/src/oat/runtime/oat_support_entrypoints.h b/src/oat/runtime/oat_support_entrypoints.h
index 75c9378..c1a2587 100644
--- a/src/oat/runtime/oat_support_entrypoints.h
+++ b/src/oat/runtime/oat_support_entrypoints.h
@@ -17,6 +17,7 @@
 #ifndef ART_SRC_OAT_RUNTIME_OAT_SUPPORT_ENTRYPOINTS_H_
 #define ART_SRC_OAT_RUNTIME_OAT_SUPPORT_ENTRYPOINTS_H_
 
+#include "dex_file-inl.h"
 #include "runtime.h"
 
 #define ENTRYPOINT_OFFSET(x) \
@@ -30,6 +31,8 @@
 class Object;
 }  // namespace mirror
 class DvmDex;
+class MethodHelper;
+class ShadowFrame;
 class Thread;
 
 struct PACKED(4) EntryPoints {
@@ -104,6 +107,14 @@
   uint64_t (*pShrLong)(uint64_t, uint32_t);
   uint64_t (*pUshrLong)(uint64_t, uint32_t);
 
+  // Interpreter
+  void (*pInterpreterToInterpreterEntry)(Thread* self, MethodHelper& mh,
+                                         const DexFile::CodeItem* code_item,
+                                         ShadowFrame* shadow_frame, JValue* result);
+  void (*pInterpreterToQuickEntry)(Thread* self, MethodHelper& mh,
+                                   const DexFile::CodeItem* code_item,
+                                   ShadowFrame* shadow_frame, JValue* result);
+
   // Intrinsics
   int32_t (*pIndexOf)(void*, uint32_t, uint32_t, uint32_t);
   int32_t (*pMemcmp16)(void*, void*, int32_t);
@@ -111,6 +122,10 @@
   void* (*pMemcpy)(void*, const void*, size_t);
 
   // Invocation
+  const void* (*pPortableResolutionTrampolineFromCode)(mirror::AbstractMethod*, mirror::Object*,
+                                                       mirror::AbstractMethod**, Thread*);
+  const void* (*pQuickResolutionTrampolineFromCode)(mirror::AbstractMethod*, mirror::Object*,
+                                                    mirror::AbstractMethod**, Thread*);
   void (*pInvokeDirectTrampolineWithAccessCheck)(uint32_t, void*);
   void (*pInvokeInterfaceTrampoline)(uint32_t, void*);
   void (*pInvokeInterfaceTrampolineWithAccessCheck)(uint32_t, void*);
diff --git a/src/oat/runtime/support_interpreter.cc b/src/oat/runtime/support_interpreter.cc
index 7060a41..55be54f 100644
--- a/src/oat/runtime/support_interpreter.cc
+++ b/src/oat/runtime/support_interpreter.cc
@@ -110,8 +110,9 @@
   return result.GetJ();
 }
 
-void artInterpreterToQuickEntry(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item,
-                                ShadowFrame* shadow_frame, JValue* result)
+extern "C" void artInterpreterToQuickEntry(Thread* self, MethodHelper& mh,
+                                           const DexFile::CodeItem* code_item,
+                                           ShadowFrame* shadow_frame, JValue* result)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   mirror::AbstractMethod* method = shadow_frame->GetMethod();
   uint16_t arg_offset = (code_item == NULL) ? 0 : code_item->registers_size_ - code_item->ins_size_;
diff --git a/src/oat/runtime/support_stubs.cc b/src/oat/runtime/support_stubs.cc
index 97d5f66..71b67d0 100644
--- a/src/oat/runtime/support_stubs.cc
+++ b/src/oat/runtime/support_stubs.cc
@@ -144,7 +144,7 @@
     // Expect class to at least be initializing.
     DCHECK(called->GetDeclaringClass()->IsInitializing());
     // Don't want infinite recursion.
-    DCHECK(code != GetResolutionTrampoline());
+    DCHECK(code != GetResolutionTrampoline(linker));
     // Set up entry into main method
     *called_addr = called;
   }
@@ -393,7 +393,7 @@
     // Expect class to at least be initializing.
     DCHECK(called->GetDeclaringClass()->IsInitializing());
     // Don't want infinite recursion.
-    DCHECK(code != GetResolutionTrampoline());
+    DCHECK(code != GetResolutionTrampoline(linker));
     // Set up entry into main method
     regs[0] = reinterpret_cast<uintptr_t>(called);
   }
diff --git a/src/oat/runtime/x86/oat_support_entrypoints_x86.cc b/src/oat/runtime/x86/oat_support_entrypoints_x86.cc
index 357bbe0..a90a583 100644
--- a/src/oat/runtime/x86/oat_support_entrypoints_x86.cc
+++ b/src/oat/runtime/x86/oat_support_entrypoints_x86.cc
@@ -75,6 +75,14 @@
 extern "C" uint64_t art_quick_lshr_from_code(uint64_t, uint32_t);
 extern "C" uint64_t art_quick_lushr_from_code(uint64_t, uint32_t);
 
+// Interpreter entrypoints.
+extern "C" void artInterpreterToInterpreterEntry(Thread* self, MethodHelper& mh,
+                                                 const DexFile::CodeItem* code_item,
+                                                 ShadowFrame* shadow_frame, JValue* result);
+extern "C" void artInterpreterToQuickEntry(Thread* self, MethodHelper& mh,
+                                           const DexFile::CodeItem* code_item,
+                                           ShadowFrame* shadow_frame, JValue* result);
+
 // Intrinsic entrypoints.
 extern "C" int32_t art_quick_memcmp16(void*, void*, int32_t);
 extern "C" int32_t art_quick_indexof(void*, uint32_t, uint32_t, uint32_t);
@@ -82,6 +90,12 @@
 extern "C" void* art_quick_memcpy(void*, const void*, size_t);
 
 // Invoke entrypoints.
+extern "C" const void* artPortableResolutionTrampoline(mirror::AbstractMethod* called,
+                                                       mirror::Object* receiver,
+                                                       mirror::AbstractMethod** sp, Thread* thread);
+extern "C" const void* artQuickResolutionTrampoline(mirror::AbstractMethod* called,
+                                                    mirror::Object* receiver,
+                                                    mirror::AbstractMethod** sp, Thread* thread);
 extern "C" void art_quick_invoke_direct_trampoline_with_access_check(uint32_t, void*);
 extern "C" void art_quick_invoke_interface_trampoline(uint32_t, void*);
 extern "C" void art_quick_invoke_interface_trampoline_with_access_check(uint32_t, void*);
@@ -171,6 +185,10 @@
   points->pShrLong = art_quick_lshr_from_code;
   points->pUshrLong = art_quick_lushr_from_code;
 
+  // Interpreter
+  points->pInterpreterToInterpreterEntry = artInterpreterToInterpreterEntry;
+  points->pInterpreterToQuickEntry = artInterpreterToQuickEntry;
+
   // Intrinsics
   points->pIndexOf = art_quick_indexof;
   points->pMemcmp16 = art_quick_memcmp16;
@@ -178,6 +196,8 @@
   points->pMemcpy = art_quick_memcpy;
 
   // Invocation
+  points->pPortableResolutionTrampolineFromCode = artPortableResolutionTrampoline;
+  points->pQuickResolutionTrampolineFromCode = artQuickResolutionTrampoline;
   points->pInvokeDirectTrampolineWithAccessCheck = art_quick_invoke_direct_trampoline_with_access_check;
   points->pInvokeInterfaceTrampoline = art_quick_invoke_interface_trampoline;
   points->pInvokeInterfaceTrampolineWithAccessCheck = art_quick_invoke_interface_trampoline_with_access_check;
diff --git a/src/oat/runtime/x86/runtime_support_x86.S b/src/oat/runtime/x86/runtime_support_x86.S
index 734aad1..ee6db0c 100644
--- a/src/oat/runtime/x86/runtime_support_x86.S
+++ b/src/oat/runtime/x86/runtime_support_x86.S
@@ -301,56 +301,6 @@
 INVOKE_TRAMPOLINE art_quick_invoke_super_trampoline_with_access_check, artInvokeSuperTrampolineWithAccessCheck
 INVOKE_TRAMPOLINE art_quick_invoke_virtual_trampoline_with_access_check, artInvokeVirtualTrampolineWithAccessCheck
 
-     /*
-     * Portable resolution trampoline.
-     */
-DEFINE_FUNCTION art_portable_resolution_trampoline
-    PUSH ebp                      // stash %ebp
-    movl %esp, %ebp               // save %esp
-    .cfi_def_cfa_register ebp
-    subl LITERAL(8), %esp         // align stack
-    movl 8(%ebp), %eax            // load the called method* into %eax
-    leal 8(%ebp), %edx            // put the called method* address in %edx
-    pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
-    pushl %edx                    // pass called method* address
-    pushl 12(%ebp)                // pass receiver
-    pushl 8(%ebp)                 // pass method*
-    call SYMBOL(artPortableResolutionTrampoline)  // (method, receiver, sp, Thread*)
-    leave                         // restore the stack and %ebp
-    .cfi_def_cfa esp, 4
-    .cfi_restore ebp
-    cmpl LITERAL(0), %eax         // check if returned method code is null
-    je resolve_fail               // if null, jump to return to handle
-    jmp *%eax                     // otherwise, tail call to intended method
-resolve_fail:
-    ret
-END_FUNCTION art_portable_resolution_trampoline
-
-    /*
-     * Quick resolution trampoline.
-     */
-DEFINE_FUNCTION art_quick_resolution_trampoline
-    SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
-    movl %esp, %edx               // save stack pointer
-    pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
-    .cfi_adjust_cfa_offset 4
-    PUSH edx                      // pass stack pointer
-    PUSH ecx                      // pass receiver
-    PUSH eax                      // pass method*
-    call SYMBOL(artQuickResolutionTrampoline)  // (method_idx, sp, Thread*)
-    movl %eax, %edi               // save returned code pointer in %edi
-    addl LITERAL(16), %esp        // pop arguments
-    .cfi_adjust_cfa_offset -16
-    POP eax                       // restore registers
-    POP ecx
-    POP edx
-    POP ebx
-    POP ebp
-    POP esi
-    xchgl %edi, (%esp)            // swap %edi and code pointer
-    ret                           // tail call to intended method
-END_FUNCTION art_quick_resolution_trampoline
-
     /*
      * Portable invocation stub.
      * On entry:
diff --git a/src/oat_test.cc b/src/oat_test.cc
index dd336d9..c7c063a 100644
--- a/src/oat_test.cc
+++ b/src/oat_test.cc
@@ -68,16 +68,16 @@
   const bool compile = false;  // DISABLED_ due to the time to compile libcore
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
 
+  // TODO: make selectable
+#if defined(ART_USE_PORTABLE_COMPILER)
+  CompilerBackend compiler_backend = kPortable;
+#else
+  CompilerBackend compiler_backend = kQuick;
+#endif
+  compiler_driver_.reset(new CompilerDriver(compiler_backend, kThumb2, false, 2, false,
+                                            NULL, true, true));
   jobject class_loader = NULL;
   if (compile) {
-    // TODO: make selectable
-#if defined(ART_USE_PORTABLE_COMPILER)
-    CompilerBackend compiler_backend = kPortable;
-#else
-    CompilerBackend compiler_backend = kQuick;
-#endif
-    compiler_driver_.reset(new CompilerDriver(compiler_backend, kThumb2, false, 2, false,
-                                              NULL, true, true));
     compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath());
   }
 
@@ -143,7 +143,7 @@
 TEST_F(OatTest, OatHeaderSizeCheck) {
   // If this test is failing and you have to update these constants,
   // it is time to update OatHeader::kOatVersion
-  EXPECT_EQ(36U, sizeof(OatHeader));
+  EXPECT_EQ(52U, sizeof(OatHeader));
   EXPECT_EQ(28U, sizeof(OatMethodOffsets));
 }
 
diff --git a/src/oat_writer.cc b/src/oat_writer.cc
index 8acbfe9..a4bd87d 100644
--- a/src/oat_writer.cc
+++ b/src/oat_writer.cc
@@ -54,13 +54,35 @@
                      uint32_t image_file_location_oat_begin,
                      const std::string& image_file_location,
                      const CompilerDriver* compiler)
-    : compiler_driver_(compiler) {
-  image_file_location_oat_checksum_ = image_file_location_oat_checksum;
-  image_file_location_oat_begin_ = image_file_location_oat_begin;
-  image_file_location_ = image_file_location;
-  dex_files_ = &dex_files;
-  oat_header_ = NULL;
-  executable_offset_padding_length_ = 0;
+  : compiler_driver_(compiler),
+    dex_files_(&dex_files),
+    image_file_location_oat_checksum_(image_file_location_oat_checksum),
+    image_file_location_oat_begin_(image_file_location_oat_begin),
+    image_file_location_(image_file_location),
+    oat_header_(NULL),
+    size_dex_file_alignment_(0),
+    size_executable_offset_alignment_(0),
+    size_oat_header_(0),
+    size_oat_header_image_file_location_(0),
+    size_dex_file_(0),
+    size_interpreter_to_interpreter_entry_(0),
+    size_interpreter_to_quick_entry_(0),
+    size_portable_resolution_trampoline_(0),
+    size_quick_resolution_trampoline_(0),
+    size_stubs_alignment_(0),
+    size_code_size_(0),
+    size_code_(0),
+    size_code_alignment_(0),
+    size_mapping_table_(0),
+    size_vmap_table_(0),
+    size_gc_map_(0),
+    size_oat_dex_file_location_size_(0),
+    size_oat_dex_file_location_data_(0),
+    size_oat_dex_file_location_checksum_(0),
+    size_oat_dex_file_offset_(0),
+    size_oat_dex_file_methods_offsets_(0),
+    size_oat_class_status_(0),
+    size_oat_class_method_offsets_(0) {
 
   size_t offset = InitOatHeader();
   offset = InitOatDexFiles(offset);
@@ -70,6 +92,7 @@
   offset = InitOatCodeDexFiles(offset);
 
   CHECK_EQ(dex_files_->size(), oat_dex_files_.size());
+  CHECK(image_file_location.empty() == compiler->IsImage());
 }
 
 OatWriter::~OatWriter() {
@@ -106,7 +129,9 @@
   // calculate the offsets within OatDexFiles to the DexFiles
   for (size_t i = 0; i != dex_files_->size(); ++i) {
     // dex files are required to be 4 byte aligned
+    size_t original_offset = offset;
     offset = RoundUp(offset, 4);
+    size_dex_file_alignment_ += offset - original_offset;
 
     // set offset in OatDexFile to DexFile
     oat_dex_files_[i]->dex_file_offset_ = offset;
@@ -162,7 +187,33 @@
   // required to be on a new page boundary
   offset = RoundUp(offset, kPageSize);
   oat_header_->SetExecutableOffset(offset);
-  executable_offset_padding_length_ = offset - old_offset;
+  size_executable_offset_alignment_ = offset - old_offset;
+  if (compiler_driver_->IsImage()) {
+    InstructionSet instruction_set = compiler_driver_->GetInstructionSet();
+    oat_header_->SetInterpreterToInterpreterEntryOffset(offset);
+    interpreter_to_interpreter_entry_.reset(compiler_driver_->CreateInterpreterToInterpreterEntry());
+    offset += interpreter_to_interpreter_entry_->size();
+
+    offset = CompiledCode::AlignCode(offset, instruction_set);
+    oat_header_->SetInterpreterToQuickEntryOffset(offset);
+    interpreter_to_quick_entry_.reset(compiler_driver_->CreateInterpreterToQuickEntry());
+    offset += interpreter_to_quick_entry_->size();
+
+    offset = CompiledCode::AlignCode(offset, instruction_set);
+    oat_header_->SetPortableResolutionTrampolineOffset(offset);
+    portable_resolution_trampoline_.reset(compiler_driver_->CreatePortableResolutionTrampoline());
+    offset += portable_resolution_trampoline_->size();
+
+    offset = CompiledCode::AlignCode(offset, instruction_set);
+    oat_header_->SetQuickResolutionTrampolineOffset(offset);
+    quick_resolution_trampoline_.reset(compiler_driver_->CreateQuickResolutionTrampoline());
+    offset += quick_resolution_trampoline_->size();
+  } else {
+    oat_header_->SetInterpreterToInterpreterEntryOffset(0);
+    oat_header_->SetInterpreterToQuickEntryOffset(0);
+    oat_header_->SetPortableResolutionTrampolineOffset(0);
+    oat_header_->SetQuickResolutionTrampolineOffset(0);
+  }
   return offset;
 }
 
@@ -389,11 +440,13 @@
     PLOG(ERROR) << "Failed to write oat header to " << out.GetLocation();
     return false;
   }
+  size_oat_header_ += sizeof(*oat_header_);
 
   if (!out.WriteFully(image_file_location_.data(), image_file_location_.size())) {
     PLOG(ERROR) << "Failed to write oat header image file location to " << out.GetLocation();
     return false;
   }
+  size_oat_header_image_file_location_ += image_file_location_.size();
 
   if (!WriteTables(out)) {
     LOG(ERROR) << "Failed to write oat tables to " << out.GetLocation();
@@ -412,12 +465,64 @@
     return false;
   }
 
+  LOG(INFO) << "size_dex_file_alignment_=" << size_dex_file_alignment_;
+  LOG(INFO) << "size_executable_offset_alignment_=" << size_executable_offset_alignment_;
+  LOG(INFO) << "size_oat_header_=" << size_oat_header_;
+  LOG(INFO) << "size_oat_header_image_file_location_=" << size_oat_header_image_file_location_;
+  LOG(INFO) << "size_dex_file_=" << size_dex_file_;
+  LOG(INFO) << "size_interpreter_to_interpreter_entry_=" << size_interpreter_to_interpreter_entry_;
+  LOG(INFO) << "size_interpreter_to_quick_entry_=" << size_interpreter_to_quick_entry_;
+  LOG(INFO) << "size_portable_resolution_trampoline_=" << size_portable_resolution_trampoline_;
+  LOG(INFO) << "size_quick_resolution_trampoline_=" << size_quick_resolution_trampoline_;
+  LOG(INFO) << "size_stubs_alignment_=" << size_stubs_alignment_;
+  LOG(INFO) << "size_code_size_=" << size_code_size_;
+  LOG(INFO) << "size_code_=" << size_code_;
+  LOG(INFO) << "size_code_alignment_=" << size_code_alignment_;
+  LOG(INFO) << "size_mapping_table_=" << size_mapping_table_;
+  LOG(INFO) << "size_vmap_table_=" << size_vmap_table_;
+  LOG(INFO) << "size_gc_map_=" << size_gc_map_;
+  LOG(INFO) << "size_oat_dex_file_location_size_=" << size_oat_dex_file_location_size_;
+  LOG(INFO) << "size_oat_dex_file_location_data=" << size_oat_dex_file_location_data_;
+  LOG(INFO) << "size_oat_dex_file_location_checksum_=" << size_oat_dex_file_location_checksum_;
+  LOG(INFO) << "size_oat_dex_file_offset_=" << size_oat_dex_file_offset_;
+  LOG(INFO) << "size_oat_dex_file_methods_offsets_=" << size_oat_dex_file_methods_offsets_;
+  LOG(INFO) << "size_oat_class_status_=" << size_oat_class_status_;
+  LOG(INFO) << "size_oat_class_method_offsets=" << size_oat_class_method_offsets_;
+
+  uint32_t size_total =
+      size_dex_file_alignment_ +
+      size_executable_offset_alignment_ +
+      size_oat_header_ +
+      size_oat_header_image_file_location_ +
+      size_dex_file_ +
+      size_interpreter_to_interpreter_entry_ +
+      size_interpreter_to_quick_entry_ +
+      size_portable_resolution_trampoline_ +
+      size_quick_resolution_trampoline_ +
+      size_stubs_alignment_ +
+      size_code_size_ +
+      size_code_ +
+      size_code_alignment_ +
+      size_mapping_table_ +
+      size_vmap_table_ +
+      size_gc_map_ +
+      size_oat_dex_file_location_size_ +
+      size_oat_dex_file_location_data_ +
+      size_oat_dex_file_location_checksum_ +
+      size_oat_dex_file_offset_ +
+      size_oat_dex_file_methods_offsets_ +
+      size_oat_class_status_ +
+      size_oat_class_method_offsets_;
+
+  LOG(INFO) << "size_total=" << size_total;
+  DCHECK_EQ(size_total, static_cast<uint32_t>(out.Seek(0, kSeekCurrent)));
+
   return true;
 }
 
 bool OatWriter::WriteTables(OutputStream& out) {
   for (size_t i = 0; i != oat_dex_files_.size(); ++i) {
-    if (!oat_dex_files_[i]->Write(out)) {
+    if (!oat_dex_files_[i]->Write(this, out)) {
       PLOG(ERROR) << "Failed to write oat dex information to " << out.GetLocation();
       return false;
     }
@@ -436,9 +541,10 @@
       PLOG(ERROR) << "Failed to write dex file " << dex_file->GetLocation() << " to " << out.GetLocation();
       return false;
     }
+    size_dex_file_ += dex_file->GetHeader().file_size_;
   }
   for (size_t i = 0; i != oat_classes_.size(); ++i) {
-    if (!oat_classes_[i]->Write(out)) {
+    if (!oat_classes_[i]->Write(this, out)) {
       PLOG(ERROR) << "Failed to write oat methods information to " << out.GetLocation();
       return false;
     }
@@ -448,13 +554,59 @@
 
 size_t OatWriter::WriteCode(OutputStream& out) {
   uint32_t offset = oat_header_->GetExecutableOffset();
-  off_t new_offset = out.Seek(executable_offset_padding_length_, kSeekCurrent);
+  off_t new_offset = out.Seek(size_executable_offset_alignment_, kSeekCurrent);
   if (static_cast<uint32_t>(new_offset) != offset) {
     PLOG(ERROR) << "Failed to seek to oat code section. Actual: " << new_offset
                 << " Expected: " << offset << " File: " << out.GetLocation();
     return 0;
   }
   DCHECK_OFFSET();
+  if (compiler_driver_->IsImage()) {
+    InstructionSet instruction_set = compiler_driver_->GetInstructionSet();
+    if (!out.WriteFully(&(*interpreter_to_interpreter_entry_)[0], interpreter_to_interpreter_entry_->size())) {
+      PLOG(ERROR) << "Failed to write interpreter to interpreter entry to " << out.GetLocation();
+      return false;
+    }
+    size_interpreter_to_interpreter_entry_ += interpreter_to_interpreter_entry_->size();
+    offset += interpreter_to_interpreter_entry_->size();
+    DCHECK_OFFSET();
+
+    uint32_t aligned_offset = CompiledCode::AlignCode(offset, instruction_set);
+    uint32_t alignment_padding = aligned_offset - offset;
+    out.Seek(alignment_padding, kSeekCurrent);
+    size_stubs_alignment_ += alignment_padding;
+    if (!out.WriteFully(&(*interpreter_to_quick_entry_)[0], interpreter_to_quick_entry_->size())) {
+      PLOG(ERROR) << "Failed to write interpreter to quick entry to " << out.GetLocation();
+      return false;
+    }
+    size_interpreter_to_quick_entry_ += interpreter_to_quick_entry_->size();
+    offset += alignment_padding + interpreter_to_quick_entry_->size();
+    DCHECK_OFFSET();
+
+    aligned_offset = CompiledCode::AlignCode(offset, instruction_set);
+    alignment_padding = aligned_offset - offset;
+    out.Seek(alignment_padding, kSeekCurrent);
+    size_stubs_alignment_ += alignment_padding;
+    if (!out.WriteFully(&(*portable_resolution_trampoline_)[0], portable_resolution_trampoline_->size())) {
+      PLOG(ERROR) << "Failed to write portable resolution trampoline to " << out.GetLocation();
+      return false;
+    }
+    size_portable_resolution_trampoline_ += portable_resolution_trampoline_->size();
+    offset += alignment_padding + portable_resolution_trampoline_->size();
+    DCHECK_OFFSET();
+
+    aligned_offset = CompiledCode::AlignCode(offset, instruction_set);
+    alignment_padding = aligned_offset - offset;
+    out.Seek(alignment_padding, kSeekCurrent);
+    size_stubs_alignment_ += alignment_padding;
+    if (!out.WriteFully(&(*quick_resolution_trampoline_)[0], quick_resolution_trampoline_->size())) {
+      PLOG(ERROR) << "Failed to write quick resolution trampoline to " << out.GetLocation();
+      return false;
+    }
+    size_quick_resolution_trampoline_ += quick_resolution_trampoline_->size();
+    offset += alignment_padding + quick_resolution_trampoline_->size();
+    DCHECK_OFFSET();
+  }
   return offset;
 }
 
@@ -547,6 +699,7 @@
     uint32_t aligned_code_delta = aligned_offset - offset;
     if (aligned_code_delta != 0) {
       off_t new_offset = out.Seek(aligned_code_delta, kSeekCurrent);
+      size_code_alignment_ += aligned_code_delta;
       if (static_cast<uint32_t>(new_offset) != aligned_offset) {
         PLOG(ERROR) << "Failed to seek to align oat code. Actual: " << new_offset
                     << " Expected: " << aligned_offset << " File: " << out.GetLocation();
@@ -572,12 +725,14 @@
         ReportWriteFailure("method code size", method_idx, dex_file, out);
         return 0;
       }
+      size_code_size_ += sizeof(code_size);
       offset += sizeof(code_size);
       DCHECK_OFFSET();
       if (!out.WriteFully(&code[0], code_size)) {
         ReportWriteFailure("method code", method_idx, dex_file, out);
         return 0;
       }
+      size_code_ += code_size;
       offset += code_size;
     }
     DCHECK_OFFSET();
@@ -602,6 +757,7 @@
         ReportWriteFailure("mapping table", method_idx, dex_file, out);
         return 0;
       }
+      size_mapping_table_ += mapping_table_size;
       offset += mapping_table_size;
     }
     DCHECK_OFFSET();
@@ -625,6 +781,7 @@
         ReportWriteFailure("vmap table", method_idx, dex_file, out);
         return 0;
       }
+      size_vmap_table_ += vmap_table_size;
       offset += vmap_table_size;
     }
     DCHECK_OFFSET();
@@ -648,6 +805,7 @@
         ReportWriteFailure("GC map", method_idx, dex_file, out);
         return 0;
       }
+      size_gc_map_ += gc_map_size;
       offset += gc_map_size;
     }
     DCHECK_OFFSET();
@@ -683,29 +841,35 @@
                             sizeof(methods_offsets_[0]) * methods_offsets_.size());
 }
 
-bool OatWriter::OatDexFile::Write(OutputStream& out) const {
+bool OatWriter::OatDexFile::Write(OatWriter* oat_writer, OutputStream& out) const {
   DCHECK_OFFSET_();
   if (!out.WriteFully(&dex_file_location_size_, sizeof(dex_file_location_size_))) {
     PLOG(ERROR) << "Failed to write dex file location length to " << out.GetLocation();
     return false;
   }
+  oat_writer->size_oat_dex_file_location_size_ += sizeof(dex_file_location_size_);
   if (!out.WriteFully(dex_file_location_data_, dex_file_location_size_)) {
     PLOG(ERROR) << "Failed to write dex file location data to " << out.GetLocation();
     return false;
   }
+  oat_writer->size_oat_dex_file_location_data_ += dex_file_location_size_;
   if (!out.WriteFully(&dex_file_location_checksum_, sizeof(dex_file_location_checksum_))) {
     PLOG(ERROR) << "Failed to write dex file location checksum to " << out.GetLocation();
     return false;
   }
+  oat_writer->size_oat_dex_file_location_checksum_ += sizeof(dex_file_location_checksum_);
   if (!out.WriteFully(&dex_file_offset_, sizeof(dex_file_offset_))) {
     PLOG(ERROR) << "Failed to write dex file offset to " << out.GetLocation();
     return false;
   }
+  oat_writer->size_oat_dex_file_offset_ += sizeof(dex_file_offset_);
   if (!out.WriteFully(&methods_offsets_[0],
                       sizeof(methods_offsets_[0]) * methods_offsets_.size())) {
     PLOG(ERROR) << "Failed to write methods offsets to " << out.GetLocation();
     return false;
   }
+  oat_writer->size_oat_dex_file_methods_offsets_ +=
+      sizeof(methods_offsets_[0]) * methods_offsets_.size();
   return true;
 }
 
@@ -736,12 +900,13 @@
                             sizeof(method_offsets_[0]) * method_offsets_.size());
 }
 
-bool OatWriter::OatClass::Write(OutputStream& out) const {
+bool OatWriter::OatClass::Write(OatWriter* oat_writer, OutputStream& out) const {
   DCHECK_OFFSET_();
   if (!out.WriteFully(&status_, sizeof(status_))) {
     PLOG(ERROR) << "Failed to write class status to " << out.GetLocation();
     return false;
   }
+  oat_writer->size_oat_class_status_ += sizeof(status_);
   DCHECK_EQ(static_cast<off_t>(GetOatMethodOffsetsOffsetFromOatHeader(0)),
             out.Seek(0, kSeekCurrent));
   if (!out.WriteFully(&method_offsets_[0],
@@ -749,6 +914,7 @@
     PLOG(ERROR) << "Failed to write method offsets to " << out.GetLocation();
     return false;
   }
+  oat_writer->size_oat_class_method_offsets_ += sizeof(method_offsets_[0]) * method_offsets_.size();
   DCHECK_EQ(static_cast<off_t>(GetOatMethodOffsetsOffsetFromOatHeader(method_offsets_.size())),
             out.Seek(0, kSeekCurrent));
   return true;
diff --git a/src/oat_writer.h b/src/oat_writer.h
index e1d76f4..b201d6b 100644
--- a/src/oat_writer.h
+++ b/src/oat_writer.h
@@ -83,7 +83,8 @@
   size_t InitOatDexFiles(size_t offset);
   size_t InitDexFiles(size_t offset);
   size_t InitOatClasses(size_t offset);
-  size_t InitOatCode(size_t offset);
+  size_t InitOatCode(size_t offset)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   size_t InitOatCodeDexFiles(size_t offset)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   size_t InitOatCodeDexFile(size_t offset,
@@ -120,7 +121,7 @@
     explicit OatDexFile(size_t offset, const DexFile& dex_file);
     size_t SizeOf() const;
     void UpdateChecksum(OatHeader& oat_header) const;
-    bool Write(OutputStream& out) const;
+    bool Write(OatWriter* oat_writer, OutputStream& out) const;
 
     // Offset of start of OatDexFile from beginning of OatHeader. It is
     // used to validate file position when writing.
@@ -144,7 +145,7 @@
     size_t GetOatMethodOffsetsOffsetFromOatClass(size_t class_def_method_index_) const;
     size_t SizeOf() const;
     void UpdateChecksum(OatHeader& oat_header) const;
-    bool Write(OutputStream& out) const;
+    bool Write(OatWriter* oat_writer, OutputStream& out) const;
 
     // Offset of start of OatClass from beginning of OatHeader. It is
     // used to validate file position when writing. For Portable, it
@@ -175,7 +176,35 @@
   OatHeader* oat_header_;
   std::vector<OatDexFile*> oat_dex_files_;
   std::vector<OatClass*> oat_classes_;
-  uint32_t executable_offset_padding_length_;
+  UniquePtr<const std::vector<uint8_t> > interpreter_to_interpreter_entry_;
+  UniquePtr<const std::vector<uint8_t> > interpreter_to_quick_entry_;
+  UniquePtr<const std::vector<uint8_t> > portable_resolution_trampoline_;
+  UniquePtr<const std::vector<uint8_t> > quick_resolution_trampoline_;
+
+  // output stats
+  uint32_t size_dex_file_alignment_;
+  uint32_t size_executable_offset_alignment_;
+  uint32_t size_oat_header_;
+  uint32_t size_oat_header_image_file_location_;
+  uint32_t size_dex_file_;
+  uint32_t size_interpreter_to_interpreter_entry_;
+  uint32_t size_interpreter_to_quick_entry_;
+  uint32_t size_portable_resolution_trampoline_;
+  uint32_t size_quick_resolution_trampoline_;
+  uint32_t size_stubs_alignment_;
+  uint32_t size_code_size_;
+  uint32_t size_code_;
+  uint32_t size_code_alignment_;
+  uint32_t size_mapping_table_;
+  uint32_t size_vmap_table_;
+  uint32_t size_gc_map_;
+  uint32_t size_oat_dex_file_location_size_;
+  uint32_t size_oat_dex_file_location_data_;
+  uint32_t size_oat_dex_file_location_checksum_;
+  uint32_t size_oat_dex_file_offset_;
+  uint32_t size_oat_dex_file_methods_offsets_;
+  uint32_t size_oat_class_status_;
+  uint32_t size_oat_class_method_offsets_;
 
   template <class T> struct MapCompare {
    public:
diff --git a/src/oatdump.cc b/src/oatdump.cc
index 7a99f8d..538e1bb 100644
--- a/src/oatdump.cc
+++ b/src/oatdump.cc
@@ -880,7 +880,7 @@
   const void* GetOatCodeBegin(mirror::AbstractMethod* m)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     const void* code = m->GetEntryPointFromCompiledCode();
-    if (code == GetResolutionTrampoline()) {
+    if (code == GetResolutionTrampoline(Runtime::Current()->GetClassLinker())) {
       code = oat_dumper_->GetOatCode(m);
     }
     if (oat_dumper_->GetInstructionSet() == kThumb2) {
diff --git a/src/runtime.cc b/src/runtime.cc
index 45d2988..c21a1c4 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -1109,7 +1109,9 @@
   // TODO: use a special method for resolution method saves
   method->SetDexMethodIndex(DexFile::kDexNoIndex16);
   // When compiling, the code pointer will get set later when the image is loaded.
-  method->SetEntryPointFromCompiledCode(Runtime::Current()->IsCompiler() ? NULL : GetResolutionTrampoline());
+  Runtime* r = Runtime::Current();
+  ClassLinker* cl = r->GetClassLinker();
+  method->SetEntryPointFromCompiledCode(r->IsCompiler() ? NULL : GetResolutionTrampoline(cl));
   return method.get();
 }
 
diff --git a/src/runtime_support.h b/src/runtime_support.h
index 5fc8da5..094e23a 100644
--- a/src/runtime_support.h
+++ b/src/runtime_support.h
@@ -34,14 +34,12 @@
 extern "C" void art_jni_dlsym_lookup_stub();
 extern "C" void art_portable_abstract_method_error_stub();
 extern "C" void art_portable_proxy_invoke_handler();
-extern "C" void art_portable_resolution_trampoline();
 extern "C" void art_quick_abstract_method_error_stub();
 extern "C" void art_quick_deoptimize();
 extern "C" void art_quick_instrumentation_entry_from_code(void*);
 extern "C" void art_quick_instrumentation_exit_from_code();
 extern "C" void art_quick_interpreter_entry(void*);
 extern "C" void art_quick_proxy_invoke_handler();
-extern "C" void art_quick_resolution_trampoline();
 extern "C" void art_work_around_app_jni_bugs();
 
 extern "C" double art_l2d(int64_t l);
@@ -373,22 +371,20 @@
   return reinterpret_cast<void*>(art_quick_interpreter_entry);
 }
 
-// Return address of portable resolution trampoline stub.
-static inline void* GetPortableResolutionTrampoline() {
-  return reinterpret_cast<void*>(art_portable_resolution_trampoline);
+static inline const void* GetPortableResolutionTrampoline(ClassLinker* class_linker) {
+  return class_linker->GetPortableResolutionTrampoline();
 }
 
-// Return address of quick resolution trampoline stub.
-static inline void* GetQuickResolutionTrampoline() {
-  return reinterpret_cast<void*>(art_quick_resolution_trampoline);
+static inline const void* GetQuickResolutionTrampoline(ClassLinker* class_linker) {
+  return class_linker->GetQuickResolutionTrampoline();
 }
 
 // Return address of resolution trampoline stub for defined compiler.
-static inline void* GetResolutionTrampoline() {
+static inline const void* GetResolutionTrampoline(ClassLinker* class_linker) {
 #if defined(ART_USE_PORTABLE_COMPILER)
-  return GetPortableResolutionTrampoline();
+  return GetPortableResolutionTrampoline(class_linker);
 #else
-  return GetQuickResolutionTrampoline();
+  return GetQuickResolutionTrampoline(class_linker);
 #endif
 }
 
diff --git a/src/thread.cc b/src/thread.cc
index c5bfb20..9e86532 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -865,9 +865,10 @@
   // TODO: we call this code when dying but may not have suspended the thread ourself. The
   //       IsSuspended check is therefore racy with the use for dumping (normally we inhibit
   //       the race with the thread_suspend_count_lock_).
-  if (this == Thread::Current() || IsSuspended()) {
+  bool dump_for_abort = (gAborting > 0);
+  if (this == Thread::Current() || IsSuspended() || dump_for_abort) {
     // If we're currently in native code, dump that stack before dumping the managed stack.
-    if (ShouldShowNativeStack(this)) {
+    if (dump_for_abort || ShouldShowNativeStack(this)) {
       DumpKernelStack(os, GetTid(), "  kernel: ", false);
       DumpNativeStack(os, GetTid(), "  native: ", false);
     }
@@ -1647,6 +1648,8 @@
   ENTRY_POINT_INFO(pMemcmp16),
   ENTRY_POINT_INFO(pStringCompareTo),
   ENTRY_POINT_INFO(pMemcpy),
+  ENTRY_POINT_INFO(pPortableResolutionTrampolineFromCode),
+  ENTRY_POINT_INFO(pQuickResolutionTrampolineFromCode),
   ENTRY_POINT_INFO(pInvokeDirectTrampolineWithAccessCheck),
   ENTRY_POINT_INFO(pInvokeInterfaceTrampoline),
   ENTRY_POINT_INFO(pInvokeInterfaceTrampolineWithAccessCheck),
diff --git a/src/verifier/method_verifier.cc b/src/verifier/method_verifier.cc
index 2798ab3..8ca5b82 100644
--- a/src/verifier/method_verifier.cc
+++ b/src/verifier/method_verifier.cc
@@ -1253,6 +1253,11 @@
     if (dead_start >= 0) {
       LogVerifyInfo() << "dead code " << reinterpret_cast<void*>(dead_start) << "-" << reinterpret_cast<void*>(insn_idx - 1);
     }
+    // To dump the state of the verify after a method, do something like:
+    // if (PrettyMethod(dex_method_idx_, *dex_file_) ==
+    //     "boolean java.lang.String.equals(java.lang.Object)") {
+    //   LOG(INFO) << info_messages_.str();
+    // }
   }
   return true;
 }
@@ -1762,18 +1767,18 @@
       }
 
       // Find previous instruction - its existence is a precondition to peephole optimization.
-      uint32_t prev_idx = 0;
+      uint32_t instance_of_idx = 0;
       if (0 != work_insn_idx_) {
-        prev_idx = work_insn_idx_ - 1;
-        while(0 != prev_idx && !insn_flags_[prev_idx].IsOpcode()) {
-          prev_idx--;
+        instance_of_idx = work_insn_idx_ - 1;
+        while(0 != instance_of_idx && !insn_flags_[instance_of_idx].IsOpcode()) {
+          instance_of_idx--;
         }
-        CHECK(insn_flags_[prev_idx].IsOpcode());
+        CHECK(insn_flags_[instance_of_idx].IsOpcode());
       } else {
         break;
       }
 
-      const Instruction* prev_inst = Instruction::At(code_item_->insns_+prev_idx);
+      const Instruction* instance_of_inst = Instruction::At(code_item_->insns_ + instance_of_idx);
 
       /* Check for peep-hole pattern of:
        *    ...;
@@ -1788,22 +1793,51 @@
        *  - when vX == vY.
        */
       if (!CurrentInsnFlags()->IsBranchTarget() &&
-          (Instruction::INSTANCE_OF == prev_inst->Opcode()) &&
-          (inst->VRegA_21t() == prev_inst->VRegA_22c()) &&
-          (prev_inst->VRegA_22c() != prev_inst->VRegB_22c())) {
+          (Instruction::INSTANCE_OF == instance_of_inst->Opcode()) &&
+          (inst->VRegA_21t() == instance_of_inst->VRegA_22c()) &&
+          (instance_of_inst->VRegA_22c() != instance_of_inst->VRegB_22c())) {
         // Check that the we are not attempting conversion to interface types,
         // which is not done because of the multiple inheritance implications.
-        const RegType& cast_type = ResolveClassAndCheckAccess(prev_inst->VRegC_22c());
+        const RegType& cast_type = ResolveClassAndCheckAccess(instance_of_inst->VRegC_22c());
 
         if(!cast_type.IsUnresolvedTypes() && !cast_type.GetClass()->IsInterface()) {
+          RegisterLine* update_line = new RegisterLine(code_item_->registers_size_, this);
           if (inst->Opcode() == Instruction::IF_EQZ) {
-            fallthrough_line.reset(new RegisterLine(code_item_->registers_size_, this));
-            fallthrough_line->CopyFromLine(work_line_.get());
-            fallthrough_line->SetRegisterType(prev_inst->VRegB_22c(), cast_type);
+            fallthrough_line.reset(update_line);
           } else {
-            branch_line.reset(new RegisterLine(code_item_->registers_size_, this));
-            branch_line->CopyFromLine(work_line_.get());
-            branch_line->SetRegisterType(prev_inst->VRegB_22c(), cast_type);
+            branch_line.reset(update_line);
+          }
+          update_line->CopyFromLine(work_line_.get());
+          update_line->SetRegisterType(instance_of_inst->VRegB_22c(), cast_type);
+          if (!insn_flags_[instance_of_idx].IsBranchTarget() && 0 != instance_of_idx) {
+            // See if instance-of was preceded by a move-object operation, common due to the small
+            // register encoding space of instance-of, and propagate type information to the source
+            // of the move-object.
+            uint32_t move_idx = instance_of_idx - 1;
+            while(0 != move_idx && !insn_flags_[move_idx].IsOpcode()) {
+              move_idx--;
+            }
+            CHECK(insn_flags_[move_idx].IsOpcode());
+            const Instruction* move_inst = Instruction::At(code_item_->insns_ + move_idx);
+            switch (move_inst->Opcode()) {
+              case Instruction::MOVE_OBJECT:
+                if (move_inst->VRegA_12x() == instance_of_inst->VRegB_22c()) {
+                  update_line->SetRegisterType(move_inst->VRegB_12x(), cast_type);
+                }
+                break;
+              case Instruction::MOVE_OBJECT_FROM16:
+                if (move_inst->VRegA_22x() == instance_of_inst->VRegB_22c()) {
+                  update_line->SetRegisterType(move_inst->VRegB_22x(), cast_type);
+                }
+                break;
+              case Instruction::MOVE_OBJECT_16:
+                if (move_inst->VRegA_32x() == instance_of_inst->VRegB_22c()) {
+                  update_line->SetRegisterType(move_inst->VRegB_32x(), cast_type);
+                }
+                break;
+              default:
+                break;
+            }
           }
         }
       }
@@ -2528,12 +2562,12 @@
       if (!CheckNotMoveException(code_item_->insns_, next_insn_idx)) {
         return false;
       }
+      if (NULL != fallthrough_line.get()) {
+        // Make workline consistent with fallthrough computed from peephole optimization.
+        work_line_->CopyFromLine(fallthrough_line.get());
+      }
       RegisterLine* next_line = reg_table_.GetLine(next_insn_idx);
       if (next_line != NULL) {
-        if (NULL != fallthrough_line.get()) {
-          // Make workline consistent with fallthrough computed from peephole optimization.
-          work_line_->CopyFromLine(fallthrough_line.get());
-        }
         // Merge registers into what we have for the next instruction,
         // and set the "changed" flag if needed.
         if (!UpdateRegisters(next_insn_idx, work_line_.get())) {
@@ -3318,7 +3352,7 @@
     RegisterLine* line = reg_table_.GetLine(dex_pc);
     const RegType& reg_type(line->GetRegisterType(inst->VRegA_21c()));
     const RegType& cast_type = ResolveClassAndCheckAccess(inst->VRegB_21c());
-    if (cast_type.IsAssignableFrom(reg_type)) {
+    if (cast_type.IsStrictlyAssignableFrom(reg_type)) {
       if (mscs.get() == NULL) {
         mscs.reset(new MethodSafeCastSet());
       }
diff --git a/src/verifier/reg_type.cc b/src/verifier/reg_type.cc
index 8111db6..1c61a29 100644
--- a/src/verifier/reg_type.cc
+++ b/src/verifier/reg_type.cc
@@ -708,54 +708,62 @@
   : ConstantType(constat, cache_id) {
 }
 
-bool RegType::IsAssignableFrom(const RegType& src) const {
-  if (Equals(src)) {
+static bool AssignableFrom(const RegType& lhs, const RegType& rhs, bool strict)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  if (lhs.Equals(rhs)) {
     return true;
   } else {
-    if (IsBoolean()) {
-      return src.IsBooleanTypes();
-    } else if (IsByte()) {
-      return src.IsByteTypes();
-    } else if (IsShort()) {
-      return src.IsShortTypes();
-    } else if (IsChar()) {
-      return src.IsCharTypes();
-    } else if (IsInteger()) {
-      return src.IsIntegralTypes();
-    } else if (IsFloat()) {
-      return src.IsFloatTypes();
-    } else if (IsLongLo()) {
-      return src.IsLongTypes();
-    } else if (IsDoubleLo()) {
-      return src.IsDoubleTypes();
+    if (lhs.IsBoolean()) {
+      return rhs.IsBooleanTypes();
+    } else if (lhs.IsByte()) {
+      return rhs.IsByteTypes();
+    } else if (lhs.IsShort()) {
+      return rhs.IsShortTypes();
+    } else if (lhs.IsChar()) {
+      return rhs.IsCharTypes();
+    } else if (lhs.IsInteger()) {
+      return rhs.IsIntegralTypes();
+    } else if (lhs.IsFloat()) {
+      return rhs.IsFloatTypes();
+    } else if (lhs.IsLongLo()) {
+      return rhs.IsLongTypes();
+    } else if (lhs.IsDoubleLo()) {
+      return rhs.IsDoubleTypes();
     } else {
-      if (!IsReferenceTypes()) {
-        LOG(FATAL) << "Unexpected register type in 4bleFrom: '" << src << "'";
-      }
-      if (src.IsZero()) {
-        return true;  // all reference types can be assigned null
-      } else if (!src.IsReferenceTypes()) {
-        return false;  // expect src to be a reference type
-      } else if (IsJavaLangObject()) {
-        return true;  // all reference types can be assigned to Object
-      } else if (!IsUnresolvedTypes() && GetClass()->IsInterface()) {
-        return true;  // We allow assignment to any interface, see comment in ClassJoin
-      } else if (IsJavaLangObjectArray()) {
-        return src.IsObjectArrayTypes();  // All reference arrays may be assigned to Object[]
-      } else if (!IsUnresolvedTypes() && !src.IsUnresolvedTypes() &&
-          GetClass()->IsAssignableFrom(src.GetClass())) {
-        // We're assignable from the Class point-of-view
+      CHECK(lhs.IsReferenceTypes())
+          << "Unexpected register type in IsAssignableFrom: '"
+          << lhs << "' := '" << rhs << "'";
+      if (rhs.IsZero()) {
+        return true;  // All reference types can be assigned null.
+      } else if (!rhs.IsReferenceTypes()) {
+        return false;  // Expect rhs to be a reference type.
+      } else if (lhs.IsJavaLangObject()) {
+        return true;  // All reference types can be assigned to Object.
+      } else if (!strict && !lhs.IsUnresolvedTypes() && lhs.GetClass()->IsInterface()) {
+        // If we're not strict allow assignment to any interface, see comment in ClassJoin.
         return true;
-      } else if (IsUnresolvedTypes()) {
-        // Unresolved types are only assignable for null and equality.
-        return src.IsZero();
+      } else if (lhs.IsJavaLangObjectArray()) {
+        return rhs.IsObjectArrayTypes();  // All reference arrays may be assigned to Object[]
+      } else if (lhs.HasClass() && rhs.HasClass() &&
+                 lhs.GetClass()->IsAssignableFrom(rhs.GetClass())) {
+        // We're assignable from the Class point-of-view.
+        return true;
       } else {
+        // Unresolved types are only assignable for null and equality.
         return false;
       }
     }
   }
 }
 
+bool RegType::IsAssignableFrom(const RegType& src) const {
+  return AssignableFrom(*this, src, false);
+}
+
+bool RegType::IsStrictlyAssignableFrom(const RegType& src) const {
+  return AssignableFrom(*this, src, true);
+}
+
 int32_t ConstantType::ConstantValue() const {
   DCHECK(IsConstantTypes());
   return constant_;
diff --git a/src/verifier/reg_type.h b/src/verifier/reg_type.h
index 424b071..9ac0eca 100644
--- a/src/verifier/reg_type.h
+++ b/src/verifier/reg_type.h
@@ -230,7 +230,14 @@
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Can this type be assigned by src?
-  bool IsAssignableFrom(const RegType& src) const  SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  // Note: Object and interface types may always be assigned to one another, see comment on
+  // ClassJoin.
+  bool IsAssignableFrom(const RegType& src) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  // Can this type be assigned by src? Variant of IsAssignableFrom that doesn't allow assignment to
+  // an interface from an Object.
+  bool IsStrictlyAssignableFrom(const RegType& src) const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Are these RegTypes the same?
   bool Equals(const RegType& other) const {
@@ -241,23 +248,22 @@
   virtual const RegType& Merge(const RegType& incoming_type, RegTypeCache* reg_types) const
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-    /*
-     * A basic Join operation on classes. For a pair of types S and T the Join, written S v T = J, is
-     * S <: J, T <: J and for-all U such that S <: U, T <: U then J <: U. That is J is the parent of
-     * S and T such that there isn't a parent of both S and T that isn't also the parent of J (ie J
-     * is the deepest (lowest upper bound) parent of S and T).
-     *
-     * This operation applies for regular classes and arrays, however, for interface types there needn't
-     * be a partial ordering on the types. We could solve the problem of a lack of a partial order by
-     * introducing sets of types, however, the only operation permissible on an interface is
-     * invoke-interface. In the tradition of Java verifiers [1] we defer the verification of interface
-     * types until an invoke-interface call on the interface typed reference at runtime and allow
-     * the perversion of Object being assignable to an interface type (note, however, that we don't
-     * allow assignment of Object or Interface to any concrete class and are therefore type safe).
-     *
-     * [1] Java bytecode verification: algorithms and formalizations, Xavier Leroy
-     */
-
+  /*
+   * A basic Join operation on classes. For a pair of types S and T the Join, written S v T = J, is
+   * S <: J, T <: J and for-all U such that S <: U, T <: U then J <: U. That is J is the parent of
+   * S and T such that there isn't a parent of both S and T that isn't also the parent of J (ie J
+   * is the deepest (lowest upper bound) parent of S and T).
+   *
+   * This operation applies for regular classes and arrays, however, for interface types there
+   * needn't be a partial ordering on the types. We could solve the problem of a lack of a partial
+   * order by introducing sets of types, however, the only operation permissible on an interface is
+   * invoke-interface. In the tradition of Java verifiers [1] we defer the verification of interface
+   * types until an invoke-interface call on the interface typed reference at runtime and allow
+   * the perversion of Object being assignable to an interface type (note, however, that we don't
+   * allow assignment of Object or Interface to any concrete class and are therefore type safe).
+   *
+   * [1] Java bytecode verification: algorithms and formalizations, Xavier Leroy
+   */
   static mirror::Class* ClassJoin(mirror::Class* s, mirror::Class* t)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);