diff options
author | 2024-05-20 15:51:01 +0000 | |
---|---|---|
committer | 2024-05-21 13:48:45 +0000 | |
commit | 2a25327e2bfdd7f5d128267684b13dc0f0e57e55 (patch) | |
tree | 9e9d0b80e78260cd961e47245b6676ce6e4f632e | |
parent | e0e0f0740a1c1f4f28e1d1f59fe77625aea8d4a4 (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.cc | 6 | ||||
-rw-r--r-- | runtime/class_linker.h | 6 | ||||
-rw-r--r-- | runtime/interpreter/active_transaction_checker.h | 102 | ||||
-rw-r--r-- | runtime/interpreter/interpreter_common.h | 235 | ||||
-rw-r--r-- | runtime/interpreter/interpreter_switch_impl-inl.h | 200 | ||||
-rw-r--r-- | runtime/interpreter/interpreter_switch_impl0.cc | 36 | ||||
-rw-r--r-- | runtime/interpreter/interpreter_switch_impl1.cc | 37 | ||||
-rw-r--r-- | runtime/oat/aot_class_linker.cc | 48 | ||||
-rw-r--r-- | runtime/oat/aot_class_linker.h | 9 |
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. |