ART: Faster type check bitstring initialization.
Reuse depth from recursive call instead of calculating it
repeatedly at every level of recursion. Pass pointers by
value instead of reference.
Test: m test-art-host-gtest
Test: testrunner.py --host --jit
Bug: 70734806
Change-Id: Idd405a2c3b04adbfd544639358dc562b32e4c34f
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 192517f..727dd14 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -462,10 +462,8 @@
//
// We take the lock here to avoid using NO_THREAD_SAFETY_ANALYSIS.
MutexLock subtype_check_lock(Thread::Current(), *Locks::subtype_check_lock_);
- mirror::Class* java_lang_Object_ptr = java_lang_Object.Get();
- SubtypeCheck<mirror::Class*>::EnsureInitialized(java_lang_Object_ptr);
- mirror::Class* java_lang_Class_ptr = java_lang_Class.Get();
- SubtypeCheck<mirror::Class*>::EnsureInitialized(java_lang_Class_ptr);
+ SubtypeCheck<ObjPtr<mirror::Class>>::EnsureInitialized(java_lang_Object.Get());
+ SubtypeCheck<ObjPtr<mirror::Class>>::EnsureInitialized(java_lang_Class.Get());
}
// Object[] next to hold class roots.
@@ -1872,8 +1870,7 @@
ScopedTrace trace("Recalculate app image SubtypeCheck bitstrings");
MutexLock subtype_check_lock(Thread::Current(), *Locks::subtype_check_lock_);
for (const ClassTable::TableSlot& root : temp_set) {
- mirror::Class* root_klass = root.Read();
- SubtypeCheck<mirror::Class*>::EnsureInitialized(root_klass);
+ SubtypeCheck<ObjPtr<mirror::Class>>::EnsureInitialized(root.Read());
}
}
}
@@ -5220,8 +5217,7 @@
// or Overflowed (can be used as a source for IsSubClass check).
{
MutexLock subtype_check_lock(Thread::Current(), *Locks::subtype_check_lock_);
- ObjPtr<mirror::Class> c_ptr(c.Get());
- SubtypeCheck<ObjPtr<mirror::Class>>::EnsureInitialized(c_ptr);
+ SubtypeCheck<ObjPtr<mirror::Class>>::EnsureInitialized(c.Get());
// TODO: Avoid taking subtype_check_lock_ if SubtypeCheck is already initialized.
}
const bool success = InitializeClass(self, c, can_init_fields, can_init_parents);
diff --git a/runtime/subtype_check.h b/runtime/subtype_check.h
index 1556abe..54d2f00 100644
--- a/runtime/subtype_check.h
+++ b/runtime/subtype_check.h
@@ -216,12 +216,13 @@
* All node targets (in `src <: target`) get Assigned, and any parent of an Initialized
* node also gets Assigned.
*/
-struct MockSubtypeCheck; // Forward declaration for testing.
namespace art {
-// This is class is using a template parameter to enable testability without losing performance.
-// KlassT is almost always `mirror::Class*` or `ObjPtr<mirror::Class>`.
-template <typename KlassT /* Pointer-like type to Class */>
+struct MockSubtypeCheck; // Forward declaration for testing.
+
+// This class is using a template parameter to enable testability without losing performance.
+// ClassPtr is almost always `mirror::Class*` or `ObjPtr<mirror::Class>`.
+template <typename ClassPtr /* Pointer-like type to Class */>
struct SubtypeCheck {
// Force this class's SubtypeCheckInfo state into at least Initialized.
// As a side-effect, all parent classes also become Assigned|Overflowed.
@@ -230,7 +231,7 @@
//
// Post-condition: State is >= Initialized.
// Returns: The precise SubtypeCheckInfo::State.
- static SubtypeCheckInfo::State EnsureInitialized(KlassT& klass)
+ static SubtypeCheckInfo::State EnsureInitialized(ClassPtr klass)
REQUIRES(Locks::subtype_check_lock_)
REQUIRES_SHARED(Locks::mutator_lock_) {
return InitializeOrAssign(klass, /*assign*/false).GetState();
@@ -243,7 +244,7 @@
//
// Post-condition: State is Assigned|Overflowed.
// Returns: The precise SubtypeCheckInfo::State.
- static SubtypeCheckInfo::State EnsureAssigned(KlassT& klass)
+ static SubtypeCheckInfo::State EnsureAssigned(ClassPtr klass)
REQUIRES(Locks::subtype_check_lock_)
REQUIRES_SHARED(Locks::mutator_lock_) {
return InitializeOrAssign(klass, /*assign*/true).GetState();
@@ -258,7 +259,7 @@
// Cost: O(1).
//
// Returns: A state that is always Uninitialized.
- static SubtypeCheckInfo::State ForceUninitialize(KlassT& klass)
+ static SubtypeCheckInfo::State ForceUninitialize(ClassPtr klass)
REQUIRES(Locks::subtype_check_lock_)
REQUIRES_SHARED(Locks::mutator_lock_) {
// Trying to do this in a real runtime will break thread safety invariants
@@ -288,7 +289,7 @@
// Cost: O(Depth(Class)).
//
// Returns the encoded_src value. Must be >= Initialized (EnsureInitialized).
- static BitString::StorageType GetEncodedPathToRootForSource(const KlassT& klass)
+ static BitString::StorageType GetEncodedPathToRootForSource(ClassPtr klass)
REQUIRES(Locks::subtype_check_lock_)
REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK_NE(SubtypeCheckInfo::kUninitialized, GetSubtypeCheckInfo(klass).GetState());
@@ -301,7 +302,7 @@
// Cost: O(Depth(Class)).
//
// Returns the encoded_target value. Must be Assigned (EnsureAssigned).
- static BitString::StorageType GetEncodedPathToRootForTarget(const KlassT& klass)
+ static BitString::StorageType GetEncodedPathToRootForTarget(ClassPtr klass)
REQUIRES(Locks::subtype_check_lock_)
REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK_EQ(SubtypeCheckInfo::kAssigned, GetSubtypeCheckInfo(klass).GetState());
@@ -314,7 +315,7 @@
// Cost: O(Depth(Class)).
//
// Returns the mask_target value. Must be Assigned (EnsureAssigned).
- static BitString::StorageType GetEncodedPathToRootMask(const KlassT& klass)
+ static BitString::StorageType GetEncodedPathToRootMask(ClassPtr klass)
REQUIRES(Locks::subtype_check_lock_)
REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK_EQ(SubtypeCheckInfo::kAssigned, GetSubtypeCheckInfo(klass).GetState());
@@ -333,7 +334,7 @@
// Runtime cost: O(Depth(Class)), but would be O(1) if depth was known.
//
// If the result is known, return kSubtypeOf or kNotSubtypeOf.
- static SubtypeCheckInfo::Result IsSubtypeOf(const KlassT& source, const KlassT& target)
+ static SubtypeCheckInfo::Result IsSubtypeOf(ClassPtr source, ClassPtr target)
REQUIRES_SHARED(Locks::mutator_lock_) {
SubtypeCheckInfo sci = GetSubtypeCheckInfo(source);
SubtypeCheckInfo target_sci = GetSubtypeCheckInfo(target);
@@ -342,24 +343,24 @@
}
// Print SubtypeCheck bitstring and overflow to a stream (e.g. for oatdump).
- static std::ostream& Dump(const KlassT& klass, std::ostream& os)
+ static std::ostream& Dump(ClassPtr klass, std::ostream& os)
REQUIRES_SHARED(Locks::mutator_lock_) {
return os << GetSubtypeCheckInfo(klass);
}
- static void WriteStatus(const KlassT& klass, ClassStatus status)
+ static void WriteStatus(ClassPtr klass, ClassStatus status)
REQUIRES_SHARED(Locks::mutator_lock_) {
WriteStatusImpl(klass, status);
}
private:
- static KlassT GetParentClass(const KlassT& klass)
+ static ClassPtr GetParentClass(ClassPtr klass)
REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(klass->HasSuperClass());
- return KlassT(klass->GetSuperClass());
+ return ClassPtr(klass->GetSuperClass());
}
- static SubtypeCheckInfo InitializeOrAssign(KlassT& klass, bool assign)
+ static SubtypeCheckInfo InitializeOrAssign(ClassPtr klass, bool assign)
REQUIRES(Locks::subtype_check_lock_)
REQUIRES_SHARED(Locks::mutator_lock_) {
if (UNLIKELY(!klass->HasSuperClass())) {
@@ -380,8 +381,8 @@
}
// Force all ancestors to Assigned | Overflowed.
- KlassT parent_klass = GetParentClass(klass);
- EnsureAssigned(parent_klass);
+ ClassPtr parent_klass = GetParentClass(klass);
+ size_t parent_depth = InitializeOrAssign(parent_klass, /*assign*/true).GetDepth();
if (kIsDebugBuild) {
SubtypeCheckInfo::State parent_state = GetSubtypeCheckInfo(parent_klass).GetState();
DCHECK(parent_state == SubtypeCheckInfo::kAssigned ||
@@ -390,8 +391,8 @@
}
// Read.
- SubtypeCheckInfo sci = GetSubtypeCheckInfo(klass);
- SubtypeCheckInfo parent_sci = GetSubtypeCheckInfo(parent_klass);
+ SubtypeCheckInfo sci = GetSubtypeCheckInfo(klass, parent_depth + 1u);
+ SubtypeCheckInfo parent_sci = GetSubtypeCheckInfo(parent_klass, parent_depth);
// Modify.
const SubtypeCheckInfo::State sci_state = sci.GetState();
@@ -426,7 +427,7 @@
return sci;
}
- static SubtypeCheckBitsAndStatus ReadField(const KlassT& klass)
+ static SubtypeCheckBitsAndStatus ReadField(ClassPtr klass)
REQUIRES_SHARED(Locks::mutator_lock_) {
SubtypeCheckBitsAndStatus current_bits_and_status;
@@ -441,7 +442,7 @@
return current_bits_and_status;
}
- static void WriteSubtypeCheckBits(const KlassT& klass, const SubtypeCheckBits& new_bits)
+ static void WriteSubtypeCheckBits(ClassPtr klass, const SubtypeCheckBits& new_bits)
REQUIRES(Locks::subtype_check_lock_)
REQUIRES_SHARED(Locks::mutator_lock_) {
// Use a "CAS" to write the SubtypeCheckBits in the class.
@@ -490,7 +491,7 @@
}
}
- static void WriteStatusImpl(const KlassT& klass, ClassStatus status)
+ static void WriteStatusImpl(ClassPtr klass, ClassStatus status)
REQUIRES_SHARED(Locks::mutator_lock_) {
// Despite not having a lock annotation, this is done with mutual exclusion.
// See Class::SetStatus for more details.
@@ -519,7 +520,7 @@
}
}
- static bool CasFieldWeakSequentiallyConsistent32(const KlassT& klass,
+ static bool CasFieldWeakSequentiallyConsistent32(ClassPtr klass,
MemberOffset offset,
int32_t old_value,
int32_t new_value)
@@ -541,18 +542,23 @@
// it also requires calling klass->Depth.
//
// Anything calling this function will also be O(Depth(Class)).
- static SubtypeCheckInfo GetSubtypeCheckInfo(const KlassT& klass)
+ static SubtypeCheckInfo GetSubtypeCheckInfo(ClassPtr klass)
REQUIRES_SHARED(Locks::mutator_lock_) {
+ return GetSubtypeCheckInfo(klass, klass->Depth());
+ }
+
+ // Get the SubtypeCheckInfo for a klass with known depth.
+ static SubtypeCheckInfo GetSubtypeCheckInfo(ClassPtr klass, size_t depth)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK_EQ(depth, klass->Depth());
SubtypeCheckBitsAndStatus current_bits_and_status = ReadField(klass);
- size_t depth = klass->Depth();
const SubtypeCheckInfo current =
SubtypeCheckInfo::Create(current_bits_and_status.subtype_check_info_, depth);
return current;
}
- static void SetSubtypeCheckInfo(KlassT& klass,
- const SubtypeCheckInfo& new_sci)
+ static void SetSubtypeCheckInfo(ClassPtr klass, const SubtypeCheckInfo& new_sci)
REQUIRES(Locks::subtype_check_lock_)
REQUIRES_SHARED(Locks::mutator_lock_) {
SubtypeCheckBits new_bits = new_sci.GetSubtypeCheckBits();
@@ -565,7 +571,7 @@
SubtypeCheck(SubtypeCheck&& other) = default;
~SubtypeCheck() = default;
- friend struct ::MockSubtypeCheck;
+ friend struct MockSubtypeCheck;
};
} // namespace art
diff --git a/runtime/subtype_check_info.h b/runtime/subtype_check_info.h
index 8320fb3..61d590b 100644
--- a/runtime/subtype_check_info.h
+++ b/runtime/subtype_check_info.h
@@ -138,6 +138,11 @@
kSubtypeOf // Enough data. src is a subchild of the target.
};
+ // Get the raw depth.
+ size_t GetDepth() const {
+ return depth_;
+ }
+
// Chop off the depth, returning only the bitstring+of state.
// (Used to store into memory, since storing the depth would be redundant.)
SubtypeCheckBits GetSubtypeCheckBits() const {
diff --git a/runtime/subtype_check_test.cc b/runtime/subtype_check_test.cc
index dd51c18..e297d0b 100644
--- a/runtime/subtype_check_test.cc
+++ b/runtime/subtype_check_test.cc
@@ -24,10 +24,6 @@
constexpr size_t BitString::kBitSizeAtPosition[BitString::kCapacity];
constexpr size_t BitString::kCapacity;
-}; // namespace art
-
-using namespace art; // NOLINT
-
struct MockClass {
explicit MockClass(MockClass* parent, size_t x = 0, size_t y = 0) {
parent_ = parent;
@@ -1061,3 +1057,5 @@
}
// TODO: add dcheck for child-parent invariants (e.g. child < parent.next) and death tests
+
+} // namespace art