[VM/runtime] Speed up runtime type equality for closures.

This avoids allocating and instantiating the runtime type of closures with identical signatures and instantiators.
This also fixes runtime type equivalence for types (FunctionType and Type).

TEST=all existing ones.

Change-Id: Ic05f2abf1398546d565b98a04cd5f67416840443
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/202780
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Commit-Queue: Régis Crelier <regis@google.com>
This commit is contained in:
Regis Crelier 2021-06-08 16:54:17 +00:00 committed by commit-bot@chromium.org
parent b666964948
commit 3ff71e0a47
6 changed files with 114 additions and 42 deletions

View file

@ -114,11 +114,23 @@ DEFINE_NATIVE_ENTRY(Object_haveSameRuntimeType, 0, 2) {
const Class& cls = Class::Handle(left.clazz());
if (cls.IsClosureClass()) {
// TODO(vegorov): provide faster implementation for closure classes.
const Function& left_function =
Function::Handle(zone, Closure::Cast(left).function());
const Function& right_function =
Function::Handle(zone, Closure::Cast(right).function());
if (left_function.signature() == right_function.signature() &&
Closure::Cast(left).function_type_arguments() ==
Closure::Cast(right).function_type_arguments() &&
Closure::Cast(left).delayed_type_arguments() ==
Closure::Cast(right).delayed_type_arguments() &&
Closure::Cast(left).instantiator_type_arguments() ==
Closure::Cast(right).instantiator_type_arguments()) {
return Bool::True().ptr();
}
const AbstractType& left_type =
AbstractType::Handle(left.GetType(Heap::kNew));
AbstractType::Handle(zone, left.GetType(Heap::kNew));
const AbstractType& right_type =
AbstractType::Handle(right.GetType(Heap::kNew));
AbstractType::Handle(zone, right.GetType(Heap::kNew));
return Bool::Get(
left_type.IsEquivalent(right_type, TypeEquality::kSyntactical))
.ptr();
@ -132,9 +144,9 @@ DEFINE_NATIVE_ENTRY(Object_haveSameRuntimeType, 0, 2) {
return Bool::True().ptr();
}
const TypeArguments& left_type_arguments =
TypeArguments::Handle(left.GetTypeArguments());
TypeArguments::Handle(zone, left.GetTypeArguments());
const TypeArguments& right_type_arguments =
TypeArguments::Handle(right.GetTypeArguments());
TypeArguments::Handle(zone, right.GetTypeArguments());
const intptr_t num_type_args = cls.NumTypeArguments();
const intptr_t num_type_params = cls.NumTypeParameters();
return Bool::Get(left_type_arguments.IsSubvectorEquivalent(

View file

@ -1198,6 +1198,14 @@ static void JumpIfNotString(Assembler* assembler,
kIfNotInRange, target);
}
static void JumpIfType(Assembler* assembler,
Register cid,
Register tmp,
Label* target) {
RangeCheck(assembler, cid, tmp, kTypeCid, kFunctionTypeCid, kIfInRange,
target);
}
static void JumpIfNotType(Assembler* assembler,
Register cid,
Register tmp,
@ -1272,7 +1280,7 @@ static void EquivalentClassIds(Assembler* assembler,
Register cid1,
Register cid2,
Register scratch) {
Label different_cids, not_integer;
Label different_cids, not_integer, not_integer_or_string;
// Check if left hand side is a closure. Closures are handled in the runtime.
__ CompareImmediate(cid1, kClosureCid);
@ -1295,7 +1303,7 @@ static void EquivalentClassIds(Assembler* assembler,
__ b(equal);
// Class ids are different. Check if we are comparing two string types (with
// different representations) or two integer types.
// different representations) or two integer types or two type types.
__ Bind(&different_cids);
__ CompareImmediate(cid1, kNumPredefinedCids);
__ b(not_equal, HI);
@ -1304,20 +1312,29 @@ static void EquivalentClassIds(Assembler* assembler,
JumpIfNotInteger(assembler, cid1, scratch, &not_integer);
// First type is an integer. Check if the second is an integer too.
// Otherwise types are unequiv because only integers have the same runtime
// type as other integers.
JumpIfInteger(assembler, cid2, scratch, equal);
// Integer types are only equivalent to other integer types.
__ b(not_equal);
__ Bind(&not_integer);
// Check if the first type is String. If it is not then types are not
// equivalent because they have different class ids and they are not strings
// or integers.
JumpIfNotString(assembler, cid1, scratch, not_equal);
// Check if both are String types.
JumpIfNotString(assembler, cid1, scratch, &not_integer_or_string);
// First type is String. Check if the second is a string too.
JumpIfString(assembler, cid2, scratch, equal);
// String types are only equivalent to other String types.
__ b(not_equal);
__ Bind(&not_integer_or_string);
// Check if the first type is a Type. If it is not then types are not
// equivalent because they have different class ids and they are not String
// or integer or Type.
JumpIfNotType(assembler, cid1, scratch, not_equal);
// First type is a Type. Check if the second is a Type too.
JumpIfType(assembler, cid2, scratch, equal);
// Type types are only equivalent to other Type types.
__ b(not_equal);
}
void AsmIntrinsifier::ObjectHaveSameRuntimeType(Assembler* assembler,

View file

@ -1340,6 +1340,14 @@ static void JumpIfNotString(Assembler* assembler,
kIfNotInRange, target);
}
static void JumpIfType(Assembler* assembler,
Register cid,
Register tmp,
Label* target) {
RangeCheck(assembler, cid, tmp, kTypeCid, kFunctionTypeCid, kIfInRange,
target);
}
static void JumpIfNotType(Assembler* assembler,
Register cid,
Register tmp,
@ -1418,7 +1426,7 @@ static void EquivalentClassIds(Assembler* assembler,
Register cid1,
Register cid2,
Register scratch) {
Label different_cids, not_integer;
Label different_cids, not_integer, not_integer_or_string;
// Check if left hand side is a closure. Closures are handled in the runtime.
__ CompareImmediate(cid1, kClosureCid);
@ -1442,7 +1450,7 @@ static void EquivalentClassIds(Assembler* assembler,
__ b(equal);
// Class ids are different. Check if we are comparing two string types (with
// different representations) or two integer types.
// different representations) or two integer types or two type types.
__ Bind(&different_cids);
__ CompareImmediate(cid1, kNumPredefinedCids);
__ b(not_equal, HI);
@ -1451,20 +1459,28 @@ static void EquivalentClassIds(Assembler* assembler,
JumpIfNotInteger(assembler, cid1, scratch, &not_integer);
// First type is an integer. Check if the second is an integer too.
// Otherwise types are unequiv because only integers have the same runtime
// type as other integers.
JumpIfInteger(assembler, cid2, scratch, equal);
// Integer types are only equivalent to other integer types.
__ b(not_equal);
__ Bind(&not_integer);
// Check if the first type is String. If it is not then types are not
// equivalent because they have different class ids and they are not strings
// or integers.
JumpIfNotString(assembler, cid1, scratch, not_equal);
// Check if both are String types.
JumpIfNotString(assembler, cid1, scratch, &not_integer_or_string);
// First type is String. Check if the second is a string too.
JumpIfString(assembler, cid2, scratch, equal);
// String types are only equivalent to other String types.
// Fall-through to the not equal case.
__ b(not_equal);
__ Bind(&not_integer_or_string);
// Check if the first type is a Type. If it is not then types are not
// equivalent because they have different class ids and they are not String
// or integer or Type.
JumpIfNotType(assembler, cid1, scratch, not_equal);
// First type is a Type. Check if the second is a Type too.
JumpIfType(assembler, cid2, scratch, equal);
// Type types are only equivalent to other Type types.
__ b(not_equal);
}

View file

@ -1281,6 +1281,10 @@ static void JumpIfNotString(Assembler* assembler, Register cid, Label* target) {
kIfNotInRange, target);
}
static void JumpIfType(Assembler* assembler, Register cid, Label* target) {
RangeCheck(assembler, cid, kTypeCid, kFunctionTypeCid, kIfInRange, target);
}
static void JumpIfNotType(Assembler* assembler, Register cid, Label* target) {
RangeCheck(assembler, cid, kTypeCid, kFunctionTypeCid, kIfNotInRange, target);
}
@ -1362,7 +1366,7 @@ static void EquivalentClassIds(Assembler* assembler,
Register cid1,
Register cid2,
Register scratch) {
Label different_cids, not_integer;
Label different_cids, not_integer, not_integer_or_string;
// Check if left hand side is a closure. Closures are handled in the runtime.
__ cmpl(cid1, Immediate(kClosureCid));
@ -1381,11 +1385,11 @@ static void EquivalentClassIds(Assembler* assembler,
__ movzxw(scratch,
FieldAddress(scratch, target::Class::num_type_arguments_offset()));
__ cmpl(scratch, Immediate(0));
__ j(NOT_EQUAL, normal_ir_body, Assembler::kNearJump);
__ j(NOT_EQUAL, normal_ir_body);
__ jmp(equal);
// Class ids are different. Check if we are comparing two string types (with
// different representations) or two integer types.
// different representations) or two integer types or two type types.
__ Bind(&different_cids);
__ cmpl(cid1, Immediate(kNumPredefinedCids));
__ j(ABOVE_EQUAL, not_equal);
@ -1395,20 +1399,29 @@ static void EquivalentClassIds(Assembler* assembler,
JumpIfNotInteger(assembler, scratch, &not_integer);
// First type is an integer. Check if the second is an integer too.
// Otherwise types are unequiv because only integers have the same runtime
// type as other integers.
JumpIfInteger(assembler, cid2, equal);
// Integer types are only equivalent to other integer types.
__ jmp(not_equal);
__ Bind(&not_integer);
// Check if the first type is String. If it is not then types are not
// equivalent because they have different class ids and they are not strings
// or integers.
JumpIfNotString(assembler, cid1, not_equal);
// First type is String. Check if the second is a string too.
// Check if both are String types.
JumpIfNotString(assembler, cid1, &not_integer_or_string);
// First type is a String. Check if the second is a String too.
JumpIfString(assembler, cid2, equal);
// String types are only equivalent to other String types.
__ jmp(not_equal);
__ Bind(&not_integer_or_string);
// Check if the first type is a Type. If it is not then types are not
// equivalent because they have different class ids and they are not String
// or integer or Type.
JumpIfNotType(assembler, cid1, not_equal);
// First type is a Type. Check if the second is a Type too.
JumpIfType(assembler, cid2, equal);
// Type types are only equivalent to other Type types.
__ jmp(not_equal);
}
void AsmIntrinsifier::ObjectHaveSameRuntimeType(Assembler* assembler,

View file

@ -1183,9 +1183,14 @@ static void JumpIfNotString(Assembler* assembler, Register cid, Label* target) {
kIfNotInRange, target);
}
static void JumpIfType(Assembler* assembler, Register cid, Label* target) {
RangeCheck(assembler, cid, kTypeCid, kFunctionTypeCid, kIfInRange, target);
}
static void JumpIfNotType(Assembler* assembler, Register cid, Label* target) {
RangeCheck(assembler, cid, kTypeCid, kFunctionTypeCid, kIfNotInRange, target);
}
// Return type quickly for simple types (not parameterized and not signature).
void AsmIntrinsifier::ObjectRuntimeType(Assembler* assembler,
Label* normal_ir_body) {
@ -1265,7 +1270,7 @@ static void EquivalentClassIds(Assembler* assembler,
Register cid1,
Register cid2,
Register scratch) {
Label different_cids, not_integer;
Label different_cids, not_integer, not_integer_or_string;
// Check if left hand side is a closure. Closures are handled in the runtime.
__ cmpq(cid1, Immediate(kClosureCid));
@ -1288,7 +1293,7 @@ static void EquivalentClassIds(Assembler* assembler,
__ jmp(equal);
// Class ids are different. Check if we are comparing two string types (with
// different representations) or two integer types.
// different representations) or two integer types or two type types.
__ Bind(&different_cids);
__ cmpq(cid1, Immediate(kNumPredefinedCids));
__ j(ABOVE_EQUAL, not_equal);
@ -1298,20 +1303,29 @@ static void EquivalentClassIds(Assembler* assembler,
JumpIfNotInteger(assembler, scratch, &not_integer);
// First type is an integer. Check if the second is an integer too.
// Otherwise types are unequiv because only integers have the same runtime
// type as other integers.
JumpIfInteger(assembler, cid2, equal);
// Integer types are only equivalent to other integer types.
__ jmp(not_equal);
__ Bind(&not_integer);
// Check if the first type is String. If it is not then types are not
// equivalent because they have different class ids and they are not strings
// or integers.
JumpIfNotString(assembler, cid1, not_equal);
// First type is String. Check if the second is a string too.
// Check if both are String types.
JumpIfNotString(assembler, cid1, &not_integer_or_string);
// First type is a String. Check if the second is a String too.
JumpIfString(assembler, cid2, equal);
// String types are only equivalent to other String types.
__ jmp(not_equal);
__ Bind(&not_integer_or_string);
// Check if the first type is a Type. If it is not then types are not
// equivalent because they have different class ids and they are not String
// or integer or Type.
JumpIfNotType(assembler, cid1, not_equal);
// First type is a Type. Check if the second is a Type too.
JumpIfType(assembler, cid2, equal);
// Type types are only equivalent to other Type types.
__ jmp(not_equal);
}
void AsmIntrinsifier::ObjectHaveSameRuntimeType(Assembler* assembler,

View file

@ -25049,7 +25049,7 @@ ClosurePtr Closure::New() {
}
FunctionTypePtr Closure::GetInstantiatedSignature(Zone* zone) const {
Function& fun = Function::Handle(zone, function());
const Function& fun = Function::Handle(zone, function());
FunctionType& sig = FunctionType::Handle(zone, fun.signature());
TypeArguments& fn_type_args =
TypeArguments::Handle(zone, function_type_arguments());