Add access check slow paths to field accesses

This check makes the verifier, compiler and runtime agree with who
should perform access checks and fixes compliance for these
instructions.

Introduce new "fast" sget/sput that just get the static storage base
from a method's declaring class when the static field is within the same
class. Saves a load and branch in the common case.

Fold gen routines for wide and not wide together.

Fix bug where sub-classes could appear intialized in the image but their
parents were only verified.

Extra debug output for test case 075.

Change-Id: I934da3624ed8fa8e026b2c95d936d04b1af022ef
diff --git a/src/asm_support.h b/src/asm_support.h
index 4d3569e..63b8f3c 100644
--- a/src/asm_support.h
+++ b/src/asm_support.h
@@ -24,13 +24,13 @@
 #define rSELF r9
 #define rLR r14
 // Offset of field Thread::suspend_count_ verified in InitCpu
-#define THREAD_SUSPEND_COUNT_OFFSET 400
+#define THREAD_SUSPEND_COUNT_OFFSET 420
 // Offset of field Thread::exception_ verified in InitCpu
-#define THREAD_EXCEPTION_OFFSET 396
+#define THREAD_EXCEPTION_OFFSET 416
 
 #elif defined(__i386__)
 // Offset of field Thread::self_ verified in InitCpu
-#define THREAD_SELF_OFFSET 388
+#define THREAD_SELF_OFFSET 408
 #endif
 
 #endif  // ART_SRC_ASM_SUPPORT_H_
diff --git a/src/class_linker.cc b/src/class_linker.cc
index 9d53f93..17e4f4e 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -2237,9 +2237,9 @@
     clinit = klass->FindDeclaredDirectMethod("<clinit>", "()V");
     if (clinit != NULL && !can_run_clinit) {
       // if the class has a <clinit> but we can't run it during compilation,
-      // don't bother going to kStatusInitializing. We return true to maintain
-      // the invariant that a false result implies there is a pending exception.
-      return true;
+      // don't bother going to kStatusInitializing. We return false so that
+      // sub-classes don't believe this class is initialized.
+      return false;
     }
 
     // If the class is kStatusInitializing, either this thread is
@@ -2271,7 +2271,14 @@
   uint64_t t0 = NanoTime();
 
   if (!InitializeSuperClass(klass, can_run_clinit)) {
-    CHECK(klass->IsErroneous());
+    // Super class initialization failed, this can be because we can't run
+    // super-class class initializers in which case we'll be verified.
+    // Otherwise this class is erroneous.
+    if (!can_run_clinit) {
+      CHECK(klass->IsVerified());
+    } else {
+      CHECK(klass->IsErroneous());
+    }
     return false;
   }
 
@@ -2471,7 +2478,7 @@
   ScopedThreadStateChange tsc(self, Thread::kRunnable);
   bool success = InitializeClass(c, can_run_clinit);
   if (!success) {
-    CHECK(self->IsExceptionPending()) << PrettyClass(c);
+    CHECK(self->IsExceptionPending() || !can_run_clinit) << PrettyClass(c);
   }
   return success;
 }
diff --git a/src/compiler.cc b/src/compiler.cc
index 1f652ef..45e4d04 100644
--- a/src/compiler.cc
+++ b/src/compiler.cc
@@ -24,8 +24,8 @@
 #include "assembler.h"
 #include "class_linker.h"
 #include "class_loader.h"
+#include "compiler/CompilerIR.h"
 #include "dex_cache.h"
-#include "dex_verifier.h"
 #include "jni_compiler.h"
 #include "jni_internal.h"
 #include "oat_file.h"
@@ -197,13 +197,152 @@
   if (!IsImage()) {
     return false;
   }
-  Class* resolved_class = dex_cache->GetResolvedTypes()->Get(type_idx);
+  Class* resolved_class = dex_cache->GetResolvedType(type_idx);
   if (resolved_class == NULL) {
     return false;
   }
   return IsImageClass(ClassHelper(resolved_class).GetDescriptor());
 }
 
+bool Compiler::CanAssumeStringIsPresentInDexCache(const DexCache* dex_cache,
+                                                  uint32_t string_idx) const {
+  // TODO: Add support for loading strings referenced by image_classes_
+  // See also Compiler::ResolveDexFile
+
+  // The following is a test saying that if we're building the image without a restricted set of
+  // image classes then we can assume the string is present in the dex cache if it is there now
+  return IsImage() && image_classes_ == NULL && dex_cache->GetResolvedString(string_idx) != NULL;
+}
+
+bool Compiler::CanAccessTypeWithoutChecks(uint32_t referrer_idx, const DexCache* dex_cache,
+                                          const DexFile& dex_file, uint32_t type_idx) const {
+  // Get type from dex cache assuming it was populated by the verifier
+  Class* resolved_class = dex_cache->GetResolvedType(type_idx);
+  if (resolved_class == NULL) {
+    return false;  // Unknown class needs access checks.
+  }
+  const DexFile::MethodId& method_id = dex_file.GetMethodId(referrer_idx);
+  Class* referrer_class = dex_cache->GetResolvedType(method_id.class_idx_);
+  if (referrer_class == NULL) {
+    return false;  // Incomplete referrer knowledge needs access check.
+  }
+  // Perform access check, will return true if access is ok or false if we're going to have to
+  // check this at runtime (for example for class loaders).
+  return referrer_class->CanAccess(resolved_class);
+}
+
+bool Compiler::CanAccessInstantiableTypeWithoutChecks(uint32_t referrer_idx,
+                                                      const DexCache* dex_cache,
+                                                      const DexFile& dex_file,
+                                                      uint32_t type_idx) const {
+  // Get type from dex cache assuming it was populated by the verifier.
+  Class* resolved_class = dex_cache->GetResolvedType(type_idx);
+  if (resolved_class == NULL) {
+    return false;  // Unknown class needs access checks.
+  }
+  const DexFile::MethodId& method_id = dex_file.GetMethodId(referrer_idx);
+  Class* referrer_class = dex_cache->GetResolvedType(method_id.class_idx_);
+  if (referrer_class == NULL) {
+    return false;  // Incomplete referrer knowledge needs access check.
+  }
+  // Perform access and instantiable checks, will return true if access is ok or false if we're
+  // going to have to check this at runtime (for example for class loaders).
+  return referrer_class->CanAccess(resolved_class) && resolved_class->IsInstantiable();
+}
+
+bool Compiler::ComputeInstanceFieldInfo(uint32_t field_idx, CompilationUnit* cUnit,
+                                        int& field_offset, bool& is_volatile) const {
+  // Conservative defaults
+  field_offset = -1;
+  is_volatile = true;
+  // Try to resolve field
+  Field* resolved_field =
+      cUnit->class_linker->ResolveField(*cUnit->dex_file, field_idx, cUnit->dex_cache,
+                                        cUnit->class_loader, false);
+  if (resolved_field != NULL) {
+    const DexFile::MethodId& referrer_method_id = cUnit->dex_file->GetMethodId(cUnit->method_idx);
+    Class* referrer_class =
+        cUnit->class_linker->ResolveType(*cUnit->dex_file, referrer_method_id.class_idx_,
+                                         cUnit->dex_cache, cUnit->class_loader);
+    // Try to resolve referring class then access check, failure to pass the
+    Class* fields_class = resolved_field->GetDeclaringClass();
+    if (referrer_class != NULL &&
+        referrer_class->CanAccess(fields_class) &&
+        referrer_class->CanAccessMember(fields_class,
+                                        resolved_field->GetAccessFlags())) {
+      field_offset = resolved_field->GetOffset().Int32Value();
+      is_volatile = resolved_field->IsVolatile();
+      return true;  // Fast path.
+    }
+  }
+  // Clean up any exception left by field/type resolution
+  Thread* thread = Thread::Current();
+  if (thread->IsExceptionPending()) {
+      thread->ClearException();
+  }
+  return false;  // Incomplete knowledge needs slow path.
+}
+
+bool Compiler::ComputeStaticFieldInfo(uint32_t field_idx, CompilationUnit* cUnit,
+                                      int& field_offset, int& ssb_index,
+                                      bool& is_referrers_class, bool& is_volatile) const {
+  // Conservative defaults
+  field_offset = -1;
+  ssb_index = -1;
+  is_referrers_class = false;
+  is_volatile = true;
+  // Try to resolve field
+  Field* resolved_field =
+      cUnit->class_linker->ResolveField(*cUnit->dex_file, field_idx, cUnit->dex_cache,
+                                        cUnit->class_loader, true);
+  if (resolved_field != NULL) {
+    const DexFile::MethodId& referrer_method_id = cUnit->dex_file->GetMethodId(cUnit->method_idx);
+    Class* referrer_class =
+        cUnit->class_linker->ResolveType(*cUnit->dex_file, referrer_method_id.class_idx_,
+                                         cUnit->dex_cache, cUnit->class_loader);
+    if (referrer_class != NULL) {
+      if (resolved_field->GetDeclaringClass() == referrer_class) {
+        is_referrers_class = true;  // implies no worrying about class initialization
+        field_offset = resolved_field->GetOffset().Int32Value();
+        is_volatile = resolved_field->IsVolatile();
+        return true;  // fast path
+      } else {
+        Class* fields_class = resolved_field->GetDeclaringClass();
+        if (referrer_class->CanAccess(fields_class) &&
+            referrer_class->CanAccessMember(fields_class,
+                                            resolved_field->GetAccessFlags())) {
+          // We have the resolved field, we must make it into a ssbIndex for the referrer
+          // in its static storage base (which may fail if it doesn't have a slot for it)
+          // TODO: if we know the field and referrer are in the same dex file then we can use
+          // resolved_field->GetDeclaringClass()->GetDexTypeIndex()
+          std::string descriptor(FieldHelper(resolved_field).GetDeclaringClassDescriptor());
+          const DexFile::StringId* string_id =
+          cUnit->dex_file->FindStringId(descriptor);
+          if (string_id != NULL) {
+            const DexFile::TypeId* type_id =
+               cUnit->dex_file->FindTypeId(cUnit->dex_file->GetIndexForStringId(*string_id));
+            if(type_id != NULL) {
+              // medium path, needs check of static storage base being initialized
+              // TODO: for images we can elide the static storage base null check
+              // if we know there's a no null entry in the image
+              ssb_index = cUnit->dex_file->GetIndexForTypeId(*type_id);
+              field_offset = resolved_field->GetOffset().Int32Value();
+              is_volatile = resolved_field->IsVolatile();
+              return true;
+            }
+          }
+        }
+      }
+    }
+  }
+  // Clean up any exception left by field/type resolution
+  Thread* thread = Thread::Current();
+  if (thread->IsExceptionPending()) {
+      thread->ClearException();
+  }
+  return false;  // Incomplete knowledge needs slow path.
+}
+
 // Return true if the class should be skipped during compilation. We
 // never skip classes in the boot class loader. However, if we have a
 // non-boot class loader and we can resolve the class in the boot
@@ -674,6 +813,13 @@
   }
 }
 
+namespace verifier {
+  class DexVerifier {
+   public:
+    static const std::vector<uint8_t>* GetGcMap(Compiler::MethodReference ref);
+  };
+}
+
 void Compiler::SetGcMapsMethod(const DexFile& dex_file, Method* method) {
   if (method == NULL) {
     Thread::Current()->ClearException();
diff --git a/src/compiler.h b/src/compiler.h
index 1165161..d966b02 100644
--- a/src/compiler.h
+++ b/src/compiler.h
@@ -35,6 +35,7 @@
 
 class Context;
 class TimingLogger;
+typedef struct CompilationUnit CompilationUnit;
 
 class Compiler {
  public:
@@ -85,43 +86,29 @@
   const CompiledInvokeStub* FindInvokeStub(bool is_static, const char* shorty) const;
 
   // Callbacks from OAT/ART compiler to see what runtime checks must be generated
-  bool CanAssumeTypeIsPresentInDexCache(const DexCache* dex_cache, uint32_t type_idx) const;
-  bool CanAssumeStringIsPresentInDexCache(const DexCache* dex_cache, uint32_t string_idx) const {
-    // TODO: Add support for loading strings referenced by image_classes_
-    // See also Compiler::ResolveDexFile
-    return IsImage() && image_classes_ == NULL && dex_cache->GetResolvedString(string_idx) != NULL;
-  }
-  bool CanAccessTypeWithoutChecks(uint32_t referrer_idx, const DexCache* dex_cache,
-                                  const DexFile& dex_file, uint32_t type_idx) const {
-    Class* resolved_class = dex_cache->GetResolvedType(type_idx);
-    if (resolved_class == NULL) {
-      return false;  // Unknown class needs access checks.
-    }
-    const DexFile::MethodId& method_id = dex_file.GetMethodId(referrer_idx);
-    Class* referrer_class = dex_cache->GetResolvedType(method_id.class_idx_);
-    if (referrer_class == NULL) {
-      return false;  // Incomplete referrer knowledge needs access check.
-    }
-    // Perform access check, will return true if access is ok or false if we're going to have to
-    // check this at runtime (for example for class loaders).
-    return referrer_class->CanAccess(resolved_class);
-  }
 
+  bool CanAssumeTypeIsPresentInDexCache(const DexCache* dex_cache, uint32_t type_idx) const;
+
+  bool CanAssumeStringIsPresentInDexCache(const DexCache* dex_cache, uint32_t string_idx) const;
+
+  // Are runtime access checks necessary in the compiled code?
+  bool CanAccessTypeWithoutChecks(uint32_t referrer_idx, const DexCache* dex_cache,
+                                  const DexFile& dex_file, uint32_t type_idx) const;
+
+  // Are runtime access and instantiable checks necessary in the code?
   bool CanAccessInstantiableTypeWithoutChecks(uint32_t referrer_idx, const DexCache* dex_cache,
-                                              const DexFile& dex_file, uint32_t type_idx) const {
-    Class* resolved_class = dex_cache->GetResolvedType(type_idx);
-    if (resolved_class == NULL) {
-      return false;  // Unknown class needs access checks.
-    }
-    const DexFile::MethodId& method_id = dex_file.GetMethodId(referrer_idx);
-    Class* referrer_class = dex_cache->GetResolvedType(method_id.class_idx_);
-    if (referrer_class == NULL) {
-      return false;  // Incomplete referrer knowledge needs access check.
-    }
-    // Perform access check, will return true if access is ok or false if we're going to have to
-    // check this at runtime (for example for class loaders).
-    return referrer_class->CanAccess(resolved_class) && resolved_class->IsInstantiable();
-  }
+                                              const DexFile& dex_file, uint32_t type_idx) const;
+
+  // Can we fast path instance field access? Computes field's offset and volatility
+  bool ComputeInstanceFieldInfo(uint32_t field_idx, CompilationUnit* cUnit,
+                                int& field_offset, bool& is_volatile) const;
+
+  // Can we fastpath static field access? Computes field's offset, volatility and whether the
+  // field is within the referrer (which can avoid checking class initialization)
+  bool ComputeStaticFieldInfo(uint32_t field_idx, CompilationUnit* cUnit,
+                              int& field_offset, int& ssb_index,
+                              bool& is_referrers_class, bool& is_volatile) const;
+
  private:
 
   // Checks if class specified by type_idx is one of the image_classes_
diff --git a/src/compiler/CompilerIR.h b/src/compiler/CompilerIR.h
index 720a31a..d04b97c 100644
--- a/src/compiler/CompilerIR.h
+++ b/src/compiler/CompilerIR.h
@@ -18,6 +18,7 @@
 #define ART_SRC_COMPILER_COMPILER_IR_H_
 
 #include "codegen/Optimizer.h"
+#include "CompilerUtility.h"
 #include <vector>
 
 namespace art {
diff --git a/src/compiler/codegen/arm/MethodCodegenDriver.cc b/src/compiler/codegen/arm/MethodCodegenDriver.cc
index 6866423..c512e8b 100644
--- a/src/compiler/codegen/arm/MethodCodegenDriver.cc
+++ b/src/compiler/codegen/arm/MethodCodegenDriver.cc
@@ -166,265 +166,188 @@
     }
 }
 
-Field* FindFieldWithResolvedStaticStorage(CompilationUnit* cUnit,
-                                          const uint32_t fieldIdx,
-                                          uint32_t& resolvedTypeIdx) {
-    Field* field = cUnit->class_linker->ResolveField(*cUnit->dex_file,
-                                                     fieldIdx,
-                                                     cUnit->dex_cache,
-                                                     cUnit->class_loader,
-                                                     true);
-    if (field == NULL) {
-        Thread* thread = Thread::Current();
-        if (thread->IsExceptionPending()) {  // clear any exception left by resolve field
-            thread->ClearException();
-        }
-        return NULL;
-    }
-    const DexFile::FieldId& field_id = cUnit->dex_file->GetFieldId(fieldIdx);
-    int type_idx = field_id.class_idx_;
-    Class* klass = cUnit->dex_cache->GetResolvedTypes()->Get(type_idx);
-    // Check if storage class is the same as class referred to by type idx.
-    // They may not be if the FieldId refers a subclass, but storage is in super
-    if (field->GetDeclaringClass() == klass) {
-        resolvedTypeIdx = type_idx;
-        return field;
-    }
-    // See if we can find a dex reference for the storage class.
-    // we may not if the dex file never references the super class,
-    // but usually it will.
-    std::string descriptor(FieldHelper(field).GetDeclaringClassDescriptor());
-    const DexFile::StringId* string_id =
-        cUnit->dex_file->FindStringId(descriptor);
-    if (string_id == NULL) {
-        return NULL;  // descriptor not found, resort to slow path
-    }
-    const DexFile::TypeId* type_id =
-        cUnit->dex_file->FindTypeId(cUnit->dex_file->GetIndexForStringId(*string_id));
-    if (type_id == NULL) {
-        return NULL;  // type id not found, resort to slow path
-    }
-    resolvedTypeIdx = cUnit->dex_file->GetIndexForTypeId(*type_id);
-    return field;
-}
-
-STATIC void genSput(CompilationUnit* cUnit, MIR* mir, RegLocation rlSrc)
+STATIC void genSput(CompilationUnit* cUnit, MIR* mir, RegLocation rlSrc,
+                    bool isLongOrDouble, bool isObject)
 {
-    bool isObject = ((mir->dalvikInsn.opcode == OP_SPUT_OBJECT) ||
-                     (mir->dalvikInsn.opcode == OP_SPUT_OBJECT_VOLATILE));
-    int fieldIdx = mir->dalvikInsn.vB;
-    uint32_t typeIdx;
-    Field* field = FindFieldWithResolvedStaticStorage(cUnit, fieldIdx, typeIdx);
-    oatFlushAllRegs(cUnit);
-    if (SLOW_FIELD_PATH || field == NULL) {
-        // Slow path
-        warnIfUnresolved(cUnit, fieldIdx, field);
-        int funcOffset = isObject ? OFFSETOF_MEMBER(Thread, pSetObjStatic)
-                                  : OFFSETOF_MEMBER(Thread, pSet32Static);
-        loadWordDisp(cUnit, rSELF, funcOffset, rLR);
-        loadConstant(cUnit, r0, mir->dalvikInsn.vB);
-        loadCurrMethodDirect(cUnit, r1);
-        loadValueDirect(cUnit, rlSrc, r2);
-        callRuntimeHelper(cUnit, rLR);
-    } else {
-        // fast path
-        int fieldOffset = field->GetOffset().Int32Value();
-        // Using fixed register to sync with slow path
-        int rMethod = r1;
-        oatLockTemp(cUnit, rMethod);
-        loadCurrMethodDirect(cUnit, rMethod);
-        int rBase = r0;
-        oatLockTemp(cUnit, rBase);
-        loadWordDisp(cUnit, rMethod,
-            Method::DexCacheInitializedStaticStorageOffset().Int32Value(),
-            rBase);
-        loadWordDisp(cUnit, rBase, Array::DataOffset().Int32Value() +
-                      sizeof(int32_t*)* typeIdx, rBase);
-        // TUNING: fast path should fall through
-        // TUNING: Try a conditional skip here, might be faster
-        ArmLIR* branchOver = genCmpImmBranch(cUnit, kArmCondNe, rBase, 0);
-        loadWordDisp(cUnit, rSELF,
-                     OFFSETOF_MEMBER(Thread, pInitializeStaticStorage), rLR);
-        loadConstant(cUnit, r0, typeIdx);
-        callRuntimeHelper(cUnit, rLR);
-        ArmLIR* skipTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
-        skipTarget->defMask = ENCODE_ALL;
-        branchOver->generic.target = (LIR*)skipTarget;
-        rlSrc = oatGetSrc(cUnit, mir, 0);
-        rlSrc = loadValue(cUnit, rlSrc, kAnyReg);
-#if ANDROID_SMP != 0
-        if (field->IsVolatile()) {
+    int fieldOffset;
+    int ssbIndex;
+    bool isVolatile;
+    bool isReferrersClass;
+    uint32_t fieldIdx = mir->dalvikInsn.vB;
+    bool fastPath =
+        cUnit->compiler->ComputeStaticFieldInfo(fieldIdx, cUnit,
+                                                fieldOffset, ssbIndex,
+                                                isReferrersClass, isVolatile);
+    if (fastPath && !SLOW_FIELD_PATH) {
+        DCHECK_GE(fieldOffset, 0);
+        int rBase;
+        int rMethod;
+        if (isReferrersClass) {
+            // Fast path, static storage base is this method's class
+            rMethod  = loadCurrMethod(cUnit);
+            rBase = oatAllocTemp(cUnit);
+            loadWordDisp(cUnit, rMethod,
+                         Method::DeclaringClassOffset().Int32Value(), rBase);
+        } else {
+            // Medium path, static storage base in a different class which
+            // requires checks that the other class is initialized.
+            DCHECK_GE(ssbIndex, 0);
+            // May do runtime call so everything to home locations.
+            oatFlushAllRegs(cUnit);
+            // Using fixed register to sync with possible call to runtime
+            // support.
+            rMethod = r1;
+            oatLockTemp(cUnit, rMethod);
+            loadCurrMethodDirect(cUnit, rMethod);
+            rBase = r0;
+            oatLockTemp(cUnit, rBase);
+            loadWordDisp(cUnit, rMethod,
+                Method::DexCacheInitializedStaticStorageOffset().Int32Value(),
+                rBase);
+            loadWordDisp(cUnit, rBase,
+                         Array::DataOffset().Int32Value() + sizeof(int32_t*) * ssbIndex,
+                         rBase);
+            // rBase now points at appropriate static storage base (Class*)
+            // or NULL if not initialized. Check for NULL and call helper if NULL.
+            // TUNING: fast path should fall through
+            ArmLIR* branchOver = genCmpImmBranch(cUnit, kArmCondNe, rBase, 0);
+            loadWordDisp(cUnit, rSELF,
+                         OFFSETOF_MEMBER(Thread, pInitializeStaticStorage), rLR);
+            loadConstant(cUnit, r0, ssbIndex);
+            callRuntimeHelper(cUnit, rLR);
+            ArmLIR* skipTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
+            skipTarget->defMask = ENCODE_ALL;
+            branchOver->generic.target = (LIR*)skipTarget;
+        }
+        // rBase now holds static storage base
+        oatFreeTemp(cUnit, rMethod);
+        if (isLongOrDouble) {
+            rlSrc = oatGetSrcWide(cUnit, mir, 0, 1);
+            rlSrc = loadValueWide(cUnit, rlSrc, kAnyReg);
+        } else {
+            rlSrc = oatGetSrc(cUnit, mir, 0);
+            rlSrc = loadValue(cUnit, rlSrc, kAnyReg);
+        }
+        if (isVolatile) {
             oatGenMemBarrier(cUnit, kST);
         }
-#endif
-        storeWordDisp(cUnit, rBase, fieldOffset, rlSrc.lowReg);
-#if ANDROID_SMP != 0
-        if (field->IsVolatile()) {
+        if (isLongOrDouble) {
+            storeBaseDispWide(cUnit, rBase, fieldOffset, rlSrc.lowReg,
+                              rlSrc.highReg);
+        } else {
+            storeWordDisp(cUnit, rBase, fieldOffset, rlSrc.lowReg);
+        }
+        if (isVolatile) {
             oatGenMemBarrier(cUnit, kSY);
         }
-#endif
         if (isObject) {
             markGCCard(cUnit, rlSrc.lowReg, rBase);
         }
         oatFreeTemp(cUnit, rBase);
+    } else {
+        oatFlushAllRegs(cUnit);  // Everything to home locations
+        int setterOffset = isLongOrDouble ? OFFSETOF_MEMBER(Thread, pSet64Static) :
+                           (isObject ? OFFSETOF_MEMBER(Thread, pSetObjStatic)
+                                     : OFFSETOF_MEMBER(Thread, pSet32Static));
+        loadWordDisp(cUnit, rSELF, setterOffset, rLR);
+        loadConstant(cUnit, r0, fieldIdx);
+        if (isLongOrDouble) {
+            loadValueDirectWideFixed(cUnit, rlSrc, r2, r3);
+        } else {
+            loadValueDirect(cUnit, rlSrc, r1);
+        }
+        callRuntimeHelper(cUnit, rLR);
     }
 }
 
-STATIC void genSputWide(CompilationUnit* cUnit, MIR* mir, RegLocation rlSrc)
+STATIC void genSget(CompilationUnit* cUnit, MIR* mir, RegLocation rlDest,
+                    bool isLongOrDouble, bool isObject)
 {
-    int fieldIdx = mir->dalvikInsn.vB;
-    uint32_t typeIdx;
-    Field* field = FindFieldWithResolvedStaticStorage(cUnit, fieldIdx, typeIdx);
-    oatFlushAllRegs(cUnit);
-#if ANDROID_SMP != 0
-    bool isVolatile = (field == NULL) || field->IsVolatile();
-#else
-    bool isVolatile = false;
-#endif
-    if (SLOW_FIELD_PATH || field == NULL || isVolatile) {
-        warnIfUnresolved(cUnit, fieldIdx, field);
-        loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pSet64Static), rLR);
-        loadConstant(cUnit, r0, mir->dalvikInsn.vB);
-        loadCurrMethodDirect(cUnit, r1);
-        loadValueDirectWideFixed(cUnit, rlSrc, r2, r3);
-        callRuntimeHelper(cUnit, rLR);
-    } else {
-        // fast path
-        int fieldOffset = field->GetOffset().Int32Value();
-        // Using fixed register to sync with slow path
-        int rMethod = r1;
-        oatLockTemp(cUnit, rMethod);
-        loadCurrMethodDirect(cUnit, r1);
-        int rBase = r0;
-        oatLockTemp(cUnit, rBase);
-        loadWordDisp(cUnit, rMethod,
-            Method::DexCacheInitializedStaticStorageOffset().Int32Value(),
-            rBase);
-        loadWordDisp(cUnit, rBase, Array::DataOffset().Int32Value() +
-                      sizeof(int32_t*)* typeIdx, rBase);
-        // TUNING: fast path should fall through
-        ArmLIR* branchOver = genCmpImmBranch(cUnit, kArmCondNe, rBase, 0);
-        loadWordDisp(cUnit, rSELF,
-                     OFFSETOF_MEMBER(Thread, pInitializeStaticStorage), rLR);
-        loadConstant(cUnit, r0, typeIdx);
-        callRuntimeHelper(cUnit, rLR);
-        ArmLIR* skipTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
-        skipTarget->defMask = ENCODE_ALL;
-        branchOver->generic.target = (LIR*)skipTarget;
-        rlSrc = oatGetSrcWide(cUnit, mir, 0, 1);
-        rlSrc = loadValueWide(cUnit, rlSrc, kAnyReg);
-        storeBaseDispWide(cUnit, rBase, fieldOffset, rlSrc.lowReg,
-                          rlSrc.highReg);
-        oatFreeTemp(cUnit, rBase);
-    }
-}
-
-
-STATIC void genSgetWide(CompilationUnit* cUnit, MIR* mir,
-                 RegLocation rlResult, RegLocation rlDest)
-{
-    int fieldIdx = mir->dalvikInsn.vB;
-    uint32_t typeIdx;
-    Field* field = FindFieldWithResolvedStaticStorage(cUnit, fieldIdx, typeIdx);
-#if ANDROID_SMP != 0
-    bool isVolatile = (field == NULL) || field->IsVolatile();
-#else
-    bool isVolatile = false;
-#endif
-    oatFlushAllRegs(cUnit);
-    if (SLOW_FIELD_PATH || field == NULL || isVolatile) {
-        warnIfUnresolved(cUnit, fieldIdx, field);
-        loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pGet64Static), rLR);
-        loadConstant(cUnit, r0, mir->dalvikInsn.vB);
-        loadCurrMethodDirect(cUnit, r1);
-        callRuntimeHelper(cUnit, rLR);
-        RegLocation rlResult = oatGetReturnWide(cUnit);
-        storeValueWide(cUnit, rlDest, rlResult);
-    } else {
-        // Fast path
-        int fieldOffset = field->GetOffset().Int32Value();
-        // Using fixed register to sync with slow path
-        int rMethod = r1;
-        oatLockTemp(cUnit, rMethod);
-        loadCurrMethodDirect(cUnit, rMethod);
-        int rBase = r0;
-        oatLockTemp(cUnit, rBase);
-        loadWordDisp(cUnit, rMethod,
-            Method::DexCacheInitializedStaticStorageOffset().Int32Value(),
-            rBase);
-        loadWordDisp(cUnit, rBase, Array::DataOffset().Int32Value() +
-                      sizeof(int32_t*)* typeIdx, rBase);
-        // TUNING: fast path should fall through
-        ArmLIR* branchOver = genCmpImmBranch(cUnit, kArmCondNe, rBase, 0);
-        loadWordDisp(cUnit, rSELF,
-                     OFFSETOF_MEMBER(Thread, pInitializeStaticStorage), rLR);
-        loadConstant(cUnit, r0, typeIdx);
-        callRuntimeHelper(cUnit, rLR);
-        ArmLIR* skipTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
-        skipTarget->defMask = ENCODE_ALL;
-        branchOver->generic.target = (LIR*)skipTarget;
-        rlDest = oatGetDestWide(cUnit, mir, 0, 1);
+    int fieldOffset;
+    int ssbIndex;
+    bool isVolatile;
+    bool isReferrersClass;
+    uint32_t fieldIdx = mir->dalvikInsn.vB;
+    bool fastPath =
+        cUnit->compiler->ComputeStaticFieldInfo(fieldIdx, cUnit,
+                                                fieldOffset, ssbIndex,
+                                                isReferrersClass, isVolatile);
+    if (fastPath && !SLOW_FIELD_PATH) {
+        DCHECK_GE(fieldOffset, 0);
+        int rBase;
+        int rMethod;
+        if (isReferrersClass) {
+            // Fast path, static storage base is this method's class
+            rMethod  = loadCurrMethod(cUnit);
+            rBase = oatAllocTemp(cUnit);
+            loadWordDisp(cUnit, rMethod,
+                         Method::DeclaringClassOffset().Int32Value(), rBase);
+        } else {
+            // Medium path, static storage base in a different class which
+            // requires checks that the other class is initialized
+            DCHECK_GE(ssbIndex, 0);
+            // May do runtime call so everything to home locations.
+            oatFlushAllRegs(cUnit);
+            // Using fixed register to sync with possible call to runtime
+            // support
+            rMethod = r1;
+            oatLockTemp(cUnit, rMethod);
+            loadCurrMethodDirect(cUnit, rMethod);
+            rBase = r0;
+            oatLockTemp(cUnit, rBase);
+            loadWordDisp(cUnit, rMethod,
+                Method::DexCacheInitializedStaticStorageOffset().Int32Value(),
+                rBase);
+            loadWordDisp(cUnit, rBase,
+                         Array::DataOffset().Int32Value() + sizeof(int32_t*) * ssbIndex,
+                         rBase);
+            // rBase now points at appropriate static storage base (Class*)
+            // or NULL if not initialized. Check for NULL and call helper if NULL.
+            // TUNING: fast path should fall through
+            ArmLIR* branchOver = genCmpImmBranch(cUnit, kArmCondNe, rBase, 0);
+            loadWordDisp(cUnit, rSELF,
+                         OFFSETOF_MEMBER(Thread, pInitializeStaticStorage), rLR);
+            loadConstant(cUnit, r0, ssbIndex);
+            callRuntimeHelper(cUnit, rLR);
+            ArmLIR* skipTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
+            skipTarget->defMask = ENCODE_ALL;
+            branchOver->generic.target = (LIR*)skipTarget;
+        }
+        // rBase now holds static storage base
+        oatFreeTemp(cUnit, rMethod);
+        rlDest = isLongOrDouble ? oatGetDestWide(cUnit, mir, 0, 1)
+                                : oatGetDest(cUnit, mir, 0);
         RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kAnyReg, true);
-        loadBaseDispWide(cUnit, NULL, rBase, fieldOffset, rlResult.lowReg,
-                         rlResult.highReg, INVALID_SREG);
-        oatFreeTemp(cUnit, rBase);
-        storeValueWide(cUnit, rlDest, rlResult);
-    }
-}
-
-STATIC void genSget(CompilationUnit* cUnit, MIR* mir,
-             RegLocation rlResult, RegLocation rlDest)
-{
-    int fieldIdx = mir->dalvikInsn.vB;
-    uint32_t typeIdx;
-    Field* field = FindFieldWithResolvedStaticStorage(cUnit, fieldIdx, typeIdx);
-    bool isObject = ((mir->dalvikInsn.opcode == OP_SGET_OBJECT) ||
-                     (mir->dalvikInsn.opcode == OP_SGET_OBJECT_VOLATILE));
-    oatFlushAllRegs(cUnit);
-    if (SLOW_FIELD_PATH || field == NULL) {
-        // Slow path
-        warnIfUnresolved(cUnit, fieldIdx, field);
-        int funcOffset = isObject ? OFFSETOF_MEMBER(Thread, pGetObjStatic)
-                                  : OFFSETOF_MEMBER(Thread, pGet32Static);
-        loadWordDisp(cUnit, rSELF, funcOffset, rLR);
-        loadConstant(cUnit, r0, mir->dalvikInsn.vB);
-        loadCurrMethodDirect(cUnit, r1);
-        callRuntimeHelper(cUnit, rLR);
-        RegLocation rlResult = oatGetReturn(cUnit);
-        storeValue(cUnit, rlDest, rlResult);
-    } else {
-        // Fast path
-        int fieldOffset = field->GetOffset().Int32Value();
-        // Using fixed register to sync with slow path
-        int rMethod = r1;
-        oatLockTemp(cUnit, rMethod);
-        loadCurrMethodDirect(cUnit, rMethod);
-        int rBase = r0;
-        oatLockTemp(cUnit, rBase);
-        loadWordDisp(cUnit, rMethod,
-            Method::DexCacheInitializedStaticStorageOffset().Int32Value(),
-            rBase);
-        loadWordDisp(cUnit, rBase, Array::DataOffset().Int32Value() +
-                      sizeof(int32_t*)* typeIdx, rBase);
-        // TUNING: fast path should fall through
-        ArmLIR* branchOver = genCmpImmBranch(cUnit, kArmCondNe, rBase, 0);
-        loadWordDisp(cUnit, rSELF,
-                     OFFSETOF_MEMBER(Thread, pInitializeStaticStorage), rLR);
-        loadConstant(cUnit, r0, typeIdx);
-        callRuntimeHelper(cUnit, rLR);
-        ArmLIR* skipTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
-        skipTarget->defMask = ENCODE_ALL;
-        branchOver->generic.target = (LIR*)skipTarget;
-        rlDest = oatGetDest(cUnit, mir, 0);
-        rlResult = oatEvalLoc(cUnit, rlDest, kAnyReg, true);
-#if ANDROID_SMP != 0
-        if (field->IsVolatile()) {
+        if (isVolatile) {
             oatGenMemBarrier(cUnit, kSY);
         }
-#endif
-        loadWordDisp(cUnit, rBase, fieldOffset, rlResult.lowReg);
+        if (isLongOrDouble) {
+            loadBaseDispWide(cUnit, NULL, rBase, fieldOffset, rlResult.lowReg,
+                             rlResult.highReg, INVALID_SREG);
+        } else {
+            loadWordDisp(cUnit, rBase, fieldOffset, rlResult.lowReg);
+        }
         oatFreeTemp(cUnit, rBase);
-        storeValue(cUnit, rlDest, rlResult);
+        if (isLongOrDouble) {
+            storeValueWide(cUnit, rlDest, rlResult);
+        } else {
+            storeValue(cUnit, rlDest, rlResult);
+        }
+    } else {
+        oatFlushAllRegs(cUnit);  // Everything to home locations
+        int getterOffset = isLongOrDouble ? OFFSETOF_MEMBER(Thread, pGet64Static) :
+                           (isObject ? OFFSETOF_MEMBER(Thread, pGetObjStatic)
+                                     : OFFSETOF_MEMBER(Thread, pGet32Static));
+        loadWordDisp(cUnit, rSELF, getterOffset, rLR);
+        loadConstant(cUnit, r0, fieldIdx);
+        callRuntimeHelper(cUnit, rLR);
+        if (isLongOrDouble) {
+            RegLocation rlResult = oatGetReturnWide(cUnit);
+            storeValueWide(cUnit, rlDest, rlResult);
+        } else {
+            RegLocation rlResult = oatGetReturn(cUnit);
+            storeValue(cUnit, rlDest, rlResult);
+        }
     }
 }
 
@@ -1557,83 +1480,91 @@
                         rlSrc[0], 0);
             break;
 
+        case OP_IGET_OBJECT:
+        case OP_IGET_OBJECT_VOLATILE:
+            genIGet(cUnit, mir, kWord, rlDest, rlSrc[0], false, true);
+            break;
+
         case OP_IGET_WIDE:
         case OP_IGET_WIDE_VOLATILE:
-            genIGetWide(cUnit, mir, rlDest, rlSrc[0]);
+            genIGet(cUnit, mir, kLong, rlDest, rlSrc[0], true, false);
             break;
 
         case OP_IGET:
         case OP_IGET_VOLATILE:
-        case OP_IGET_OBJECT:
-        case OP_IGET_OBJECT_VOLATILE:
-            genIGet(cUnit, mir, kWord, rlDest, rlSrc[0]);
+            genIGet(cUnit, mir, kWord, rlDest, rlSrc[0], false, false);
+            break;
+
+        case OP_IGET_CHAR:
+            genIGet(cUnit, mir, kUnsignedHalf, rlDest, rlSrc[0], false, false);
+            break;
+
+        case OP_IGET_SHORT:
+            genIGet(cUnit, mir, kSignedHalf, rlDest, rlSrc[0], false, false);
             break;
 
         case OP_IGET_BOOLEAN:
         case OP_IGET_BYTE:
-            genIGet(cUnit, mir, kUnsignedByte, rlDest, rlSrc[0]);
-            break;
-
-        case OP_IGET_CHAR:
-            genIGet(cUnit, mir, kUnsignedHalf, rlDest, rlSrc[0]);
-            break;
-
-        case OP_IGET_SHORT:
-            genIGet(cUnit, mir, kSignedHalf, rlDest, rlSrc[0]);
+            genIGet(cUnit, mir, kUnsignedByte, rlDest, rlSrc[0], false, false);
             break;
 
         case OP_IPUT_WIDE:
         case OP_IPUT_WIDE_VOLATILE:
-            genIPutWide(cUnit, mir, rlSrc[0], rlSrc[1]);
+            genIPut(cUnit, mir, kLong, rlSrc[0], rlSrc[1], true, false);
             break;
 
         case OP_IPUT_OBJECT:
         case OP_IPUT_OBJECT_VOLATILE:
-            genIPut(cUnit, mir, kWord, rlSrc[0], rlSrc[1], true);
+            genIPut(cUnit, mir, kWord, rlSrc[0], rlSrc[1], false, true);
             break;
 
         case OP_IPUT:
         case OP_IPUT_VOLATILE:
-            genIPut(cUnit, mir, kWord, rlSrc[0], rlSrc[1], false);
+            genIPut(cUnit, mir, kWord, rlSrc[0], rlSrc[1], false, false);
             break;
 
         case OP_IPUT_BOOLEAN:
         case OP_IPUT_BYTE:
-            genIPut(cUnit, mir, kUnsignedByte, rlSrc[0], rlSrc[1], false);
+            genIPut(cUnit, mir, kUnsignedByte, rlSrc[0], rlSrc[1], false, false);
             break;
 
         case OP_IPUT_CHAR:
-            genIPut(cUnit, mir, kUnsignedHalf, rlSrc[0], rlSrc[1], false);
+            genIPut(cUnit, mir, kUnsignedHalf, rlSrc[0], rlSrc[1], false, false);
             break;
 
         case OP_IPUT_SHORT:
-            genIPut(cUnit, mir, kSignedHalf, rlSrc[0], rlSrc[1], false);
+            genIPut(cUnit, mir, kSignedHalf, rlSrc[0], rlSrc[1], false, false);
             break;
 
-        case OP_SGET:
         case OP_SGET_OBJECT:
+          genSget(cUnit, mir, rlDest, false, true);
+          break;
+        case OP_SGET:
         case OP_SGET_BOOLEAN:
         case OP_SGET_BYTE:
         case OP_SGET_CHAR:
         case OP_SGET_SHORT:
-            genSget(cUnit, mir, rlResult, rlDest);
+            genSget(cUnit, mir, rlDest, false, false);
             break;
 
         case OP_SGET_WIDE:
-            genSgetWide(cUnit, mir, rlResult, rlDest);
+            genSget(cUnit, mir, rlDest, true, false);
             break;
 
-        case OP_SPUT:
         case OP_SPUT_OBJECT:
+          genSput(cUnit, mir, rlSrc[0], false, true);
+          break;
+
+        case OP_SPUT:
         case OP_SPUT_BOOLEAN:
         case OP_SPUT_BYTE:
         case OP_SPUT_CHAR:
         case OP_SPUT_SHORT:
-            genSput(cUnit, mir, rlSrc[0]);
+            genSput(cUnit, mir, rlSrc[0], false, false);
             break;
 
         case OP_SPUT_WIDE:
-            genSputWide(cUnit, mir, rlSrc[0]);
+            genSput(cUnit, mir, rlSrc[0], true, false);
             break;
 
         case OP_INVOKE_STATIC_RANGE:
diff --git a/src/compiler/codegen/arm/Thumb2/Gen.cc b/src/compiler/codegen/arm/Thumb2/Gen.cc
index 423ef46..2a3b807 100644
--- a/src/compiler/codegen/arm/Thumb2/Gen.cc
+++ b/src/compiler/codegen/arm/Thumb2/Gen.cc
@@ -406,187 +406,113 @@
     oatFreeTemp(cUnit, regCardNo);
 }
 
-/*
- * 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, Field* fieldPtr)
-{
-    int fieldIdx = mir->dalvikInsn.vC;
-    oatFlushAllRegs(cUnit);
-    warnIfUnresolved(cUnit, fieldIdx, fieldPtr);
-    oatLockCallTemps(cUnit);  // Explicit register usage
-    loadCurrMethodDirect(cUnit, r1);              // arg1 <= Method*
-    loadWordDisp(cUnit, r1,
-                 Method::DexCacheResolvedFieldsOffset().Int32Value(), r0);
-    loadWordDisp(cUnit, r0, 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.
-     */
-    ArmLIR* branchOver = NULL;
-    if (!EXERCISE_SLOWEST_FIELD_PATH) {
-        branchOver = genCmpImmBranch(cUnit, kArmCondNe, r0, 0);
-    }
-    // Resolve
-    loadWordDisp(cUnit, rSELF,
-                 OFFSETOF_MEMBER(Thread, pFindInstanceFieldFromCode), rLR);
-    loadConstant(cUnit, r0, fieldIdx);
-    callRuntimeHelper(cUnit, rLR);  // FindInstanceFieldFromCoderesolveTypeFromCode(idx, method)
-    ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel);
-    target->defMask = ENCODE_ALL;
-    if (!EXERCISE_SLOWEST_FIELD_PATH) {
-        branchOver->generic.target = (LIR*)target;
-    }
-    // Free temps (except for r0)
-    oatFreeTemp(cUnit, r1);
-    oatFreeTemp(cUnit, r2);
-    oatFreeTemp(cUnit, r3);
-    loadWordDisp(cUnit, r0, Field::OffsetOffset().Int32Value(), r0);
-}
-
 STATIC void genIGet(CompilationUnit* cUnit, MIR* mir, OpSize size,
-                     RegLocation rlDest, RegLocation rlObj)
+                    RegLocation rlDest, RegLocation rlObj,
+                    bool isLongOrDouble, bool isObject)
 {
-    Field* fieldPtr = cUnit->dex_cache->GetResolvedField(mir->dalvikInsn.vC);
-    RegLocation rlResult;
-    RegisterClass regClass = oatRegClassBySize(size);
-    if (SLOW_FIELD_PATH || fieldPtr == NULL) {
-        getFieldOffset(cUnit, mir, fieldPtr);
-        // Field offset in r0
+    int fieldOffset;
+    bool isVolatile;
+    uint32_t fieldIdx = mir->dalvikInsn.vC;
+    bool fastPath =
+        cUnit->compiler->ComputeInstanceFieldInfo(fieldIdx, cUnit,
+                                                  fieldOffset, isVolatile);
+    if (fastPath && !SLOW_FIELD_PATH) {
+        RegLocation rlResult;
+        RegisterClass regClass = oatRegClassBySize(size);
+        DCHECK_GE(fieldOffset, 0);
         rlObj = loadValue(cUnit, rlObj, kCoreReg);
-        rlResult = oatEvalLoc(cUnit, rlDest, regClass, true);
-        genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null object? */
-        loadBaseIndexed(cUnit, rlObj.lowReg, r0, rlResult.lowReg, 0, kWord);
-        oatGenMemBarrier(cUnit, kSY);
-        storeValue(cUnit, rlDest, rlResult);
-    } else {
-#if ANDROID_SMP != 0
-        bool isVolatile = fieldPtr->IsVolatile();
-#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);/* null object? */
-        loadBaseDisp(cUnit, mir, rlObj.lowReg, fieldOffset, rlResult.lowReg,
-                     kWord, rlObj.sRegLow);
-        if (isVolatile) {
-            oatGenMemBarrier(cUnit, kSY);
+        if (isLongOrDouble) {
+            DCHECK(rlDest.wide);
+            genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null obj? */
+            int regPtr = oatAllocTemp(cUnit);
+            opRegRegImm(cUnit, kOpAdd, regPtr, rlObj.lowReg, fieldOffset);
+            rlResult = oatEvalLoc(cUnit, rlDest, regClass, true);
+            loadPair(cUnit, regPtr, rlResult.lowReg, rlResult.highReg);
+            if (isVolatile) {
+                oatGenMemBarrier(cUnit, kSY);
+            }
+            oatFreeTemp(cUnit, regPtr);
+            storeValueWide(cUnit, rlDest, rlResult);
+        } else {
+            rlResult = oatEvalLoc(cUnit, rlDest, regClass, true);
+            genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null object? */
+            loadBaseDisp(cUnit, mir, rlObj.lowReg, fieldOffset, rlResult.lowReg,
+                         kWord, rlObj.sRegLow);
+            if (isVolatile) {
+                oatGenMemBarrier(cUnit, kSY);
+            }
+            storeValue(cUnit, rlDest, rlResult);
         }
-        storeValue(cUnit, rlDest, rlResult);
+    } else {
+        int getterOffset = isLongOrDouble ? OFFSETOF_MEMBER(Thread, pGet64Instance) :
+                           (isObject ? OFFSETOF_MEMBER(Thread, pGetObjInstance)
+                                     : OFFSETOF_MEMBER(Thread, pGet32Instance));
+        loadWordDisp(cUnit, rSELF, getterOffset, rLR);
+        loadValueDirect(cUnit, rlObj, r1);
+        loadConstant(cUnit, r0, fieldIdx);
+        callRuntimeHelper(cUnit, rLR);
+        if (isLongOrDouble) {
+            RegLocation rlResult = oatGetReturnWide(cUnit);
+            storeValueWide(cUnit, rlDest, rlResult);
+        } else {
+            RegLocation rlResult = oatGetReturn(cUnit);
+            storeValue(cUnit, rlDest, rlResult);
+        }
     }
 }
 
 STATIC void genIPut(CompilationUnit* cUnit, MIR* mir, OpSize size,
-                    RegLocation rlSrc, RegLocation rlObj, bool isObject)
+                    RegLocation rlSrc, RegLocation rlObj,
+                    bool isLongOrDouble, bool isObject)
 {
-    Field* fieldPtr = cUnit->dex_cache->GetResolvedField(mir->dalvikInsn.vC);
-    RegisterClass regClass = oatRegClassBySize(size);
-    if (SLOW_FIELD_PATH || fieldPtr == NULL) {
-        getFieldOffset(cUnit, mir, fieldPtr);
-        // Field offset in r0
+    int fieldOffset;
+    bool isVolatile;
+    uint32_t fieldIdx = mir->dalvikInsn.vC;
+    bool fastPath =
+        cUnit->compiler->ComputeInstanceFieldInfo(fieldIdx, cUnit,
+                                                  fieldOffset, isVolatile);
+    if (fastPath && !SLOW_FIELD_PATH) {
+        RegisterClass regClass = oatRegClassBySize(size);
+        DCHECK_GE(fieldOffset, 0);
         rlObj = loadValue(cUnit, rlObj, kCoreReg);
-        rlSrc = loadValue(cUnit, rlSrc, regClass);
-        genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null object? */
-        oatGenMemBarrier(cUnit, kSY);
-        storeBaseIndexed(cUnit, rlObj.lowReg, r0, rlSrc.lowReg, 0, kWord);
-    } else {
-#if ANDROID_SMP != 0
-        bool isVolatile = fieldPtr->IsVolatile();
-#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);/* null obj? */
-
-        if (isVolatile) {
-            oatGenMemBarrier(cUnit, kST);
+        if (isLongOrDouble) {
+            int regPtr;
+            rlSrc = loadValueWide(cUnit, rlSrc, kAnyReg);
+            genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null obj? */
+            regPtr = oatAllocTemp(cUnit);
+            opRegRegImm(cUnit, kOpAdd, regPtr, rlObj.lowReg, fieldOffset);
+            if (isVolatile) {
+                oatGenMemBarrier(cUnit, kST);
+            }
+            storePair(cUnit, regPtr, rlSrc.lowReg, rlSrc.highReg);
+            if (isVolatile) {
+                oatGenMemBarrier(cUnit, kSY);
+            }
+            oatFreeTemp(cUnit, regPtr);
+        } else {
+            rlSrc = loadValue(cUnit, rlSrc, regClass);
+            genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null obj? */
+            if (isVolatile) {
+                oatGenMemBarrier(cUnit, kST);
+            }
+            storeBaseDisp(cUnit, rlObj.lowReg, fieldOffset, rlSrc.lowReg, kWord);
+            if (isVolatile) {
+                oatGenMemBarrier(cUnit, kSY);
+            }
         }
-        storeBaseDisp(cUnit, rlObj.lowReg, fieldOffset, rlSrc.lowReg, kWord);
-        if (isVolatile) {
-            oatGenMemBarrier(cUnit, kSY);
+    } else {
+        int setterOffset = isLongOrDouble ? OFFSETOF_MEMBER(Thread, pSet64Instance) :
+                           (isObject ? OFFSETOF_MEMBER(Thread, pSetObjInstance)
+                                     : OFFSETOF_MEMBER(Thread, pSet32Instance));
+        loadWordDisp(cUnit, rSELF, setterOffset, rLR);
+        loadValueDirect(cUnit, rlObj, r1);
+        if (isLongOrDouble) {
+            loadValueDirectWide(cUnit, rlSrc, r2, r3);
+        } else {
+            loadValueDirect(cUnit, rlSrc, r2);
         }
-    }
-    if (isObject) {
-        /* NOTE: marking card based on object head */
-        markGCCard(cUnit, rlSrc.lowReg, rlObj.lowReg);
-    }
-}
-
-STATIC void genIGetWide(CompilationUnit* cUnit, MIR* mir, RegLocation rlDest,
-                        RegLocation rlObj)
-{
-    RegLocation rlResult;
-    Field* fieldPtr = cUnit->dex_cache->GetResolvedField(mir->dalvikInsn.vC);
-#if ANDROID_SMP != 0
-    bool isVolatile = (fieldPtr == NULL) || fieldPtr->IsVolatile();
-#else
-    bool isVolatile = false;
-#endif
-    if (SLOW_FIELD_PATH || (fieldPtr == NULL) || isVolatile) {
-        getFieldOffset(cUnit, mir, fieldPtr);
-        // Field offset in r0
-        rlObj = loadValue(cUnit, rlObj, kCoreReg);
-        rlResult = oatEvalLoc(cUnit, rlDest, kAnyReg, true);
-        genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null obj? */
-        opRegReg(cUnit, kOpAdd, r0, rlObj.lowReg);
-        loadPair(cUnit, r0, rlResult.lowReg, rlResult.highReg);
-        oatGenMemBarrier(cUnit, kSY);
-        storeValueWide(cUnit, rlDest, rlResult);
-    } else {
-        int fieldOffset = fieldPtr->GetOffset().Int32Value();
-        rlObj = loadValue(cUnit, rlObj, kCoreReg);
-        int regPtr = oatAllocTemp(cUnit);
-
-        DCHECK(rlDest.wide);
-
-        genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null obj? */
-        opRegRegImm(cUnit, kOpAdd, regPtr, rlObj.lowReg, fieldOffset);
-        rlResult = oatEvalLoc(cUnit, rlDest, kAnyReg, true);
-
-        loadPair(cUnit, regPtr, rlResult.lowReg, rlResult.highReg);
-
-        oatFreeTemp(cUnit, regPtr);
-        storeValueWide(cUnit, rlDest, rlResult);
-    }
-}
-
-STATIC void genIPutWide(CompilationUnit* cUnit, MIR* mir, RegLocation rlSrc,
-                        RegLocation rlObj)
-{
-    Field* fieldPtr = cUnit->dex_cache->GetResolvedField(mir->dalvikInsn.vC);
-#if ANDROID_SMP != 0
-    bool isVolatile = (fieldPtr == NULL) || fieldPtr->IsVolatile();
-#else
-    bool isVolatile = false;
-#endif
-    if (SLOW_FIELD_PATH || (fieldPtr == NULL) || isVolatile) {
-        getFieldOffset(cUnit, mir, fieldPtr);
-        // Field offset in r0
-        rlObj = loadValue(cUnit, rlObj, kCoreReg);
-        rlSrc = loadValueWide(cUnit, rlSrc, kAnyReg);
-        genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null obj? */
-        opRegReg(cUnit, kOpAdd, r0, rlObj.lowReg);
-        oatGenMemBarrier(cUnit, kSY);
-        storePair(cUnit, r0, rlSrc.lowReg, rlSrc.highReg);
-    } else {
-        int fieldOffset = fieldPtr->GetOffset().Int32Value();
-
-        rlObj = loadValue(cUnit, rlObj, kCoreReg);
-        int regPtr;
-        rlSrc = loadValueWide(cUnit, rlSrc, kAnyReg);
-        genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null obj? */
-        regPtr = oatAllocTemp(cUnit);
-        opRegRegImm(cUnit, kOpAdd, regPtr, rlObj.lowReg, fieldOffset);
-
-        storePair(cUnit, regPtr, rlSrc.lowReg, rlSrc.highReg);
-
-        oatFreeTemp(cUnit, regPtr);
+        loadConstant(cUnit, r0, fieldIdx);
+        callRuntimeHelper(cUnit, rLR);
     }
 }
 
diff --git a/src/object_test.cc b/src/object_test.cc
index 82f52de..31b255e 100644
--- a/src/object_test.cc
+++ b/src/object_test.cc
@@ -217,7 +217,8 @@
   ASSERT_TRUE(field_id != NULL);
   uint32_t field_idx = dex_file->GetIndexForFieldId(*field_id);
 
-  Field* field = FindFieldFromCode(field_idx, clinit, true);
+  Field* field = FindFieldFromCode(field_idx, clinit, Thread::Current(), true,
+                                   false, sizeof(Object*));
   Object* s0 = field->GetObj(NULL);
   EXPECT_EQ(NULL, s0);
 
diff --git a/src/runtime_support.cc b/src/runtime_support.cc
index ff43d1f..668191b 100644
--- a/src/runtime_support.cc
+++ b/src/runtime_support.cc
@@ -445,175 +445,215 @@
 }
 
 // Fast path field resolution that can't throw exceptions
-static Field* FindFieldFast(uint32_t field_idx, const Method* referrer) {
+static Field* FindFieldFast(uint32_t field_idx, const Method* referrer, bool is_primitive,
+                            size_t expected_size) {
   Field* resolved_field = referrer->GetDexCacheResolvedFields()->Get(field_idx);
   if (UNLIKELY(resolved_field == NULL)) {
     return NULL;
   }
   Class* fields_class = resolved_field->GetDeclaringClass();
-  // Check class is initilaized or initializing
+  // Check class is initiliazed or initializing
   if (UNLIKELY(!fields_class->IsInitializing())) {
     return NULL;
   }
+  Class* referring_class = referrer->GetDeclaringClass();
+  if (UNLIKELY(!referring_class->CanAccess(fields_class) ||
+               !referring_class->CanAccessMember(fields_class,
+                                                 resolved_field->GetAccessFlags()))) {
+    // illegal access
+    return NULL;
+  }
+  FieldHelper fh(resolved_field);
+  if (UNLIKELY(fh.IsPrimitiveType() != is_primitive ||
+               fh.FieldSize() != expected_size)) {
+    return NULL;
+  }
   return resolved_field;
 }
 
 // Slow path field resolution and declaring class initialization
-Field* FindFieldFromCode(uint32_t field_idx, const Method* referrer, bool is_static) {
+Field* FindFieldFromCode(uint32_t field_idx, const Method* referrer, Thread* self,
+                         bool is_static, bool is_primitive, size_t expected_size) {
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
   Field* resolved_field = class_linker->ResolveField(field_idx, referrer, is_static);
   if (LIKELY(resolved_field != NULL)) {
     Class* fields_class = resolved_field->GetDeclaringClass();
-    // If the class is already initializing, we must be inside <clinit>, or
-    // we'd still be waiting for the lock.
-    if (fields_class->IsInitializing()) {
-      return resolved_field;
+    Class* referring_class = referrer->GetDeclaringClass();
+    if (UNLIKELY(!referring_class->CanAccess(fields_class))) {
+      self->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;","%s tried to access class %s",
+                               PrettyMethod(referrer).c_str(),
+                               PrettyDescriptor(fields_class).c_str());
+    } else if (UNLIKELY(!referring_class->CanAccessMember(fields_class,
+                                                          resolved_field->GetAccessFlags()))) {
+      self->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;","%s tried to access field %s",
+                               PrettyMethod(referrer).c_str(),
+                               PrettyField(resolved_field, false).c_str());
+      return NULL;  // failure
     }
-    if (Runtime::Current()->GetClassLinker()->EnsureInitialized(fields_class, true)) {
+    FieldHelper fh(resolved_field);
+    if (UNLIKELY(fh.IsPrimitiveType() != is_primitive ||
+                 fh.FieldSize() != expected_size)) {
+      self->ThrowNewExceptionF("Ljava/lang/NoSuchFieldError;",
+                               "Attempted read of %d-bit %s on field '%s'",
+                               expected_size * (32 / sizeof(int32_t)),
+                               is_primitive ? "primitive" : "non-primitive",
+                               PrettyField(resolved_field, true).c_str());
+    }
+    if (!is_static) {
+      // instance fields must be being accessed on an initialized class
       return resolved_field;
+    } else {
+      // If the class is already initializing, we must be inside <clinit>, or
+      // we'd still be waiting for the lock.
+      if (fields_class->IsInitializing()) {
+        return resolved_field;
+      } else if (Runtime::Current()->GetClassLinker()->EnsureInitialized(fields_class, true)) {
+        return resolved_field;
+      }
     }
   }
-  DCHECK(Thread::Current()->IsExceptionPending()); // Throw exception and unwind
+  DCHECK(self->IsExceptionPending());  // Throw exception and unwind
   return NULL;
 }
 
-extern "C" Field* artFindInstanceFieldFromCode(uint32_t field_idx, const Method* referrer,
-                                               Thread* self, Method** sp) {
-  Field* resolved_field = FindFieldFast(field_idx, referrer);
-  if (UNLIKELY(resolved_field == NULL)) {
-    FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
-    resolved_field = FindFieldFromCode(field_idx, referrer, false);
-  }
-  return resolved_field;
+static void ThrowNullPointerExceptionForFieldAccess(Thread* self, Field* field, bool is_read) {
+  self->ThrowNewExceptionF("Ljava/lang/NullPointerException;",
+                           "Attempt to %s field '%s' of a null object",
+                           is_read ? "read from" : "write to",
+                           PrettyField(field, true).c_str());
 }
 
 extern "C" uint32_t artGet32StaticFromCode(uint32_t field_idx, const Method* referrer,
                                            Thread* self, Method** sp) {
-  Field* field = FindFieldFast(field_idx, referrer);
+  Field* field = FindFieldFast(field_idx, referrer, true, sizeof(int32_t));
   if (LIKELY(field != NULL)) {
-    FieldHelper fh(field);
-    if (LIKELY(fh.IsPrimitiveType() && fh.FieldSize() == sizeof(int32_t))) {
-      return field->Get32(NULL);
-    }
+    return field->Get32(NULL);
   }
   FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
-  field = FindFieldFromCode(field_idx, referrer, true);
-  if (field != NULL) {
-    FieldHelper fh(field);
-    if (!fh.IsPrimitiveType() || fh.FieldSize() != sizeof(int32_t)) {
-      self->ThrowNewExceptionF("Ljava/lang/NoSuchFieldError;",
-                               "Attempted read of 32-bit primitive on field '%s'",
-                               PrettyField(field, true).c_str());
-    } else {
-      return field->Get32(NULL);
-    }
+  field = FindFieldFromCode(field_idx, referrer, self, true, true, sizeof(int32_t));
+  if (LIKELY(field != NULL)) {
+    return field->Get32(NULL);
   }
   return 0;  // Will throw exception by checking with Thread::Current
 }
 
 extern "C" uint64_t artGet64StaticFromCode(uint32_t field_idx, const Method* referrer,
                                            Thread* self, Method** sp) {
-  Field* field = FindFieldFast(field_idx, referrer);
+  Field* field = FindFieldFast(field_idx, referrer, true, sizeof(int64_t));
   if (LIKELY(field != NULL)) {
-    FieldHelper fh(field);
-    if (LIKELY(fh.IsPrimitiveType() && fh.FieldSize() == sizeof(int64_t))) {
-      return field->Get64(NULL);
-    }
+    return field->Get64(NULL);
   }
   FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
-  field = FindFieldFromCode(field_idx, referrer, true);
-  if (field != NULL) {
-    FieldHelper fh(field);
-    if (!fh.IsPrimitiveType() || fh.FieldSize() != sizeof(int64_t)) {
-      self->ThrowNewExceptionF("Ljava/lang/NoSuchFieldError;",
-                               "Attempted read of 64-bit primitive on field '%s'",
-                               PrettyField(field, true).c_str());
-    } else {
-      return field->Get64(NULL);
-    }
+  field = FindFieldFromCode(field_idx, referrer, self, true, true, sizeof(int64_t));
+  if (LIKELY(field != NULL)) {
+    return field->Get64(NULL);
   }
   return 0;  // Will throw exception by checking with Thread::Current
 }
 
 extern "C" Object* artGetObjStaticFromCode(uint32_t field_idx, const Method* referrer,
                                            Thread* self, Method** sp) {
-  Field* field = FindFieldFast(field_idx, referrer);
+  Field* field = FindFieldFast(field_idx, referrer, false, sizeof(Object*));
   if (LIKELY(field != NULL)) {
-    FieldHelper fh(field);
-    if (LIKELY(!fh.IsPrimitiveType())) {
-      return field->GetObj(NULL);
-    }
+    return field->GetObj(NULL);
   }
   FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
-  field = FindFieldFromCode(field_idx, referrer, true);
-  if (field != NULL) {
-    FieldHelper fh(field);
-    if (fh.IsPrimitiveType()) {
-      self->ThrowNewExceptionF("Ljava/lang/NoSuchFieldError;",
-                               "Attempted read of reference on primitive field '%s'",
-                               PrettyField(field, true).c_str());
+  field = FindFieldFromCode(field_idx, referrer, self, true, false, sizeof(Object*));
+  if (LIKELY(field != NULL)) {
+    return field->GetObj(NULL);
+  }
+  return NULL;  // Will throw exception by checking with Thread::Current
+}
+
+extern "C" uint32_t artGet32InstanceFromCode(uint32_t field_idx, Object* obj,
+                                             const Method* referrer, Thread* self, Method** sp) {
+  Field* field = FindFieldFast(field_idx, referrer, true, sizeof(int32_t));
+  if (LIKELY(field != NULL && obj != NULL)) {
+    return field->Get32(obj);
+  }
+  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
+  field = FindFieldFromCode(field_idx, referrer, self, false, true, sizeof(int32_t));
+  if (LIKELY(field != NULL)) {
+    if (UNLIKELY(obj == NULL)) {
+      ThrowNullPointerExceptionForFieldAccess(self, field, true);
     } else {
-      return field->GetObj(NULL);
+      return field->Get32(obj);
+    }
+  }
+  return 0;  // Will throw exception by checking with Thread::Current
+}
+
+extern "C" uint64_t artGet64InstanceFromCode(uint32_t field_idx, Object* obj,
+                                             const Method* referrer, Thread* self, Method** sp) {
+  Field* field = FindFieldFast(field_idx, referrer, true, sizeof(int64_t));
+  if (LIKELY(field != NULL && obj != NULL)) {
+    return field->Get64(obj);
+  }
+  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
+  field = FindFieldFromCode(field_idx, referrer, self, false, true, sizeof(int64_t));
+  if (LIKELY(field != NULL)) {
+    if (UNLIKELY(obj == NULL)) {
+      ThrowNullPointerExceptionForFieldAccess(self, field, true);
+    } else {
+      return field->Get64(obj);
+    }
+  }
+  return 0;  // Will throw exception by checking with Thread::Current
+}
+
+extern "C" Object* artGetObjInstanceFromCode(uint32_t field_idx, Object* obj,
+                                              const Method* referrer, Thread* self, Method** sp) {
+  Field* field = FindFieldFast(field_idx, referrer, false, sizeof(Object*));
+  if (LIKELY(field != NULL && obj != NULL)) {
+    return field->GetObj(obj);
+  }
+  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
+  field = FindFieldFromCode(field_idx, referrer, self, false, false, sizeof(Object*));
+  if (LIKELY(field != NULL)) {
+    if (UNLIKELY(obj == NULL)) {
+      ThrowNullPointerExceptionForFieldAccess(self, field, true);
+    } else {
+      return field->GetObj(obj);
     }
   }
   return NULL;  // Will throw exception by checking with Thread::Current
 }
 
-extern "C" int artSet32StaticFromCode(uint32_t field_idx, const Method* referrer,
-                                       uint32_t new_value, Thread* self, Method** sp) {
-  Field* field = FindFieldFast(field_idx, referrer);
+extern "C" int artSet32StaticFromCode(uint32_t field_idx, uint32_t new_value,
+                                      const Method* referrer, Thread* self, Method** sp) {
+  Field* field = FindFieldFast(field_idx, referrer, true, sizeof(int32_t));
   if (LIKELY(field != NULL)) {
-    FieldHelper fh(field);
-    if (LIKELY(fh.IsPrimitiveType() && fh.FieldSize() == sizeof(int32_t))) {
-      field->Set32(NULL, new_value);
-      return 0;  // success
-    }
+    field->Set32(NULL, new_value);
+    return 0;  // success
   }
   FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
-  field = FindFieldFromCode(field_idx, referrer, true);
-  if (field != NULL) {
-    FieldHelper fh(field);
-    if (!fh.IsPrimitiveType() || fh.FieldSize() != sizeof(int32_t)) {
-      self->ThrowNewExceptionF("Ljava/lang/NoSuchFieldError;",
-                               "Attempted write of 32-bit primitive to field '%s'",
-                               PrettyField(field, true).c_str());
-    } else {
-      field->Set32(NULL, new_value);
-      return 0;  // success
-    }
+  field = FindFieldFromCode(field_idx, referrer, self, true, true, sizeof(int32_t));
+  if (LIKELY(field != NULL)) {
+    field->Set32(NULL, new_value);
+    return 0;  // success
   }
   return -1;  // failure
 }
 
 extern "C" int artSet64StaticFromCode(uint32_t field_idx, const Method* referrer,
                                       uint64_t new_value, Thread* self, Method** sp) {
-  Field* field = FindFieldFast(field_idx, referrer);
+  Field* field = FindFieldFast(field_idx, referrer, true, sizeof(int64_t));
   if (LIKELY(field != NULL)) {
-    FieldHelper fh(field);
-    if (LIKELY(fh.IsPrimitiveType() && fh.FieldSize() == sizeof(int64_t))) {
-      field->Set64(NULL, new_value);
-      return 0;  // success
-    }
+    field->Set64(NULL, new_value);
+    return 0;  // success
   }
   FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
-  field = FindFieldFromCode(field_idx, referrer, true);
+  field = FindFieldFromCode(field_idx, referrer, self, true, true, sizeof(int64_t));
   if (LIKELY(field != NULL)) {
-    FieldHelper fh(field);
-    if (UNLIKELY(!fh.IsPrimitiveType() || fh.FieldSize() != sizeof(int64_t))) {
-      self->ThrowNewExceptionF("Ljava/lang/NoSuchFieldError;",
-                               "Attempted write of 64-bit primitive to field '%s'",
-                               PrettyField(field, true).c_str());
-    } else {
-      field->Set64(NULL, new_value);
-      return 0;  // success
-    }
+    field->Set64(NULL, new_value);
+    return 0;  // success
   }
   return -1;  // failure
 }
 
-extern "C" int artSetObjStaticFromCode(uint32_t field_idx, const Method* referrer,
-                                       Object* new_value, Thread* self, Method** sp) {
-  Field* field = FindFieldFast(field_idx, referrer);
+extern "C" int artSetObjStaticFromCode(uint32_t field_idx, Object* new_value,
+                                       const Method* referrer, Thread* self, Method** sp) {
+  Field* field = FindFieldFast(field_idx, referrer, false, sizeof(Object*));
   if (LIKELY(field != NULL)) {
     if (LIKELY(!FieldHelper(field).IsPrimitiveType())) {
       field->SetObj(NULL, new_value);
@@ -621,14 +661,71 @@
     }
   }
   FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
-  field = FindFieldFromCode(field_idx, referrer, true);
-  if (field != NULL) {
-    if (FieldHelper(field).IsPrimitiveType()) {
-      self->ThrowNewExceptionF("Ljava/lang/NoSuchFieldError;",
-                               "Attempted write of reference to primitive field '%s'",
-                               PrettyField(field, true).c_str());
+  field = FindFieldFromCode(field_idx, referrer, self, true, false, sizeof(Object*));
+  if (LIKELY(field != NULL)) {
+    field->SetObj(NULL, new_value);
+    return 0;  // success
+  }
+  return -1;  // failure
+}
+
+extern "C" int artSet32InstanceFromCode(uint32_t field_idx, Object* obj, uint32_t new_value,
+                                        const Method* referrer, Thread* self, Method** sp) {
+  Field* field = FindFieldFast(field_idx, referrer, true, sizeof(int32_t));
+  if (LIKELY(field != NULL && obj != NULL)) {
+    field->Set32(obj, new_value);
+    return 0;  // success
+  }
+  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
+  field = FindFieldFromCode(field_idx, referrer, self, false, true, sizeof(int32_t));
+  if (LIKELY(field != NULL)) {
+    if (UNLIKELY(obj == NULL)) {
+      ThrowNullPointerExceptionForFieldAccess(self, field, false);
     } else {
-      field->SetObj(NULL, new_value);
+      field->Set32(obj, new_value);
+      return 0;  // success
+    }
+  }
+  return -1;  // failure
+}
+
+extern "C" int artSet64InstanceFromCode(uint32_t field_idx, Object* obj, uint64_t new_value,
+                                        Thread* self, Method** sp) {
+  Method* callee_save = Runtime::Current()->GetCalleeSaveMethod(Runtime::kRefsOnly);
+  Method* referrer = sp[callee_save->GetFrameSizeInBytes() / sizeof(Method*)];
+  Field* field = FindFieldFast(field_idx, referrer, true, sizeof(int64_t));
+  if (LIKELY(field != NULL  && obj != NULL)) {
+    field->Set64(obj, new_value);
+    return 0;  // success
+  }
+  *sp = callee_save;
+  self->SetTopOfStack(sp, 0);
+  field = FindFieldFromCode(field_idx, referrer, self, false, true, sizeof(int64_t));
+  if (LIKELY(field != NULL)) {
+    if (UNLIKELY(obj == NULL)) {
+      ThrowNullPointerExceptionForFieldAccess(self, field, false);
+    } else {
+      field->Set64(obj, new_value);
+      return 0;  // success
+    }
+  }
+  return -1;  // failure
+}
+
+extern "C" int artSetObjInstanceFromCode(uint32_t field_idx, Object* obj, Object* new_value,
+                                         const Method* referrer, Thread* self, Method** sp) {
+  Field* field = FindFieldFast(field_idx, referrer, false, sizeof(Object*));
+  if (LIKELY(field != NULL && obj != NULL)) {
+    field->SetObj(obj, new_value);
+    return 0;  // success
+  }
+  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
+  field = FindFieldFromCode(field_idx, referrer, self, false, false, sizeof(Object*));
+  if (LIKELY(field != NULL)) {
+    if (UNLIKELY(obj == NULL)) {
+      ThrowNullPointerExceptionForFieldAccess(self, field, false);
+    } else {
+      field->SetObj(obj, new_value);
       return 0;  // success
     }
   }
diff --git a/src/runtime_support.h b/src/runtime_support.h
index 68e9587..e5097e5 100644
--- a/src/runtime_support.h
+++ b/src/runtime_support.h
@@ -29,7 +29,8 @@
                                          Thread* self, bool access_check);
 extern void DebugMe(Method* method, uint32_t info);
 extern Object* DecodeJObjectInThread(Thread* thread, jobject obj);
-extern Field* FindFieldFromCode(uint32_t field_idx, const Method* referrer, bool is_static);
+extern Field* FindFieldFromCode(uint32_t field_idx, const Method* referrer, Thread* self,
+                                bool is_static, bool is_primitive, size_t expected_size);
 extern void* FindNativeMethod(Thread* thread);
 extern void ThrowAbstractMethodErrorFromCode(Method* method, Thread* thread, Method** sp);
 void* UnresolvedDirectMethodTrampolineFromCode(int32_t, Method**, Thread*, Runtime::TrampolineType);
@@ -51,11 +52,18 @@
 
 #if defined(__arm__)
   /* Compiler helpers */
-  extern "C" int art_set32_static_from_code(uint32_t, void*, int32_t);
-  extern "C" int art_set64_static_from_code(uint32_t, void*, int64_t);
-  extern "C" int art_set_obj_static_from_code(uint32_t, void*, void*);
-  extern "C" int32_t art_get32_static_from_code(uint32_t, void*);
-  extern "C" int64_t art_get64_static_from_code(uint32_t, void*);
+  extern "C" int32_t art_get32_static_from_code(uint32_t);
+  extern "C" int64_t art_get64_static_from_code(uint32_t);
+  extern "C" void* art_get_obj_static_from_code(uint32_t);
+  extern "C" int32_t art_get32_instance_from_code(uint32_t, void*);
+  extern "C" int64_t art_get64_instance_from_code(uint32_t, void*);
+  extern "C" void* art_get_obj_instance_from_code(uint32_t, void*);
+  extern "C" int art_set32_static_from_code(uint32_t, int32_t);
+  extern "C" int art_set64_static_from_code(uint32_t, int64_t);
+  extern "C" int art_set_obj_static_from_code(uint32_t, void*);
+  extern "C" int art_set32_instance_from_code(uint32_t, void*, int32_t);
+  extern "C" int art_set64_instance_from_code(uint32_t, void*, int64_t);
+  extern "C" int art_set_obj_instance_from_code(uint32_t, void*, void*);
   extern "C" void art_can_put_array_element_from_code(void*, void*);
   extern "C" void art_check_cast_from_code(void*, void*);
   extern "C" void art_do_long_jump(uint32_t*, uint32_t*);
@@ -78,9 +86,6 @@
   extern "C" void* art_alloc_object_from_code_with_access_check(uint32_t type_idx, void* method);
   extern "C" void* art_check_and_alloc_array_from_code(uint32_t, void*, int32_t);
   extern "C" void* art_check_and_alloc_array_from_code_with_access_check(uint32_t, void*, int32_t);
-  extern "C" void* art_find_instance_field_from_code(uint32_t, void*);
-  extern "C" void* art_find_static_field_from_code(uint32_t, void*);
-  extern "C" void* art_get_obj_static_from_code(uint32_t, void*);
   extern "C" void* art_initialize_static_storage_from_code(uint32_t, void*);
   extern "C" void* art_initialize_type_from_code(uint32_t, void*);
   extern "C" void* art_initialize_type_and_verify_access_from_code(uint32_t, void*);
diff --git a/src/runtime_support_arm.S b/src/runtime_support_arm.S
index cee9043..d406436 100644
--- a/src/runtime_support_arm.S
+++ b/src/runtime_support_arm.S
@@ -311,21 +311,6 @@
     bxne   lr                                  @ return on success
     DELIVER_PENDING_EXCEPTION
 
-    .global art_find_instance_field_from_code
-    .extern artFindInstanceFieldFromCode
-    /*
-     * Called by managed code to resolve a field of an object
-     */
-art_find_instance_field_from_code:
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME     @ save callee saves in case of GC
-    mov    r2, r9                        @ pass Thread::Current
-    mov    r3, sp                        @ pass SP
-    bl     artFindInstanceFieldFromCode  @ (uint32_t field_idx, const Method* referrer, Thread*, SP)
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
-    cmp    r0, #0                        @ success if result is non-null
-    bxne   lr                            @ return on success
-    DELIVER_PENDING_EXCEPTION
-
     .global art_get32_static_from_code
     .extern artGet32StaticFromCode
     /*
@@ -333,6 +318,7 @@
      */
 art_get32_static_from_code:
     SETUP_REF_ONLY_CALLEE_SAVE_FRAME     @ save callee saves in case of GC
+    ldr    r1, [sp, #32]                 @ pass referrer
     mov    r2, r9                        @ pass Thread::Current
     mov    r3, sp                        @ pass SP
     bl     artGet32StaticFromCode        @ (uint32_t field_idx, const Method* referrer, Thread*, SP)
@@ -349,6 +335,7 @@
      */
 art_get64_static_from_code:
     SETUP_REF_ONLY_CALLEE_SAVE_FRAME     @ save callee saves in case of GC
+    ldr    r1, [sp, #32]                 @ pass referrer
     mov    r2, r9                        @ pass Thread::Current
     mov    r3, sp                        @ pass SP
     bl     artGet64StaticFromCode        @ (uint32_t field_idx, const Method* referrer, Thread*, SP)
@@ -365,6 +352,7 @@
      */
 art_get_obj_static_from_code:
     SETUP_REF_ONLY_CALLEE_SAVE_FRAME     @ save callee saves in case of GC
+    ldr    r1, [sp, #32]                 @ pass referrer
     mov    r2, r9                        @ pass Thread::Current
     mov    r3, sp                        @ pass SP
     bl     artGetObjStaticFromCode       @ (uint32_t field_idx, const Method* referrer, Thread*, SP)
@@ -374,6 +362,57 @@
     bxeq   lr                            @ return on success
     DELIVER_PENDING_EXCEPTION
 
+    .global art_get32_instance_from_code
+    .extern artGet32InstanceFromCode
+    /*
+     * Called by managed code to resolve an instance field and load a 32-bit primitive value
+     */
+art_get32_instance_from_code:
+    SETUP_REF_ONLY_CALLEE_SAVE_FRAME     @ save callee saves in case of GC
+    ldr    r2, [sp, #32]                 @ pass referrer
+    mov    r3, r9                        @ pass Thread::Current
+    str    sp, [sp, #0]                  @ pass SP
+    bl     artGet32InstanceFromCode      @ (field_idx, Object*, referrer, Thread*, SP)
+    ldr    r12, [r9, #THREAD_EXCEPTION_OFFSET]  @ load Thread::Current()->exception_
+    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+    cmp    r12, #0                       @ success if no exception is pending
+    bxeq   lr                            @ return on success
+    DELIVER_PENDING_EXCEPTION
+
+    .global art_get64_instance_from_code
+    .extern artGet64InstanceFromCode
+    /*
+     * Called by managed code to resolve an instance field and load a 64-bit primitive value
+     */
+art_get64_instance_from_code:
+    SETUP_REF_ONLY_CALLEE_SAVE_FRAME     @ save callee saves in case of GC
+    ldr    r2, [sp, #32]                 @ pass referrer
+    mov    r3, r9                        @ pass Thread::Current
+    str    sp, [sp, #0]                  @ pass SP
+    bl     artGet64InstanceFromCode      @ (field_idx, Object*, referrer, Thread*, SP)
+    ldr    r12, [r9, #THREAD_EXCEPTION_OFFSET]  @ load Thread::Current()->exception_
+    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+    cmp    r12, #0                       @ success if no exception is pending
+    bxeq    lr                           @ return on success
+    DELIVER_PENDING_EXCEPTION
+
+    .global art_get_obj_instance_from_code
+    .extern artGetObjInstanceFromCode
+    /*
+     * Called by managed code to resolve an instance field and load an object reference
+     */
+art_get_obj_instance_from_code:
+    SETUP_REF_ONLY_CALLEE_SAVE_FRAME     @ save callee saves in case of GC
+    ldr    r2, [sp, #32]                 @ pass referrer
+    mov    r3, r9                        @ pass Thread::Current
+    str    sp, [sp, #0]                  @ pass SP
+    bl     artGetObjInstanceFromCode     @ (field_idx, Object*, referrer, Thread*, SP)
+    ldr    r12, [r9, #THREAD_EXCEPTION_OFFSET]  @ load Thread::Current()->exception_
+    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+    cmp    r12, #0                       @ success if no exception is pending
+    bxeq   lr                            @ return on success
+    DELIVER_PENDING_EXCEPTION
+
     .global art_set32_static_from_code
     .extern artSet32StaticFromCode
     /*
@@ -381,9 +420,10 @@
      */
 art_set32_static_from_code:
     SETUP_REF_ONLY_CALLEE_SAVE_FRAME     @ save callee saves in case of GC
+    ldr    r2, [sp, #32]                 @ pass referrer
     mov    r3, r9                        @ pass Thread::Current
     str    sp, [sp, #0]                  @ pass SP
-    bl     artSet32StaticFromCode        @ (field_idx, referrer, new_val, Thread*, SP)
+    bl     artSet32StaticFromCode        @ (field_idx, new_val, referrer, Thread*, SP)
     RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
     cmp    r0, #0                        @ success if result is 0
     bxeq   lr                            @ return on success
@@ -396,6 +436,7 @@
      */
 art_set64_static_from_code:
     SETUP_REF_ONLY_CALLEE_SAVE_FRAME     @ save callee saves in case of GC
+    ldr    r1, [sp, #32]                 @ pass referrer
     mov    r12, sp                       @ save SP
     sub    sp, #8                        @ grow frame for alignment with stack args
     push   {r9, r12}                     @ pass Thread::Current and SP
@@ -413,14 +454,68 @@
      */
 art_set_obj_static_from_code:
     SETUP_REF_ONLY_CALLEE_SAVE_FRAME     @ save callee saves in case of GC
+    ldr    r2, [sp, #32]                 @ pass referrer
     mov    r3, r9                        @ pass Thread::Current
     str    sp, [sp, #0]                  @ pass SP
-    bl     artSetObjStaticFromCode       @ (field_idx, referrer, new_val, Thread*, SP)
+    bl     artSetObjStaticFromCode       @ (field_idx, new_val, referrer, Thread*, SP)
     RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
     cmp    r0, #0                        @ success if result is 0
     bxeq   lr                            @ return on success
     DELIVER_PENDING_EXCEPTION
 
+    .global art_set32_instance_from_code
+    .extern artSet32InstanceFromCode
+    /*
+     * Called by managed code to resolve an instance field and store a 32-bit primitive value
+     */
+art_set32_instance_from_code:
+    SETUP_REF_ONLY_CALLEE_SAVE_FRAME     @ save callee saves in case of GC
+    ldr    r3, [sp, #32]                 @ pass referrer
+    mov    r12, sp                       @ save SP
+    sub    sp, #8                        @ grow frame for alignment with stack args
+    push   {r9, r12}                     @ pass Thread::Current and SP
+    bl     artSet32InstanceFromCode      @ (field_idx, Object*, new_val, referrer, Thread*, SP)
+    add    sp, #16                       @ release out args
+    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME   @ TODO: we can clearly save an add here
+    cmp    r0, #0                        @ success if result is 0
+    bxeq   lr                            @ return on success
+    DELIVER_PENDING_EXCEPTION
+
+    .global art_set64_instance_from_code
+    .extern artSet32InstanceFromCode
+    /*
+     * Called by managed code to resolve an instance field and store a 64-bit primitive value
+     */
+art_set64_instance_from_code:
+    SETUP_REF_ONLY_CALLEE_SAVE_FRAME     @ save callee saves in case of GC
+    mov    r12, sp                       @ save SP
+    sub    sp, #8                        @ grow frame for alignment with stack args
+    push   {r9, r12}                     @ pass Thread::Current and SP
+    bl     artSet64InstanceFromCode      @ (field_idx, Object*, new_val, Thread*, SP)
+    add    sp, #16                       @ release out args
+    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME   @ TODO: we can clearly save an add here
+    cmp    r0, #0                        @ success if result is 0
+    bxeq   lr                            @ return on success
+    DELIVER_PENDING_EXCEPTION
+
+    .global art_set_obj_instance_from_code
+    .extern artSetObjInstanceFromCode
+    /*
+     * Called by managed code to resolve an instance field and store an object reference
+     */
+art_set_obj_instance_from_code:
+    SETUP_REF_ONLY_CALLEE_SAVE_FRAME     @ save callee saves in case of GC
+    ldr    r3, [sp, #32]                 @ pass referrer
+    mov    r12, sp                       @ save SP
+    sub    sp, #8                        @ grow frame for alignment with stack args
+    push   {r9, r12}                     @ pass Thread::Current and SP
+    bl     artSetObjInstanceFromCode     @ (field_idx, Object*, new_val, referrer, Thread*, SP)
+    add    sp, #16                       @ release out args
+    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME   @ TODO: we can clearly save an add here
+    cmp    r0, #0                        @ success if result is 0
+    bxeq   lr                            @ return on success
+    DELIVER_PENDING_EXCEPTION
+
    .global art_resolve_method_from_code
     .extern artResolveMethodFromCode
     /*
diff --git a/src/thread.cc b/src/thread.cc
index 6c8e1bb..a484f92 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -105,7 +105,9 @@
   pCheckAndAllocArrayFromCode = art_check_and_alloc_array_from_code;
   pCheckAndAllocArrayFromCodeWithAccessCheck = art_check_and_alloc_array_from_code_with_access_check;
   pCheckCastFromCode = art_check_cast_from_code;
-  pFindInstanceFieldFromCode = art_find_instance_field_from_code;
+  pGet32Instance = art_get32_instance_from_code;
+  pGet64Instance = art_get64_instance_from_code;
+  pGetObjInstance = art_get_obj_instance_from_code;
   pGet32Static = art_get32_static_from_code;
   pGet64Static = art_get64_static_from_code;
   pGetObjStatic = art_get_obj_static_from_code;
@@ -117,6 +119,9 @@
   pLockObjectFromCode = art_lock_object_from_code;
   pObjectInit = art_object_init_from_code;
   pResolveStringFromCode = art_resolve_string_from_code;
+  pSet32Instance = art_set32_instance_from_code;
+  pSet64Instance = art_set64_instance_from_code;
+  pSetObjInstance = art_set_obj_instance_from_code;
   pSet32Static = art_set32_static_from_code;
   pSet64Static = art_set64_static_from_code;
   pSetObjStatic = art_set_obj_static_from_code;
diff --git a/src/thread.h b/src/thread.h
index bade52e..a6fa0ca 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -131,12 +131,14 @@
   void (*pCheckCastFromCode)(void*, void*);
   Object* (*pDecodeJObjectInThread)(Thread* thread, jobject obj);
   void (*pDeliverException)(void*);
-  void* (*pFindInstanceFieldFromCode)(uint32_t, void*);
   Method* (*pFindInterfaceMethodInCache)(Class*, uint32_t, const Method*, struct DvmDex*);
   void* (*pFindNativeMethod)(Thread* thread);
-  int32_t (*pGet32Static)(uint32_t, void*);
-  int64_t (*pGet64Static)(uint32_t, void*);
-  void* (*pGetObjStatic)(uint32_t, void*);
+  int32_t (*pGet32Instance)(uint32_t, void*);
+  int64_t (*pGet64Instance)(uint32_t, void*);
+  void* (*pGetObjInstance)(uint32_t, void*);
+  int32_t (*pGet32Static)(uint32_t);
+  int64_t (*pGet64Static)(uint32_t);
+  void* (*pGetObjStatic)(uint32_t);
   void (*pHandleFillArrayDataFromCode)(void*, void*);
   void* (*pInitializeStaticStorage)(uint32_t, void*);
   uint32_t (*pInstanceofNonTrivialFromCode)(const Class*, const Class*);
@@ -147,9 +149,12 @@
   void (*pObjectInit)(void*);
   void* (*pResolveMethodFromCode)(void*, uint32_t, bool);
   void* (*pResolveStringFromCode)(void*, uint32_t);
-  int (*pSet32Static)(uint32_t, void*, int32_t);
-  int (*pSet64Static)(uint32_t, void*, int64_t);
-  int (*pSetObjStatic)(uint32_t, void*, void*);
+  int (*pSet32Instance)(uint32_t, void*, int32_t);  // field_idx, obj, src
+  int (*pSet64Instance)(uint32_t, void*, int64_t);
+  int (*pSetObjInstance)(uint32_t, void*, void*);
+  int (*pSet32Static)(uint32_t, int32_t);
+  int (*pSet64Static)(uint32_t, int64_t);
+  int (*pSetObjStatic)(uint32_t, void*);
   void (*pThrowStackOverflowFromCode)(void*);
   void (*pThrowNullPointerFromCode)();
   void (*pThrowArrayBoundsFromCode)(int32_t, int32_t);
diff --git a/test/075-verification-error/src/Main.java b/test/075-verification-error/src/Main.java
index 51d648c..9b66a8d 100644
--- a/test/075-verification-error/src/Main.java
+++ b/test/075-verification-error/src/Main.java
@@ -88,7 +88,7 @@
 
         try {
             int x = mutant.inaccessibleField;
-            System.err.println("ERROR: bad access succeeded\n");
+            System.err.println("ERROR: bad access succeeded (ifield)");
         } catch (IllegalAccessError iae) {
             System.out.println("Got expected IllegalAccessError (ifield)");
             if (VERBOSE) System.out.println("--- " + iae);
@@ -96,7 +96,7 @@
 
         try {
             int y = Mutant.inaccessibleStaticField;
-            System.err.println("ERROR: bad access succeeded\n");
+            System.err.println("ERROR: bad access succeeded (sfield)");
         } catch (IllegalAccessError iae) {
             System.out.println("Got expected IllegalAccessError (sfield)");
             if (VERBOSE) System.out.println("--- " + iae);
@@ -104,7 +104,7 @@
 
         try {
             mutant.inaccessibleMethod();
-            System.err.println("ERROR: bad access succeeded\n");
+            System.err.println("ERROR: bad access succeeded (method)");
         } catch (IllegalAccessError iae) {
             System.out.println("Got expected IllegalAccessError (method)");
             if (VERBOSE) System.out.println("--- " + iae);
@@ -112,7 +112,7 @@
 
         try {
             Mutant.inaccessibleStaticMethod();
-            System.err.println("ERROR: bad access succeeded\n");
+            System.err.println("ERROR: bad access succeeded (smethod)");
         } catch (IllegalAccessError iae) {
             System.out.println("Got expected IllegalAccessError (smethod)");
             if (VERBOSE) System.out.println("--- " + iae);
@@ -121,7 +121,7 @@
         try {
             /* accessible static method in an inaccessible class */
             InaccessibleClass.test();
-            System.err.println("ERROR: bad meth-class access succeeded\n");
+            System.err.println("ERROR: bad meth-class access succeeded (meth-class)");
         } catch (IllegalAccessError iae) {
             System.out.println("Got expected IllegalAccessError (meth-class)");
             if (VERBOSE) System.out.println("--- " + iae);
@@ -130,7 +130,7 @@
         try {
             /* accessible static field in an inaccessible class */
             int blah = InaccessibleClass.blah;
-            System.err.println("ERROR: bad field-class access succeeded\n");
+            System.err.println("ERROR: bad field-class access succeeded (field-class)");
         } catch (IllegalAccessError iae) {
             System.out.println("Got expected IllegalAccessError (field-class)");
             if (VERBOSE) System.out.println("--- " + iae);
@@ -139,7 +139,7 @@
         try {
             /* inaccessible static method in an accessible class */
             InaccessibleMethod.test();
-            System.err.println("ERROR: bad access succeeded\n");
+            System.err.println("ERROR: bad access succeeded (meth-meth)");
         } catch (IllegalAccessError iae) {
             System.out.println("Got expected IllegalAccessError (meth-meth)");
             if (VERBOSE) System.out.println("--- " + iae);