Merge "Verifier now generates register maps, though nothing uses them yet." into dalvik-dev
diff --git a/src/class_linker.cc b/src/class_linker.cc
index 0df0cb7..01e283e 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -2114,10 +2114,10 @@
 
   const char* name = dex_file.dexStringById(method_id.name_idx_);
   std::string signature(dex_file.CreateMethodDescriptor(method_id.proto_idx_, NULL));
-  if (klass->IsInterface()) {
-    resolved = klass->FindInterfaceMethod(name, signature);
-  } else if (is_direct) {
+  if (is_direct) {
     resolved = klass->FindDirectMethod(name, signature);
+  } else if (klass->IsInterface()) {
+    resolved = klass->FindInterfaceMethod(name, signature);
   } else {
     resolved = klass->FindVirtualMethod(name, signature);
   }
diff --git a/src/compiler/codegen/Ralloc.h b/src/compiler/codegen/Ralloc.h
index c702204..67ed6ee 100644
--- a/src/compiler/codegen/Ralloc.h
+++ b/src/compiler/codegen/Ralloc.h
@@ -171,6 +171,8 @@
 /* To be used when explicitly managing register use */
 extern void oatLockCallTemps(CompilationUnit* cUnit);
 
+extern void oatFreeCallTemps(CompilationUnit* cUnit);
+
 extern void oatFlushAllRegs(CompilationUnit* cUnit);
 
 extern RegLocation oatGetReturnWideAlt(CompilationUnit* cUnit);
diff --git a/src/compiler/codegen/RallocUtil.cc b/src/compiler/codegen/RallocUtil.cc
index 5951ad4..1fe680f 100644
--- a/src/compiler/codegen/RallocUtil.cc
+++ b/src/compiler/codegen/RallocUtil.cc
@@ -712,6 +712,16 @@
     oatLockTemp(cUnit, r3);
 }
 
+/* To be used when explicitly managing register use */
+extern void oatFreeCallTemps(CompilationUnit* cUnit)
+{
+    //TODO: Arm specific - move to target dependent code
+    oatFreeTemp(cUnit, r0);
+    oatFreeTemp(cUnit, r1);
+    oatFreeTemp(cUnit, r2);
+    oatFreeTemp(cUnit, r3);
+}
+
 // Make sure nothing is live and dirty
 static void flushAllRegsBody(CompilationUnit* cUnit, RegisterInfo* info,
                              int numRegs)
diff --git a/src/compiler/codegen/arm/MethodCodegenDriver.cc b/src/compiler/codegen/arm/MethodCodegenDriver.cc
index 44cbc0e..cf1ad04 100644
--- a/src/compiler/codegen/arm/MethodCodegenDriver.cc
+++ b/src/compiler/codegen/arm/MethodCodegenDriver.cc
@@ -14,9 +14,6 @@
  * limitations under the License.
  */
 
-#define FORCE_SLOW 0
-#define DISPLAY_MISSING_TARGETS
-
 static const RegLocation badLoc = {kLocDalvikFrame, 0, 0, INVALID_REG,
                                    INVALID_REG, INVALID_SREG, 0,
                                    kLocDalvikFrame, INVALID_REG, INVALID_REG,
@@ -146,6 +143,8 @@
     Field* field = cUnit->method->GetDexCacheResolvedFields()->Get(fieldIdx);
     if (field == NULL) {
         // Slow path
+        LOG(INFO) << "Field " << fieldNameFromIndex(cUnit->method, fieldIdx)
+            << " unresolved at compile time";
         int funcOffset = isObject ? OFFSETOF_MEMBER(Thread, pSetObjStatic)
                                   : OFFSETOF_MEMBER(Thread, pSet32Static);
         oatFlushAllRegs(cUnit);
@@ -203,7 +202,9 @@
 {
     int fieldIdx = mir->dalvikInsn.vB;
     Field* field = cUnit->method->GetDexCacheResolvedFields()->Get(fieldIdx);
-    if (FORCE_SLOW || field == NULL) {
+    if (SLOW_FIELD_PATH || field == NULL) {
+        LOG(INFO) << "Field " << fieldNameFromIndex(cUnit->method, fieldIdx)
+            << " unresolved at compile time";
         oatFlushAllRegs(cUnit);
         loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pSet64Static), rLR);
         loadConstant(cUnit, r0, mir->dalvikInsn.vB);
@@ -259,7 +260,9 @@
 {
     int fieldIdx = mir->dalvikInsn.vB;
     Field* field = cUnit->method->GetDexCacheResolvedFields()->Get(fieldIdx);
-    if (FORCE_SLOW || field == NULL) {
+    if (SLOW_FIELD_PATH || field == NULL) {
+        LOG(INFO) << "Field " << fieldNameFromIndex(cUnit->method, fieldIdx)
+            << " unresolved at compile time";
         oatFlushAllRegs(cUnit);
         loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pGet64Static), rLR);
         loadConstant(cUnit, r0, mir->dalvikInsn.vB);
@@ -317,7 +320,9 @@
     Field* field = cUnit->method->GetDexCacheResolvedFields()->Get(fieldIdx);
     bool isObject = ((mir->dalvikInsn.opcode == OP_SGET_OBJECT) ||
                      (mir->dalvikInsn.opcode == OP_SGET_OBJECT_VOLATILE));
-    if (FORCE_SLOW || field == NULL) {
+    if (SLOW_FIELD_PATH || field == NULL) {
+        LOG(INFO) << "Field " << fieldNameFromIndex(cUnit->method, fieldIdx)
+            << " unresolved at compile time";
         // Slow path
         int funcOffset = isObject ? OFFSETOF_MEMBER(Thread, pGetObjStatic)
                                   : OFFSETOF_MEMBER(Thread, pGet32Static);
@@ -950,7 +955,7 @@
 
     // Explicit register usage
     oatLockCallTemps(cUnit);
-    if (FORCE_SLOW || baseMethod == NULL) {
+    if (SLOW_INVOKE_PATH || baseMethod == NULL) {
         fastPath = false;
     } else {
         Class* superClass = cUnit->method->GetDeclaringClass()->GetSuperClass();
@@ -1001,7 +1006,7 @@
 
     // Explicit register usage
     oatLockCallTemps(cUnit);
-    if (FORCE_SLOW || method == NULL) {
+    if (SLOW_INVOKE_PATH || method == NULL) {
         // Slow path
         nextCallInsn = nextVCallInsnSP;
         // If we need a slow-path callout, we'll restart here
diff --git a/src/compiler/codegen/arm/Thumb2/Gen.cc b/src/compiler/codegen/arm/Thumb2/Gen.cc
index dbaf9ea..ce1c9a7 100644
--- a/src/compiler/codegen/arm/Thumb2/Gen.cc
+++ b/src/compiler/codegen/arm/Thumb2/Gen.cc
@@ -22,6 +22,22 @@
  *
  */
 
+#define SLOW_FIELD_PATH 0
+#define SLOW_INVOKE_PATH 0
+#define DISPLAY_MISSING_TARGETS
+//#define EXERCISE_SLOWEST_FIELD_PATH
+
+std::string fieldNameFromIndex(const Method* method, uint32_t fieldIdx)
+{
+    art::ClassLinker* class_linker = art::Runtime::Current()->GetClassLinker();
+    const art::DexFile& dex_file = class_linker->FindDexFile(
+         method->GetDeclaringClass()->GetDexCache());
+    const art::DexFile::FieldId& field_id = dex_file.GetFieldId(fieldIdx);
+    std::string class_name = dex_file.dexStringById(field_id.class_idx_);
+    std::string field_name = dex_file.dexStringById(field_id.name_idx_);
+    return class_name + "." + field_name;
+}
+
 /*
  * Construct an s4 from two consecutive half-words of switch data.
  * This needs to check endianness because the DEX optimizer only swaps
@@ -361,9 +377,8 @@
  */
 static void markGCCard(CompilationUnit* cUnit, int valReg, int tgtAddrReg)
 {
-#if 1
-  UNIMPLEMENTED(WARNING);
-#else
+#if 0
+    // TODO: re-enable when concurrent collector is active
     int regCardBase = oatAllocTemp(cUnit);
     int regCardNo = oatAllocTemp(cUnit);
     ArmLIR* branchOver = genCmpImmBranch(cUnit, kArmCondEq, valReg, 0);
@@ -380,33 +395,80 @@
 #endif
 }
 
+/*
+ * Helper function for Iget/put when field not resolved at compile time.
+ * Will trash call temps and return with the field offset in r0.
+ */
+static void getFieldOffset(CompilationUnit* cUnit, MIR* mir)
+{
+    int fieldIdx = mir->dalvikInsn.vC;
+    LOG(INFO) << "Field " << fieldNameFromIndex(cUnit->method, fieldIdx)
+        << " unresolved at compile time";
+    oatLockCallTemps(cUnit);  // Explicit register usage
+    loadCurrMethodDirect(cUnit, r1);              // arg1 <= Method*
+    loadWordDisp(cUnit, r1,
+                 Method::DexCacheResolvedFieldsOffset().Int32Value(), r0);
+    loadWordDisp(cUnit, r0, art::Array::DataOffset().Int32Value() +
+                 sizeof(int32_t*)* fieldIdx, r0);
+    /*
+     * For testing, omit the test for run-time resolution. This will
+     * force all accesses to go through the runtime resolution path.
+     */
+#ifndef EXERCISE_SLOWEST_FIELD_PATH
+    ArmLIR* branchOver = genCmpImmBranch(cUnit, kArmCondNe, r0, 0);
+#endif
+    // Resolve
+    loadWordDisp(cUnit, rSELF,
+                 OFFSETOF_MEMBER(Thread, pFindFieldFromCode), rLR);
+    loadConstant(cUnit, r0, fieldIdx);
+    opReg(cUnit, kOpBlx, rLR); // resolveTypeFromCode(idx, method)
+    ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel);
+    target->defMask = ENCODE_ALL;
+#ifndef EXERCISE_SLOWEST_FIELD_PATH
+    branchOver->generic.target = (LIR*)target;
+#endif
+    // Free temps (except for r0)
+    oatFreeTemp(cUnit, r1);
+    oatFreeTemp(cUnit, r2);
+    oatFreeTemp(cUnit, r3);
+    loadWordDisp(cUnit, r0, art::Field::OffsetOffset().Int32Value(), r0);
+}
+
 static void genIGetX(CompilationUnit* cUnit, MIR* mir, OpSize size,
                      RegLocation rlDest, RegLocation rlObj)
 {
     Field* fieldPtr = cUnit->method->GetDeclaringClass()->GetDexCache()->
         GetResolvedField(mir->dalvikInsn.vC);
-    if (fieldPtr == NULL) {
-        UNIMPLEMENTED(FATAL) << "Need to handle unresolved field";
-    }
-#if ANDROID_SMP != 0
-    bool isVolatile = dvmIsVolatileField(fieldPtr);
-#else
-    bool isVolatile = false;
-#endif
-    int fieldOffset = fieldPtr->GetOffset().Int32Value();
     RegLocation rlResult;
     RegisterClass regClass = oatRegClassBySize(size);
-    rlObj = loadValue(cUnit, rlObj, kCoreReg);
-    rlResult = oatEvalLoc(cUnit, rlDest, regClass, true);
-    genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset,
-                 NULL);/* null object? */
-    loadBaseDisp(cUnit, mir, rlObj.lowReg, fieldOffset, rlResult.lowReg,
-                 size, rlObj.sRegLow);
-    if (isVolatile) {
+    if (SLOW_FIELD_PATH || fieldPtr == NULL) {
+        getFieldOffset(cUnit, mir);
+        // Field offset in r0
+        rlObj = loadValue(cUnit, rlObj, kCoreReg);
+        rlResult = oatEvalLoc(cUnit, rlDest, regClass, true);
+        genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset,
+                     NULL);/* null object? */
+        loadBaseIndexed(cUnit, rlObj.lowReg, r0, rlResult.lowReg, 0, size);
         oatGenMemBarrier(cUnit, kSY);
+        storeValue(cUnit, rlDest, rlResult);
+    } else {
+#if ANDROID_SMP != 0
+        bool isVolatile = dvmIsVolatileField(fieldPtr);
+#else
+        bool isVolatile = false;
+#endif
+        int fieldOffset = fieldPtr->GetOffset().Int32Value();
+        rlObj = loadValue(cUnit, rlObj, kCoreReg);
+        rlResult = oatEvalLoc(cUnit, rlDest, regClass, true);
+        genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset,
+                     NULL);/* null object? */
+        loadBaseDisp(cUnit, mir, rlObj.lowReg, fieldOffset, rlResult.lowReg,
+                     size, rlObj.sRegLow);
+        if (isVolatile) {
+            oatGenMemBarrier(cUnit, kSY);
+        }
+        storeValue(cUnit, rlDest, rlResult);
     }
-
-    storeValue(cUnit, rlDest, rlResult);
 }
 
 static void genIPutX(CompilationUnit* cUnit, MIR* mir, OpSize size,
@@ -414,25 +476,33 @@
 {
     Field* fieldPtr = cUnit->method->GetDeclaringClass()->GetDexCache()->
         GetResolvedField(mir->dalvikInsn.vC);
-    if (fieldPtr == NULL) {
-        UNIMPLEMENTED(FATAL) << "Need to handle unresolved field";
-    }
-#if ANDROID_SMP != 0
-    bool isVolatile = dvmIsVolatileField(fieldPtr);
-#else
-    bool isVolatile = false;
-#endif
-    int fieldOffset = fieldPtr->GetOffset().Int32Value();
     RegisterClass regClass = oatRegClassBySize(size);
-    rlObj = loadValue(cUnit, rlObj, kCoreReg);
-    rlSrc = loadValue(cUnit, rlSrc, regClass);
-    genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset,
-                 NULL);/* null object? */
-
-    if (isVolatile) {
+    if (SLOW_FIELD_PATH || fieldPtr == NULL) {
+        getFieldOffset(cUnit, mir);
+        // Field offset in r0
+        rlObj = loadValue(cUnit, rlObj, kCoreReg);
+        rlSrc = loadValue(cUnit, rlSrc, regClass);
+        genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset,
+                     NULL);/* null object? */
         oatGenMemBarrier(cUnit, kSY);
+        storeBaseIndexed(cUnit, rlObj.lowReg, r0, rlSrc.lowReg, 0, size);
+    } else {
+#if ANDROID_SMP != 0
+        bool isVolatile = dvmIsVolatileField(fieldPtr);
+#else
+        bool isVolatile = false;
+#endif
+        int fieldOffset = fieldPtr->GetOffset().Int32Value();
+        rlObj = loadValue(cUnit, rlObj, kCoreReg);
+        rlSrc = loadValue(cUnit, rlSrc, regClass);
+        genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset,
+                    NULL);/* null object? */
+
+        if (isVolatile) {
+            oatGenMemBarrier(cUnit, kSY);
+        }
+        storeBaseDisp(cUnit, rlObj.lowReg, fieldOffset, rlSrc.lowReg, size);
     }
-    storeBaseDisp(cUnit, rlObj.lowReg, fieldOffset, rlSrc.lowReg, size);
     if (isObject) {
         /* NOTE: marking card based on object head */
         markGCCard(cUnit, rlSrc.lowReg, rlObj.lowReg);
@@ -444,34 +514,44 @@
 {
     Field* fieldPtr = cUnit->method->GetDeclaringClass()->GetDexCache()->
         GetResolvedField(mir->dalvikInsn.vC);
-    if (fieldPtr == NULL) {
-        UNIMPLEMENTED(FATAL) << "Need to handle unresolved field";
-    }
-#if ANDROID_SMP != 0
-    bool isVolatile = dvmIsVolatileField(fieldPtr);
-#else
-    bool isVolatile = false;
-#endif
-    int fieldOffset = fieldPtr->GetOffset().Int32Value();
     RegLocation rlResult;
-    rlObj = loadValue(cUnit, rlObj, kCoreReg);
-    int regPtr = oatAllocTemp(cUnit);
-
-    assert(rlDest.wide);
-
-    genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset,
-                 NULL);/* null object? */
-    opRegRegImm(cUnit, kOpAdd, regPtr, rlObj.lowReg, fieldOffset);
-    rlResult = oatEvalLoc(cUnit, rlDest, kAnyReg, true);
-
-    loadPair(cUnit, regPtr, rlResult.lowReg, rlResult.highReg);
-
-    if (isVolatile) {
+    if (fieldPtr == NULL) {
+        getFieldOffset(cUnit, mir);
+        // Field offset in r0
+        rlObj = loadValue(cUnit, rlObj, kCoreReg);
+        rlResult = oatEvalLoc(cUnit, rlDest, kAnyReg, true);
+        genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset,
+                     NULL);/* null object? */
+        opRegReg(cUnit, kOpAdd, r0, rlObj.lowReg);
+        loadPair(cUnit, r0, rlResult.lowReg, rlResult.highReg);
         oatGenMemBarrier(cUnit, kSY);
-    }
+        storeValue(cUnit, rlDest, rlResult);
+    } else {
+#if ANDROID_SMP != 0
+        bool isVolatile = dvmIsVolatileField(fieldPtr);
+#else
+        bool isVolatile = false;
+#endif
+        int fieldOffset = fieldPtr->GetOffset().Int32Value();
+        rlObj = loadValue(cUnit, rlObj, kCoreReg);
+        int regPtr = oatAllocTemp(cUnit);
 
-    oatFreeTemp(cUnit, regPtr);
-    storeValueWide(cUnit, rlDest, rlResult);
+        assert(rlDest.wide);
+
+        genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset,
+                     NULL);/* null object? */
+        opRegRegImm(cUnit, kOpAdd, regPtr, rlObj.lowReg, fieldOffset);
+        rlResult = oatEvalLoc(cUnit, rlDest, kAnyReg, true);
+
+        loadPair(cUnit, regPtr, rlResult.lowReg, rlResult.highReg);
+
+        if (isVolatile) {
+            oatGenMemBarrier(cUnit, kSY);
+        }
+
+        oatFreeTemp(cUnit, regPtr);
+        storeValueWide(cUnit, rlDest, rlResult);
+    }
 }
 
 static void genIPutWideX(CompilationUnit* cUnit, MIR* mir, RegLocation rlSrc,
@@ -480,29 +560,38 @@
     Field* fieldPtr = cUnit->method->GetDeclaringClass()->GetDexCache()->
         GetResolvedField(mir->dalvikInsn.vC);
     if (fieldPtr == NULL) {
-        UNIMPLEMENTED(FATAL) << "Need to handle unresolved field";
-    }
-#if ANDROID_SMP != 0
-    bool isVolatile = dvmIsVolatileField(fieldPtr);
-#else
-    bool isVolatile = false;
-#endif
-    int fieldOffset = fieldPtr->GetOffset().Int32Value();
-
-    rlObj = loadValue(cUnit, rlObj, kCoreReg);
-    int regPtr;
-    rlSrc = loadValueWide(cUnit, rlSrc, kAnyReg);
-    genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset,
-                 NULL);/* null object? */
-    regPtr = oatAllocTemp(cUnit);
-    opRegRegImm(cUnit, kOpAdd, regPtr, rlObj.lowReg, fieldOffset);
-
-    if (isVolatile) {
+        getFieldOffset(cUnit, mir);
+        // Field offset in r0
+        rlObj = loadValue(cUnit, rlObj, kCoreReg);
+        rlSrc = loadValueWide(cUnit, rlSrc, kAnyReg);
+        genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset,
+                     NULL);/* null object? */
+        opRegReg(cUnit, kOpAdd, r0, rlObj.lowReg);
         oatGenMemBarrier(cUnit, kSY);
-    }
-    storePair(cUnit, regPtr, rlSrc.lowReg, rlSrc.highReg);
+        storePair(cUnit, r0, rlSrc.lowReg, rlSrc.highReg);
+    } else {
+#if ANDROID_SMP != 0
+        bool isVolatile = dvmIsVolatileField(fieldPtr);
+#else
+        bool isVolatile = false;
+#endif
+        int fieldOffset = fieldPtr->GetOffset().Int32Value();
 
-    oatFreeTemp(cUnit, regPtr);
+        rlObj = loadValue(cUnit, rlObj, kCoreReg);
+        int regPtr;
+        rlSrc = loadValueWide(cUnit, rlSrc, kAnyReg);
+        genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset,
+                     NULL);/* null object? */
+        regPtr = oatAllocTemp(cUnit);
+        opRegRegImm(cUnit, kOpAdd, regPtr, rlObj.lowReg, fieldOffset);
+
+        if (isVolatile) {
+            oatGenMemBarrier(cUnit, kSY);
+        }
+        storePair(cUnit, regPtr, rlSrc.lowReg, rlSrc.highReg);
+
+        oatFreeTemp(cUnit, regPtr);
+    }
 }
 
 static void genConstClass(CompilationUnit* cUnit, MIR* mir,
@@ -1618,22 +1707,22 @@
     return false;
 }
 
-/*
- * Fetch *self->info.breakFlags. If the breakFlags are non-zero,
- * punt to the interpreter.
- */
+/* Check for pending suspend request.  */
 static void genSuspendPoll(CompilationUnit* cUnit, MIR* mir)
 {
-    UNIMPLEMENTED(WARNING);
-#if 0
-    int rTemp = oatAllocTemp(cUnit);
+    oatLockCallTemps(cUnit);   // Explicit register usage
+    int rSuspendCount = r1;
     ArmLIR* ld;
-    ld = loadBaseDisp(cUnit, NULL, rSELF,
-                      offsetof(Thread, interpBreak.ctl.breakFlags),
-                      rTemp, kUnsignedByte, INVALID_SREG);
+    ld = loadWordDisp(cUnit, rSELF,
+        art::Thread::SuspendCountOffset().Int32Value(), rSuspendCount);
     setMemRefType(ld, true /* isLoad */, kMustNotAlias);
-    genRegImmCheck(cUnit, kArmCondNe, rTemp, 0, mir->offset, NULL);
-#endif
+    loadWordDisp(cUnit, rSELF,
+                 OFFSETOF_MEMBER(Thread, pCheckSuspendFromCode), rLR);
+    genRegCopy(cUnit, r0, rSELF);
+    opRegImm(cUnit, kOpCmp, rSuspendCount, 0);
+    genIT(cUnit, kArmCondNe, "");
+    opReg(cUnit, kOpBlx, rLR); // CheckSuspendFromCode(self)
+    oatFreeCallTemps(cUnit);
 }
 
 /*
diff --git a/src/compiler_test.cc b/src/compiler_test.cc
index e232858..5766a4f 100644
--- a/src/compiler_test.cc
+++ b/src/compiler_test.cc
@@ -56,7 +56,7 @@
   }
 };
 
-// TODO renenable when compiler can handle libcore
+// Disabled due to 10 second runtime on host
 TEST_F(CompilerTest, DISABLED_CompileDexLibCore) {
   Compiler compiler;
   compiler.CompileAll(NULL);
@@ -67,22 +67,27 @@
   EXPECT_EQ(dex->NumStringIds(), dex_cache->NumStrings());
   for (size_t i = 0; i < dex_cache->NumStrings(); i++) {
     const String* string = dex_cache->GetResolvedString(i);
-    EXPECT_TRUE(string != NULL);
+    EXPECT_TRUE(string != NULL) << "string_idx=" << i;
   }
   EXPECT_EQ(dex->NumTypeIds(), dex_cache->NumResolvedTypes());
   for (size_t i = 0; i < dex_cache->NumResolvedTypes(); i++) {
     Class* type = dex_cache->GetResolvedType(i);
-    EXPECT_TRUE(type != NULL);
+    EXPECT_TRUE(type != NULL) << "type_idx=" << i
+                              << " " << dex->GetTypeDescriptor(dex->GetTypeId(i));
   }
   EXPECT_EQ(dex->NumMethodIds(), dex_cache->NumResolvedMethods());
   for (size_t i = 0; i < dex_cache->NumResolvedMethods(); i++) {
     Method* method = dex_cache->GetResolvedMethod(i);
-    EXPECT_TRUE(method != NULL);
+    EXPECT_TRUE(method != NULL) << "method_idx=" << i
+                                << " " << dex->GetMethodClassDescriptor(dex->GetMethodId(i))
+                                << " " << dex->GetMethodName(dex->GetMethodId(i));
   }
   EXPECT_EQ(dex->NumFieldIds(), dex_cache->NumResolvedFields());
   for (size_t i = 0; i < dex_cache->NumResolvedFields(); i++) {
     Field* field = dex_cache->GetResolvedField(i);
-    EXPECT_TRUE(field != NULL);
+    EXPECT_TRUE(field != NULL) << "field_idx=" << i
+                               << " " << dex->GetFieldClassDescriptor(dex->GetFieldId(i))
+                               << " " << dex->GetFieldName(dex->GetFieldId(i));
   }
 
   // TODO check Class::IsVerified for all classes
@@ -93,8 +98,13 @@
   CodeAndDirectMethods* code_and_direct_methods = dex_cache->GetCodeAndDirectMethods();
   for (size_t i = 0; i < dex_cache->NumCodeAndDirectMethods(); i++) {
     Method* method = dex_cache->GetResolvedMethod(i);
-    EXPECT_EQ(method->GetCode(), code_and_direct_methods->GetResolvedCode(i));
-    EXPECT_EQ(method,            code_and_direct_methods->GetResolvedMethod(i));
+    if (method->IsDirect()) {
+      EXPECT_EQ(method->GetCode(), code_and_direct_methods->GetResolvedCode(i));
+      EXPECT_EQ(method,            code_and_direct_methods->GetResolvedMethod(i));
+    } else {
+      EXPECT_EQ(0U, code_and_direct_methods->GetResolvedCode(i));
+      EXPECT_TRUE(code_and_direct_methods->GetResolvedMethod(i) == NULL);
+    }
   }
 }
 
diff --git a/src/dex_file.h b/src/dex_file.h
index 98e79a7..cf0bf20 100644
--- a/src/dex_file.h
+++ b/src/dex_file.h
@@ -428,6 +428,17 @@
     return dexStringById(field_id.name_idx_);
   }
 
+  // Returns the class descriptor string of a method id.
+  const char* GetMethodClassDescriptor(const MethodId& method_id) const {
+    const DexFile::TypeId& type_id = GetTypeId(method_id.class_idx_);
+    return GetTypeDescriptor(type_id);
+  }
+
+  // Returns the name of a method id.
+  const char* GetMethodName(const MethodId& method_id) const {
+    return dexStringById(method_id.name_idx_);
+  }
+
   // Returns the StringId at the specified index.
   const StringId& GetStringId(uint32_t idx) const {
     CHECK_LT(idx, NumStringIds());
diff --git a/src/mark_sweep.cc b/src/mark_sweep.cc
index b3d21a3..307a804 100644
--- a/src/mark_sweep.cc
+++ b/src/mark_sweep.cc
@@ -31,6 +31,8 @@
 
   // TODO: if concurrent, clear the card table.
 
+  // TODO: if concurrent, enable card marking in compiler
+
   // TODO: check that the mark bitmap is entirely clear.
 
   return true;
diff --git a/src/object.cc b/src/object.cc
index 7c27ec7..c66791b 100644
--- a/src/object.cc
+++ b/src/object.cc
@@ -56,7 +56,7 @@
   return Runtime::Current()->GetClassLinker()->ResolveType(GetTypeIdx(), this);
 }
 
-Field* FindFieldFromCode(uint32_t field_idx, const Method* referrer) {
+Field* Field::FindFieldFromCode(uint32_t field_idx, const Method* referrer) {
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
   Field* f = class_linker->ResolveField(field_idx, referrer);
   if (f != NULL) {
diff --git a/src/object.h b/src/object.h
index 9e5cf2d..72a03ba 100644
--- a/src/object.h
+++ b/src/object.h
@@ -524,6 +524,12 @@
   // Offset to field within an Object
   MemberOffset GetOffset() const;
 
+  static MemberOffset OffsetOffset() {
+    return MemberOffset(OFFSETOF_MEMBER(Field, offset_));
+  }
+
+  static Field* FindFieldFromCode(uint32_t field_idx, const Method* referrer);
+
   MemberOffset GetOffsetDuringLinking() const;
 
   void SetOffset(MemberOffset num_bytes);
@@ -778,6 +784,10 @@
     return OFFSET_OF_OBJECT_MEMBER(Method, dex_cache_resolved_types_);
   }
 
+  static MemberOffset DexCacheResolvedFieldsOffset() {
+    return OFFSET_OF_OBJECT_MEMBER(Method, dex_cache_resolved_fields_);
+  }
+
   static MemberOffset DexCacheInitializedStaticStorageOffset() {
     return OFFSET_OF_OBJECT_MEMBER(Method,
         dex_cache_initialized_static_storage_);
diff --git a/src/thread.cc b/src/thread.cc
index 3081d91..6109825 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -127,6 +127,15 @@
     obj->MonitorEnter(thread);
 }
 
+// TODO: placeholder
+static void CheckSuspendFromCode(Thread* thread) {
+    /*
+     * Code is at a safe point, suspend if needed.
+     * Also, this is where a pending safepoint callback
+     * would be fired.
+     */
+}
+
 void Thread::InitFunctionPointers() {
 #if defined(__arm__)
   pShlLong = art_shl_long;
@@ -177,6 +186,8 @@
   pCheckCastFromCode = CheckCastFromCode;
   pLockObjectFromCode = LockObjectFromCode;
   pUnlockObjectFromCode = UnlockObjectFromCode;
+  pFindFieldFromCode = Field::FindFieldFromCode;
+  pCheckSuspendFromCode = CheckSuspendFromCode;
   pDebugMe = DebugMe;
 }
 
diff --git a/src/thread.h b/src/thread.h
index df162b9..ba5d6ef 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -229,6 +229,8 @@
   void (*pResolveMethodFromCode)(Method*, uint32_t);
   void (*pInvokeInterfaceTrampoline)(void*, void*, void*, void*);
   StaticStorageBase* (*pInitializeStaticStorage)(uint32_t, const Method*);
+  Field* (*pFindFieldFromCode)(uint32_t, const Method*);
+  void (*pCheckSuspendFromCode)(Thread*);
 
   class StackVisitor {
    public: