[vm] Simplify is-deeply-immutable check in transitive object copy

Now that we have an immutability bit, we can set the bit in object
header of newly allocated objects if those objects are always
deeply immutable.

That allows only checking for the immutable bit instead of checking
against a whole range of cids when sending objects via
`SendPort.send()` / `Isolate.exit()`.

TEST=ci

Change-Id: I4451a9fe11f68c7dd874d49ab632daa3102be19e
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/281842
Commit-Queue: Martin Kustermann <kustermann@google.com>
Reviewed-by: Ryan Macnak <rmacnak@google.com>
This commit is contained in:
Martin Kustermann 2023-02-09 00:23:22 +00:00 committed by Commit Queue
parent ebdb53fe85
commit 7db7bd0573
6 changed files with 28 additions and 53 deletions

View file

@ -214,32 +214,11 @@ static ObjectPtr ValidateMessageObject(Zone* zone,
thread->CheckForSafepoint();
ObjectPtr raw = working_set->RemoveLast();
if (CanShareObjectAcrossIsolates(raw)) {
continue;
}
const intptr_t cid = raw->GetClassId();
// Keep the list in sync with the one in runtime/vm/object_graph_copy.cc
switch (cid) {
// Can be shared.
case kOneByteStringCid:
case kTwoByteStringCid:
case kExternalOneByteStringCid:
case kExternalTwoByteStringCid:
case kMintCid:
case kImmutableArrayCid:
case kNeverCid:
case kSentinelCid:
case kInt32x4Cid:
case kSendPortCid:
case kCapabilityCid:
case kRegExpCid:
case kStackTraceCid:
continue;
// Cannot be shared due to possibly being mutable boxes for unboxed
// fields in JIT, but can be transferred via Isolate.exit()
case kDoubleCid:
case kFloat32x4Cid:
case kFloat64x2Cid:
continue;
case kArrayCid: {
array ^= Array::RawCast(raw);
visitor.VisitObject(array.GetTypeArguments());

View file

@ -419,6 +419,16 @@ inline bool IsUnmodifiableTypedDataViewClassId(intptr_t index) {
kTypedDataCidRemainderUnmodifiable);
}
inline bool ShouldHaveImmutabilityBitSet(intptr_t index) {
return IsUnmodifiableTypedDataViewClassId(index) || IsStringClassId(index) ||
index == kMintCid || index == kNeverCid || index == kSentinelCid ||
index == kStackTraceCid || index == kDoubleCid ||
index == kFloat32x4Cid || index == kFloat64x2Cid ||
index == kInt32x4Cid || index == kSendPortCid ||
index == kCapabilityCid || index == kRegExpCid || index == kBoolCid ||
index == kNullCid;
}
inline bool IsFfiTypeClassId(intptr_t index) {
switch (index) {
case kPointerCid:

View file

@ -349,7 +349,7 @@ uword MakeTagWordForNewSpaceObject(classid_t cid, uword instance_size) {
dart::UntaggedObject::ClassIdTag::encode(cid) |
dart::UntaggedObject::NewBit::encode(true) |
dart::UntaggedObject::ImmutableBit::encode(
IsUnmodifiableTypedDataViewClassId(cid));
ShouldHaveImmutabilityBitSet(cid));
}
word Object::tags_offset() {

View file

@ -2766,7 +2766,7 @@ void Object::InitializeObject(uword address,
tags = UntaggedObject::OldAndNotRememberedBit::update(is_old, tags);
tags = UntaggedObject::NewBit::update(!is_old, tags);
tags = UntaggedObject::ImmutableBit::update(
IsUnmodifiableTypedDataViewClassId(class_id), tags);
ShouldHaveImmutabilityBitSet(class_id), tags);
#if defined(HASH_IN_OBJECT_HEADER)
tags = UntaggedObject::HashTag::update(0, tags);
#endif

View file

@ -144,47 +144,30 @@ static ObjectPtr Marker() {
return Object::unknown_constant().ptr();
}
// Keep in sync with runtime/lib/isolate.cc:ValidateMessageObject
DART_FORCE_INLINE
static bool CanShareObject(ObjectPtr obj, uword tags) {
if ((tags & UntaggedObject::CanonicalBit::mask_in_place()) != 0) {
return true;
}
const auto cid = UntaggedObject::ClassIdTag::decode(tags);
if (cid == kOneByteStringCid) return true;
if (cid == kTwoByteStringCid) return true;
if (cid == kExternalOneByteStringCid) return true;
if (cid == kExternalTwoByteStringCid) return true;
if (cid == kMintCid) return true;
if (cid == kNeverCid) return true;
if (cid == kSentinelCid) return true;
if (cid == kStackTraceCid) return true;
if (cid == kDoubleCid || cid == kFloat32x4Cid || cid == kFloat64x2Cid ||
cid == kInt32x4Cid) {
if ((tags & UntaggedObject::ImmutableBit::mask_in_place()) != 0) {
if (IsUnmodifiableTypedDataViewClassId(cid)) {
// Unmodifiable typed data views may have mutable backing stores.
return TypedDataView::RawCast(obj)
->untag()
->typed_data()
->untag()
->IsImmutable();
}
// All other objects that have immutability bit set are deeply immutable.
return true;
}
if (cid == kSendPortCid) return true;
if (cid == kCapabilityCid) return true;
if (cid == kRegExpCid) return true;
if (cid == kClosureCid) {
// We can share a closure iff it doesn't close over any state.
return Closure::RawCast(obj)->untag()->context() == Object::null();
}
if (IsUnmodifiableTypedDataViewClassId(cid)) {
// Unmodifiable typed data views may have mutable backing stores.
return TypedDataView::RawCast(obj)
->untag()
->typed_data()
->untag()
->IsImmutable();
} else {
if ((tags & UntaggedObject::ImmutableBit::mask_in_place()) != 0) {
return true;
}
}
return false;
}

View file

@ -253,6 +253,9 @@ class UntaggedObject {
class OldAndNotRememberedBit
: public BitField<uword, bool, kOldAndNotRememberedBit, 1> {};
// Will be set to 1 iff
// - is unmodifiable typed data view (backing store may be mutable)
// - is transitively immutable
class ImmutableBit : public BitField<uword, bool, kImmutableBit, 1> {};
class ReservedBit : public BitField<uword, intptr_t, kReservedBit, 1> {};