mirror of
https://github.com/dart-lang/sdk
synced 2024-11-02 12:24:24 +00:00
[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:
parent
b666964948
commit
3ff71e0a47
6 changed files with 114 additions and 42 deletions
|
@ -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(
|
||||
|
|
|
@ -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, ¬_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(¬_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, ¬_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(¬_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,
|
||||
|
|
|
@ -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, ¬_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(¬_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, ¬_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(¬_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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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, ¬_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(¬_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, ¬_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(¬_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,
|
||||
|
|
|
@ -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, ¬_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(¬_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, ¬_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(¬_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,
|
||||
|
|
|
@ -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());
|
||||
|
|
Loading…
Reference in a new issue