| Index: runtime/vm/flow_graph_compiler_x64.cc
|
| diff --git a/runtime/vm/flow_graph_compiler_x64.cc b/runtime/vm/flow_graph_compiler_x64.cc
|
| index ae6d6105b9853aec50e4d1f5bd5014daa8f22ddf..7e92a96a2fef7bd04a8865137dc861517cd628e1 100644
|
| --- a/runtime/vm/flow_graph_compiler_x64.cc
|
| +++ b/runtime/vm/flow_graph_compiler_x64.cc
|
| @@ -205,7 +205,8 @@ void FlowGraphCompiler::GenerateBoolToJump(Register bool_register,
|
| 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) {
|
| @@ -215,23 +216,28 @@ RawSubtypeTestCache* FlowGraphCompiler::GenerateCallSubtypeTestStub(
|
| __ pushq(temp_reg); // Subtype test cache.
|
| __ pushq(instance_reg); // Instance.
|
| if (test_kind == kTestTypeOneArg) {
|
| - ASSERT(type_arguments_reg == kNoRegister);
|
| - __ PushObject(Object::null_object());
|
| + ASSERT(instantiator_type_arguments_reg == kNoRegister);
|
| + ASSERT(function_type_arguments_reg == kNoRegister);
|
| + __ PushObject(Object::null_object()); // Unused instantiator type args.
|
| + __ PushObject(Object::null_object()); // Unused function type args.
|
| __ Call(*StubCode::Subtype1TestCache_entry());
|
| } else if (test_kind == kTestTypeTwoArgs) {
|
| - ASSERT(type_arguments_reg == kNoRegister);
|
| - __ PushObject(Object::null_object());
|
| + ASSERT(instantiator_type_arguments_reg == kNoRegister);
|
| + ASSERT(function_type_arguments_reg == kNoRegister);
|
| + __ PushObject(Object::null_object()); // Unused instantiator type args.
|
| + __ PushObject(Object::null_object()); // Unused function type args.
|
| __ Call(*StubCode::Subtype2TestCache_entry());
|
| - } else if (test_kind == kTestTypeThreeArgs) {
|
| - __ pushq(type_arguments_reg);
|
| - __ Call(*StubCode::Subtype3TestCache_entry());
|
| + } else if (test_kind == kTestTypeFourArgs) {
|
| + __ pushq(instantiator_type_arguments_reg);
|
| + __ pushq(function_type_arguments_reg);
|
| + __ Call(*StubCode::Subtype4TestCache_entry());
|
| } else {
|
| UNREACHABLE();
|
| }
|
| // Result is in RCX: null -> not found, otherwise Bool::True or Bool::False.
|
| ASSERT(instance_reg != RCX);
|
| ASSERT(temp_reg != RCX);
|
| - __ popq(instance_reg); // Discard.
|
| + __ Drop(2);
|
| __ popq(instance_reg); // Restore receiver.
|
| __ popq(temp_reg); // Discard.
|
| GenerateBoolToJump(RCX, is_instance_lbl, is_not_instance_lbl);
|
| @@ -307,10 +313,12 @@ 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 = R10;
|
| return GenerateCallSubtypeTestStub(kTestTypeTwoArgs, kInstanceReg,
|
| - kTypeArgumentsReg, kTempReg,
|
| + kInstantiatorTypeArgumentsReg,
|
| + kFunctionTypeArgumentsReg, kTempReg,
|
| is_instance_lbl, is_not_instance_lbl);
|
| }
|
|
|
| @@ -417,10 +425,12 @@ RawSubtypeTestCache* FlowGraphCompiler::GenerateSubtype1TestCacheLookup(
|
| __ CompareImmediate(R13, Immediate(Smi::RawValue(type_class.id())));
|
| __ j(EQUAL, is_instance_lbl);
|
|
|
| - const Register kTypeArgumentsReg = kNoRegister;
|
| + const Register kInstantiatorTypeArgumentsReg = kNoRegister;
|
| + const Register kFunctionTypeArgumentsReg = kNoRegister;
|
| const Register kTempReg = R10;
|
| return GenerateCallSubtypeTestStub(kTestTypeOneArg, kInstanceReg,
|
| - kTypeArgumentsReg, kTempReg,
|
| + kInstantiatorTypeArgumentsReg,
|
| + kFunctionTypeArgumentsReg, kTempReg,
|
| is_instance_lbl, is_not_instance_lbl);
|
| }
|
|
|
| @@ -438,14 +448,17 @@ 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.
|
| - __ movq(RDX, Address(RSP, 0)); // Get instantiator type arguments.
|
| + __ movq(RDX, Address(RSP, 1 * kWordSize)); // Get instantiator type args.
|
| + __ movq(RCX, Address(RSP, 0 * kWordSize)); // Get function type args.
|
| // RDX: instantiator type arguments.
|
| + // RCX: function type arguments.
|
| + const Register kTypeArgumentsReg =
|
| + type_param.IsClassTypeParameter() ? RDX : RCX;
|
| // Check if type arguments are null, i.e. equivalent to vector of dynamic.
|
| - __ CompareObject(RDX, Object::null_object());
|
| + __ CompareObject(kTypeArgumentsReg, Object::null_object());
|
| __ j(EQUAL, is_instance_lbl);
|
| - __ movq(RDI, FieldAddress(
|
| - RDX, TypeArguments::type_at_offset(type_param.index())));
|
| + __ movq(RDI, FieldAddress(kTypeArgumentsReg, TypeArguments::type_at_offset(
|
| + type_param.index())));
|
| // RDI: Concrete type of type.
|
| // Check if type argument is dynamic.
|
| __ CompareObject(RDI, Object::dynamic_type());
|
| @@ -453,6 +466,7 @@ RawSubtypeTestCache* FlowGraphCompiler::GenerateUninstantiatedTypeTest(
|
| 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.
|
|
|
| // For Smi check quickly against int and num interfaces.
|
| Label not_smi;
|
| @@ -467,29 +481,35 @@ RawSubtypeTestCache* FlowGraphCompiler::GenerateUninstantiatedTypeTest(
|
| __ jmp(&fall_through);
|
|
|
| __ Bind(¬_smi);
|
| - // RDX: instantiator type arguments.
|
| // RAX: instance.
|
| + // RDX: instantiator type arguments.
|
| + // RCX: function type arguments.
|
| const Register kInstanceReg = RAX;
|
| - const Register kTypeArgumentsReg = RDX;
|
| + const Register kInstantiatorTypeArgumentsReg = RDX;
|
| + const Register kFunctionTypeArgumentsReg = RCX;
|
| const Register kTempReg = R10;
|
| 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 = RAX;
|
| - const Register kTypeArgumentsReg = RDX;
|
| + const Register kInstantiatorTypeArgumentsReg = RDX;
|
| + const Register kFunctionTypeArgumentsReg = RCX;
|
| __ testq(kInstanceReg, Immediate(kSmiTagMask)); // Is instance Smi?
|
| __ j(ZERO, is_not_instance_lbl);
|
| - __ movq(kTypeArgumentsReg, Address(RSP, 0)); // Instantiator type args.
|
| + __ movq(kInstantiatorTypeArgumentsReg, Address(RSP, 1 * kWordSize));
|
| + __ movq(kFunctionTypeArgumentsReg, Address(RSP, 0 * kWordSize));
|
| // Uninstantiated type class is known at compile time, but the type
|
| - // arguments are determined at runtime by the instantiator.
|
| + // arguments are determined at runtime by the instantiator(s).
|
| const Register kTempReg = R10;
|
| - return GenerateCallSubtypeTestStub(kTestTypeThreeArgs, kInstanceReg,
|
| - kTypeArgumentsReg, kTempReg,
|
| + return GenerateCallSubtypeTestStub(kTestTypeFourArgs, kInstanceReg,
|
| + kInstantiatorTypeArgumentsReg,
|
| + kFunctionTypeArgumentsReg, kTempReg,
|
| is_instance_lbl, is_not_instance_lbl);
|
| }
|
| return SubtypeTestCache::null();
|
| @@ -499,9 +519,11 @@ RawSubtypeTestCache* FlowGraphCompiler::GenerateUninstantiatedTypeTest(
|
| // Inputs:
|
| // - RAX: instance to test against (preserved).
|
| // - RDX: optional instantiator type arguments (preserved).
|
| +// - RCX: optional function type arguments (preserved).
|
| // Clobbers R10, R13.
|
| // Returns:
|
| -// - preserved instance in RAX and optional instantiator type arguments in RDX.
|
| +// - preserved instance in RAX, optional instantiator type arguments in RDX, and
|
| +// optional function type arguments in RCX.
|
| // 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.
|
| @@ -550,7 +572,7 @@ RawSubtypeTestCache* FlowGraphCompiler::GenerateInlineInstanceof(
|
| // Inputs:
|
| // - RAX: object.
|
| // - RDX: instantiator type arguments or raw_null.
|
| -// Clobbers RDX.
|
| +// - RCX: function type arguments or raw_null.
|
| // Returns:
|
| // - true or false in RAX.
|
| void FlowGraphCompiler::GenerateInstanceOf(TokenPosition token_pos,
|
| @@ -560,8 +582,10 @@ void FlowGraphCompiler::GenerateInstanceOf(TokenPosition token_pos,
|
| ASSERT(type.IsFinalized() && !type.IsMalformedOrMalbounded());
|
| ASSERT(!type.IsObjectType() && !type.IsDynamicType());
|
|
|
| - Label is_instance, is_not_instance;
|
| __ pushq(RDX); // Store instantiator type arguments.
|
| + __ pushq(RCX); // Store function type arguments.
|
| +
|
| + 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.
|
| if (type.IsInstantiated()) {
|
| @@ -585,17 +609,19 @@ void FlowGraphCompiler::GenerateInstanceOf(TokenPosition token_pos,
|
| Label done;
|
| if (!test_cache.IsNull()) {
|
| // Generate runtime call.
|
| - __ movq(RDX, Address(RSP, 0)); // Get instantiator type arguments.
|
| + __ movq(RDX, Address(RSP, 1 * kWordSize)); // Get instantiator type args.
|
| + __ movq(RCX, Address(RSP, 0 * kWordSize)); // Get function type args.
|
| __ PushObject(Object::null_object()); // Make room for the result.
|
| __ pushq(RAX); // Push the instance.
|
| __ PushObject(type); // Push the type.
|
| __ pushq(RDX); // Instantiator type arguments.
|
| + __ pushq(RCX); // Function type arguments.
|
| __ LoadUniqueObject(RAX, test_cache);
|
| __ pushq(RAX);
|
| - 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);
|
| __ popq(RAX);
|
| __ jmp(&done, Assembler::kNearJump);
|
| }
|
| @@ -606,6 +632,7 @@ void FlowGraphCompiler::GenerateInstanceOf(TokenPosition token_pos,
|
| __ Bind(&is_instance);
|
| __ LoadObject(RAX, Bool::Get(true));
|
| __ Bind(&done);
|
| + __ popq(RCX); // Remove pushed function type arguments.
|
| __ popq(RDX); // Remove pushed instantiator type arguments.
|
| }
|
|
|
| @@ -617,6 +644,7 @@ void FlowGraphCompiler::GenerateInstanceOf(TokenPosition token_pos,
|
| // Inputs:
|
| // - RAX: object.
|
| // - RDX: instantiator type arguments or raw_null.
|
| +// - RCX: function type arguments or raw_null.
|
| // Returns:
|
| // - object in RAX for successful assignable check (or throws TypeError).
|
| // Performance notes: positive checks must be quick, negative checks can be slow
|
| @@ -633,6 +661,7 @@ void FlowGraphCompiler::GenerateAssertAssignable(TokenPosition token_pos,
|
| ASSERT(dst_type.IsMalformedOrMalbounded() ||
|
| (!dst_type.IsDynamicType() && !dst_type.IsObjectType()));
|
| __ pushq(RDX); // Store instantiator type arguments.
|
| + __ pushq(RCX); // Store function type arguments.
|
| // A null object is always assignable and is returned as result.
|
| Label is_assignable, runtime_call;
|
| __ CompareObject(RAX, Object::null_object());
|
| @@ -650,6 +679,7 @@ void FlowGraphCompiler::GenerateAssertAssignable(TokenPosition token_pos,
|
| __ int3();
|
|
|
| __ Bind(&is_assignable); // For a null object.
|
| + __ popq(RCX); // Remove pushed function type arguments.
|
| __ popq(RDX); // Remove pushed instantiator type arguments.
|
| return;
|
| }
|
| @@ -660,21 +690,24 @@ void FlowGraphCompiler::GenerateAssertAssignable(TokenPosition token_pos,
|
| &runtime_call);
|
|
|
| __ Bind(&runtime_call);
|
| - __ movq(RDX, Address(RSP, 0)); // Get instantiator type arguments.
|
| + __ movq(RDX, Address(RSP, 1 * kWordSize)); // Get instantiator type args.
|
| + __ movq(RCX, Address(RSP, 0 * kWordSize)); // Get function type args.
|
| __ PushObject(Object::null_object()); // Make room for the result.
|
| __ pushq(RAX); // Push the source object.
|
| __ PushObject(dst_type); // Push the type of the destination.
|
| __ pushq(RDX); // Instantiator type arguments.
|
| + __ pushq(RCX); // Function type arguments.
|
| __ PushObject(dst_name); // Push the name of the destination.
|
| __ LoadUniqueObject(RAX, test_cache);
|
| __ pushq(RAX);
|
| - 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);
|
| __ popq(RAX);
|
|
|
| __ Bind(&is_assignable);
|
| + __ popq(RCX); // Remove pushed function type arguments.
|
| __ popq(RDX); // Remove pushed instantiator type arguments.
|
| }
|
|
|
|
|