[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:
committed by
Commit Queue
parent
1035fcc023
commit
48c7a9267e
@@ -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() {
|
||||
|
||||
@@ -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 <>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() ==
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user