[vm/nnbd] Change type of sentinel values to Never

Special sentinel values which are used for lazy initialization can be
stored in variables of any type. In NNBD world Null is no longer a
bottom type as null cannot be assigned into variables of non-nullable
types. The new universal bottom type is Never, so type of sentinel
values is changed from Null to Never.

This also allows to make late fields non-nullable in the type
propagation and avoid extra updates to field guards.

This change also adds special handling of comparisons with sentinel
values into constant propagation as it can constant fold comparisons
of values with different cids.

Fixes https://github.com/dart-lang/sdk/issues/40796

Change-Id: I8266cfba0cc434b78ffa9570c30d434d4380d4af
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/143864
Reviewed-by: Liam Appelbe <liama@google.com>
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
Alexander Markov
2020-04-17 18:32:28 +00:00
committed by commit-bot@chromium.org
parent 9ae942b083
commit 085e22dd72
7 changed files with 22 additions and 30 deletions
+11 -4
View File
@@ -894,6 +894,11 @@ void Precompiler::AddConstObject(const class Instance& instance) {
return;
}
if (instance.raw() == Object::sentinel().raw() ||
instance.raw() == Object::transition_sentinel().raw()) {
return;
}
const Class& cls = Class::Handle(Z, instance.clazz());
AddInstantiatedClass(cls);
@@ -971,14 +976,16 @@ void Precompiler::AddField(const Field& field) {
if (field.is_static()) {
const Object& value = Object::Handle(Z, field.StaticValue());
if (value.IsInstance()) {
// Should not be in the middle of initialization while precompiling.
ASSERT(value.raw() != Object::transition_sentinel().raw());
if (value.raw() != Object::sentinel().raw() &&
value.raw() != Object::null()) {
ASSERT(value.IsInstance());
AddConstObject(Instance::Cast(value));
}
if (field.has_nontrivial_initializer()) {
// Should not be in the middle of initialization while precompiling.
ASSERT(value.raw() != Object::transition_sentinel().raw());
if (!field.HasInitializerFunction() ||
!Function::Handle(Z, field.InitializerFunction()).HasCode()) {
if (FLAG_trace_precompiler) {
@@ -594,7 +594,11 @@ void ConstantPropagator::VisitStrictCompare(StrictCompareInstr* instr) {
const intptr_t right_cid = instr->right()->Type()->ToCid();
// If exact classes (cids) are known and they differ, the result
// of strict compare can be computed.
// The only exception is comparison with special sentinel value
// (used for lazy initialization) which can be compared to a
// value of any type. Sentinel value has kNeverCid.
if ((left_cid != kDynamicCid) && (right_cid != kDynamicCid) &&
(left_cid != kNeverCid) && (right_cid != kNeverCid) &&
(left_cid != right_cid)) {
const bool result = (instr->kind() != Token::kEQ_STRICT);
SetValue(instr, Bool::Get(result));
-5
View File
@@ -225,11 +225,6 @@ const Slot& Slot::Get(const Field& field,
is_nullable = false;
}
if (field.is_late()) {
// TODO(dartbug.com/40796): Extend CompileType to handle lateness.
is_nullable = true;
}
const Slot& slot = SlotCache::Instance(thread).Canonicalize(Slot(
Kind::kDartField,
IsImmutableBit::encode((field.is_final() && !field.is_late()) ||
@@ -2052,14 +2052,7 @@ void BytecodeReaderHelper::ReadFieldDeclarations(const Class& cls,
field.set_is_extension_member(is_extension_member);
field.set_has_initializer(has_initializer);
if (has_nontrivial_initializer) {
if (field.is_late() && !is_static) {
// Late fields are initialized to Object::sentinel, which is a flavor of
// null. So we need to record that store so that the field guard doesn't
// prematurely optimise out the late field's sentinel checking logic.
field.RecordStore(Object::null_object());
}
} else {
if (!has_nontrivial_initializer) {
value ^= ReadObject();
if (is_static) {
if (field.is_late() && !has_initializer) {
-8
View File
@@ -2182,14 +2182,6 @@ void KernelLoader::GenerateFieldAccessors(const Class& klass,
}
ASSERT(field.NeedsGetter());
if (field.is_late() && !field.is_static() &&
field.has_nontrivial_initializer()) {
// Late fields are initialized to Object::sentinel, which is a flavor of
// null. So we need to record that store so that the field guard doesn't
// prematurely optimise out the late field's sentinel checking logic.
field.RecordStore(Object::null_object());
}
const String& getter_name = H.DartGetterName(field_helper->canonical_name_);
const Object& script_class =
ClassForScriptAt(klass, field_helper->source_uri_index_);
+5 -5
View File
@@ -780,21 +780,21 @@ void Object::Init(Isolate* isolate) {
cls.set_is_declaration_loaded();
cls.set_is_type_finalized();
// Allocate and initialize the sentinel values of Null class.
// Allocate and initialize the sentinel values.
{
*sentinel_ ^=
Object::Allocate(kNullCid, Instance::InstanceSize(), Heap::kOld);
Object::Allocate(kNeverCid, Instance::InstanceSize(), Heap::kOld);
*transition_sentinel_ ^=
Object::Allocate(kNullCid, Instance::InstanceSize(), Heap::kOld);
Object::Allocate(kNeverCid, Instance::InstanceSize(), Heap::kOld);
}
// Allocate and initialize optimizing compiler constants.
{
*unknown_constant_ ^=
Object::Allocate(kNullCid, Instance::InstanceSize(), Heap::kOld);
Object::Allocate(kNeverCid, Instance::InstanceSize(), Heap::kOld);
*non_constant_ ^=
Object::Allocate(kNullCid, Instance::InstanceSize(), Heap::kOld);
Object::Allocate(kNeverCid, Instance::InstanceSize(), Heap::kOld);
}
// Allocate the remaining VM internal classes.
+1
View File
@@ -358,6 +358,7 @@ intptr_t RawObject::VisitPointersPredefined(ObjectPointerVisitor* visitor,
break;
}
case kNullCid:
case kNeverCid:
size = HeapSize();
break;
default: