Add run-time resolution paths for iget/iput.

Change-Id: I1bd26286a39d057aebbb0d847bc58ecd656af458
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..4ae254b 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
@@ -380,33 +396,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 +477,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 +515,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 +561,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,
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..98f7f78 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -177,6 +177,7 @@
   pCheckCastFromCode = CheckCastFromCode;
   pLockObjectFromCode = LockObjectFromCode;
   pUnlockObjectFromCode = UnlockObjectFromCode;
+  pFindFieldFromCode = Field::FindFieldFromCode;
   pDebugMe = DebugMe;
 }
 
diff --git a/src/thread.h b/src/thread.h
index df162b9..a5a6911 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -229,6 +229,7 @@
   void (*pResolveMethodFromCode)(Method*, uint32_t);
   void (*pInvokeInterfaceTrampoline)(void*, void*, void*, void*);
   StaticStorageBase* (*pInitializeStaticStorage)(uint32_t, const Method*);
+  Field* (*pFindFieldFromCode)(uint32_t, const Method*);
 
   class StackVisitor {
    public: