Codegen for invoke-super, invoke-interface

Completed fast & slow paths for invoke-super and the single path for invoke-interface.  Added test
for invoke-super, but invoke-interface is untested (needs runtime-support routines to be fleshed
out).

Some trickiness in the invoke-interface code-generation.  Because they are going to be
glacially slow anyway, inline code has been minimized and all interesting work pushed
off to TODO runtime support routines.  However, we can't simultaneously pass the arguments
needed by the final destination and the arguments needed by the runtime lookup helpers.  So,
I've added a trampoline to save the target args, load the args needed by the helpers, call
the lookup routines, restore the final target arguments and continue on the journey.

More detailed comments in the code.

Change-Id: Ice2343798a91a37da982811fd1c6384f584a3c0b
diff --git a/src/compiler/Frontend.cc b/src/compiler/Frontend.cc
index d6f891b..e9f8a3a 100644
--- a/src/compiler/Frontend.cc
+++ b/src/compiler/Frontend.cc
@@ -705,6 +705,9 @@
         if (PrettyMethod(method).find("virtualCall") != std::string::npos) {
             compiling = true;
         }
+        if (PrettyMethod(method).find("tryThing") != std::string::npos) {
+            compiling = true;
+        }
         if (PrettyMethod(method).find("getFoo") != std::string::npos) {
             compiling = true;
         }
@@ -714,6 +717,9 @@
         if (PrettyMethod(method).find("IntMath.<init>") != std::string::npos) {
             compiling = true;
         }
+        if (PrettyMethod(method).find("IntMathBase.<init>") != std::string::npos) {
+            compiling = true;
+        }
         if (PrettyMethod(method).find("java.lang.Object.<init>") != std::string::npos) {
             compiling = true;
         }
diff --git a/src/compiler/codegen/arm/MethodCodegenDriver.cc b/src/compiler/codegen/arm/MethodCodegenDriver.cc
index d95b870..eb9296b 100644
--- a/src/compiler/codegen/arm/MethodCodegenDriver.cc
+++ b/src/compiler/codegen/arm/MethodCodegenDriver.cc
@@ -478,7 +478,7 @@
             skipTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
             skipTarget->defMask = ENCODE_ALL;
             skipBranch->generic.target = (LIR*)skipTarget;
-            // Get base_method->method_index [usr rLR, set r12]
+            // Get base_method->method_index [usr rLR, set r0]
             loadBaseDisp(cUnit, mir, rLR,
                          Method::GetMethodIndexOffset().Int32Value(), r0,
                          kUnsignedHalf, INVALID_SREG);
@@ -528,62 +528,25 @@
     return callState;
 }
 
-/*
- * Interleave launch code for INVOKE_INTERFACE.  The target is
- * identified using artFindInterfaceMethodInCache(class, ref, method, dex)
- * Note that we'll have to reload "this" following the helper call.
- *
- * FIXME: do we need to have artFindInterfaceMethodInCache return
- * a NULL if not found so we can throw exception here?  Otherwise,
- * may need to pass some additional info to allow the helper function
- * to throw on its own.
- */
+// Interleave launch code for INVOKE_INTERFACE.
 static int nextInterfaceCallInsn(CompilationUnit* cUnit, MIR* mir,
                                  DecodedInstruction* dInsn, int state,
                                  ArmLIR* rollback)
 {
-    UNIMPLEMENTED(FATAL) << "Need findInterfaceMethodInCache";
-#if 0
-    RegLocation rlArg;
     switch(state) {
-        case 0:
-            // Load "this" [set r12]
-            rlArg = oatGetSrc(cUnit, mir, 0);
-            loadValueDirectFixed(cUnit, rlArg, r12);
-            // Get the current Method* [set arg2]
-            loadCurrMethodDirect(cUnit, r2);
-            // Is "this" null? [use r12]
-            genNullCheck(cUnit, oatSSASrc(mir,0), r12,
-                           mir->offset, NULL);
-            // Get curMethod->clazz [set arg3]
-            loadBaseDisp(cUnit, mir, r2, OFFSETOF_MEMBER(Method, clazz),
-                         r3, kWord, INVALID_SREG);
-            // Load this->class [usr r12, set arg0]
-            loadBaseDisp(cUnit, mir, r12, OFFSETOF_MEMBER(Class, clazz),
-                         r3, kWord, INVALID_SREG);
-            // Load address of helper function
-            loadBaseDisp(cUnit, mir, rSELF,
-                      OFFSETOF_MEMBER(Thread, pArtFindInterfaceMethodInCache),
-                      rLR, kWord, INVALID_SREG);
-            // Get dvmDex
-            loadBaseDisp(cUnit, mir, r3, OFFSETOF_MEMBER(Class, pDvmDex),
-                         r3, kWord, INVALID_SREG);
-            // Load ref [set arg1]
-            loadConstant(cUnit, r1, dInsn->vB);
-            // Call out to helper, target Method returned in ret0
-            newLIR1(cUnit, kThumbBlxR, rLR);
+        case 0: // Load trampoline target
+            loadWordDisp(cUnit, rSELF,
+                         OFFSETOF_MEMBER(Thread, pInvokeInterfaceTrampoline),
+                         rLR);
+            // Load r0 with method index
+            loadConstant(cUnit, r0, dInsn->vB);
             break;
-        case 1: // Get the target compiled code address [use r0, set rLR]
-            loadBaseDisp(cUnit, mir, r0, OFFSETOF_MEMBER(Method, compiledInsns),
-                         rLR, kWord, INVALID_SREG);
         default:
             return -1;
     }
-#endif
     return state + 1;
 }
 
-
 /*
  * Interleave launch code for INVOKE_SUPER.  See comments
  * for nextVCallIns.
@@ -592,49 +555,132 @@
                              DecodedInstruction* dInsn, int state,
                              ArmLIR* rollback)
 {
-    UNIMPLEMENTED(FATAL) << "Need INVOKE_SUPER implementation";
-#if 0
+    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.
+     */
+    Method* baseMethod = cUnit->method->GetDexCacheResolvedMethods()->
+        Get(dInsn->vB);
+    CHECK(baseMethod != NULL);
+    Class* superClass = cUnit->method->GetDeclaringClass()->GetSuperClass();
+    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);
     switch(state) {
-        case 0:
-            // Get the current Method* [set r0]
+        case 0: // Get current Method* [set r0]
             loadCurrMethodDirect(cUnit, r0);
             // Load "this" [set r1]
             rlArg = oatGetSrc(cUnit, mir, 0);
             loadValueDirectFixed(cUnit, rlArg, r1);
-            // Get method->clazz [use r0, set r12]
-            loadBaseDisp(cUnit, mir, r0, OFFSETOF_MEMBER(Method, clazz),
-                         r12, kWord, INVALID_SREG);
-            // Get pResmethods [use r0, set rLR]
-            loadBaseDisp(cUnit, mir, r0, OFFSETOF_MEMBER(Method, pResMethods),
-                         rLR, kWord, INVALID_SREG);
-            // Get clazz->super [use r12, set r12]
-            loadBaseDisp(cUnit, mir, r12, OFFSETOF_MEMBER(Class, super),
-                         r12, kWord, INVALID_SREG);
-            // Get base method [use rLR, set r0]
-            loadBaseDisp(cUnit, mir, rLR, dInsn->vB * 4, r0,
-                         kWord, INVALID_SREG);
+            // Get method->declaring_class_ [use r0, set rLR]
+            loadWordDisp(cUnit, r0, Method::DeclaringClassOffset().Int32Value(),
+                         rLR);
             // Is "this" null? [use r1]
             genNullCheck(cUnit, oatSSASrc(mir,0), r1,
                            mir->offset, NULL);
-            // Get methodIndex [use r0, set rLR]
-            loadBaseDisp(cUnit, mir, r0, OFFSETOF_MEMBER(Method, methodIndex),
-                         rLR, kUnsignedHalf, INVALID_SREG);
-            // Get vtableCount [use r12, set r0]
-            loadBaseDisp(cUnit, mir, r12,
-                         OFFSETOF_MEMBER(Class, vtableCount),
-                         r0, kWord, INVALID_SREG);
-            // Compare method index w/ vtable count [use r12, use rLR]
-            genRegRegCheck(cUnit, kArmCondGe, rLR, r0, mir->offset, NULL);
-            // get target Method* [use rLR, use r12, set r0]
-            loadBaseIndexed(cUnit, r0, r12, rLR, 2, kWord);
-        case 1: // Get the target compiled code address [use r0, set rLR]
-            loadBaseDisp(cUnit, mir, r0, OFFSETOF_MEMBER(Method, compiledInsns),
-                         rLR, kWord, INVALID_SREG);
+            break;
+        case 1: // Get method->declaring_class_->super_class [usr rLR, set rLR]
+            loadWordDisp(cUnit, rLR, Class::SuperClassOffset().Int32Value(),
+                         rLR);
+            break;
+        case 2: // Get ...->super_class_->vtable [u/s rLR]
+            loadWordDisp(cUnit, rLR, Class::VTableOffset().Int32Value(), rLR);
+            break;
+        case 3: // Get target method [use rLR, set r0]
+            loadWordDisp(cUnit, rLR, (target_idx * 4) +
+                         art::Array::DataOffset().Int32Value(), r0);
+            break;
+        case 4: // Get the target compiled code address [uses r0, sets rLR]
+            loadWordDisp(cUnit, r0, Method::GetCodeOffset().Int32Value(), rLR);
+            break;
         default:
             return -1;
     }
-#endif
+    return state + 1;
+}
+
+/* Slow-path version of nextSuperCallInsn */
+static int nextSuperCallInsnSP(CompilationUnit* cUnit, MIR* mir,
+                               DecodedInstruction* dInsn, int state,
+                               ArmLIR* rollback)
+{
+    DCHECK(rollback != NULL);
+    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.
+     */
+    switch(state) {
+        case 0:  // Get the current Method* [sets r0]
+            loadCurrMethodDirect(cUnit, r0);
+            break;
+        case 1: // Get method->dex_cache_resolved_methods_ [usr 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) +
+                         art::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);
+            newLIR1(cUnit, kThumbBlxR, 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);
+        case 4: // Get method->declaring_class_->super_class [usr r0, set r0]
+            loadWordDisp(cUnit, r0, Class::SuperClassOffset().Int32Value(), r0);
+            break;
+        case 5: // Get ...->super_class_->vtable [u/s r0]
+            loadWordDisp(cUnit, r0, Class::VTableOffset().Int32Value(), r0);
+            // In load shadow fold vtable_ object header size into method_index_
+            opRegImm(cUnit, kOpAdd, rLR,
+                     art::Array::DataOffset().Int32Value() / 4);
+            if (!(mir->OptimizationFlags & MIR_IGNORE_RANGE_CHECK)) {
+                // Range check, throw NSM on failure
+                tReg = oatAllocTemp(cUnit);
+                loadWordDisp(cUnit, r0, art::Array::LengthOffset().Int32Value(),
+                             tReg);
+                genBoundsCheck(cUnit, tReg, rLR, mir->offset, NULL);
+                oatFreeTemp(cUnit, tReg);
+            }
+            // Get target Method*
+            loadBaseIndexed(cUnit, r0, r0, rLR, 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;
 }
 
@@ -822,6 +868,10 @@
     newLIR1(cUnit, kThumbBlxR, rLR);
 }
 
+/*
+ * 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)
 {
     DecodedInstruction* dInsn = &mir->dalvikInsn;
@@ -847,13 +897,41 @@
     DecodedInstruction* dInsn = &mir->dalvikInsn;
     int callState = 0;
     ArmLIR* nullCk;
+    ArmLIR* rollback;
+    Method* baseMethod = cUnit->method->GetDexCacheResolvedMethods()->
+        Get(dInsn->vB);
+    NextCallInsn nextCallInsn;
+    bool fastPath = true;
+    if (baseMethod == NULL) {
+        fastPath = false;
+    } else {
+        Class* superClass = cUnit->method->GetDeclaringClass()->GetSuperClass();
+        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;
+    }
 // TODO - redundantly loading arg0/r1 ("this")
     if (mir->dalvikInsn.opcode == OP_INVOKE_SUPER)
         callState = genDalvikArgsNoRange(cUnit, mir, dInsn, callState, &nullCk,
-                                         false, nextSuperCallInsn, NULL);
+                                         false, nextCallInsn, rollback);
     else
         callState = genDalvikArgsRange(cUnit, mir, dInsn, callState, &nullCk,
-                                       nextSuperCallInsn, NULL);
+                                       nextCallInsn, rollback);
     // Finish up any of the call sequence not interleaved in arg loading
     while (callState >= 0) {
         callState = nextSuperCallInsn(cUnit, mir, dInsn, callState, NULL);
diff --git a/src/compiler_test.cc b/src/compiler_test.cc
index c144f7c..656c7d4 100644
--- a/src/compiler_test.cc
+++ b/src/compiler_test.cc
@@ -103,6 +103,19 @@
                         10);
 }
 
+// TODO: Need invoke-interface test
+
+TEST_F(CompilerTest, SuperTest) {
+  CompileDirectMethod(NULL, "java.lang.Object", "<init>", "()V");
+  const ClassLoader* class_loader = LoadDex("IntMath");
+  CompileDirectMethod(class_loader, "IntMathBase", "<init>", "()V");
+  CompileVirtualMethod(class_loader, "IntMathBase", "tryThing", "()I");
+  CompileDirectMethod(class_loader, "IntMath", "<init>", "()V");
+  CompileVirtualMethod(class_loader, "IntMath", "tryThing", "()I");
+  AssertStaticIntMethod(class_loader, "IntMath", "superTest", "(I)I", 4175,
+                        4141);
+}
+
 TEST_F(CompilerTest, ConstStringTest) {
   AssertStaticIntMethod(LoadDex("IntMath"), "IntMath", "constStringTest",
                                 "(I)I", 2468, 1234);
@@ -231,6 +244,7 @@
 TEST_F(CompilerTest, VirtualCall) {
   CompileDirectMethod(NULL, "java.lang.Object", "<init>", "()V");
   const ClassLoader* class_loader = LoadDex("IntMath");
+  CompileDirectMethod(class_loader, "IntMathBase", "<init>", "()V");
   CompileDirectMethod(class_loader, "IntMath", "<init>", "()V");
   CompileVirtualMethod(class_loader, "IntMath", "virtualCall", "(I)I");
   AssertStaticIntMethod(class_loader, "IntMath", "staticCall", "(I)I", 6,
@@ -240,6 +254,7 @@
 TEST_F(CompilerTest, TestIGetPut) {
   CompileDirectMethod(NULL, "java.lang.Object", "<init>", "()V");
   const ClassLoader* class_loader = LoadDex("IntMath");
+  CompileDirectMethod(class_loader, "IntMathBase", "<init>", "()V");
   CompileDirectMethod(class_loader, "IntMath", "<init>", "(I)V");
   CompileDirectMethod(class_loader, "IntMath", "<init>", "()V");
   CompileVirtualMethod(class_loader, "IntMath", "getFoo", "()I");
@@ -248,4 +263,5 @@
                         111);
 }
 
+
 }  // namespace art
diff --git a/src/object.h b/src/object.h
index 5857571..a0f3e38 100644
--- a/src/object.h
+++ b/src/object.h
@@ -1512,6 +1512,10 @@
         OFFSET_OF_OBJECT_MEMBER(Class, super_class_), false);
   }
 
+  static MemberOffset SuperClassOffset() {
+    return MemberOffset(OFFSETOF_MEMBER(Class, super_class_));
+  }
+
   void SetSuperClass(Class *new_super_class) {
     // super class is assigned once, except during class linker initialization
     Class* old_super_class = GetFieldObject<Class*>(
diff --git a/src/runtime_support.S b/src/runtime_support.S
index 46c2dd3..fe40cf2 100644
--- a/src/runtime_support.S
+++ b/src/runtime_support.S
@@ -1,6 +1,44 @@
 #if defined(__arm__)
 
     .balign 4
+
+    .global art_invoke_interface_trampoline
+    .extern artFindInterfaceMethodInCache
+    .extern artFailedInvokeInterface
+art_invoke_interface_trampoline:
+    /*
+     * 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 wrapper will
+     * save arg1-arg3, load the caller's Method*, align the stack and
+     * call the helper artFindInterfaceMethodInCache(idx, this, method);
+     * NOTE: "this" is first visable argument of the target, and so can be
+     * found in arg1/r1.
+     *
+     * artFindInterfaceMethodInCache 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, artFindInterfaceMethodInCache will return
+     * NULL/NULL.  This is somewhat different than the usual
+     * mechanism of helper routines performing the unwind & throw.
+     * The reason is that this trampoline is not unwindable.  In the
+     * event artFindInterfaceMethodInCache fails to resolve, the wrapper
+     * will prepare an unwindable environment and jump to another helper
+     * to do unwind/throw.
+     *
+     * On success this wrapper will restore arguments and *jump* to the
+     * target, leaving the lr pointing back to the original caller.
+     */
+    stmdb  sp!, {r1, r2, r3, lr}
+    ldr    r2, [sp, #16]                 @ load caller's Method*
+    bl     artFindInterfaceMethodInCache @ (method_idx, this, callerMethod)
+    mov    r12, r1                       @ save r0->code_
+    ldmia  sp!, {r1, r2, r3, lr}         @ restore arguments
+    cmp    r0, #0                        @ did we find the target?
+    bxne   r12                           @ tail call to target if so
+    b      artFailedInvokeInterface      @ Will appear as if called directly
+
     .global art_shl_long
 art_shl_long:
     /*
diff --git a/src/runtime_support.h b/src/runtime_support.h
index 8d65aaa..23126d7 100644
--- a/src/runtime_support.h
+++ b/src/runtime_support.h
@@ -8,6 +8,7 @@
   extern "C" uint64_t art_shl_long(uint64_t, uint32_t);
   extern "C" uint64_t art_shr_long(uint64_t, uint32_t);
   extern "C" uint64_t art_ushr_long(uint64_t, uint32_t);
+  extern "C" void art_invoke_interface_trampoline(void*, void*, void*, void*);
 
   /* Conversions */
   extern "C" float __aeabi_i2f(int op1);             // OP_INT_TO_FLOAT
diff --git a/src/thread.cc b/src/thread.cc
index aa24bec..31786c9 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -34,6 +34,42 @@
 
 pthread_key_t Thread::pthread_key_self_;
 
+// Temporary debugging hook for compiler.
+static void DebugMe(Method* method, uint32_t info) {
+    LOG(INFO) << "DebugMe";
+    if (method != NULL)
+        LOG(INFO) << PrettyMethod(method);
+    LOG(INFO) << "Info: " << info;
+}
+
+/*
+ * TODO: placeholder for a method that can be called by the
+ * invoke-interface trampoline to unwind and handle exception.  The
+ * trampoline will arrange it so that the caller appears to be the
+ * callsite of the failed invoke-interface.  See comments in
+ * compiler/runtime_support.S
+ */
+extern "C" void artFailedInvokeInterface()
+{
+    UNIMPLEMENTED(FATAL) << "Unimplemented exception throw";
+}
+
+// TODO: placeholder.  See comments in compiler/runtime_support.S
+extern "C" uint64_t artFindInterfaceMethodInCache(uint32_t method_idx,
+     Object* this_object , Method* caller_method)
+{
+    /*
+     * Note: this_object has not yet been null-checked.  To match
+     * the old-world state, nullcheck this_object and load
+     * Class* this_class = this_object->GetClass().
+     * See comments and possible thrown exceptions in old-world
+     * Interp.cpp:dvmInterpFindInterfaceMethod, and complete with
+     * new-world FindVirtualMethodForInterface.
+     */
+    UNIMPLEMENTED(FATAL) << "Unimplemented invoke interface";
+    return 0LL;
+}
+
 // TODO: placeholder.  This is what generated code will call to throw
 static void ThrowException(Thread* thread, Throwable* exception) {
     /*
@@ -98,6 +134,7 @@
   pD2l = D2L;
   pLdivmod = __aeabi_ldivmod;
   pLmul = __aeabi_lmul;
+  pInvokeInterfaceTrampoline = art_invoke_interface_trampoline;
 #endif
   pAllocFromCode = Array::AllocFromCode;
   pAllocObjectFromCode = Class::AllocObjectFromCode;
@@ -113,15 +150,12 @@
   pThrowException = ThrowException;
   pInitializeTypeFromCode = InitializeTypeFromCode;
   pResolveMethodFromCode = ResolveMethodFromCode;
+  pDebugMe = DebugMe;
 #if 0
 bool (Thread::*pUnlockObject)(Thread*, Object*);
 int (Thread::*pInstanceofNonTrivialFromCode)(const Class*, const Class*);
-Method* (Thread::*pFindInterfaceMethodInCache)(Class*, uint32_t, const Method*, DvmDex*);
 bool (Thread::*pUnlockObjectFromCode)(Thread*, Object*);
 void (Thread::*pLockObjectFromCode)(Thread*, Object*);
-Object* (Thread::*pAllocObjectFromCode)(Class*, int);
-void (Thread::*pThrowException)(Thread*, Object*);
-bool (Thread::*pHandleFillArrayDataFromCode)(Array*, const uint16_t*);
 #endif
 }
 
diff --git a/src/thread.h b/src/thread.h
index 8a297d6..fc523a3 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -174,6 +174,7 @@
   static const size_t kDefaultStackSize = 64 * 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);
@@ -219,6 +220,7 @@
   void (*pHandleFillArrayDataFromCode)(Array*, const uint16_t*);
   Class* (*pInitializeTypeFromCode)(uint32_t, Method*);
   void (*pResolveMethodFromCode)(Method*, uint32_t);
+  void (*pInvokeInterfaceTrampoline)(void*, void*, void*, void*);
 
   class StackVisitor {
    public: