Complete direct, static & virtual invoke

Reworked direct/static invoke to use code_and_method_ structure.  Removed
slow/fast path distinction for direct/static (because there is only
a single path).  Added fast and reworked slow path for virtual invokes.

Change-Id: I037aee26b63244d03ac2bd8720d8c55e256b9b4d
diff --git a/src/compiler/codegen/arm/MethodCodegenDriver.cc b/src/compiler/codegen/arm/MethodCodegenDriver.cc
index 8caf0d1..d95b870 100644
--- a/src/compiler/codegen/arm/MethodCodegenDriver.cc
+++ b/src/compiler/codegen/arm/MethodCodegenDriver.cc
@@ -358,76 +358,33 @@
     }
 }
 
-typedef int (*NextCallInsn)(CompilationUnit*, MIR*, DecodedInstruction*, int);
+typedef int (*NextCallInsn)(CompilationUnit*, MIR*, DecodedInstruction*, int,
+                            ArmLIR*);
 
 /*
  * 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)
+                        DecodedInstruction* dInsn, int state,
+                        ArmLIR* rollback)
 {
-    UNIMPLEMENTED(FATAL) << "Update with new cache model";
-#if 0
+    DCHECK(rollback == NULL);
+    uint32_t idx = dInsn->vB;
     switch(state) {
         case 0:  // Get the current Method* [sets r0]
             loadCurrMethodDirect(cUnit, r0);
             break;
-        case 1:  // Get the pResMethods pointer [uses r0, sets r0]
-            UNIMPLEMENTED(FATAL) << "Update with new cache";
-            loadBaseDisp(cUnit, mir, r0, OFFSETOF_MEMBER(Method, pResMethods),
-                         r0, kWord, INVALID_SREG);
+        case 1:  // Get method->code_and_direct_methods_
+            loadWordDisp(cUnit, r0,
+                Method::GetDexCacheCodeAndDirectMethodsOffset().Int32Value(),
+                r0);
             break;
-        case 2: // Get the target Method* [uses r0, sets r0]
-            loadBaseDisp(cUnit, mir, r0, dInsn->vB * 4, r0,
-                         kWord, INVALID_SREG);
-            break;
-        case 3: // Get the target compiled code address [uses r0, sets rLR]
-            loadBaseDisp(cUnit, mir, r0,
-                         OFFSETOF_MEMBER(Method, compiledInsns), rLR,
-                         kWord, INVALID_SREG);
-            break;
-        default:
-            return -1;
-    }
-#endif
-    return state + 1;
-}
-
-// Slow path static & direct invoke launch sequence
-static int nextSDCallInsnSP(CompilationUnit* cUnit, MIR* mir,
-                            DecodedInstruction* dInsn, int state)
-{
-    switch(state) {
-        case 0:  // Get the current Method* [sets r0]
-            loadCurrMethodDirect(cUnit, r0);
-            break;
-        case 1:  // Get the current Method->DeclaringClass() [sets r0]
-            loadBaseDisp(cUnit, mir, r0,
-                         art::Method::DeclaringClassOffset().Int32Value(),
-                         r0, kWord, INVALID_SREG);
-            break;
-        case 2:  // Method->DeclaringClass()->GetDexCache() [sets r0]
-            loadBaseDisp(cUnit, mir, r0,
-                         art::Class::DexCacheOffset().Int32Value(), r0, kWord,
-                         INVALID_SREG);
-            break;
-        case 3:  // Method->DeclaringClass()->GetDexCache()->methodsObjectArr
-            loadBaseDisp(cUnit, mir, r0,
-                         art::DexCache::ResolvedMethodsOffset().Int32Value(), r0,
-                         kWord, INVALID_SREG);
-            break;
-        case 4: // Skip past the object header
-            opRegImm(cUnit, kOpAdd, r0, art::Array::DataOffset().Int32Value());
-            break;
-        case 5: // Get the target Method* [uses r0, sets r0]
-            loadBaseDisp(cUnit, mir, r0, dInsn->vB * 4, r0,
-                         kWord, INVALID_SREG);
-            break;
-        case 6: // Get the target compiled code address [uses r0, sets rLR]
-            loadBaseDisp(cUnit, mir, r0,
-                         art::Method::GetCodeOffset().Int32Value(), rLR,
-                         kWord, INVALID_SREG);
+        case 2:  // Grab target method* and target code_
+            loadWordDisp(cUnit, r0,
+                art::CodeAndDirectMethods::CodeOffsetInBytes(idx), rLR);
+            loadWordDisp(cUnit, r0,
+                art::CodeAndDirectMethods::MethodOffsetInBytes(idx), r0);
             break;
         default:
             return -1;
@@ -443,111 +400,110 @@
  * r1 here rather than the standard loadArgRegs.
  */
 static int nextVCallInsn(CompilationUnit* cUnit, MIR* mir,
-                        DecodedInstruction* dInsn, int state)
+                        DecodedInstruction* dInsn, int state,
+                        ArmLIR* rollback)
 {
-    UNIMPLEMENTED(FATAL) << "Update with new cache model";
-#if 0
+    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->method->GetDexCacheResolvedMethods()->
+        Get(dInsn->vB);
+    CHECK(baseMethod != NULL);
+    uint32_t target_idx = baseMethod->GetMethodIndex();
     switch(state) {
-        case 0:  // Get the current Method* [set r0]
-            loadCurrMethodDirect(cUnit, r0);
-            // Load "this" [set r1]
+        case 0:  // Get "this" [set r1]
             rlArg = oatGetSrc(cUnit, mir, 0);
             loadValueDirectFixed(cUnit, rlArg, r1);
             break;
-        case 1:  // Get the pResMethods pointer [use r0, set r12]
-            loadBaseDisp(cUnit, mir, r0, OFFSETOF_MEMBER(Method, pResMethods),
-                         r12, kWord, INVALID_SREG);
-             // Is "this" null? [use r1]
-             genNullCheck(cUnit, oatSSASrc(mir,0), r1,
-                           mir->offset, NULL);
-             break;
-        case 2: // Get the base Method* [use r12, set r0]
-            loadBaseDisp(cUnit, mir, r12, dInsn->vB * 4, r0,
-                         kWord, INVALID_SREG);
-            // get this->clazz [use r1, set rLR]
-            loadBaseDisp(cUnit, mir, r1, OFFSETOF_MEMBER(Object, clazz), rLR,
-                         kWord, INVALID_SREG);
+        case 1: // Is "this" null? [use r1]
+            genNullCheck(cUnit, oatSSASrc(mir,0), r1, mir->offset, NULL);
+            // get this->klass_ [use r1, set rLR]
+            loadWordDisp(cUnit, r1, Object::ClassOffset().Int32Value(), rLR);
             break;
-        case 3: // Get the method index [use r0, set r12]
-            loadBaseDisp(cUnit, mir, r0, OFFSETOF_MEMBER(Method, methodIndex),
-                         r12, kUnsignedHalf, INVALID_SREG);
-            // get this->clazz->vtable [use rLR, set rLR]
-            loadBaseDisp(cUnit, mir, rLR,
-                         OFFSETOF_MEMBER(Class, vtable), rLR, kWord,
-                         INVALID_SREG);
+        case 2: // Get this->klass_->vtable [usr rLR, set rLR]
+            loadWordDisp(cUnit, rLR, Class::VTableOffset().Int32Value(), rLR);
             break;
-        case 4: // get target Method* [use rLR, use r12, set r0]
-              loadBaseIndexed(cUnit, rLR, r12, r0, 2, kWord);
-              break;
-        case 5: // Get the target compiled code address [use r0, set rLR]
-            UNIMPLEMENTED(FATAL) << "Update with new cache";
-            loadBaseDisp(cUnit, mir, r0, OFFSETOF_MEMBER(Method, compiledInsns),
-                         rLR, kWord, INVALID_SREG);
+        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 sequence for virtual calls
 static int nextVCallInsnSP(CompilationUnit* cUnit, MIR* mir,
-                           DecodedInstruction* dInsn, int state)
+                           DecodedInstruction* dInsn, int state,
+                           ArmLIR* rollback)
 {
+    DCHECK(rollback != NULL);
     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 the current Method->DeclaringClass() [uses/sets r0]
-            loadBaseDisp(cUnit, mir, r0,
-                         art::Method::DeclaringClassOffset().Int32Value(),
-                         r0, kWord, INVALID_SREG);
+        case 1: // Get method->dex_cache_resolved_methods_
+            loadWordDisp(cUnit, r0,
+                Method::GetDexCacheResolvedMethodsOffset().Int32Value(), rLR);
             break;
-        case 2:  // Method->DeclaringClass()->GetDexCache() [uses/sets r0]
-            loadBaseDisp(cUnit, mir, r0,
-                         art::Class::DexCacheOffset().Int32Value(),
-                         r0, kWord,
-                         INVALID_SREG);
+        case 2: // method->dex_cache_resolved_methods_->Get(method_idx)
+            loadWordDisp(cUnit, rLR, (dInsn->vB * 4) +
+                         art::Array::DataOffset().Int32Value(), rLR);
             break;
-        case 3:  // ...()->GetDexCache()->methodsObjectArr [uses/sets r0]
-            loadBaseDisp(cUnit, mir, r0,
-                         art::DexCache::ResolvedMethodsOffset().Int32Value(),
-                         r0, kWord, INVALID_SREG);
+        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 r12]
+            loadBaseDisp(cUnit, mir, rLR,
+                         Method::GetMethodIndexOffset().Int32Value(), r0,
+                         kUnsignedHalf, INVALID_SREG);
             // Load "this" [set r1]
             rlArg = oatGetSrc(cUnit, mir, 0);
             loadValueDirectFixed(cUnit, rlArg, r1);
-            // Skip past the object header
-            opRegImm(cUnit, kOpAdd, r0, art::Array::DataOffset().Int32Value());
             break;
         case 4:
             // Is "this" null? [use r1]
             genNullCheck(cUnit, oatSSASrc(mir,0), r1, mir->offset, NULL);
             // get this->clazz [use r1, set rLR]
-            loadBaseDisp(cUnit, mir, r1, Object::ClassOffset().Int32Value(),
-                         rLR, kWord, INVALID_SREG);
-            // Get the base Method* [uses r0, sets r0]
-            loadBaseDisp(cUnit, mir, r0, dInsn->vB * 4, r0,
-                         kWord, INVALID_SREG);
-            // get this->clazz->vtable [use rLR, set rLR]
-            loadBaseDisp(cUnit, mir, rLR,
-                         Class::VTableOffset().Int32Value(), rLR, kWord,
-                         INVALID_SREG);
-            // Get the method index [use r0, set r12]
-            loadBaseDisp(cUnit, mir, r0,
-                         Method::MethodIndexOffset().Int32Value(),
-                         r12, kUnsignedHalf, INVALID_SREG);
-            // Skip past the object header
-            opRegImm(cUnit, kOpAdd, rLR, art::Array::DataOffset().Int32Value());
-            // Get target Method*
-            loadBaseIndexed(cUnit, rLR, r12, r0, 2, kWord);
+            loadWordDisp(cUnit, r1, Object::ClassOffset().Int32Value(), rLR);
             break;
-        case 5: // Get the target compiled code address [uses r0, sets rLR]
-            loadBaseDisp(cUnit, mir, r0,
-                         art::Method::GetCodeOffset().Int32Value(), rLR,
-                         kWord, INVALID_SREG);
+        case 5:
+            // get this->klass_->vtable_ [usr rLR, set rLR]
+            loadWordDisp(cUnit, rLR, Class::VTableOffset().Int32Value(), rLR);
+            DCHECK((art::Array::DataOffset().Int32Value() & 0x3) == 0);
+            // In load shadow fold vtable_ object header size into method_index_
+            opRegImm(cUnit, kOpAdd, r0,
+                     art::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;
@@ -558,7 +514,7 @@
 /* Load up to 3 arguments in r1..r3 */
 static int loadArgRegs(CompilationUnit* cUnit, MIR* mir,
                        DecodedInstruction* dInsn, int callState,
-                       int *args, NextCallInsn nextCallInsn)
+                       int *args, NextCallInsn nextCallInsn, ArmLIR* rollback)
 {
     for (int i = 0; i < 3; i++) {
         if (args[i] != INVALID_REG) {
@@ -566,7 +522,7 @@
             // Arguments are treated as a series of untyped 32-bit values.
             rlArg.wide = false;
             loadValueDirectFixed(cUnit, rlArg, r1 + i);
-            callState = nextCallInsn(cUnit, mir, dInsn, callState);
+            callState = nextCallInsn(cUnit, mir, dInsn, callState, rollback);
         }
     }
     return callState;
@@ -583,7 +539,8 @@
  * to throw on its own.
  */
 static int nextInterfaceCallInsn(CompilationUnit* cUnit, MIR* mir,
-                                 DecodedInstruction* dInsn, int state)
+                                 DecodedInstruction* dInsn, int state,
+                                 ArmLIR* rollback)
 {
     UNIMPLEMENTED(FATAL) << "Need findInterfaceMethodInCache";
 #if 0
@@ -632,7 +589,8 @@
  * for nextVCallIns.
  */
 static int nextSuperCallInsn(CompilationUnit* cUnit, MIR* mir,
-                             DecodedInstruction* dInsn, int state)
+                             DecodedInstruction* dInsn, int state,
+                             ArmLIR* rollback)
 {
     UNIMPLEMENTED(FATAL) << "Need INVOKE_SUPER implementation";
 #if 0
@@ -690,7 +648,7 @@
 static int genDalvikArgsNoRange(CompilationUnit* cUnit, MIR* mir,
                                 DecodedInstruction* dInsn, int callState,
                                 ArmLIR** pcrLabel, bool isRange,
-                                NextCallInsn nextCallInsn)
+                                NextCallInsn nextCallInsn, ArmLIR* rollback)
 {
     RegLocation rlArg;
     int registerArgs[3];
@@ -700,7 +658,7 @@
         return callState;
 
     oatLockCallTemps(cUnit);
-    callState = nextCallInsn(cUnit, mir, dInsn, callState);
+    callState = nextCallInsn(cUnit, mir, dInsn, callState, rollback);
 
     /*
      * Load frame arguments arg4 & arg5 first. Coded a little odd to
@@ -715,10 +673,10 @@
         } else {
             reg = r1;
             loadValueDirectFixed(cUnit, rlArg, r1);
-            callState = nextCallInsn(cUnit, mir, dInsn, callState);
+            callState = nextCallInsn(cUnit, mir, dInsn, callState, rollback);
         }
         storeBaseDisp(cUnit, rSP, (i + 1) * 4, reg, kWord);
-        callState = nextCallInsn(cUnit, mir, dInsn, callState);
+        callState = nextCallInsn(cUnit, mir, dInsn, callState, rollback);
     }
 
     /* Load register arguments r1..r3 */
@@ -729,7 +687,7 @@
             registerArgs[i] = INVALID_REG;
     }
     callState = loadArgRegs(cUnit, mir, dInsn, callState, registerArgs,
-                            nextCallInsn);
+                            nextCallInsn, rollback);
 
     // Load direct & need a "this" null check?
     if (pcrLabel) {
@@ -756,7 +714,8 @@
  */
 static int genDalvikArgsRange(CompilationUnit* cUnit, MIR* mir,
                               DecodedInstruction* dInsn, int callState,
-                              ArmLIR** pcrLabel, NextCallInsn nextCallInsn)
+                              ArmLIR** pcrLabel, NextCallInsn nextCallInsn,
+                              ArmLIR* rollback)
 {
     int firstArg = dInsn->vC;
     int numArgs = dInsn->vA;
@@ -764,7 +723,7 @@
     // 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);
+                                    true, nextCallInsn, rollback);
     /*
      * Make sure range list doesn't span the break between in normal
      * Dalvik vRegs and the ins.
@@ -790,13 +749,15 @@
             if (loc.location == kLocPhysReg) {  // TUNING: if dirty?
                 storeBaseDispWide(cUnit, rSP, loc.spOffset, loc.lowReg,
                                   loc.highReg);
-                callState = nextCallInsn(cUnit, mir, dInsn, callState);
+                callState = nextCallInsn(cUnit, mir, dInsn, callState,
+                                         rollback);
             }
         } else {
             loc = oatUpdateLoc(cUnit, loc);
             if (loc.location == kLocPhysReg) {  // TUNING: if dirty?
                 storeBaseDisp(cUnit, rSP, loc.spOffset, loc.lowReg, kWord);
-                callState = nextCallInsn(cUnit, mir, dInsn, callState);
+                callState = nextCallInsn(cUnit, mir, dInsn, callState,
+                                         rollback);
             }
         }
     }
@@ -813,69 +774,50 @@
     } else {
         // Use vldm/vstm pair using r3 as a temp
         int regsLeft = std::min(numArgs - 3, 16);
-        callState = nextCallInsn(cUnit, mir, dInsn, callState);
+        callState = nextCallInsn(cUnit, mir, dInsn, callState, rollback);
         opRegRegImm(cUnit, kOpAdd, r3, rSP, startOffset);
         newLIR3(cUnit, kThumb2Vldms, r3, fr0, regsLeft);
-        callState = nextCallInsn(cUnit, mir, dInsn, callState);
+        callState = nextCallInsn(cUnit, mir, dInsn, callState, rollback);
         opRegRegImm(cUnit, kOpAdd, r3, rSP, 4 /* Method* */ + (3 * 4));
-        callState = nextCallInsn(cUnit, mir, dInsn, callState);
+        callState = nextCallInsn(cUnit, mir, dInsn, callState, rollback);
         newLIR3(cUnit, kThumb2Vstms, r3, fr0, regsLeft);
-        callState = nextCallInsn(cUnit, mir, dInsn, callState);
+        callState = nextCallInsn(cUnit, mir, dInsn, callState, rollback);
     }
 
     // Handle the 1st 3 in r1, r2 & r3
     for (unsigned int i = 0; i < dInsn->vA && i < 3; i++) {
         RegLocation loc = oatGetSrc(cUnit, mir, firstArg + i);
         loadValueDirectFixed(cUnit, loc, r1 + i);
-        callState = nextCallInsn(cUnit, mir, dInsn, callState);
+        callState = nextCallInsn(cUnit, mir, dInsn, callState, rollback);
     }
 
     // Finally, deal with the register arguments
     // We'll be using fixed registers here
     oatLockCallTemps(cUnit);
-    callState = nextCallInsn(cUnit, mir, dInsn, callState);
+    callState = nextCallInsn(cUnit, mir, dInsn, callState, rollback);
     return callState;
 }
 
-static void genInvokeStatic(CompilationUnit* cUnit, MIR* mir)
-{
-    DecodedInstruction* dInsn = &mir->dalvikInsn;
-    int callState = 0;
-    int fastPath = false;  // TODO: set based on resolution results
-
-    NextCallInsn nextCallInsn = fastPath ? nextSDCallInsn : nextSDCallInsnSP;
-
-    if (mir->dalvikInsn.opcode == OP_INVOKE_STATIC) {
-        callState = genDalvikArgsNoRange(cUnit, mir, dInsn, callState, NULL,
-                                         false, nextCallInsn);
-    } else {
-        callState = genDalvikArgsRange(cUnit, mir, dInsn, callState, NULL,
-                                       nextCallInsn);
-    }
-    // Finish up any of the call sequence not interleaved in arg loading
-    while (callState >= 0) {
-        callState = nextCallInsn(cUnit, mir, dInsn, callState);
-    }
-    newLIR1(cUnit, kThumbBlxR, rLR);
-}
-
-static void genInvokeDirect(CompilationUnit* cUnit, MIR* mir)
+static void genInvokeStaticDirect(CompilationUnit* cUnit, MIR* mir,
+                                  bool direct, bool range)
 {
     DecodedInstruction* dInsn = &mir->dalvikInsn;
     int callState = 0;
     ArmLIR* nullCk;
-    int fastPath = false;  // TODO: set based on resolution results
+    ArmLIR** pNullCk = direct ? &nullCk : NULL;
 
-    NextCallInsn nextCallInsn = fastPath ? nextSDCallInsn : nextSDCallInsnSP;
-    if (mir->dalvikInsn.opcode == OP_INVOKE_DIRECT)
-        callState = genDalvikArgsNoRange(cUnit, mir, dInsn, callState, &nullCk,
-                                         false, nextCallInsn);
-    else
-        callState = genDalvikArgsRange(cUnit, mir, dInsn, callState, &nullCk,
-                                       nextCallInsn);
+    NextCallInsn nextCallInsn = nextSDCallInsn;
+
+    if (range) {
+        callState = genDalvikArgsRange(cUnit, mir, dInsn, callState, pNullCk,
+                                       nextCallInsn, NULL);
+    } else {
+        callState = genDalvikArgsNoRange(cUnit, mir, dInsn, callState, pNullCk,
+                                         false, nextCallInsn, NULL);
+    }
     // Finish up any of the call sequence not interleaved in arg loading
     while (callState >= 0) {
-        callState = nextCallInsn(cUnit, mir, dInsn, callState);
+        callState = nextCallInsn(cUnit, mir, dInsn, callState, NULL);
     }
     newLIR1(cUnit, kThumbBlxR, rLR);
 }
@@ -886,16 +828,16 @@
     int callState = 0;
     ArmLIR* nullCk;
     /* Note: must call nextInterfaceCallInsn() prior to 1st argument load */
-    callState = nextInterfaceCallInsn(cUnit, mir, dInsn, callState);
+    callState = nextInterfaceCallInsn(cUnit, mir, dInsn, callState, NULL);
     if (mir->dalvikInsn.opcode == OP_INVOKE_INTERFACE)
         callState = genDalvikArgsNoRange(cUnit, mir, dInsn, callState, &nullCk,
-                                         false, nextInterfaceCallInsn);
+                                         false, nextInterfaceCallInsn, NULL);
     else
         callState = genDalvikArgsRange(cUnit, mir, dInsn, callState, &nullCk,
-                                       nextInterfaceCallInsn);
+                                       nextInterfaceCallInsn, NULL);
     // Finish up any of the call sequence not interleaved in arg loading
     while (callState >= 0) {
-        callState = nextInterfaceCallInsn(cUnit, mir, dInsn, callState);
+        callState = nextInterfaceCallInsn(cUnit, mir, dInsn, callState, NULL);
     }
     newLIR1(cUnit, kThumbBlxR, rLR);
 }
@@ -905,16 +847,16 @@
     DecodedInstruction* dInsn = &mir->dalvikInsn;
     int callState = 0;
     ArmLIR* nullCk;
-// FIXME - redundantly loading arg0/r1 ("this")
+// TODO - redundantly loading arg0/r1 ("this")
     if (mir->dalvikInsn.opcode == OP_INVOKE_SUPER)
         callState = genDalvikArgsNoRange(cUnit, mir, dInsn, callState, &nullCk,
-                                         false, nextSuperCallInsn);
+                                         false, nextSuperCallInsn, NULL);
     else
         callState = genDalvikArgsRange(cUnit, mir, dInsn, callState, &nullCk,
-                                       nextSuperCallInsn);
+                                       nextSuperCallInsn, NULL);
     // Finish up any of the call sequence not interleaved in arg loading
     while (callState >= 0) {
-        callState = nextSuperCallInsn(cUnit, mir, dInsn, callState);
+        callState = nextSuperCallInsn(cUnit, mir, dInsn, callState, NULL);
     }
     newLIR1(cUnit, kThumbBlxR, rLR);
 }
@@ -924,19 +866,32 @@
     DecodedInstruction* dInsn = &mir->dalvikInsn;
     int callState = 0;
     ArmLIR* nullCk;
-    int fastPath = false;  // TODO: set based on resolution results
+    ArmLIR* rollback;
+    Method* method = cUnit->method->GetDexCacheResolvedMethods()->
+        Get(dInsn->vB);
+    NextCallInsn nextCallInsn;
 
-    NextCallInsn nextCallInsn = fastPath ? nextVCallInsn : nextVCallInsnSP;
+    if (method == NULL) {
+        // Slow path
+        nextCallInsn = nextVCallInsnSP;
+        // If we need a slow-path callout, we'll restart here
+        rollback = newLIR0(cUnit, kArmPseudoTargetLabel);
+        rollback->defMask = -1;
+    } else {
+        // Fast path
+        nextCallInsn = nextVCallInsn;
+        rollback = NULL;
+    }
     // TODO - redundantly loading arg0/r1 ("this")
     if (mir->dalvikInsn.opcode == OP_INVOKE_VIRTUAL)
         callState = genDalvikArgsNoRange(cUnit, mir, dInsn, callState, &nullCk,
-                                         false, nextCallInsn);
+                                         false, nextCallInsn, rollback);
     else
         callState = genDalvikArgsRange(cUnit, mir, dInsn, callState, &nullCk,
-                                       nextCallInsn);
+                                       nextCallInsn, rollback);
     // Finish up any of the call sequence not interleaved in arg loading
     while (callState >= 0) {
-        callState = nextCallInsn(cUnit, mir, dInsn, callState);
+        callState = nextCallInsn(cUnit, mir, dInsn, callState, rollback);
     }
     newLIR1(cUnit, kThumbBlxR, rLR);
 }
@@ -1396,13 +1351,21 @@
             break;
 
         case OP_INVOKE_STATIC_RANGE:
+            genInvokeStaticDirect(cUnit, mir, false /*direct*/,
+                                  true /*range*/);
+            break;
         case OP_INVOKE_STATIC:
-            genInvokeStatic(cUnit, mir);
+            genInvokeStaticDirect(cUnit, mir, false /*direct*/,
+                                  false /*range*/);
             break;
 
         case OP_INVOKE_DIRECT:
+            genInvokeStaticDirect(cUnit, mir, true /*direct*/,
+                                  false /*range*/);
+            break;
         case OP_INVOKE_DIRECT_RANGE:
-            genInvokeDirect(cUnit, mir);
+            genInvokeStaticDirect(cUnit, mir, true /*direct*/,
+                                  true /*range*/);
             break;
 
         case OP_INVOKE_VIRTUAL:
@@ -1653,8 +1616,7 @@
         RegLocation loc = cUnit->regLocation[startLoc + 2];
         if (loc.wide && loc.location == kLocPhysReg) {
             // Load the other half of the arg into the promoted pair
-            loadBaseDisp(cUnit, NULL, rSP, loc.spOffset+4,
-                         loc.highReg, kWord, INVALID_SREG);
+            loadWordDisp(cUnit, rSP, loc.spOffset + 4, loc.highReg);
             inRegs++;
         }
     }
@@ -1674,8 +1636,7 @@
                                  loc.lowReg, loc.highReg, INVALID_SREG);
                 i++;
             } else {
-                loadBaseDisp(cUnit, NULL, rSP, loc.spOffset,
-                             loc.lowReg, kWord, INVALID_SREG);
+                loadWordDisp(cUnit, rSP, loc.spOffset, loc.lowReg);
             }
         }
         i++;
diff --git a/src/compiler_test.cc b/src/compiler_test.cc
index 305b0d8..b4e43cc 100644
--- a/src/compiler_test.cc
+++ b/src/compiler_test.cc
@@ -170,6 +170,11 @@
                                 "(I)I", 2468, 1234);
 }
 
+TEST_F(CompilerTest, ConstClassTest) {
+  AssertStaticIntMethod(LoadDex("IntMath"), "IntMath", "constClassTest",
+                                "(I)I", 2222, 1111);
+}
+
 TEST_F(CompilerTest, DISABLED_CatchTest) {
   CompileDirectMethod(NULL, "java.lang.Object", "<init>", "()V");
   CompileDirectMethod(NULL, "java.lang.NullPointerException", "<init>", "()V");
diff --git a/src/dex_cache.h b/src/dex_cache.h
index 137f267..804f680 100644
--- a/src/dex_cache.h
+++ b/src/dex_cache.h
@@ -39,9 +39,21 @@
     Set(method_idx * kMax + kMethod, reinterpret_cast<int32_t>(method));
   }
 
- static size_t LengthAsArray(size_t elements) {
-   return kMax * elements;
- }
+  static size_t LengthAsArray(size_t elements) {
+    return kMax * elements;
+  }
+
+  // Offset of resolved method entry from start of code_and_direct_methods_
+  static size_t MethodOffsetInBytes(uint32_t method_idx) {
+    return ((method_idx * kMax + kMethod) * sizeof(ElementType) +
+             Array::DataOffset().Int32Value());
+  }
+
+  // Offset of resolved method's code_ from start of code_and_direct_methods_
+  static size_t CodeOffsetInBytes(uint32_t method_idx) {
+    return ((method_idx * kMax + kCode) * sizeof(ElementType) +
+             Array::DataOffset().Int32Value());
+  }
 
  size_t NumCodeAndDirectMethods() const {
    return GetLength() / kMax;
diff --git a/src/object.h b/src/object.h
index 5a47c46..046f74e 100644
--- a/src/object.h
+++ b/src/object.h
@@ -903,6 +903,18 @@
     return OFFSET_OF_OBJECT_MEMBER(Method, invoke_stub_);
   }
 
+  static MemberOffset GetDexCacheCodeAndDirectMethodsOffset() {
+    return OFFSET_OF_OBJECT_MEMBER(Method, dex_cache_code_and_direct_methods_);
+  }
+
+  static MemberOffset GetDexCacheResolvedMethodsOffset() {
+    return OFFSET_OF_OBJECT_MEMBER(Method, dex_cache_resolved_methods_);
+  }
+
+  static MemberOffset GetMethodIndexOffset() {
+    return OFFSET_OF_OBJECT_MEMBER(Method, method_index_);
+  }
+
   void SetInvokeStub(const InvokeStub* new_invoke_stub) {
     SetFieldPtr<const InvokeStub*>(
         OFFSET_OF_OBJECT_MEMBER(Method, invoke_stub_), new_invoke_stub, false);
diff --git a/src/thread.cc b/src/thread.cc
index c0e986c..aa24bec 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -58,6 +58,18 @@
   return NULL;
 }
 
+// TODO: placeholder.  Helper function to resolve virtual method
+static void ResolveMethodFromCode(Method* method, uint32_t method_idx) {
+    /*
+     * Slow-path handler on invoke virtual method path in which
+     * base method is unresolved at compile-time.  Doesn't need to
+     * return anything - just either ensure that
+     * method->dex_cache_resolved_methods_(method_idx) != NULL or
+     * throw and unwind.  The caller will restart call sequence
+     * from the beginning.
+     */
+}
+
 void Thread::InitFunctionPointers() {
 #if defined(__arm__)
   pShlLong = art_shl_long;
@@ -100,6 +112,7 @@
   pCanPutArrayElementFromCode = Class::CanPutArrayElementFromCode;
   pThrowException = ThrowException;
   pInitializeTypeFromCode = InitializeTypeFromCode;
+  pResolveMethodFromCode = ResolveMethodFromCode;
 #if 0
 bool (Thread::*pUnlockObject)(Thread*, Object*);
 int (Thread::*pInstanceofNonTrivialFromCode)(const Class*, const Class*);
diff --git a/src/thread.h b/src/thread.h
index 2e2444f..0a94151 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -218,6 +218,7 @@
   void (*pThrowException)(Thread*, Throwable*);
   void (*pHandleFillArrayDataFromCode)(Array*, const uint16_t*);
   Class* (*pInitializeTypeFromCode)(uint32_t, Method*);
+  void (*pResolveMethodFromCode)(Method*, uint32_t);
 
   class StackVisitor {
    public:
diff --git a/test/IntMath/IntMath.java b/test/IntMath/IntMath.java
index 0d52ffc..05cecce 100644
--- a/test/IntMath/IntMath.java
+++ b/test/IntMath/IntMath.java
@@ -23,6 +23,15 @@
         foo_ = 123;
     }
 
+    static int constClassTest(int x) {
+        Class c = String.class;
+        if (c != null) {
+           return x * 2;
+        } else {
+           return x;
+       }
+    }
+
     static int constStringTest(int x) {
         /* TODO: flesh this test out when we can call string library */
         String str = "Hello World!";