Index: runtime/vm/flow_graph_compiler_arm64.cc |
diff --git a/runtime/vm/flow_graph_compiler_arm64.cc b/runtime/vm/flow_graph_compiler_arm64.cc |
index 6f02236c483f9568f99b6bb6f4168a35dad99857..3a8310a89778ba27fdb203f1192c3f534445cbed 100644 |
--- a/runtime/vm/flow_graph_compiler_arm64.cc |
+++ b/runtime/vm/flow_graph_compiler_arm64.cc |
@@ -202,29 +202,32 @@ void FlowGraphCompiler::GenerateBoolToJump(Register bool_register, |
// R0: instance (must be preserved). |
// R1: instantiator type arguments (if used). |
+// R2: function type arguments (if used). |
RawSubtypeTestCache* FlowGraphCompiler::GenerateCallSubtypeTestStub( |
TypeTestStubKind test_kind, |
Register instance_reg, |
- Register type_arguments_reg, |
+ Register instantiator_type_arguments_reg, |
+ Register function_type_arguments_reg, |
Register temp_reg, |
Label* is_instance_lbl, |
Label* is_not_instance_lbl) { |
ASSERT(instance_reg == R0); |
- ASSERT(temp_reg == kNoRegister); // Unused on ARM. |
+ ASSERT(temp_reg == kNoRegister); // Unused on ARM64. |
const SubtypeTestCache& type_test_cache = |
SubtypeTestCache::ZoneHandle(zone(), SubtypeTestCache::New()); |
- __ LoadUniqueObject(R2, type_test_cache); |
+ __ LoadUniqueObject(R3, type_test_cache); |
if (test_kind == kTestTypeOneArg) { |
- ASSERT(type_arguments_reg == kNoRegister); |
- __ LoadObject(R1, Object::null_object()); |
+ ASSERT(instantiator_type_arguments_reg == kNoRegister); |
+ ASSERT(function_type_arguments_reg == kNoRegister); |
__ BranchLink(*StubCode::Subtype1TestCache_entry()); |
} else if (test_kind == kTestTypeTwoArgs) { |
- ASSERT(type_arguments_reg == kNoRegister); |
- __ LoadObject(R1, Object::null_object()); |
+ ASSERT(instantiator_type_arguments_reg == kNoRegister); |
+ ASSERT(function_type_arguments_reg == kNoRegister); |
__ BranchLink(*StubCode::Subtype2TestCache_entry()); |
- } else if (test_kind == kTestTypeThreeArgs) { |
- ASSERT(type_arguments_reg == R1); |
- __ BranchLink(*StubCode::Subtype3TestCache_entry()); |
+ } else if (test_kind == kTestTypeFourArgs) { |
+ ASSERT(instantiator_type_arguments_reg == R1); |
+ ASSERT(function_type_arguments_reg == R2); |
+ __ BranchLink(*StubCode::Subtype4TestCache_entry()); |
} else { |
UNREACHABLE(); |
} |
@@ -238,7 +241,7 @@ RawSubtypeTestCache* FlowGraphCompiler::GenerateCallSubtypeTestStub( |
// type test is conclusive, otherwise fallthrough if a type test could not |
// be completed. |
// R0: instance being type checked (preserved). |
-// Clobbers R2. |
+// Clobbers R1, R2. |
RawSubtypeTestCache* |
FlowGraphCompiler::GenerateInstantiatedTypeWithArgumentsTest( |
TokenPosition token_pos, |
@@ -302,11 +305,13 @@ FlowGraphCompiler::GenerateInstantiatedTypeWithArgumentsTest( |
} |
} |
// Regular subtype test cache involving instance's type arguments. |
- const Register kTypeArgumentsReg = kNoRegister; |
+ const Register kInstantiatorTypeArgumentsReg = kNoRegister; |
+ const Register kFunctionTypeArgumentsReg = kNoRegister; |
const Register kTempReg = kNoRegister; |
// R0: instance (must be preserved). |
return GenerateCallSubtypeTestStub(kTestTypeTwoArgs, kInstanceReg, |
- kTypeArgumentsReg, kTempReg, |
+ kInstantiatorTypeArgumentsReg, |
+ kFunctionTypeArgumentsReg, kTempReg, |
is_instance_lbl, is_not_instance_lbl); |
} |
@@ -413,10 +418,12 @@ RawSubtypeTestCache* FlowGraphCompiler::GenerateSubtype1TestCacheLookup( |
__ CompareImmediate(R2, Smi::RawValue(type_class.id())); |
__ b(is_instance_lbl, EQ); |
- const Register kTypeArgumentsReg = kNoRegister; |
+ const Register kInstantiatorTypeArgumentsReg = kNoRegister; |
+ const Register kFunctionTypeArgumentsReg = kNoRegister; |
const Register kTempReg = kNoRegister; |
return GenerateCallSubtypeTestStub(kTestTypeOneArg, kInstanceReg, |
- kTypeArgumentsReg, kTempReg, |
+ kInstantiatorTypeArgumentsReg, |
+ kFunctionTypeArgumentsReg, kTempReg, |
is_instance_lbl, is_not_instance_lbl); |
} |
@@ -433,28 +440,32 @@ RawSubtypeTestCache* FlowGraphCompiler::GenerateUninstantiatedTypeTest( |
// Skip check if destination is a dynamic type. |
if (type.IsTypeParameter()) { |
const TypeParameter& type_param = TypeParameter::Cast(type); |
- // Load instantiator type arguments on stack. |
- __ ldr(R1, Address(SP)); // Get instantiator type arguments. |
+ // Get instantiator type args (high, R1) and function type args (low, R2). |
+ __ ldp(R2, R1, Address(SP, 0 * kWordSize, Address::PairOffset)); |
// R1: instantiator type arguments. |
+ // R2: function type arguments. |
+ const Register kTypeArgumentsReg = |
+ type_param.IsClassTypeParameter() ? R1 : R2; |
// Check if type arguments are null, i.e. equivalent to vector of dynamic. |
- __ CompareObject(R1, Object::null_object()); |
+ __ CompareObject(kTypeArgumentsReg, Object::null_object()); |
__ b(is_instance_lbl, EQ); |
- __ LoadFieldFromOffset(R2, R1, |
+ __ LoadFieldFromOffset(R3, kTypeArgumentsReg, |
TypeArguments::type_at_offset(type_param.index())); |
- // R2: concrete type of type. |
+ // R3: concrete type of type. |
// Check if type argument is dynamic. |
- __ CompareObject(R2, Object::dynamic_type()); |
+ __ CompareObject(R3, Object::dynamic_type()); |
__ b(is_instance_lbl, EQ); |
- __ CompareObject(R2, Type::ZoneHandle(zone(), Type::ObjectType())); |
+ __ CompareObject(R3, Type::ZoneHandle(zone(), Type::ObjectType())); |
__ b(is_instance_lbl, EQ); |
+ // TODO(regis): Optimize void type as well once allowed as type argument. |
// For Smi check quickly against int and num interfaces. |
Label not_smi; |
__ tsti(R0, Immediate(kSmiTagMask)); // Value is Smi? |
__ b(¬_smi, NE); |
- __ CompareObject(R2, Type::ZoneHandle(zone(), Type::IntType())); |
+ __ CompareObject(R3, Type::ZoneHandle(zone(), Type::IntType())); |
__ b(is_instance_lbl, EQ); |
- __ CompareObject(R2, Type::ZoneHandle(zone(), Type::Number())); |
+ __ CompareObject(R3, Type::ZoneHandle(zone(), Type::Number())); |
__ b(is_instance_lbl, EQ); |
// Smi must be handled in runtime. |
Label fall_through; |
@@ -462,28 +473,34 @@ RawSubtypeTestCache* FlowGraphCompiler::GenerateUninstantiatedTypeTest( |
__ Bind(¬_smi); |
// R1: instantiator type arguments. |
+ // R2: function type arguments. |
// R0: instance. |
const Register kInstanceReg = R0; |
- const Register kTypeArgumentsReg = R1; |
+ const Register kInstantiatorTypeArgumentsReg = R1; |
+ const Register kFunctionTypeArgumentsReg = R2; |
const Register kTempReg = kNoRegister; |
const SubtypeTestCache& type_test_cache = SubtypeTestCache::ZoneHandle( |
zone(), GenerateCallSubtypeTestStub( |
- kTestTypeThreeArgs, kInstanceReg, kTypeArgumentsReg, |
+ kTestTypeFourArgs, kInstanceReg, |
+ kInstantiatorTypeArgumentsReg, kFunctionTypeArgumentsReg, |
kTempReg, is_instance_lbl, is_not_instance_lbl)); |
__ Bind(&fall_through); |
return type_test_cache.raw(); |
} |
if (type.IsType()) { |
const Register kInstanceReg = R0; |
- const Register kTypeArgumentsReg = R1; |
+ const Register kInstantiatorTypeArgumentsReg = R1; |
+ const Register kFunctionTypeArgumentsReg = R2; |
__ tsti(kInstanceReg, Immediate(kSmiTagMask)); // Is instance Smi? |
__ b(is_not_instance_lbl, EQ); |
- __ ldr(kTypeArgumentsReg, Address(SP)); // Instantiator type args. |
+ __ ldp(kFunctionTypeArgumentsReg, kInstantiatorTypeArgumentsReg, |
+ Address(SP, 0 * kWordSize, Address::PairOffset)); |
// Uninstantiated type class is known at compile time, but the type |
// arguments are determined at runtime by the instantiator. |
const Register kTempReg = kNoRegister; |
- return GenerateCallSubtypeTestStub(kTestTypeThreeArgs, kInstanceReg, |
- kTypeArgumentsReg, kTempReg, |
+ return GenerateCallSubtypeTestStub(kTestTypeFourArgs, kInstanceReg, |
+ kInstantiatorTypeArgumentsReg, |
+ kFunctionTypeArgumentsReg, kTempReg, |
is_instance_lbl, is_not_instance_lbl); |
} |
return SubtypeTestCache::null(); |
@@ -493,9 +510,11 @@ RawSubtypeTestCache* FlowGraphCompiler::GenerateUninstantiatedTypeTest( |
// Inputs: |
// - R0: instance being type checked (preserved). |
// - R1: optional instantiator type arguments (preserved). |
-// Clobbers R2, R3. |
+// - R2: optional function type arguments (preserved). |
+// Clobbers R3, R4, R8, R9. |
// Returns: |
-// - preserved instance in R0 and optional instantiator type arguments in R1. |
+// - preserved instance in R0, optional instantiator type arguments in R1, and |
+// optional function type arguments in R2. |
// Note that this inlined code must be followed by the runtime_call code, as it |
// may fall through to it. Otherwise, this inline code will jump to the label |
// is_instance or to the label is_not_instance. |
@@ -544,6 +563,7 @@ RawSubtypeTestCache* FlowGraphCompiler::GenerateInlineInstanceof( |
// Inputs: |
// - R0: object. |
// - R1: instantiator type arguments or raw_null. |
+// - R2: function type arguments or raw_null. |
// Returns: |
// - true or false in R0. |
void FlowGraphCompiler::GenerateInstanceOf(TokenPosition token_pos, |
@@ -552,10 +572,9 @@ void FlowGraphCompiler::GenerateInstanceOf(TokenPosition token_pos, |
LocationSummary* locs) { |
ASSERT(type.IsFinalized() && !type.IsMalformed() && !type.IsMalbounded()); |
ASSERT(!type.IsObjectType() && !type.IsDynamicType()); |
- |
- // Preserve instantiator type arguments (R1). |
- __ Push(R1); |
- |
+ const Register kInstantiatorTypeArgumentsReg = R1; |
+ const Register kFunctionTypeArgumentsReg = R2; |
+ __ PushPair(kFunctionTypeArgumentsReg, kInstantiatorTypeArgumentsReg); |
Label is_instance, is_not_instance; |
// If type is instantiated and non-parameterized, we can inline code |
// checking whether the tested instance is a Smi. |
@@ -580,18 +599,20 @@ void FlowGraphCompiler::GenerateInstanceOf(TokenPosition token_pos, |
Label done; |
if (!test_cache.IsNull()) { |
// Generate runtime call. |
- // Load instantiator (R2) and its type arguments (R1). |
- __ ldr(R1, Address(SP, 0 * kWordSize)); |
+ const Register kInstantiatorTypeArgumentsReg = R1; |
+ const Register kFunctionTypeArgumentsReg = R2; |
+ __ ldp(kFunctionTypeArgumentsReg, kInstantiatorTypeArgumentsReg, |
+ Address(SP, 0 * kWordSize, Address::PairOffset)); |
__ PushObject(Object::null_object()); // Make room for the result. |
__ Push(R0); // Push the instance. |
__ PushObject(type); // Push the type. |
- __ Push(R1); // Push instantiator type arguments (R1). |
+ __ PushPair(kFunctionTypeArgumentsReg, kInstantiatorTypeArgumentsReg); |
__ LoadUniqueObject(R0, test_cache); |
__ Push(R0); |
- GenerateRuntimeCall(token_pos, deopt_id, kInstanceofRuntimeEntry, 4, locs); |
+ GenerateRuntimeCall(token_pos, deopt_id, kInstanceofRuntimeEntry, 5, locs); |
// Pop the parameters supplied to the runtime entry. The result of the |
// instanceof runtime call will be left as the result of the operation. |
- __ Drop(4); |
+ __ Drop(5); |
__ Pop(R0); |
__ b(&done); |
} |
@@ -602,8 +623,8 @@ void FlowGraphCompiler::GenerateInstanceOf(TokenPosition token_pos, |
__ Bind(&is_instance); |
__ LoadObject(R0, Bool::Get(true)); |
__ Bind(&done); |
- // Remove instantiator type arguments (R1). |
- __ Drop(1); |
+ // Remove instantiator type arguments and function type arguments. |
+ __ Drop(2); |
} |
@@ -614,6 +635,7 @@ void FlowGraphCompiler::GenerateInstanceOf(TokenPosition token_pos, |
// Inputs: |
// - R0: instance being type checked. |
// - R1: instantiator type arguments or raw_null. |
+// - R2: function type arguments or raw_null. |
// Returns: |
// - object in R0 for successful assignable check (or throws TypeError). |
// Performance notes: positive checks must be quick, negative checks can be slow |
@@ -629,8 +651,9 @@ void FlowGraphCompiler::GenerateAssertAssignable(TokenPosition token_pos, |
// Assignable check is skipped in FlowGraphBuilder, not here. |
ASSERT(dst_type.IsMalformedOrMalbounded() || |
(!dst_type.IsDynamicType() && !dst_type.IsObjectType())); |
- // Preserve instantiator type arguments (R1). |
- __ Push(R1); |
+ const Register kInstantiatorTypeArgumentsReg = R1; |
+ const Register kFunctionTypeArgumentsReg = R2; |
+ __ PushPair(kFunctionTypeArgumentsReg, kInstantiatorTypeArgumentsReg); |
// A null object is always assignable and is returned as result. |
Label is_assignable, runtime_call; |
__ CompareObject(R0, Object::null_object()); |
@@ -648,8 +671,7 @@ void FlowGraphCompiler::GenerateAssertAssignable(TokenPosition token_pos, |
__ brk(0); |
__ Bind(&is_assignable); // For a null object. |
- // Restore instantiator type arguments (R1). |
- __ Pop(R1); |
+ __ PopPair(kFunctionTypeArgumentsReg, kInstantiatorTypeArgumentsReg); |
return; |
} |
@@ -659,23 +681,23 @@ void FlowGraphCompiler::GenerateAssertAssignable(TokenPosition token_pos, |
&runtime_call); |
__ Bind(&runtime_call); |
- __ ldr(R1, Address(SP)); // Load instantiator type arguments (R1). |
+ __ ldp(kFunctionTypeArgumentsReg, kInstantiatorTypeArgumentsReg, |
+ Address(SP, 0 * kWordSize, Address::PairOffset)); |
__ PushObject(Object::null_object()); // Make room for the result. |
__ Push(R0); // Push the source object. |
__ PushObject(dst_type); // Push the type of the destination. |
- __ Push(R1); // Push instantiator type arguments (R1). |
+ __ PushPair(kFunctionTypeArgumentsReg, kInstantiatorTypeArgumentsReg); |
__ PushObject(dst_name); // Push the name of the destination. |
__ LoadUniqueObject(R0, test_cache); |
__ Push(R0); |
- GenerateRuntimeCall(token_pos, deopt_id, kTypeCheckRuntimeEntry, 5, locs); |
+ GenerateRuntimeCall(token_pos, deopt_id, kTypeCheckRuntimeEntry, 6, locs); |
// Pop the parameters supplied to the runtime entry. The result of the |
// type check runtime call is the checked value. |
- __ Drop(5); |
+ __ Drop(6); |
__ Pop(R0); |
__ Bind(&is_assignable); |
- // Restore instantiator type arguments (R1). |
- __ Pop(R1); |
+ __ PopPair(kFunctionTypeArgumentsReg, kInstantiatorTypeArgumentsReg); |
} |