598 lines
23 KiB
C++
598 lines
23 KiB
C++
/*
|
||
* Copyright (C) 2017 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_SUBTYPE_CHECK_H_
|
||
#define ART_RUNTIME_SUBTYPE_CHECK_H_
|
||
|
||
#include "subtype_check_bits_and_status.h"
|
||
#include "subtype_check_info.h"
|
||
|
||
#include "base/locks.h"
|
||
#include "mirror/class.h"
|
||
#include "runtime.h"
|
||
|
||
// Build flag for the bitstring subtype check runtime hooks.
|
||
constexpr bool kBitstringSubtypeCheckEnabled = false;
|
||
|
||
/**
|
||
* Any node in a tree can have its path (from the root to the node) represented as a string by
|
||
* concatenating the path of the parent to that of the current node.
|
||
*
|
||
* We can annotate each node with a `sibling-label` which is some value unique amongst all of the
|
||
* node's siblings. As a special case, the root is empty.
|
||
*
|
||
* (none)
|
||
* / | \
|
||
* A B C
|
||
* / \
|
||
* A’ B’
|
||
* |
|
||
* A’’
|
||
* |
|
||
* A’’’
|
||
* |
|
||
* A’’’’
|
||
*
|
||
* Given these sibling-labels, we can now encode the path from any node to the root by starting at
|
||
* the node and going up to the root, marking each node with this `path-label`. The special
|
||
* character $ means "end of path".
|
||
*
|
||
* $
|
||
* / | \
|
||
* A$ B$ C$
|
||
* / \
|
||
* A’A$ B’A$
|
||
* |
|
||
* A’’B’A$
|
||
* |
|
||
* A’’’A’’B’A$
|
||
* |
|
||
* A’’’’A’’B’A$
|
||
*
|
||
* Given the above `path-label` we can express if any two nodes are an offspring of the other
|
||
* through a O(1) expression:
|
||
*
|
||
* x <: y :=
|
||
* suffix(x, y) == y
|
||
*
|
||
* In the above example suffix(x,y) means the suffix of x that is as long as y (right-padded with
|
||
* $s if x is shorter than y) :
|
||
*
|
||
* suffix(x,y) := x(x.length - y.length .. 0]
|
||
* + repeat($, max(y.length - x.length, 0))
|
||
*
|
||
* A few generalities here to elaborate:
|
||
*
|
||
* - There can be at most D levels in the tree.
|
||
* - Each level L has an alphabet A, and the maximum number of
|
||
* nodes is determined by |A|
|
||
* - The alphabet A can be a subset, superset, equal, or unique with respect to the other alphabets
|
||
* without loss of generality. (In practice it would almost always be a subset of the previous
|
||
* level’s alphabet as we assume most classes have less children the deeper they are.)
|
||
* - The `sibling-label` doesn’t need to be stored as an explicit value. It can a temporary when
|
||
* visiting every immediate child of a node. Only the `path-label` needs to be actually stored for
|
||
* every node.
|
||
*
|
||
* The path can also be reversed, and use a prefix instead of a suffix to define the subchild
|
||
* relation.
|
||
*
|
||
* $
|
||
* / | \ \
|
||
* A$ B$ C$ D$
|
||
* / \
|
||
* AA’$ AB’$
|
||
* |
|
||
* AB’A’’$
|
||
* |
|
||
* AB’A’’A’’’$
|
||
* |
|
||
* AB’A’’A’’’A’’’’$
|
||
*
|
||
* x <: y :=
|
||
* prefix(x, y) == y
|
||
*
|
||
* prefix(x,y) := x[0 .. y.length)
|
||
* + repeat($, max(y.length - x.length, 0))
|
||
*
|
||
* In a dynamic tree, new nodes can be inserted at any time. This means if a minimal alphabet is
|
||
* selected to contain the initial tree hierarchy, later node insertions will be illegal because
|
||
* there is no more room to encode the path.
|
||
*
|
||
* In this simple example with an alphabet A,B,C and max level 1:
|
||
*
|
||
* Level
|
||
* 0: $
|
||
* / | \ \
|
||
* 1: A$ B$ C$ D$ (illegal)
|
||
* |
|
||
* 2: AA$ (illegal)
|
||
*
|
||
* Attempting to insert the sibling “D” at Level 1 would be illegal because the Alphabet(1) is
|
||
* {A,B,C} and inserting an extra node would mean the `sibling-label` is no longer unique.
|
||
* Attempting to insert “AA$” is illegal because the level 2 is more than the max level 1.
|
||
*
|
||
* One solution to this would be to revisit the entire graph, select a larger alphabet to that
|
||
* every `sibling-label` is unique, pick a larger max level count, and then store the updated
|
||
* `path-label` accordingly.
|
||
*
|
||
* The more common approach would instead be to select a set of alphabets and max levels statically,
|
||
* with large enough sizes, for example:
|
||
*
|
||
* Alphabets = {{A,B,C,D}, {A,B,C}, {A,B}, {A}}
|
||
* Max Levels = |Alphabets|
|
||
*
|
||
* Which would allow up to 4 levels with each successive level having 1 less max siblings.
|
||
*
|
||
* Attempting to insert a new node into the graph which does not fit into that level’s alphabet
|
||
* would be represented by re-using the `path-label` of the parent. Such a `path_label` would be
|
||
* considered truncated (because it would only have a prefix of the full path from the root to the
|
||
* node).
|
||
*
|
||
* Level
|
||
* 0: $
|
||
* / | \ \
|
||
* 1: A$ B$ C$ $ (same as parent)
|
||
* |
|
||
* 2: A$ (same as parent)
|
||
*
|
||
* The updated relation for offspring is then:
|
||
*
|
||
* x <: y :=
|
||
* if !truncated_path(y):
|
||
* return prefix(x, y) == y // O(1)
|
||
* else:
|
||
* return slow_check_is_offspring(x, y) // worse than O(1)
|
||
*
|
||
* (Example definition of truncated_path -- any semantically equivalent way to check that the
|
||
* sibling's `sibling-label` is not unique will do)
|
||
*
|
||
* truncated_path(y) :=
|
||
* return y == parent(y)
|
||
*
|
||
* (Example definition. Any slower-than-O(1) definition will do here. This is the traversing
|
||
* superclass hierarchy solution)
|
||
*
|
||
* slow_check_is_offspring(x, y) :=
|
||
* if not x: return false
|
||
* else: return x == y || recursive_is_offspring(parent(x), y)
|
||
*
|
||
* In which case slow_check_is_offspring is some non-O(1) way to check if x and is an offspring of y.
|
||
*
|
||
* In addition, note that it doesn’t matter if the "x" from above is a unique sibling or not; the
|
||
* relation will still be correct.
|
||
*
|
||
* ------------------------------------------------------------------------------------------------
|
||
*
|
||
* Leveraging truncated paths to minimize path lengths.
|
||
*
|
||
* As observed above, for any x <: y, it is sufficient to have a full path only for y,
|
||
* and x can be truncated (to its nearest ancestor's full path).
|
||
*
|
||
* We call a node that stores a full path "Assigned", and a node that stores a truncated path
|
||
* either "Initialized" or "Overflowed."
|
||
*
|
||
* "Initialized" means it is still possible to assign a full path to the node, and "Overflowed"
|
||
* means there is insufficient characters in the alphabet left.
|
||
*
|
||
* In this example, assume that we attempt to "Assign" all non-leafs if possible. Leafs
|
||
* always get truncated (as either Initialized or Overflowed).
|
||
*
|
||
* Alphabets = {{A,B,C,D}, {A,B}}
|
||
* Max Levels = |Alphabets|
|
||
*
|
||
* Level
|
||
* 0: $
|
||
* / | \ \ \
|
||
* 1: A$ B$ C$ D$ $ (Overflowed: Too wide)
|
||
* | |
|
||
* 2: AA$ C$ (Initialized)
|
||
* |
|
||
* 3: AA$ (Overflowed: Too deep)
|
||
*
|
||
* (All un-annotated nodes are "Assigned").
|
||
* Above, the node at level 3 becomes overflowed because it exceeds the max levels. The
|
||
* right-most node at level 1 becomes overflowed because there's no characters in the alphabet
|
||
* left in that level.
|
||
*
|
||
* The "C$" node is Initialized at level 2, but it can still be promoted to "Assigned" later on
|
||
* if we wanted to.
|
||
*
|
||
* In particular, this is the strategy we use in our implementation
|
||
* (SubtypeCheck::EnsureInitialized, SubtypeCheck::EnsureAssigned).
|
||
*
|
||
* Since the # of characters in our alphabet (BitString) is very limited, we want to avoid
|
||
* allocating a character to a node until its absolutely necessary.
|
||
*
|
||
* All node targets (in `src <: target`) get Assigned, and any parent of an Initialized
|
||
* node also gets Assigned.
|
||
*/
|
||
namespace art {
|
||
|
||
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.
|
||
//
|
||
// Cost: O(Depth(Class))
|
||
//
|
||
// Post-condition: State is >= Initialized.
|
||
// Returns: The precise SubtypeCheckInfo::State.
|
||
static SubtypeCheckInfo::State EnsureInitialized(ClassPtr klass)
|
||
REQUIRES(Locks::subtype_check_lock_)
|
||
REQUIRES_SHARED(Locks::mutator_lock_) {
|
||
return InitializeOrAssign(klass, /*assign=*/false).GetState();
|
||
}
|
||
|
||
// Force this class's SubtypeCheckInfo state into Assigned|Overflowed.
|
||
// As a side-effect, all parent classes also become Assigned|Overflowed.
|
||
//
|
||
// Cost: O(Depth(Class))
|
||
//
|
||
// Post-condition: State is Assigned|Overflowed.
|
||
// Returns: The precise SubtypeCheckInfo::State.
|
||
static SubtypeCheckInfo::State EnsureAssigned(ClassPtr klass)
|
||
REQUIRES(Locks::subtype_check_lock_)
|
||
REQUIRES_SHARED(Locks::mutator_lock_) {
|
||
return InitializeOrAssign(klass, /*assign=*/true).GetState();
|
||
}
|
||
|
||
// Resets the SubtypeCheckInfo into the Uninitialized state.
|
||
//
|
||
// Intended only for the AOT image writer.
|
||
// This is a static function to avoid calling klass.Depth(), which is unsupported
|
||
// in some portions of the image writer.
|
||
//
|
||
// Cost: O(1).
|
||
//
|
||
// Returns: A state that is always Uninitialized.
|
||
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
|
||
// of existing live objects in the class hierarchy.
|
||
// This is only safe as the last step when the classes are about to be
|
||
// written out as an image and IsSubClass is never used again.
|
||
DCHECK(Runtime::Current() == nullptr || Runtime::Current()->IsAotCompiler())
|
||
<< "This only makes sense when compiling an app image.";
|
||
|
||
// Directly read/write the class field here.
|
||
// As this method is used by image_writer on a copy,
|
||
// the Class* there is not a real class and using it for anything
|
||
// more complicated (e.g. ObjPtr or Depth call) will fail dchecks.
|
||
|
||
// OK. zero-initializing subtype_check_info_ puts us into the kUninitialized state.
|
||
SubtypeCheckBits scb_uninitialized = SubtypeCheckBits{};
|
||
WriteSubtypeCheckBits(klass, scb_uninitialized);
|
||
|
||
// Do not use "SubtypeCheckInfo" API here since that requires Depth()
|
||
// which would cause a dcheck failure.
|
||
return SubtypeCheckInfo::kUninitialized;
|
||
}
|
||
|
||
// Retrieve the state of this class's SubtypeCheckInfo.
|
||
//
|
||
// Cost: O(Depth(Class)).
|
||
//
|
||
// Returns: The precise SubtypeCheckInfo::State.
|
||
static SubtypeCheckInfo::State GetState(ClassPtr klass)
|
||
REQUIRES(Locks::subtype_check_lock_)
|
||
REQUIRES_SHARED(Locks::mutator_lock_) {
|
||
return GetSubtypeCheckInfo(klass).GetState();
|
||
}
|
||
|
||
// Retrieve the path to root bitstring as a plain uintN_t value that is amenable to
|
||
// be used by a fast check "encoded_src & mask_target == encoded_target".
|
||
//
|
||
// Cost: O(Depth(Class)).
|
||
//
|
||
// Returns the encoded_src value. Must be >= Initialized (EnsureInitialized).
|
||
static BitString::StorageType GetEncodedPathToRootForSource(ClassPtr klass)
|
||
REQUIRES(Locks::subtype_check_lock_)
|
||
REQUIRES_SHARED(Locks::mutator_lock_) {
|
||
DCHECK_NE(SubtypeCheckInfo::kUninitialized, GetSubtypeCheckInfo(klass).GetState());
|
||
return GetSubtypeCheckInfo(klass).GetEncodedPathToRoot();
|
||
}
|
||
|
||
// Retrieve the path to root bitstring as a plain uintN_t value that is amenable to
|
||
// be used by a fast check "encoded_src & mask_target == encoded_target".
|
||
//
|
||
// Cost: O(Depth(Class)).
|
||
//
|
||
// Returns the encoded_target value. Must be Assigned (EnsureAssigned).
|
||
static BitString::StorageType GetEncodedPathToRootForTarget(ClassPtr klass)
|
||
REQUIRES(Locks::subtype_check_lock_)
|
||
REQUIRES_SHARED(Locks::mutator_lock_) {
|
||
SubtypeCheckInfo sci = GetSubtypeCheckInfo(klass);
|
||
DCHECK_EQ(SubtypeCheckInfo::kAssigned, sci.GetState());
|
||
return sci.GetEncodedPathToRoot();
|
||
}
|
||
|
||
// Retrieve the path to root bitstring mask as a plain uintN_t value that is amenable to
|
||
// be used by a fast check "encoded_src & mask_target == encoded_target".
|
||
//
|
||
// Cost: O(Depth(Class)).
|
||
//
|
||
// Returns the mask_target value. Must be Assigned (EnsureAssigned).
|
||
static BitString::StorageType GetEncodedPathToRootMask(ClassPtr klass)
|
||
REQUIRES(Locks::subtype_check_lock_)
|
||
REQUIRES_SHARED(Locks::mutator_lock_) {
|
||
SubtypeCheckInfo sci = GetSubtypeCheckInfo(klass);
|
||
DCHECK_EQ(SubtypeCheckInfo::kAssigned, sci.GetState());
|
||
return sci.GetEncodedPathToRootMask();
|
||
}
|
||
|
||
// Is the source class a subclass of the target?
|
||
//
|
||
// The source state must be at least Initialized, and the target state
|
||
// must be Assigned, otherwise the result will return kUnknownSubtypeOf.
|
||
//
|
||
// See EnsureInitialized and EnsureAssigned. Ideally,
|
||
// EnsureInitialized will be called previously on all possible sources,
|
||
// and EnsureAssigned will be called previously on all possible targets.
|
||
//
|
||
// 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(ClassPtr source, ClassPtr target)
|
||
REQUIRES_SHARED(Locks::mutator_lock_) {
|
||
SubtypeCheckInfo sci = GetSubtypeCheckInfo(source);
|
||
SubtypeCheckInfo target_sci = GetSubtypeCheckInfo(target);
|
||
|
||
return sci.IsSubtypeOf(target_sci);
|
||
}
|
||
|
||
// Print SubtypeCheck bitstring and overflow to a stream (e.g. for oatdump).
|
||
static std::ostream& Dump(ClassPtr klass, std::ostream& os)
|
||
REQUIRES_SHARED(Locks::mutator_lock_) {
|
||
return os << GetSubtypeCheckInfo(klass);
|
||
}
|
||
|
||
static void WriteStatus(ClassPtr klass, ClassStatus status)
|
||
REQUIRES_SHARED(Locks::mutator_lock_) {
|
||
WriteStatusImpl(klass, status);
|
||
}
|
||
|
||
private:
|
||
static ClassPtr GetParentClass(ClassPtr klass)
|
||
REQUIRES_SHARED(Locks::mutator_lock_) {
|
||
DCHECK(klass->HasSuperClass());
|
||
return ClassPtr(klass->GetSuperClass());
|
||
}
|
||
|
||
static SubtypeCheckInfo InitializeOrAssign(ClassPtr klass, bool assign)
|
||
REQUIRES(Locks::subtype_check_lock_)
|
||
REQUIRES_SHARED(Locks::mutator_lock_) {
|
||
if (UNLIKELY(!klass->HasSuperClass())) {
|
||
// Object root always goes directly from Uninitialized -> Assigned.
|
||
|
||
const SubtypeCheckInfo root_sci = GetSubtypeCheckInfo(klass);
|
||
if (root_sci.GetState() != SubtypeCheckInfo::kUninitialized) {
|
||
return root_sci; // No change needed.
|
||
}
|
||
|
||
const SubtypeCheckInfo new_root_sci = root_sci.CreateRoot();
|
||
SetSubtypeCheckInfo(klass, new_root_sci);
|
||
|
||
// The object root is always in the Uninitialized|Assigned state.
|
||
DCHECK_EQ(SubtypeCheckInfo::kAssigned, GetSubtypeCheckInfo(klass).GetState())
|
||
<< "Invalid object root state, must be Assigned";
|
||
return new_root_sci;
|
||
}
|
||
|
||
// Force all ancestors to Assigned | Overflowed.
|
||
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 ||
|
||
parent_state == SubtypeCheckInfo::kOverflowed)
|
||
<< "Expected parent Assigned|Overflowed, but was: " << parent_state;
|
||
}
|
||
|
||
// Read.
|
||
SubtypeCheckInfo sci = GetSubtypeCheckInfo(klass, parent_depth + 1u);
|
||
SubtypeCheckInfo parent_sci = GetSubtypeCheckInfo(parent_klass, parent_depth);
|
||
|
||
// Modify.
|
||
const SubtypeCheckInfo::State sci_state = sci.GetState();
|
||
// Skip doing any work if the state is already up-to-date.
|
||
// - assign == false -> Initialized or higher.
|
||
// - assign == true -> Assigned or higher.
|
||
if (sci_state == SubtypeCheckInfo::kUninitialized ||
|
||
(sci_state == SubtypeCheckInfo::kInitialized && assign)) {
|
||
// Copy parent path into the child.
|
||
//
|
||
// If assign==true, this also appends Parent.Next value to the end.
|
||
// Then the Parent.Next value is incremented to avoid allocating
|
||
// the same value again to another node.
|
||
sci = parent_sci.CreateChild(assign); // Note: Parent could be mutated.
|
||
} else {
|
||
// Nothing to do, already >= Initialized.
|
||
return sci;
|
||
}
|
||
|
||
// Post-condition: EnsureAssigned -> Assigned|Overflowed.
|
||
// Post-condition: EnsureInitialized -> Not Uninitialized.
|
||
DCHECK_NE(sci.GetState(), SubtypeCheckInfo::kUninitialized);
|
||
|
||
if (assign) {
|
||
DCHECK_NE(sci.GetState(), SubtypeCheckInfo::kInitialized);
|
||
}
|
||
|
||
// Write.
|
||
SetSubtypeCheckInfo(klass, sci); // self
|
||
SetSubtypeCheckInfo(parent_klass, parent_sci); // parent
|
||
|
||
return sci;
|
||
}
|
||
|
||
static SubtypeCheckBitsAndStatus ReadField(ClassPtr klass)
|
||
REQUIRES_SHARED(Locks::mutator_lock_) {
|
||
SubtypeCheckBitsAndStatus current_bits_and_status;
|
||
|
||
int32_t int32_data = klass->GetField32Volatile(klass->StatusOffset());
|
||
current_bits_and_status.int32_alias_ = int32_data;
|
||
|
||
if (kIsDebugBuild) {
|
||
SubtypeCheckBitsAndStatus tmp;
|
||
memcpy(&tmp, &int32_data, sizeof(tmp));
|
||
DCHECK_EQ(0, memcmp(&tmp, ¤t_bits_and_status, sizeof(tmp))) << int32_data;
|
||
}
|
||
return current_bits_and_status;
|
||
}
|
||
|
||
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.
|
||
// Although we have exclusive access to the bitstrings, because
|
||
// ClassStatus and SubtypeCheckBits share the same word, another thread could
|
||
// potentially overwrite that word still.
|
||
|
||
SubtypeCheckBitsAndStatus new_value;
|
||
ClassStatus old_status;
|
||
SubtypeCheckBitsAndStatus full_old;
|
||
while (true) {
|
||
// TODO: Atomic compare-and-swap does not update the 'expected' parameter,
|
||
// so we have to read it as a separate step instead.
|
||
SubtypeCheckBitsAndStatus old_value = ReadField(klass);
|
||
|
||
{
|
||
SubtypeCheckBits old_bits = old_value.subtype_check_info_;
|
||
if (memcmp(&old_bits, &new_bits, sizeof(old_bits)) == 0) {
|
||
// Avoid dirtying memory when the data hasn't changed.
|
||
return;
|
||
}
|
||
}
|
||
|
||
full_old = old_value;
|
||
old_status = old_value.status_;
|
||
|
||
new_value = old_value;
|
||
new_value.subtype_check_info_ = new_bits;
|
||
|
||
if (kIsDebugBuild) {
|
||
int32_t int32_data = 0;
|
||
memcpy(&int32_data, &new_value, sizeof(int32_t));
|
||
DCHECK_EQ(int32_data, new_value.int32_alias_) << int32_data;
|
||
|
||
DCHECK_EQ(old_status, new_value.status_)
|
||
<< "full new: " << bit_cast<uint32_t>(new_value)
|
||
<< ", full old: " << bit_cast<uint32_t>(full_old);
|
||
}
|
||
|
||
if (CasFieldWeakSequentiallyConsistent32(klass,
|
||
klass->StatusOffset(),
|
||
old_value.int32_alias_,
|
||
new_value.int32_alias_)) {
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
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.
|
||
SubtypeCheckBitsAndStatus new_value;
|
||
ClassStatus old_status;
|
||
while (true) {
|
||
// TODO: Atomic compare-and-swap does not update the 'expected' parameter,
|
||
// so we have to read it as a separate step instead.
|
||
SubtypeCheckBitsAndStatus old_value = ReadField(klass);
|
||
old_status = old_value.status_;
|
||
|
||
if (memcmp(&old_status, &status, sizeof(status)) == 0) {
|
||
// Avoid dirtying memory when the data hasn't changed.
|
||
return;
|
||
}
|
||
|
||
new_value = old_value;
|
||
new_value.status_ = status;
|
||
|
||
if (CasFieldWeakSequentiallyConsistent32(klass,
|
||
klass->StatusOffset(),
|
||
old_value.int32_alias_,
|
||
new_value.int32_alias_)) {
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
static bool CasFieldWeakSequentiallyConsistent32(ClassPtr klass,
|
||
MemberOffset offset,
|
||
int32_t old_value,
|
||
int32_t new_value)
|
||
REQUIRES_SHARED(Locks::mutator_lock_) {
|
||
if (Runtime::Current() != nullptr && Runtime::Current()->IsActiveTransaction()) {
|
||
return klass->template CasField32</*kTransactionActive=*/true>(offset,
|
||
old_value,
|
||
new_value,
|
||
CASMode::kWeak,
|
||
std::memory_order_seq_cst);
|
||
} else {
|
||
return klass->template CasField32</*kTransactionActive=*/false>(offset,
|
||
old_value,
|
||
new_value,
|
||
CASMode::kWeak,
|
||
std::memory_order_seq_cst);
|
||
}
|
||
}
|
||
|
||
// Get the SubtypeCheckInfo for a klass. O(Depth(Class)) since
|
||
// it also requires calling klass->Depth.
|
||
//
|
||
// Anything calling this function will also be O(Depth(Class)).
|
||
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);
|
||
|
||
const SubtypeCheckInfo current =
|
||
SubtypeCheckInfo::Create(current_bits_and_status.subtype_check_info_, depth);
|
||
return current;
|
||
}
|
||
|
||
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();
|
||
WriteSubtypeCheckBits(klass, new_bits);
|
||
}
|
||
|
||
// Tests can inherit this class. Normal code should use static methods.
|
||
SubtypeCheck() = default;
|
||
SubtypeCheck(const SubtypeCheck& other) = default;
|
||
SubtypeCheck(SubtypeCheck&& other) noexcept = default;
|
||
~SubtypeCheck() = default;
|
||
|
||
friend struct MockSubtypeCheck;
|
||
};
|
||
|
||
} // namespace art
|
||
|
||
#endif // ART_RUNTIME_SUBTYPE_CHECK_H_
|