[vm] Unify nullability and type state among all types

This change moves 'nullability' and 'type_state' from all kinds of
types to AbstractType base class. This removes a lot of code
duplication and allows uniform access to nullability and type state
for all kinds of types.

TEST=ci
Fixes https://github.com/dart-lang/sdk/issues/47034

Change-Id: I1f0dc7fda78426db83fec6a20ebebcd632ad6d99
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/260662
Reviewed-by: Tess Strickland <sstrickl@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Ryan Macnak <rmacnak@google.com>
This commit is contained in:
Alexander Markov 2022-09-27 17:30:03 +00:00 committed by Commit Queue
parent f04984b432
commit d592882f49
25 changed files with 681 additions and 1113 deletions

View file

@ -4046,10 +4046,6 @@ class LibraryPrefixDeserializationCluster : public DeserializationCluster {
}
};
// Used to pack nullability into other serialized values.
static constexpr intptr_t kNullabilityBitSize = 2;
static constexpr intptr_t kNullabilityBitMask = (1 << kNullabilityBitSize) - 1;
#if !defined(DART_PRECOMPILED_RUNTIME)
class TypeSerializationCluster
: public CanonicalSetSerializationCluster<
@ -4073,9 +4069,9 @@ class TypeSerializationCluster
PushFromTo(type);
ASSERT(type->untag()->type_class_id_ != kIllegalCid);
ASSERT(type->untag()->type_class_id() != kIllegalCid);
ClassPtr type_class =
s->isolate_group()->class_table()->At(type->untag()->type_class_id_);
s->isolate_group()->class_table()->At(type->untag()->type_class_id());
s->Push(type_class);
}
@ -4108,7 +4104,7 @@ class TypeSerializationCluster
// Keep in sync with Type::Canonicalize.
virtual bool IsInCanonicalSet(Serializer* s, TypePtr type) {
ClassPtr type_class =
s->isolate_group()->class_table()->At(type->untag()->type_class_id_);
s->isolate_group()->class_table()->At(type->untag()->type_class_id());
if (type_class->untag()->declaration_type() != type) {
return true;
}
@ -4123,25 +4119,12 @@ class TypeSerializationCluster
#if defined(DART_PRECOMPILER)
if (FLAG_write_v8_snapshot_profile_to != nullptr) {
ClassPtr type_class =
s->isolate_group()->class_table()->At(type->untag()->type_class_id_);
s->isolate_group()->class_table()->At(type->untag()->type_class_id());
s->AttributePropertyRef(type_class, "<type_class>");
}
#endif
WriteFromTo(type);
COMPILE_ASSERT(
std::is_unsigned<decltype(UntaggedType::type_class_id_)>::value);
s->WriteUnsigned(type->untag()->type_class_id_);
ASSERT(type->untag()->type_state_ < (1 << UntaggedType::kTypeStateBitSize));
ASSERT(type->untag()->nullability_ < (1 << kNullabilityBitSize));
static_assert(UntaggedType::kTypeStateBitSize + kNullabilityBitSize <=
kBitsPerByte * sizeof(uint8_t),
"Cannot pack type_state_ and nullability_ into a uint8_t");
const uint8_t combined =
(type->untag()->type_state_ << kNullabilityBitSize) |
type->untag()->nullability_;
ASSERT_EQUAL(type->untag()->type_state_, combined >> kNullabilityBitSize);
ASSERT_EQUAL(type->untag()->nullability_, combined & kNullabilityBitMask);
s->Write<uint8_t>(combined);
s->WriteUnsigned(type->untag()->flags_);
}
};
#endif // !DART_PRECOMPILED_RUNTIME
@ -4170,12 +4153,7 @@ class TypeDeserializationCluster
Deserializer::InitializeHeader(type, kTypeCid, Type::InstanceSize(),
mark_canonical);
d.ReadFromTo(type);
COMPILE_ASSERT(
std::is_unsigned<decltype(UntaggedType::type_class_id_)>::value);
type->untag()->type_class_id_ = d.ReadUnsigned();
const uint8_t combined = d.Read<uint8_t>();
type->untag()->type_state_ = combined >> kNullabilityBitSize;
type->untag()->nullability_ = combined & kNullabilityBitMask;
type->untag()->flags_ = d.ReadUnsigned();
}
}
@ -4257,19 +4235,8 @@ class FunctionTypeSerializationCluster
void WriteFunctionType(Serializer* s, FunctionTypePtr type) {
AutoTraceObject(type);
WriteFromTo(type);
ASSERT(type->untag()->type_state_ <
(1 << UntaggedFunctionType::kTypeStateBitSize));
ASSERT(type->untag()->nullability_ < (1 << kNullabilityBitSize));
static_assert(
UntaggedFunctionType::kTypeStateBitSize + kNullabilityBitSize <=
kBitsPerByte * sizeof(uint8_t),
"Cannot pack type_state_ and nullability_ into a uint8_t");
const uint8_t combined =
(type->untag()->type_state_ << kNullabilityBitSize) |
type->untag()->nullability_;
ASSERT_EQUAL(type->untag()->type_state_, combined >> kNullabilityBitSize);
ASSERT_EQUAL(type->untag()->nullability_, combined & kNullabilityBitMask);
s->Write<uint8_t>(combined);
ASSERT(Utils::IsUint(8, type->untag()->flags_));
s->Write<uint8_t>(type->untag()->flags_);
s->Write<uint32_t>(type->untag()->packed_parameter_counts_);
s->Write<uint16_t>(type->untag()->packed_type_parameter_counts_);
}
@ -4300,9 +4267,7 @@ class FunctionTypeDeserializationCluster
Deserializer::InitializeHeader(
type, kFunctionTypeCid, FunctionType::InstanceSize(), mark_canonical);
d.ReadFromTo(type);
const uint8_t combined = d.Read<uint8_t>();
type->untag()->type_state_ = combined >> kNullabilityBitSize;
type->untag()->nullability_ = combined & kNullabilityBitMask;
type->untag()->flags_ = d.Read<uint8_t>();
type->untag()->packed_parameter_counts_ = d.Read<uint32_t>();
type->untag()->packed_type_parameter_counts_ = d.Read<uint16_t>();
}
@ -4386,18 +4351,8 @@ class RecordTypeSerializationCluster
void WriteRecordType(Serializer* s, RecordTypePtr type) {
AutoTraceObject(type);
WriteFromTo(type);
ASSERT(type->untag()->type_state_ <
(1 << UntaggedRecordType::kTypeStateBitSize));
ASSERT(type->untag()->nullability_ < (1 << kNullabilityBitSize));
static_assert(UntaggedRecordType::kTypeStateBitSize + kNullabilityBitSize <=
kBitsPerByte * sizeof(uint8_t),
"Cannot pack type_state_ and nullability_ into a uint8_t");
const uint8_t combined =
(type->untag()->type_state_ << kNullabilityBitSize) |
type->untag()->nullability_;
ASSERT_EQUAL(type->untag()->type_state_, combined >> kNullabilityBitSize);
ASSERT_EQUAL(type->untag()->nullability_, combined & kNullabilityBitMask);
s->Write<uint8_t>(combined);
ASSERT(Utils::IsUint(8, type->untag()->flags_));
s->Write<uint8_t>(type->untag()->flags_);
}
};
#endif // !DART_PRECOMPILED_RUNTIME
@ -4425,9 +4380,7 @@ class RecordTypeDeserializationCluster
Deserializer::InitializeHeader(
type, kRecordTypeCid, RecordType::InstanceSize(), mark_canonical);
d.ReadFromTo(type);
const uint8_t combined = d.Read<uint8_t>();
type->untag()->type_state_ = combined >> kNullabilityBitSize;
type->untag()->nullability_ = combined & kNullabilityBitMask;
type->untag()->flags_ = d.Read<uint8_t>();
}
}
@ -4534,16 +4487,20 @@ class TypeRefDeserializationCluster : public DeserializationCluster {
}
TypeRef& type_ref = TypeRef::Handle(d->zone());
AbstractType& type = AbstractType::Handle(d->zone());
Code& stub = Code::Handle(d->zone());
const bool includes_code = Snapshot::IncludesCode(d->kind());
if (Snapshot::IncludesCode(d->kind())) {
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
type_ref ^= refs.At(id);
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
type_ref ^= refs.At(id);
// Refresh finalization state and nullability.
type = type_ref.type();
type_ref.set_type(type);
if (includes_code) {
type_ref.UpdateTypeTestingStubEntryPoint();
}
} else {
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
type_ref ^= refs.At(id);
} else {
stub = TypeTestingStubGenerator::DefaultCodeForType(type_ref);
type_ref.InitializeTypeTestingStubNonAtomic(stub);
}
@ -4599,16 +4556,8 @@ class TypeParameterSerializationCluster
s->Write<int32_t>(type->untag()->parameterized_class_id_);
s->Write<uint8_t>(type->untag()->base_);
s->Write<uint8_t>(type->untag()->index_);
ASSERT(type->untag()->flags_ < (1 << UntaggedTypeParameter::kFlagsBitSize));
ASSERT(type->untag()->nullability_ < (1 << kNullabilityBitSize));
static_assert(UntaggedTypeParameter::kFlagsBitSize + kNullabilityBitSize <=
kBitsPerByte * sizeof(uint8_t),
"Cannot pack flags_ and nullability_ into a uint8_t");
const uint8_t combined = (type->untag()->flags_ << kNullabilityBitSize) |
type->untag()->nullability_;
ASSERT_EQUAL(type->untag()->flags_, combined >> kNullabilityBitSize);
ASSERT_EQUAL(type->untag()->nullability_, combined & kNullabilityBitMask);
s->Write<uint8_t>(combined);
ASSERT(Utils::IsUint(8, type->untag()->flags_));
s->Write<uint8_t>(type->untag()->flags_);
}
};
#endif // !DART_PRECOMPILED_RUNTIME
@ -4641,9 +4590,7 @@ class TypeParameterDeserializationCluster
type->untag()->parameterized_class_id_ = d.Read<int32_t>();
type->untag()->base_ = d.Read<uint8_t>();
type->untag()->index_ = d.Read<uint8_t>();
const uint8_t combined = d.Read<uint8_t>();
type->untag()->flags_ = combined >> kNullabilityBitSize;
type->untag()->nullability_ = combined & kNullabilityBitMask;
type->untag()->flags_ = d.Read<uint8_t>();
}
}

View file

@ -659,6 +659,11 @@ void ClassFinalizer::FillAndFinalizeTypeArguments(
AbstractType& unfinalized_type = AbstractType::Handle(zone);
if (super_type_arg.IsTypeRef()) {
unfinalized_type = TypeRef::Cast(super_type_arg).type();
if (unfinalized_type.IsFinalized()) {
super_type_arg.SetIsFinalized();
arguments.SetTypeAt(i, super_type_arg);
continue;
}
} else {
ASSERT(super_type_arg.IsType());
unfinalized_type = super_type_arg.ptr();
@ -755,10 +760,13 @@ AbstractTypePtr ClassFinalizer::FinalizeType(const AbstractType& type,
// is_being_finalized mark bit.
return type.ptr();
}
type.SetIsBeingFinalized();
AbstractType& ref_type =
AbstractType::Handle(zone, TypeRef::Cast(type).type());
ref_type = FinalizeType(ref_type, finalization, pending_types);
ASSERT(ref_type.IsFinalized());
TypeRef::Cast(type).set_type(ref_type);
ASSERT(type.IsFinalized());
return type.ptr();
}
@ -1563,7 +1571,7 @@ class CidRewriteVisitor : public ObjectVisitor {
Map(param->untag()->parameterized_class_id_);
} else if (obj->IsType()) {
TypePtr type = Type::RawCast(obj);
type->untag()->type_class_id_ = Map(type->untag()->type_class_id_);
type->untag()->set_type_class_id(Map(type->untag()->type_class_id()));
} else {
intptr_t old_cid = obj->GetClassId();
intptr_t new_cid = Map(old_cid);
@ -1619,7 +1627,7 @@ void ClassFinalizer::RemapClassIds(intptr_t* old_to_new_cid) {
// In the Dart VM heap the following instances directly use cids for the
// computation of canonical hash codes:
//
// * TypePtr (due to UntaggedType::type_class_id_)
// * TypePtr (due to UntaggedType::type_class_id)
// * TypeParameterPtr (due to UntaggedTypeParameter::parameterized_class_id_)
//
// The following instances use cids for the computation of canonical hash codes

View file

@ -1296,8 +1296,8 @@ void AsmIntrinsifier::Type_equality(Assembler* assembler,
// Check nullability.
__ Bind(&equiv_cids);
__ ldrb(R1, FieldAddress(R1, target::Type::nullability_offset()));
__ ldrb(R2, FieldAddress(R2, target::Type::nullability_offset()));
__ LoadAbstractTypeNullability(R1, R1);
__ LoadAbstractTypeNullability(R2, R2);
__ cmp(R1, Operand(R2));
__ b(&check_legacy, NE);
// Fall through to equal case if nullability is strictly equal.

View file

@ -1463,10 +1463,8 @@ void AsmIntrinsifier::Type_equality(Assembler* assembler,
// Check nullability.
__ Bind(&equiv_cids);
__ ldr(R1, FieldAddress(R1, target::Type::nullability_offset()),
kUnsignedByte);
__ ldr(R2, FieldAddress(R2, target::Type::nullability_offset()),
kUnsignedByte);
__ LoadAbstractTypeNullability(R1, R1);
__ LoadAbstractTypeNullability(R2, R2);
__ cmp(R1, Operand(R2));
__ b(&check_legacy, NE);
// Fall through to equal case if nullability is strictly equal.

View file

@ -1432,8 +1432,8 @@ void AsmIntrinsifier::Type_equality(Assembler* assembler,
// Check nullability.
__ Bind(&equiv_cids);
__ movzxb(EDI, FieldAddress(EDI, target::Type::nullability_offset()));
__ movzxb(EBX, FieldAddress(EBX, target::Type::nullability_offset()));
__ LoadAbstractTypeNullability(EDI, EDI);
__ LoadAbstractTypeNullability(EBX, EBX);
__ cmpl(EDI, EBX);
__ j(NOT_EQUAL, &check_legacy, Assembler::kNearJump);
// Fall through to equal case if nullability is strictly equal.

View file

@ -1487,8 +1487,8 @@ void AsmIntrinsifier::Type_equality(Assembler* assembler,
// Check nullability.
__ Bind(&equiv_cids);
__ lbu(A0, FieldAddress(A0, target::Type::nullability_offset()));
__ lbu(A1, FieldAddress(A1, target::Type::nullability_offset()));
__ LoadAbstractTypeNullability(A0, A0);
__ LoadAbstractTypeNullability(A1, A1);
__ bne(A0, A1, &check_legacy);
// Fall through to equal case if nullability is strictly equal.

View file

@ -1341,8 +1341,8 @@ void AsmIntrinsifier::Type_equality(Assembler* assembler,
// Check nullability.
__ Bind(&equiv_cids);
__ movzxb(RCX, FieldAddress(RCX, target::Type::nullability_offset()));
__ movzxb(RDX, FieldAddress(RDX, target::Type::nullability_offset()));
__ LoadAbstractTypeNullability(RCX, RCX);
__ LoadAbstractTypeNullability(RDX, RDX);
__ cmpq(RCX, RDX);
__ j(NOT_EQUAL, &check_legacy, Assembler::kNearJump);
// Fall through to equal case if nullability is strictly equal.

View file

@ -441,17 +441,17 @@ class Assembler : public AssemblerBase {
cmp(value, Operand(TMP));
}
void CompareFunctionTypeNullabilityWith(Register type,
int8_t value) override {
EnsureHasClassIdInDEBUG(kFunctionTypeCid, type, TMP);
ldrb(TMP, FieldAddress(
type, compiler::target::FunctionType::nullability_offset()));
cmp(TMP, Operand(value));
void LoadAbstractTypeNullability(Register dst, Register type) override {
ldrb(dst,
FieldAddress(type, compiler::target::AbstractType::flags_offset()));
and_(dst, dst,
Operand(compiler::target::UntaggedAbstractType::kNullabilityMask));
}
void CompareTypeNullabilityWith(Register type, int8_t value) override {
EnsureHasClassIdInDEBUG(kTypeCid, type, TMP);
ldrb(TMP, FieldAddress(type, compiler::target::Type::nullability_offset()));
cmp(TMP, Operand(value));
void CompareAbstractTypeNullabilityWith(Register type,
/*Nullability*/ int8_t value,
Register scratch) override {
LoadAbstractTypeNullability(scratch, type);
cmp(scratch, Operand(value));
}
// Misc. functionality
@ -869,6 +869,13 @@ class Assembler : public AssemblerBase {
void LslImmediate(Register rd, int32_t shift) {
LslImmediate(rd, rd, shift);
}
void LsrImmediate(Register rd, Register rn, int32_t shift) {
ASSERT((shift >= 0) && (shift < kBitsPerInt32));
Lsr(rd, rn, Operand(shift));
}
void LsrImmediate(Register rd, int32_t shift) override {
LsrImmediate(rd, rd, shift);
}
// Test rn and immediate. May clobber IP.
void TestImmediate(Register rn, int32_t imm, Condition cond = AL);

View file

@ -648,20 +648,17 @@ class Assembler : public AssemblerBase {
cmp(value, Operand(TMP), sz);
}
void CompareFunctionTypeNullabilityWith(Register type,
int8_t value) override {
EnsureHasClassIdInDEBUG(kFunctionTypeCid, type, TMP);
ldr(TMP,
FieldAddress(type,
compiler::target::FunctionType::nullability_offset()),
void LoadAbstractTypeNullability(Register dst, Register type) override {
ldr(dst, FieldAddress(type, compiler::target::AbstractType::flags_offset()),
kUnsignedByte);
cmp(TMP, Operand(value));
AndImmediate(dst, dst,
compiler::target::UntaggedAbstractType::kNullabilityMask);
}
void CompareTypeNullabilityWith(Register type, int8_t value) override {
EnsureHasClassIdInDEBUG(kTypeCid, type, TMP);
ldr(TMP, FieldAddress(type, compiler::target::Type::nullability_offset()),
kUnsignedByte);
cmp(TMP, Operand(value));
void CompareAbstractTypeNullabilityWith(Register type,
/*Nullability*/ int8_t value,
Register scratch) override {
LoadAbstractTypeNullability(scratch, type);
cmp(scratch, Operand(value));
}
bool use_far_branches() const {
@ -1707,6 +1704,9 @@ class Assembler : public AssemblerBase {
ASSERT((shift >= 0) && (shift < reg_size));
ubfm(rd, rn, shift, reg_size - 1, sz);
}
void LsrImmediate(Register rd, int32_t shift) override {
LsrImmediate(rd, rd, shift);
}
void AsrImmediate(Register rd,
Register rn,
int shift,

View file

@ -782,30 +782,24 @@ class AssemblerBase : public StackResource {
Register address,
int32_t offset = 0) = 0;
// Retrieves nullability from a FunctionTypePtr in [type] and compares it
// to [value].
//
// TODO(dartbug.com/47034): Change how nullability is stored so that it
// can be accessed without checking the class id first.
virtual void CompareFunctionTypeNullabilityWith(Register type,
int8_t value) = 0;
// Loads nullability from an AbstractType [type] to [dst].
virtual void LoadAbstractTypeNullability(Register dst, Register type) = 0;
// Loads nullability from an AbstractType [type] and compares it
// to [value]. Clobbers [scratch].
virtual void CompareAbstractTypeNullabilityWith(Register type,
/*Nullability*/ int8_t value,
Register scratch) = 0;
// Retrieves nullability from a TypePtr in [type] and compares it to [value].
//
// TODO(dartbug.com/47034): Change how nullability is stored so that it
// can be accessed without checking the class id first.
virtual void CompareTypeNullabilityWith(Register type, int8_t value) = 0;
virtual void LsrImmediate(Register dst, int32_t shift) = 0;
void LoadTypeClassId(Register dst, Register src) {
#if !defined(TARGET_ARCH_IA32)
EnsureHasClassIdInDEBUG(kTypeCid, src, TMP);
#endif
ASSERT(!compiler::target::UntaggedType::kTypeClassIdIsSigned);
ASSERT_EQUAL(compiler::target::UntaggedType::kTypeClassIdBitSize,
kBitsPerInt16);
LoadFieldFromOffset(dst, src,
compiler::target::Type::type_class_id_offset(),
kUnsignedTwoBytes);
compiler::target::AbstractType::flags_offset(),
kUnsignedFourBytes);
LsrImmediate(dst, compiler::target::UntaggedType::kTypeClassIdShift);
}
virtual void EnsureHasClassIdInDEBUG(intptr_t cid,

View file

@ -766,6 +766,9 @@ class Assembler : public AssemblerBase {
void LslImmediate(Register dst, int32_t shift) {
shll(dst, Immediate(shift));
}
void LsrImmediate(Register dst, int32_t shift) override {
shrl(dst, Immediate(shift));
}
void CompareImmediate(Register reg, int32_t immediate) {
cmpl(reg, Immediate(immediate));
@ -878,15 +881,17 @@ class Assembler : public AssemblerBase {
cmpxchgl(address, reg);
}
void CompareFunctionTypeNullabilityWith(Register type,
int8_t value) override {
cmpb(FieldAddress(type,
compiler::target::FunctionType::nullability_offset()),
Immediate(value));
void LoadAbstractTypeNullability(Register dst, Register type) override {
movzxb(dst,
FieldAddress(type, compiler::target::AbstractType::flags_offset()));
andl(dst,
Immediate(compiler::target::UntaggedAbstractType::kNullabilityMask));
}
void CompareTypeNullabilityWith(Register type, int8_t value) override {
cmpb(FieldAddress(type, compiler::target::Type::nullability_offset()),
Immediate(value));
void CompareAbstractTypeNullabilityWith(Register type,
/*Nullability*/ int8_t value,
Register scratch) override {
LoadAbstractTypeNullability(scratch, type);
cmpl(scratch, Immediate(value));
}
void EnterFrame(intptr_t frame_space);

View file

@ -2389,17 +2389,16 @@ void Assembler::CompareWithMemoryValue(Register value, Address address) {
CompareRegisters(value, TMP2);
}
void Assembler::CompareFunctionTypeNullabilityWith(Register type,
int8_t value) {
EnsureHasClassIdInDEBUG(kFunctionTypeCid, type, TMP);
lbu(TMP,
FieldAddress(type, compiler::target::FunctionType::nullability_offset()));
CompareImmediate(TMP, value);
void Assembler::LoadAbstractTypeNullability(Register dst, Register type) {
lbu(dst, FieldAddress(type, compiler::target::AbstractType::flags_offset()));
andi(dst, dst, compiler::target::UntaggedAbstractType::kNullabilityMask);
}
void Assembler::CompareTypeNullabilityWith(Register type, int8_t value) {
EnsureHasClassIdInDEBUG(kTypeCid, type, TMP);
lbu(TMP, FieldAddress(type, compiler::target::Type::nullability_offset()));
CompareImmediate(TMP, value);
void Assembler::CompareAbstractTypeNullabilityWith(Register type,
/*Nullability*/ int8_t value,
Register scratch) {
LoadAbstractTypeNullability(scratch, type);
CompareImmediate(scratch, value);
}
void Assembler::ReserveAlignedFrameSpace(intptr_t frame_space) {

View file

@ -846,8 +846,10 @@ class Assembler : public MicroAssembler {
void CompareWithMemoryValue(Register value, Address address);
void CompareFunctionTypeNullabilityWith(Register type, int8_t value) override;
void CompareTypeNullabilityWith(Register type, int8_t value) override;
void LoadAbstractTypeNullability(Register dst, Register type) override;
void CompareAbstractTypeNullabilityWith(Register type,
/*Nullability*/ int8_t value,
Register scratch) override;
// Debugging and bringup support.
void Breakpoint() override { trap(); }
@ -986,6 +988,9 @@ class Assembler : public MicroAssembler {
void LslImmediate(Register rd, int32_t shift) {
slli(rd, rd, shift);
}
void LsrImmediate(Register rd, int32_t shift) override {
srli(rd, rd, shift);
}
void TestImmediate(Register rn, intx_t imm, OperandSize sz = kWordBytes);
void CompareImmediate(Register rn, intx_t imm, OperandSize sz = kWordBytes);

View file

@ -594,6 +594,9 @@ class Assembler : public AssemblerBase {
void LslImmediate(Register dst, int32_t shift) {
shlq(dst, Immediate(shift));
}
void LsrImmediate(Register dst, int32_t shift) override {
shrq(dst, Immediate(shift));
}
void shldq(Register dst, Register src, Register shifter) {
ASSERT(shifter == RCX);
@ -1219,17 +1222,17 @@ class Assembler : public AssemblerBase {
OBJ(cmp)(value, FieldAddress(base, offset));
}
void CompareFunctionTypeNullabilityWith(Register type,
int8_t value) override {
EnsureHasClassIdInDEBUG(kFunctionTypeCid, type, TMP);
cmpb(FieldAddress(type,
compiler::target::FunctionType::nullability_offset()),
Immediate(value));
void LoadAbstractTypeNullability(Register dst, Register type) override {
movzxb(dst,
FieldAddress(type, compiler::target::AbstractType::flags_offset()));
andl(dst,
Immediate(compiler::target::UntaggedAbstractType::kNullabilityMask));
}
void CompareTypeNullabilityWith(Register type, int8_t value) override {
EnsureHasClassIdInDEBUG(kTypeCid, type, TMP);
cmpb(FieldAddress(type, compiler::target::Type::nullability_offset()),
Immediate(value));
void CompareAbstractTypeNullabilityWith(Register type,
/*Nullability*/ int8_t value,
Register scratch) override {
LoadAbstractTypeNullability(scratch, type);
cmpl(scratch, Immediate(value));
}
void RestoreCodePointer();

View file

@ -183,8 +183,7 @@ NONNULLABLE_BOXED_NATIVE_SLOTS_LIST(FOR_EACH_NATIVE_SLOT)
V(FunctionType, UntaggedFunctionType, packed_type_parameter_counts, Uint16, \
FINAL) \
V(PointerBase, UntaggedPointerBase, data, IntPtr, VAR) \
V(Record, UntaggedRecord, num_fields, Int32, FINAL) \
V(TypeParameter, UntaggedTypeParameter, flags, Uint8, FINAL)
V(Record, UntaggedRecord, num_fields, Int32, FINAL)
// For uses that do not need the exact_type (boxed) or representation (unboxed)
// or whether a boxed native slot is nullable. (Generally, such users only need

View file

@ -391,12 +391,15 @@ const word UntaggedObject::kTagBitsSizeTagPos =
const word UntaggedAbstractType::kTypeStateFinalizedInstantiated =
dart::UntaggedAbstractType::kFinalizedInstantiated;
const word UntaggedAbstractType::kTypeStateShift =
dart::UntaggedAbstractType::kTypeStateShift;
const word UntaggedAbstractType::kTypeStateBits =
dart::UntaggedAbstractType::kTypeStateBits;
const word UntaggedAbstractType::kNullabilityMask =
dart::UntaggedAbstractType::kNullabilityMask;
const bool UntaggedType::kTypeClassIdIsSigned =
std::is_signed<decltype(dart::UntaggedType::type_class_id_)>::value;
const word UntaggedType::kTypeClassIdBitSize =
sizeof(dart::UntaggedType::type_class_id_) * kBitsPerByte;
const word UntaggedType::kTypeClassIdShift =
dart::UntaggedType::kTypeClassIdShift;
const word UntaggedObject::kBarrierOverlapShift =
dart::UntaggedObject::kBarrierOverlapShift;

View file

@ -432,12 +432,14 @@ class UntaggedObject : public AllStatic {
class UntaggedAbstractType : public AllStatic {
public:
static const word kTypeStateFinalizedInstantiated;
static const word kTypeStateShift;
static const word kTypeStateBits;
static const word kNullabilityMask;
};
class UntaggedType : public AllStatic {
public:
static const bool kTypeClassIdIsSigned;
static const word kTypeClassIdBitSize;
static const word kTypeClassIdShift;
};
class Object : public AllStatic {
@ -695,6 +697,7 @@ class Pointer : public AllStatic {
class AbstractType : public AllStatic {
public:
static word flags_offset();
static word type_test_stub_entry_point_offset();
static word InstanceSize();
FINAL_CLASS();
@ -703,10 +706,7 @@ class AbstractType : public AllStatic {
class Type : public AllStatic {
public:
static word hash_offset();
static word type_state_offset();
static word arguments_offset();
static word type_class_id_offset();
static word nullability_offset();
static word InstanceSize();
FINAL_CLASS();
};
@ -714,13 +714,11 @@ class Type : public AllStatic {
class FunctionType : public AllStatic {
public:
static word hash_offset();
static word type_state_offset();
static word packed_parameter_counts_offset();
static word packed_type_parameter_counts_offset();
static word named_parameter_names_offset();
static word parameter_types_offset();
static word type_parameters_offset();
static word nullability_offset();
static word InstanceSize();
FINAL_CLASS();
};
@ -966,12 +964,10 @@ class Bool : public AllStatic {
class TypeParameter : public AllStatic {
public:
static word bound_offset();
static word flags_offset();
static word InstanceSize();
FINAL_CLASS();
static word parameterized_class_id_offset();
static word index_offset();
static word nullability_offset();
};
class LibraryPrefix : public AllStatic {

File diff suppressed because it is too large Load diff

View file

@ -88,6 +88,7 @@
CONSTANT(SubtypeTestCache, kTestEntryLength) \
CONSTANT(SubtypeTestCache, kTestResult) \
CONSTANT(TypeArguments, kMaxElements) \
FIELD(AbstractType, flags_offset) \
FIELD(AbstractType, type_test_stub_entry_point_offset) \
FIELD(ArgumentsDescriptor, count_offset) \
FIELD(ArgumentsDescriptor, size_offset) \
@ -336,9 +337,6 @@
FIELD(TwoByteString, data_offset) \
FIELD(Type, arguments_offset) \
FIELD(Type, hash_offset) \
FIELD(Type, type_class_id_offset) \
FIELD(Type, type_state_offset) \
FIELD(Type, nullability_offset) \
FIELD(Finalizer, type_arguments_offset) \
FIELD(Finalizer, callback_offset) \
FIELD(FinalizerBase, all_entries_offset) \
@ -354,14 +352,12 @@
FIELD(NativeFinalizer, callback_offset) \
FIELD(FunctionType, hash_offset) \
FIELD(FunctionType, named_parameter_names_offset) \
FIELD(FunctionType, nullability_offset) \
FIELD(FunctionType, packed_parameter_counts_offset) \
FIELD(FunctionType, packed_type_parameter_counts_offset) \
FIELD(FunctionType, parameter_types_offset) \
FIELD(FunctionType, type_parameters_offset) \
FIELD(TypeParameter, parameterized_class_id_offset) \
FIELD(TypeParameter, index_offset) \
FIELD(TypeParameter, nullability_offset) \
FIELD(TypeArguments, instantiations_offset) \
FIELD(TypeArguments, length_offset) \
FIELD(TypeArguments, nullability_offset) \
@ -371,7 +367,6 @@
FIELD(TypeParameters, bounds_offset) \
FIELD(TypeParameters, defaults_offset) \
FIELD(TypeParameter, bound_offset) \
FIELD(TypeParameter, flags_offset) \
FIELD(TypeRef, type_offset) \
FIELD(TypedDataBase, length_offset) \
FIELD(TypedDataView, typed_data_offset) \

View file

@ -312,50 +312,27 @@ static void BuildInstantiateTypeParameterStub(Assembler* assembler,
__ LoadClassId(InstantiateTypeABI::kScratchReg,
InstantiateTypeABI::kResultTypeReg);
// The loaded value from the TAV can be [Type], [FunctionType] or [TypeRef].
// Handle/unwrap TypeRefs in runtime.
__ CompareImmediate(InstantiateTypeABI::kScratchReg, kTypeRefCid);
__ BranchIf(EQUAL, &runtime_call);
// Handle [Type]s.
__ CompareImmediate(InstantiateTypeABI::kScratchReg, kTypeCid);
__ BranchIf(NOT_EQUAL, &type_parameter_value_is_not_type);
switch (nullability) {
case Nullability::kNonNullable:
__ Ret();
break;
case Nullability::kNullable:
__ CompareTypeNullabilityWith(
__ CompareAbstractTypeNullabilityWith(
InstantiateTypeABI::kResultTypeReg,
static_cast<int8_t>(Nullability::kNullable));
static_cast<int8_t>(Nullability::kNullable),
InstantiateTypeABI::kScratchReg);
__ BranchIf(NOT_EQUAL, &runtime_call);
__ Ret();
break;
case Nullability::kLegacy:
__ CompareTypeNullabilityWith(
__ CompareAbstractTypeNullabilityWith(
InstantiateTypeABI::kResultTypeReg,
static_cast<int8_t>(Nullability::kNonNullable));
__ BranchIf(EQUAL, &runtime_call);
__ Ret();
}
// TODO(dartbug.com/49719)
// Handle [FunctionType]s.
__ Bind(&type_parameter_value_is_not_type);
__ CompareImmediate(InstantiateTypeABI::kScratchReg, kFunctionTypeCid);
__ BranchIf(NOT_EQUAL, &runtime_call);
switch (nullability) {
case Nullability::kNonNullable:
__ Ret();
break;
case Nullability::kNullable:
__ CompareFunctionTypeNullabilityWith(
InstantiateTypeABI::kResultTypeReg,
static_cast<int8_t>(Nullability::kNullable));
__ BranchIf(NOT_EQUAL, &runtime_call);
__ Ret();
break;
case Nullability::kLegacy:
__ CompareFunctionTypeNullabilityWith(
InstantiateTypeABI::kResultTypeReg,
static_cast<int8_t>(Nullability::kNonNullable));
static_cast<int8_t>(Nullability::kNonNullable),
InstantiateTypeABI::kScratchReg);
__ BranchIf(EQUAL, &runtime_call);
__ Ret();
}
@ -520,8 +497,9 @@ static void GenerateTypeIsTopTypeForSubtyping(Assembler* assembler,
__ BranchIf(NOT_EQUAL, &done, compiler::Assembler::kNearJump);
if (null_safety) {
// Instance type isn't a top type if non-nullable in null safe mode.
__ CompareTypeNullabilityWith(
scratch1_reg, static_cast<int8_t>(Nullability::kNonNullable));
__ CompareAbstractTypeNullabilityWith(
scratch1_reg, static_cast<int8_t>(Nullability::kNonNullable),
scratch2_reg);
__ BranchIf(EQUAL, &done, compiler::Assembler::kNearJump);
}
__ Bind(&is_top_type);
@ -619,8 +597,9 @@ static void GenerateNullIsAssignableToType(Assembler* assembler,
compiler::Label is_not_type;
__ CompareClassId(kCurrentTypeReg, kTypeCid, kScratchReg);
__ BranchIf(NOT_EQUAL, &is_not_type, compiler::Assembler::kNearJump);
__ CompareTypeNullabilityWith(
kCurrentTypeReg, static_cast<int8_t>(Nullability::kNonNullable));
__ CompareAbstractTypeNullabilityWith(
kCurrentTypeReg, static_cast<int8_t>(Nullability::kNonNullable),
kScratchReg);
__ BranchIf(NOT_EQUAL, &is_assignable);
// FutureOr is a special case because it may have the non-nullable bit set,
// but FutureOr<T> functions as the union of T and Future<T>, so it must be
@ -644,11 +623,9 @@ static void GenerateNullIsAssignableToType(Assembler* assembler,
__ Bind(&is_not_type);
// Null is assignable to a type parameter only if it is nullable or if the
// instantiation is nullable.
__ LoadFieldFromOffset(
kScratchReg, kCurrentTypeReg,
compiler::target::TypeParameter::nullability_offset(), kByte);
__ CompareImmediate(kScratchReg,
static_cast<int8_t>(Nullability::kNonNullable));
__ CompareAbstractTypeNullabilityWith(
kCurrentTypeReg, static_cast<int8_t>(Nullability::kNonNullable),
kScratchReg);
__ BranchIf(NOT_EQUAL, &is_assignable);
// Don't set kScratchReg in here as on IA32, that's the function TAV reg.
@ -878,10 +855,15 @@ void StubCodeCompiler::GenerateSlowTypeTestStub(Assembler* assembler) {
// Check whether this [Type] is instantiated/uninstantiated.
__ LoadFieldFromOffset(TypeTestABI::kScratchReg, TypeTestABI::kDstTypeReg,
target::Type::type_state_offset(), kByte);
target::AbstractType::flags_offset(), kByte);
__ AndImmediate(
TypeTestABI::kScratchReg,
Utils::NBitMask<int32_t>(target::UntaggedAbstractType::kTypeStateBits)
<< target::UntaggedAbstractType::kTypeStateShift);
__ CompareImmediate(
TypeTestABI::kScratchReg,
target::UntaggedAbstractType::kTypeStateFinalizedInstantiated);
target::UntaggedAbstractType::kTypeStateFinalizedInstantiated
<< target::UntaggedAbstractType::kTypeStateShift);
__ BranchIf(NOT_EQUAL, &is_complex_case, Assembler::kNearJump);
// This [Type] could be a FutureOr. Subtype2TestCache does not support Smi.

View file

@ -10554,20 +10554,15 @@ FunctionTypePtr FunctionType::New(intptr_t num_parent_type_arguments,
result.set_packed_type_parameter_counts(0);
result.set_named_parameter_names(Object::empty_array());
result.SetNumParentTypeArguments(num_parent_type_arguments);
result.set_nullability(nullability);
result.SetHash(0);
result.StoreNonPointer(&result.untag()->type_state_,
UntaggedType::kAllocated);
result.set_flags(0);
result.set_nullability(nullability);
result.set_type_state(UntaggedAbstractType::kAllocated);
result.InitializeTypeTestingStubNonAtomic(
Code::Handle(Z, TypeTestingStubGenerator::DefaultCodeForType(result)));
return result.ptr();
}
void FunctionType::set_type_state(uint8_t state) const {
ASSERT(state <= UntaggedFunctionType::kFinalizedUninstantiated);
StoreNonPointer(&untag()->type_state_, state);
}
const char* FunctionType::ToUserVisibleCString() const {
Zone* zone = Thread::Current()->zone();
ZoneTextBuffer printer(zone);
@ -20133,12 +20128,6 @@ void AbstractType::set_arguments(const TypeArguments& value) const {
UNREACHABLE();
}
Nullability AbstractType::nullability() const {
// AbstractType is an abstract class.
UNREACHABLE();
return Nullability::kNullable;
}
bool AbstractType::IsStrictlyNonNullable() const {
// Null can be assigned to legacy and nullable types.
if (!IsNonNullable()) {
@ -20259,26 +20248,32 @@ bool AbstractType::IsInstantiated(Genericity genericity,
return false;
}
bool AbstractType::IsFinalized() const {
// AbstractType is an abstract class.
UNREACHABLE();
return false;
}
void AbstractType::SetIsFinalized() const {
// AbstractType is an abstract class.
UNREACHABLE();
}
bool AbstractType::IsBeingFinalized() const {
// AbstractType is an abstract class.
UNREACHABLE();
return false;
ASSERT(!IsFinalized());
set_type_state(IsInstantiated()
? UntaggedAbstractType::kFinalizedInstantiated
: UntaggedAbstractType::kFinalizedUninstantiated);
}
void AbstractType::SetIsBeingFinalized() const {
// AbstractType is an abstract class.
UNREACHABLE();
ASSERT(!IsFinalized() && !IsBeingFinalized());
set_type_state(UntaggedAbstractType::kBeingFinalized);
}
void AbstractType::set_flags(uint32_t value) const {
StoreNonPointer(&untag()->flags_, value);
}
void AbstractType::set_type_state(UntaggedAbstractType::TypeState value) const {
ASSERT(!IsCanonical());
set_flags(
UntaggedAbstractType::TypeStateBits::update(value, untag()->flags_));
}
void AbstractType::set_nullability(Nullability value) const {
ASSERT(!IsCanonical());
set_flags(UntaggedAbstractType::NullabilityBits::update(
static_cast<uint8_t>(value), untag()->flags_));
}
bool AbstractType::IsEquivalent(const Instance& other,
@ -21066,34 +21061,6 @@ TypePtr Type::NewNonParameterizedType(const Class& type_class) {
return type.ptr();
}
void Type::SetIsFinalized() const {
ASSERT(!IsFinalized());
if (IsInstantiated()) {
set_type_state(UntaggedType::kFinalizedInstantiated);
} else {
set_type_state(UntaggedType::kFinalizedUninstantiated);
}
}
void FunctionType::SetIsFinalized() const {
ASSERT(!IsFinalized());
if (IsInstantiated()) {
set_type_state(UntaggedFunctionType::kFinalizedInstantiated);
} else {
set_type_state(UntaggedFunctionType::kFinalizedUninstantiated);
}
}
void Type::SetIsBeingFinalized() const {
ASSERT(!IsFinalized() && !IsBeingFinalized());
set_type_state(UntaggedType::kBeingFinalized);
}
void FunctionType::SetIsBeingFinalized() const {
ASSERT(!IsFinalized() && !IsBeingFinalized());
set_type_state(UntaggedFunctionType::kBeingFinalized);
}
TypePtr Type::ToNullability(Nullability value, Heap::Space space) const {
if (nullability() == value) {
return ptr();
@ -21151,7 +21118,7 @@ FunctionTypePtr FunctionType::ToNullability(Nullability value,
}
classid_t Type::type_class_id() const {
return untag()->type_class_id_;
return untag()->type_class_id();
}
ClassPtr Type::type_class() const {
@ -21161,11 +21128,11 @@ ClassPtr Type::type_class() const {
bool Type::IsInstantiated(Genericity genericity,
intptr_t num_free_fun_type_params,
TrailPtr trail) const {
if (untag()->type_state_ == UntaggedType::kFinalizedInstantiated) {
if (type_state() == UntaggedType::kFinalizedInstantiated) {
return true;
}
if ((genericity == kAny) && (num_free_fun_type_params == kAllFree) &&
(untag()->type_state_ == UntaggedType::kFinalizedUninstantiated)) {
(type_state() == UntaggedType::kFinalizedUninstantiated)) {
return false;
}
if (arguments() == TypeArguments::null()) {
@ -21723,12 +21690,12 @@ TypePtr Type::New(const Class& clazz,
Heap::Space space) {
Zone* Z = Thread::Current()->zone();
const Type& result = Type::Handle(Z, Type::New(space));
result.set_type_class(clazz);
result.set_arguments(arguments);
result.SetHash(0);
result.StoreNonPointer(&result.untag()->type_state_,
UntaggedType::kAllocated);
result.set_flags(0);
result.set_nullability(nullability);
result.set_type_state(UntaggedAbstractType::kAllocated);
result.set_type_class(clazz);
result.InitializeTypeTestingStubNonAtomic(
Code::Handle(Z, TypeTestingStubGenerator::DefaultCodeForType(result)));
@ -21736,19 +21703,13 @@ TypePtr Type::New(const Class& clazz,
}
void Type::set_type_class_id(intptr_t id) const {
COMPILE_ASSERT(
std::is_unsigned<decltype(UntaggedType::type_class_id_)>::value);
ASSERT(Utils::IsUint(sizeof(untag()->type_class_id_) * kBitsPerByte, id));
COMPILE_ASSERT(std::is_unsigned<ClassIdTagType>::value);
ASSERT(Utils::IsUint(sizeof(ClassIdTagType) * kBitsPerByte, id));
// We should never need a Type object for a top-level class.
ASSERT(!ClassTable::IsTopLevelCid(id));
ASSERT(id != kIllegalCid);
ASSERT(!IsInternalOnlyClassId(id));
StoreNonPointer(&untag()->type_class_id_, id);
}
void Type::set_type_state(uint8_t state) const {
ASSERT(state <= UntaggedType::kFinalizedUninstantiated);
StoreNonPointer(&untag()->type_state_, state);
untag()->set_type_class_id(id);
}
const char* Type::ToCString() const {
@ -22064,6 +22025,12 @@ AbstractTypePtr TypeRef::InstantiateFrom(
void TypeRef::set_type(const AbstractType& value) const {
ASSERT(!value.IsTypeRef());
if (value.IsNull()) {
ASSERT(!IsFinalized());
} else {
set_type_state(value.type_state());
set_nullability(value.nullability());
}
untag()->set_type(value.ptr());
}
@ -22165,23 +22132,6 @@ const char* TypeRef::ToCString() const {
return printer.buffer();
}
void TypeParameter::SetIsFinalized() const {
ASSERT(!IsFinalized());
set_flags(UntaggedTypeParameter::FinalizedBit::update(
true, UntaggedTypeParameter::BeingFinalizedBit::update(false,
untag()->flags_)));
}
void TypeParameter::SetIsBeingFinalized() const {
ASSERT(!IsFinalized());
set_flags(
UntaggedTypeParameter::BeingFinalizedBit::update(true, untag()->flags_));
}
void TypeParameter::set_nullability(Nullability value) const {
StoreNonPointer(&untag()->nullability_, static_cast<uint8_t>(value));
}
TypeParameterPtr TypeParameter::ToNullability(Nullability value,
Heap::Space space) const {
if (nullability() == value) {
@ -22539,19 +22489,16 @@ TypeParameterPtr TypeParameter::New(const Class& parameterized_class,
result.set_base(base);
result.set_index(index);
result.set_bound(bound);
result.SetHash(0);
result.set_flags(0);
result.set_nullability(nullability);
result.SetHash(0);
result.set_type_state(UntaggedAbstractType::kAllocated);
result.InitializeTypeTestingStubNonAtomic(
Code::Handle(Z, TypeTestingStubGenerator::DefaultCodeForType(result)));
return result.ptr();
}
void TypeParameter::set_flags(uint8_t flags) const {
StoreNonPointer(&untag()->flags_, flags);
}
const char* TypeParameter::CanonicalNameCString(bool is_class_type_parameter,
intptr_t base,
intptr_t index) {
@ -27252,34 +27199,15 @@ RecordTypePtr RecordType::New(const Array& field_types,
const RecordType& result = RecordType::Handle(Z, RecordType::New(space));
result.set_field_types(field_types);
result.set_field_names(field_names);
result.set_nullability(nullability);
result.SetHash(0);
result.StoreNonPointer(&result.untag()->type_state_,
UntaggedType::kAllocated);
result.set_flags(0);
result.set_nullability(nullability);
result.set_type_state(UntaggedAbstractType::kAllocated);
result.InitializeTypeTestingStubNonAtomic(
Code::Handle(Z, TypeTestingStubGenerator::DefaultCodeForType(result)));
return result.ptr();
}
void RecordType::set_type_state(uint8_t state) const {
ASSERT(state <= UntaggedRecordType::kFinalizedUninstantiated);
StoreNonPointer(&untag()->type_state_, state);
}
void RecordType::SetIsFinalized() const {
ASSERT(!IsFinalized());
if (IsInstantiated()) {
set_type_state(UntaggedRecordType::kFinalizedInstantiated);
} else {
set_type_state(UntaggedRecordType::kFinalizedUninstantiated);
}
}
void RecordType::SetIsBeingFinalized() const {
ASSERT(!IsFinalized() && !IsBeingFinalized());
set_type_state(UntaggedRecordType::kBeingFinalized);
}
RecordTypePtr RecordType::ToNullability(Nullability value,
Heap::Space space) const {
if (nullability() == value) {

View file

@ -8165,12 +8165,25 @@ class TypeArguments : public Instance {
// Subclasses of AbstractType are Type and TypeParameter.
class AbstractType : public Instance {
public:
virtual bool IsFinalized() const;
virtual void SetIsFinalized() const;
virtual bool IsBeingFinalized() const;
virtual void SetIsBeingFinalized() const;
static intptr_t flags_offset() {
return OFFSET_OF(UntaggedAbstractType, flags_);
}
virtual Nullability nullability() const;
bool IsFinalized() const {
const auto state = type_state();
return (state == UntaggedAbstractType::kFinalizedInstantiated) ||
(state == UntaggedAbstractType::kFinalizedUninstantiated);
}
void SetIsFinalized() const;
bool IsBeingFinalized() const {
return type_state() == UntaggedAbstractType::kBeingFinalized;
}
void SetIsBeingFinalized() const;
Nullability nullability() const {
return static_cast<Nullability>(
UntaggedAbstractType::NullabilityBits::decode(untag()->flags_));
}
// Returns true if type has '?' nullability suffix, or it is a
// built-in type which is always nullable (Null, dynamic or void).
bool IsNullable() const { return nullability() == Nullability::kNullable; }
@ -8462,45 +8475,33 @@ class AbstractType : public Instance {
const AbstractType& other_type,
TypeEquality kind) const;
UntaggedAbstractType::TypeState type_state() const {
return static_cast<UntaggedAbstractType::TypeState>(
UntaggedAbstractType::TypeStateBits::decode(untag()->flags_));
}
void set_flags(uint32_t value) const;
void set_type_state(UntaggedAbstractType::TypeState value) const;
void set_nullability(Nullability value) const;
HEAP_OBJECT_IMPLEMENTATION(AbstractType, Instance);
friend class Class;
friend class Function;
friend class TypeArguments;
friend class TypeRef;
};
// A Type consists of a class, possibly parameterized with type
// arguments. Example: C<T1, T2>.
class Type : public AbstractType {
public:
static intptr_t type_class_id_offset() {
return OFFSET_OF(UntaggedType, type_class_id_);
}
static intptr_t arguments_offset() {
return OFFSET_OF(UntaggedType, arguments_);
}
static intptr_t type_state_offset() {
return OFFSET_OF(UntaggedType, type_state_);
}
static intptr_t hash_offset() { return OFFSET_OF(UntaggedType, hash_); }
static intptr_t nullability_offset() {
return OFFSET_OF(UntaggedType, nullability_);
}
virtual bool IsFinalized() const {
return (untag()->type_state_ == UntaggedType::kFinalizedInstantiated) ||
(untag()->type_state_ == UntaggedType::kFinalizedUninstantiated);
}
virtual void SetIsFinalized() const;
virtual bool IsBeingFinalized() const {
return untag()->type_state_ == UntaggedType::kBeingFinalized;
}
virtual void SetIsBeingFinalized() const;
virtual bool HasTypeClass() const {
ASSERT(type_class_id() != kIllegalCid);
return true;
}
virtual Nullability nullability() const {
return static_cast<Nullability>(untag()->nullability_);
}
TypePtr ToNullability(Nullability value, Heap::Space space) const;
virtual classid_t type_class_id() const;
virtual ClassPtr type_class() const;
@ -8616,11 +8617,6 @@ class Type : public AbstractType {
// in ClassIdTagType. This allows us to guard against that case, instead of
// silently truncating the cid.
void set_type_class_id(intptr_t id) const;
void set_type_state(uint8_t state) const;
void set_nullability(Nullability value) const {
ASSERT(!IsCanonical());
StoreNonPointer(&untag()->nullability_, static_cast<uint8_t>(value));
}
static TypePtr New(Heap::Space space = Heap::kOld);
@ -8648,28 +8644,10 @@ class FunctionType : public AbstractType {
using PackedNumOptionalParameters =
UntaggedFunctionType::PackedNumOptionalParameters;
static intptr_t type_state_offset() {
return OFFSET_OF(UntaggedFunctionType, type_state_);
}
static intptr_t hash_offset() {
return OFFSET_OF(UntaggedFunctionType, hash_);
}
static intptr_t nullability_offset() {
return OFFSET_OF(UntaggedFunctionType, nullability_);
}
virtual bool IsFinalized() const {
return (untag()->type_state_ == UntaggedType::kFinalizedInstantiated) ||
(untag()->type_state_ == UntaggedType::kFinalizedUninstantiated);
}
virtual void SetIsFinalized() const;
virtual bool IsBeingFinalized() const {
return untag()->type_state_ == UntaggedType::kBeingFinalized;
}
virtual void SetIsBeingFinalized() const;
virtual bool HasTypeClass() const { return false; }
virtual Nullability nullability() const {
return static_cast<Nullability>(untag()->nullability_);
}
FunctionTypePtr ToNullability(Nullability value, Heap::Space space) const;
virtual classid_t type_class_id() const { return kIllegalCid; }
virtual bool IsInstantiated(Genericity genericity = kAny,
@ -8919,12 +8897,6 @@ class FunctionType : public AbstractType {
private:
void SetHash(intptr_t value) const;
void set_type_state(uint8_t state) const;
void set_nullability(Nullability value) const {
ASSERT(!IsCanonical());
StoreNonPointer(&untag()->nullability_, static_cast<uint8_t>(value));
}
static FunctionTypePtr New(Heap::Space space);
FINAL_HEAP_OBJECT_IMPLEMENTATION(FunctionType, AbstractType);
@ -8938,26 +8910,8 @@ class FunctionType : public AbstractType {
// names of the named fields.
class RecordType : public AbstractType {
public:
static intptr_t type_state_offset() {
return OFFSET_OF(UntaggedRecordType, type_state_);
}
static intptr_t hash_offset() { return OFFSET_OF(UntaggedRecordType, hash_); }
static intptr_t nullability_offset() {
return OFFSET_OF(UntaggedRecordType, nullability_);
}
virtual bool IsFinalized() const {
return (untag()->type_state_ == UntaggedType::kFinalizedInstantiated) ||
(untag()->type_state_ == UntaggedType::kFinalizedUninstantiated);
}
virtual void SetIsFinalized() const;
virtual bool IsBeingFinalized() const {
return untag()->type_state_ == UntaggedType::kBeingFinalized;
}
virtual void SetIsBeingFinalized() const;
virtual bool HasTypeClass() const { return false; }
virtual Nullability nullability() const {
return static_cast<Nullability>(untag()->nullability_);
}
RecordTypePtr ToNullability(Nullability value, Heap::Space space) const;
virtual classid_t type_class_id() const { return kIllegalCid; }
virtual bool IsInstantiated(Genericity genericity = kAny,
@ -9021,11 +8975,6 @@ class RecordType : public AbstractType {
private:
void SetHash(intptr_t value) const;
void set_type_state(uint8_t state) const;
void set_nullability(Nullability value) const {
ASSERT(!IsCanonical());
StoreNonPointer(&untag()->nullability_, static_cast<uint8_t>(value));
}
void set_field_types(const Array& value) const;
void set_field_names(const Array& value) const;
@ -9046,19 +8995,6 @@ class TypeRef : public AbstractType {
public:
static intptr_t type_offset() { return OFFSET_OF(UntaggedTypeRef, type_); }
virtual bool IsFinalized() const {
const AbstractType& ref_type = AbstractType::Handle(type());
return !ref_type.IsNull() && ref_type.IsFinalized();
}
virtual bool IsBeingFinalized() const {
const AbstractType& ref_type = AbstractType::Handle(type());
return ref_type.IsNull() || ref_type.IsBeingFinalized();
}
virtual Nullability nullability() const {
const AbstractType& ref_type = AbstractType::Handle(type());
ASSERT(!ref_type.IsNull());
return ref_type.nullability();
}
virtual bool HasTypeClass() const {
return (type() != AbstractType::null()) &&
AbstractType::Handle(type()).HasTypeClass();
@ -9123,23 +9059,6 @@ class TypeRef : public AbstractType {
// to the ObjectType.
class TypeParameter : public AbstractType {
public:
virtual bool IsFinalized() const {
return UntaggedTypeParameter::FinalizedBit::decode(untag()->flags_);
}
virtual void SetIsFinalized() const;
virtual bool IsBeingFinalized() const {
return UntaggedTypeParameter::BeingFinalizedBit::decode(untag()->flags_);
}
virtual void SetIsBeingFinalized() const;
static intptr_t flags_offset() {
return OFFSET_OF(UntaggedTypeParameter, flags_);
}
static intptr_t nullability_offset() {
return OFFSET_OF(UntaggedTypeParameter, nullability_);
}
virtual Nullability nullability() const {
return static_cast<Nullability>(untag()->nullability_);
}
TypeParameterPtr ToNullability(Nullability value, Heap::Space space) const;
virtual bool HasTypeClass() const { return false; }
virtual classid_t type_class_id() const { return kIllegalCid; }
@ -9232,8 +9151,6 @@ class TypeParameter : public AbstractType {
void set_parameterized_class(const Class& value) const;
void set_name(const String& value) const;
void set_flags(uint8_t flags) const;
void set_nullability(Nullability value) const;
static TypeParameterPtr New();

View file

@ -2607,26 +2607,29 @@ class UntaggedTypeParameters : public UntaggedObject {
};
class UntaggedAbstractType : public UntaggedInstance {
protected:
// Accessed from generated code.
std::atomic<uword> type_test_stub_entry_point_;
// Accessed from generated code.
uint32_t flags_;
COMPRESSED_POINTER_FIELD(CodePtr, type_test_stub)
VISIT_FROM(type_test_stub)
public:
enum TypeState {
kAllocated, // Initial state.
kBeingFinalized, // In the process of being finalized.
kFinalizedInstantiated, // Instantiated type ready for use.
kFinalizedUninstantiated, // Uninstantiated type ready for use.
// Adjust kTypeStateBitSize if more are added.
};
protected:
static constexpr intptr_t kTypeStateBitSize = 2;
COMPILE_ASSERT(sizeof(std::atomic<word>) == sizeof(word));
using NullabilityBits = BitField<decltype(flags_), uint8_t, 0, 2>;
static constexpr intptr_t kNullabilityMask = NullabilityBits::mask();
// Accessed from generated code.
std::atomic<uword> type_test_stub_entry_point_;
#if defined(DART_COMPRESSED_POINTERS)
uint32_t padding_; // Makes Windows and Posix agree on layout.
#endif
COMPRESSED_POINTER_FIELD(CodePtr, type_test_stub)
VISIT_FROM(type_test_stub)
static constexpr intptr_t kTypeStateShift = NullabilityBits::kNextBit;
static constexpr intptr_t kTypeStateBits = 2;
using TypeStateBits =
BitField<decltype(flags_), uint8_t, kTypeStateShift, kTypeStateBits>;
private:
RAW_HEAP_OBJECT_IMPLEMENTATION(AbstractType);
@ -2636,18 +2639,29 @@ class UntaggedAbstractType : public UntaggedInstance {
};
class UntaggedType : public UntaggedAbstractType {
public:
static constexpr intptr_t kTypeClassIdShift = TypeStateBits::kNextBit;
using TypeClassIdBits = BitField<decltype(flags_),
ClassIdTagType,
kTypeClassIdShift,
sizeof(ClassIdTagType) * kBitsPerByte>;
private:
RAW_HEAP_OBJECT_IMPLEMENTATION(Type);
COMPRESSED_POINTER_FIELD(TypeArgumentsPtr, arguments)
COMPRESSED_POINTER_FIELD(SmiPtr, hash)
VISIT_TO(hash)
ClassIdTagType type_class_id_;
uint8_t type_state_;
uint8_t nullability_;
CompressedObjectPtr* to_snapshot(Snapshot::Kind kind) { return to(); }
ClassIdTagType type_class_id() const {
return TypeClassIdBits::decode(flags_);
}
void set_type_class_id(ClassIdTagType value) {
flags_ = TypeClassIdBits::update(value, flags_);
}
friend class compiler::target::UntaggedType;
friend class CidRewriteVisitor;
friend class UntaggedTypeArguments;
@ -2665,8 +2679,6 @@ class UntaggedFunctionType : public UntaggedAbstractType {
VISIT_TO(hash)
AtomicBitFieldContainer<uint32_t> packed_parameter_counts_;
AtomicBitFieldContainer<uint16_t> packed_type_parameter_counts_;
uint8_t type_state_;
uint8_t nullability_;
// The bit fields are public for use in kernel_to_il.cc.
public:
@ -2716,8 +2728,6 @@ class UntaggedRecordType : public UntaggedAbstractType {
COMPRESSED_POINTER_FIELD(ArrayPtr, field_names);
COMPRESSED_POINTER_FIELD(SmiPtr, hash)
VISIT_TO(hash)
uint8_t type_state_;
uint8_t nullability_;
CompressedObjectPtr* to_snapshot(Snapshot::Kind kind) { return to(); }
};
@ -2742,14 +2752,6 @@ class UntaggedTypeParameter : public UntaggedAbstractType {
ClassIdTagType parameterized_class_id_; // Or kFunctionCid for function tp.
uint8_t base_; // Number of enclosing function type parameters.
uint8_t index_; // Keep size in sync with BuildTypeParameterTypeTestStub.
uint8_t flags_;
uint8_t nullability_;
public:
using BeingFinalizedBit = BitField<decltype(flags_), bool, 0, 1>;
using FinalizedBit =
BitField<decltype(flags_), bool, BeingFinalizedBit::kNextBit, 1>;
static constexpr intptr_t kFlagsBitSize = FinalizedBit::kNextBit;
private:
CompressedObjectPtr* to_snapshot(Snapshot::Kind kind) { return to(); }

View file

@ -135,7 +135,6 @@ namespace dart {
F(TypeArguments, nullability_) \
F(AbstractType, type_test_stub_) \
F(Type, type_test_stub_) \
F(Type, type_class_id_) \
F(Type, arguments_) \
F(Type, hash_) \
F(FunctionType, type_test_stub_) \

View file

@ -1066,22 +1066,26 @@ void TypeTestingStubGenerator::BuildOptimizedTypeParameterArgumentValueCheck(
if (strict_null_safety) {
__ BranchIf(NOT_EQUAL, &check_subtype_type_class_ids);
// If non-nullable Object, then the subtype must be legacy or non-nullable.
__ CompareTypeNullabilityWith(
__ CompareAbstractTypeNullabilityWith(
TTSInternalRegs::kSuperTypeArgumentReg,
static_cast<int8_t>(Nullability::kNonNullable));
static_cast<int8_t>(Nullability::kNonNullable),
TTSInternalRegs::kScratchReg);
__ BranchIf(NOT_EQUAL, &is_subtype);
__ Comment("Checking for legacy or non-nullable instance type argument");
compiler::Label subtype_is_type;
UnwrapAbstractType(assembler, TTSInternalRegs::kSubTypeArgumentReg,
TTSInternalRegs::kScratchReg, &subtype_is_type);
__ CompareFunctionTypeNullabilityWith(
__ CompareAbstractTypeNullabilityWith(
TTSInternalRegs::kSubTypeArgumentReg,
static_cast<int8_t>(Nullability::kNullable));
static_cast<int8_t>(Nullability::kNullable),
TTSInternalRegs::kScratchReg);
__ BranchIf(EQUAL, check_failed);
__ Jump(&is_subtype);
__ Bind(&subtype_is_type);
__ CompareTypeNullabilityWith(TTSInternalRegs::kSubTypeArgumentReg,
static_cast<int8_t>(Nullability::kNullable));
__ CompareAbstractTypeNullabilityWith(
TTSInternalRegs::kSubTypeArgumentReg,
static_cast<int8_t>(Nullability::kNullable),
TTSInternalRegs::kScratchReg);
__ BranchIf(EQUAL, check_failed);
__ Jump(&is_subtype);
} else {
@ -1107,15 +1111,17 @@ void TypeTestingStubGenerator::BuildOptimizedTypeParameterArgumentValueCheck(
compiler::Label supertype_is_type;
UnwrapAbstractType(assembler, TTSInternalRegs::kSuperTypeArgumentReg,
TTSInternalRegs::kScratchReg, &supertype_is_type);
__ CompareFunctionTypeNullabilityWith(
__ CompareAbstractTypeNullabilityWith(
TTSInternalRegs::kSuperTypeArgumentReg,
static_cast<int8_t>(Nullability::kNonNullable));
static_cast<int8_t>(Nullability::kNonNullable),
TTSInternalRegs::kScratchReg);
__ BranchIf(EQUAL, check_failed);
__ Jump(&is_subtype, compiler::Assembler::kNearJump);
__ Bind(&supertype_is_type);
__ CompareTypeNullabilityWith(
__ CompareAbstractTypeNullabilityWith(
TTSInternalRegs::kSuperTypeArgumentReg,
static_cast<int8_t>(Nullability::kNonNullable));
static_cast<int8_t>(Nullability::kNonNullable),
TTSInternalRegs::kScratchReg);
__ BranchIf(EQUAL, check_failed);
}
@ -1165,9 +1171,10 @@ void TypeTestingStubGenerator::BuildOptimizedTypeArgumentValueCheck(
if (type.IsObjectType() || type.IsDartFunctionType()) {
if (strict_null_safety && type.IsNonNullable()) {
// Nullable types cannot be a subtype of a non-nullable type.
__ CompareFunctionTypeNullabilityWith(
__ CompareAbstractTypeNullabilityWith(
TTSInternalRegs::kSubTypeArgumentReg,
compiler::target::Nullability::kNullable);
static_cast<int8_t>(Nullability::kNullable),
TTSInternalRegs::kScratchReg);
__ BranchIf(EQUAL, check_failed);
}
// No further checks needed for non-nullable Object or Function.
@ -1183,8 +1190,10 @@ void TypeTestingStubGenerator::BuildOptimizedTypeArgumentValueCheck(
__ Bind(&sub_is_type);
if (strict_null_safety && type.IsNonNullable()) {
// Nullable types cannot be a subtype of a non-nullable type in strict mode.
__ CompareTypeNullabilityWith(TTSInternalRegs::kSubTypeArgumentReg,
compiler::target::Nullability::kNullable);
__ CompareAbstractTypeNullabilityWith(
TTSInternalRegs::kSubTypeArgumentReg,
static_cast<int8_t>(Nullability::kNullable),
TTSInternalRegs::kScratchReg);
__ BranchIf(EQUAL, check_failed);
// Fall through to bottom type checks.
}