Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 #include "vm/globals.h" // Needed here to get TARGET_ARCH_X64. | 5 #include "vm/globals.h" // Needed here to get TARGET_ARCH_X64. |
| 6 #if defined(TARGET_ARCH_X64) | 6 #if defined(TARGET_ARCH_X64) |
| 7 | 7 |
| 8 #include "vm/flow_graph_compiler.h" | 8 #include "vm/flow_graph_compiler.h" |
| 9 | 9 |
| 10 #include "vm/ast_printer.h" | 10 #include "vm/ast_printer.h" |
| (...skipping 187 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 198 __ j(EQUAL, is_true); | 198 __ j(EQUAL, is_true); |
| 199 __ jmp(is_false); | 199 __ jmp(is_false); |
| 200 __ Bind(&fall_through); | 200 __ Bind(&fall_through); |
| 201 } | 201 } |
| 202 | 202 |
| 203 | 203 |
| 204 // Clobbers RCX. | 204 // Clobbers RCX. |
| 205 RawSubtypeTestCache* FlowGraphCompiler::GenerateCallSubtypeTestStub( | 205 RawSubtypeTestCache* FlowGraphCompiler::GenerateCallSubtypeTestStub( |
| 206 TypeTestStubKind test_kind, | 206 TypeTestStubKind test_kind, |
| 207 Register instance_reg, | 207 Register instance_reg, |
| 208 Register type_arguments_reg, | 208 Register instantiator_type_arguments_reg, |
| 209 Register function_type_arguments_reg, | |
| 209 Register temp_reg, | 210 Register temp_reg, |
| 210 Label* is_instance_lbl, | 211 Label* is_instance_lbl, |
| 211 Label* is_not_instance_lbl) { | 212 Label* is_not_instance_lbl) { |
| 212 const SubtypeTestCache& type_test_cache = | 213 const SubtypeTestCache& type_test_cache = |
| 213 SubtypeTestCache::ZoneHandle(zone(), SubtypeTestCache::New()); | 214 SubtypeTestCache::ZoneHandle(zone(), SubtypeTestCache::New()); |
| 214 __ LoadUniqueObject(temp_reg, type_test_cache); | 215 __ LoadUniqueObject(temp_reg, type_test_cache); |
| 215 __ pushq(temp_reg); // Subtype test cache. | 216 __ pushq(temp_reg); // Subtype test cache. |
| 216 __ pushq(instance_reg); // Instance. | 217 __ pushq(instance_reg); // Instance. |
| 217 if (test_kind == kTestTypeOneArg) { | 218 if (test_kind == kTestTypeOneArg) { |
| 218 ASSERT(type_arguments_reg == kNoRegister); | 219 ASSERT(instantiator_type_arguments_reg == kNoRegister); |
| 220 ASSERT(function_type_arguments_reg == kNoRegister); | |
| 221 __ PushObject(Object::null_object()); | |
| 219 __ PushObject(Object::null_object()); | 222 __ PushObject(Object::null_object()); |
|
siva
2017/04/10 22:04:55
Do you think it helps to comment as, here and othe
regis
2017/04/11 04:23:08
Done.
| |
| 220 __ Call(*StubCode::Subtype1TestCache_entry()); | 223 __ Call(*StubCode::Subtype1TestCache_entry()); |
| 221 } else if (test_kind == kTestTypeTwoArgs) { | 224 } else if (test_kind == kTestTypeTwoArgs) { |
| 222 ASSERT(type_arguments_reg == kNoRegister); | 225 ASSERT(instantiator_type_arguments_reg == kNoRegister); |
| 226 ASSERT(function_type_arguments_reg == kNoRegister); | |
| 227 __ PushObject(Object::null_object()); | |
| 223 __ PushObject(Object::null_object()); | 228 __ PushObject(Object::null_object()); |
| 224 __ Call(*StubCode::Subtype2TestCache_entry()); | 229 __ Call(*StubCode::Subtype2TestCache_entry()); |
| 225 } else if (test_kind == kTestTypeThreeArgs) { | 230 } else if (test_kind == kTestTypeFourArgs) { |
| 226 __ pushq(type_arguments_reg); | 231 __ pushq(instantiator_type_arguments_reg); |
| 227 __ Call(*StubCode::Subtype3TestCache_entry()); | 232 __ pushq(function_type_arguments_reg); |
| 233 __ Call(*StubCode::Subtype4TestCache_entry()); | |
| 228 } else { | 234 } else { |
| 229 UNREACHABLE(); | 235 UNREACHABLE(); |
| 230 } | 236 } |
| 231 // Result is in RCX: null -> not found, otherwise Bool::True or Bool::False. | 237 // Result is in RCX: null -> not found, otherwise Bool::True or Bool::False. |
| 232 ASSERT(instance_reg != RCX); | 238 ASSERT(instance_reg != RCX); |
| 233 ASSERT(temp_reg != RCX); | 239 ASSERT(temp_reg != RCX); |
| 234 __ popq(instance_reg); // Discard. | 240 __ popq(instance_reg); // Discard. |
| 241 __ popq(instance_reg); // Discard. | |
| 235 __ popq(instance_reg); // Restore receiver. | 242 __ popq(instance_reg); // Restore receiver. |
| 236 __ popq(temp_reg); // Discard. | 243 __ popq(temp_reg); // Discard. |
|
siva
2017/04/10 22:04:55
Does it make sense to use
__ Drop(4, temp_reg);
he
regis
2017/04/11 04:23:08
Replaced first 2 popq with Drop(2), since we resto
| |
| 237 GenerateBoolToJump(RCX, is_instance_lbl, is_not_instance_lbl); | 244 GenerateBoolToJump(RCX, is_instance_lbl, is_not_instance_lbl); |
| 238 return type_test_cache.raw(); | 245 return type_test_cache.raw(); |
| 239 } | 246 } |
| 240 | 247 |
| 241 | 248 |
| 242 // Jumps to labels 'is_instance' or 'is_not_instance' respectively, if | 249 // Jumps to labels 'is_instance' or 'is_not_instance' respectively, if |
| 243 // type test is conclusive, otherwise fallthrough if a type test could not | 250 // type test is conclusive, otherwise fallthrough if a type test could not |
| 244 // be completed. | 251 // be completed. |
| 245 // RAX: instance (must survive). | 252 // RAX: instance (must survive). |
| 246 // Clobbers R10. | 253 // Clobbers R10. |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 300 const Type& object_type = Type::Handle(zone(), Type::ObjectType()); | 307 const Type& object_type = Type::Handle(zone(), Type::ObjectType()); |
| 301 if (object_type.IsSubtypeOf(tp_argument, NULL, NULL, Heap::kOld)) { | 308 if (object_type.IsSubtypeOf(tp_argument, NULL, NULL, Heap::kOld)) { |
| 302 // Instance class test only necessary. | 309 // Instance class test only necessary. |
| 303 return GenerateSubtype1TestCacheLookup( | 310 return GenerateSubtype1TestCacheLookup( |
| 304 token_pos, type_class, is_instance_lbl, is_not_instance_lbl); | 311 token_pos, type_class, is_instance_lbl, is_not_instance_lbl); |
| 305 } | 312 } |
| 306 } | 313 } |
| 307 } | 314 } |
| 308 } | 315 } |
| 309 // Regular subtype test cache involving instance's type arguments. | 316 // Regular subtype test cache involving instance's type arguments. |
| 310 const Register kTypeArgumentsReg = kNoRegister; | 317 const Register kInstantiatorTypeArgumentsReg = kNoRegister; |
| 318 const Register kFunctionTypeArgumentsReg = kNoRegister; | |
| 311 const Register kTempReg = R10; | 319 const Register kTempReg = R10; |
| 312 return GenerateCallSubtypeTestStub(kTestTypeTwoArgs, kInstanceReg, | 320 return GenerateCallSubtypeTestStub(kTestTypeTwoArgs, kInstanceReg, |
| 313 kTypeArgumentsReg, kTempReg, | 321 kInstantiatorTypeArgumentsReg, |
| 322 kFunctionTypeArgumentsReg, kTempReg, | |
| 314 is_instance_lbl, is_not_instance_lbl); | 323 is_instance_lbl, is_not_instance_lbl); |
| 315 } | 324 } |
| 316 | 325 |
| 317 | 326 |
| 318 void FlowGraphCompiler::CheckClassIds(Register class_id_reg, | 327 void FlowGraphCompiler::CheckClassIds(Register class_id_reg, |
| 319 const GrowableArray<intptr_t>& class_ids, | 328 const GrowableArray<intptr_t>& class_ids, |
| 320 Label* is_equal_lbl, | 329 Label* is_equal_lbl, |
| 321 Label* is_not_equal_lbl) { | 330 Label* is_not_equal_lbl) { |
| 322 for (intptr_t i = 0; i < class_ids.length(); i++) { | 331 for (intptr_t i = 0; i < class_ids.length(); i++) { |
| 323 __ cmpl(class_id_reg, Immediate(class_ids[i])); | 332 __ cmpl(class_id_reg, Immediate(class_ids[i])); |
| (...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 410 __ Comment("Subtype1TestCacheLookup"); | 419 __ Comment("Subtype1TestCacheLookup"); |
| 411 const Register kInstanceReg = RAX; | 420 const Register kInstanceReg = RAX; |
| 412 __ LoadClass(R10, kInstanceReg); | 421 __ LoadClass(R10, kInstanceReg); |
| 413 // R10: instance class. | 422 // R10: instance class. |
| 414 // Check immediate superclass equality. | 423 // Check immediate superclass equality. |
| 415 __ movq(R13, FieldAddress(R10, Class::super_type_offset())); | 424 __ movq(R13, FieldAddress(R10, Class::super_type_offset())); |
| 416 __ movq(R13, FieldAddress(R13, Type::type_class_id_offset())); | 425 __ movq(R13, FieldAddress(R13, Type::type_class_id_offset())); |
| 417 __ CompareImmediate(R13, Immediate(Smi::RawValue(type_class.id()))); | 426 __ CompareImmediate(R13, Immediate(Smi::RawValue(type_class.id()))); |
| 418 __ j(EQUAL, is_instance_lbl); | 427 __ j(EQUAL, is_instance_lbl); |
| 419 | 428 |
| 420 const Register kTypeArgumentsReg = kNoRegister; | 429 const Register kInstantiatorTypeArgumentsReg = kNoRegister; |
| 430 const Register kFunctionTypeArgumentsReg = kNoRegister; | |
| 421 const Register kTempReg = R10; | 431 const Register kTempReg = R10; |
| 422 return GenerateCallSubtypeTestStub(kTestTypeOneArg, kInstanceReg, | 432 return GenerateCallSubtypeTestStub(kTestTypeOneArg, kInstanceReg, |
| 423 kTypeArgumentsReg, kTempReg, | 433 kInstantiatorTypeArgumentsReg, |
| 434 kFunctionTypeArgumentsReg, kTempReg, | |
| 424 is_instance_lbl, is_not_instance_lbl); | 435 is_instance_lbl, is_not_instance_lbl); |
| 425 } | 436 } |
| 426 | 437 |
| 427 | 438 |
| 428 // Generates inlined check if 'type' is a type parameter or type itself | 439 // Generates inlined check if 'type' is a type parameter or type itself |
| 429 // RAX: instance (preserved). | 440 // RAX: instance (preserved). |
| 430 // Clobbers RDI, RDX, R10. | 441 // Clobbers RDI, RDX, R10. |
| 431 RawSubtypeTestCache* FlowGraphCompiler::GenerateUninstantiatedTypeTest( | 442 RawSubtypeTestCache* FlowGraphCompiler::GenerateUninstantiatedTypeTest( |
| 432 TokenPosition token_pos, | 443 TokenPosition token_pos, |
| 433 const AbstractType& type, | 444 const AbstractType& type, |
| 434 Label* is_instance_lbl, | 445 Label* is_instance_lbl, |
| 435 Label* is_not_instance_lbl) { | 446 Label* is_not_instance_lbl) { |
| 436 __ Comment("UninstantiatedTypeTest"); | 447 __ Comment("UninstantiatedTypeTest"); |
| 437 ASSERT(!type.IsInstantiated()); | 448 ASSERT(!type.IsInstantiated()); |
| 438 // Skip check if destination is a dynamic type. | 449 // Skip check if destination is a dynamic type. |
| 439 if (type.IsTypeParameter()) { | 450 if (type.IsTypeParameter()) { |
| 440 const TypeParameter& type_param = TypeParameter::Cast(type); | 451 const TypeParameter& type_param = TypeParameter::Cast(type); |
| 441 // Load instantiator type arguments on stack. | 452 __ movq(RDX, Address(RSP, 1 * kWordSize)); // Get instantiator type args. |
| 442 __ movq(RDX, Address(RSP, 0)); // Get instantiator type arguments. | 453 __ movq(RCX, Address(RSP, 0 * kWordSize)); // Get function type args. |
| 443 // RDX: instantiator type arguments. | 454 // RDX: instantiator type arguments. |
| 455 // RCX: function type arguments. | |
| 456 const Register kTypeArgumentsReg = | |
| 457 type_param.IsClassTypeParameter() ? RDX : RCX; | |
| 444 // Check if type arguments are null, i.e. equivalent to vector of dynamic. | 458 // Check if type arguments are null, i.e. equivalent to vector of dynamic. |
| 445 __ CompareObject(RDX, Object::null_object()); | 459 __ CompareObject(kTypeArgumentsReg, Object::null_object()); |
| 446 __ j(EQUAL, is_instance_lbl); | 460 __ j(EQUAL, is_instance_lbl); |
| 447 __ movq(RDI, FieldAddress( | 461 __ movq(RDI, FieldAddress(kTypeArgumentsReg, TypeArguments::type_at_offset( |
| 448 RDX, TypeArguments::type_at_offset(type_param.index()))); | 462 type_param.index()))); |
| 449 // RDI: Concrete type of type. | 463 // RDI: Concrete type of type. |
| 450 // Check if type argument is dynamic. | 464 // Check if type argument is dynamic. |
| 451 __ CompareObject(RDI, Object::dynamic_type()); | 465 __ CompareObject(RDI, Object::dynamic_type()); |
| 452 __ j(EQUAL, is_instance_lbl); | 466 __ j(EQUAL, is_instance_lbl); |
| 453 const Type& object_type = Type::ZoneHandle(zone(), Type::ObjectType()); | 467 const Type& object_type = Type::ZoneHandle(zone(), Type::ObjectType()); |
| 454 __ CompareObject(RDI, object_type); | 468 __ CompareObject(RDI, object_type); |
| 455 __ j(EQUAL, is_instance_lbl); | 469 __ j(EQUAL, is_instance_lbl); |
| 470 // TODO(regis): Optimize void type as well once allowed as type argument. | |
| 456 | 471 |
| 457 // For Smi check quickly against int and num interfaces. | 472 // For Smi check quickly against int and num interfaces. |
| 458 Label not_smi; | 473 Label not_smi; |
| 459 __ testq(RAX, Immediate(kSmiTagMask)); // Value is Smi? | 474 __ testq(RAX, Immediate(kSmiTagMask)); // Value is Smi? |
| 460 __ j(NOT_ZERO, ¬_smi, Assembler::kNearJump); | 475 __ j(NOT_ZERO, ¬_smi, Assembler::kNearJump); |
| 461 __ CompareObject(RDI, Type::ZoneHandle(zone(), Type::IntType())); | 476 __ CompareObject(RDI, Type::ZoneHandle(zone(), Type::IntType())); |
| 462 __ j(EQUAL, is_instance_lbl); | 477 __ j(EQUAL, is_instance_lbl); |
| 463 __ CompareObject(RDI, Type::ZoneHandle(zone(), Type::Number())); | 478 __ CompareObject(RDI, Type::ZoneHandle(zone(), Type::Number())); |
| 464 __ j(EQUAL, is_instance_lbl); | 479 __ j(EQUAL, is_instance_lbl); |
| 465 // Smi must be handled in runtime. | 480 // Smi must be handled in runtime. |
| 466 Label fall_through; | 481 Label fall_through; |
| 467 __ jmp(&fall_through); | 482 __ jmp(&fall_through); |
| 468 | 483 |
| 469 __ Bind(¬_smi); | 484 __ Bind(¬_smi); |
| 485 // RAX: instance. | |
| 470 // RDX: instantiator type arguments. | 486 // RDX: instantiator type arguments. |
| 471 // RAX: instance. | 487 // RCX: function type arguments. |
| 472 const Register kInstanceReg = RAX; | 488 const Register kInstanceReg = RAX; |
| 473 const Register kTypeArgumentsReg = RDX; | 489 const Register kInstantiatorTypeArgumentsReg = RDX; |
| 490 const Register kFunctionTypeArgumentsReg = RCX; | |
| 474 const Register kTempReg = R10; | 491 const Register kTempReg = R10; |
| 475 const SubtypeTestCache& type_test_cache = SubtypeTestCache::ZoneHandle( | 492 const SubtypeTestCache& type_test_cache = SubtypeTestCache::ZoneHandle( |
| 476 zone(), GenerateCallSubtypeTestStub( | 493 zone(), GenerateCallSubtypeTestStub( |
| 477 kTestTypeThreeArgs, kInstanceReg, kTypeArgumentsReg, | 494 kTestTypeFourArgs, kInstanceReg, |
| 495 kInstantiatorTypeArgumentsReg, kFunctionTypeArgumentsReg, | |
| 478 kTempReg, is_instance_lbl, is_not_instance_lbl)); | 496 kTempReg, is_instance_lbl, is_not_instance_lbl)); |
| 479 __ Bind(&fall_through); | 497 __ Bind(&fall_through); |
| 480 return type_test_cache.raw(); | 498 return type_test_cache.raw(); |
| 481 } | 499 } |
| 482 if (type.IsType()) { | 500 if (type.IsType()) { |
| 483 const Register kInstanceReg = RAX; | 501 const Register kInstanceReg = RAX; |
| 484 const Register kTypeArgumentsReg = RDX; | 502 const Register kInstantiatorTypeArgumentsReg = RDX; |
| 503 const Register kFunctionTypeArgumentsReg = RCX; | |
| 485 __ testq(kInstanceReg, Immediate(kSmiTagMask)); // Is instance Smi? | 504 __ testq(kInstanceReg, Immediate(kSmiTagMask)); // Is instance Smi? |
| 486 __ j(ZERO, is_not_instance_lbl); | 505 __ j(ZERO, is_not_instance_lbl); |
| 487 __ movq(kTypeArgumentsReg, Address(RSP, 0)); // Instantiator type args. | 506 __ movq(kInstantiatorTypeArgumentsReg, Address(RSP, 1 * kWordSize)); |
| 507 __ movq(kFunctionTypeArgumentsReg, Address(RSP, 0 * kWordSize)); | |
| 488 // Uninstantiated type class is known at compile time, but the type | 508 // Uninstantiated type class is known at compile time, but the type |
| 489 // arguments are determined at runtime by the instantiator. | 509 // arguments are determined at runtime by the instantiator(s). |
| 490 const Register kTempReg = R10; | 510 const Register kTempReg = R10; |
| 491 return GenerateCallSubtypeTestStub(kTestTypeThreeArgs, kInstanceReg, | 511 return GenerateCallSubtypeTestStub(kTestTypeFourArgs, kInstanceReg, |
| 492 kTypeArgumentsReg, kTempReg, | 512 kInstantiatorTypeArgumentsReg, |
| 513 kFunctionTypeArgumentsReg, kTempReg, | |
| 493 is_instance_lbl, is_not_instance_lbl); | 514 is_instance_lbl, is_not_instance_lbl); |
| 494 } | 515 } |
| 495 return SubtypeTestCache::null(); | 516 return SubtypeTestCache::null(); |
| 496 } | 517 } |
| 497 | 518 |
| 498 | 519 |
| 499 // Inputs: | 520 // Inputs: |
| 500 // - RAX: instance to test against (preserved). | 521 // - RAX: instance to test against (preserved). |
| 501 // - RDX: optional instantiator type arguments (preserved). | 522 // - RDX: optional instantiator type arguments (preserved). |
| 523 // - RCX: optional function type arguments (preserved). | |
| 502 // Clobbers R10, R13. | 524 // Clobbers R10, R13. |
| 503 // Returns: | 525 // Returns: |
| 504 // - preserved instance in RAX and optional instantiator type arguments in RDX. | 526 // - preserved instance in RAX, optional instantiator type arguments in RDX, and |
| 527 // optional function type arguments in RCX. | |
| 505 // Note that this inlined code must be followed by the runtime_call code, as it | 528 // Note that this inlined code must be followed by the runtime_call code, as it |
| 506 // may fall through to it. Otherwise, this inline code will jump to the label | 529 // may fall through to it. Otherwise, this inline code will jump to the label |
| 507 // is_instance or to the label is_not_instance. | 530 // is_instance or to the label is_not_instance. |
| 508 RawSubtypeTestCache* FlowGraphCompiler::GenerateInlineInstanceof( | 531 RawSubtypeTestCache* FlowGraphCompiler::GenerateInlineInstanceof( |
| 509 TokenPosition token_pos, | 532 TokenPosition token_pos, |
| 510 const AbstractType& type, | 533 const AbstractType& type, |
| 511 Label* is_instance_lbl, | 534 Label* is_instance_lbl, |
| 512 Label* is_not_instance_lbl) { | 535 Label* is_not_instance_lbl) { |
| 513 __ Comment("InlineInstanceof"); | 536 __ Comment("InlineInstanceof"); |
| 514 if (type.IsVoidType()) { | 537 if (type.IsVoidType()) { |
| (...skipping 28 matching lines...) Expand all Loading... | |
| 543 | 566 |
| 544 | 567 |
| 545 // If instanceof type test cannot be performed successfully at compile time and | 568 // If instanceof type test cannot be performed successfully at compile time and |
| 546 // therefore eliminated, optimize it by adding inlined tests for: | 569 // therefore eliminated, optimize it by adding inlined tests for: |
| 547 // - NULL -> return type == Null (type is not Object or dynamic). | 570 // - NULL -> return type == Null (type is not Object or dynamic). |
| 548 // - Smi -> compile time subtype check (only if dst class is not parameterized). | 571 // - Smi -> compile time subtype check (only if dst class is not parameterized). |
| 549 // - Class equality (only if class is not parameterized). | 572 // - Class equality (only if class is not parameterized). |
| 550 // Inputs: | 573 // Inputs: |
| 551 // - RAX: object. | 574 // - RAX: object. |
| 552 // - RDX: instantiator type arguments or raw_null. | 575 // - RDX: instantiator type arguments or raw_null. |
| 553 // Clobbers RDX. | 576 // - RCX: function type arguments or raw_null. |
| 554 // Returns: | 577 // Returns: |
| 555 // - true or false in RAX. | 578 // - true or false in RAX. |
| 556 void FlowGraphCompiler::GenerateInstanceOf(TokenPosition token_pos, | 579 void FlowGraphCompiler::GenerateInstanceOf(TokenPosition token_pos, |
| 557 intptr_t deopt_id, | 580 intptr_t deopt_id, |
| 558 const AbstractType& type, | 581 const AbstractType& type, |
| 559 LocationSummary* locs) { | 582 LocationSummary* locs) { |
| 560 ASSERT(type.IsFinalized() && !type.IsMalformedOrMalbounded()); | 583 ASSERT(type.IsFinalized() && !type.IsMalformedOrMalbounded()); |
| 561 ASSERT(!type.IsObjectType() && !type.IsDynamicType()); | 584 ASSERT(!type.IsObjectType() && !type.IsDynamicType()); |
| 562 | 585 |
| 586 __ pushq(RDX); // Store instantiator type arguments. | |
| 587 __ pushq(RCX); // Store function type arguments. | |
| 588 | |
| 563 Label is_instance, is_not_instance; | 589 Label is_instance, is_not_instance; |
| 564 __ pushq(RDX); // Store instantiator type arguments. | |
| 565 // If type is instantiated and non-parameterized, we can inline code | 590 // If type is instantiated and non-parameterized, we can inline code |
| 566 // checking whether the tested instance is a Smi. | 591 // checking whether the tested instance is a Smi. |
| 567 if (type.IsInstantiated()) { | 592 if (type.IsInstantiated()) { |
| 568 // A null object is only an instance of Null, Object, and dynamic. | 593 // A null object is only an instance of Null, Object, and dynamic. |
| 569 // Object and dynamic have already been checked above (if the type is | 594 // Object and dynamic have already been checked above (if the type is |
| 570 // instantiated). So we can return false here if the instance is null, | 595 // instantiated). So we can return false here if the instance is null, |
| 571 // unless the type is Null (and if the type is instantiated). | 596 // unless the type is Null (and if the type is instantiated). |
| 572 // We can only inline this null check if the type is instantiated at compile | 597 // We can only inline this null check if the type is instantiated at compile |
| 573 // time, since an uninstantiated type at compile time could be Null, Object, | 598 // time, since an uninstantiated type at compile time could be Null, Object, |
| 574 // or dynamic at run time. | 599 // or dynamic at run time. |
| 575 __ CompareObject(RAX, Object::null_object()); | 600 __ CompareObject(RAX, Object::null_object()); |
| 576 __ j(EQUAL, type.IsNullType() ? &is_instance : &is_not_instance); | 601 __ j(EQUAL, type.IsNullType() ? &is_instance : &is_not_instance); |
| 577 } | 602 } |
| 578 | 603 |
| 579 // Generate inline instanceof test. | 604 // Generate inline instanceof test. |
| 580 SubtypeTestCache& test_cache = SubtypeTestCache::ZoneHandle(zone()); | 605 SubtypeTestCache& test_cache = SubtypeTestCache::ZoneHandle(zone()); |
| 581 test_cache = | 606 test_cache = |
| 582 GenerateInlineInstanceof(token_pos, type, &is_instance, &is_not_instance); | 607 GenerateInlineInstanceof(token_pos, type, &is_instance, &is_not_instance); |
| 583 | 608 |
| 584 // test_cache is null if there is no fall-through. | 609 // test_cache is null if there is no fall-through. |
| 585 Label done; | 610 Label done; |
| 586 if (!test_cache.IsNull()) { | 611 if (!test_cache.IsNull()) { |
| 587 // Generate runtime call. | 612 // Generate runtime call. |
| 588 __ movq(RDX, Address(RSP, 0)); // Get instantiator type arguments. | 613 __ movq(RDX, Address(RSP, 1 * kWordSize)); // Get instantiator type args. |
| 614 __ movq(RCX, Address(RSP, 0 * kWordSize)); // Get function type args. | |
| 589 __ PushObject(Object::null_object()); // Make room for the result. | 615 __ PushObject(Object::null_object()); // Make room for the result. |
| 590 __ pushq(RAX); // Push the instance. | 616 __ pushq(RAX); // Push the instance. |
| 591 __ PushObject(type); // Push the type. | 617 __ PushObject(type); // Push the type. |
| 592 __ pushq(RDX); // Instantiator type arguments. | 618 __ pushq(RDX); // Instantiator type arguments. |
| 619 __ pushq(RCX); // Function type arguments. | |
| 593 __ LoadUniqueObject(RAX, test_cache); | 620 __ LoadUniqueObject(RAX, test_cache); |
| 594 __ pushq(RAX); | 621 __ pushq(RAX); |
| 595 GenerateRuntimeCall(token_pos, deopt_id, kInstanceofRuntimeEntry, 4, locs); | 622 GenerateRuntimeCall(token_pos, deopt_id, kInstanceofRuntimeEntry, 5, locs); |
| 596 // Pop the parameters supplied to the runtime entry. The result of the | 623 // Pop the parameters supplied to the runtime entry. The result of the |
| 597 // instanceof runtime call will be left as the result of the operation. | 624 // instanceof runtime call will be left as the result of the operation. |
| 598 __ Drop(4); | 625 __ Drop(5); |
| 599 __ popq(RAX); | 626 __ popq(RAX); |
| 600 __ jmp(&done, Assembler::kNearJump); | 627 __ jmp(&done, Assembler::kNearJump); |
| 601 } | 628 } |
| 602 __ Bind(&is_not_instance); | 629 __ Bind(&is_not_instance); |
| 603 __ LoadObject(RAX, Bool::Get(false)); | 630 __ LoadObject(RAX, Bool::Get(false)); |
| 604 __ jmp(&done, Assembler::kNearJump); | 631 __ jmp(&done, Assembler::kNearJump); |
| 605 | 632 |
| 606 __ Bind(&is_instance); | 633 __ Bind(&is_instance); |
| 607 __ LoadObject(RAX, Bool::Get(true)); | 634 __ LoadObject(RAX, Bool::Get(true)); |
| 608 __ Bind(&done); | 635 __ Bind(&done); |
| 636 __ popq(RCX); // Remove pushed function type arguments. | |
| 609 __ popq(RDX); // Remove pushed instantiator type arguments. | 637 __ popq(RDX); // Remove pushed instantiator type arguments. |
| 610 } | 638 } |
| 611 | 639 |
| 612 | 640 |
| 613 // Optimize assignable type check by adding inlined tests for: | 641 // Optimize assignable type check by adding inlined tests for: |
| 614 // - NULL -> return NULL. | 642 // - NULL -> return NULL. |
| 615 // - Smi -> compile time subtype check (only if dst class is not parameterized). | 643 // - Smi -> compile time subtype check (only if dst class is not parameterized). |
| 616 // - Class equality (only if class is not parameterized). | 644 // - Class equality (only if class is not parameterized). |
| 617 // Inputs: | 645 // Inputs: |
| 618 // - RAX: object. | 646 // - RAX: object. |
| 619 // - RDX: instantiator type arguments or raw_null. | 647 // - RDX: instantiator type arguments or raw_null. |
| 648 // - RCX: function type arguments or raw_null. | |
| 620 // Returns: | 649 // Returns: |
| 621 // - object in RAX for successful assignable check (or throws TypeError). | 650 // - object in RAX for successful assignable check (or throws TypeError). |
| 622 // Performance notes: positive checks must be quick, negative checks can be slow | 651 // Performance notes: positive checks must be quick, negative checks can be slow |
| 623 // as they throw an exception. | 652 // as they throw an exception. |
| 624 void FlowGraphCompiler::GenerateAssertAssignable(TokenPosition token_pos, | 653 void FlowGraphCompiler::GenerateAssertAssignable(TokenPosition token_pos, |
| 625 intptr_t deopt_id, | 654 intptr_t deopt_id, |
| 626 const AbstractType& dst_type, | 655 const AbstractType& dst_type, |
| 627 const String& dst_name, | 656 const String& dst_name, |
| 628 LocationSummary* locs) { | 657 LocationSummary* locs) { |
| 629 ASSERT(!token_pos.IsClassifying()); | 658 ASSERT(!token_pos.IsClassifying()); |
| 630 ASSERT(!dst_type.IsNull()); | 659 ASSERT(!dst_type.IsNull()); |
| 631 ASSERT(dst_type.IsFinalized()); | 660 ASSERT(dst_type.IsFinalized()); |
| 632 // Assignable check is skipped in FlowGraphBuilder, not here. | 661 // Assignable check is skipped in FlowGraphBuilder, not here. |
| 633 ASSERT(dst_type.IsMalformedOrMalbounded() || | 662 ASSERT(dst_type.IsMalformedOrMalbounded() || |
| 634 (!dst_type.IsDynamicType() && !dst_type.IsObjectType())); | 663 (!dst_type.IsDynamicType() && !dst_type.IsObjectType())); |
| 635 __ pushq(RDX); // Store instantiator type arguments. | 664 __ pushq(RDX); // Store instantiator type arguments. |
| 665 __ pushq(RCX); // Store function type arguments. | |
| 636 // A null object is always assignable and is returned as result. | 666 // A null object is always assignable and is returned as result. |
| 637 Label is_assignable, runtime_call; | 667 Label is_assignable, runtime_call; |
| 638 __ CompareObject(RAX, Object::null_object()); | 668 __ CompareObject(RAX, Object::null_object()); |
| 639 __ j(EQUAL, &is_assignable); | 669 __ j(EQUAL, &is_assignable); |
| 640 | 670 |
| 641 // Generate throw new TypeError() if the type is malformed or malbounded. | 671 // Generate throw new TypeError() if the type is malformed or malbounded. |
| 642 if (dst_type.IsMalformedOrMalbounded()) { | 672 if (dst_type.IsMalformedOrMalbounded()) { |
| 643 __ PushObject(Object::null_object()); // Make room for the result. | 673 __ PushObject(Object::null_object()); // Make room for the result. |
| 644 __ pushq(RAX); // Push the source object. | 674 __ pushq(RAX); // Push the source object. |
| 645 __ PushObject(dst_name); // Push the name of the destination. | 675 __ PushObject(dst_name); // Push the name of the destination. |
| 646 __ PushObject(dst_type); // Push the type of the destination. | 676 __ PushObject(dst_type); // Push the type of the destination. |
| 647 GenerateRuntimeCall(token_pos, deopt_id, kBadTypeErrorRuntimeEntry, 3, | 677 GenerateRuntimeCall(token_pos, deopt_id, kBadTypeErrorRuntimeEntry, 3, |
| 648 locs); | 678 locs); |
| 649 // We should never return here. | 679 // We should never return here. |
| 650 __ int3(); | 680 __ int3(); |
| 651 | 681 |
| 652 __ Bind(&is_assignable); // For a null object. | 682 __ Bind(&is_assignable); // For a null object. |
| 683 __ popq(RCX); // Remove pushed function type arguments. | |
| 653 __ popq(RDX); // Remove pushed instantiator type arguments. | 684 __ popq(RDX); // Remove pushed instantiator type arguments. |
| 654 return; | 685 return; |
| 655 } | 686 } |
| 656 | 687 |
| 657 // Generate inline type check, linking to runtime call if not assignable. | 688 // Generate inline type check, linking to runtime call if not assignable. |
| 658 SubtypeTestCache& test_cache = SubtypeTestCache::ZoneHandle(zone()); | 689 SubtypeTestCache& test_cache = SubtypeTestCache::ZoneHandle(zone()); |
| 659 test_cache = GenerateInlineInstanceof(token_pos, dst_type, &is_assignable, | 690 test_cache = GenerateInlineInstanceof(token_pos, dst_type, &is_assignable, |
| 660 &runtime_call); | 691 &runtime_call); |
| 661 | 692 |
| 662 __ Bind(&runtime_call); | 693 __ Bind(&runtime_call); |
| 663 __ movq(RDX, Address(RSP, 0)); // Get instantiator type arguments. | 694 __ movq(RDX, Address(RSP, 1 * kWordSize)); // Get instantiator type args. |
| 695 __ movq(RCX, Address(RSP, 0 * kWordSize)); // Get function type args. | |
| 664 __ PushObject(Object::null_object()); // Make room for the result. | 696 __ PushObject(Object::null_object()); // Make room for the result. |
| 665 __ pushq(RAX); // Push the source object. | 697 __ pushq(RAX); // Push the source object. |
| 666 __ PushObject(dst_type); // Push the type of the destination. | 698 __ PushObject(dst_type); // Push the type of the destination. |
| 667 __ pushq(RDX); // Instantiator type arguments. | 699 __ pushq(RDX); // Instantiator type arguments. |
| 700 __ pushq(RCX); // Function type arguments. | |
| 668 __ PushObject(dst_name); // Push the name of the destination. | 701 __ PushObject(dst_name); // Push the name of the destination. |
| 669 __ LoadUniqueObject(RAX, test_cache); | 702 __ LoadUniqueObject(RAX, test_cache); |
| 670 __ pushq(RAX); | 703 __ pushq(RAX); |
| 671 GenerateRuntimeCall(token_pos, deopt_id, kTypeCheckRuntimeEntry, 5, locs); | 704 GenerateRuntimeCall(token_pos, deopt_id, kTypeCheckRuntimeEntry, 6, locs); |
| 672 // Pop the parameters supplied to the runtime entry. The result of the | 705 // Pop the parameters supplied to the runtime entry. The result of the |
| 673 // type check runtime call is the checked value. | 706 // type check runtime call is the checked value. |
| 674 __ Drop(5); | 707 __ Drop(6); |
| 675 __ popq(RAX); | 708 __ popq(RAX); |
| 676 | 709 |
| 677 __ Bind(&is_assignable); | 710 __ Bind(&is_assignable); |
| 711 __ popq(RCX); // Remove pushed function type arguments. | |
| 678 __ popq(RDX); // Remove pushed instantiator type arguments. | 712 __ popq(RDX); // Remove pushed instantiator type arguments. |
| 679 } | 713 } |
| 680 | 714 |
| 681 | 715 |
| 682 void FlowGraphCompiler::EmitInstructionEpilogue(Instruction* instr) { | 716 void FlowGraphCompiler::EmitInstructionEpilogue(Instruction* instr) { |
| 683 if (is_optimizing()) { | 717 if (is_optimizing()) { |
| 684 return; | 718 return; |
| 685 } | 719 } |
| 686 Definition* defn = instr->AsDefinition(); | 720 Definition* defn = instr->AsDefinition(); |
| 687 if ((defn != NULL) && defn->HasTemp()) { | 721 if ((defn != NULL) && defn->HasTemp()) { |
| (...skipping 1073 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1761 __ movups(reg, Address(RSP, 0)); | 1795 __ movups(reg, Address(RSP, 0)); |
| 1762 __ AddImmediate(RSP, Immediate(kFpuRegisterSize)); | 1796 __ AddImmediate(RSP, Immediate(kFpuRegisterSize)); |
| 1763 } | 1797 } |
| 1764 | 1798 |
| 1765 | 1799 |
| 1766 #undef __ | 1800 #undef __ |
| 1767 | 1801 |
| 1768 } // namespace dart | 1802 } // namespace dart |
| 1769 | 1803 |
| 1770 #endif // defined TARGET_ARCH_X64 | 1804 #endif // defined TARGET_ARCH_X64 |
| OLD | NEW |