Reland "Use nterp cache for fields in switch interpreter."

This reverts commit 6f78517b8de12bac6fc2015ffaaab712347aea59.

Reason for revert: Fix in https://android-review.googlesource.com/c/platform/art/+/2267547.

Change-Id: If33e6d7e7a6852aa36e3b9da96e88c3a5b3f01a8
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index 91c2663..847cbfb 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -376,76 +376,77 @@
                                              allocator_type);
 }
 
-template<FindFieldType type, bool access_check>
+FLATTEN
+inline ArtField* ResolveFieldWithAccessChecks(Thread* self,
+                                              ClassLinker* class_linker,
+                                              uint16_t field_index,
+                                              ArtMethod* caller,
+                                              bool is_static,
+                                              bool is_put,
+                                              size_t resolve_field_type)  // Resolve if not zero
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  if (caller->SkipAccessChecks()) {
+    return class_linker->ResolveField(field_index, caller, is_static);
+  }
+
+  caller = caller->GetInterfaceMethodIfProxy(class_linker->GetImagePointerSize());
+
+  StackHandleScope<2> hs(self);
+  Handle<mirror::DexCache> h_dex_cache(hs.NewHandle(caller->GetDexCache()));
+  Handle<mirror::ClassLoader> h_class_loader(hs.NewHandle(caller->GetClassLoader()));
+
+  ArtField* resolved_field = class_linker->ResolveFieldJLS(field_index,
+                                                           h_dex_cache,
+                                                           h_class_loader);
+  if (resolved_field == nullptr) {
+    return nullptr;
+  }
+
+  ObjPtr<mirror::Class> fields_class = resolved_field->GetDeclaringClass();
+  if (UNLIKELY(resolved_field->IsStatic() != is_static)) {
+    ThrowIncompatibleClassChangeErrorField(resolved_field, is_static, caller);
+    return nullptr;
+  }
+  ObjPtr<mirror::Class> referring_class = caller->GetDeclaringClass();
+  if (UNLIKELY(!referring_class->CheckResolvedFieldAccess(fields_class,
+                                                          resolved_field,
+                                                          caller->GetDexCache(),
+                                                          field_index))) {
+    DCHECK(self->IsExceptionPending());
+    return nullptr;
+  }
+  if (UNLIKELY(is_put && !resolved_field->CanBeChangedBy(caller))) {
+    ThrowIllegalAccessErrorFinalField(caller, resolved_field);
+    return nullptr;
+  }
+
+  if (resolve_field_type != 0u) {
+    StackArtFieldHandleScope<1> rhs(self);
+    ReflectiveHandle<ArtField> field_handle(rhs.NewHandle(resolved_field));
+    if (resolved_field->ResolveType().IsNull()) {
+      DCHECK(self->IsExceptionPending());
+      return nullptr;
+    }
+    resolved_field = field_handle.Get();
+  }
+  return resolved_field;
+}
+
+template<FindFieldType type>
 inline ArtField* FindFieldFromCode(uint32_t field_idx,
                                    ArtMethod* referrer,
                                    Thread* self,
-                                   size_t expected_size) {
-  constexpr bool is_primitive = (type & FindFieldFlags::PrimitiveBit) != 0;
+                                   bool should_resolve_type = false) {
   constexpr bool is_set = (type & FindFieldFlags::WriteBit) != 0;
   constexpr bool is_static = (type & FindFieldFlags::StaticBit) != 0;
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-
-  ArtField* resolved_field;
-  if (access_check) {
-    // Slow path: According to JLS 13.4.8, a linkage error may occur if a compile-time
-    // qualifying type of a field and the resolved run-time qualifying type of a field differed
-    // in their static-ness.
-    //
-    // In particular, don't assume the dex instruction already correctly knows if the
-    // real field is static or not. The resolution must not be aware of this.
-    ArtMethod* method = referrer->GetInterfaceMethodIfProxy(kRuntimePointerSize);
-
-    StackHandleScope<2> hs(self);
-    Handle<mirror::DexCache> h_dex_cache(hs.NewHandle(method->GetDexCache()));
-    Handle<mirror::ClassLoader> h_class_loader(hs.NewHandle(method->GetClassLoader()));
-
-    resolved_field = class_linker->ResolveFieldJLS(field_idx,
-                                                   h_dex_cache,
-                                                   h_class_loader);
-  } else {
-    // Fast path: Verifier already would've called ResolveFieldJLS and we wouldn't
-    // be executing here if there was a static/non-static mismatch.
-    resolved_field = class_linker->ResolveField(field_idx, referrer, is_static);
-  }
-
-  if (UNLIKELY(resolved_field == nullptr)) {
-    DCHECK(self->IsExceptionPending());  // Throw exception and unwind.
-    return nullptr;  // Failure.
-  }
-  ObjPtr<mirror::Class> fields_class = resolved_field->GetDeclaringClass();
-  if (access_check) {
-    if (UNLIKELY(resolved_field->IsStatic() != is_static)) {
-      ThrowIncompatibleClassChangeErrorField(resolved_field, is_static, referrer);
-      return nullptr;
-    }
-    ObjPtr<mirror::Class> referring_class = referrer->GetDeclaringClass();
-    if (UNLIKELY(!referring_class->CheckResolvedFieldAccess(fields_class,
-                                                            resolved_field,
-                                                            referrer->GetDexCache(),
-                                                            field_idx))) {
-      DCHECK(self->IsExceptionPending());  // Throw exception and unwind.
-      return nullptr;  // Failure.
-    }
-    if (UNLIKELY(is_set && !resolved_field->CanBeChangedBy(referrer))) {
-      ThrowIllegalAccessErrorFinalField(referrer, resolved_field);
-      return nullptr;  // Failure.
-    } else {
-      if (UNLIKELY(resolved_field->IsPrimitiveType() != is_primitive ||
-                   resolved_field->FieldSize() != expected_size)) {
-        self->ThrowNewExceptionF("Ljava/lang/NoSuchFieldError;",
-                                 "Attempted read of %zd-bit %s on field '%s'",
-                                 expected_size * (32 / sizeof(int32_t)),
-                                 is_primitive ? "primitive" : "non-primitive",
-                                 resolved_field->PrettyField(true).c_str());
-        return nullptr;  // Failure.
-      }
-    }
-  }
-  if (!is_static) {
+  ArtField* resolved_field = ResolveFieldWithAccessChecks(
+      self, class_linker, field_idx, referrer, is_static, is_set, should_resolve_type ? 1u : 0u);
+  if (!is_static || resolved_field == nullptr) {
     // instance fields must be being accessed on an initialized class
     return resolved_field;
   } else {
+    ObjPtr<mirror::Class> fields_class = resolved_field->GetDeclaringClass();
     // If the class is initialized we're done.
     if (LIKELY(fields_class->IsVisiblyInitialized())) {
       return resolved_field;
@@ -463,28 +464,26 @@
   }
 }
 
+// NOLINTBEGIN(bugprone-macro-parentheses)
 // Explicit template declarations of FindFieldFromCode for all field access types.
-#define EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(_type, _access_check) \
+#define EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(_type) \
 template REQUIRES_SHARED(Locks::mutator_lock_) ALWAYS_INLINE \
-ArtField* FindFieldFromCode<_type, _access_check>(uint32_t field_idx, \
-                                                  ArtMethod* referrer, \
-                                                  Thread* self, size_t expected_size) \
+ArtField* FindFieldFromCode<_type>(uint32_t field_idx, \
+                                   ArtMethod* referrer, \
+                                   Thread* self, \
+                                   bool should_resolve_type = false) \
 
-#define EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(_type) \
-    EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(_type, false); \
-    EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(_type, true)
+EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(InstanceObjectRead);
+EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(InstanceObjectWrite);
+EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(InstancePrimitiveRead);
+EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(InstancePrimitiveWrite);
+EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(StaticObjectRead);
+EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(StaticObjectWrite);
+EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(StaticPrimitiveRead);
+EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(StaticPrimitiveWrite);
 
-EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(InstanceObjectRead);
-EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(InstanceObjectWrite);
-EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(InstancePrimitiveRead);
-EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(InstancePrimitiveWrite);
-EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(StaticObjectRead);
-EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(StaticObjectWrite);
-EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(StaticPrimitiveRead);
-EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(StaticPrimitiveWrite);
-
-#undef EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL
 #undef EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL
+// NOLINTEND(bugprone-macro-parentheses)
 
 static inline bool IsStringInit(const DexFile* dex_file, uint32_t method_idx)
     REQUIRES_SHARED(Locks::mutator_lock_) {
diff --git a/runtime/entrypoints/quick/quick_field_entrypoints.cc b/runtime/entrypoints/quick/quick_field_entrypoints.cc
index 81a9e21..e2fc232 100644
--- a/runtime/entrypoints/quick/quick_field_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_field_entrypoints.cc
@@ -32,7 +32,7 @@
 inline ArtField* FindFieldFast(uint32_t field_idx,
                                ArtMethod* referrer,
                                FindFieldType type,
-                               size_t expected_size)
+                               bool should_resolve_type = false)
     REQUIRES(!Roles::uninterruptible_)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   ScopedAssertNoThreadSuspension ants(__FUNCTION__);
@@ -41,7 +41,6 @@
     return nullptr;
   }
   // Check for incompatible class change.
-  const bool is_primitive = (type & FindFieldFlags::PrimitiveBit) != 0;
   const bool is_set = (type & FindFieldFlags::WriteBit) != 0;
   const bool is_static = (type & FindFieldFlags::StaticBit) != 0;
   if (UNLIKELY(resolved_field->IsStatic() != is_static)) {
@@ -63,8 +62,7 @@
     // Illegal access.
     return nullptr;
   }
-  if (UNLIKELY(resolved_field->IsPrimitiveType() != is_primitive ||
-               resolved_field->FieldSize() != expected_size)) {
+  if (should_resolve_type && resolved_field->LookupResolvedType() == nullptr) {
     return nullptr;
   }
   return resolved_field;
@@ -73,17 +71,17 @@
 // Helper function to do a null check after trying to resolve the field. Not for statics since obj
 // does not exist there. There is a suspend check, object is a double pointer to update the value
 // in the caller in case it moves.
-template<FindFieldType type, bool kAccessCheck>
+template<FindFieldType type>
 ALWAYS_INLINE static inline ArtField* FindInstanceField(uint32_t field_idx,
                                                         ArtMethod* referrer,
                                                         Thread* self,
-                                                        size_t size,
-                                                        mirror::Object** obj)
+                                                        mirror::Object** obj,
+                                                        bool should_resolve_type = false)
     REQUIRES(!Roles::uninterruptible_)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   StackHandleScope<1> hs(self);
   HandleWrapper<mirror::Object> h(hs.NewHandleWrapper(obj));
-  ArtField* field = FindFieldFromCode<type, kAccessCheck>(field_idx, referrer, self, size);
+  ArtField* field = FindFieldFromCode<type>(field_idx, referrer, self, should_resolve_type);
   if (LIKELY(field != nullptr) && UNLIKELY(h == nullptr)) {
     ThrowNullPointerExceptionForFieldAccess(field, referrer, (type & FindFieldFlags::ReadBit) != 0);
     return nullptr;
@@ -116,13 +114,12 @@
       REQUIRES_SHARED(Locks::mutator_lock_) {                                  \
     ScopedQuickEntrypointChecks sqec(self);                                    \
     ArtField* field = FindFieldFast(                                           \
-        field_idx, referrer, Static ## PrimitiveOrObject ## Read,              \
-        sizeof(PrimitiveType));                                                \
+        field_idx, referrer, Static ## PrimitiveOrObject ## Read);             \
     if (LIKELY(field != nullptr)) {                                            \
       return field->Get ## Kind (field->GetDeclaringClass())Ptr;  /* NOLINT */ \
     }                                                                          \
-    field = FindFieldFromCode<Static ## PrimitiveOrObject ## Read, true>(      \
-        field_idx, referrer, self, sizeof(PrimitiveType));                     \
+    field = FindFieldFromCode<Static ## PrimitiveOrObject ## Read>(            \
+        field_idx, referrer, self);                                            \
     if (LIKELY(field != nullptr)) {                                            \
       return field->Get ## Kind (field->GetDeclaringClass())Ptr;  /* NOLINT */ \
     }                                                                          \
@@ -137,13 +134,12 @@
       REQUIRES_SHARED(Locks::mutator_lock_) {                                  \
     ScopedQuickEntrypointChecks sqec(self);                                    \
     ArtField* field = FindFieldFast(                                           \
-        field_idx, referrer, Instance ## PrimitiveOrObject ## Read,            \
-        sizeof(PrimitiveType));                                                \
+        field_idx, referrer, Instance ## PrimitiveOrObject ## Read);           \
     if (LIKELY(field != nullptr) && obj != nullptr) {                          \
       return field->Get ## Kind (obj)Ptr;  /* NOLINT */                        \
     }                                                                          \
-    field = FindInstanceField<Instance ## PrimitiveOrObject ## Read, true>(    \
-        field_idx, referrer, self, sizeof(PrimitiveType), &obj);               \
+    field = FindInstanceField<Instance ## PrimitiveOrObject ## Read>(          \
+        field_idx, referrer, self, &obj);                                      \
     if (LIKELY(field != nullptr)) {                                            \
       return field->Get ## Kind (obj)Ptr;  /* NOLINT */                        \
     }                                                                          \
@@ -157,33 +153,30 @@
                                                   Thread* self)                \
       REQUIRES_SHARED(Locks::mutator_lock_) {                                  \
     ScopedQuickEntrypointChecks sqec(self);                                    \
+    bool should_resolve_type = (IsObject) && new_value != 0;                   \
     ArtField* field = FindFieldFast(                                           \
-        field_idx, referrer, Static ## PrimitiveOrObject ## Write,             \
-        sizeof(PrimitiveType));                                                \
+        field_idx,                                                             \
+        referrer,                                                              \
+        Static ## PrimitiveOrObject ## Write,                                  \
+        should_resolve_type);                                                  \
     if (UNLIKELY(field == nullptr)) {                                          \
       if (IsObject) {                                                          \
         StackHandleScope<1> hs(self);                                          \
         HandleWrapper<mirror::Object> h_obj(hs.NewHandleWrapper(               \
             reinterpret_cast<mirror::Object**>(&new_value)));                  \
-        field = FindFieldFromCode<Static ## PrimitiveOrObject ## Write, true>( \
-            field_idx, referrer, self, sizeof(PrimitiveType));                 \
+        field = FindFieldFromCode<Static ## PrimitiveOrObject ## Write>(       \
+            field_idx,                                                         \
+            referrer,                                                          \
+            self,                                                              \
+            should_resolve_type);                                              \
       } else {                                                                 \
-        field = FindFieldFromCode<Static ## PrimitiveOrObject ## Write, true>( \
-            field_idx, referrer, self, sizeof(PrimitiveType));                 \
+        field = FindFieldFromCode<Static ## PrimitiveOrObject ## Write>(       \
+            field_idx, referrer, self);                                        \
       }                                                                        \
       if (UNLIKELY(field == nullptr)) {                                        \
         return -1;                                                             \
       }                                                                        \
     }                                                                          \
-    if (!referrer->SkipAccessChecks() && (IsObject) && new_value != 0) {       \
-      StackArtFieldHandleScope<1> rhs(self);                                   \
-      ReflectiveHandle<ArtField> field_handle(rhs.NewHandle(field));           \
-      if (field->ResolveType().IsNull()) {                                     \
-        self->AssertPendingException();                                        \
-        return -1;                                                             \
-      }                                                                        \
-      field = field_handle.Get();                                              \
-    }                                                                          \
     field->Set ## Kind <false>(field->GetDeclaringClass(), new_value);         \
     return 0;                                                                  \
   }                                                                            \
@@ -195,43 +188,31 @@
                                                     Thread* self)              \
     REQUIRES_SHARED(Locks::mutator_lock_) {                                    \
     ScopedQuickEntrypointChecks sqec(self);                                    \
+    bool should_resolve_type = (IsObject) && new_value != 0;                   \
     ArtField* field = FindFieldFast(                                           \
-        field_idx, referrer, Instance ## PrimitiveOrObject ## Write,           \
-        sizeof(PrimitiveType));                                                \
+        field_idx,                                                             \
+        referrer,                                                              \
+        Instance ## PrimitiveOrObject ## Write,                                \
+        should_resolve_type);                                                  \
     if (UNLIKELY(field == nullptr || obj == nullptr)) {                        \
       if (IsObject) {                                                          \
         StackHandleScope<1> hs(self);                                          \
         HandleWrapper<mirror::Object> h_obj(hs.NewHandleWrapper(               \
             reinterpret_cast<mirror::Object**>(&new_value)));                  \
-        field =                                                                \
-            FindInstanceField<Instance ## PrimitiveOrObject ## Write, true>(   \
-                field_idx,                                                     \
-                referrer,                                                      \
-                self,                                                          \
-                sizeof(PrimitiveType),                                         \
-                &obj);                                                         \
+        field = FindInstanceField<Instance ## PrimitiveOrObject ## Write>(     \
+            field_idx,                                                         \
+            referrer,                                                          \
+            self,                                                              \
+            &obj,                                                              \
+            should_resolve_type);                                              \
       } else {                                                                 \
-        field =                                                                \
-            FindInstanceField<Instance ## PrimitiveOrObject ## Write, true>(   \
-                field_idx,                                                     \
-                referrer,                                                      \
-                self,                                                          \
-                sizeof(PrimitiveType),                                         \
-                &obj);                                                         \
+        field = FindInstanceField<Instance ## PrimitiveOrObject ## Write>(     \
+            field_idx, referrer, self, &obj);                                  \
       }                                                                        \
       if (UNLIKELY(field == nullptr)) {                                        \
         return -1;                                                             \
       }                                                                        \
     }                                                                          \
-    if (!referrer->SkipAccessChecks() && (IsObject) && new_value != 0) {       \
-      StackArtFieldHandleScope<1> rhs(self);                                   \
-      ReflectiveHandle<ArtField> field_handle(rhs.NewHandle(field));           \
-      if (field->ResolveType().IsNull()) {                                     \
-        self->AssertPendingException();                                        \
-        return -1;                                                             \
-      }                                                                        \
-      field = field_handle.Get();                                              \
-    }                                                                          \
     field->Set ## Kind<false>(obj, new_value);                                 \
     return 0;                                                                  \
   }                                                                            \
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index 49d7e64..64d4d71 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -346,24 +346,78 @@
   return field_value;
 }
 
+extern "C" size_t NterpGetStaticField(Thread* self,
+                                      ArtMethod* caller,
+                                      const uint16_t* dex_pc_ptr,
+                                      size_t resolve_field_type);
+
+extern "C" uint32_t NterpGetInstanceFieldOffset(Thread* self,
+                                                ArtMethod* caller,
+                                                const uint16_t* dex_pc_ptr,
+                                                size_t resolve_field_type);
+
+static inline void GetFieldInfo(Thread* self,
+                                ArtMethod* caller,
+                                const uint16_t* dex_pc_ptr,
+                                bool is_static,
+                                bool resolve_field_type,
+                                ArtField** field,
+                                bool* is_volatile,
+                                MemberOffset* offset) {
+  size_t tls_value = 0u;
+  if (!self->GetInterpreterCache()->Get(self, dex_pc_ptr, &tls_value)) {
+    if (is_static) {
+      tls_value = NterpGetStaticField(self, caller, dex_pc_ptr, resolve_field_type);
+    } else {
+      tls_value = NterpGetInstanceFieldOffset(self, caller, dex_pc_ptr, resolve_field_type);
+    }
+
+    if (self->IsExceptionPending()) {
+      return;
+    }
+  }
+
+  if (is_static) {
+    DCHECK_NE(tls_value, 0u);
+    *is_volatile = ((tls_value & 1) != 0);
+    *field = reinterpret_cast<ArtField*>(tls_value & ~static_cast<size_t>(1u));
+    *offset = (*field)->GetOffset();
+  } else {
+    *is_volatile = (static_cast<int32_t>(tls_value) < 0);
+    *offset = MemberOffset(std::abs(static_cast<int32_t>(tls_value)));
+  }
+}
+
 // Handles iget-XXX and sget-XXX instructions.
 // Returns true on success, otherwise throws an exception and returns false.
-template<FindFieldType find_type, Primitive::Type field_type, bool do_access_check,
+template<FindFieldType find_type,
+         Primitive::Type field_type,
+         bool do_access_check,
          bool transaction_active = false>
-ALWAYS_INLINE bool DoFieldGet(Thread* self, ShadowFrame& shadow_frame, const Instruction* inst,
+ALWAYS_INLINE bool DoFieldGet(Thread* self,
+                              ShadowFrame& shadow_frame,
+                              const Instruction* inst,
                               uint16_t inst_data) REQUIRES_SHARED(Locks::mutator_lock_) {
   const bool is_static = (find_type == StaticObjectRead) || (find_type == StaticPrimitiveRead);
-  const uint32_t field_idx = is_static ? inst->VRegB_21c() : inst->VRegC_22c();
-  ArtMethod* method = shadow_frame.GetMethod();
-  ArtField* f = FindFieldFromCode<find_type, do_access_check>(
-      field_idx, method, self, Primitive::ComponentSize(field_type));
-  if (UNLIKELY(f == nullptr)) {
-    CHECK(self->IsExceptionPending());
+  bool should_report = Runtime::Current()->GetInstrumentation()->HasFieldReadListeners();
+  ArtField* field = nullptr;
+  MemberOffset offset(0u);
+  bool is_volatile;
+  GetFieldInfo(self,
+               shadow_frame.GetMethod(),
+               reinterpret_cast<const uint16_t*>(inst),
+               is_static,
+               /*resolve_field_type=*/ false,
+               &field,
+               &is_volatile,
+               &offset);
+  if (self->IsExceptionPending()) {
     return false;
   }
+
   ObjPtr<mirror::Object> obj;
   if (is_static) {
-    obj = f->GetDeclaringClass();
+    obj = field->GetDeclaringClass();
     if (transaction_active) {
       if (Runtime::Current()->GetTransaction()->ReadConstraint(obj)) {
         Runtime::Current()->AbortTransactionAndThrowAbortError(self, "Can't read static fields of "
@@ -373,40 +427,57 @@
     }
   } else {
     obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data));
-    if (UNLIKELY(obj == nullptr)) {
-      ThrowNullPointerExceptionForFieldAccess(f, method, true);
+    if (should_report || obj == nullptr) {
+      field = ResolveFieldWithAccessChecks(self,
+                                           Runtime::Current()->GetClassLinker(),
+                                           inst->VRegC_22c(),
+                                           shadow_frame.GetMethod(),
+                                           /* is_static= */ false,
+                                           /* is_put= */ false,
+                                           /* resolve_field_type= */ false);
+      if (obj == nullptr) {
+        ThrowNullPointerExceptionForFieldAccess(
+            field, shadow_frame.GetMethod(), /* is_read= */ true);
+        return false;
+      }
+      // Reload in case suspension happened during field resolution.
+      obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data));
+    }
+  }
+
+  uint32_t vregA = is_static ? inst->VRegA_21c(inst_data) : inst->VRegA_22c(inst_data);
+  JValue result;
+  if (should_report) {
+    DCHECK(field != nullptr);
+    if (UNLIKELY(!DoFieldGetCommon<field_type>(self, shadow_frame, obj, field, &result))) {
+      // Instrumentation threw an error!
+      CHECK(self->IsExceptionPending());
       return false;
     }
   }
 
-  JValue result;
-  if (UNLIKELY(!DoFieldGetCommon<field_type>(self, shadow_frame, obj, f, &result))) {
-    // Instrumentation threw an error!
-    CHECK(self->IsExceptionPending());
-    return false;
-  }
-  uint32_t vregA = is_static ? inst->VRegA_21c(inst_data) : inst->VRegA_22c(inst_data);
+#define FIELD_GET(prim, type, jtype, vreg)                                      \
+  case Primitive::kPrim ##prim:                                                 \
+    shadow_frame.SetVReg ##vreg(vregA,                                          \
+        should_report ? result.Get ##jtype()                                    \
+                      : is_volatile ? obj->GetField ## type ## Volatile(offset) \
+                                    : obj->GetField ##type(offset));            \
+    break;
+
   switch (field_type) {
-    case Primitive::kPrimBoolean:
-      shadow_frame.SetVReg(vregA, result.GetZ());
-      break;
-    case Primitive::kPrimByte:
-      shadow_frame.SetVReg(vregA, result.GetB());
-      break;
-    case Primitive::kPrimChar:
-      shadow_frame.SetVReg(vregA, result.GetC());
-      break;
-    case Primitive::kPrimShort:
-      shadow_frame.SetVReg(vregA, result.GetS());
-      break;
-    case Primitive::kPrimInt:
-      shadow_frame.SetVReg(vregA, result.GetI());
-      break;
-    case Primitive::kPrimLong:
-      shadow_frame.SetVRegLong(vregA, result.GetJ());
-      break;
+    FIELD_GET(Boolean, Boolean, Z, )
+    FIELD_GET(Byte, Byte, B, )
+    FIELD_GET(Char, Char, C, )
+    FIELD_GET(Short, Short, S, )
+    FIELD_GET(Int, 32, I, )
+    FIELD_GET(Long, 64, J, Long)
+#undef FIELD_GET
     case Primitive::kPrimNot:
-      shadow_frame.SetVRegReference(vregA, result.GetL());
+      shadow_frame.SetVRegReference(
+          vregA,
+          should_report ? result.GetL()
+                        : is_volatile ? obj->GetFieldObjectVolatile<mirror::Object>(offset)
+                                      : obj->GetFieldObject<mirror::Object>(offset));
       break;
     default:
       LOG(FATAL) << "Unreachable: " << field_type;
@@ -447,34 +518,57 @@
 // Returns true on success, otherwise throws an exception and returns false.
 template<FindFieldType find_type, Primitive::Type field_type, bool do_access_check,
          bool transaction_active>
-ALWAYS_INLINE bool DoFieldPut(Thread* self, const ShadowFrame& shadow_frame,
-                              const Instruction* inst, uint16_t inst_data)
+ALWAYS_INLINE bool DoFieldPut(Thread* self,
+                              const ShadowFrame& shadow_frame,
+                              const Instruction* inst,
+                              uint16_t inst_data)
     REQUIRES_SHARED(Locks::mutator_lock_) {
+  bool should_report = Runtime::Current()->GetInstrumentation()->HasFieldWriteListeners();
   const bool do_assignability_check = do_access_check;
   bool is_static = (find_type == StaticObjectWrite) || (find_type == StaticPrimitiveWrite);
-  uint32_t field_idx = is_static ? inst->VRegB_21c() : inst->VRegC_22c();
-  ArtMethod* method = shadow_frame.GetMethod();
-  ArtField* f = FindFieldFromCode<find_type, do_access_check>(
-      field_idx, method, self, Primitive::ComponentSize(field_type));
-  if (UNLIKELY(f == nullptr)) {
-    CHECK(self->IsExceptionPending());
+  uint32_t vregA = is_static ? inst->VRegA_21c(inst_data) : inst->VRegA_22c(inst_data);
+  bool resolve_field_type = (shadow_frame.GetVRegReference(vregA) != nullptr);
+  ArtField* field = nullptr;
+  MemberOffset offset(0u);
+  bool is_volatile;
+  GetFieldInfo(self,
+               shadow_frame.GetMethod(),
+               reinterpret_cast<const uint16_t*>(inst),
+               is_static,
+               resolve_field_type,
+               &field,
+               &is_volatile,
+               &offset);
+  if (self->IsExceptionPending()) {
     return false;
   }
+
   ObjPtr<mirror::Object> obj;
   if (is_static) {
-    obj = f->GetDeclaringClass();
+    obj = field->GetDeclaringClass();
   } else {
     obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data));
-    if (UNLIKELY(obj == nullptr)) {
-      ThrowNullPointerExceptionForFieldAccess(f, method, false);
-      return false;
+    if (should_report || obj == nullptr) {
+      field = ResolveFieldWithAccessChecks(self,
+                                           Runtime::Current()->GetClassLinker(),
+                                           inst->VRegC_22c(),
+                                           shadow_frame.GetMethod(),
+                                           /* is_static= */ false,
+                                           /* is_put= */ true,
+                                           resolve_field_type);
+      if (UNLIKELY(obj == nullptr)) {
+        ThrowNullPointerExceptionForFieldAccess(
+            field, shadow_frame.GetMethod(), /* is_read= */ false);
+        return false;
+      }
+      // Reload in case suspension happened during field resolution.
+      obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data));
     }
   }
   if (transaction_active && !CheckWriteConstraint(self, obj)) {
     return false;
   }
 
-  uint32_t vregA = is_static ? inst->VRegA_21c(inst_data) : inst->VRegA_22c(inst_data);
   JValue value = GetFieldValue<field_type>(shadow_frame, vregA);
 
   if (transaction_active &&
@@ -482,12 +576,43 @@
       !CheckWriteValueConstraint(self, value.GetL())) {
     return false;
   }
+  if (should_report) {
+    return DoFieldPutCommon<field_type, do_assignability_check, transaction_active>(self,
+                                                                                    shadow_frame,
+                                                                                    obj,
+                                                                                    field,
+                                                                                    value);
+  }
+#define FIELD_SET(prim, type, jtype) \
+  case Primitive::kPrim ## prim: \
+    if (is_volatile) { \
+      obj->SetField ## type ## Volatile<transaction_active>(offset, value.Get ## jtype()); \
+    } else { \
+      obj->SetField ## type<transaction_active>(offset, value.Get ## jtype()); \
+    } \
+    break;
 
-  return DoFieldPutCommon<field_type, do_assignability_check, transaction_active>(self,
-                                                                                  shadow_frame,
-                                                                                  obj,
-                                                                                  f,
-                                                                                  value);
+  switch (field_type) {
+    FIELD_SET(Boolean, Boolean, Z)
+    FIELD_SET(Byte, Byte, B)
+    FIELD_SET(Char, Char, C)
+    FIELD_SET(Short, Short, S)
+    FIELD_SET(Int, 32, I)
+    FIELD_SET(Long, 64, J)
+    FIELD_SET(Not, Object, L)
+    case Primitive::kPrimVoid: {
+      LOG(FATAL) << "Unreachable " << field_type;
+      break;
+    }
+  }
+#undef FIELD_SET
+
+  if (transaction_active) {
+    if (UNLIKELY(self->IsExceptionPending())) {
+      return false;
+    }
+  }
+  return true;
 }
 
 // Handles string resolution for const-string and const-string-jumbo instructions. Also ensures the
diff --git a/runtime/interpreter/mterp/nterp.cc b/runtime/interpreter/mterp/nterp.cc
index e58d77d..cb3cf12 100644
--- a/runtime/interpreter/mterp/nterp.cc
+++ b/runtime/interpreter/mterp/nterp.cc
@@ -379,58 +379,9 @@
   }
 }
 
-FLATTEN
-static ArtField* ResolveFieldWithAccessChecks(Thread* self,
-                                              ClassLinker* class_linker,
-                                              uint16_t field_index,
-                                              ArtMethod* caller,
-                                              bool is_static,
-                                              bool is_put,
-                                              size_t resolve_field_type)  // Resolve if not zero
-    REQUIRES_SHARED(Locks::mutator_lock_) {
-  if (caller->SkipAccessChecks()) {
-    return class_linker->ResolveField(field_index, caller, is_static);
-  }
-
-  caller = caller->GetInterfaceMethodIfProxy(kRuntimePointerSize);
-
-  StackHandleScope<2> hs(self);
-  Handle<mirror::DexCache> h_dex_cache(hs.NewHandle(caller->GetDexCache()));
-  Handle<mirror::ClassLoader> h_class_loader(hs.NewHandle(caller->GetClassLoader()));
-
-  ArtField* resolved_field = class_linker->ResolveFieldJLS(field_index,
-                                                           h_dex_cache,
-                                                           h_class_loader);
-  if (resolved_field == nullptr) {
-    return nullptr;
-  }
-
-  ObjPtr<mirror::Class> fields_class = resolved_field->GetDeclaringClass();
-  if (UNLIKELY(resolved_field->IsStatic() != is_static)) {
-    ThrowIncompatibleClassChangeErrorField(resolved_field, is_static, caller);
-    return nullptr;
-  }
-  ObjPtr<mirror::Class> referring_class = caller->GetDeclaringClass();
-  if (UNLIKELY(!referring_class->CheckResolvedFieldAccess(fields_class,
-                                                          resolved_field,
-                                                          caller->GetDexCache(),
-                                                          field_index))) {
-    return nullptr;
-  }
-  if (UNLIKELY(is_put && !resolved_field->CanBeChangedBy(caller))) {
-    ThrowIllegalAccessErrorFinalField(caller, resolved_field);
-    return nullptr;
-  }
-  if (resolve_field_type != 0u && resolved_field->ResolveType() == nullptr) {
-    DCHECK(self->IsExceptionPending());
-    return nullptr;
-  }
-  return resolved_field;
-}
-
 extern "C" size_t NterpGetStaticField(Thread* self,
                                       ArtMethod* caller,
-                                      uint16_t* dex_pc_ptr,
+                                      const uint16_t* dex_pc_ptr,
                                       size_t resolve_field_type)  // Resolve if not zero
     REQUIRES_SHARED(Locks::mutator_lock_) {
   UpdateHotness(caller);
@@ -483,7 +434,7 @@
 
 extern "C" uint32_t NterpGetInstanceFieldOffset(Thread* self,
                                                 ArtMethod* caller,
-                                                uint16_t* dex_pc_ptr,
+                                                const uint16_t* dex_pc_ptr,
                                                 size_t resolve_field_type)  // Resolve if not zero
     REQUIRES_SHARED(Locks::mutator_lock_) {
   UpdateHotness(caller);
diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc
index f67f6f3..7edeacb 100644
--- a/runtime/mirror/object_test.cc
+++ b/runtime/mirror/object_test.cc
@@ -424,10 +424,12 @@
   ASSERT_TRUE(field_id != nullptr);
   uint32_t field_idx = dex_file->GetIndexForFieldId(*field_id);
 
-  ArtField* field = FindFieldFromCode<StaticObjectRead, true>(field_idx, clinit, Thread::Current(),
-                                                              sizeof(HeapReference<Object>));
+  ArtField* field = FindFieldFromCode<StaticObjectRead>(field_idx,
+                                                        clinit,
+                                                        Thread::Current(),
+                                                        sizeof(HeapReference<Object>));
   ObjPtr<Object> s0 = field->GetObj(klass.Get());
-  EXPECT_TRUE(s0 != nullptr);
+  EXPECT_TRUE(s0 != nullptr) << field->PrettyField();
 
   Handle<CharArray> char_array(hs.NewHandle(CharArray::Alloc(soa.Self(), 0)));
   field->SetObj<false>(field->GetDeclaringClass(), char_array.Get());