summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Vladimir Marko <vmarko@google.com> 2024-05-20 15:51:01 +0000
committer VladimĂ­r Marko <vmarko@google.com> 2024-05-21 13:48:45 +0000
commit2a25327e2bfdd7f5d128267684b13dc0f0e57e55 (patch)
tree9e9d0b80e78260cd961e47245b6676ce6e4f632e
parente0e0f0740a1c1f4f28e1d1f59fe77625aea8d4a4 (diff)
Further refactor transaction checks in interpreter.
Reverse the relationship between `ActiveTransactionChecker` and `AotClassLinker` by moving the checks to the latter. Move `{Active,Inactive}TransactionChecker` and functions `DoField{Get,Put}()` to the `interpreter_switch_impl*`. Test: m test-art-host-gtest Test: testrunner.py --host --optimizing --interp-ac Change-Id: I509083b4ee77a69a3ba7e0d277b0e9e5fb7e32c8
-rw-r--r--runtime/class_linker.cc6
-rw-r--r--runtime/class_linker.h6
-rw-r--r--runtime/interpreter/active_transaction_checker.h102
-rw-r--r--runtime/interpreter/interpreter_common.h235
-rw-r--r--runtime/interpreter/interpreter_switch_impl-inl.h200
-rw-r--r--runtime/interpreter/interpreter_switch_impl0.cc36
-rw-r--r--runtime/interpreter/interpreter_switch_impl1.cc37
-rw-r--r--runtime/oat/aot_class_linker.cc48
-rw-r--r--runtime/oat/aot_class_linker.h9
9 files changed, 323 insertions, 356 deletions
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 823d79e7d0..7359056d32 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -11168,21 +11168,21 @@ void ClassLinker::SetEnablePublicSdkChecks([[maybe_unused]] bool enabled) {
}
bool ClassLinker::TransactionWriteConstraint(
- [[maybe_unused]] Thread* self, [[maybe_unused]] ObjPtr<mirror::Object> obj) const {
+ [[maybe_unused]] Thread* self, [[maybe_unused]] ObjPtr<mirror::Object> obj) {
// Should not be called on ClassLinker, only on AotClassLinker that overrides this.
LOG(FATAL) << "UNREACHABLE";
UNREACHABLE();
}
bool ClassLinker::TransactionWriteValueConstraint(
- [[maybe_unused]] Thread* self, [[maybe_unused]] ObjPtr<mirror::Object> value) const {
+ [[maybe_unused]] Thread* self, [[maybe_unused]] ObjPtr<mirror::Object> value) {
// Should not be called on ClassLinker, only on AotClassLinker that overrides this.
LOG(FATAL) << "UNREACHABLE";
UNREACHABLE();
}
bool ClassLinker::TransactionAllocationConstraint(
- [[maybe_unused]] Thread* self, [[maybe_unused]] ObjPtr<mirror::Class> klass) const {
+ [[maybe_unused]] Thread* self, [[maybe_unused]] ObjPtr<mirror::Class> klass) {
// Should not be called on ClassLinker, only on AotClassLinker that overrides this.
LOG(FATAL) << "UNREACHABLE";
UNREACHABLE();
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 26146db15d..daf9534c3a 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -891,11 +891,11 @@ class EXPORT ClassLinker {
virtual void SetEnablePublicSdkChecks(bool enabled);
// Transaction constraint checks for AOT compilation.
- virtual bool TransactionWriteConstraint(Thread* self, ObjPtr<mirror::Object> obj) const
+ virtual bool TransactionWriteConstraint(Thread* self, ObjPtr<mirror::Object> obj)
REQUIRES_SHARED(Locks::mutator_lock_);
- virtual bool TransactionWriteValueConstraint(Thread* self, ObjPtr<mirror::Object> value) const
+ virtual bool TransactionWriteValueConstraint(Thread* self, ObjPtr<mirror::Object> value)
REQUIRES_SHARED(Locks::mutator_lock_);
- virtual bool TransactionAllocationConstraint(Thread* self, ObjPtr<mirror::Class> klass) const
+ virtual bool TransactionAllocationConstraint(Thread* self, ObjPtr<mirror::Class> klass)
REQUIRES_SHARED(Locks::mutator_lock_);
// Transaction bookkeeping for AOT compilation.
diff --git a/runtime/interpreter/active_transaction_checker.h b/runtime/interpreter/active_transaction_checker.h
deleted file mode 100644
index f299bbc290..0000000000
--- a/runtime/interpreter/active_transaction_checker.h
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_RUNTIME_INTERPRETER_ACTIVE_TRANSACTION_CHECKER_H_
-#define ART_RUNTIME_INTERPRETER_ACTIVE_TRANSACTION_CHECKER_H_
-
-#include "oat/aot_class_linker.h"
-#include "base/casts.h"
-#include "base/macros.h"
-#include "gc/heap.h"
-#include "mirror/object-inl.h"
-#include "runtime.h"
-#include "transaction.h"
-
-namespace art HIDDEN {
-namespace interpreter {
-
-class ActiveTransactionChecker {
- public:
- static inline bool WriteConstraint(Thread* self, ObjPtr<mirror::Object> obj)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- Runtime* runtime = Runtime::Current();
- AotClassLinker* class_linker = GetClassLinker();
- if (class_linker->GetTransaction()->WriteConstraint(obj)) {
- DCHECK(runtime->GetHeap()->ObjectIsInBootImageSpace(obj) || obj->IsClass());
- const char* extra = runtime->GetHeap()->ObjectIsInBootImageSpace(obj) ? "boot image " : "";
- class_linker->AbortTransactionF(
- self, "Can't set fields of %s%s", extra, obj->PrettyTypeOf().c_str());
- return true;
- }
- return false;
- }
-
- static inline bool WriteValueConstraint(Thread* self, ObjPtr<mirror::Object> value)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- AotClassLinker* class_linker = GetClassLinker();
- if (class_linker->GetTransaction()->WriteValueConstraint(value)) {
- DCHECK(value != nullptr);
- const char* description = value->IsClass() ? "class" : "instance of";
- ObjPtr<mirror::Class> klass = value->IsClass() ? value->AsClass() : value->GetClass();
- class_linker->AbortTransactionF(
- self, "Can't store reference to %s %s", description, klass->PrettyDescriptor().c_str());
- return true;
- }
- return false;
- }
-
- static inline bool ReadConstraint(Thread* self, ObjPtr<mirror::Object> obj)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- DCHECK(obj->IsClass());
- AotClassLinker* class_linker = GetClassLinker();
- if (class_linker->GetTransaction()->ReadConstraint(obj)) {
- class_linker->AbortTransactionF(
- self,
- "Can't read static fields of %s since it does not belong to clinit's class.",
- obj->PrettyTypeOf().c_str());
- return true;
- }
- return false;
- }
-
- static inline bool AllocationConstraint(Thread* self, ObjPtr<mirror::Class> klass)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- if (klass->IsFinalizable()) {
- GetClassLinker()->AbortTransactionF(self,
- "Allocating finalizable object in transaction: %s",
- klass->PrettyDescriptor().c_str());
- return true;
- }
- return false;
- }
-
- static inline bool IsTransactionAborted() {
- return GetClassLinker()->IsTransactionAborted();
- }
-
- static void RecordArrayElementsInTransaction(ObjPtr<mirror::Object> array, int32_t count)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
- private:
- static AotClassLinker* GetClassLinker() {
- return down_cast<AotClassLinker*>(Runtime::Current()->GetClassLinker());
- }
-};
-
-} // namespace interpreter
-} // namespace art
-
-#endif // ART_RUNTIME_INTERPRETER_ACTIVE_TRANSACTION_CHECKER_H_
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index 912beb352b..ba61562d9b 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -66,46 +66,6 @@
namespace art HIDDEN {
namespace interpreter {
-// We declare the helper class for transaction checks here but it shall be defined
-// only when compiling the transactional interpreter.
-class ActiveTransactionChecker;
-
-// Define the helper class that does not do any transaction checks.
-class InactiveTransactionChecker {
- public:
- ALWAYS_INLINE static bool WriteConstraint([[maybe_unused]] Thread* self,
- [[maybe_unused]] ObjPtr<mirror::Object> obj)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- return false;
- }
-
- ALWAYS_INLINE static bool WriteValueConstraint([[maybe_unused]] Thread* self,
- [[maybe_unused]] ObjPtr<mirror::Object> value)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- return false;
- }
-
- ALWAYS_INLINE static bool ReadConstraint([[maybe_unused]] Thread* self,
- [[maybe_unused]] ObjPtr<mirror::Object> value)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- return false;
- }
-
- ALWAYS_INLINE static bool AllocationConstraint([[maybe_unused]] Thread* self,
- [[maybe_unused]] ObjPtr<mirror::Class> klass)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- return false;
- }
-
- ALWAYS_INLINE static bool IsTransactionAborted() {
- return false;
- }
-
- static void RecordArrayElementsInTransaction([[maybe_unused]] ObjPtr<mirror::Object> array,
- [[maybe_unused]] int32_t count)
- REQUIRES_SHARED(Locks::mutator_lock_) {}
-};
-
void ThrowNullPointerExceptionFromInterpreter()
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -378,201 +338,6 @@ static inline void GetFieldInfo(Thread* self,
}
}
-// 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 transaction_active = false>
-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);
- 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 = field->GetDeclaringClass();
- using TransactionChecker = typename std::conditional_t<
- transaction_active, ActiveTransactionChecker, InactiveTransactionChecker>;
- if (TransactionChecker::ReadConstraint(self, obj)) {
- return false;
- }
- } else {
- obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data));
- 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;
- }
- }
-
-#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) {
- 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,
- should_report ? result.GetL()
- : is_volatile ? obj->GetFieldObjectVolatile<mirror::Object>(offset)
- : obj->GetFieldObject<mirror::Object>(offset));
- break;
- default:
- LOG(FATAL) << "Unreachable: " << field_type;
- UNREACHABLE();
- }
- return true;
-}
-
-// Handles iput-XXX and sput-XXX instructions.
-// Returns true on success, otherwise throws an exception and returns false.
-template<FindFieldType find_type, Primitive::Type field_type, bool transaction_active>
-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();
- bool is_static = (find_type == StaticObjectWrite) || (find_type == StaticPrimitiveWrite);
- 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 = field->GetDeclaringClass();
- } else {
- obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data));
- 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));
- }
- }
- using TransactionChecker = typename std::conditional_t<
- transaction_active, ActiveTransactionChecker, InactiveTransactionChecker>;
- if (TransactionChecker::WriteConstraint(self, obj)) {
- return false;
- }
-
- JValue value = GetFieldValue<field_type>(shadow_frame, vregA);
-
- if (field_type == Primitive::kPrimNot &&
- TransactionChecker::WriteValueConstraint(self, value.GetL())) {
- return false;
- }
- if (should_report) {
- return DoFieldPutCommon<field_type, 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;
-
- 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
// java.lang.String class is initialized.
static inline ObjPtr<mirror::String> ResolveString(Thread* self,
diff --git a/runtime/interpreter/interpreter_switch_impl-inl.h b/runtime/interpreter/interpreter_switch_impl-inl.h
index ce0117f724..35708b00c6 100644
--- a/runtime/interpreter/interpreter_switch_impl-inl.h
+++ b/runtime/interpreter/interpreter_switch_impl-inl.h
@@ -44,6 +44,206 @@
namespace art HIDDEN {
namespace interpreter {
+// We declare the helpers classes for transaction checks here but they shall be defined
+// only when compiling the transactional and non-transactional interpreter.
+class ActiveTransactionChecker; // For transactional interpreter.
+class InactiveTransactionChecker; // For non-transactional interpreter.
+
+// 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 transaction_active = false>
+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);
+ 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 = field->GetDeclaringClass();
+ using TransactionChecker = typename std::conditional_t<
+ transaction_active, ActiveTransactionChecker, InactiveTransactionChecker>;
+ if (TransactionChecker::ReadConstraint(self, obj)) {
+ return false;
+ }
+ } else {
+ obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data));
+ 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;
+ }
+ }
+
+#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) {
+ 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,
+ should_report ? result.GetL()
+ : is_volatile ? obj->GetFieldObjectVolatile<mirror::Object>(offset)
+ : obj->GetFieldObject<mirror::Object>(offset));
+ break;
+ default:
+ LOG(FATAL) << "Unreachable: " << field_type;
+ UNREACHABLE();
+ }
+ return true;
+}
+
+// Handles iput-XXX and sput-XXX instructions.
+// Returns true on success, otherwise throws an exception and returns false.
+template<FindFieldType find_type, Primitive::Type field_type, bool transaction_active>
+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();
+ bool is_static = (find_type == StaticObjectWrite) || (find_type == StaticPrimitiveWrite);
+ 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 = field->GetDeclaringClass();
+ } else {
+ obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data));
+ 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));
+ }
+ }
+ using TransactionChecker = typename std::conditional_t<
+ transaction_active, ActiveTransactionChecker, InactiveTransactionChecker>;
+ if (TransactionChecker::WriteConstraint(self, obj)) {
+ return false;
+ }
+
+ JValue value = GetFieldValue<field_type>(shadow_frame, vregA);
+
+ if (field_type == Primitive::kPrimNot &&
+ TransactionChecker::WriteValueConstraint(self, value.GetL())) {
+ return false;
+ }
+ if (should_report) {
+ return DoFieldPutCommon<field_type, 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;
+
+ 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;
+}
+
// Short-lived helper class which executes single DEX bytecode. It is inlined by compiler.
// Any relevant execution information is stored in the fields - it should be kept to minimum.
// All instance functions must be inlined so that the fields can be stored in registers.
diff --git a/runtime/interpreter/interpreter_switch_impl0.cc b/runtime/interpreter/interpreter_switch_impl0.cc
index 65ae2fe333..517cce8088 100644
--- a/runtime/interpreter/interpreter_switch_impl0.cc
+++ b/runtime/interpreter/interpreter_switch_impl0.cc
@@ -22,6 +22,42 @@
namespace art HIDDEN {
namespace interpreter {
+// Define the helper class that does not do any transaction checks.
+class InactiveTransactionChecker {
+ public:
+ ALWAYS_INLINE static bool WriteConstraint([[maybe_unused]] Thread* self,
+ [[maybe_unused]] ObjPtr<mirror::Object> obj)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ return false;
+ }
+
+ ALWAYS_INLINE static bool WriteValueConstraint([[maybe_unused]] Thread* self,
+ [[maybe_unused]] ObjPtr<mirror::Object> value)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ return false;
+ }
+
+ ALWAYS_INLINE static bool ReadConstraint([[maybe_unused]] Thread* self,
+ [[maybe_unused]] ObjPtr<mirror::Object> value)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ return false;
+ }
+
+ ALWAYS_INLINE static bool AllocationConstraint([[maybe_unused]] Thread* self,
+ [[maybe_unused]] ObjPtr<mirror::Class> klass)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ return false;
+ }
+
+ ALWAYS_INLINE static bool IsTransactionAborted() {
+ return false;
+ }
+
+ static void RecordArrayElementsInTransaction([[maybe_unused]] ObjPtr<mirror::Object> array,
+ [[maybe_unused]] int32_t count)
+ REQUIRES_SHARED(Locks::mutator_lock_) {}
+};
+
// Explicit definition of ExecuteSwitchImplCpp.
template HOT_ATTR
void ExecuteSwitchImplCpp<false>(SwitchImplContext* ctx);
diff --git a/runtime/interpreter/interpreter_switch_impl1.cc b/runtime/interpreter/interpreter_switch_impl1.cc
index 8c8db386b3..f0f38514db 100644
--- a/runtime/interpreter/interpreter_switch_impl1.cc
+++ b/runtime/interpreter/interpreter_switch_impl1.cc
@@ -19,11 +19,46 @@
#include "interpreter_switch_impl-inl.h"
-#include "active_transaction_checker.h"
+#include "oat/aot_class_linker.h"
namespace art HIDDEN {
namespace interpreter {
+class ActiveTransactionChecker {
+ public:
+ static inline bool WriteConstraint(Thread* self, ObjPtr<mirror::Object> obj)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ return GetClassLinker()->TransactionWriteConstraint(self, obj);
+ }
+
+ static inline bool WriteValueConstraint(Thread* self, ObjPtr<mirror::Object> value)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ return GetClassLinker()->TransactionWriteValueConstraint(self, value);
+ }
+
+ static inline bool ReadConstraint(Thread* self, ObjPtr<mirror::Object> obj)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ return GetClassLinker()->TransactionReadConstraint(self, obj);
+ }
+
+ static inline bool AllocationConstraint(Thread* self, ObjPtr<mirror::Class> klass)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ return GetClassLinker()->TransactionAllocationConstraint(self, klass);
+ }
+
+ static inline bool IsTransactionAborted() {
+ return GetClassLinker()->IsTransactionAborted();
+ }
+
+ static void RecordArrayElementsInTransaction(ObjPtr<mirror::Object> array, int32_t count)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ private:
+ static AotClassLinker* GetClassLinker() {
+ return down_cast<AotClassLinker*>(Runtime::Current()->GetClassLinker());
+ }
+};
+
// TODO: Use ObjPtr here.
template<typename T>
static void RecordArrayElementsInTransactionImpl(ObjPtr<mirror::PrimitiveArray<T>> array,
diff --git a/runtime/oat/aot_class_linker.cc b/runtime/oat/aot_class_linker.cc
index 6a6bec4101..0a85fa094b 100644
--- a/runtime/oat/aot_class_linker.cc
+++ b/runtime/oat/aot_class_linker.cc
@@ -22,7 +22,6 @@
#include "dex/class_reference.h"
#include "gc/heap.h"
#include "handle_scope-inl.h"
-#include "interpreter/active_transaction_checker.h"
#include "mirror/class-inl.h"
#include "runtime.h"
#include "scoped_thread_state_change-inl.h"
@@ -331,21 +330,52 @@ bool AotClassLinker::IsActiveStrictTransactionMode() const {
return IsActiveTransaction() && GetTransaction()->IsStrict();
}
-bool AotClassLinker::TransactionWriteConstraint(Thread* self, ObjPtr<mirror::Object> obj) const {
+bool AotClassLinker::TransactionWriteConstraint(Thread* self, ObjPtr<mirror::Object> obj) {
DCHECK(IsActiveTransaction());
- return interpreter::ActiveTransactionChecker::WriteConstraint(self, obj);
+ if (GetTransaction()->WriteConstraint(obj)) {
+ Runtime* runtime = Runtime::Current();
+ DCHECK(runtime->GetHeap()->ObjectIsInBootImageSpace(obj) || obj->IsClass());
+ const char* extra = runtime->GetHeap()->ObjectIsInBootImageSpace(obj) ? "boot image " : "";
+ AbortTransactionF(
+ self, "Can't set fields of %s%s", extra, obj->PrettyTypeOf().c_str());
+ return true;
+ }
+ return false;
}
-bool AotClassLinker::TransactionWriteValueConstraint(
- Thread* self, ObjPtr<mirror::Object> value) const {
+bool AotClassLinker::TransactionWriteValueConstraint(Thread* self, ObjPtr<mirror::Object> value) {
DCHECK(IsActiveTransaction());
- return interpreter::ActiveTransactionChecker::WriteValueConstraint(self, value);
+ if (GetTransaction()->WriteValueConstraint(value)) {
+ DCHECK(value != nullptr);
+ const char* description = value->IsClass() ? "class" : "instance of";
+ ObjPtr<mirror::Class> klass = value->IsClass() ? value->AsClass() : value->GetClass();
+ AbortTransactionF(
+ self, "Can't store reference to %s %s", description, klass->PrettyDescriptor().c_str());
+ return true;
+ }
+ return false;
+}
+
+bool AotClassLinker::TransactionReadConstraint(Thread* self, ObjPtr<mirror::Object> obj) {
+ DCHECK(obj->IsClass());
+ if (GetTransaction()->ReadConstraint(obj)) {
+ AbortTransactionF(self,
+ "Can't read static fields of %s since it does not belong to clinit's class.",
+ obj->PrettyTypeOf().c_str());
+ return true;
+ }
+ return false;
}
-bool AotClassLinker::TransactionAllocationConstraint(Thread* self,
- ObjPtr<mirror::Class> klass) const {
+bool AotClassLinker::TransactionAllocationConstraint(Thread* self, ObjPtr<mirror::Class> klass) {
DCHECK(IsActiveTransaction());
- return interpreter::ActiveTransactionChecker::AllocationConstraint(self, klass);
+ if (klass->IsFinalizable()) {
+ AbortTransactionF(self,
+ "Allocating finalizable object in transaction: %s",
+ klass->PrettyDescriptor().c_str());
+ return true;
+ }
+ return false;
}
void AotClassLinker::RecordWriteFieldBoolean(mirror::Object* obj,
diff --git a/runtime/oat/aot_class_linker.h b/runtime/oat/aot_class_linker.h
index 2d395da199..0ee2fe1b42 100644
--- a/runtime/oat/aot_class_linker.h
+++ b/runtime/oat/aot_class_linker.h
@@ -73,11 +73,14 @@ class AotClassLinker : public ClassLinker {
bool IsActiveStrictTransactionMode() const;
// Transaction constraint checks for AOT compilation.
- bool TransactionWriteConstraint(Thread* self, ObjPtr<mirror::Object> obj) const override
+ bool TransactionWriteConstraint(Thread* self, ObjPtr<mirror::Object> obj) override
REQUIRES_SHARED(Locks::mutator_lock_);
- bool TransactionWriteValueConstraint(Thread* self, ObjPtr<mirror::Object> value) const override
+ bool TransactionWriteValueConstraint(Thread* self, ObjPtr<mirror::Object> value) override
REQUIRES_SHARED(Locks::mutator_lock_);
- bool TransactionAllocationConstraint(Thread* self, ObjPtr<mirror::Class> klass) const override
+ // Note: The read constraint check is non-virtual because it's not needed by `UnstartedRuntime`.
+ bool TransactionReadConstraint(Thread* self, ObjPtr<mirror::Object> obj)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ bool TransactionAllocationConstraint(Thread* self, ObjPtr<mirror::Class> klass) override
REQUIRES_SHARED(Locks::mutator_lock_);
// Transaction bookkeeping for AOT compilation.