Runtime access checks on virtual method calls

At verification time we may not know that an illegal access or method
not found exception should be raised and so we defer the decision to
runtime. When the decision is deferred we perform an appropriate slow
path method invocation that can check for access violations.

This change also attempts to reduce code duplication, improve the
diagnostic information in exceptions, clean up field slow paths slightly
and to move the slow path calls lower in the Thread class so that they
don't effect the offsets of data items when calls are added or removed.

Change-Id: I8376b83dcd7e302cbbddf44c1a55a25687b9dcdb
diff --git a/src/asm_support.h b/src/asm_support.h
index d3e18bc..6ba23bc 100644
--- a/src/asm_support.h
+++ b/src/asm_support.h
@@ -24,13 +24,13 @@
 #define rSELF r9
 #define rLR r14
 // Offset of field Thread::suspend_count_ verified in InitCpu
-#define THREAD_SUSPEND_COUNT_OFFSET 424
+#define THREAD_SUSPEND_COUNT_OFFSET 120
 // Offset of field Thread::exception_ verified in InitCpu
-#define THREAD_EXCEPTION_OFFSET 420
+#define THREAD_EXCEPTION_OFFSET 116
 
 #elif defined(__i386__)
 // Offset of field Thread::self_ verified in InitCpu
-#define THREAD_SELF_OFFSET 412
+#define THREAD_SELF_OFFSET 108
 #endif
 
 #endif  // ART_SRC_ASM_SUPPORT_H_
diff --git a/src/class_linker_test.cc b/src/class_linker_test.cc
index bc13cb2..e9a94a9 100644
--- a/src/class_linker_test.cc
+++ b/src/class_linker_test.cc
@@ -922,9 +922,9 @@
   EXPECT_NE(Jj2, Aj2);
   EXPECT_EQ(Kj1, Jj1);
   EXPECT_EQ(Kj2, Jj2);
-  EXPECT_EQ(Ai, A->FindVirtualMethodForInterface(Ii, true));
-  EXPECT_EQ(Aj1, A->FindVirtualMethodForInterface(Jj1, true));
-  EXPECT_EQ(Aj2, A->FindVirtualMethodForInterface(Jj2, true));
+  EXPECT_EQ(Ai, A->FindVirtualMethodForInterface(Ii));
+  EXPECT_EQ(Aj1, A->FindVirtualMethodForInterface(Jj1));
+  EXPECT_EQ(Aj2, A->FindVirtualMethodForInterface(Jj2));
   EXPECT_EQ(Ai, A->FindVirtualMethodForVirtualOrInterface(Ii));
   EXPECT_EQ(Aj1, A->FindVirtualMethodForVirtualOrInterface(Jj1));
   EXPECT_EQ(Aj2, A->FindVirtualMethodForVirtualOrInterface(Jj2));
diff --git a/src/compiler.cc b/src/compiler.cc
index c4c8424..8210c9b 100644
--- a/src/compiler.cc
+++ b/src/compiler.cc
@@ -250,20 +250,32 @@
   return referrer_class->CanAccess(resolved_class) && resolved_class->IsInstantiable();
 }
 
+static Class* ComputeReferrerClass(CompilationUnit* cUnit) {
+  const DexFile::MethodId& referrer_method_id = cUnit->dex_file->GetMethodId(cUnit->method_idx);
+  return cUnit->class_linker->ResolveType(*cUnit->dex_file, referrer_method_id.class_idx_,
+                                          cUnit->dex_cache, cUnit->class_loader);
+}
+
+static Field* ComputeReferrerField(CompilationUnit* cUnit, uint32_t field_idx) {
+  return cUnit->class_linker->ResolveField(*cUnit->dex_file, field_idx, cUnit->dex_cache,
+                                           cUnit->class_loader, false);
+
+}
+
+static Method* ComputeReferrerMethod(CompilationUnit* cUnit, uint32_t method_idx) {
+  return cUnit->class_linker->ResolveMethod(*cUnit->dex_file, method_idx, cUnit->dex_cache,
+                                            cUnit->class_loader, true);
+}
+
 bool Compiler::ComputeInstanceFieldInfo(uint32_t field_idx, CompilationUnit* cUnit,
                                         int& field_offset, bool& is_volatile) const {
   // Conservative defaults
   field_offset = -1;
   is_volatile = true;
   // Try to resolve field
-  Field* resolved_field =
-      cUnit->class_linker->ResolveField(*cUnit->dex_file, field_idx, cUnit->dex_cache,
-                                        cUnit->class_loader, false);
+  Field* resolved_field = ComputeReferrerField(cUnit, field_idx);
   if (resolved_field != NULL) {
-    const DexFile::MethodId& referrer_method_id = cUnit->dex_file->GetMethodId(cUnit->method_idx);
-    Class* referrer_class =
-        cUnit->class_linker->ResolveType(*cUnit->dex_file, referrer_method_id.class_idx_,
-                                         cUnit->dex_cache, cUnit->class_loader);
+    Class* referrer_class = ComputeReferrerClass(cUnit);
     // Try to resolve referring class then access check, failure to pass the
     Class* fields_class = resolved_field->GetDeclaringClass();
     if (referrer_class != NULL &&
@@ -292,14 +304,10 @@
   is_referrers_class = false;
   is_volatile = true;
   // Try to resolve field
-  Field* resolved_field =
-      cUnit->class_linker->ResolveField(*cUnit->dex_file, field_idx, cUnit->dex_cache,
-                                        cUnit->class_loader, true);
+  Field* resolved_field = ComputeReferrerField(cUnit, field_idx);
   if (resolved_field != NULL) {
-    const DexFile::MethodId& referrer_method_id = cUnit->dex_file->GetMethodId(cUnit->method_idx);
-    Class* referrer_class =
-        cUnit->class_linker->ResolveType(*cUnit->dex_file, referrer_method_id.class_idx_,
-                                         cUnit->dex_cache, cUnit->class_loader);
+    DCHECK(resolved_field->IsStatic());
+    Class* referrer_class = ComputeReferrerClass(cUnit);
     if (referrer_class != NULL) {
       if (resolved_field->GetDeclaringClass() == referrer_class) {
         is_referrers_class = true;  // implies no worrying about class initialization
@@ -350,6 +358,54 @@
   return false;  // Incomplete knowledge needs slow path.
 }
 
+bool Compiler::ComputeInvokeInfo(uint32_t method_idx, CompilationUnit* cUnit,
+                                 bool is_interface, bool is_super,
+                                 int& vtable_idx) const {
+  vtable_idx = -1;
+  Method* resolved_method = ComputeReferrerMethod(cUnit, method_idx);
+  if (resolved_method != NULL) {
+    Class* referrer_class = ComputeReferrerClass(cUnit);
+    if (referrer_class != NULL) {
+      Class* methods_class = resolved_method->GetDeclaringClass();
+      if (!referrer_class->CanAccess(methods_class) ||
+          !referrer_class->CanAccessMember(methods_class,
+                                          resolved_method->GetAccessFlags())) {
+        // The referring class can't access the resolved method, this may occur as a result of a
+        // protected method being made public by implementing an interface that re-declares the
+        // method public. Resort to the dex file to determine the correct class for the access check
+        const DexFile& dex_file = cUnit->class_linker->FindDexFile(referrer_class->GetDexCache());
+        methods_class =
+            cUnit->class_linker->ResolveType(dex_file,
+                                             dex_file.GetMethodId(method_idx).class_idx_,
+                                             referrer_class);
+
+      }
+      if (referrer_class->CanAccess(methods_class) &&
+          referrer_class->CanAccessMember(methods_class,
+                                          resolved_method->GetAccessFlags())) {
+        vtable_idx = resolved_method->GetMethodIndex();
+        if (is_interface || is_super) {
+          // nothing left to do for virtual/interface dispatch
+          return true;
+        } else {
+          // ensure the vtable index will be correct to dispatch in the vtable of the super class
+          Class* super_class = methods_class->GetSuperClass();
+          if (super_class != NULL && vtable_idx <= super_class->GetVTable()->GetLength()) {
+            vtable_idx = resolved_method->GetMethodIndex();
+            return true;
+          }
+        }
+      }
+    }
+  }
+  // Clean up any exception left by method/type resolution
+  Thread* thread = Thread::Current();
+  if (thread->IsExceptionPending()) {
+      thread->ClearException();
+  }
+  return false;  // Incomplete knowledge needs slow path.
+}
+
 // Return true if the class should be skipped during compilation. We
 // never skip classes in the boot class loader. However, if we have a
 // non-boot class loader and we can resolve the class in the boot
diff --git a/src/compiler.h b/src/compiler.h
index d966b02..b55960b 100644
--- a/src/compiler.h
+++ b/src/compiler.h
@@ -109,6 +109,10 @@
                               int& field_offset, int& ssb_index,
                               bool& is_referrers_class, bool& is_volatile) const;
 
+  // Can we fastpath a interface, super class or virtual method call? Computes method's vtable index
+  bool ComputeInvokeInfo(uint32_t method_idx, CompilationUnit* cUnit, bool is_interface,
+                         bool is_super, int& vtable_idx) const;
+
  private:
 
   // Checks if class specified by type_idx is one of the image_classes_
diff --git a/src/compiler/codegen/arm/MethodCodegenDriver.cc b/src/compiler/codegen/arm/MethodCodegenDriver.cc
index e4073d4..eb2c06b 100644
--- a/src/compiler/codegen/arm/MethodCodegenDriver.cc
+++ b/src/compiler/codegen/arm/MethodCodegenDriver.cc
@@ -351,19 +351,16 @@
     }
 }
 
-typedef int (*NextCallInsn)(CompilationUnit*, MIR*, DecodedInstruction*, int,
-                            ArmLIR*);
+typedef int (*NextCallInsn)(CompilationUnit*, MIR*, int, uint32_t dexIdx,
+                            uint32_t methodIdx);
 
 /*
  * Bit of a hack here - in leiu of a real scheduling pass,
  * emit the next instruction in static & direct invoke sequences.
  */
 STATIC int nextSDCallInsn(CompilationUnit* cUnit, MIR* mir,
-                        DecodedInstruction* dInsn, int state,
-                        ArmLIR* rollback)
+                          int state, uint32_t dexIdx, uint32_t unused)
 {
-    DCHECK(rollback == NULL);
-    uint32_t idx = dInsn->vB;
     switch(state) {
         case 0:  // Get the current Method* [sets r0]
             loadCurrMethodDirect(cUnit, r0);
@@ -375,9 +372,9 @@
             break;
         case 2:  // Grab target method* and target code_
             loadWordDisp(cUnit, r0,
-                CodeAndDirectMethods::CodeOffsetInBytes(idx), rLR);
+                CodeAndDirectMethods::CodeOffsetInBytes(dexIdx), rLR);
             loadWordDisp(cUnit, r0,
-                CodeAndDirectMethods::MethodOffsetInBytes(idx), r0);
+                CodeAndDirectMethods::MethodOffsetInBytes(dexIdx), r0);
             break;
         default:
             return -1;
@@ -393,22 +390,13 @@
  * r1 here rather than the standard loadArgRegs.
  */
 STATIC int nextVCallInsn(CompilationUnit* cUnit, MIR* mir,
-                        DecodedInstruction* dInsn, int state,
-                        ArmLIR* rollback)
+                         int state, uint32_t dexIdx, uint32_t methodIdx)
 {
-    DCHECK(rollback == NULL);
     RegLocation rlArg;
     /*
      * This is the fast path in which the target virtual method is
      * fully resolved at compile time.
      */
-    Method* baseMethod = cUnit->class_linker->ResolveMethod(*cUnit->dex_file,
-                                                            dInsn->vB,
-                                                            cUnit->dex_cache,
-                                                            cUnit->class_loader,
-                                                            false);
-    CHECK(baseMethod != NULL);
-    uint32_t target_idx = baseMethod->GetMethodIndex();
     switch(state) {
         case 0:  // Get "this" [set r1]
             rlArg = oatGetSrc(cUnit, mir, 0);
@@ -423,7 +411,7 @@
             loadWordDisp(cUnit, rLR, Class::VTableOffset().Int32Value(), rLR);
             break;
         case 3: // Get target method [use rLR, set r0]
-            loadWordDisp(cUnit, rLR, (target_idx * 4) +
+            loadWordDisp(cUnit, rLR, (methodIdx * 4) +
                          Array::DataOffset().Int32Value(), r0);
             break;
         case 4: // Get the target compiled code address [uses r0, sets rLR]
@@ -435,157 +423,20 @@
     return state + 1;
 }
 
-STATIC int nextVCallInsnSP(CompilationUnit* cUnit, MIR* mir,
-                           DecodedInstruction* dInsn, int state,
-                           ArmLIR* rollback)
-{
-    RegLocation rlArg;
-    ArmLIR* skipBranch;
-    ArmLIR* skipTarget;
-    /*
-     * This handles the case in which the base method is not fully
-     * resolved at compile time.  We must generate code to test
-     * for resolution a run time, bail to the slow path if not to
-     * fill in all the tables.  In the latter case, we'll restart at
-     * at the beginning of the sequence.
-     */
-    switch(state) {
-        case 0:  // Get the current Method* [sets r0]
-            loadCurrMethodDirect(cUnit, r0);
-            break;
-        case 1: // Get method->dex_cache_resolved_methods_
-            loadWordDisp(cUnit, r0,
-                Method::GetDexCacheResolvedMethodsOffset().Int32Value(), rLR);
-            break;
-        case 2: // method->dex_cache_resolved_methods_->Get(method_idx)
-            loadWordDisp(cUnit, rLR, (dInsn->vB * 4) +
-                         Array::DataOffset().Int32Value(), rLR);
-            break;
-        case 3: // Resolved?
-            skipBranch = genCmpImmBranch(cUnit, kArmCondNe, rLR, 0);
-            // Slowest path, bail to helper, rollback and retry
-            loadWordDisp(cUnit, rSELF,
-                         OFFSETOF_MEMBER(Thread, pResolveMethodFromCode), rLR);
-            loadConstant(cUnit, r1, dInsn->vB);
-            loadConstant(cUnit, r2, false);
-            callRuntimeHelper(cUnit, rLR);
-            genUnconditionalBranch(cUnit, rollback);
-            // Resume normal slow path
-            skipTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
-            skipTarget->defMask = ENCODE_ALL;
-            skipBranch->generic.target = (LIR*)skipTarget;
-            // Get base_method->method_index [usr rLR, set r0]
-            loadBaseDisp(cUnit, mir, rLR,
-                         Method::GetMethodIndexOffset().Int32Value(), r0,
-                         kUnsignedHalf, INVALID_SREG);
-            // Load "this" [set r1]
-            rlArg = oatGetSrc(cUnit, mir, 0);
-            loadValueDirectFixed(cUnit, rlArg, r1);
-            break;
-        case 4:
-            // Is "this" null? [use r1]
-            genNullCheck(cUnit, oatSSASrc(mir,0), r1, mir);
-            // get this->clazz [use r1, set rLR]
-            loadWordDisp(cUnit, r1, Object::ClassOffset().Int32Value(), rLR);
-            break;
-        case 5:
-            // get this->klass_->vtable_ [usr rLR, set rLR]
-            loadWordDisp(cUnit, rLR, Class::VTableOffset().Int32Value(), rLR);
-            DCHECK_EQ((Array::DataOffset().Int32Value() & 0x3), 0);
-            // In load shadow fold vtable_ object header size into method_index_
-            opRegImm(cUnit, kOpAdd, r0,
-                     Array::DataOffset().Int32Value() / 4);
-            // Get target Method*
-            loadBaseIndexed(cUnit, rLR, r0, r0, 2, kWord);
-            break;
-        case 6: // Get the target compiled code address [uses r0, sets rLR]
-            loadWordDisp(cUnit, r0, Method::GetCodeOffset().Int32Value(), rLR);
-            break;
-        default:
-            return -1;
-    }
-    return state + 1;
-}
-
-STATIC int loadArgRegs(CompilationUnit* cUnit, MIR* mir,
-                          DecodedInstruction* dInsn, int callState,
-                          NextCallInsn nextCallInsn, ArmLIR* rollback,
-                          bool skipThis)
-{
-    int nextReg = r1;
-    int nextArg = 0;
-    if (skipThis) {
-        nextReg++;
-        nextArg++;
-    }
-    for (; (nextReg <= r3) && (nextArg < mir->ssaRep->numUses); nextReg++) {
-        RegLocation rlArg = oatGetRawSrc(cUnit, mir, nextArg++);
-        rlArg = oatUpdateRawLoc(cUnit, rlArg);
-        if (rlArg.wide && (nextReg <= r2)) {
-            loadValueDirectWideFixed(cUnit, rlArg, nextReg, nextReg + 1);
-            nextReg++;
-            nextArg++;
-        } else {
-            rlArg.wide = false;
-            loadValueDirectFixed(cUnit, rlArg, nextReg);
-        }
-        callState = nextCallInsn(cUnit, mir, dInsn, callState, rollback);
-    }
-    return callState;
-}
-
-// Interleave launch code for INVOKE_INTERFACE.
-STATIC int nextInterfaceCallInsn(CompilationUnit* cUnit, MIR* mir,
-                                 DecodedInstruction* dInsn, int state,
-                                 ArmLIR* rollback)
-{
-    DCHECK(rollback == NULL);
-    switch(state) {
-        case 0: // Load trampoline target
-            loadWordDisp(cUnit, rSELF,
-                         OFFSETOF_MEMBER(Thread, pInvokeInterfaceTrampoline),
-                         rLR);
-            // Load r0 with method index
-            loadConstant(cUnit, r0, dInsn->vB);
-            break;
-        default:
-            return -1;
-    }
-    return state + 1;
-}
-
 /*
  * Interleave launch code for INVOKE_SUPER.  See comments
  * for nextVCallIns.
  */
 STATIC int nextSuperCallInsn(CompilationUnit* cUnit, MIR* mir,
-                             DecodedInstruction* dInsn, int state,
-                             ArmLIR* rollback)
+                             int state, uint32_t dexIdx, uint32_t methodIdx)
 {
-    DCHECK(rollback == NULL);
-    RegLocation rlArg;
     /*
      * This is the fast path in which the target virtual method is
      * fully resolved at compile time.  Note also that this path assumes
      * that the check to verify that the target method index falls
      * within the size of the super's vtable has been done at compile-time.
      */
-    ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-    Method* baseMethod = class_linker->ResolveMethod(*cUnit->dex_file,
-                                                     dInsn->vB,
-                                                     cUnit->dex_cache,
-                                                     cUnit->class_loader,
-                                                     false);
-    CHECK(baseMethod != NULL);
-    Class* declaring_class = cUnit->dex_cache->GetResolvedTypes()
-        ->Get(cUnit->dex_file->GetMethodId(cUnit->method_idx).class_idx_);
-    Class* superClass = (declaring_class != NULL)
-        ? declaring_class->GetSuperClass() : NULL;
-    CHECK(superClass != NULL);
-    int32_t target_idx = baseMethod->GetMethodIndex();
-    CHECK(superClass->GetVTable()->GetLength() > target_idx);
-    Method* targetMethod = superClass->GetVTable()->Get(target_idx);
-    CHECK(targetMethod != NULL);
+    RegLocation rlArg;
     switch(state) {
         case 0: // Get current Method* [set r0]
             loadCurrMethodDirect(cUnit, r0);
@@ -606,7 +457,7 @@
             loadWordDisp(cUnit, rLR, Class::VTableOffset().Int32Value(), rLR);
             break;
         case 3: // Get target method [use rLR, set r0]
-            loadWordDisp(cUnit, rLR, (target_idx * 4) +
+            loadWordDisp(cUnit, rLR, (methodIdx * 4) +
                          Array::DataOffset().Int32Value(), r0);
             break;
         case 4: // Get the target compiled code address [uses r0, sets rLR]
@@ -618,85 +469,95 @@
     return state + 1;
 }
 
-/* Slow-path version of nextSuperCallInsn */
-STATIC int nextSuperCallInsnSP(CompilationUnit* cUnit, MIR* mir,
-                               DecodedInstruction* dInsn, int state,
-                               ArmLIR* rollback)
+STATIC int nextInvokeInsnSP(CompilationUnit* cUnit, MIR* mir,
+                                  bool accessCheck, bool isInterface,
+                                  bool isSuper, int state, uint32_t dexIdx,
+                                  uint32_t methodIdx)
 {
-    RegLocation rlArg;
-    ArmLIR* skipBranch;
-    ArmLIR* skipTarget;
-    int tReg;
     /*
      * This handles the case in which the base method is not fully
-     * resolved at compile time.  We must generate code to test
-     * for resolution a run time, bail to the slow path if not to
-     * fill in all the tables.  In the latter case, we'll restart at
-     * at the beginning of the sequence.
+     * resolved at compile time, we bail to a runtime helper.
      */
-    switch(state) {
-        case 0:  // Get the current Method* [sets r0]
-            loadCurrMethodDirect(cUnit, r0);
-            break;
-        case 1: // Get method->dex_cache_resolved_methods_ [uses r0, set rLR]
-            loadWordDisp(cUnit, r0,
-                Method::GetDexCacheResolvedMethodsOffset().Int32Value(), rLR);
-            break;
-        case 2: // method->dex_cache_resolved_methods_->Get(meth_idx) [u/s rLR]
-            loadWordDisp(cUnit, rLR, (dInsn->vB * 4) +
-                         Array::DataOffset().Int32Value(), rLR);
-            break;
-        case 3: // Resolved?
-            skipBranch = genCmpImmBranch(cUnit, kArmCondNe, rLR, 0);
-            // Slowest path, bail to helper, rollback and retry
-            loadWordDisp(cUnit, rSELF,
-                         OFFSETOF_MEMBER(Thread, pResolveMethodFromCode), rLR);
-            loadConstant(cUnit, r1, dInsn->vB);
-            loadConstant(cUnit, r2, true);
-            callRuntimeHelper(cUnit, rLR);
-            genUnconditionalBranch(cUnit, rollback);
-            // Resume normal slow path
-            skipTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
-            skipTarget->defMask = ENCODE_ALL;
-            skipBranch->generic.target = (LIR*)skipTarget;
-            // Get base_method->method_index [usr rLR, set rLR]
-            loadBaseDisp(cUnit, mir, rLR,
-                         Method::GetMethodIndexOffset().Int32Value(), rLR,
-                         kUnsignedHalf, INVALID_SREG);
-            // Load "this" [set r1]
-            rlArg = oatGetSrc(cUnit, mir, 0);
-            loadValueDirectFixed(cUnit, rlArg, r1);
-            // Load curMethod->declaring_class_ [uses r0, sets r0]
-            loadWordDisp(cUnit, r0, Method::DeclaringClassOffset().Int32Value(),
-                         r0);
-            // Null this?
-            genNullCheck(cUnit, oatSSASrc(mir,0), r1, mir);
-            // Get method->declaring_class_->super_class [usr r0, set r0]
-            loadWordDisp(cUnit, r0, Class::SuperClassOffset().Int32Value(), r0);
-            break;
-        case 4: // Get ...->super_class_->vtable [u/s r0]
-            loadWordDisp(cUnit, r0, Class::VTableOffset().Int32Value(), r0);
-            if (!(mir->optimizationFlags & MIR_IGNORE_RANGE_CHECK)) {
-                // Range check, throw NSM on failure
-                tReg = oatAllocTemp(cUnit);
-                loadWordDisp(cUnit, r0, Array::LengthOffset().Int32Value(),
-                             tReg);
-                genRegRegCheck(cUnit, kArmCondCs, rLR, tReg, mir,
-                               kArmThrowNoSuchMethod);
-                oatFreeTemp(cUnit, tReg);
-            }
-            // Adjust vtable_ base past object header
-            opRegImm(cUnit, kOpAdd, r0, Array::DataOffset().Int32Value());
-            // Get target Method*
-            loadBaseIndexed(cUnit, r0, rLR, r0, 2, kWord);
-            break;
-        case 5: // Get the target compiled code address [uses r0, sets rLR]
-            loadWordDisp(cUnit, r0, Method::GetCodeOffset().Int32Value(), rLR);
-            break;
-        default:
-            return -1;
+    if (state == 0) {
+        int trampoline;
+        if (!accessCheck) {
+          DCHECK(isInterface);
+          trampoline = OFFSETOF_MEMBER(Thread, pInvokeInterfaceTrampoline);
+        } else {
+          if (isInterface) {
+            trampoline = OFFSETOF_MEMBER(Thread, pInvokeInterfaceTrampolineWithAccessCheck);
+          } else if (isSuper) {
+            trampoline = OFFSETOF_MEMBER(Thread, pInvokeSuperTrampolineWithAccessCheck);
+          } else {
+            trampoline = OFFSETOF_MEMBER(Thread, pInvokeVirtualTrampolineWithAccessCheck);
+          }
+        }
+        // Load trampoline target
+        loadWordDisp(cUnit, rSELF, trampoline, rLR);
+        // Load r0 with method index
+        loadConstant(cUnit, r0, dexIdx);
+        return 1;
     }
-    return state + 1;
+    return -1;
+}
+
+STATIC int nextSuperCallInsnSP(CompilationUnit* cUnit, MIR* mir,
+                               int state, uint32_t dexIdx, uint32_t methodIdx)
+{
+  return nextInvokeInsnSP(cUnit, mir, true, false, true, state, dexIdx,
+                          methodIdx);
+}
+
+STATIC int nextVCallInsnSP(CompilationUnit* cUnit, MIR* mir,
+                           int state, uint32_t dexIdx, uint32_t methodIdx)
+{
+  return nextInvokeInsnSP(cUnit, mir, true, false, false, state, dexIdx,
+                          methodIdx);
+}
+
+/*
+ * All invoke-interface calls bounce off of art_invoke_interface_trampoline,
+ * which will locate the target and continue on via a tail call.
+ */
+STATIC int nextInterfaceCallInsn(CompilationUnit* cUnit, MIR* mir,
+                                 int state, uint32_t dexIdx, uint32_t unused)
+{
+  return nextInvokeInsnSP(cUnit, mir, false, true, false, state, dexIdx, 0);
+}
+
+STATIC int nextInterfaceCallInsnWithAccessCheck(CompilationUnit* cUnit,
+                                                MIR* mir, int state,
+                                                uint32_t dexIdx,
+                                                uint32_t unused)
+{
+  return nextInvokeInsnSP(cUnit, mir, true, true, false, state, dexIdx, 0);
+}
+
+STATIC int loadArgRegs(CompilationUnit* cUnit, MIR* mir,
+                          DecodedInstruction* dInsn, int callState,
+                          NextCallInsn nextCallInsn, uint32_t dexIdx,
+                          uint32_t methodIdx, bool skipThis)
+{
+    int nextReg = r1;
+    int nextArg = 0;
+    if (skipThis) {
+        nextReg++;
+        nextArg++;
+    }
+    for (; (nextReg <= r3) && (nextArg < mir->ssaRep->numUses); nextReg++) {
+        RegLocation rlArg = oatGetRawSrc(cUnit, mir, nextArg++);
+        rlArg = oatUpdateRawLoc(cUnit, rlArg);
+        if (rlArg.wide && (nextReg <= r2)) {
+            loadValueDirectWideFixed(cUnit, rlArg, nextReg, nextReg + 1);
+            nextReg++;
+            nextArg++;
+        } else {
+            rlArg.wide = false;
+            loadValueDirectFixed(cUnit, rlArg, nextReg);
+        }
+        callState = nextCallInsn(cUnit, mir, callState, dexIdx, methodIdx);
+    }
+    return callState;
 }
 
 /*
@@ -709,8 +570,8 @@
 STATIC int genDalvikArgsNoRange(CompilationUnit* cUnit, MIR* mir,
                                 DecodedInstruction* dInsn, int callState,
                                 ArmLIR** pcrLabel, bool isRange,
-                                NextCallInsn nextCallInsn, ArmLIR* rollback,
-                                bool skipThis)
+                                NextCallInsn nextCallInsn, uint32_t dexIdx,
+                                uint32_t methodIdx, bool skipThis)
 {
     RegLocation rlArg;
 
@@ -718,7 +579,7 @@
     if (dInsn->vA == 0)
         return callState;
 
-    callState = nextCallInsn(cUnit, mir, dInsn, callState, rollback);
+    callState = nextCallInsn(cUnit, mir, callState, dexIdx, methodIdx);
 
     DCHECK_LE(dInsn->vA, 5U);
     if (dInsn->vA > 3) {
@@ -739,12 +600,12 @@
                 reg = r3;
                 loadWordDisp(cUnit, rSP,
                              oatSRegOffset(cUnit, rlArg.sRegLow) + 4, reg);
-                callState = nextCallInsn(cUnit, mir, dInsn, callState,
-                                         rollback);
+                callState = nextCallInsn(cUnit, mir, callState, dexIdx,
+                                         methodIdx);
             }
             storeBaseDisp(cUnit, rSP, (nextUse + 1) * 4, reg, kWord);
             storeBaseDisp(cUnit, rSP, 16 /* (3+1)*4 */, reg, kWord);
-            callState = nextCallInsn(cUnit, mir, dInsn, callState, rollback);
+            callState = nextCallInsn(cUnit, mir, callState, dexIdx, methodIdx);
             nextUse++;
         }
         // Loop through the rest
@@ -764,8 +625,8 @@
                 } else {
                     loadValueDirectFixed(cUnit, rlArg, lowReg);
                 }
-                callState = nextCallInsn(cUnit, mir, dInsn, callState,
-                                         rollback);
+                callState = nextCallInsn(cUnit, mir, callState, dexIdx,
+                                         methodIdx);
             }
             int outsOffset = (nextUse + 1) * 4;
             if (rlArg.wide) {
@@ -775,12 +636,12 @@
                 storeWordDisp(cUnit, rSP, outsOffset, lowReg);
                 nextUse++;
             }
-            callState = nextCallInsn(cUnit, mir, dInsn, callState, rollback);
+            callState = nextCallInsn(cUnit, mir, callState, dexIdx, methodIdx);
         }
     }
 
     callState = loadArgRegs(cUnit, mir, dInsn, callState, nextCallInsn,
-                            rollback, skipThis);
+                            dexIdx, methodIdx, skipThis);
 
     if (pcrLabel) {
         *pcrLabel = genNullCheck(cUnit, oatSSASrc(mir,0), r1, mir);
@@ -806,7 +667,8 @@
 STATIC int genDalvikArgsRange(CompilationUnit* cUnit, MIR* mir,
                               DecodedInstruction* dInsn, int callState,
                               ArmLIR** pcrLabel, NextCallInsn nextCallInsn,
-                              ArmLIR* rollback, bool skipThis)
+                              uint32_t dexIdx, uint32_t methodIdx,
+                              bool skipThis)
 {
     int firstArg = dInsn->vC;
     int numArgs = dInsn->vA;
@@ -814,7 +676,8 @@
     // If we can treat it as non-range (Jumbo ops will use range form)
     if (numArgs <= 5)
         return genDalvikArgsNoRange(cUnit, mir, dInsn, callState, pcrLabel,
-                                    true, nextCallInsn, rollback, skipThis);
+                                    true, nextCallInsn, dexIdx, methodIdx,
+                                    skipThis);
     /*
      * Make sure range list doesn't span the break between in normal
      * Dalvik vRegs and the ins.
@@ -867,25 +730,25 @@
     } else {
         // Use vldm/vstm pair using r3 as a temp
         int regsLeft = std::min(numArgs - 3, 16);
-        callState = nextCallInsn(cUnit, mir, dInsn, callState, rollback);
+        callState = nextCallInsn(cUnit, mir, callState, dexIdx, methodIdx);
         opRegRegImm(cUnit, kOpAdd, r3, rSP, startOffset);
         ArmLIR* ld = newLIR3(cUnit, kThumb2Vldms, r3, fr0, regsLeft);
         //TUNING: loosen barrier
         ld->defMask = ENCODE_ALL;
         setMemRefType(ld, true /* isLoad */, kDalvikReg);
-        callState = nextCallInsn(cUnit, mir, dInsn, callState, rollback);
+        callState = nextCallInsn(cUnit, mir, callState, dexIdx, methodIdx);
         opRegRegImm(cUnit, kOpAdd, r3, rSP, 4 /* Method* */ + (3 * 4));
-        callState = nextCallInsn(cUnit, mir, dInsn, callState, rollback);
+        callState = nextCallInsn(cUnit, mir, callState, dexIdx, methodIdx);
         ArmLIR* st = newLIR3(cUnit, kThumb2Vstms, r3, fr0, regsLeft);
         setMemRefType(st, false /* isLoad */, kDalvikReg);
         st->defMask = ENCODE_ALL;
-        callState = nextCallInsn(cUnit, mir, dInsn, callState, rollback);
+        callState = nextCallInsn(cUnit, mir, callState, dexIdx, methodIdx);
     }
 
     callState = loadArgRegs(cUnit, mir, dInsn, callState, nextCallInsn,
-                            rollback, skipThis);
+                            dexIdx, methodIdx, skipThis);
 
-    callState = nextCallInsn(cUnit, mir, dInsn, callState, rollback);
+    callState = nextCallInsn(cUnit, mir, callState, dexIdx, methodIdx);
     if (pcrLabel) {
         *pcrLabel = genNullCheck(cUnit, oatSSASrc(mir,0), r1, mir);
     }
@@ -916,10 +779,10 @@
     // Explicit register usage
     oatLockCallTemps(cUnit);
 
+    uint32_t dexMethodIdx = mir->dalvikInsn.vB;
     // Is this the special "Ljava/lang/Object;.<init>:()V" case?
-    if (mir->dalvikInsn.opcode == OP_INVOKE_DIRECT) {
-        int idx = mir->dalvikInsn.vB;
-        Method* target = cUnit->dex_cache->GetResolvedMethods()->Get(idx);
+    if (direct && !range) {
+        Method* target = cUnit->dex_cache->GetResolvedMethods()->Get(dexMethodIdx);
         if (target) {
             if (PrettyMethod(target) == "java.lang.Object.<init>()V") {
                 RegLocation rlArg = oatGetSrc(cUnit, mir, 0);
@@ -936,14 +799,15 @@
 
     if (range) {
         callState = genDalvikArgsRange(cUnit, mir, dInsn, callState, pNullCk,
-                                       nextCallInsn, NULL, false);
+                                       nextCallInsn, dexMethodIdx, 0, false);
     } else {
         callState = genDalvikArgsNoRange(cUnit, mir, dInsn, callState, pNullCk,
-                                         false, nextCallInsn, NULL, false);
+                                         false, nextCallInsn, dexMethodIdx,
+                                         0, false);
     }
     // Finish up any of the call sequence not interleaved in arg loading
     while (callState >= 0) {
-        callState = nextCallInsn(cUnit, mir, dInsn, callState, NULL);
+        callState = nextCallInsn(cUnit, mir, callState, dexMethodIdx, 0);
     }
     if (DISPLAY_MISSING_TARGETS) {
         genShowTarget(cUnit);
@@ -952,164 +816,44 @@
     oatClobberCalleeSave(cUnit);
 }
 
-/*
- * All invoke-interface calls bounce off of art_invoke_interface_trampoline,
- * which will locate the target and continue on via a tail call.
- */
-STATIC void genInvokeInterface(CompilationUnit* cUnit, MIR* mir)
+STATIC void genInvoke(CompilationUnit* cUnit, MIR* mir, bool isInterface,
+                      bool isSuper, bool isRange)
 {
     DecodedInstruction* dInsn = &mir->dalvikInsn;
     int callState = 0;
-    ArmLIR* nullCk;
-    oatFlushAllRegs(cUnit);    /* Everything to home location */
-
-    // Explicit register usage
-    oatLockCallTemps(cUnit);
-    /* Note: must call nextInterfaceCallInsn() prior to 1st argument load */
-    callState = nextInterfaceCallInsn(cUnit, mir, dInsn, callState, NULL);
-    if (mir->dalvikInsn.opcode == OP_INVOKE_INTERFACE)
-        callState = genDalvikArgsNoRange(cUnit, mir, dInsn, callState, &nullCk,
-                                         false, nextInterfaceCallInsn, NULL,
-                                         false);
-    else
-        callState = genDalvikArgsRange(cUnit, mir, dInsn, callState, &nullCk,
-                                       nextInterfaceCallInsn, NULL, false);
-    // Finish up any of the call sequence not interleaved in arg loading
-    while (callState >= 0) {
-        callState = nextInterfaceCallInsn(cUnit, mir, dInsn, callState, NULL);
-    }
-    if (DISPLAY_MISSING_TARGETS) {
-        genShowTarget(cUnit);
-    }
-    opReg(cUnit, kOpBlx, rLR);
-    oatClobberCalleeSave(cUnit);
-}
-
-STATIC void genInvokeSuper(CompilationUnit* cUnit, MIR* mir)
-{
-    DecodedInstruction* dInsn = &mir->dalvikInsn;
-    int callState = 0;
-    ArmLIR* rollback;
-    ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-    Method* baseMethod = class_linker->ResolveMethod(*cUnit->dex_file,
-                                                     dInsn->vB,
-                                                     cUnit->dex_cache,
-                                                     cUnit->class_loader,
-                                                     false);
-    NextCallInsn nextCallInsn;
-    bool fastPath = true;
-    oatFlushAllRegs(cUnit);    /* Everything to home location */
-
-    // Explicit register usage
-    oatLockCallTemps(cUnit);
-
-    // For testing, force call to artResolveMethodFromCode & ignore result
-    if (EXERCISE_RESOLVE_METHOD) {
-        loadCurrMethodDirect(cUnit, r0);
-        loadWordDisp(cUnit, rSELF,
-                     OFFSETOF_MEMBER(Thread, pResolveMethodFromCode), rLR);
-        loadConstant(cUnit, r1, dInsn->vB);
-        loadConstant(cUnit, r2, true);
-        callRuntimeHelper(cUnit, rLR);
-    }
-
-    if (SLOW_INVOKE_PATH || baseMethod == NULL) {
-        Thread* thread = Thread::Current();
-        if (thread->IsExceptionPending()) {  // clear any exception left by resolve method
-            thread->ClearException();
-        }
-        fastPath = false;
-    } else {
-        Class* declaring_class = cUnit->dex_cache->GetResolvedTypes()
-            ->Get(cUnit->dex_file->GetMethodId(cUnit->method_idx).class_idx_);
-        Class* superClass = (declaring_class != NULL)
-            ? declaring_class->GetSuperClass() : NULL;
-        if (superClass == NULL) {
-            fastPath = false;
-        } else {
-            int32_t target_idx = baseMethod->GetMethodIndex();
-            if (superClass->GetVTable()->GetLength() <= target_idx) {
-                fastPath = false;
-            } else {
-                fastPath = (superClass->GetVTable()->Get(target_idx) != NULL);
-            }
-        }
-    }
-    if (fastPath) {
-        nextCallInsn = nextSuperCallInsn;
-        rollback = NULL;
-    } else {
-        nextCallInsn = nextSuperCallInsnSP;
-        rollback = newLIR0(cUnit, kArmPseudoTargetLabel);
-        rollback->defMask = -1;
-    }
-    if (mir->dalvikInsn.opcode == OP_INVOKE_SUPER)
-        callState = genDalvikArgsNoRange(cUnit, mir, dInsn, callState, NULL,
-                                         false, nextCallInsn, rollback, true);
-    else
-        callState = genDalvikArgsRange(cUnit, mir, dInsn, callState, NULL,
-                                       nextCallInsn, rollback, true);
-    // Finish up any of the call sequence not interleaved in arg loading
-    while (callState >= 0) {
-        callState = nextCallInsn(cUnit, mir, dInsn, callState, rollback);
-    }
-    if (DISPLAY_MISSING_TARGETS) {
-        genShowTarget(cUnit);
-    }
-    opReg(cUnit, kOpBlx, rLR);
-    oatClobberCalleeSave(cUnit);
-}
-
-STATIC void genInvokeVirtual(CompilationUnit* cUnit, MIR* mir)
-{
-    DecodedInstruction* dInsn = &mir->dalvikInsn;
-    int callState = 0;
-    ArmLIR* rollback;
-    ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-    Method* method = class_linker->ResolveMethod(*cUnit->dex_file,
-                                                 dInsn->vB,
-                                                 cUnit->dex_cache,
-                                                 cUnit->class_loader,
-                                                 false);
     NextCallInsn nextCallInsn;
     oatFlushAllRegs(cUnit);    /* Everything to home location */
     // Explicit register usage
     oatLockCallTemps(cUnit);
 
-    // For testing, force call to artResolveMethodFromCode & ignore result
-    if (EXERCISE_RESOLVE_METHOD) {
-        loadCurrMethodDirect(cUnit, r0);
-        loadWordDisp(cUnit, rSELF,
-                     OFFSETOF_MEMBER(Thread, pResolveMethodFromCode), rLR);
-        loadConstant(cUnit, r1, dInsn->vB);
-        loadConstant(cUnit, r2, false);
-        callRuntimeHelper(cUnit, rLR);
-    }
-
-    if (SLOW_INVOKE_PATH || method == NULL) {
-        Thread* thread = Thread::Current();
-        if (thread->IsExceptionPending()) {  // clear any exception left by resolve method
-            thread->ClearException();
-        }
-        // Slow path
-        nextCallInsn = nextVCallInsnSP;
-        // If we need a slow-path callout, we'll restart here
-        rollback = newLIR0(cUnit, kArmPseudoTargetLabel);
-        rollback->defMask = -1;
+    uint32_t dexMethodIdx = dInsn->vB;
+    int vtableIdx;
+    bool fastPath =
+        cUnit->compiler->ComputeInvokeInfo(dexMethodIdx, cUnit,
+                                           isInterface, isSuper, vtableIdx)
+        && !SLOW_INVOKE_PATH;
+    if (isInterface) {
+      nextCallInsn = fastPath ? nextInterfaceCallInsn
+                              : nextInterfaceCallInsnWithAccessCheck;
+    } else if (isSuper) {
+      nextCallInsn = fastPath ? nextSuperCallInsn : nextSuperCallInsnSP;
     } else {
-        // Fast path
-        nextCallInsn = nextVCallInsn;
-        rollback = NULL;
+      nextCallInsn = fastPath ? nextVCallInsn : nextVCallInsnSP;
     }
-    if (mir->dalvikInsn.opcode == OP_INVOKE_VIRTUAL)
+    bool skipThis = fastPath && !isInterface;
+    if (!isRange) {
         callState = genDalvikArgsNoRange(cUnit, mir, dInsn, callState, NULL,
-                                         false, nextCallInsn, rollback, true);
-    else
+                                         false, nextCallInsn, dexMethodIdx,
+                                         vtableIdx, skipThis);
+    } else {
         callState = genDalvikArgsRange(cUnit, mir, dInsn, callState, NULL,
-                                       nextCallInsn, rollback, true);
+                                       nextCallInsn, dexMethodIdx, vtableIdx,
+                                       skipThis);
+    }
     // Finish up any of the call sequence not interleaved in arg loading
     while (callState >= 0) {
-        callState = nextCallInsn(cUnit, mir, dInsn, callState, rollback);
+        callState = nextCallInsn(cUnit, mir, callState, dexMethodIdx,
+                                 vtableIdx);
     }
     if (DISPLAY_MISSING_TARGETS) {
         genShowTarget(cUnit);
@@ -1586,18 +1330,30 @@
             break;
 
         case OP_INVOKE_VIRTUAL:
+            genInvoke(cUnit, mir, false /*interface*/, false /*super*/,
+                      false /*range*/);
+            break;
         case OP_INVOKE_VIRTUAL_RANGE:
-            genInvokeVirtual(cUnit, mir);
+            genInvoke(cUnit, mir, false /*interface*/, false /*super*/,
+                      true /*range*/);
             break;
 
         case OP_INVOKE_SUPER:
+            genInvoke(cUnit, mir, false /*interface*/, true /*super*/,
+                      false /*range*/);
+            break;
         case OP_INVOKE_SUPER_RANGE:
-            genInvokeSuper(cUnit, mir);
+            genInvoke(cUnit, mir, false /*interface*/, true /*super*/,
+                      true /*range*/);
             break;
 
         case OP_INVOKE_INTERFACE:
+            genInvoke(cUnit, mir, true /*interface*/, false /*super*/,
+                      false /*range*/);
+            break;
         case OP_INVOKE_INTERFACE_RANGE:
-            genInvokeInterface(cUnit, mir);
+            genInvoke(cUnit, mir, true /*interface*/, false /*super*/,
+                      true /*range*/);
             break;
 
         case OP_NEG_INT:
diff --git a/src/dex_verifier.cc b/src/dex_verifier.cc
index 3e5ebd7..dfaaf01 100644
--- a/src/dex_verifier.cc
+++ b/src/dex_verifier.cc
@@ -3172,7 +3172,7 @@
   if (is_super) {
     DCHECK(method_type == METHOD_VIRTUAL);
     Class* super = method_->GetDeclaringClass()->GetSuperClass();
-    if (super == NULL || res_method->GetMethodIndex() > super->GetVTable()->GetLength()) {
+    if (super == NULL || res_method->GetMethodIndex() >= super->GetVTable()->GetLength()) {
       if (super == NULL) {  // Only Object has no super class
         Fail(VERIFY_ERROR_NO_METHOD) << "invalid invoke-super from " << PrettyMethod(method_)
                                      << " to super " << PrettyMethod(res_method);
diff --git a/src/object.cc b/src/object.cc
index 6be1136..bb87698 100644
--- a/src/object.cc
+++ b/src/object.cc
@@ -909,7 +909,7 @@
   SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, class_loader_), new_class_loader, false);
 }
 
-Method* Class::FindVirtualMethodForInterface(Method* method, bool can_throw) {
+Method* Class::FindVirtualMethodForInterface(Method* method) {
   Class* declaring_class = method->GetDeclaringClass();
   DCHECK(declaring_class != NULL) << PrettyClass(this);
   DCHECK(declaring_class->IsInterface()) << PrettyMethod(method);
@@ -922,11 +922,6 @@
       return interface_entry->GetMethodArray()->Get(method->GetMethodIndex());
     }
   }
-  if (can_throw) {
-    Thread::Current()->ThrowNewExceptionF("Ljava/lang/IncompatibleClassChangeError;",
-        "Class %s does not implement interface %s",
-        PrettyDescriptor(this).c_str(), PrettyDescriptor(declaring_class).c_str());
-  }
   return NULL;
 }
 
@@ -1346,7 +1341,7 @@
 std::string Throwable::Dump() const {
   std::string result(PrettyTypeOf(this));
   result += ": ";
-  String* msg = GetFieldObject<String*>(OFFSET_OF_OBJECT_MEMBER(Throwable, detail_message_), false);
+  String* msg = GetDetailMessage();
   if (msg != NULL) {
     result += msg->ToModifiedUtf8();
   }
diff --git a/src/object.h b/src/object.h
index 19ca325..738d2b0 100644
--- a/src/object.h
+++ b/src/object.h
@@ -1015,7 +1015,7 @@
 
 template<class T>
 T* ObjectArray<T>::Get(int32_t i) const {
-  if (!IsValidIndex(i)) {
+  if (UNLIKELY(!IsValidIndex(i))) {
     return NULL;
   }
   MemberOffset data_offset(DataOffset().Int32Value() + i * sizeof(Object*));
@@ -1523,7 +1523,7 @@
   // Given a method implemented by this class, but potentially from a
   // super class or interface, return the specific implementation
   // method for this class.
-  Method* FindVirtualMethodForInterface(Method* method, bool can_throw);
+  Method* FindVirtualMethodForInterface(Method* method);
 
   Method* FindInterfaceMethod(const StringPiece& name, const StringPiece& descriptor) const;
 
@@ -1532,7 +1532,7 @@
       return method;
     }
     if (method->GetDeclaringClass()->IsInterface()) {
-      return FindVirtualMethodForInterface(method, true);
+      return FindVirtualMethodForInterface(method);
     }
     return FindVirtualMethodForVirtual(method);
   }
@@ -1941,10 +1941,10 @@
 
 template<class T>
 void ObjectArray<T>::Set(int32_t i, T* object) {
-  if (IsValidIndex(i)) {
+  if (LIKELY(IsValidIndex(i))) {
     if (object != NULL) {
       Class* element_class = GetClass()->GetComponentType();
-      if (!object->InstanceOf(element_class)) {
+      if (UNLIKELY(!object->InstanceOf(element_class))) {
         ThrowArrayStoreException(object);
         return;
       }
@@ -2261,6 +2261,9 @@
     SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Throwable, detail_message_),
                    new_detail_message, false);
   }
+  String* GetDetailMessage() const {
+    return GetFieldObject<String*>(OFFSET_OF_OBJECT_MEMBER(Throwable, detail_message_), false);
+  }
   std::string Dump() const;
 
   // This is a runtime version of initCause, you shouldn't use it if initCause may have been
diff --git a/src/runtime_support.cc b/src/runtime_support.cc
index 7c47a8f..1ac92fd 100644
--- a/src/runtime_support.cc
+++ b/src/runtime_support.cc
@@ -34,6 +34,71 @@
   self->SetTopOfStack(sp, 0);
 }
 
+static void ThrowNewIllegalAccessErrorClass(Thread* self, Class* referrer, Class* accessed) {
+  self->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;",
+                           "illegal class access: '%s' -> '%s'",
+                           PrettyDescriptor(referrer).c_str(),
+                           PrettyDescriptor(accessed).c_str());
+}
+
+static void ThrowNewIllegalAccessErrorClassForMethodDispatch(Thread* self, Class* referrer,
+                                                             Class* accessed, const Method* caller,
+                                                             const Method* called,
+                                                             bool is_interface, bool is_super) {
+  self->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;",
+                           "illegal class access ('%s' -> '%s')"
+                           "in attempt to invoke %s method '%s' from '%s'",
+                           PrettyDescriptor(referrer).c_str(),
+                           PrettyDescriptor(accessed).c_str(),
+                           (is_interface ? "interface" : (is_super ? "super class" : "virtual")),
+                           PrettyMethod(called).c_str(),
+                           PrettyMethod(caller).c_str());
+}
+
+static void ThrowNewIncompatibleClassChangeErrorClassForInterfaceDispatch(Thread* self,
+                                                                          const Method* referrer,
+                                                                          const Method* interface_method,
+                                                                          Object* this_object) {
+  Thread::Current()->ThrowNewExceptionF("Ljava/lang/IncompatibleClassChangeError;",
+      "class '%s' does not implement interface '%s' in call to '%s' from '%s'",
+      PrettyDescriptor(this_object->GetClass()).c_str(),
+      PrettyDescriptor(interface_method->GetDeclaringClass()).c_str(),
+      PrettyMethod(interface_method).c_str(), PrettyMethod(referrer).c_str());
+}
+
+static void ThrowNewIllegalAccessErrorField(Thread* self, Class* referrer, Field* accessed) {
+  self->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;",
+                           "Field '%s' is inaccessible to class '%s'",
+                           PrettyField(accessed, false).c_str(),
+                           PrettyDescriptor(referrer).c_str());
+}
+
+static void ThrowNewIllegalAccessErrorMethod(Thread* self, Class* referrer, Method* accessed) {
+  self->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;",
+                           "Method '%s' is inaccessible to class '%s'",
+                           PrettyMethod(accessed).c_str(),
+                           PrettyDescriptor(referrer).c_str());
+}
+
+static void ThrowNullPointerExceptionForFieldAccess(Thread* self, Field* field, bool is_read) {
+  self->ThrowNewExceptionF("Ljava/lang/NullPointerException;",
+                           "Attempt to %s field '%s' on a null object reference",
+                           is_read ? "read from" : "write to",
+                           PrettyField(field, true).c_str());
+}
+
+static void ThrowNullPointerExceptionForMethodAccess(Thread* self, Method* caller,
+                                                     uint32_t method_idx, bool is_interface,
+                                                     bool is_super) {
+  const DexFile& dex_file =
+      Runtime::Current()->GetClassLinker()->FindDexFile(caller->GetDeclaringClass()->GetDexCache());
+  self->ThrowNewExceptionF("Ljava/lang/NullPointerException;",
+                           "Attempt to invoke %s method '%s' from '%s' on a null object reference",
+                           (is_interface ? "interface" : (is_super ? "super class" : "virtual")),
+                           PrettyMethod(method_idx, dex_file, true).c_str(),
+                           PrettyMethod(caller).c_str());
+}
+
 /*
  * Report location to debugger.  Note: dalvikPC is the current offset within
  * the method.  However, because the offset alone cannot distinguish between
@@ -482,6 +547,7 @@
   return resolved_field;
 }
 
+
 // Slow path field resolution and declaring class initialization
 Field* FindFieldFromCode(uint32_t field_idx, const Method* referrer, Thread* self,
                          bool is_static, bool is_primitive, size_t expected_size) {
@@ -491,14 +557,10 @@
     Class* fields_class = resolved_field->GetDeclaringClass();
     Class* referring_class = referrer->GetDeclaringClass();
     if (UNLIKELY(!referring_class->CanAccess(fields_class))) {
-      self->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;","%s tried to access class %s",
-                               PrettyMethod(referrer).c_str(),
-                               PrettyDescriptor(fields_class).c_str());
+      ThrowNewIllegalAccessErrorClass(self, referring_class, fields_class);
     } else if (UNLIKELY(!referring_class->CanAccessMember(fields_class,
                                                           resolved_field->GetAccessFlags()))) {
-      self->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;","%s tried to access field %s",
-                               PrettyMethod(referrer).c_str(),
-                               PrettyField(resolved_field, false).c_str());
+      ThrowNewIllegalAccessErrorField(self, referring_class, resolved_field);
       return NULL;  // failure
     }
     FieldHelper fh(resolved_field);
@@ -527,13 +589,6 @@
   return NULL;
 }
 
-static void ThrowNullPointerExceptionForFieldAccess(Thread* self, Field* field, bool is_read) {
-  self->ThrowNewExceptionF("Ljava/lang/NullPointerException;",
-                           "Attempt to %s field '%s' of a null object",
-                           is_read ? "read from" : "write to",
-                           PrettyField(field, true).c_str());
-}
-
 extern "C" uint32_t artGet32StaticFromCode(uint32_t field_idx, const Method* referrer,
                                            Thread* self, Method** sp) {
   Field* field = FindFieldFast(field_idx, referrer, true, sizeof(int32_t));
@@ -766,10 +821,7 @@
     }
     Class* referrer = method->GetDeclaringClass();
     if (UNLIKELY(!referrer->CanAccess(klass))) {
-      self->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;",
-                               "illegal class access: '%s' -> '%s'",
-                               PrettyDescriptor(referrer).c_str(),
-                               PrettyDescriptor(klass).c_str());
+      ThrowNewIllegalAccessErrorClass(self, referrer, klass);
       return NULL;  // Failure
     }
   }
@@ -815,10 +867,7 @@
   if (access_check) {
     Class* referrer = method->GetDeclaringClass();
     if (UNLIKELY(!referrer->CanAccess(klass))) {
-      self->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;",
-                               "illegal class access: '%s' -> '%s'",
-                               PrettyDescriptor(referrer).c_str(),
-                               PrettyDescriptor(klass).c_str());
+      ThrowNewIllegalAccessErrorClass(self, referrer, klass);
       return NULL;  // Failure
     }
   }
@@ -868,10 +917,7 @@
     if (access_check) {
       Class* referrer = method->GetDeclaringClass();
       if (UNLIKELY(!referrer->CanAccess(klass))) {
-        self->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;",
-                                 "illegal class access: '%s' -> '%s'",
-                                 PrettyDescriptor(referrer).c_str(),
-                                 PrettyDescriptor(klass).c_str());
+        ThrowNewIllegalAccessErrorClass(self, referrer, klass);
         return NULL;  // Failure
       }
     }
@@ -945,11 +991,9 @@
     return NULL;  // Failure - Indicate to caller to deliver exception
   }
   // Perform access check if necessary.
-  if (verify_access && !referrer->GetDeclaringClass()->CanAccess(klass)) {
-    self->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;",
-                             "Class %s is inaccessible to method %s",
-                             PrettyDescriptor(klass).c_str(),
-                             PrettyMethod(referrer, true).c_str());
+  Class* referring_class = referrer->GetDeclaringClass();
+  if (verify_access && UNLIKELY(!referring_class->CanAccess(klass))) {
+    ThrowNewIllegalAccessErrorClass(self, referring_class, klass);
     return NULL;  // Failure - Indicate to caller to deliver exception
   }
   // If we're just implementing const-class, we shouldn't call <clinit>.
@@ -960,7 +1004,7 @@
   //
   // Do not set the DexCache InitializedStaticStorage, since that implies <clinit> has finished
   // running.
-  if (klass == referrer->GetDeclaringClass() && MethodHelper(referrer).IsClassInitializer()) {
+  if (klass == referring_class && MethodHelper(referrer).IsClassInitializer()) {
     return klass;
   }
   if (!class_linker->EnsureInitialized(klass, true)) {
@@ -1086,46 +1130,191 @@
   return 0;  // Success
 }
 
-// See comments in runtime_support_asm.S
-extern "C" uint64_t artFindInterfaceMethodInCacheFromCode(uint32_t method_idx,
-                                                          Object* this_object,
-                                                          Method* caller_method,
-                                                          Thread* thread, Method** sp) {
-  Method* interface_method = caller_method->GetDexCacheResolvedMethods()->Get(method_idx);
-  Method* found_method = NULL;  // The found method
-  if (LIKELY(interface_method != NULL && this_object != NULL)) {
-    found_method = this_object->GetClass()->FindVirtualMethodForInterface(interface_method, false);
+// Fast path method resolution that can't throw exceptions
+static Method* FindMethodFast(uint32_t method_idx, Object* this_object, const Method* referrer,
+                              bool access_check, bool is_interface, bool is_super) {
+  if (UNLIKELY(this_object == NULL)) {
+    return NULL;
   }
-  if (UNLIKELY(found_method == NULL)) {
-    FinishCalleeSaveFrameSetup(thread, sp, Runtime::kRefsAndArgs);
-    if (this_object == NULL) {
-      thread->ThrowNewExceptionF("Ljava/lang/NullPointerException;",
-          "null receiver during interface dispatch");
-      return 0;
+  Method* resolved_method =
+      referrer->GetDeclaringClass()->GetDexCache()->GetResolvedMethod(method_idx);
+  if (UNLIKELY(resolved_method == NULL)) {
+    return NULL;
+  }
+  if (access_check) {
+    Class* methods_class = resolved_method->GetDeclaringClass();
+    Class* referring_class = referrer->GetDeclaringClass();
+    if (UNLIKELY(!referring_class->CanAccess(methods_class) ||
+                 !referring_class->CanAccessMember(methods_class,
+                                                   resolved_method->GetAccessFlags()))) {
+      // potential illegal access
+      return NULL;
     }
-    if (interface_method == NULL) {
-      ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-      interface_method = class_linker->ResolveMethod(method_idx, caller_method, false);
-      if (interface_method == NULL) {
-        // Could not resolve interface method. Throw error and unwind
-        CHECK(thread->IsExceptionPending());
-        return 0;
+  }
+  if (is_interface) {
+    return this_object->GetClass()->FindVirtualMethodForInterface(resolved_method);
+  } else if (is_super) {
+    return referrer->GetDeclaringClass()->GetSuperClass()->GetVTable()->Get(resolved_method->GetMethodIndex());
+  } else {
+    return this_object->GetClass()->GetVTable()->Get(resolved_method->GetMethodIndex());
+  }
+}
+
+// Slow path method resolution
+static Method* FindMethodFromCode(uint32_t method_idx, Object* this_object, const Method* referrer,
+                                  Thread* self, bool access_check, bool is_interface,
+                                  bool is_super) {
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  Method* resolved_method = class_linker->ResolveMethod(method_idx, referrer, false);
+  if (LIKELY(resolved_method != NULL)) {
+    if (!access_check) {
+      if (is_interface) {
+        Method* interface_method =
+            this_object->GetClass()->FindVirtualMethodForInterface(resolved_method);
+        if (UNLIKELY(interface_method == NULL)) {
+          ThrowNewIncompatibleClassChangeErrorClassForInterfaceDispatch(self, referrer,
+                                                                        resolved_method,
+                                                                        this_object);
+          return NULL;
+        } else {
+          return interface_method;
+        }
+      } else {
+        ObjectArray<Method>* vtable;
+        uint16_t vtable_index = resolved_method->GetMethodIndex();
+        if (is_super) {
+          vtable = referrer->GetDeclaringClass()->GetSuperClass()->GetVTable();
+        } else {
+          vtable = this_object->GetClass()->GetVTable();
+        }
+        // TODO: eliminate bounds check?
+        return vtable->Get(vtable_index);
+      }
+    } else {
+      Class* methods_class = resolved_method->GetDeclaringClass();
+      Class* referring_class = referrer->GetDeclaringClass();
+      if (UNLIKELY(!referring_class->CanAccess(methods_class) ||
+                   !referring_class->CanAccessMember(methods_class,
+                                                     resolved_method->GetAccessFlags()))) {
+        // The referring class can't access the resolved method, this may occur as a result of a
+        // protected method being made public by implementing an interface that re-declares the
+        // method public. Resort to the dex file to determine the correct class for the access check
+        const DexFile& dex_file = class_linker->FindDexFile(referring_class->GetDexCache());
+        methods_class = class_linker->ResolveType(dex_file,
+                                                  dex_file.GetMethodId(method_idx).class_idx_,
+                                                  referring_class);
+        if (UNLIKELY(!referring_class->CanAccess(methods_class))) {
+          ThrowNewIllegalAccessErrorClassForMethodDispatch(self, referring_class, methods_class,
+                                                           referrer, resolved_method, is_interface,
+                                                           is_super);
+          return NULL;  // failure
+        } else if (UNLIKELY(!referring_class->CanAccessMember(methods_class,
+                                                              resolved_method->GetAccessFlags()))) {
+          ThrowNewIllegalAccessErrorMethod(self, referring_class, resolved_method);
+          return NULL;  // failure
+        }
+      }
+      if (is_interface) {
+        Method* interface_method =
+            this_object->GetClass()->FindVirtualMethodForInterface(resolved_method);
+        if (UNLIKELY(interface_method == NULL)) {
+          ThrowNewIncompatibleClassChangeErrorClassForInterfaceDispatch(self, referrer,
+                                                                        resolved_method,
+                                                                        this_object);
+          return NULL;
+        } else {
+          return interface_method;
+        }
+      } else {
+        ObjectArray<Method>* vtable;
+        uint16_t vtable_index = resolved_method->GetMethodIndex();
+        if (is_super) {
+          Class* super_class = referring_class->GetSuperClass();
+          if (LIKELY(super_class != NULL)) {
+            vtable = referring_class->GetSuperClass()->GetVTable();
+          } else {
+            vtable = NULL;
+          }
+        } else {
+          vtable = this_object->GetClass()->GetVTable();
+        }
+        if (LIKELY(vtable != NULL &&
+                   vtable_index < static_cast<uint32_t>(vtable->GetLength()))) {
+          return vtable->GetWithoutChecks(vtable_index);
+        } else {
+          // Behavior to agree with that of the verifier
+          self->ThrowNewExceptionF("Ljava/lang/NoSuchMethodError;",
+                                   "attempt to invoke %s method '%s' from '%s'"
+                                   " using incorrect form of method dispatch",
+                                   (is_super ? "super class" : "virtual"),
+                                   PrettyMethod(resolved_method).c_str(),
+                                   PrettyMethod(referrer).c_str());
+          return NULL;
+        }
       }
     }
-    found_method = this_object->GetClass()->FindVirtualMethodForInterface(interface_method, true);
-    if (found_method == NULL) {
-      CHECK(thread->IsExceptionPending());
-      return 0;
+  }
+  DCHECK(self->IsExceptionPending());  // Throw exception and unwind
+  return NULL;
+}
+
+static uint64_t artInvokeCommon(uint32_t method_idx, Object* this_object, Method* caller_method,
+                                Thread* self, Method** sp, bool access_check, bool is_interface,
+                                bool is_super){
+  Method* method = FindMethodFast(method_idx, this_object, caller_method, access_check,
+                                  is_interface, is_super);
+  if (UNLIKELY(method == NULL)) {
+    FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsAndArgs);
+    if (UNLIKELY(this_object == NULL)) {
+      ThrowNullPointerExceptionForMethodAccess(self, caller_method, method_idx, is_interface,
+                                               is_super);
+      return 0;  // failure
+    }
+    method = FindMethodFromCode(method_idx, this_object, caller_method, self, access_check,
+                                is_interface, is_super);
+    if (UNLIKELY(method == NULL)) {
+      CHECK(self->IsExceptionPending());
+      return 0;  // failure
     }
   }
-  const void* code = found_method->GetCode();
+  // TODO: DCHECK
+  CHECK(!self->IsExceptionPending());
+  const void* code = method->GetCode();
 
-  uint32_t method_uint = reinterpret_cast<uint32_t>(found_method);
+  uint32_t method_uint = reinterpret_cast<uint32_t>(method);
   uint64_t code_uint = reinterpret_cast<uint32_t>(code);
   uint64_t result = ((code_uint << 32) | method_uint);
   return result;
 }
 
+// See comments in runtime_support_asm.S
+extern "C" uint64_t artInvokeInterfaceTrampoline(uint32_t method_idx, Object* this_object,
+                                                 Method* caller_method, Thread* self,
+                                                 Method** sp) {
+  return artInvokeCommon(method_idx, this_object, caller_method, self, sp, false, true, false);
+}
+
+extern "C" uint64_t artInvokeInterfaceTrampolineWithAccessCheck(uint32_t method_idx,
+                                                                Object* this_object,
+                                                                Method* caller_method, Thread* self,
+                                                                Method** sp) {
+  return artInvokeCommon(method_idx, this_object, caller_method, self, sp, true, true, false);
+}
+
+extern "C" uint64_t artInvokeSuperTrampolineWithAccessCheck(uint32_t method_idx,
+                                                            Object* this_object,
+                                                            Method* caller_method, Thread* self,
+                                                            Method** sp) {
+  return artInvokeCommon(method_idx, this_object, caller_method, self, sp, true, false, true);
+}
+
+extern "C" uint64_t artInvokeVirtualTrampolineWithAccessCheck(uint32_t method_idx,
+                                                              Object* this_object,
+                                                              Method* caller_method, Thread* self,
+                                                              Method** sp) {
+  return artInvokeCommon(method_idx, this_object, caller_method, self, sp, true, false, false);
+}
+
 static void ThrowNewUndeclaredThrowableException(Thread* self, JNIEnv* env, Throwable* exception) {
   ScopedLocalRef<jclass> jlr_UTE_class(env,
       env->FindClass("java/lang/reflect/UndeclaredThrowableException"));
diff --git a/src/runtime_support.h b/src/runtime_support.h
index b9c03e4..c8db48c 100644
--- a/src/runtime_support.h
+++ b/src/runtime_support.h
@@ -70,6 +70,9 @@
   extern "C" void art_do_long_jump(uint32_t*, uint32_t*);
   extern "C" void art_handle_fill_data_from_code(void*, void*);
   extern "C" void art_invoke_interface_trampoline(uint32_t, void*);
+  extern "C" void art_invoke_interface_trampoline_with_access_check(uint32_t, void*);
+  extern "C" void art_invoke_super_trampoline_with_access_check(uint32_t, void*);
+  extern "C" void art_invoke_virtual_trampoline_with_access_check(uint32_t, void*);
   extern "C" void art_lock_object_from_code(void*);
   extern "C" void art_object_init_from_code(void*);
   extern "C" void art_test_suspend();
diff --git a/src/runtime_support_arm.S b/src/runtime_support_arm.S
index c45583a..8113b5b 100644
--- a/src/runtime_support_arm.S
+++ b/src/runtime_support_arm.S
@@ -171,7 +171,7 @@
     b   artThrowVerificationErrorFromCode @ artThrowVerificationErrorFromCode(kind, ref, Thread*, SP)
 
     .global art_invoke_interface_trampoline
-    .extern artFindInterfaceMethodInCacheFromCode
+    .extern artInvokeInterfaceTrampoline
     /*
      * All generated callsites for interface invokes will load arguments as usual - except instead
      * of loading arg0/r0 with the target Method*, arg0/r0 will contain the method_idx.  This
@@ -179,10 +179,10 @@
      * artFindInterfaceMethodInCacheFromCode(idx, this, method);
      * NOTE: "this" is first visable argument of the target, and so can be found in arg1/r1.
      *
-     * artFindInterfaceMethodInCacheFromCode will attempt to locate the target and return a 64-bit
+     * artInvokeInterfaceTrampoline will attempt to locate the target and return a 64-bit
      * result in r0/r1 consisting of the target Method* in r0 and method->code_ in r1.
      *
-     * If unsuccessful, artFindInterfaceMethodInCacheFromCode will return NULL/NULL. There will be
+     * If unsuccessful, artInvokeInterfaceTrampoline will return NULL/NULL. There will be
      * a pending exception in the thread and we branch to another stub to deliver it.
      *
      * On success this wrapper will restore arguments and *jump* to the target, leaving the lr
@@ -193,7 +193,49 @@
     ldr    r2, [sp, #48]                          @ pass caller Method*
     mov    r3, r9                                 @ pass Thread::Current
     str    sp, [sp, #0]                           @ pass SP
-    bl     artFindInterfaceMethodInCacheFromCode  @ (method_idx, this, caller, Thread*, SP)
+    bl     artInvokeInterfaceTrampoline           @ (method_idx, this, caller, Thread*, SP)
+    mov    r12, r1                                @ save r0->code_
+    RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
+    cmp    r0, #0                                 @ did we find the target?
+    bxne   r12                                    @ tail call to target if so
+    DELIVER_PENDING_EXCEPTION
+
+    .global art_invoke_interface_trampoline_with_access_check
+    .extern artInvokeInterfaceTrampolineWithAccessCheck
+art_invoke_interface_trampoline_with_access_check:
+    SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME  @ save callee saves in case allocation triggers GC
+    ldr    r2, [sp, #48]                          @ pass caller Method*
+    mov    r3, r9                                 @ pass Thread::Current
+    str    sp, [sp, #0]                           @ pass SP
+    bl     artInvokeInterfaceTrampolineWithAccessCheck @ (method_idx, this, caller, Thread*, SP)
+    mov    r12, r1                                @ save r0->code_
+    RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
+    cmp    r0, #0                                 @ did we find the target?
+    bxne   r12                                    @ tail call to target if so
+    DELIVER_PENDING_EXCEPTION
+
+    .global art_invoke_super_trampoline_with_access_check
+    .extern artInvokeSuperTrampolineWithAccessCheck
+art_invoke_super_trampoline_with_access_check:
+    SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME  @ save callee saves in case allocation triggers GC
+    ldr    r2, [sp, #48]                          @ pass caller Method*
+    mov    r3, r9                                 @ pass Thread::Current
+    str    sp, [sp, #0]                           @ pass SP
+    bl     artInvokeSuperTrampolineWithAccessCheck @ (method_idx, this, caller, Thread*, SP)
+    mov    r12, r1                                @ save r0->code_
+    RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
+    cmp    r0, #0                                 @ did we find the target?
+    bxne   r12                                    @ tail call to target if so
+    DELIVER_PENDING_EXCEPTION
+
+    .global art_invoke_virtual_trampoline_with_access_check
+    .extern artInvokeVirtualTrampolineWithAccessCheck
+art_invoke_virtual_trampoline_with_access_check:
+    SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME  @ save callee saves in case allocation triggers GC
+    ldr    r2, [sp, #48]                          @ pass caller Method*
+    mov    r3, r9                                 @ pass Thread::Current
+    str    sp, [sp, #0]                           @ pass SP
+    bl     artInvokeVirtualTrampolineWithAccessCheck @ (method_idx, this, caller, Thread*, SP)
     mov    r12, r1                                @ save r0->code_
     RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
     cmp    r0, #0                                 @ did we find the target?
diff --git a/src/thread.cc b/src/thread.cc
index 582832d..8592bd3 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -116,6 +116,9 @@
   pInitializeTypeFromCode = art_initialize_type_from_code;
   pInitializeTypeAndVerifyAccessFromCode = art_initialize_type_and_verify_access_from_code;
   pInvokeInterfaceTrampoline = art_invoke_interface_trampoline;
+  pInvokeInterfaceTrampolineWithAccessCheck = art_invoke_interface_trampoline_with_access_check;
+  pInvokeSuperTrampolineWithAccessCheck = art_invoke_super_trampoline_with_access_check;
+  pInvokeVirtualTrampolineWithAccessCheck = art_invoke_virtual_trampoline_with_access_check;
   pLockObjectFromCode = art_lock_object_from_code;
   pObjectInit = art_object_init_from_code;
   pResolveStringFromCode = art_resolve_string_from_code;
@@ -1451,7 +1454,10 @@
   // resolution.
   ClearException();
   if (kDebugExceptionDelivery) {
-    DumpStack(LOG(INFO) << "Delivering exception: " << PrettyTypeOf(exception) << std::endl);
+    String* msg = exception->GetDetailMessage();
+    std::string str_msg(msg != NULL ? msg->ToModifiedUtf8() : "");
+    DumpStack(LOG(INFO) << "Delivering exception: " << PrettyTypeOf(exception)
+                        << ": " << str_msg << std::endl);
   }
 
   Context* long_jump_context = GetLongJumpContext();
diff --git a/src/thread.h b/src/thread.h
index b1b8a1e..b21af96 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -89,85 +89,6 @@
 
   static const size_t kDefaultStackSize = 96 * KB;
 
-  // Runtime support function pointers
-  void (*pDebugMe)(Method*, uint32_t);
-  void* (*pMemcpy)(void*, const void*, size_t);
-  uint64_t (*pShlLong)(uint64_t, uint32_t);
-  uint64_t (*pShrLong)(uint64_t, uint32_t);
-  uint64_t (*pUshrLong)(uint64_t, uint32_t);
-  float (*pI2f)(int);
-  int (*pF2iz)(float);
-  float (*pD2f)(double);
-  double (*pF2d)(float);
-  double (*pI2d)(int);
-  int (*pD2iz)(double);
-  float (*pL2f)(long);
-  double (*pL2d)(long);
-  long long (*pF2l)(float);
-  long long (*pD2l)(double);
-  float (*pFadd)(float, float);
-  float (*pFsub)(float, float);
-  float (*pFdiv)(float, float);
-  float (*pFmul)(float, float);
-  float (*pFmodf)(float, float);
-  double (*pDadd)(double, double);
-  double (*pDsub)(double, double);
-  double (*pDdiv)(double, double);
-  double (*pDmul)(double, double);
-  double (*pFmod)(double, double);
-  int (*pIdivmod)(int, int);
-  int (*pIdiv)(int, int);
-  long long (*pLmul)(long long, long long);
-  long long (*pLdivmod)(long long, long long);
-  void (*pCheckSuspendFromCode)(Thread*);  // Stub that is called when the suspend count is non-zero
-  void (*pTestSuspendFromCode)();  // Stub that is periodically called to test the suspend count
-  void* (*pAllocObjectFromCode)(uint32_t, void*);
-  void* (*pAllocObjectFromCodeWithAccessCheck)(uint32_t, void*);
-  void* (*pAllocArrayFromCode)(uint32_t, void*, int32_t);
-  void* (*pAllocArrayFromCodeWithAccessCheck)(uint32_t, void*, int32_t);
-  void (*pCanPutArrayElementFromCode)(void*, void*);
-  void* (*pCheckAndAllocArrayFromCode)(uint32_t, void*, int32_t);
-  void* (*pCheckAndAllocArrayFromCodeWithAccessCheck)(uint32_t, void*, int32_t);
-  void (*pCheckCastFromCode)(void*, void*);
-  Object* (*pDecodeJObjectInThread)(Thread* thread, jobject obj);
-  void (*pDeliverException)(void*);
-  Method* (*pFindInterfaceMethodInCache)(Class*, uint32_t, const Method*, struct DvmDex*);
-  void* (*pFindNativeMethod)(Thread* thread);
-  int32_t (*pGet32Instance)(uint32_t, void*);
-  int64_t (*pGet64Instance)(uint32_t, void*);
-  void* (*pGetObjInstance)(uint32_t, void*);
-  int32_t (*pGet32Static)(uint32_t);
-  int64_t (*pGet64Static)(uint32_t);
-  void* (*pGetObjStatic)(uint32_t);
-  void (*pHandleFillArrayDataFromCode)(void*, void*);
-  void* (*pInitializeStaticStorage)(uint32_t, void*);
-  uint32_t (*pInstanceofNonTrivialFromCode)(const Class*, const Class*);
-  void (*pInvokeInterfaceTrampoline)(uint32_t, void*);
-  void* (*pInitializeTypeFromCode)(uint32_t, void*);
-  void* (*pInitializeTypeAndVerifyAccessFromCode)(uint32_t, void*);
-  void (*pLockObjectFromCode)(void*);
-  void (*pObjectInit)(void*);
-  void* (*pResolveMethodFromCode)(void*, uint32_t, bool);
-  void* (*pResolveStringFromCode)(void*, uint32_t);
-  int (*pSet32Instance)(uint32_t, void*, int32_t);  // field_idx, obj, src
-  int (*pSet64Instance)(uint32_t, void*, int64_t);
-  int (*pSetObjInstance)(uint32_t, void*, void*);
-  int (*pSet32Static)(uint32_t, int32_t);
-  int (*pSet64Static)(uint32_t, int64_t);
-  int (*pSetObjStatic)(uint32_t, void*);
-  void (*pThrowStackOverflowFromCode)(void*);
-  void (*pThrowNullPointerFromCode)();
-  void (*pThrowArrayBoundsFromCode)(int32_t, int32_t);
-  void (*pThrowDivZeroFromCode)();
-  void (*pThrowVerificationErrorFromCode)(int32_t, int32_t);
-  void (*pThrowNegArraySizeFromCode)(int32_t);
-  void (*pThrowNoSuchMethodFromCode)(int32_t);
-  void (*pThrowAbstractMethodErrorFromCode)(Method* method, Thread* thread, Method** sp);
-  void (*pUnlockObjectFromCode)(void*);
-  void* (*pUnresolvedDirectMethodTrampolineFromCode)(int32_t, Method**, Thread*,
-                                                     Runtime::TrampolineType);
-  void (*pUpdateDebuggerFromCode)(void*, void*, int32_t, void*);
-
   class StackVisitor {
    public:
     virtual ~StackVisitor() {}
@@ -642,6 +563,90 @@
   // A cached copy of the java.lang.Thread's name.
   std::string* name_;
 
+ public:
+  // Runtime support function pointers
+  void (*pDebugMe)(Method*, uint32_t);
+  void* (*pMemcpy)(void*, const void*, size_t);
+  uint64_t (*pShlLong)(uint64_t, uint32_t);
+  uint64_t (*pShrLong)(uint64_t, uint32_t);
+  uint64_t (*pUshrLong)(uint64_t, uint32_t);
+  float (*pI2f)(int);
+  int (*pF2iz)(float);
+  float (*pD2f)(double);
+  double (*pF2d)(float);
+  double (*pI2d)(int);
+  int (*pD2iz)(double);
+  float (*pL2f)(long);
+  double (*pL2d)(long);
+  long long (*pF2l)(float);
+  long long (*pD2l)(double);
+  float (*pFadd)(float, float);
+  float (*pFsub)(float, float);
+  float (*pFdiv)(float, float);
+  float (*pFmul)(float, float);
+  float (*pFmodf)(float, float);
+  double (*pDadd)(double, double);
+  double (*pDsub)(double, double);
+  double (*pDdiv)(double, double);
+  double (*pDmul)(double, double);
+  double (*pFmod)(double, double);
+  int (*pIdivmod)(int, int);
+  int (*pIdiv)(int, int);
+  long long (*pLmul)(long long, long long);
+  long long (*pLdivmod)(long long, long long);
+  void (*pCheckSuspendFromCode)(Thread*);  // Stub that is called when the suspend count is non-zero
+  void (*pTestSuspendFromCode)();  // Stub that is periodically called to test the suspend count
+  void* (*pAllocObjectFromCode)(uint32_t, void*);
+  void* (*pAllocObjectFromCodeWithAccessCheck)(uint32_t, void*);
+  void* (*pAllocArrayFromCode)(uint32_t, void*, int32_t);
+  void* (*pAllocArrayFromCodeWithAccessCheck)(uint32_t, void*, int32_t);
+  void (*pCanPutArrayElementFromCode)(void*, void*);
+  void* (*pCheckAndAllocArrayFromCode)(uint32_t, void*, int32_t);
+  void* (*pCheckAndAllocArrayFromCodeWithAccessCheck)(uint32_t, void*, int32_t);
+  void (*pCheckCastFromCode)(void*, void*);
+  Object* (*pDecodeJObjectInThread)(Thread* thread, jobject obj);
+  void (*pDeliverException)(void*);
+  Method* (*pFindInterfaceMethodInCache)(Class*, uint32_t, const Method*, struct DvmDex*);
+  void* (*pFindNativeMethod)(Thread* thread);
+  int32_t (*pGet32Instance)(uint32_t, void*);
+  int64_t (*pGet64Instance)(uint32_t, void*);
+  void* (*pGetObjInstance)(uint32_t, void*);
+  int32_t (*pGet32Static)(uint32_t);
+  int64_t (*pGet64Static)(uint32_t);
+  void* (*pGetObjStatic)(uint32_t);
+  void (*pHandleFillArrayDataFromCode)(void*, void*);
+  void* (*pInitializeStaticStorage)(uint32_t, void*);
+  uint32_t (*pInstanceofNonTrivialFromCode)(const Class*, const Class*);
+  void (*pInvokeInterfaceTrampoline)(uint32_t, void*);
+  void (*pInvokeInterfaceTrampolineWithAccessCheck)(uint32_t, void*);
+  void (*pInvokeSuperTrampolineWithAccessCheck)(uint32_t, void*);
+  void (*pInvokeVirtualTrampolineWithAccessCheck)(uint32_t, void*);
+  void* (*pInitializeTypeFromCode)(uint32_t, void*);
+  void* (*pInitializeTypeAndVerifyAccessFromCode)(uint32_t, void*);
+  void (*pLockObjectFromCode)(void*);
+  void (*pObjectInit)(void*);
+  void* (*pResolveMethodFromCode)(void*, uint32_t, bool);
+  void* (*pResolveStringFromCode)(void*, uint32_t);
+  int (*pSet32Instance)(uint32_t, void*, int32_t);  // field_idx, obj, src
+  int (*pSet64Instance)(uint32_t, void*, int64_t);
+  int (*pSetObjInstance)(uint32_t, void*, void*);
+  int (*pSet32Static)(uint32_t, int32_t);
+  int (*pSet64Static)(uint32_t, int64_t);
+  int (*pSetObjStatic)(uint32_t, void*);
+  void (*pThrowStackOverflowFromCode)(void*);
+  void (*pThrowNullPointerFromCode)();
+  void (*pThrowArrayBoundsFromCode)(int32_t, int32_t);
+  void (*pThrowDivZeroFromCode)();
+  void (*pThrowVerificationErrorFromCode)(int32_t, int32_t);
+  void (*pThrowNegArraySizeFromCode)(int32_t);
+  void (*pThrowNoSuchMethodFromCode)(int32_t);
+  void (*pThrowAbstractMethodErrorFromCode)(Method* method, Thread* thread, Method** sp);
+  void (*pUnlockObjectFromCode)(void*);
+  void* (*pUnresolvedDirectMethodTrampolineFromCode)(int32_t, Method**, Thread*,
+                                                     Runtime::TrampolineType);
+  void (*pUpdateDebuggerFromCode)(void*, void*, int32_t, void*);
+
+ private:
   DISALLOW_COPY_AND_ASSIGN(Thread);
 };