[vm/compiler] Consolidate type information in Slot

Before this change, Slot had separate fields and bits for type info:
* cid_
* static_type_
* IsNullableBit
* IsSentinelVisibleBit
This change consolidates all these pieces into a CompileType.

Also, native Slot objects are now Zone-allocated like all other Slot objects and can contain Zone-allocated handles (such as static type).

TEST=ci

Change-Id: I133179a08b8a88176129f571f01f568ea61b78df
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/320240
Commit-Queue: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
Reviewed-by: Slava Egorov <vegorov@google.com>
This commit is contained in:
Alexander Markov
2023-08-15 14:54:58 +00:00
committed by Commit Queue
parent 1035fcc023
commit 48c7a9267e
11 changed files with 193 additions and 203 deletions
@@ -37,7 +37,7 @@ void AssemblerBase::LoadFromSlot(Register dst,
return LoadFromOffset(dst, address, sz);
}
if (slot.is_compressed()) {
if (slot.ComputeCompileType().ToCid() == kSmiCid) {
if (slot.type().ToCid() == kSmiCid) {
return LoadCompressedSmi(dst, address);
} else {
return LoadCompressedField(dst, address);
@@ -56,11 +56,11 @@ void AssemblerBase::StoreToSlot(Register src, Register base, const Slot& slot) {
if (slot.is_compressed()) {
return StoreCompressedIntoObject(
base, address, src,
slot.ComputeCompileType().CanBeSmi() ? kValueCanBeSmi : kValueIsNotSmi);
slot.type().CanBeSmi() ? kValueCanBeSmi : kValueIsNotSmi);
}
return StoreIntoObject(
base, address, src,
slot.ComputeCompileType().CanBeSmi() ? kValueCanBeSmi : kValueIsNotSmi);
slot.type().CanBeSmi() ? kValueCanBeSmi : kValueIsNotSmi);
}
void AssemblerBase::StoreToSlotNoBarrier(Register src,
@@ -11,6 +11,7 @@
#include "vm/allocation.h"
#include "vm/class_id.h"
#include "vm/compiler/backend/locations.h"
#include "vm/compiler/runtime_api.h"
namespace dart {
@@ -137,6 +138,11 @@ class CompileType : public ZoneAllocated {
// kSentinelCid.
static CompileType FromCid(intptr_t cid);
// Create a new CompileType representing an unboxed value
// with given unboxed representation.
// Resulting CompileType cannot be null and cannot be sentinel.
static CompileType FromUnboxedRepresentation(Representation rep);
// Create None CompileType. It is the bottom of the lattice and is used to
// represent type of the phi that was not yet inferred.
static CompileType None() {
+12 -11
View File
@@ -2331,7 +2331,7 @@ void Slot::Write(FlowGraphSerializer* s) const {
s->Write<int8_t>(flags_);
s->Write<intptr_t>(offset_in_bytes_);
s->Write<const String&>(*DataAs<const String>());
s->Write<const AbstractType&>(*static_type_);
type_.Write(s);
break;
case Kind::kDartField:
s->Write<const Field&>(field());
@@ -2344,18 +2344,17 @@ void Slot::Write(FlowGraphSerializer* s) const {
const Slot& Slot::Read(FlowGraphDeserializer* d) {
const Kind kind = static_cast<Kind>(d->Read<serializable_type_t<Kind>>());
int8_t flags = 0;
ClassIdTagType cid = kDynamicCid;
intptr_t offset = -1;
const void* data = nullptr;
const AbstractType* static_type = nullptr;
CompileType type = CompileType::None();
Representation representation = kTagged;
switch (kind) {
case Kind::kTypeArguments:
flags = d->Read<int8_t>();
offset = d->Read<intptr_t>();
cid = kTypeArgumentsCid;
data = ":type_arguments";
type = CompileType::FromCid(kTypeArgumentsCid);
break;
case Kind::kTypeArgumentsIndex:
flags =
@@ -2363,24 +2362,26 @@ const Slot& Slot::Read(FlowGraphDeserializer* d) {
IsCompressedBit::encode(TypeArguments::ContainsCompressedPointers());
offset = d->Read<intptr_t>();
data = ":argument";
type = CompileType(CompileType::kCannotBeNull,
CompileType::kCannotBeSentinel, kDynamicCid, nullptr);
break;
case Kind::kArrayElement:
flags = IsNullableBit::encode(true) |
IsCompressedBit::encode(Array::ContainsCompressedPointers());
flags = IsCompressedBit::encode(Array::ContainsCompressedPointers());
offset = d->Read<intptr_t>();
data = ":array_element";
type = CompileType::Dynamic();
break;
case Kind::kRecordField:
flags = IsNullableBit::encode(true) |
IsCompressedBit::encode(Record::ContainsCompressedPointers());
flags = IsCompressedBit::encode(Record::ContainsCompressedPointers());
offset = d->Read<intptr_t>();
data = ":record_field";
type = CompileType::Dynamic();
break;
case Kind::kCapturedVariable:
flags = d->Read<int8_t>();
offset = d->Read<intptr_t>();
data = &d->Read<const String&>();
static_type = &d->Read<const AbstractType&>();
type = CompileType(d);
break;
case Kind::kDartField: {
const Field& field = d->Read<const Field&>();
@@ -2390,8 +2391,8 @@ const Slot& Slot::Read(FlowGraphDeserializer* d) {
return Slot::GetNativeSlot(kind);
}
return GetCanonicalSlot(d->thread(), kind, flags, cid, offset, data,
static_type, representation);
return GetCanonicalSlot(d->thread(), kind, flags, offset, data, type,
representation);
}
template <>
+37 -14
View File
@@ -1320,8 +1320,15 @@ ISOLATE_UNIT_TEST_CASE(IL_Canonicalize_RepresentationChange) {
TestRepresentationChangeDuringCanonicalization(thread, false);
}
static void TestCanonicalizationOfTypedDataViewFieldLoads(Thread* thread,
const Slot& field) {
enum TypeDataField {
TypedDataBase_length,
TypedDataView_offset_in_bytes,
TypedDataView_typed_data,
};
static void TestCanonicalizationOfTypedDataViewFieldLoads(
Thread* thread,
TypeDataField field_kind) {
const auto& typed_data_lib = Library::Handle(Library::TypedDataLibrary());
const auto& view_cls = Class::Handle(
typed_data_lib.LookupClassAllowPrivate(Symbols::_Float32ArrayView()));
@@ -1336,6 +1343,19 @@ static void TestCanonicalizationOfTypedDataViewFieldLoads(Thread* thread,
CompilerState S(thread, /*is_aot=*/false, /*is_optimizing=*/true);
FlowGraphBuilderHelper H;
const Slot* field = nullptr;
switch (field_kind) {
case TypedDataBase_length:
field = &Slot::TypedDataBase_length();
break;
case TypedDataView_offset_in_bytes:
field = &Slot::TypedDataView_offset_in_bytes();
break;
case TypedDataView_typed_data:
field = &Slot::TypedDataView_typed_data();
break;
}
auto b1 = H.flow_graph()->graph_entry()->normal_entry();
const auto constant_4 = H.IntConstant(4);
@@ -1359,29 +1379,32 @@ static void TestCanonicalizationOfTypedDataViewFieldLoads(Thread* thread,
DeoptId::kNone, 1, ICData::RebindRule::kStatic));
// array_alias <- LoadField(view.length)
load = builder.AddDefinition(
new LoadFieldInstr(new Value(view), field, InstructionSource()));
new LoadFieldInstr(new Value(view), *field, InstructionSource()));
// Return(load)
ret = builder.AddReturn(new Value(load));
}
H.FinishGraph();
H.flow_graph()->Canonicalize();
if (field.IsIdentical(Slot::TypedDataBase_length())) {
EXPECT_PROPERTY(ret->value()->definition(), &it == constant_1);
} else if (field.IsIdentical(Slot::TypedDataView_offset_in_bytes())) {
EXPECT_PROPERTY(ret->value()->definition(), &it == constant_4);
} else if (field.IsIdentical(Slot::TypedDataView_typed_data())) {
EXPECT_PROPERTY(ret->value()->definition(), &it == array);
switch (field_kind) {
case TypedDataBase_length:
EXPECT_PROPERTY(ret->value()->definition(), &it == constant_1);
break;
case TypedDataView_offset_in_bytes:
EXPECT_PROPERTY(ret->value()->definition(), &it == constant_4);
break;
case TypedDataView_typed_data:
EXPECT_PROPERTY(ret->value()->definition(), &it == array);
break;
}
}
ISOLATE_UNIT_TEST_CASE(IL_Canonicalize_TypedDataViewFactory) {
TestCanonicalizationOfTypedDataViewFieldLoads(thread, TypedDataBase_length);
TestCanonicalizationOfTypedDataViewFieldLoads(thread,
Slot::TypedDataBase_length());
TestCanonicalizationOfTypedDataViewFieldLoads(
thread, Slot::TypedDataView_offset_in_bytes());
TestCanonicalizationOfTypedDataViewFieldLoads(
thread, Slot::TypedDataView_typed_data());
TypedDataView_offset_in_bytes);
TestCanonicalizationOfTypedDataViewFieldLoads(thread,
TypedDataView_typed_data);
}
// Check that canonicalize can devirtualize InstanceCall based on type
+98 -132
View File
@@ -13,6 +13,13 @@
namespace dart {
enum NativeSlotsEnumeration {
#define DECLARE_KIND(CN, __, FN, ___, ____) k##CN##_##FN,
NATIVE_SLOTS_LIST(DECLARE_KIND)
#undef DECLARE_KIND
kNativeSlotsCount
};
// Canonicalization cache for Slot objects.
//
// This cache is attached to the CompilerState to ensure that we preserve
@@ -38,96 +45,90 @@ class SlotCache : public ZoneAllocated {
return *result;
}
const Slot& GetNativeSlot(Slot::Kind kind) {
const intptr_t index = static_cast<intptr_t>(kind);
ASSERT((index >= 0) && (index < kNativeSlotsCount));
const Slot* slot = native_fields_[index];
if (slot == nullptr) {
native_fields_[index] = slot = CreateNativeSlot(kind);
}
return *slot;
}
private:
explicit SlotCache(Thread* thread)
: zone_(thread->zone()), fields_(thread->zone()) {}
Slot* CreateNativeSlot(Slot::Kind kind);
Zone* const zone_;
PointerSet<const Slot> fields_;
const Slot* native_fields_[kNativeSlotsCount] = {nullptr};
};
static classid_t GetUnboxedNativeSlotCid(Representation rep) {
// Currently we only support integer unboxed fields.
if (RepresentationUtils::IsUnboxedInteger(rep)) {
return Boxing::BoxCid(rep);
}
UNREACHABLE();
return kIllegalCid;
}
AcqRelAtomic<Slot*> Slot::native_fields_(nullptr);
enum NativeSlotsEnumeration {
#define DECLARE_KIND(CN, __, FN, ___, ____) k##CN##_##FN,
NATIVE_SLOTS_LIST(DECLARE_KIND)
#undef DECLARE_KIND
kNativeSlotsCount
};
const Slot& Slot::GetNativeSlot(Kind kind) {
if (native_fields_.load() == nullptr) {
Slot* new_value = new Slot[kNativeSlotsCount]{
#define NULLABLE_FIELD_FINAL(ClassName) \
(IsNullableBit::encode(true) | IsImmutableBit::encode(true) | \
IsCompressedBit::encode(ClassName::ContainsCompressedPointers()))
#define NULLABLE_FIELD_VAR(ClassName) \
(IsNullableBit::encode(true) | \
IsCompressedBit::encode(ClassName::ContainsCompressedPointers()))
Slot* SlotCache::CreateNativeSlot(Slot::Kind kind) {
switch (kind) {
#define FIELD_FINAL true
#define FIELD_VAR false
#define DEFINE_NULLABLE_BOXED_NATIVE_FIELD(ClassName, UnderlyingType, \
FieldName, cid, mutability) \
Slot(Kind::k##ClassName##_##FieldName, \
NULLABLE_FIELD_##mutability(ClassName), k##cid##Cid, \
compiler::target::ClassName::FieldName##_offset(), \
#ClassName "." #FieldName, nullptr, kTagged),
case Slot::Kind::k##ClassName##_##FieldName: \
return new (zone_) Slot( \
Slot::Kind::k##ClassName##_##FieldName, \
(Slot::IsImmutableBit::encode(FIELD_##mutability) | \
Slot::IsCompressedBit::encode( \
ClassName::ContainsCompressedPointers())), \
compiler::target::ClassName::FieldName##_offset(), \
#ClassName "." #FieldName, \
CompileType(CompileType::kCanBeNull, CompileType::kCannotBeSentinel, \
k##cid##Cid, nullptr), \
kTagged);
NULLABLE_BOXED_NATIVE_SLOTS_LIST(DEFINE_NULLABLE_BOXED_NATIVE_FIELD)
NULLABLE_BOXED_NATIVE_SLOTS_LIST(DEFINE_NULLABLE_BOXED_NATIVE_FIELD)
#undef DEFINE_NULLABLE_BOXED_NATIVE_FIELD
#undef NULLABLE_FIELD_FINAL
#undef NULLABLE_FIELD_VAR
#define NONNULLABLE_FIELD_FINAL(ClassName) \
(Slot::IsImmutableBit::encode(true) | \
IsCompressedBit::encode(ClassName::ContainsCompressedPointers()))
#define NONNULLABLE_FIELD_VAR(ClassName) \
(IsCompressedBit::encode(ClassName::ContainsCompressedPointers()))
#define DEFINE_NONNULLABLE_BOXED_NATIVE_FIELD(ClassName, UnderlyingType, \
FieldName, cid, mutability) \
Slot(Kind::k##ClassName##_##FieldName, \
NONNULLABLE_FIELD_##mutability(ClassName), k##cid##Cid, \
compiler::target::ClassName::FieldName##_offset(), \
#ClassName "." #FieldName, nullptr, kTagged),
case Slot::Kind::k##ClassName##_##FieldName: \
return new (zone_) Slot( \
Slot::Kind::k##ClassName##_##FieldName, \
(Slot::IsImmutableBit::encode(FIELD_##mutability) | \
Slot::IsCompressedBit::encode( \
ClassName::ContainsCompressedPointers())), \
compiler::target::ClassName::FieldName##_offset(), \
#ClassName "." #FieldName, \
CompileType(CompileType::kCannotBeNull, \
CompileType::kCannotBeSentinel, k##cid##Cid, nullptr), \
kTagged);
NONNULLABLE_BOXED_NATIVE_SLOTS_LIST(
DEFINE_NONNULLABLE_BOXED_NATIVE_FIELD)
NONNULLABLE_BOXED_NATIVE_SLOTS_LIST(DEFINE_NONNULLABLE_BOXED_NATIVE_FIELD)
#undef DEFINE_NONNULLABLE_BOXED_NATIVE_FIELD
#undef NONNULLABLE_FIELD_VAR
#undef NONNULLABLE_FIELD_FINAL
#define UNBOXED_FIELD_FINAL (Slot::IsImmutableBit::encode(true))
#define UNBOXED_FIELD_VAR (0)
#define DEFINE_UNBOXED_NATIVE_FIELD(ClassName, UnderlyingType, FieldName, \
representation, mutability) \
Slot(Kind::k##ClassName##_##FieldName, UNBOXED_FIELD_##mutability, \
GetUnboxedNativeSlotCid(kUnboxed##representation), \
compiler::target::ClassName::FieldName##_offset(), \
#ClassName "." #FieldName, nullptr, kUnboxed##representation),
case Slot::Kind::k##ClassName##_##FieldName: \
return new (zone_) \
Slot(Slot::Kind::k##ClassName##_##FieldName, \
Slot::IsImmutableBit::encode(FIELD_##mutability), \
compiler::target::ClassName::FieldName##_offset(), \
#ClassName "." #FieldName, \
CompileType::FromUnboxedRepresentation(kUnboxed##representation), \
kUnboxed##representation);
UNBOXED_NATIVE_SLOTS_LIST(DEFINE_UNBOXED_NATIVE_FIELD)
UNBOXED_NATIVE_SLOTS_LIST(DEFINE_UNBOXED_NATIVE_FIELD)
#undef DEFINE_UNBOXED_NATIVE_FIELD
#undef UNBOXED_FIELD_VAR
#undef UNBOXED_FIELD_FINAL
};
Slot* old_value = nullptr;
if (!native_fields_.compare_exchange_strong(old_value, new_value)) {
delete[] new_value;
}
#undef FIELD_VAR
#undef FIELD_FINAL
default:
UNREACHABLE();
}
}
ASSERT(static_cast<uint8_t>(kind) < kNativeSlotsCount);
return native_fields_.load()[static_cast<uint8_t>(kind)];
const Slot& Slot::GetNativeSlot(Kind kind) {
return SlotCache::Instance(Thread::Current()).GetNativeSlot(kind);
}
bool Slot::IsImmutableLengthSlot() const {
@@ -256,8 +257,8 @@ const Slot& Slot::GetTypeArgumentsSlotFor(Thread* thread, const Class& cls) {
IsImmutableBit::encode(true) |
IsCompressedBit::encode(
compiler::target::Class::HasCompressedPointers(cls)),
kTypeArgumentsCid, offset, ":type_arguments",
/*static_type=*/nullptr, kTagged);
offset, ":type_arguments", CompileType::FromCid(kTypeArgumentsCid),
kTagged);
}
const Slot& Slot::GetContextVariableSlotFor(Thread* thread,
@@ -266,12 +267,12 @@ const Slot& Slot::GetContextVariableSlotFor(Thread* thread,
return GetCanonicalSlot(
thread, Kind::kCapturedVariable,
IsImmutableBit::encode(variable.is_final() && !variable.is_late()) |
IsNullableBit::encode(true) |
IsCompressedBit::encode(Context::ContainsCompressedPointers()) |
IsSentinelVisibleBit::encode(variable.is_late()),
kDynamicCid,
IsCompressedBit::encode(Context::ContainsCompressedPointers()),
compiler::target::Context::variable_offset(variable.index().value()),
&variable.name(), &variable.type(), kTagged);
&variable.name(),
CompileType::FromAbstractType(variable.type(), CompileType::kCanBeNull,
variable.is_late()),
kTagged);
}
const Slot& Slot::GetTypeArgumentsIndexSlot(Thread* thread, intptr_t index) {
@@ -281,38 +282,36 @@ const Slot& Slot::GetTypeArgumentsIndexSlot(Thread* thread, intptr_t index) {
thread, Kind::kTypeArgumentsIndex,
IsImmutableBit::encode(true) |
IsCompressedBit::encode(TypeArguments::ContainsCompressedPointers()),
kDynamicCid, offset, ":argument", /*static_type=*/nullptr, kTagged);
offset, ":argument",
CompileType(CompileType::kCannotBeNull, CompileType::kCannotBeSentinel,
kDynamicCid, nullptr),
kTagged);
}
const Slot& Slot::GetArrayElementSlot(Thread* thread,
intptr_t offset_in_bytes) {
return GetCanonicalSlot(
thread, Kind::kArrayElement,
IsNullableBit::encode(true) |
IsCompressedBit::encode(Array::ContainsCompressedPointers()),
kDynamicCid, offset_in_bytes, ":array_element",
/*static_type=*/nullptr, kTagged);
IsCompressedBit::encode(Array::ContainsCompressedPointers()),
offset_in_bytes, ":array_element", CompileType::Dynamic(), kTagged);
}
const Slot& Slot::GetRecordFieldSlot(Thread* thread, intptr_t offset_in_bytes) {
return GetCanonicalSlot(
thread, Kind::kRecordField,
IsNullableBit::encode(true) |
IsCompressedBit::encode(Record::ContainsCompressedPointers()),
kDynamicCid, offset_in_bytes, ":record_field",
/*static_type=*/nullptr, kTagged);
IsCompressedBit::encode(Record::ContainsCompressedPointers()),
offset_in_bytes, ":record_field", CompileType::Dynamic(), kTagged);
}
const Slot& Slot::GetCanonicalSlot(Thread* thread,
Slot::Kind kind,
int8_t flags,
ClassIdTagType cid,
intptr_t offset_in_bytes,
const void* data,
const AbstractType* static_type,
CompileType type,
Representation representation,
const FieldGuardState& field_guard_state) {
const Slot& slot = Slot(kind, flags, cid, offset_in_bytes, data, static_type,
const Slot& slot = Slot(kind, flags, offset_in_bytes, data, type,
representation, field_guard_state);
return SlotCache::Instance(thread).Canonicalize(slot);
}
@@ -352,8 +351,8 @@ const Slot& Slot::Get(const Field& field,
}
}
AbstractType& type = AbstractType::ZoneHandle(zone, field.type());
if (type.IsStrictlyNonNullable()) {
AbstractType& field_type = AbstractType::ZoneHandle(zone, field.type());
if (field_type.IsStrictlyNonNullable()) {
is_nullable = false;
}
@@ -380,7 +379,7 @@ const Slot& Slot::Get(const Field& field,
if (needs_load_guard) {
// Should be kept in sync with LoadStaticFieldInstr::ComputeType.
type = Type::DynamicType();
field_type = Type::DynamicType();
nullable_cid = kDynamicCid;
is_nullable = true;
used_guarded_state = false;
@@ -409,20 +408,25 @@ const Slot& Slot::Get(const Field& field,
}
}
const bool is_sentinel_visible =
field.is_late() && field.is_final() && !field.has_initializer();
CompileType type = (rep != kTagged)
? CompileType::FromUnboxedRepresentation(rep)
: CompileType(is_nullable, is_sentinel_visible,
nullable_cid, &field_type);
Class& owner = Class::Handle(zone, field.Owner());
const Slot& slot = GetCanonicalSlot(
thread, Kind::kDartField,
IsImmutableBit::encode((field.is_final() && !field.is_late()) ||
field.is_const()) |
IsNullableBit::encode(is_nullable) |
IsGuardedBit::encode(used_guarded_state) |
IsCompressedBit::encode(
compiler::target::Class::HasCompressedPointers(owner)) |
IsSentinelVisibleBit::encode(field.is_late() && field.is_final() &&
!field.has_initializer()) |
IsUnboxedBit::encode(is_unboxed),
nullable_cid, compiler::target::Field::OffsetOf(field), &field, &type,
rep, field_guard_state);
compiler::target::Field::OffsetOf(field), &field, type, rep,
field_guard_state);
// If properties of this slot were based on the guarded state make sure
// to add the field to the list of guarded fields. Note that during background
@@ -451,46 +455,6 @@ const Slot& Slot::Get(const Field& field,
return slot;
}
CompileType Slot::ComputeCompileType() const {
// If we unboxed the slot, we may know a more precise type.
switch (representation()) {
#if defined(TARGET_ARCH_IS_32_BIT)
// Int32/Uint32 values are not guaranteed to fit in a Smi.
case kUnboxedInt32:
case kUnboxedUint32:
#endif
case kUnboxedInt64:
if (nullable_cid() == kDynamicCid) {
return CompileType::Int();
}
break;
#if defined(TARGET_ARCH_IS_64_BIT)
// Int32/Uint32 values are guaranteed to fit in a Smi.
case kUnboxedInt32:
case kUnboxedUint32:
#endif
case kUnboxedUint8:
return CompileType::Smi();
case kUnboxedDouble:
return CompileType::FromCid(kDoubleCid);
case kUnboxedInt32x4:
return CompileType::FromCid(kInt32x4Cid);
case kUnboxedFloat32x4:
return CompileType::FromCid(kFloat32x4Cid);
case kUnboxedFloat64x2:
return CompileType::FromCid(kFloat64x2Cid);
default:
break;
}
return CompileType(is_nullable(), is_sentinel_visible(), nullable_cid(),
static_type_);
}
const AbstractType& Slot::static_type() const {
return static_type_ != nullptr ? *static_type_ : Object::null_abstract_type();
}
const char* Slot::Name() const {
if (IsLocalVariable()) {
return DataAs<const String>()->ToCString();
@@ -513,11 +477,13 @@ bool Slot::Equals(const Slot& other) const {
case Kind::kRecordField:
return true;
case Kind::kCapturedVariable:
case Kind::kCapturedVariable: {
auto other_type = other.type();
return (flags_ == other.flags_) &&
(DataAs<const String>()->ptr() ==
other.DataAs<const String>()->ptr()) &&
static_type_->Equals(*(other.static_type_));
type().IsEqualTo(&other_type);
}
case Kind::kDartField:
return other.DataAs<const Field>()->Original() ==
+10 -37
View File
@@ -311,28 +311,14 @@ class Slot : public ZoneAllocated {
bool is_immutable() const { return IsImmutableBit::decode(flags_); }
intptr_t nullable_cid() const { return cid_; }
bool is_nullable() const { return IsNullableBit::decode(flags_); }
// Returns true if properties of this slot were based on the guarded state
// of the corresponding Dart field.
bool is_guarded_field() const { return IsGuardedBit::decode(flags_); }
bool is_compressed() const { return IsCompressedBit::decode(flags_); }
// Returns true if load from this slot can return sentinel value.
bool is_sentinel_visible() const {
return IsSentinelVisibleBit::decode(flags_);
}
// Static type of the slots if any.
//
// A value that is read from the slot is guaranteed to be assignable to its
// static type.
const AbstractType& static_type() const;
// More precise type information about values that can be read from this slot.
CompileType ComputeCompileType() const;
// Type information about values that can be read from this slot.
CompileType type() const { return type_; }
const Field& field() const {
ASSERT(IsDartField());
@@ -360,39 +346,32 @@ class Slot : public ZoneAllocated {
private:
Slot(Kind kind,
int8_t flags,
ClassIdTagType cid,
intptr_t offset_in_bytes,
const void* data,
const AbstractType* static_type,
CompileType type,
Representation representation,
const FieldGuardState& field_guard_state = FieldGuardState())
: kind_(kind),
flags_(flags),
cid_(cid),
offset_in_bytes_(offset_in_bytes),
representation_(representation),
field_guard_state_(field_guard_state),
data_(data),
static_type_(static_type) {}
type_(type) {}
Slot(const Slot& other)
: Slot(other.kind_,
other.flags_,
other.cid_,
other.offset_in_bytes_,
other.data_,
other.static_type_,
other.type_,
other.representation_,
other.field_guard_state_) {}
using IsImmutableBit = BitField<int8_t, bool, 0, 1>;
using IsNullableBit = BitField<int8_t, bool, IsImmutableBit::kNextBit, 1>;
using IsGuardedBit = BitField<int8_t, bool, IsNullableBit::kNextBit, 1>;
using IsGuardedBit = BitField<int8_t, bool, IsImmutableBit::kNextBit, 1>;
using IsCompressedBit = BitField<int8_t, bool, IsGuardedBit::kNextBit, 1>;
using IsSentinelVisibleBit =
BitField<int8_t, bool, IsCompressedBit::kNextBit, 1>;
using IsUnboxedBit =
BitField<int8_t, bool, IsSentinelVisibleBit::kNextBit, 1>;
using IsUnboxedBit = BitField<int8_t, bool, IsCompressedBit::kNextBit, 1>;
template <typename T>
const T* DataAs() const {
@@ -403,16 +382,12 @@ class Slot : public ZoneAllocated {
Thread* thread,
Kind kind,
int8_t flags,
ClassIdTagType cid,
intptr_t offset_in_bytes,
const void* data,
const AbstractType* static_type,
CompileType type,
Representation representation,
const FieldGuardState& field_guard_state = FieldGuardState());
// There is a fixed statically known number of native slots so we cache
// them statically.
static AcqRelAtomic<Slot*> native_fields_;
static const Slot& GetNativeSlot(Kind kind);
const FieldGuardState& field_guard_state() const {
@@ -420,9 +395,7 @@ class Slot : public ZoneAllocated {
}
const Kind kind_;
const int8_t flags_; // is_immutable, is_nullable
const ClassIdTagType cid_; // Concrete cid of a value or kDynamicCid.
const int8_t flags_;
const intptr_t offset_in_bytes_;
const Representation representation_;
@@ -434,7 +407,7 @@ class Slot : public ZoneAllocated {
// - Field object for Dart fields;
const void* data_;
const AbstractType* static_type_;
CompileType type_;
friend class SlotCache;
};
+2 -2
View File
@@ -78,8 +78,8 @@ TEST_CASE(SlotFromGuardedField) {
const Slot& slot2 = Slot::Get(field_clone_2, parsed_function);
EXPECT_EQ(&slot1, &slot2);
EXPECT(slot1.is_guarded_field());
EXPECT(!slot1.is_nullable());
EXPECT_EQ(kSmiCid, slot1.nullable_cid());
EXPECT(!slot1.type().is_nullable());
EXPECT_EQ(kSmiCid, slot1.type().ToCid());
// Check that the field was added (once) to the list of guarded fields.
EXPECT_EQ(1, parsed_function->guarded_fields()->Length());
+15 -3
View File
@@ -690,6 +690,18 @@ CompileType CompileType::FromCid(intptr_t cid) {
return CompileType(cid == kNullCid, cid == kSentinelCid, cid, nullptr);
}
CompileType CompileType::FromUnboxedRepresentation(Representation rep) {
ASSERT(rep != kTagged);
ASSERT(rep != kUntagged);
if (RepresentationUtils::IsUnboxedInteger(rep)) {
if (!Boxing::RequiresAllocation(rep)) {
return CompileType::Smi();
}
return CompileType::Int();
}
return CompileType::FromCid(Boxing::BoxCid(rep));
}
CompileType CompileType::Dynamic() {
return CompileType(kCanBeNull, kCannotBeSentinel, kDynamicCid,
&Object::dynamic_type());
@@ -1698,7 +1710,7 @@ CompileType LoadFieldInstr::ComputeType() const {
}
}
}
CompileType type = slot().ComputeCompileType();
CompileType type = slot().type();
if (calls_initializer()) {
type = type.CopyNonSentinel();
}
@@ -1974,8 +1986,8 @@ static CompileType ComputeArrayElementType(Value* array) {
// type arguments which can be used to figure out element type.
if (auto* load_field = array->definition()->AsLoadField()) {
if (load_field->slot().IsDartField()) {
elem_type =
ExtractElementTypeFromArrayType(load_field->slot().static_type());
elem_type = load_field->slot().field().type();
elem_type = ExtractElementTypeFromArrayType(elem_type);
}
}
return CompileType::FromAbstractType(elem_type, CompileType::kCanBeNull,
+1 -1
View File
@@ -776,7 +776,7 @@ void CallSpecializer::InlineImplicitInstanceGetter(Definition* call,
}
ReplaceCall(call, load);
if (load->slot().nullable_cid() != kDynamicCid) {
if (load->slot().type().ToNullableCid() != kDynamicCid) {
// Reset value types if we know concrete cid.
for (Value::Iterator it(load->input_use_list()); !it.Done(); it.Advance()) {
it.Current()->SetReachingType(nullptr);
+6
View File
@@ -97,6 +97,8 @@ CodePtr StubCode::Generate(const char* name,
SafepointWriteRwLocker ml(thread, thread->isolate_group()->program_lock());
compiler::Assembler assembler(object_pool_builder);
CompilerState compiler_state(thread, /*is_aot=*/FLAG_precompiled_mode,
/*is_optimizing=*/false);
Zone* zone = thread->zone();
auto* pc_descriptors_list = new (zone) DescriptorList(zone);
compiler::StubCodeCompiler stubCodeCompiler(&assembler, pc_descriptors_list);
@@ -225,6 +227,8 @@ CodePtr StubCode::GetAllocationStubForClass(const Class& cls) {
}
compiler::Assembler assembler(wrapper);
CompilerState compiler_state(thread, /*is_aot=*/FLAG_precompiled_mode,
/*is_optimizing=*/false);
compiler::UnresolvedPcRelativeCalls unresolved_calls;
const char* name = cls.ToCString();
compiler::StubCodeCompiler stubCodeCompiler(&assembler, nullptr);
@@ -323,6 +327,8 @@ CodePtr StubCode::GetBuildMethodExtractorStub(compiler::ObjectPoolBuilder* pool,
compiler::ObjectPoolBuilder object_pool_builder;
compiler::Assembler assembler(pool != nullptr ? pool : &object_pool_builder);
CompilerState compiler_state(thread, /*is_aot=*/FLAG_precompiled_mode,
/*is_optimizing=*/false);
compiler::StubCodeCompiler stubCodeCompiler(&assembler, nullptr);
stubCodeCompiler.GenerateBuildMethodExtractorStub(
closure_allocation_stub, context_allocation_stub, generic);
+3
View File
@@ -260,6 +260,9 @@ CodePtr TypeTestingStubGenerator::BuildCodeForType(const AbstractType& type) {
slow_tts_stub = thread->isolate_group()->object_store()->slow_tts_stub();
}
CompilerState compiler_state(thread, /*is_aot=*/FLAG_precompiled_mode,
/*is_optimizing=*/false);
const Code& code = Code::Handle(
thread->zone(),
RetryCompilationWithFarBranches(