mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 02:07:06 +00:00
[vm/compiler] Recognize and propagate "value can be sentinel" property
This change adds can_be_sentinel() flag to CompileType and prevents unboxing of phis which can be sentinel. This flag means that set of values can potentially contain Object::sentinel() which is used as a marker for the uninitialized value of late variables. TEST=runtime/tests/vm/dart/regress_46141_test.dart Fixes https://github.com/dart-lang/sdk/issues/46141 Change-Id: I32f19488f54c6f69932584ecec3094e3b78cc0d0 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/201600 Reviewed-by: Slava Egorov <vegorov@google.com> Commit-Queue: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
parent
48a67a24f5
commit
69167e2bdb
43
runtime/tests/vm/dart/regress_46141_test.dart
Normal file
43
runtime/tests/vm/dart/regress_46141_test.dart
Normal file
|
@ -0,0 +1,43 @@
|
|||
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
// Regression test for https://github.com/dart-lang/sdk/issues/46141.
|
||||
// Verifies that unboxing of phi corresponding to a late variable doesn't
|
||||
// happen.
|
||||
|
||||
// VMOptions=--deterministic
|
||||
|
||||
import 'package:expect/expect.dart';
|
||||
|
||||
final values = List.filled(100, 0.0);
|
||||
|
||||
double? fn(double day) {
|
||||
double? last;
|
||||
late int lastDay;
|
||||
|
||||
int i = 0;
|
||||
while (i < values.length) {
|
||||
final t = values[i];
|
||||
if (day > 95) {
|
||||
print(lastDay);
|
||||
break;
|
||||
}
|
||||
last = t;
|
||||
lastDay = i;
|
||||
i++;
|
||||
}
|
||||
return last;
|
||||
}
|
||||
|
||||
void main() {
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
bool wasError = false;
|
||||
try {
|
||||
fn(i.toDouble());
|
||||
} on Error {
|
||||
wasError = true;
|
||||
}
|
||||
Expect.isTrue(wasError == (i > 95));
|
||||
}
|
||||
}
|
|
@ -217,6 +217,8 @@ enum ClassId : intptr_t {
|
|||
kNumPredefinedCids,
|
||||
};
|
||||
|
||||
constexpr ClassId kSentinelCid = kNeverCid;
|
||||
|
||||
// Keep these in sync with the cid numbering above.
|
||||
const int kTypedDataCidRemainderInternal = 0;
|
||||
const int kTypedDataCidRemainderView = 1;
|
||||
|
|
|
@ -27,6 +27,8 @@ class GrowableArray;
|
|||
// It captures the following properties:
|
||||
// - whether the value can potentially be null or if it is definitely not
|
||||
// null;
|
||||
// - whether the value can potentially be sentinel or if it is definitely
|
||||
// not sentinel;
|
||||
// - concrete class id of the value or kDynamicCid if unknown statically;
|
||||
// - abstract super type of the value, where the concrete type of the value
|
||||
// in runtime is guaranteed to be sub type of this type.
|
||||
|
@ -36,27 +38,42 @@ class GrowableArray;
|
|||
// operation for the lattice.
|
||||
class CompileType : public ZoneAllocated {
|
||||
public:
|
||||
static const bool kNullable = true;
|
||||
static const bool kNonNullable = false;
|
||||
static constexpr bool kCanBeNull = true;
|
||||
static constexpr bool kCannotBeNull = false;
|
||||
|
||||
CompileType(bool is_nullable, intptr_t cid, const AbstractType* type)
|
||||
: is_nullable_(is_nullable), cid_(cid), type_(type) {}
|
||||
static constexpr bool kCanBeSentinel = true;
|
||||
static constexpr bool kCannotBeSentinel = false;
|
||||
|
||||
CompileType(bool can_be_null,
|
||||
bool can_be_sentinel,
|
||||
intptr_t cid,
|
||||
const AbstractType* type)
|
||||
: can_be_null_(can_be_null),
|
||||
can_be_sentinel_(can_be_sentinel),
|
||||
cid_(cid),
|
||||
type_(type) {}
|
||||
|
||||
CompileType(const CompileType& other)
|
||||
: ZoneAllocated(),
|
||||
is_nullable_(other.is_nullable_),
|
||||
can_be_null_(other.can_be_null_),
|
||||
can_be_sentinel_(other.can_be_sentinel_),
|
||||
cid_(other.cid_),
|
||||
type_(other.type_) {}
|
||||
|
||||
CompileType& operator=(const CompileType& other) {
|
||||
// This intentionally does not change the owner of this type.
|
||||
is_nullable_ = other.is_nullable_;
|
||||
can_be_null_ = other.can_be_null_;
|
||||
can_be_sentinel_ = other.can_be_sentinel_;
|
||||
cid_ = other.cid_;
|
||||
type_ = other.type_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool is_nullable() const { return is_nullable_; }
|
||||
bool is_nullable() const { return can_be_null_; }
|
||||
|
||||
// Return true if value of this type can be Object::sentinel().
|
||||
// Such values cannot be unboxed.
|
||||
bool can_be_sentinel() const { return can_be_sentinel_; }
|
||||
|
||||
// Return type such that concrete value's type in runtime is guaranteed to
|
||||
// be subtype of it.
|
||||
|
@ -88,10 +105,6 @@ class CompileType : public ZoneAllocated {
|
|||
// against given type.
|
||||
bool IsInstanceOf(const AbstractType& other);
|
||||
|
||||
// Create a new CompileType representing given combination of class id and
|
||||
// abstract type. The pair is assumed to be coherent.
|
||||
static CompileType Create(intptr_t cid, const AbstractType& type);
|
||||
|
||||
// Return the non-nullable version of this type.
|
||||
CompileType CopyNonNullable() {
|
||||
if (IsNull()) {
|
||||
|
@ -100,28 +113,32 @@ class CompileType : public ZoneAllocated {
|
|||
return None();
|
||||
}
|
||||
|
||||
return CompileType(kNonNullable, cid_, type_);
|
||||
return CompileType(kCannotBeNull, can_be_sentinel_, cid_, type_);
|
||||
}
|
||||
|
||||
static CompileType CreateNullable(bool is_nullable, intptr_t cid) {
|
||||
return CompileType(is_nullable, cid, nullptr);
|
||||
// Return the non-sentinel version of this type.
|
||||
CompileType CopyNonSentinel() {
|
||||
return CompileType(can_be_null_, kCannotBeSentinel, cid_, type_);
|
||||
}
|
||||
|
||||
// Create a new CompileType representing given abstract type.
|
||||
// By default nullability of values is determined by type.
|
||||
// CompileType can be further constrained to non-nullable values by
|
||||
// passing kNonNullable as an optional parameter.
|
||||
// passing kCannotBeNull as |can_be_null| parameter.
|
||||
static CompileType FromAbstractType(const AbstractType& type,
|
||||
bool is_nullable = kNullable);
|
||||
bool can_be_null,
|
||||
bool can_be_sentinel);
|
||||
|
||||
// Create a new CompileType representing a value with the given class id.
|
||||
// Resulting CompileType is nullable only if cid is kDynamicCid or kNullCid.
|
||||
// Resulting CompileType can be null only if cid is kDynamicCid or kNullCid.
|
||||
// Resulting CompileType can be sentinel only if cid is kDynamicCid or
|
||||
// kSentinelCid.
|
||||
static CompileType FromCid(intptr_t cid);
|
||||
|
||||
// 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() {
|
||||
return CompileType(kNullable, kIllegalCid, nullptr);
|
||||
return CompileType(kCanBeNull, kCanBeSentinel, kIllegalCid, nullptr);
|
||||
}
|
||||
|
||||
// Create Dynamic CompileType. It is the top of the lattice and is used to
|
||||
|
@ -147,12 +164,12 @@ class CompileType : public ZoneAllocated {
|
|||
|
||||
// Create nullable Smi type.
|
||||
static CompileType NullableSmi() {
|
||||
return CreateNullable(kNullable, kSmiCid);
|
||||
return CompileType(kCanBeNull, kCannotBeSentinel, kSmiCid, nullptr);
|
||||
}
|
||||
|
||||
// Create nullable Mint type.
|
||||
static CompileType NullableMint() {
|
||||
return CreateNullable(kNullable, kMintCid);
|
||||
return CompileType(kCanBeNull, kCannotBeSentinel, kMintCid, nullptr);
|
||||
}
|
||||
|
||||
// Create non-nullable Double type.
|
||||
|
@ -175,7 +192,8 @@ class CompileType : public ZoneAllocated {
|
|||
|
||||
// Return true if this and other types are the same.
|
||||
bool IsEqualTo(CompileType* other) {
|
||||
return (is_nullable_ == other->is_nullable_) &&
|
||||
return (can_be_null_ == other->can_be_null_) &&
|
||||
(can_be_sentinel_ == other->can_be_sentinel_) &&
|
||||
(ToNullableCid() == other->ToNullableCid()) &&
|
||||
(compiler::IsEqualType(*ToAbstractType(), *other->ToAbstractType()));
|
||||
}
|
||||
|
@ -258,7 +276,8 @@ class CompileType : public ZoneAllocated {
|
|||
Definition* owner() const { return owner_; }
|
||||
|
||||
private:
|
||||
bool is_nullable_;
|
||||
bool can_be_null_;
|
||||
bool can_be_sentinel_;
|
||||
classid_t cid_;
|
||||
const AbstractType* type_;
|
||||
Definition* owner_ = nullptr;
|
||||
|
|
|
@ -1479,8 +1479,9 @@ void FlowGraph::RenameRecursive(
|
|||
// Check if phi corresponds to the same slot.
|
||||
auto* phis = phi->block()->phis();
|
||||
if ((index < phis->length()) && (*phis)[index] == phi) {
|
||||
phi->UpdateType(
|
||||
CompileType::FromAbstractType(load->local().type()));
|
||||
phi->UpdateType(CompileType::FromAbstractType(
|
||||
load->local().type(), CompileType::kCanBeNull,
|
||||
/*can_be_sentinel=*/load->local().is_late()));
|
||||
} else {
|
||||
ASSERT(IsCompiledForOsr() && (phi->block()->stack_depth() > 0));
|
||||
}
|
||||
|
@ -2006,7 +2007,8 @@ static void UnboxPhi(PhiInstr* phi, bool is_aot) {
|
|||
}
|
||||
}
|
||||
|
||||
if ((unboxed == kTagged) && phi->Type()->IsInt()) {
|
||||
if ((unboxed == kTagged) && phi->Type()->IsInt() &&
|
||||
!phi->Type()->can_be_sentinel()) {
|
||||
// Conservatively unbox phis that:
|
||||
// - are proven to be of type Int;
|
||||
// - fit into 64bits range;
|
||||
|
@ -2059,6 +2061,7 @@ static void UnboxPhi(PhiInstr* phi, bool is_aot) {
|
|||
// to how we treat doubles and other boxed numeric types).
|
||||
// In JIT mode only unbox phis which are not fully known to be Smi.
|
||||
if ((unboxed == kTagged) && phi->Type()->IsInt() &&
|
||||
!phi->Type()->can_be_sentinel() &&
|
||||
(is_aot || phi->Type()->ToCid() != kSmiCid)) {
|
||||
unboxed = kUnboxedInt64;
|
||||
}
|
||||
|
|
|
@ -81,4 +81,69 @@ ISOLATE_UNIT_TEST_CASE(FlowGraph_UnboxInt64Phi) {
|
|||
}
|
||||
#endif // defined(TARGET_ARCH_IS_64_BIT)
|
||||
|
||||
ISOLATE_UNIT_TEST_CASE(FlowGraph_LateVariablePhiUnboxing) {
|
||||
using compiler::BlockBuilder;
|
||||
|
||||
CompilerState S(thread, /*is_aot=*/true, /*is_optimizing=*/true);
|
||||
FlowGraphBuilderHelper H;
|
||||
|
||||
auto normal_entry = H.flow_graph()->graph_entry()->normal_entry();
|
||||
auto loop_header = H.JoinEntry();
|
||||
auto loop_body = H.TargetEntry();
|
||||
auto loop_exit = H.TargetEntry();
|
||||
|
||||
ConstantInstr* sentinel = H.flow_graph()->GetConstant(Object::sentinel());
|
||||
|
||||
PhiInstr* loop_var;
|
||||
PhiInstr* late_var;
|
||||
Definition* add1;
|
||||
|
||||
{
|
||||
BlockBuilder builder(H.flow_graph(), normal_entry);
|
||||
builder.AddInstruction(new GotoInstr(loop_header, S.GetNextDeoptId()));
|
||||
}
|
||||
|
||||
{
|
||||
BlockBuilder builder(H.flow_graph(), loop_header);
|
||||
loop_var = H.Phi(loop_header,
|
||||
{{normal_entry, H.IntConstant(0)}, {loop_body, &add1}});
|
||||
builder.AddPhi(loop_var);
|
||||
loop_var->UpdateType(CompileType::Int());
|
||||
loop_var->UpdateType(CompileType::FromAbstractType(
|
||||
Type::ZoneHandle(Type::IntType()), CompileType::kCannotBeNull,
|
||||
CompileType::kCanBeSentinel));
|
||||
late_var =
|
||||
H.Phi(loop_header, {{normal_entry, sentinel}, {loop_body, &add1}});
|
||||
builder.AddPhi(late_var);
|
||||
builder.AddBranch(new RelationalOpInstr(
|
||||
InstructionSource(), Token::kLT, new Value(loop_var),
|
||||
new Value(H.IntConstant(10)), kMintCid,
|
||||
S.GetNextDeoptId(), Instruction::kNotSpeculative),
|
||||
loop_body, loop_exit);
|
||||
}
|
||||
|
||||
{
|
||||
BlockBuilder builder(H.flow_graph(), loop_body);
|
||||
add1 = builder.AddDefinition(new BinaryInt64OpInstr(
|
||||
Token::kADD, new Value(loop_var), new Value(H.IntConstant(1)),
|
||||
S.GetNextDeoptId(), Instruction::kNotSpeculative));
|
||||
builder.AddInstruction(new GotoInstr(loop_header, S.GetNextDeoptId()));
|
||||
}
|
||||
|
||||
{
|
||||
BlockBuilder builder(H.flow_graph(), loop_exit);
|
||||
builder.AddReturn(new Value(late_var));
|
||||
}
|
||||
|
||||
H.FinishGraph();
|
||||
|
||||
FlowGraphTypePropagator::Propagate(H.flow_graph());
|
||||
H.flow_graph()->SelectRepresentations();
|
||||
|
||||
#if defined(TARGET_ARCH_IS_64_BIT)
|
||||
EXPECT_PROPERTY(loop_var, it.representation() == kUnboxedInt64);
|
||||
#endif
|
||||
EXPECT_PROPERTY(late_var, it.representation() == kTagged);
|
||||
}
|
||||
|
||||
} // namespace dart
|
||||
|
|
|
@ -299,7 +299,8 @@ const Slot& Slot::GetContextVariableSlotFor(Thread* thread,
|
|||
Slot(Kind::kCapturedVariable,
|
||||
IsImmutableBit::encode(variable.is_final() && !variable.is_late()) |
|
||||
IsNullableBit::encode(true) |
|
||||
IsCompressedBit::encode(Context::ContainsCompressedPointers()),
|
||||
IsCompressedBit::encode(Context::ContainsCompressedPointers()) |
|
||||
IsSentinelVisibleBit::encode(variable.is_late()),
|
||||
kDynamicCid,
|
||||
compiler::target::Context::variable_offset(variable.index().value()),
|
||||
&variable.name(), &variable.type(), kTagged));
|
||||
|
@ -382,16 +383,18 @@ const Slot& Slot::Get(const Field& field,
|
|||
}
|
||||
|
||||
Class& owner = Class::Handle(zone, field.Owner());
|
||||
const Slot& slot = SlotCache::Instance(thread).Canonicalize(
|
||||
Slot(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)),
|
||||
nullable_cid, compiler::target::Field::OffsetOf(field), &field,
|
||||
&type, rep));
|
||||
const Slot& slot = SlotCache::Instance(thread).Canonicalize(Slot(
|
||||
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()),
|
||||
nullable_cid, compiler::target::Field::OffsetOf(field), &field, &type,
|
||||
rep));
|
||||
|
||||
// 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
|
||||
|
@ -452,7 +455,7 @@ CompileType Slot::ComputeCompileType() const {
|
|||
break;
|
||||
}
|
||||
|
||||
return CompileType(is_nullable(), nullable_cid(),
|
||||
return CompileType(is_nullable(), is_sentinel_visible(), nullable_cid(),
|
||||
nullable_cid() == kDynamicCid ? static_type_ : nullptr);
|
||||
}
|
||||
|
||||
|
|
|
@ -261,6 +261,11 @@ class Slot : public ZoneAllocated {
|
|||
|
||||
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
|
||||
|
@ -316,6 +321,8 @@ class Slot : public ZoneAllocated {
|
|||
using IsNullableBit = BitField<int8_t, bool, IsImmutableBit::kNextBit, 1>;
|
||||
using IsGuardedBit = BitField<int8_t, bool, IsNullableBit::kNextBit, 1>;
|
||||
using IsCompressedBit = BitField<int8_t, bool, IsGuardedBit::kNextBit, 1>;
|
||||
using IsSentinelVisibleBit =
|
||||
BitField<int8_t, bool, IsCompressedBit::kNextBit, 1>;
|
||||
|
||||
template <typename T>
|
||||
const T* DataAs() const {
|
||||
|
|
|
@ -361,7 +361,9 @@ void FlowGraphTypePropagator::VisitGuardFieldClass(
|
|||
(current->is_nullable() && !guard->field().is_nullable())) {
|
||||
const bool is_nullable =
|
||||
guard->field().is_nullable() && current->is_nullable();
|
||||
SetTypeOf(def, new (zone()) CompileType(is_nullable, cid, NULL));
|
||||
SetTypeOf(def,
|
||||
new (zone()) CompileType(
|
||||
is_nullable, CompileType::kCannotBeSentinel, cid, nullptr));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -422,11 +424,12 @@ void FlowGraphTypePropagator::VisitBranch(BranchInstr* instr) {
|
|||
if (!type->IsTopTypeForInstanceOf()) {
|
||||
const bool is_nullable = (type->IsNullable() || type->IsTypeParameter() ||
|
||||
(type->IsNeverType() && type->IsLegacy()))
|
||||
? CompileType::kNullable
|
||||
: CompileType::kNonNullable;
|
||||
? CompileType::kCanBeNull
|
||||
: CompileType::kCannotBeNull;
|
||||
EnsureMoreAccurateRedefinition(
|
||||
true_successor, left,
|
||||
CompileType::FromAbstractType(*type, is_nullable));
|
||||
CompileType::FromAbstractType(*type, is_nullable,
|
||||
CompileType::kCannotBeSentinel));
|
||||
}
|
||||
} else if (comparison->InputAt(0)->BindsToConstant() &&
|
||||
comparison->InputAt(0)->BoundConstant().IsNull()) {
|
||||
|
@ -445,6 +448,25 @@ void FlowGraphTypePropagator::VisitBranch(BranchInstr* instr) {
|
|||
EnsureMoreAccurateRedefinition(
|
||||
true_successor, comparison->InputAt(0)->definition(),
|
||||
comparison->InputAt(0)->Type()->CopyNonNullable());
|
||||
} else if (comparison->InputAt(0)->BindsToConstant() &&
|
||||
comparison->InputAt(0)->BoundConstant().ptr() ==
|
||||
Object::sentinel().ptr()) {
|
||||
// Handle for expr != sentinel.
|
||||
BlockEntryInstr* true_successor =
|
||||
negated ? instr->true_successor() : instr->false_successor();
|
||||
EnsureMoreAccurateRedefinition(
|
||||
true_successor, comparison->InputAt(1)->definition(),
|
||||
comparison->InputAt(1)->Type()->CopyNonSentinel());
|
||||
|
||||
} else if (comparison->InputAt(1)->BindsToConstant() &&
|
||||
comparison->InputAt(1)->BoundConstant().ptr() ==
|
||||
Object::sentinel().ptr()) {
|
||||
// Handle for sentinel != expr.
|
||||
BlockEntryInstr* true_successor =
|
||||
negated ? instr->true_successor() : instr->false_successor();
|
||||
EnsureMoreAccurateRedefinition(
|
||||
true_successor, comparison->InputAt(0)->definition(),
|
||||
comparison->InputAt(0)->Type()->CopyNonSentinel());
|
||||
}
|
||||
// TODO(fschneider): Add propagation for generic is-tests.
|
||||
}
|
||||
|
@ -543,21 +565,23 @@ void CompileType::Union(CompileType* other) {
|
|||
return;
|
||||
}
|
||||
|
||||
is_nullable_ = is_nullable_ || other->is_nullable_;
|
||||
can_be_null_ = can_be_null_ || other->can_be_null_;
|
||||
can_be_sentinel_ = can_be_sentinel_ || other->can_be_sentinel_;
|
||||
|
||||
if (ToNullableCid() == kNullCid) {
|
||||
ToNullableCid(); // Ensure cid_ is set.
|
||||
if ((cid_ == kNullCid) || (cid_ == kSentinelCid)) {
|
||||
cid_ = other->cid_;
|
||||
type_ = other->type_;
|
||||
return;
|
||||
}
|
||||
|
||||
if (other->ToNullableCid() == kNullCid) {
|
||||
other->ToNullableCid(); // Ensure other->cid_ is set.
|
||||
if ((other->cid_ == kNullCid) || (other->cid_ == kSentinelCid)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const AbstractType* abstract_type = ToAbstractType();
|
||||
if (ToNullableCid() != other->ToNullableCid()) {
|
||||
ASSERT(cid_ != kNullCid);
|
||||
if (cid_ != other->cid_) {
|
||||
cid_ = kDynamicCid;
|
||||
}
|
||||
|
||||
|
@ -625,50 +649,53 @@ CompileType* CompileType::ComputeRefinedType(CompileType* old_type,
|
|||
preferred_type = new_type;
|
||||
}
|
||||
|
||||
// Refine non-nullability.
|
||||
bool is_nullable = old_type->is_nullable() && new_type->is_nullable();
|
||||
// Refine non-nullability and whether it can be sentinel.
|
||||
const bool can_be_null = old_type->is_nullable() && new_type->is_nullable();
|
||||
const bool can_be_sentinel =
|
||||
old_type->can_be_sentinel() && new_type->can_be_sentinel();
|
||||
|
||||
if (preferred_type->is_nullable() && !is_nullable) {
|
||||
return new CompileType(preferred_type->CopyNonNullable());
|
||||
if ((preferred_type->is_nullable() && !can_be_null) ||
|
||||
(preferred_type->can_be_sentinel() && !can_be_sentinel)) {
|
||||
return new CompileType(can_be_null, can_be_sentinel, preferred_type->cid_,
|
||||
preferred_type->type_);
|
||||
} else {
|
||||
ASSERT(preferred_type->is_nullable() == is_nullable);
|
||||
ASSERT(preferred_type->is_nullable() == can_be_null);
|
||||
ASSERT(preferred_type->can_be_sentinel() == can_be_sentinel);
|
||||
return preferred_type;
|
||||
}
|
||||
}
|
||||
|
||||
static bool IsNullableCid(intptr_t cid) {
|
||||
ASSERT(cid != kIllegalCid);
|
||||
return cid == kNullCid || cid == kDynamicCid;
|
||||
}
|
||||
|
||||
CompileType CompileType::Create(intptr_t cid, const AbstractType& type) {
|
||||
return CompileType(IsNullableCid(cid), cid, &type);
|
||||
}
|
||||
|
||||
CompileType CompileType::FromAbstractType(const AbstractType& type,
|
||||
bool is_nullable) {
|
||||
return CompileType(is_nullable && !type.IsStrictlyNonNullable(), kIllegalCid,
|
||||
&type);
|
||||
bool can_be_null,
|
||||
bool can_be_sentinel) {
|
||||
return CompileType(can_be_null && !type.IsStrictlyNonNullable(),
|
||||
can_be_sentinel, kIllegalCid, &type);
|
||||
}
|
||||
|
||||
CompileType CompileType::FromCid(intptr_t cid) {
|
||||
return CompileType(IsNullableCid(cid), cid, NULL);
|
||||
ASSERT(cid != kIllegalCid);
|
||||
ASSERT(cid != kDynamicCid);
|
||||
return CompileType(cid == kNullCid, cid == kSentinelCid, cid, nullptr);
|
||||
}
|
||||
|
||||
CompileType CompileType::Dynamic() {
|
||||
return Create(kDynamicCid, Object::dynamic_type());
|
||||
return CompileType(kCanBeNull, kCannotBeSentinel, kDynamicCid,
|
||||
&Object::dynamic_type());
|
||||
}
|
||||
|
||||
CompileType CompileType::Null() {
|
||||
return Create(kNullCid, Type::ZoneHandle(Type::NullType()));
|
||||
return CompileType(kCanBeNull, kCannotBeSentinel, kNullCid,
|
||||
&Type::ZoneHandle(Type::NullType()));
|
||||
}
|
||||
|
||||
CompileType CompileType::Bool() {
|
||||
return Create(kBoolCid, Type::ZoneHandle(Type::BoolType()));
|
||||
return CompileType(kCannotBeNull, kCannotBeSentinel, kBoolCid,
|
||||
&Type::ZoneHandle(Type::BoolType()));
|
||||
}
|
||||
|
||||
CompileType CompileType::Int() {
|
||||
return FromAbstractType(Type::ZoneHandle(Type::IntType()), kNonNullable);
|
||||
return FromAbstractType(Type::ZoneHandle(Type::IntType()), kCannotBeNull,
|
||||
kCannotBeSentinel);
|
||||
}
|
||||
|
||||
CompileType CompileType::Int32() {
|
||||
|
@ -680,23 +707,28 @@ CompileType CompileType::Int32() {
|
|||
}
|
||||
|
||||
CompileType CompileType::NullableInt() {
|
||||
return FromAbstractType(Type::ZoneHandle(Type::NullableIntType()), kNullable);
|
||||
return FromAbstractType(Type::ZoneHandle(Type::NullableIntType()), kCanBeNull,
|
||||
kCannotBeSentinel);
|
||||
}
|
||||
|
||||
CompileType CompileType::Smi() {
|
||||
return Create(kSmiCid, Type::ZoneHandle(Type::SmiType()));
|
||||
return CompileType(kCannotBeNull, kCannotBeSentinel, kSmiCid,
|
||||
&Type::ZoneHandle(Type::SmiType()));
|
||||
}
|
||||
|
||||
CompileType CompileType::Double() {
|
||||
return Create(kDoubleCid, Type::ZoneHandle(Type::Double()));
|
||||
return CompileType(kCannotBeNull, kCannotBeSentinel, kDoubleCid,
|
||||
&Type::ZoneHandle(Type::Double()));
|
||||
}
|
||||
|
||||
CompileType CompileType::NullableDouble() {
|
||||
return FromAbstractType(Type::ZoneHandle(Type::NullableDouble()), kNullable);
|
||||
return FromAbstractType(Type::ZoneHandle(Type::NullableDouble()), kCanBeNull,
|
||||
kCannotBeSentinel);
|
||||
}
|
||||
|
||||
CompileType CompileType::String() {
|
||||
return FromAbstractType(Type::ZoneHandle(Type::StringType()), kNonNullable);
|
||||
return FromAbstractType(Type::ZoneHandle(Type::StringType()), kCannotBeNull,
|
||||
kCannotBeSentinel);
|
||||
}
|
||||
|
||||
intptr_t CompileType::ToCid() {
|
||||
|
@ -706,13 +738,18 @@ intptr_t CompileType::ToCid() {
|
|||
if ((type_ != NULL) && type_->IsNullType()) {
|
||||
cid_ = kNullCid;
|
||||
}
|
||||
// Same for sentinel.
|
||||
if ((type_ != NULL) && type_->IsNeverType()) {
|
||||
cid_ = kNeverCid;
|
||||
}
|
||||
}
|
||||
|
||||
if ((cid_ == kNullCid) || (cid_ == kDynamicCid)) {
|
||||
return cid_;
|
||||
if ((cid_ == kDynamicCid) || (can_be_null_ && (cid_ != kNullCid)) ||
|
||||
(can_be_sentinel_ && (cid_ != kSentinelCid))) {
|
||||
return kDynamicCid;
|
||||
}
|
||||
|
||||
return is_nullable_ ? static_cast<intptr_t>(kDynamicCid) : ToNullableCid();
|
||||
return ToNullableCid();
|
||||
}
|
||||
|
||||
intptr_t CompileType::ToNullableCid() {
|
||||
|
@ -724,6 +761,8 @@ intptr_t CompileType::ToNullableCid() {
|
|||
cid_ = kDynamicCid;
|
||||
} else if (type_->IsNullType()) {
|
||||
cid_ = kNullCid;
|
||||
} else if (type_->IsNeverType()) {
|
||||
cid_ = kNeverCid;
|
||||
} else if (type_->IsFunctionType() || type_->IsDartFunctionType()) {
|
||||
cid_ = kClosureCid;
|
||||
} else if (type_->type_class_id() != kIllegalCid) {
|
||||
|
@ -758,11 +797,15 @@ intptr_t CompileType::ToNullableCid() {
|
|||
}
|
||||
}
|
||||
|
||||
if (can_be_sentinel_ && (cid_ != kSentinelCid)) {
|
||||
return kDynamicCid;
|
||||
}
|
||||
|
||||
return cid_;
|
||||
}
|
||||
|
||||
bool CompileType::HasDecidableNullability() {
|
||||
return !is_nullable_ || IsNull();
|
||||
return !can_be_null_ || IsNull();
|
||||
}
|
||||
|
||||
bool CompileType::IsNull() {
|
||||
|
@ -838,9 +881,12 @@ bool CompileType::Specialize(GrowableArray<intptr_t>* class_ids) {
|
|||
if (type_ != nullptr && type_->type_class_id() != kIllegalCid) {
|
||||
const Class& type_class = Class::Handle(type_->type_class());
|
||||
if (!CHA::ConcreteSubclasses(type_class, class_ids)) return false;
|
||||
if (is_nullable_) {
|
||||
if (can_be_null_) {
|
||||
class_ids->Add(kNullCid);
|
||||
}
|
||||
if (can_be_sentinel_) {
|
||||
class_ids->Add(kSentinelCid);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -905,7 +951,8 @@ void CompileType::PrintTo(BaseTextBuffer* f) const {
|
|||
type_name = "!null";
|
||||
}
|
||||
|
||||
f->Printf("T{%s%s}", type_name, is_nullable_ ? "?" : "");
|
||||
f->Printf("T{%s%s%s}", type_name, can_be_null_ ? "?" : "",
|
||||
can_be_sentinel_ ? "~" : "");
|
||||
}
|
||||
|
||||
const char* CompileType::ToCString() const {
|
||||
|
@ -974,15 +1021,17 @@ CompileType RedefinitionInstr::ComputeType() const {
|
|||
// If either type is non-nullable, the resulting type is non-nullable.
|
||||
const bool is_nullable =
|
||||
value()->Type()->is_nullable() && constrained_type_->is_nullable();
|
||||
const bool can_be_sentinel = value()->Type()->can_be_sentinel() &&
|
||||
constrained_type_->can_be_sentinel();
|
||||
|
||||
// If either type has a concrete cid, stick with it.
|
||||
if (value()->Type()->ToNullableCid() != kDynamicCid) {
|
||||
return CompileType::CreateNullable(is_nullable,
|
||||
value()->Type()->ToNullableCid());
|
||||
return CompileType(is_nullable, can_be_sentinel,
|
||||
value()->Type()->ToNullableCid(), nullptr);
|
||||
}
|
||||
if (constrained_type_->ToNullableCid() != kDynamicCid) {
|
||||
return CompileType::CreateNullable(is_nullable,
|
||||
constrained_type_->ToNullableCid());
|
||||
return CompileType(is_nullable, can_be_sentinel,
|
||||
constrained_type_->ToNullableCid(), nullptr);
|
||||
}
|
||||
if (value()->Type()->IsSubtypeOf(*constrained_type_->ToAbstractType())) {
|
||||
return is_nullable ? *value()->Type()
|
||||
|
@ -1088,7 +1137,8 @@ CompileType ParameterInstr::ComputeType() const {
|
|||
graph_entry->parsed_function().RawParameterVariable(0)->type();
|
||||
if (type.IsObjectType() || type.IsNullType()) {
|
||||
// Receiver can be null.
|
||||
return CompileType::FromAbstractType(type);
|
||||
return CompileType::FromAbstractType(type, CompileType::kCanBeNull,
|
||||
CompileType::kCannotBeSentinel);
|
||||
}
|
||||
|
||||
// Receiver can't be null but can be an instance of a subclass.
|
||||
|
@ -1121,7 +1171,8 @@ CompileType ParameterInstr::ComputeType() const {
|
|||
}
|
||||
}
|
||||
|
||||
return CompileType(CompileType::kNonNullable, cid, &type);
|
||||
return CompileType(CompileType::kCannotBeNull,
|
||||
CompileType::kCannotBeSentinel, cid, &type);
|
||||
}
|
||||
|
||||
const bool is_unchecked_entry_param =
|
||||
|
@ -1160,7 +1211,9 @@ CompileType ParameterInstr::ComputeType() const {
|
|||
const bool is_nullable =
|
||||
(inferred_type == NULL) || inferred_type->is_nullable();
|
||||
TraceStrongModeType(this, param->type());
|
||||
return CompileType::FromAbstractType(param->type(), is_nullable);
|
||||
return CompileType::FromAbstractType(
|
||||
param->type(), is_nullable,
|
||||
block_->IsCatchBlockEntry() && param->is_late());
|
||||
}
|
||||
// Last resort: use inferred non-nullability.
|
||||
if (inferred_type != NULL) {
|
||||
|
@ -1184,16 +1237,17 @@ CompileType ConstantInstr::ComputeType() const {
|
|||
intptr_t cid = value().GetClassId();
|
||||
|
||||
if (cid == kSmiCid && !compiler::target::IsSmi(Smi::Cast(value()).Value())) {
|
||||
return CompileType::Create(kMintCid,
|
||||
AbstractType::ZoneHandle(Type::MintType()));
|
||||
return CompileType(CompileType::kCannotBeNull,
|
||||
CompileType::kCannotBeSentinel, kMintCid,
|
||||
&AbstractType::ZoneHandle(Type::MintType()));
|
||||
}
|
||||
|
||||
if ((cid != kTypeArgumentsCid) && value().IsInstance()) {
|
||||
// Allocate in old-space since this may be invoked from the
|
||||
// background compiler.
|
||||
return CompileType::Create(
|
||||
cid,
|
||||
AbstractType::ZoneHandle(Instance::Cast(value()).GetType(Heap::kOld)));
|
||||
return CompileType(
|
||||
cid == kNullCid, cid == kSentinelCid, cid,
|
||||
&AbstractType::ZoneHandle(Instance::Cast(value()).GetType(Heap::kOld)));
|
||||
} else {
|
||||
// Type info for non-instance objects.
|
||||
return CompileType::FromCid(cid);
|
||||
|
@ -1211,7 +1265,8 @@ CompileType AssertAssignableInstr::ComputeType() const {
|
|||
return *value_type;
|
||||
}
|
||||
}
|
||||
return CompileType::FromAbstractType(*abs_type, value_type->is_nullable());
|
||||
return CompileType::FromAbstractType(*abs_type, value_type->is_nullable(),
|
||||
CompileType::kCannotBeSentinel);
|
||||
}
|
||||
|
||||
bool AssertAssignableInstr::RecomputeType() {
|
||||
|
@ -1261,7 +1316,8 @@ CompileType SpecialParameterInstr::ComputeType() const {
|
|||
case kArgDescriptor:
|
||||
return CompileType::FromCid(kImmutableArrayCid);
|
||||
case kException:
|
||||
return CompileType(CompileType::kNonNullable, kDynamicCid,
|
||||
return CompileType(CompileType::kCannotBeNull,
|
||||
CompileType::kCannotBeSentinel, kDynamicCid,
|
||||
&Object::dynamic_type());
|
||||
case kStackTrace:
|
||||
// We cannot use [kStackTraceCid] here because any kind of object can be
|
||||
|
@ -1273,18 +1329,18 @@ CompileType SpecialParameterInstr::ComputeType() const {
|
|||
}
|
||||
|
||||
CompileType CloneContextInstr::ComputeType() const {
|
||||
return CompileType(CompileType::kNonNullable, kContextCid,
|
||||
&Object::dynamic_type());
|
||||
return CompileType(CompileType::kCannotBeNull, CompileType::kCannotBeSentinel,
|
||||
kContextCid, &Object::dynamic_type());
|
||||
}
|
||||
|
||||
CompileType AllocateContextInstr::ComputeType() const {
|
||||
return CompileType(CompileType::kNonNullable, kContextCid,
|
||||
&Object::dynamic_type());
|
||||
return CompileType(CompileType::kCannotBeNull, CompileType::kCannotBeSentinel,
|
||||
kContextCid, &Object::dynamic_type());
|
||||
}
|
||||
|
||||
CompileType AllocateUninitializedContextInstr::ComputeType() const {
|
||||
return CompileType(CompileType::kNonNullable, kContextCid,
|
||||
&Object::dynamic_type());
|
||||
return CompileType(CompileType::kCannotBeNull, CompileType::kCannotBeSentinel,
|
||||
kContextCid, &Object::dynamic_type());
|
||||
}
|
||||
|
||||
CompileType InstanceCallBaseInstr::ComputeType() const {
|
||||
|
@ -1313,7 +1369,8 @@ CompileType InstanceCallBaseInstr::ComputeType() const {
|
|||
TraceStrongModeType(this, result_type);
|
||||
const bool is_nullable =
|
||||
(inferred_type == NULL) || inferred_type->is_nullable();
|
||||
return CompileType::FromAbstractType(result_type, is_nullable);
|
||||
return CompileType::FromAbstractType(result_type, is_nullable,
|
||||
CompileType::kCannotBeSentinel);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1327,14 +1384,15 @@ CompileType DispatchTableCallInstr::ComputeType() const {
|
|||
const auto& result_type = AbstractType::ZoneHandle(target.result_type());
|
||||
if (result_type.IsInstantiated()) {
|
||||
TraceStrongModeType(this, result_type);
|
||||
return CompileType::FromAbstractType(result_type);
|
||||
return CompileType::FromAbstractType(result_type, CompileType::kCanBeNull,
|
||||
CompileType::kCannotBeSentinel);
|
||||
}
|
||||
|
||||
return CompileType::Dynamic();
|
||||
}
|
||||
|
||||
CompileType PolymorphicInstanceCallInstr::ComputeType() const {
|
||||
bool is_nullable = CompileType::kNullable;
|
||||
bool is_nullable = CompileType::kCanBeNull;
|
||||
if (IsSureToCallSingleRecognizedTarget()) {
|
||||
const Function& target = *targets_.TargetAt(0)->target;
|
||||
if (target.has_pragma()) {
|
||||
|
@ -1342,7 +1400,7 @@ CompileType PolymorphicInstanceCallInstr::ComputeType() const {
|
|||
if (cid != kDynamicCid) {
|
||||
return CompileType::FromCid(cid);
|
||||
} else if (MethodRecognizer::HasNonNullableResultTypeFromPragma(target)) {
|
||||
is_nullable = CompileType::kNonNullable;
|
||||
is_nullable = CompileType::kCannotBeNull;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1369,7 +1427,8 @@ static CompileType ComputeListFactoryType(CompileType* inferred_type,
|
|||
Type::ZoneHandle(Type::New(cls, type_args, Nullability::kNonNullable));
|
||||
ASSERT(type.IsInstantiated());
|
||||
type.SetIsFinalized();
|
||||
return CompileType(CompileType::kNonNullable, cid, &type);
|
||||
return CompileType(CompileType::kCannotBeNull,
|
||||
CompileType::kCannotBeSentinel, cid, &type);
|
||||
}
|
||||
return *inferred_type;
|
||||
}
|
||||
|
@ -1386,14 +1445,14 @@ CompileType StaticCallInstr::ComputeType() const {
|
|||
return *inferred_type;
|
||||
}
|
||||
|
||||
bool is_nullable = CompileType::kNullable;
|
||||
bool is_nullable = CompileType::kCanBeNull;
|
||||
if (function_.has_pragma()) {
|
||||
const intptr_t cid = MethodRecognizer::ResultCidFromPragma(function_);
|
||||
if (cid != kDynamicCid) {
|
||||
return CompileType::FromCid(cid);
|
||||
}
|
||||
if (MethodRecognizer::HasNonNullableResultTypeFromPragma(function_)) {
|
||||
is_nullable = CompileType::kNonNullable;
|
||||
is_nullable = CompileType::kCannotBeNull;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1406,7 +1465,8 @@ CompileType StaticCallInstr::ComputeType() const {
|
|||
TraceStrongModeType(this, result_type);
|
||||
is_nullable = is_nullable &&
|
||||
(inferred_type == nullptr || inferred_type->is_nullable());
|
||||
return CompileType::FromAbstractType(result_type, is_nullable);
|
||||
return CompileType::FromAbstractType(result_type, is_nullable,
|
||||
CompileType::kCannotBeSentinel);
|
||||
}
|
||||
|
||||
return CompileType::Dynamic();
|
||||
|
@ -1417,11 +1477,12 @@ CompileType LoadLocalInstr::ComputeType() const {
|
|||
// We may not yet have checked the actual type of the parameter value.
|
||||
// Assuming that the value has the required type can lead to unsound
|
||||
// optimizations. See dartbug.com/43464.
|
||||
return CompileType::FromCid(kDynamicCid);
|
||||
return CompileType::Dynamic();
|
||||
}
|
||||
const AbstractType& local_type = local().type();
|
||||
TraceStrongModeType(this, local_type);
|
||||
return CompileType::FromAbstractType(local_type);
|
||||
return CompileType::FromAbstractType(local_type, CompileType::kCanBeNull,
|
||||
local().is_late());
|
||||
}
|
||||
|
||||
CompileType DropTempsInstr::ComputeType() const {
|
||||
|
@ -1448,7 +1509,7 @@ CompileType StringInterpolateInstr::ComputeType() const {
|
|||
|
||||
CompileType LoadStaticFieldInstr::ComputeType() const {
|
||||
const Field& field = this->field();
|
||||
bool is_nullable = CompileType::kNullable;
|
||||
bool is_nullable = CompileType::kCanBeNull;
|
||||
intptr_t cid = kIllegalCid; // Abstract type is known, calculate cid lazily.
|
||||
AbstractType* abstract_type = &AbstractType::ZoneHandle(field.type());
|
||||
TraceStrongModeType(this, *abstract_type);
|
||||
|
@ -1457,7 +1518,7 @@ CompileType LoadStaticFieldInstr::ComputeType() const {
|
|||
const bool is_initialized = IsFieldInitialized(&obj);
|
||||
if (field.is_final() && is_initialized) {
|
||||
if (!obj.IsNull()) {
|
||||
is_nullable = CompileType::kNonNullable;
|
||||
is_nullable = CompileType::kCannotBeNull;
|
||||
cid = obj.GetClassId();
|
||||
abstract_type = nullptr; // Cid is known, calculate abstract type lazily.
|
||||
}
|
||||
|
@ -1473,7 +1534,9 @@ CompileType LoadStaticFieldInstr::ComputeType() const {
|
|||
DEBUG_ASSERT(IsolateGroup::Current()->HasAttemptedReload());
|
||||
return CompileType::Dynamic();
|
||||
}
|
||||
return CompileType(is_nullable, cid, abstract_type);
|
||||
const bool can_be_sentinel = !calls_initializer() && field.is_late() &&
|
||||
field.is_final() && !field.has_initializer();
|
||||
return CompileType(is_nullable, can_be_sentinel, cid, abstract_type);
|
||||
}
|
||||
|
||||
CompileType CreateArrayInstr::ComputeType() const {
|
||||
|
@ -1494,7 +1557,8 @@ CompileType AllocateClosureInstr::ComputeType() const {
|
|||
const auto& func = known_function();
|
||||
if (!func.IsNull()) {
|
||||
const auto& sig = FunctionType::ZoneHandle(func.signature());
|
||||
return CompileType(CompileType::kNonNullable, kClosureCid, &sig);
|
||||
return CompileType(CompileType::kCannotBeNull,
|
||||
CompileType::kCannotBeSentinel, kClosureCid, &sig);
|
||||
}
|
||||
return CompileType::FromCid(kClosureCid);
|
||||
}
|
||||
|
@ -1509,6 +1573,9 @@ CompileType LoadClassIdInstr::ComputeType() const {
|
|||
|
||||
CompileType LoadFieldInstr::ComputeType() const {
|
||||
CompileType type = slot().ComputeCompileType();
|
||||
if (calls_initializer()) {
|
||||
type = type.CopyNonSentinel();
|
||||
}
|
||||
TraceStrongModeType(this, &type);
|
||||
return type;
|
||||
}
|
||||
|
@ -1754,7 +1821,8 @@ static CompileType ComputeArrayElementType(Value* array) {
|
|||
// 1. Try to extract element type from array value.
|
||||
auto& elem_type = AbstractType::Handle(GetElementTypeFromArray(array));
|
||||
if (!elem_type.IsDynamicType()) {
|
||||
return CompileType::FromAbstractType(elem_type);
|
||||
return CompileType::FromAbstractType(elem_type, CompileType::kCanBeNull,
|
||||
CompileType::kCannotBeSentinel);
|
||||
}
|
||||
|
||||
// 2. Array value may be loaded from GrowableObjectArray.data.
|
||||
|
@ -1764,7 +1832,8 @@ static CompileType ComputeArrayElementType(Value* array) {
|
|||
array = load_field->instance();
|
||||
elem_type = GetElementTypeFromArray(array);
|
||||
if (!elem_type.IsDynamicType()) {
|
||||
return CompileType::FromAbstractType(elem_type);
|
||||
return CompileType::FromAbstractType(elem_type, CompileType::kCanBeNull,
|
||||
CompileType::kCannotBeSentinel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1778,7 +1847,8 @@ static CompileType ComputeArrayElementType(Value* array) {
|
|||
ExtractElementTypeFromArrayType(load_field->slot().static_type());
|
||||
}
|
||||
}
|
||||
return CompileType::FromAbstractType(elem_type);
|
||||
return CompileType::FromAbstractType(elem_type, CompileType::kCanBeNull,
|
||||
CompileType::kCannotBeSentinel);
|
||||
}
|
||||
|
||||
CompileType LoadIndexedInstr::ComputeType() const {
|
||||
|
@ -1793,7 +1863,8 @@ CompileType LoadIndexedInstr::ComputeType() const {
|
|||
|
||||
case kTypeArgumentsCid:
|
||||
return CompileType::FromAbstractType(Object::dynamic_type(),
|
||||
/*is_nullable=*/false);
|
||||
CompileType::kCannotBeNull,
|
||||
CompileType::kCannotBeSentinel);
|
||||
|
||||
case kTypedDataFloat32ArrayCid:
|
||||
case kTypedDataFloat64ArrayCid:
|
||||
|
|
|
@ -481,7 +481,8 @@ class C<NoBound,
|
|||
field ^= fields.At(i);
|
||||
type = field.type();
|
||||
|
||||
auto compile_type = CompileType::FromAbstractType(type);
|
||||
auto compile_type = CompileType::FromAbstractType(
|
||||
type, CompileType::kCanBeNull, CompileType::kCannotBeSentinel);
|
||||
if (compile_type.CanBeSmi() != expected_can_be_smi(field)) {
|
||||
dart::Expect(__FILE__, __LINE__)
|
||||
.Fail("expected that CanBeSmi() returns %s for compile type %s\n",
|
||||
|
|
|
@ -576,7 +576,8 @@ Fragment BaseFlowGraphBuilder::LoadStaticField(const Field& field,
|
|||
Fragment BaseFlowGraphBuilder::RedefinitionWithType(const AbstractType& type) {
|
||||
auto redefinition = new (Z) RedefinitionInstr(Pop());
|
||||
redefinition->set_constrained_type(
|
||||
new (Z) CompileType(CompileType::FromAbstractType(type)));
|
||||
new (Z) CompileType(CompileType::FromAbstractType(
|
||||
type, CompileType::kCanBeNull, CompileType::kCannotBeSentinel)));
|
||||
Push(redefinition);
|
||||
return Fragment(redefinition);
|
||||
}
|
||||
|
|
|
@ -976,9 +976,10 @@ struct InferredTypeMetadata {
|
|||
return CompileType::FromAbstractType(
|
||||
Type::ZoneHandle(
|
||||
zone, (IsNullable() ? Type::NullableIntType() : Type::IntType())),
|
||||
IsNullable());
|
||||
IsNullable(), CompileType::kCannotBeSentinel);
|
||||
} else {
|
||||
return CompileType::CreateNullable(IsNullable(), cid);
|
||||
return CompileType(IsNullable(), CompileType::kCannotBeSentinel, cid,
|
||||
nullptr);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -26,10 +26,11 @@ static CompileType ParameterType(LocalVariable* param,
|
|||
Representation representation = kTagged) {
|
||||
return param->was_type_checked_by_caller()
|
||||
? CompileType::FromAbstractType(param->type(),
|
||||
representation == kTagged)
|
||||
representation == kTagged,
|
||||
CompileType::kCannotBeSentinel)
|
||||
: ((representation == kTagged)
|
||||
? CompileType::Dynamic()
|
||||
: CompileType::FromCid(kDynamicCid).CopyNonNullable());
|
||||
: CompileType::Dynamic().CopyNonNullable());
|
||||
}
|
||||
|
||||
bool PrologueBuilder::PrologueSkippableOnUncheckedEntry(
|
||||
|
@ -357,7 +358,8 @@ Fragment PrologueBuilder::BuildTypeArgumentsHandling() {
|
|||
store_type_args += LoadFpRelativeSlot(
|
||||
compiler::target::kWordSize *
|
||||
(1 + compiler::target::frame_layout.param_end_from_fp),
|
||||
CompileType::CreateNullable(/*is_nullable=*/true, kTypeArgumentsCid));
|
||||
CompileType(CompileType::kCanBeNull, CompileType::kCannotBeSentinel,
|
||||
kTypeArgumentsCid, nullptr));
|
||||
store_type_args += StoreLocal(TokenPosition::kNoSource, type_args_var);
|
||||
store_type_args += Drop();
|
||||
|
||||
|
|
Loading…
Reference in a new issue