[VM runtime] Support Smi instances in type test cache.
This adds SubtypeTestCache-based optimizations for type tests against
* dst_type = FutureOr<T> (when T=int/num)
* dst_type = T (when T = FutureOr<int/num>)
Remove dangerous LoadClass pseudo assembler instruction (does not work for Smi).
Handle instantiated void in type tests (along with dynamic and Object).
Change-Id: I0df0fc72ff173b9464d16cc971969132b055a429
Reviewed-on: https://dart-review.googlesource.com/c/81182
Commit-Queue: Régis Crelier <regis@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
diff --git a/runtime/vm/compiler/assembler/assembler_arm.cc b/runtime/vm/compiler/assembler/assembler_arm.cc
index 1f56574..e35c3c1 100644
--- a/runtime/vm/compiler/assembler/assembler_arm.cc
+++ b/runtime/vm/compiler/assembler/assembler_arm.cc
@@ -1825,12 +1825,6 @@
ldr(result, Address(result, class_id, LSL, kSizeOfClassPairLog2));
}
-void Assembler::LoadClass(Register result, Register object, Register scratch) {
- ASSERT(scratch != result);
- LoadClassId(scratch, object);
- LoadClassById(result, scratch);
-}
-
void Assembler::CompareClassId(Register object,
intptr_t class_id,
Register scratch) {
diff --git a/runtime/vm/compiler/assembler/assembler_arm.h b/runtime/vm/compiler/assembler/assembler_arm.h
index e78943db..93da8c4 100644
--- a/runtime/vm/compiler/assembler/assembler_arm.h
+++ b/runtime/vm/compiler/assembler/assembler_arm.h
@@ -860,7 +860,6 @@
void LoadClassId(Register result, Register object, Condition cond = AL);
void LoadClassById(Register result, Register class_id);
- void LoadClass(Register result, Register object, Register scratch);
void CompareClassId(Register object, intptr_t class_id, Register scratch);
void LoadClassIdMayBeSmi(Register result, Register object);
void LoadTaggedClassIdMayBeSmi(Register result, Register object);
diff --git a/runtime/vm/compiler/assembler/assembler_arm64.cc b/runtime/vm/compiler/assembler/assembler_arm64.cc
index cdd45c8..c258d9a 100644
--- a/runtime/vm/compiler/assembler/assembler_arm64.cc
+++ b/runtime/vm/compiler/assembler/assembler_arm64.cc
@@ -1115,12 +1115,6 @@
ldr(result, Address(result, class_id, UXTX, Address::Scaled));
}
-void Assembler::LoadClass(Register result, Register object) {
- ASSERT(object != TMP);
- LoadClassId(TMP, object);
- LoadClassById(result, TMP);
-}
-
void Assembler::CompareClassId(Register object,
intptr_t class_id,
Register scratch) {
diff --git a/runtime/vm/compiler/assembler/assembler_arm64.h b/runtime/vm/compiler/assembler/assembler_arm64.h
index 785b0ac..7807f87 100644
--- a/runtime/vm/compiler/assembler/assembler_arm64.h
+++ b/runtime/vm/compiler/assembler/assembler_arm64.h
@@ -1531,7 +1531,6 @@
void LoadClassId(Register result, Register object);
// Overwrites class_id register (it will be tagged afterwards).
void LoadClassById(Register result, Register class_id);
- void LoadClass(Register result, Register object);
void CompareClassId(Register object,
intptr_t class_id,
Register scratch = kNoRegister);
diff --git a/runtime/vm/compiler/assembler/assembler_ia32.cc b/runtime/vm/compiler/assembler/assembler_ia32.cc
index 1f528e18..e2074e5 100644
--- a/runtime/vm/compiler/assembler/assembler_ia32.cc
+++ b/runtime/vm/compiler/assembler/assembler_ia32.cc
@@ -2434,12 +2434,6 @@
movl(result, Address(result, class_id, TIMES_8, 0));
}
-void Assembler::LoadClass(Register result, Register object, Register scratch) {
- ASSERT(scratch != result);
- LoadClassId(scratch, object);
- LoadClassById(result, scratch);
-}
-
void Assembler::CompareClassId(Register object,
intptr_t class_id,
Register scratch) {
diff --git a/runtime/vm/compiler/assembler/assembler_ia32.h b/runtime/vm/compiler/assembler/assembler_ia32.h
index c3b8492..cb75d28 100644
--- a/runtime/vm/compiler/assembler/assembler_ia32.h
+++ b/runtime/vm/compiler/assembler/assembler_ia32.h
@@ -666,8 +666,6 @@
void LoadClassById(Register result, Register class_id);
- void LoadClass(Register result, Register object, Register scratch);
-
void CompareClassId(Register object, intptr_t class_id, Register scratch);
void LoadClassIdMayBeSmi(Register result, Register object);
diff --git a/runtime/vm/compiler/assembler/assembler_x64.cc b/runtime/vm/compiler/assembler/assembler_x64.cc
index d3df2c3..c91098b 100644
--- a/runtime/vm/compiler/assembler/assembler_x64.cc
+++ b/runtime/vm/compiler/assembler/assembler_x64.cc
@@ -1952,11 +1952,6 @@
movq(result, Address(result, class_id, TIMES_8, 0));
}
-void Assembler::LoadClass(Register result, Register object) {
- LoadClassId(TMP, object);
- LoadClassById(result, TMP);
-}
-
void Assembler::CompareClassId(Register object,
intptr_t class_id,
Register scratch) {
diff --git a/runtime/vm/compiler/assembler/assembler_x64.h b/runtime/vm/compiler/assembler/assembler_x64.h
index 70f7d9e..6090c76 100644
--- a/runtime/vm/compiler/assembler/assembler_x64.h
+++ b/runtime/vm/compiler/assembler/assembler_x64.h
@@ -782,8 +782,6 @@
// Overwrites class_id register (it will be tagged afterwards).
void LoadClassById(Register result, Register class_id);
- void LoadClass(Register result, Register object);
-
void CompareClassId(Register object,
intptr_t class_id,
Register scratch = kNoRegister);
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler_arm.cc b/runtime/vm/compiler/backend/flow_graph_compiler_arm.cc
index b6a070f..729d9ff 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler_arm.cc
+++ b/runtime/vm/compiler/backend/flow_graph_compiler_arm.cc
@@ -248,13 +248,14 @@
ASSERT(type_class.NumTypeArguments() > 0);
const Register kInstanceReg = R0;
Error& bound_error = Error::Handle(zone());
- const Type& int_type = Type::Handle(zone(), Type::IntType());
+ const Type& smi_type = Type::Handle(zone(), Type::SmiType());
const bool smi_is_ok =
- int_type.IsSubtypeOf(type, &bound_error, NULL, Heap::kOld);
+ smi_type.IsSubtypeOf(type, &bound_error, NULL, Heap::kOld);
// Malformed type should have been handled at graph construction time.
ASSERT(smi_is_ok || bound_error.IsNull());
__ tst(kInstanceReg, Operand(kSmiTagMask));
if (smi_is_ok) {
+ // Fast case for type = FutureOr<int/num/top-type>.
__ b(is_instance_lbl, EQ);
} else {
__ b(is_not_instance_lbl, EQ);
@@ -286,7 +287,7 @@
ASSERT(!tp_argument.IsMalformed());
if (tp_argument.IsType()) {
ASSERT(tp_argument.HasResolvedTypeClass());
- // Check if type argument is dynamic or Object.
+ // Check if type argument is dynamic, Object, or void.
const Type& object_type = Type::Handle(zone(), Type::ObjectType());
if (object_type.IsSubtypeOf(tp_argument, NULL, NULL, Heap::kOld)) {
// Instance class test only necessary.
@@ -341,6 +342,7 @@
if (smi_class.IsSubtypeOf(Object::null_type_arguments(), type_class,
Object::null_type_arguments(), NULL, NULL,
Heap::kOld)) {
+ // Fast case for type = int/num/top-type.
__ b(is_instance_lbl, EQ);
} else {
__ b(is_not_instance_lbl, EQ);
@@ -398,7 +400,14 @@
Label* is_not_instance_lbl) {
__ Comment("Subtype1TestCacheLookup");
const Register kInstanceReg = R0;
- __ LoadClass(R1, kInstanceReg, R2);
+#if defined(DEBUG)
+ Label ok;
+ __ BranchIfNotSmi(kInstanceReg, &ok);
+ __ Breakpoint();
+ __ Bind(&ok);
+#endif
+ __ LoadClassId(R2, kInstanceReg);
+ __ LoadClassById(R1, R2);
// R1: instance class.
// Check immediate superclass equality.
__ ldr(R2, FieldAddress(R1, Class::super_type_offset()));
@@ -447,12 +456,13 @@
__ ldr(R3, FieldAddress(kTypeArgumentsReg,
TypeArguments::type_at_offset(type_param.index())));
// R3: concrete type of type.
- // Check if type argument is dynamic.
+ // Check if type argument is dynamic, Object, or void.
__ CompareObject(R3, Object::dynamic_type());
__ b(is_instance_lbl, EQ);
__ CompareObject(R3, Type::ZoneHandle(zone(), Type::ObjectType()));
__ b(is_instance_lbl, EQ);
- // TODO(regis): Optimize void type as well once allowed as type argument.
+ __ CompareObject(R3, Object::void_type());
+ __ b(is_instance_lbl, EQ);
// For Smi check quickly against int and num interfaces.
Label not_smi;
@@ -462,9 +472,8 @@
__ b(is_instance_lbl, EQ);
__ CompareObject(R3, Type::ZoneHandle(zone(), Type::Number()));
__ b(is_instance_lbl, EQ);
- // Smi must be handled in runtime.
- Label fall_through;
- __ b(&fall_through);
+ // Smi can be handled by type test cache.
+ __ Bind(¬_smi);
// If it's guaranteed, by type-parameter bound, that the type parameter will
// never have a value of a function type, then we can safely do a 4-type
@@ -474,17 +483,18 @@
? kTestTypeSixArgs
: kTestTypeFourArgs;
- __ Bind(¬_smi);
const SubtypeTestCache& type_test_cache = SubtypeTestCache::ZoneHandle(
zone(), GenerateCallSubtypeTestStub(
test_kind, kInstanceReg, kInstantiatorTypeArgumentsReg,
kFunctionTypeArgumentsReg, kTempReg, is_instance_lbl,
is_not_instance_lbl));
- __ Bind(&fall_through);
return type_test_cache.raw();
}
if (type.IsType()) {
- __ BranchIfSmi(kInstanceReg, is_not_instance_lbl);
+ // Smi is FutureOr<T>, when T is a top type or int or num.
+ if (!FLAG_strong || !Class::Handle(type.type_class()).IsFutureOrClass()) {
+ __ BranchIfSmi(kInstanceReg, is_not_instance_lbl);
+ }
__ ldm(IA, SP,
(1 << kFunctionTypeArgumentsReg) |
(1 << kInstantiatorTypeArgumentsReg));
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler_arm64.cc b/runtime/vm/compiler/backend/flow_graph_compiler_arm64.cc
index eb23398..4812ac4 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler_arm64.cc
+++ b/runtime/vm/compiler/backend/flow_graph_compiler_arm64.cc
@@ -243,11 +243,12 @@
ASSERT(type_class.NumTypeArguments() > 0);
const Register kInstanceReg = R0;
Error& bound_error = Error::Handle(zone());
- const Type& int_type = Type::Handle(zone(), Type::IntType());
+ const Type& smi_type = Type::Handle(zone(), Type::SmiType());
const bool smi_is_ok =
- int_type.IsSubtypeOf(type, &bound_error, NULL, Heap::kOld);
+ smi_type.IsSubtypeOf(type, &bound_error, NULL, Heap::kOld);
// Malformed type should have been handled at graph construction time.
ASSERT(smi_is_ok || bound_error.IsNull());
+ // Fast case for type = FutureOr<int/num/top-type>.
__ BranchIfSmi(kInstanceReg,
smi_is_ok ? is_instance_lbl : is_not_instance_lbl);
@@ -278,7 +279,7 @@
ASSERT(!tp_argument.IsMalformed());
if (tp_argument.IsType()) {
ASSERT(tp_argument.HasResolvedTypeClass());
- // Check if type argument is dynamic or Object.
+ // Check if type argument is dynamic, Object, or void.
const Type& object_type = Type::Handle(zone(), Type::ObjectType());
if (object_type.IsSubtypeOf(tp_argument, NULL, NULL, Heap::kOld)) {
// Instance class test only necessary.
@@ -331,6 +332,7 @@
if (smi_class.IsSubtypeOf(Object::null_type_arguments(), type_class,
Object::null_type_arguments(), NULL, NULL,
Heap::kOld)) {
+ // Fast case for type = int/num/top-type.
__ BranchIfSmi(kInstanceReg, is_instance_lbl);
} else {
__ BranchIfSmi(kInstanceReg, is_not_instance_lbl);
@@ -388,7 +390,14 @@
Label* is_not_instance_lbl) {
__ Comment("Subtype1TestCacheLookup");
const Register kInstanceReg = R0;
- __ LoadClass(R1, kInstanceReg);
+#if defined(DEBUG)
+ Label ok;
+ __ BranchIfNotSmi(kInstanceReg, &ok);
+ __ Breakpoint();
+ __ Bind(&ok);
+#endif
+ __ LoadClassId(TMP, kInstanceReg);
+ __ LoadClassById(R1, TMP);
// R1: instance class.
// Check immediate superclass equality.
__ LoadFieldFromOffset(R2, R1, Class::super_type_offset());
@@ -435,12 +444,13 @@
__ LoadFieldFromOffset(R3, kTypeArgumentsReg,
TypeArguments::type_at_offset(type_param.index()));
// R3: concrete type of type.
- // Check if type argument is dynamic.
+ // Check if type argument is dynamic, Object, or void.
__ CompareObject(R3, Object::dynamic_type());
__ b(is_instance_lbl, EQ);
__ CompareObject(R3, Type::ZoneHandle(zone(), Type::ObjectType()));
__ b(is_instance_lbl, EQ);
- // TODO(regis): Optimize void type as well once allowed as type argument.
+ __ CompareObject(R3, Object::void_type());
+ __ b(is_instance_lbl, EQ);
// For Smi check quickly against int and num interfaces.
Label not_smi;
@@ -449,9 +459,8 @@
__ b(is_instance_lbl, EQ);
__ CompareObject(R3, Type::ZoneHandle(zone(), Type::Number()));
__ b(is_instance_lbl, EQ);
- // Smi must be handled in runtime.
- Label fall_through;
- __ b(&fall_through);
+ // Smi can be handled by type test cache.
+ __ Bind(¬_smi);
// If it's guaranteed, by type-parameter bound, that the type parameter will
// never have a value of a function type, then we can safely do a 4-type
@@ -461,17 +470,18 @@
? kTestTypeSixArgs
: kTestTypeFourArgs;
- __ Bind(¬_smi);
const SubtypeTestCache& type_test_cache = SubtypeTestCache::ZoneHandle(
zone(), GenerateCallSubtypeTestStub(
test_kind, kInstanceReg, kInstantiatorTypeArgumentsReg,
kFunctionTypeArgumentsReg, kTempReg, is_instance_lbl,
is_not_instance_lbl));
- __ Bind(&fall_through);
return type_test_cache.raw();
}
if (type.IsType()) {
- __ BranchIfSmi(kInstanceReg, is_not_instance_lbl);
+ // Smi is FutureOr<T>, when T is a top type or int or num.
+ if (!FLAG_strong || !Class::Handle(type.type_class()).IsFutureOrClass()) {
+ __ BranchIfSmi(kInstanceReg, is_not_instance_lbl);
+ }
__ ldp(kFunctionTypeArgumentsReg, kInstantiatorTypeArgumentsReg,
Address(SP, 0 * kWordSize, Address::PairOffset));
// Uninstantiated type class is known at compile time, but the type
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler_ia32.cc b/runtime/vm/compiler/backend/flow_graph_compiler_ia32.cc
index d8de557..f6dc95a 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler_ia32.cc
+++ b/runtime/vm/compiler/backend/flow_graph_compiler_ia32.cc
@@ -256,13 +256,14 @@
ASSERT(type_class.NumTypeArguments() > 0);
const Register kInstanceReg = EAX;
Error& bound_error = Error::Handle(zone());
- const Type& int_type = Type::Handle(zone(), Type::IntType());
+ const Type& smi_type = Type::Handle(zone(), Type::SmiType());
const bool smi_is_ok =
- int_type.IsSubtypeOf(type, &bound_error, NULL, Heap::kOld);
+ smi_type.IsSubtypeOf(type, &bound_error, NULL, Heap::kOld);
// Malformed type should have been handled at graph construction time.
ASSERT(smi_is_ok || bound_error.IsNull());
__ testl(kInstanceReg, Immediate(kSmiTagMask));
if (smi_is_ok) {
+ // Fast case for type = FutureOr<int/num/top-type>.
__ j(ZERO, is_instance_lbl);
} else {
__ j(ZERO, is_not_instance_lbl);
@@ -294,7 +295,7 @@
ASSERT(!tp_argument.IsMalformed());
if (tp_argument.IsType()) {
ASSERT(tp_argument.HasResolvedTypeClass());
- // Check if type argument is dynamic or Object.
+ // Check if type argument is dynamic, Object, or void.
const Type& object_type = Type::Handle(zone(), Type::ObjectType());
if (object_type.IsSubtypeOf(tp_argument, NULL, NULL, Heap::kOld)) {
// Instance class test only necessary.
@@ -347,6 +348,7 @@
if (smi_class.IsSubtypeOf(Object::null_type_arguments(), type_class,
Object::null_type_arguments(), NULL, NULL,
Heap::kOld)) {
+ // Fast case for type = int/num/top-type.
__ j(ZERO, is_instance_lbl);
} else {
__ j(ZERO, is_not_instance_lbl);
@@ -404,7 +406,14 @@
Label* is_not_instance_lbl) {
__ Comment("Subtype1TestCacheLookup");
const Register kInstanceReg = EAX;
- __ LoadClass(ECX, kInstanceReg, EDI);
+#if defined(DEBUG)
+ Label ok;
+ __ BranchIfNotSmi(kInstanceReg, &ok);
+ __ Breakpoint();
+ __ Bind(&ok);
+#endif
+ __ LoadClassId(EDI, kInstanceReg);
+ __ LoadClassById(ECX, EDI);
// ECX: instance class.
// Check immediate superclass equality.
__ movl(EDI, FieldAddress(ECX, Class::super_type_offset()));
@@ -455,12 +464,13 @@
__ movl(EDI, FieldAddress(kTypeArgumentsReg, TypeArguments::type_at_offset(
type_param.index())));
// EDI: concrete type of type.
- // Check if type argument is dynamic.
+ // Check if type argument is dynamic, Object, or void.
__ CompareObject(EDI, Object::dynamic_type());
__ j(EQUAL, is_instance_lbl);
__ CompareObject(EDI, Type::ZoneHandle(zone(), Type::ObjectType()));
__ j(EQUAL, is_instance_lbl);
- // TODO(regis): Optimize void type as well once allowed as type argument.
+ __ CompareObject(EDI, Object::void_type());
+ __ j(EQUAL, is_instance_lbl);
// For Smi check quickly against int and num interfaces.
Label not_smi;
@@ -470,9 +480,8 @@
__ j(EQUAL, is_instance_lbl);
__ CompareObject(EDI, Type::ZoneHandle(zone(), Type::Number()));
__ j(EQUAL, is_instance_lbl);
- // Smi must be handled in runtime.
- Label fall_through;
- __ jmp(&fall_through);
+ // Smi can be handled by type test cache.
+ __ Bind(¬_smi);
// If it's guaranteed, by type-parameter bound, that the type parameter will
// never have a value of a function type.
@@ -481,18 +490,19 @@
? kTestTypeSixArgs
: kTestTypeFourArgs;
- __ Bind(¬_smi);
const SubtypeTestCache& type_test_cache = SubtypeTestCache::ZoneHandle(
zone(), GenerateCallSubtypeTestStub(
test_kind, kInstanceReg, kInstantiatorTypeArgumentsReg,
kFunctionTypeArgumentsReg, kTempReg, is_instance_lbl,
is_not_instance_lbl));
- __ Bind(&fall_through);
return type_test_cache.raw();
}
if (type.IsType()) {
- __ testl(kInstanceReg, Immediate(kSmiTagMask)); // Is instance Smi?
- __ j(ZERO, is_not_instance_lbl);
+ // Smi is FutureOr<T>, when T is a top type or int or num.
+ if (!FLAG_strong || !Class::Handle(type.type_class()).IsFutureOrClass()) {
+ __ testl(kInstanceReg, Immediate(kSmiTagMask)); // Is instance Smi?
+ __ j(ZERO, is_not_instance_lbl);
+ }
__ movl(kInstantiatorTypeArgumentsReg, Address(ESP, 1 * kWordSize));
__ movl(kFunctionTypeArgumentsReg, Address(ESP, 0 * kWordSize));
// Uninstantiated type class is known at compile time, but the type
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler_x64.cc b/runtime/vm/compiler/backend/flow_graph_compiler_x64.cc
index d3e58d8..2471b4e 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler_x64.cc
+++ b/runtime/vm/compiler/backend/flow_graph_compiler_x64.cc
@@ -248,13 +248,14 @@
ASSERT(type_class.NumTypeArguments() > 0);
const Register kInstanceReg = RAX;
Error& bound_error = Error::Handle(zone());
- const Type& int_type = Type::Handle(zone(), Type::IntType());
+ const Type& smi_type = Type::Handle(zone(), Type::SmiType());
const bool smi_is_ok =
- int_type.IsSubtypeOf(type, &bound_error, NULL, Heap::kOld);
+ smi_type.IsSubtypeOf(type, &bound_error, NULL, Heap::kOld);
// Malformed type should have been handled at graph construction time.
ASSERT(smi_is_ok || bound_error.IsNull());
__ testq(kInstanceReg, Immediate(kSmiTagMask));
if (smi_is_ok) {
+ // Fast case for type = FutureOr<int/num/top-type>.
__ j(ZERO, is_instance_lbl);
} else {
__ j(ZERO, is_not_instance_lbl);
@@ -287,7 +288,7 @@
ASSERT(!tp_argument.IsMalformed());
if (tp_argument.IsType()) {
ASSERT(tp_argument.HasResolvedTypeClass());
- // Check if type argument is dynamic or Object.
+ // Check if type argument is dynamic, Object, or void.
const Type& object_type = Type::Handle(zone(), Type::ObjectType());
if (object_type.IsSubtypeOf(tp_argument, NULL, NULL, Heap::kOld)) {
// Instance class test only necessary.
@@ -345,6 +346,7 @@
if (smi_class.IsSubtypeOf(Object::null_type_arguments(), type_class,
Object::null_type_arguments(), NULL, NULL,
Heap::kOld)) {
+ // Fast case for type = int/num/top-type.
__ j(ZERO, is_instance_lbl);
} else {
__ j(ZERO, is_not_instance_lbl);
@@ -406,7 +408,14 @@
Label* is_not_instance_lbl) {
__ Comment("Subtype1TestCacheLookup");
const Register kInstanceReg = RAX;
- __ LoadClass(R10, kInstanceReg);
+#if defined(DEBUG)
+ Label ok;
+ __ BranchIfNotSmi(kInstanceReg, &ok);
+ __ Breakpoint();
+ __ Bind(&ok);
+#endif
+ __ LoadClassId(TMP, kInstanceReg);
+ __ LoadClassById(R10, TMP);
// R10: instance class.
// Check immediate superclass equality.
__ movq(R13, FieldAddress(R10, Class::super_type_offset()));
@@ -458,13 +467,14 @@
__ movq(RDI, FieldAddress(kTypeArgumentsReg, TypeArguments::type_at_offset(
type_param.index())));
// RDI: Concrete type of type.
- // Check if type argument is dynamic.
+ // Check if type argument is dynamic, Object, or void.
__ CompareObject(RDI, Object::dynamic_type());
__ j(EQUAL, is_instance_lbl);
const Type& object_type = Type::ZoneHandle(zone(), Type::ObjectType());
__ CompareObject(RDI, object_type);
__ j(EQUAL, is_instance_lbl);
- // TODO(regis): Optimize void type as well once allowed as type argument.
+ __ CompareObject(RDI, Object::void_type());
+ __ j(EQUAL, is_instance_lbl);
// For Smi check quickly against int and num interfaces.
Label not_smi;
@@ -474,9 +484,8 @@
__ j(EQUAL, is_instance_lbl);
__ CompareObject(RDI, Type::ZoneHandle(zone(), Type::Number()));
__ j(EQUAL, is_instance_lbl);
- // Smi must be handled in runtime.
- Label fall_through;
- __ jmp(&fall_through);
+ // Smi can be handled by type test cache.
+ __ Bind(¬_smi);
// If it's guaranteed, by type-parameter bound, that the type parameter will
// never have a value of a function type, then we can safely do a 4-type
@@ -486,18 +495,19 @@
? kTestTypeSixArgs
: kTestTypeFourArgs;
- __ Bind(¬_smi);
const SubtypeTestCache& type_test_cache = SubtypeTestCache::ZoneHandle(
zone(), GenerateCallSubtypeTestStub(
test_kind, kInstanceReg, kInstantiatorTypeArgumentsReg,
kFunctionTypeArgumentsReg, kTempReg, is_instance_lbl,
is_not_instance_lbl));
- __ Bind(&fall_through);
return type_test_cache.raw();
}
if (type.IsType()) {
- __ testq(kInstanceReg, Immediate(kSmiTagMask)); // Is instance Smi?
- __ j(ZERO, is_not_instance_lbl);
+ // Smi is FutureOr<T>, when T is a top type or int or num.
+ if (!FLAG_strong || !Class::Handle(type.type_class()).IsFutureOrClass()) {
+ __ testq(kInstanceReg, Immediate(kSmiTagMask)); // Is instance Smi?
+ __ j(ZERO, is_not_instance_lbl);
+ }
// Uninstantiated type class is known at compile time, but the type
// arguments are determined at runtime by the instantiator(s).
return GenerateCallSubtypeTestStub(kTestTypeFourArgs, kInstanceReg,
diff --git a/runtime/vm/runtime_entry.cc b/runtime/vm/runtime_entry.cc
index ad00ea5..5994aed 100644
--- a/runtime/vm/runtime_entry.cc
+++ b/runtime/vm/runtime_entry.cc
@@ -651,18 +651,17 @@
}
return;
}
+ Class& instance_class = Class::Handle(zone);
if (instance.IsSmi()) {
- if (FLAG_trace_type_checks) {
- OS::PrintErr("UpdateTypeTestCache: instance is Smi\n");
- }
- return;
+ instance_class = Smi::Class();
+ } else {
+ instance_class = instance.clazz();
}
// If the type is uninstantiated and refers to parent function type
// parameters, the function_type_arguments have been canonicalized
// when concatenated.
ASSERT(function_type_arguments.IsNull() ||
function_type_arguments.IsCanonical());
- const Class& instance_class = Class::Handle(zone, instance.clazz());
auto& instance_class_id_or_function = Object::Handle(zone);
auto& instance_type_arguments = TypeArguments::Handle(zone);
auto& instance_parent_function_type_arguments = TypeArguments::Handle(zone);
@@ -748,14 +747,16 @@
" Updated test cache %p ix: %" Pd
" with "
"(cid-or-fun: %p, type-args: %p, i-type-args: %p, f-type-args: %p, "
- "result: %s)\n"
+ "p-type-args: %p, d-type-args: %p, result: %s)\n"
" instance [class: (%p '%s' cid: %" Pd
"), type-args: %p %s]\n"
" test-type [class: (%p '%s' cid: %" Pd
"), i-type-args: %p %s, f-type-args: %p %s]\n",
new_cache.raw(), len, instance_class_id_or_function.raw(),
instance_type_arguments.raw(), instantiator_type_arguments.raw(),
- instantiator_type_arguments.raw(), result.ToCString(),
+ function_type_arguments.raw(),
+ instance_parent_function_type_arguments.raw(),
+ instance_delayed_type_arguments.raw(), result.ToCString(),
instance_class.raw(), instance_class_name.ToCString(),
instance_class.id(), instance_type_arguments.raw(),
instance_type_arguments.ToCString(), type_class.raw(),
diff --git a/runtime/vm/stub_code_arm.cc b/runtime/vm/stub_code_arm.cc
index 8791013..2ddd1dc 100644
--- a/runtime/vm/stub_code_arm.cc
+++ b/runtime/vm/stub_code_arm.cc
@@ -2007,7 +2007,11 @@
__ AddImmediate(kCacheReg, Array::data_offset() - kHeapObjectTag);
Label loop, not_closure;
- __ LoadClassId(kInstanceCidOrFunction, kInstanceReg);
+ if (n >= 4) {
+ __ LoadClassIdMayBeSmi(kInstanceCidOrFunction, kInstanceReg);
+ } else {
+ __ LoadClassId(kInstanceCidOrFunction, kInstanceReg);
+ }
__ CompareImmediate(kInstanceCidOrFunction, kClosureCid);
__ b(¬_closure, NE);
@@ -2295,7 +2299,6 @@
void StubCode::GenerateSlowTypeTestStub(Assembler* assembler) {
Label done, call_runtime;
- const Register kInstanceReg = R0;
const Register kFunctionTypeArgumentsReg = R1;
const Register kDstTypeReg = R8;
const Register kSubtypeTestCacheReg = R3;
@@ -2303,6 +2306,7 @@
__ EnterStubFrame();
#ifdef DEBUG
+ const Register kInstanceReg = R0;
// Guaranteed by caller.
Label no_error;
__ CompareObject(kInstanceReg, Object::null_object());
@@ -2311,11 +2315,6 @@
__ Bind(&no_error);
#endif
- // Need to handle slow cases of [Smi]s here because the
- // [SubtypeTestCache]-based stubs do not handle [Smi]s.
- Label non_smi_value;
- __ BranchIfSmi(kInstanceReg, &call_runtime);
-
// If the subtype-cache is null, it needs to be lazily-created by the runtime.
__ CompareObject(kSubtypeTestCacheReg, Object::null_object());
__ BranchIf(EQUAL, &call_runtime);
diff --git a/runtime/vm/stub_code_arm64.cc b/runtime/vm/stub_code_arm64.cc
index 8aa8213..fc87279 100644
--- a/runtime/vm/stub_code_arm64.cc
+++ b/runtime/vm/stub_code_arm64.cc
@@ -2271,7 +2271,11 @@
__ AddImmediate(kCacheReg, Array::data_offset() - kHeapObjectTag);
Label loop, not_closure;
- __ LoadClassId(kInstanceCidOrFunction, kInstanceReg);
+ if (n >= 4) {
+ __ LoadClassIdMayBeSmi(kInstanceCidOrFunction, kInstanceReg);
+ } else {
+ __ LoadClassId(kInstanceCidOrFunction, kInstanceReg);
+ }
__ CompareImmediate(kInstanceCidOrFunction, kClosureCid);
__ b(¬_closure, NE);
@@ -2567,7 +2571,6 @@
void StubCode::GenerateSlowTypeTestStub(Assembler* assembler) {
Label done, call_runtime;
- const Register kInstanceReg = R0;
const Register kInstantiatorTypeArgumentsReg = R1;
const Register kSubtypeTestCacheReg = R3;
@@ -2576,6 +2579,7 @@
__ EnterStubFrame();
#ifdef DEBUG
+ const Register kInstanceReg = R0;
// Guaranteed by caller.
Label no_error;
__ CompareObject(kInstanceReg, Object::null_object());
@@ -2584,11 +2588,6 @@
__ Bind(&no_error);
#endif
- // Need to handle slow cases of [Smi]s here because the
- // [SubtypeTestCache]-based stubs do not handle [Smi]s.
- Label non_smi_value;
- __ BranchIfSmi(kInstanceReg, &call_runtime);
-
// If the subtype-cache is null, it needs to be lazily-created by the runtime.
__ CompareObject(kSubtypeTestCacheReg, Object::null_object());
__ BranchIf(EQUAL, &call_runtime);
diff --git a/runtime/vm/stub_code_ia32.cc b/runtime/vm/stub_code_ia32.cc
index 4535bad..d609e2a 100644
--- a/runtime/vm/stub_code_ia32.cc
+++ b/runtime/vm/stub_code_ia32.cc
@@ -1752,8 +1752,11 @@
__ addl(EDX, Immediate(Array::data_offset() - kHeapObjectTag));
Label loop, not_closure;
-
- __ LoadClassId(kInstanceCidOrFunction, kInstanceReg);
+ if (n >= 4) {
+ __ LoadClassIdMayBeSmi(kInstanceCidOrFunction, kInstanceReg);
+ } else {
+ __ LoadClassId(kInstanceCidOrFunction, kInstanceReg);
+ }
__ cmpl(kInstanceCidOrFunction, Immediate(kClosureCid));
__ j(NOT_EQUAL, ¬_closure, Assembler::kNearJump);
diff --git a/runtime/vm/stub_code_x64.cc b/runtime/vm/stub_code_x64.cc
index c8f4218..295d7ce 100644
--- a/runtime/vm/stub_code_x64.cc
+++ b/runtime/vm/stub_code_x64.cc
@@ -2291,8 +2291,12 @@
__ addq(RSI, Immediate(Array::data_offset() - kHeapObjectTag));
Label loop, not_closure;
- __ LoadClassId(R10, kInstanceReg);
- __ cmpq(R10, Immediate(kClosureCid));
+ if (n >= 4) {
+ __ LoadClassIdMayBeSmi(kInstanceCidOrFunction, kInstanceReg);
+ } else {
+ __ LoadClassId(kInstanceCidOrFunction, kInstanceReg);
+ }
+ __ cmpq(kInstanceCidOrFunction, Immediate(kClosureCid));
__ j(NOT_EQUAL, ¬_closure, Assembler::kNearJump);
// Closure handling.
@@ -2581,13 +2585,13 @@
void StubCode::GenerateSlowTypeTestStub(Assembler* assembler) {
Label done, call_runtime;
- const Register kInstanceReg = RAX;
const Register kDstTypeReg = RBX;
const Register kSubtypeTestCacheReg = R9;
__ EnterStubFrame();
#ifdef DEBUG
+ const Register kInstanceReg = RAX;
// Guaranteed by caller.
Label no_error;
__ CompareObject(kInstanceReg, Object::null_object());
@@ -2596,10 +2600,6 @@
__ Bind(&no_error);
#endif
- // Need to handle slow cases of [Smi]s here because the
- // [SubtypeTestCache]-based stubs do not handle [Smi]s.
- __ BranchIfSmi(kInstanceReg, &call_runtime);
-
// If the subtype-cache is null, it needs to be lazily-created by the runtime.
__ CompareObject(kSubtypeTestCacheReg, Object::null_object());
__ BranchIf(EQUAL, &call_runtime);