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_ARM. | 5 #include "vm/globals.h" // Needed here to get TARGET_ARCH_ARM. |
6 #if defined(TARGET_ARCH_ARM) | 6 #if defined(TARGET_ARCH_ARM) |
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 190 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
201 __ CompareObject(bool_register, Object::null_object()); | 201 __ CompareObject(bool_register, Object::null_object()); |
202 __ b(&fall_through, EQ); | 202 __ b(&fall_through, EQ); |
203 __ CompareObject(bool_register, Bool::True()); | 203 __ CompareObject(bool_register, Bool::True()); |
204 __ b(is_true, EQ); | 204 __ b(is_true, EQ); |
205 __ b(is_false); | 205 __ b(is_false); |
206 __ Bind(&fall_through); | 206 __ Bind(&fall_through); |
207 } | 207 } |
208 | 208 |
209 | 209 |
210 // R0: instance (must be preserved). | 210 // R0: instance (must be preserved). |
211 // R1: instantiator type arguments (if used). | 211 // R2: instantiator type arguments (if used). |
212 // R2: function type arguments (if used). | 212 // R1: function type arguments (if used). |
213 // R3: type test cache. | 213 // R3: type test cache. |
214 RawSubtypeTestCache* FlowGraphCompiler::GenerateCallSubtypeTestStub( | 214 RawSubtypeTestCache* FlowGraphCompiler::GenerateCallSubtypeTestStub( |
215 TypeTestStubKind test_kind, | 215 TypeTestStubKind test_kind, |
216 Register instance_reg, | 216 Register instance_reg, |
217 Register instantiator_type_arguments_reg, | 217 Register instantiator_type_arguments_reg, |
218 Register function_type_arguments_reg, | 218 Register function_type_arguments_reg, |
219 Register temp_reg, | 219 Register temp_reg, |
220 Label* is_instance_lbl, | 220 Label* is_instance_lbl, |
221 Label* is_not_instance_lbl) { | 221 Label* is_not_instance_lbl) { |
222 ASSERT(instance_reg == R0); | 222 ASSERT(instance_reg == R0); |
223 ASSERT(temp_reg == kNoRegister); // Unused on ARM. | 223 ASSERT(temp_reg == kNoRegister); // Unused on ARM. |
224 const SubtypeTestCache& type_test_cache = | 224 const SubtypeTestCache& type_test_cache = |
225 SubtypeTestCache::ZoneHandle(zone(), SubtypeTestCache::New()); | 225 SubtypeTestCache::ZoneHandle(zone(), SubtypeTestCache::New()); |
226 __ LoadUniqueObject(R3, type_test_cache); | 226 __ LoadUniqueObject(R3, type_test_cache); |
227 if (test_kind == kTestTypeOneArg) { | 227 if (test_kind == kTestTypeOneArg) { |
228 ASSERT(instantiator_type_arguments_reg == kNoRegister); | 228 ASSERT(instantiator_type_arguments_reg == kNoRegister); |
229 ASSERT(function_type_arguments_reg == kNoRegister); | 229 ASSERT(function_type_arguments_reg == kNoRegister); |
230 __ BranchLink(*StubCode::Subtype1TestCache_entry()); | 230 __ BranchLink(*StubCode::Subtype1TestCache_entry()); |
231 } else if (test_kind == kTestTypeTwoArgs) { | 231 } else if (test_kind == kTestTypeTwoArgs) { |
232 ASSERT(instantiator_type_arguments_reg == kNoRegister); | 232 ASSERT(instantiator_type_arguments_reg == kNoRegister); |
233 ASSERT(function_type_arguments_reg == kNoRegister); | 233 ASSERT(function_type_arguments_reg == kNoRegister); |
234 __ BranchLink(*StubCode::Subtype2TestCache_entry()); | 234 __ BranchLink(*StubCode::Subtype2TestCache_entry()); |
235 } else if (test_kind == kTestTypeFourArgs) { | 235 } else if (test_kind == kTestTypeFourArgs) { |
236 ASSERT(instantiator_type_arguments_reg == R1); | 236 ASSERT(instantiator_type_arguments_reg == R2); |
237 ASSERT(function_type_arguments_reg == R2); | 237 ASSERT(function_type_arguments_reg == R1); |
238 __ BranchLink(*StubCode::Subtype4TestCache_entry()); | 238 __ BranchLink(*StubCode::Subtype4TestCache_entry()); |
239 } else { | 239 } else { |
240 UNREACHABLE(); | 240 UNREACHABLE(); |
241 } | 241 } |
242 // Result is in R1: null -> not found, otherwise Bool::True or Bool::False. | 242 // Result is in R1: null -> not found, otherwise Bool::True or Bool::False. |
243 GenerateBoolToJump(R1, is_instance_lbl, is_not_instance_lbl); | 243 GenerateBoolToJump(R1, is_instance_lbl, is_not_instance_lbl); |
244 return type_test_cache.raw(); | 244 return type_test_cache.raw(); |
245 } | 245 } |
246 | 246 |
247 | 247 |
(...skipping 193 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
441 RawSubtypeTestCache* FlowGraphCompiler::GenerateUninstantiatedTypeTest( | 441 RawSubtypeTestCache* FlowGraphCompiler::GenerateUninstantiatedTypeTest( |
442 TokenPosition token_pos, | 442 TokenPosition token_pos, |
443 const AbstractType& type, | 443 const AbstractType& type, |
444 Label* is_instance_lbl, | 444 Label* is_instance_lbl, |
445 Label* is_not_instance_lbl) { | 445 Label* is_not_instance_lbl) { |
446 __ Comment("UninstantiatedTypeTest"); | 446 __ Comment("UninstantiatedTypeTest"); |
447 ASSERT(!type.IsInstantiated()); | 447 ASSERT(!type.IsInstantiated()); |
448 // Skip check if destination is a dynamic type. | 448 // Skip check if destination is a dynamic type. |
449 if (type.IsTypeParameter()) { | 449 if (type.IsTypeParameter()) { |
450 const TypeParameter& type_param = TypeParameter::Cast(type); | 450 const TypeParameter& type_param = TypeParameter::Cast(type); |
451 __ ldr(R1, Address(SP, 1 * kWordSize)); // Get instantiator type args. | 451 const Register kInstantiatorTypeArgumentsReg = R2; |
452 __ ldr(R2, Address(SP, 0 * kWordSize)); // Get function type args. | 452 const Register kFunctionTypeArgumentsReg = R1; |
453 // TODO(regis): Renumber registers and use ldm. | 453 __ ldm(IA, SP, (1 << kFunctionTypeArgumentsReg) | |
454 // R1: instantiator type arguments. | 454 (1 << kInstantiatorTypeArgumentsReg)); |
455 // R2: function type arguments. | 455 // R2: instantiator type arguments. |
| 456 // R1: function type arguments. |
456 const Register kTypeArgumentsReg = | 457 const Register kTypeArgumentsReg = |
457 type_param.IsClassTypeParameter() ? R1 : R2; | 458 type_param.IsClassTypeParameter() ? R2 : R1; |
458 // Check if type arguments are null, i.e. equivalent to vector of dynamic. | 459 // Check if type arguments are null, i.e. equivalent to vector of dynamic. |
459 __ CompareObject(kTypeArgumentsReg, Object::null_object()); | 460 __ CompareObject(kTypeArgumentsReg, Object::null_object()); |
460 __ b(is_instance_lbl, EQ); | 461 __ b(is_instance_lbl, EQ); |
461 __ ldr(R3, FieldAddress(kTypeArgumentsReg, | 462 __ ldr(R3, FieldAddress(kTypeArgumentsReg, |
462 TypeArguments::type_at_offset(type_param.index()))); | 463 TypeArguments::type_at_offset(type_param.index()))); |
463 // R3: concrete type of type. | 464 // R3: concrete type of type. |
464 // Check if type argument is dynamic. | 465 // Check if type argument is dynamic. |
465 __ CompareObject(R3, Object::dynamic_type()); | 466 __ CompareObject(R3, Object::dynamic_type()); |
466 __ b(is_instance_lbl, EQ); | 467 __ b(is_instance_lbl, EQ); |
467 __ CompareObject(R3, Type::ZoneHandle(zone(), Type::ObjectType())); | 468 __ CompareObject(R3, Type::ZoneHandle(zone(), Type::ObjectType())); |
468 __ b(is_instance_lbl, EQ); | 469 __ b(is_instance_lbl, EQ); |
469 // TODO(regis): Optimize void type as well once allowed as type argument. | 470 // TODO(regis): Optimize void type as well once allowed as type argument. |
470 | 471 |
471 // For Smi check quickly against int and num interfaces. | 472 // For Smi check quickly against int and num interfaces. |
472 Label not_smi; | 473 Label not_smi; |
473 __ tst(R0, Operand(kSmiTagMask)); // Value is Smi? | 474 __ tst(R0, Operand(kSmiTagMask)); // Value is Smi? |
474 __ b(¬_smi, NE); | 475 __ b(¬_smi, NE); |
475 __ CompareObject(R3, Type::ZoneHandle(zone(), Type::IntType())); | 476 __ CompareObject(R3, Type::ZoneHandle(zone(), Type::IntType())); |
476 __ b(is_instance_lbl, EQ); | 477 __ b(is_instance_lbl, EQ); |
477 __ CompareObject(R3, Type::ZoneHandle(zone(), Type::Number())); | 478 __ CompareObject(R3, Type::ZoneHandle(zone(), Type::Number())); |
478 __ b(is_instance_lbl, EQ); | 479 __ b(is_instance_lbl, EQ); |
479 // Smi must be handled in runtime. | 480 // Smi must be handled in runtime. |
480 Label fall_through; | 481 Label fall_through; |
481 __ b(&fall_through); | 482 __ b(&fall_through); |
482 | 483 |
483 __ Bind(¬_smi); | 484 __ Bind(¬_smi); |
484 // R0: instance. | 485 // R0: instance. |
485 // R1: instantiator type arguments. | 486 // R2: instantiator type arguments. |
486 // R2: function type arguments. | 487 // R1: function type arguments. |
487 const Register kInstanceReg = R0; | 488 const Register kInstanceReg = R0; |
488 const Register kInstantiatorTypeArgumentsReg = R1; | |
489 const Register kFunctionTypeArgumentsReg = R2; | |
490 const Register kTempReg = kNoRegister; | 489 const Register kTempReg = kNoRegister; |
491 const SubtypeTestCache& type_test_cache = SubtypeTestCache::ZoneHandle( | 490 const SubtypeTestCache& type_test_cache = SubtypeTestCache::ZoneHandle( |
492 zone(), GenerateCallSubtypeTestStub( | 491 zone(), GenerateCallSubtypeTestStub( |
493 kTestTypeFourArgs, kInstanceReg, | 492 kTestTypeFourArgs, kInstanceReg, |
494 kInstantiatorTypeArgumentsReg, kFunctionTypeArgumentsReg, | 493 kInstantiatorTypeArgumentsReg, kFunctionTypeArgumentsReg, |
495 kTempReg, is_instance_lbl, is_not_instance_lbl)); | 494 kTempReg, is_instance_lbl, is_not_instance_lbl)); |
496 __ Bind(&fall_through); | 495 __ Bind(&fall_through); |
497 return type_test_cache.raw(); | 496 return type_test_cache.raw(); |
498 } | 497 } |
499 if (type.IsType()) { | 498 if (type.IsType()) { |
500 const Register kInstanceReg = R0; | 499 const Register kInstanceReg = R0; |
501 const Register kInstantiatorTypeArgumentsReg = R1; | 500 const Register kInstantiatorTypeArgumentsReg = R2; |
502 const Register kFunctionTypeArgumentsReg = R2; | 501 const Register kFunctionTypeArgumentsReg = R1; |
503 __ tst(kInstanceReg, Operand(kSmiTagMask)); // Is instance Smi? | 502 __ tst(kInstanceReg, Operand(kSmiTagMask)); // Is instance Smi? |
504 __ b(is_not_instance_lbl, EQ); | 503 __ b(is_not_instance_lbl, EQ); |
505 __ ldr(kInstantiatorTypeArgumentsReg, Address(SP, 1 * kWordSize)); | 504 __ ldm(IA, SP, (1 << kFunctionTypeArgumentsReg) | |
506 __ ldr(kFunctionTypeArgumentsReg, Address(SP, 0 * kWordSize)); | 505 (1 << kInstantiatorTypeArgumentsReg)); |
507 // TODO(regis): Renumber registers and use ldm. | |
508 // Uninstantiated type class is known at compile time, but the type | 506 // Uninstantiated type class is known at compile time, but the type |
509 // arguments are determined at runtime by the instantiator(s). | 507 // arguments are determined at runtime by the instantiator(s). |
510 const Register kTempReg = kNoRegister; | 508 const Register kTempReg = kNoRegister; |
511 return GenerateCallSubtypeTestStub(kTestTypeFourArgs, kInstanceReg, | 509 return GenerateCallSubtypeTestStub(kTestTypeFourArgs, kInstanceReg, |
512 kInstantiatorTypeArgumentsReg, | 510 kInstantiatorTypeArgumentsReg, |
513 kFunctionTypeArgumentsReg, kTempReg, | 511 kFunctionTypeArgumentsReg, kTempReg, |
514 is_instance_lbl, is_not_instance_lbl); | 512 is_instance_lbl, is_not_instance_lbl); |
515 } | 513 } |
516 return SubtypeTestCache::null(); | 514 return SubtypeTestCache::null(); |
517 } | 515 } |
518 | 516 |
519 | 517 |
520 // Inputs: | 518 // Inputs: |
521 // - R0: instance being type checked (preserved). | 519 // - R0: instance being type checked (preserved). |
522 // - R1: optional instantiator type arguments (preserved). | 520 // - R2: optional instantiator type arguments (preserved). |
523 // - R2: optional function type arguments (preserved). | 521 // - R1: optional function type arguments (preserved). |
524 // Clobbers R3, R4, R8, R9. | 522 // Clobbers R3, R4, R8, R9. |
525 // Returns: | 523 // Returns: |
526 // - preserved instance in R0, optional instantiator type arguments in R1, and | 524 // - preserved instance in R0, optional instantiator type arguments in R2, and |
527 // optional function type arguments in R2. | 525 // optional function type arguments in R1. |
528 // Note that this inlined code must be followed by the runtime_call code, as it | 526 // Note that this inlined code must be followed by the runtime_call code, as it |
529 // may fall through to it. Otherwise, this inline code will jump to the label | 527 // may fall through to it. Otherwise, this inline code will jump to the label |
530 // is_instance or to the label is_not_instance. | 528 // is_instance or to the label is_not_instance. |
531 RawSubtypeTestCache* FlowGraphCompiler::GenerateInlineInstanceof( | 529 RawSubtypeTestCache* FlowGraphCompiler::GenerateInlineInstanceof( |
532 TokenPosition token_pos, | 530 TokenPosition token_pos, |
533 const AbstractType& type, | 531 const AbstractType& type, |
534 Label* is_instance_lbl, | 532 Label* is_instance_lbl, |
535 Label* is_not_instance_lbl) { | 533 Label* is_not_instance_lbl) { |
536 __ Comment("InlineInstanceof"); | 534 __ Comment("InlineInstanceof"); |
537 if (type.IsVoidType()) { | 535 if (type.IsVoidType()) { |
(...skipping 27 matching lines...) Expand all Loading... |
565 } | 563 } |
566 | 564 |
567 | 565 |
568 // If instanceof type test cannot be performed successfully at compile time and | 566 // If instanceof type test cannot be performed successfully at compile time and |
569 // therefore eliminated, optimize it by adding inlined tests for: | 567 // therefore eliminated, optimize it by adding inlined tests for: |
570 // - NULL -> return type == Null (type is not Object or dynamic). | 568 // - NULL -> return type == Null (type is not Object or dynamic). |
571 // - Smi -> compile time subtype check (only if dst class is not parameterized). | 569 // - Smi -> compile time subtype check (only if dst class is not parameterized). |
572 // - Class equality (only if class is not parameterized). | 570 // - Class equality (only if class is not parameterized). |
573 // Inputs: | 571 // Inputs: |
574 // - R0: object. | 572 // - R0: object. |
575 // - R1: instantiator type arguments or raw_null. | 573 // - R2: instantiator type arguments or raw_null. |
576 // - R2: function type arguments or raw_null. | 574 // - R1: function type arguments or raw_null. |
577 // Returns: | 575 // Returns: |
578 // - true or false in R0. | 576 // - true or false in R0. |
579 void FlowGraphCompiler::GenerateInstanceOf(TokenPosition token_pos, | 577 void FlowGraphCompiler::GenerateInstanceOf(TokenPosition token_pos, |
580 intptr_t deopt_id, | 578 intptr_t deopt_id, |
581 const AbstractType& type, | 579 const AbstractType& type, |
582 LocationSummary* locs) { | 580 LocationSummary* locs) { |
583 ASSERT(type.IsFinalized() && !type.IsMalformed() && !type.IsMalbounded()); | 581 ASSERT(type.IsFinalized() && !type.IsMalformed() && !type.IsMalbounded()); |
584 ASSERT(!type.IsObjectType() && !type.IsDynamicType()); | 582 ASSERT(!type.IsObjectType() && !type.IsDynamicType()); |
585 | 583 const Register kInstantiatorTypeArgumentsReg = R2; |
586 __ Push(R1); // Store instantiator type arguments. | 584 const Register kFunctionTypeArgumentsReg = R1; |
587 __ Push(R2); // Store function type arguments. | 585 __ PushList((1 << kInstantiatorTypeArgumentsReg) | |
588 // TODO(regis): Renumber registers and use PushList. | 586 (1 << kFunctionTypeArgumentsReg)); |
589 | |
590 Label is_instance, is_not_instance; | 587 Label is_instance, is_not_instance; |
591 // If type is instantiated and non-parameterized, we can inline code | 588 // If type is instantiated and non-parameterized, we can inline code |
592 // checking whether the tested instance is a Smi. | 589 // checking whether the tested instance is a Smi. |
593 if (type.IsInstantiated()) { | 590 if (type.IsInstantiated()) { |
594 // A null object is only an instance of Null, Object, and dynamic. | 591 // A null object is only an instance of Null, Object, and dynamic. |
595 // Object and dynamic have already been checked above (if the type is | 592 // Object and dynamic have already been checked above (if the type is |
596 // instantiated). So we can return false here if the instance is null, | 593 // instantiated). So we can return false here if the instance is null, |
597 // unless the type is Null (and if the type is instantiated). | 594 // unless the type is Null (and if the type is instantiated). |
598 // We can only inline this null check if the type is instantiated at compile | 595 // We can only inline this null check if the type is instantiated at compile |
599 // time, since an uninstantiated type at compile time could be Null, Object, | 596 // time, since an uninstantiated type at compile time could be Null, Object, |
600 // or dynamic at run time. | 597 // or dynamic at run time. |
601 __ CompareObject(R0, Object::null_object()); | 598 __ CompareObject(R0, Object::null_object()); |
602 __ b(type.IsNullType() ? &is_instance : &is_not_instance, EQ); | 599 __ b(type.IsNullType() ? &is_instance : &is_not_instance, EQ); |
603 } | 600 } |
604 | 601 |
605 // Generate inline instanceof test. | 602 // Generate inline instanceof test. |
606 SubtypeTestCache& test_cache = SubtypeTestCache::ZoneHandle(zone()); | 603 SubtypeTestCache& test_cache = SubtypeTestCache::ZoneHandle(zone()); |
607 test_cache = | 604 test_cache = |
608 GenerateInlineInstanceof(token_pos, type, &is_instance, &is_not_instance); | 605 GenerateInlineInstanceof(token_pos, type, &is_instance, &is_not_instance); |
609 | 606 |
610 // test_cache is null if there is no fall-through. | 607 // test_cache is null if there is no fall-through. |
611 Label done; | 608 Label done; |
612 if (!test_cache.IsNull()) { | 609 if (!test_cache.IsNull()) { |
613 // Generate runtime call. | 610 // Generate runtime call. |
614 __ ldr(R1, Address(SP, 1 * kWordSize)); // Get instantiator type args. | 611 __ ldm(IA, SP, (1 << kFunctionTypeArgumentsReg) | |
615 __ ldr(R2, Address(SP, 0 * kWordSize)); // Get function type args. | 612 (1 << kInstantiatorTypeArgumentsReg)); |
616 // TODO(regis): Renumber registers and use ldm. | |
617 __ PushObject(Object::null_object()); // Make room for the result. | 613 __ PushObject(Object::null_object()); // Make room for the result. |
618 __ Push(R0); // Push the instance. | 614 __ Push(R0); // Push the instance. |
619 __ PushObject(type); // Push the type. | 615 __ PushObject(type); // Push the type. |
620 __ Push(R1); // Instantiator type arguments. | 616 __ PushList((1 << kInstantiatorTypeArgumentsReg) | |
621 __ Push(R2); // Function type arguments. | 617 (1 << kFunctionTypeArgumentsReg)); |
622 // TODO(regis): Renumber registers and use PushList. | |
623 __ LoadUniqueObject(R0, test_cache); | 618 __ LoadUniqueObject(R0, test_cache); |
624 __ Push(R0); | 619 __ Push(R0); |
625 GenerateRuntimeCall(token_pos, deopt_id, kInstanceofRuntimeEntry, 5, locs); | 620 GenerateRuntimeCall(token_pos, deopt_id, kInstanceofRuntimeEntry, 5, locs); |
626 // Pop the parameters supplied to the runtime entry. The result of the | 621 // Pop the parameters supplied to the runtime entry. The result of the |
627 // instanceof runtime call will be left as the result of the operation. | 622 // instanceof runtime call will be left as the result of the operation. |
628 __ Drop(5); | 623 __ Drop(5); |
629 __ Pop(R0); | 624 __ Pop(R0); |
630 __ b(&done); | 625 __ b(&done); |
631 } | 626 } |
632 __ Bind(&is_not_instance); | 627 __ Bind(&is_not_instance); |
633 __ LoadObject(R0, Bool::Get(false)); | 628 __ LoadObject(R0, Bool::Get(false)); |
634 __ b(&done); | 629 __ b(&done); |
635 | 630 |
636 __ Bind(&is_instance); | 631 __ Bind(&is_instance); |
637 __ LoadObject(R0, Bool::Get(true)); | 632 __ LoadObject(R0, Bool::Get(true)); |
638 __ Bind(&done); | 633 __ Bind(&done); |
639 // Remove instantiator type arguments and function type arguments. | 634 // Remove instantiator type arguments and function type arguments. |
640 __ Drop(2); | 635 __ Drop(2); |
641 } | 636 } |
642 | 637 |
643 | 638 |
644 // Optimize assignable type check by adding inlined tests for: | 639 // Optimize assignable type check by adding inlined tests for: |
645 // - NULL -> return NULL. | 640 // - NULL -> return NULL. |
646 // - Smi -> compile time subtype check (only if dst class is not parameterized). | 641 // - Smi -> compile time subtype check (only if dst class is not parameterized). |
647 // - Class equality (only if class is not parameterized). | 642 // - Class equality (only if class is not parameterized). |
648 // Inputs: | 643 // Inputs: |
649 // - R0: instance being type checked. | 644 // - R0: instance being type checked. |
650 // - R1: instantiator type arguments or raw_null. | 645 // - R2: instantiator type arguments or raw_null. |
651 // - R2: function type arguments or raw_null. | 646 // - R1: function type arguments or raw_null. |
652 // Returns: | 647 // Returns: |
653 // - object in R0 for successful assignable check (or throws TypeError). | 648 // - object in R0 for successful assignable check (or throws TypeError). |
654 // Performance notes: positive checks must be quick, negative checks can be slow | 649 // Performance notes: positive checks must be quick, negative checks can be slow |
655 // as they throw an exception. | 650 // as they throw an exception. |
656 void FlowGraphCompiler::GenerateAssertAssignable(TokenPosition token_pos, | 651 void FlowGraphCompiler::GenerateAssertAssignable(TokenPosition token_pos, |
657 intptr_t deopt_id, | 652 intptr_t deopt_id, |
658 const AbstractType& dst_type, | 653 const AbstractType& dst_type, |
659 const String& dst_name, | 654 const String& dst_name, |
660 LocationSummary* locs) { | 655 LocationSummary* locs) { |
661 ASSERT(!token_pos.IsClassifying()); | 656 ASSERT(!token_pos.IsClassifying()); |
662 ASSERT(!dst_type.IsNull()); | 657 ASSERT(!dst_type.IsNull()); |
663 ASSERT(dst_type.IsFinalized()); | 658 ASSERT(dst_type.IsFinalized()); |
664 // Assignable check is skipped in FlowGraphBuilder, not here. | 659 // Assignable check is skipped in FlowGraphBuilder, not here. |
665 ASSERT(dst_type.IsMalformedOrMalbounded() || | 660 ASSERT(dst_type.IsMalformedOrMalbounded() || |
666 (!dst_type.IsDynamicType() && !dst_type.IsObjectType())); | 661 (!dst_type.IsDynamicType() && !dst_type.IsObjectType())); |
667 __ Push(R1); // Store instantiator type arguments. | 662 const Register kInstantiatorTypeArgumentsReg = R2; |
668 __ Push(R2); // Store function type arguments. | 663 const Register kFunctionTypeArgumentsReg = R1; |
669 // TODO(regis): Renumber registers and use PushList. | 664 __ PushList((1 << kInstantiatorTypeArgumentsReg) | |
| 665 (1 << kFunctionTypeArgumentsReg)); |
670 // A null object is always assignable and is returned as result. | 666 // A null object is always assignable and is returned as result. |
671 Label is_assignable, runtime_call; | 667 Label is_assignable, runtime_call; |
672 __ CompareObject(R0, Object::null_object()); | 668 __ CompareObject(R0, Object::null_object()); |
673 __ b(&is_assignable, EQ); | 669 __ b(&is_assignable, EQ); |
674 | 670 |
675 // Generate throw new TypeError() if the type is malformed or malbounded. | 671 // Generate throw new TypeError() if the type is malformed or malbounded. |
676 if (dst_type.IsMalformedOrMalbounded()) { | 672 if (dst_type.IsMalformedOrMalbounded()) { |
677 __ PushObject(Object::null_object()); // Make room for the result. | 673 __ PushObject(Object::null_object()); // Make room for the result. |
678 __ Push(R0); // Push the source object. | 674 __ Push(R0); // Push the source object. |
679 __ PushObject(dst_name); // Push the name of the destination. | 675 __ PushObject(dst_name); // Push the name of the destination. |
680 __ PushObject(dst_type); // Push the type of the destination. | 676 __ PushObject(dst_type); // Push the type of the destination. |
681 GenerateRuntimeCall(token_pos, deopt_id, kBadTypeErrorRuntimeEntry, 3, | 677 GenerateRuntimeCall(token_pos, deopt_id, kBadTypeErrorRuntimeEntry, 3, |
682 locs); | 678 locs); |
683 // We should never return here. | 679 // We should never return here. |
684 __ bkpt(0); | 680 __ bkpt(0); |
685 | 681 |
686 __ Bind(&is_assignable); // For a null object. | 682 __ Bind(&is_assignable); // For a null object. |
687 __ Pop(R2); // Remove pushed function type arguments. | 683 __ PopList((1 << kFunctionTypeArgumentsReg) | |
688 __ Pop(R1); // Remove pushed instantiator type arguments. | 684 (1 << kInstantiatorTypeArgumentsReg)); |
689 // TODO(regis): Renumber registers and use PopList. | |
690 return; | 685 return; |
691 } | 686 } |
692 | 687 |
693 // Generate inline type check, linking to runtime call if not assignable. | 688 // Generate inline type check, linking to runtime call if not assignable. |
694 SubtypeTestCache& test_cache = SubtypeTestCache::ZoneHandle(zone()); | 689 SubtypeTestCache& test_cache = SubtypeTestCache::ZoneHandle(zone()); |
695 test_cache = GenerateInlineInstanceof(token_pos, dst_type, &is_assignable, | 690 test_cache = GenerateInlineInstanceof(token_pos, dst_type, &is_assignable, |
696 &runtime_call); | 691 &runtime_call); |
697 | 692 |
698 __ Bind(&runtime_call); | 693 __ Bind(&runtime_call); |
699 __ ldr(R1, Address(SP, 1 * kWordSize)); // Get instantiator type args. | 694 __ ldm(IA, SP, (1 << kFunctionTypeArgumentsReg) | |
700 __ ldr(R2, Address(SP, 0 * kWordSize)); // Get function type args. | 695 (1 << kInstantiatorTypeArgumentsReg)); |
701 // TODO(regis): Renumber registers and use ldm. | |
702 __ PushObject(Object::null_object()); // Make room for the result. | 696 __ PushObject(Object::null_object()); // Make room for the result. |
703 __ Push(R0); // Push the source object. | 697 __ Push(R0); // Push the source object. |
704 __ PushObject(dst_type); // Push the type of the destination. | 698 __ PushObject(dst_type); // Push the type of the destination. |
705 __ Push(R1); // Instantiator type arguments. | 699 __ PushList((1 << kInstantiatorTypeArgumentsReg) | |
706 __ Push(R2); // Function type arguments. | 700 (1 << kFunctionTypeArgumentsReg)); |
707 // TODO(regis): Renumber registers and use PushList. | |
708 __ PushObject(dst_name); // Push the name of the destination. | 701 __ PushObject(dst_name); // Push the name of the destination. |
709 __ LoadUniqueObject(R0, test_cache); | 702 __ LoadUniqueObject(R0, test_cache); |
710 __ Push(R0); | 703 __ Push(R0); |
711 GenerateRuntimeCall(token_pos, deopt_id, kTypeCheckRuntimeEntry, 6, locs); | 704 GenerateRuntimeCall(token_pos, deopt_id, kTypeCheckRuntimeEntry, 6, locs); |
712 // 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 |
713 // type check runtime call is the checked value. | 706 // type check runtime call is the checked value. |
714 __ Drop(6); | 707 __ Drop(6); |
715 __ Pop(R0); | 708 __ Pop(R0); |
716 | 709 |
717 __ Bind(&is_assignable); | 710 __ Bind(&is_assignable); |
718 __ Pop(R2); // Remove pushed function type arguments. | 711 __ PopList((1 << kFunctionTypeArgumentsReg) | |
719 __ Pop(R1); // Remove pushed instantiator type arguments. | 712 (1 << kInstantiatorTypeArgumentsReg)); |
720 // TODO(regis): Renumber registers and use PopList. | |
721 } | 713 } |
722 | 714 |
723 | 715 |
724 void FlowGraphCompiler::EmitInstructionEpilogue(Instruction* instr) { | 716 void FlowGraphCompiler::EmitInstructionEpilogue(Instruction* instr) { |
725 if (is_optimizing()) { | 717 if (is_optimizing()) { |
726 return; | 718 return; |
727 } | 719 } |
728 Definition* defn = instr->AsDefinition(); | 720 Definition* defn = instr->AsDefinition(); |
729 if ((defn != NULL) && defn->HasTemp()) { | 721 if ((defn != NULL) && defn->HasTemp()) { |
730 __ Push(defn->locs()->out(0).reg()); | 722 __ Push(defn->locs()->out(0).reg()); |
(...skipping 698 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1429 ClobberDeadTempRegisters(locs); | 1421 ClobberDeadTempRegisters(locs); |
1430 #endif | 1422 #endif |
1431 | 1423 |
1432 // TODO(vegorov): consider saving only caller save (volatile) registers. | 1424 // TODO(vegorov): consider saving only caller save (volatile) registers. |
1433 const intptr_t fpu_regs_count = locs->live_registers()->FpuRegisterCount(); | 1425 const intptr_t fpu_regs_count = locs->live_registers()->FpuRegisterCount(); |
1434 if (fpu_regs_count > 0) { | 1426 if (fpu_regs_count > 0) { |
1435 __ AddImmediate(SP, -(fpu_regs_count * kFpuRegisterSize)); | 1427 __ AddImmediate(SP, -(fpu_regs_count * kFpuRegisterSize)); |
1436 // Store fpu registers with the lowest register number at the lowest | 1428 // Store fpu registers with the lowest register number at the lowest |
1437 // address. | 1429 // address. |
1438 intptr_t offset = 0; | 1430 intptr_t offset = 0; |
| 1431 __ mov(IP, Operand(SP)); |
1439 for (intptr_t i = 0; i < kNumberOfFpuRegisters; ++i) { | 1432 for (intptr_t i = 0; i < kNumberOfFpuRegisters; ++i) { |
1440 QRegister fpu_reg = static_cast<QRegister>(i); | 1433 QRegister fpu_reg = static_cast<QRegister>(i); |
1441 if (locs->live_registers()->ContainsFpuRegister(fpu_reg)) { | 1434 if (locs->live_registers()->ContainsFpuRegister(fpu_reg)) { |
1442 DRegister d1 = EvenDRegisterOf(fpu_reg); | 1435 DRegister d = EvenDRegisterOf(fpu_reg); |
1443 DRegister d2 = OddDRegisterOf(fpu_reg); | 1436 ASSERT(d + 1 == OddDRegisterOf(fpu_reg)); |
1444 // TODO(regis): merge stores using vstmd instruction. | 1437 __ vstmd(IA_W, IP, d, 2); |
1445 __ vstrd(d1, Address(SP, offset)); | |
1446 __ vstrd(d2, Address(SP, offset + 2 * kWordSize)); | |
1447 offset += kFpuRegisterSize; | 1438 offset += kFpuRegisterSize; |
1448 } | 1439 } |
1449 } | 1440 } |
1450 ASSERT(offset == (fpu_regs_count * kFpuRegisterSize)); | 1441 ASSERT(offset == (fpu_regs_count * kFpuRegisterSize)); |
1451 } | 1442 } |
1452 | 1443 |
1453 // The order in which the registers are pushed must match the order | 1444 // The order in which the registers are pushed must match the order |
1454 // in which the registers are encoded in the safe point's stack map. | 1445 // in which the registers are encoded in the safe point's stack map. |
1455 // NOTE: This matches the order of ARM's multi-register push. | 1446 // NOTE: This matches the order of ARM's multi-register push. |
1456 RegList reg_list = 0; | 1447 RegList reg_list = 0; |
(...skipping 21 matching lines...) Expand all Loading... |
1478 __ PopList(reg_list); | 1469 __ PopList(reg_list); |
1479 } | 1470 } |
1480 | 1471 |
1481 const intptr_t fpu_regs_count = locs->live_registers()->FpuRegisterCount(); | 1472 const intptr_t fpu_regs_count = locs->live_registers()->FpuRegisterCount(); |
1482 if (fpu_regs_count > 0) { | 1473 if (fpu_regs_count > 0) { |
1483 // Fpu registers have the lowest register number at the lowest address. | 1474 // Fpu registers have the lowest register number at the lowest address. |
1484 intptr_t offset = 0; | 1475 intptr_t offset = 0; |
1485 for (intptr_t i = 0; i < kNumberOfFpuRegisters; ++i) { | 1476 for (intptr_t i = 0; i < kNumberOfFpuRegisters; ++i) { |
1486 QRegister fpu_reg = static_cast<QRegister>(i); | 1477 QRegister fpu_reg = static_cast<QRegister>(i); |
1487 if (locs->live_registers()->ContainsFpuRegister(fpu_reg)) { | 1478 if (locs->live_registers()->ContainsFpuRegister(fpu_reg)) { |
1488 DRegister d1 = EvenDRegisterOf(fpu_reg); | 1479 DRegister d = EvenDRegisterOf(fpu_reg); |
1489 DRegister d2 = OddDRegisterOf(fpu_reg); | 1480 ASSERT(d + 1 == OddDRegisterOf(fpu_reg)); |
1490 // TODO(regis): merge loads using vldmd instruction. | 1481 __ vldmd(IA_W, SP, d, 2); |
1491 __ vldrd(d1, Address(SP, offset)); | |
1492 __ vldrd(d2, Address(SP, offset + 2 * kWordSize)); | |
1493 offset += kFpuRegisterSize; | 1482 offset += kFpuRegisterSize; |
1494 } | 1483 } |
1495 } | 1484 } |
1496 ASSERT(offset == (fpu_regs_count * kFpuRegisterSize)); | 1485 ASSERT(offset == (fpu_regs_count * kFpuRegisterSize)); |
1497 __ AddImmediate(SP, offset); | |
1498 } | 1486 } |
1499 } | 1487 } |
1500 | 1488 |
1501 | 1489 |
1502 #if defined(DEBUG) | 1490 #if defined(DEBUG) |
1503 void FlowGraphCompiler::ClobberDeadTempRegisters(LocationSummary* locs) { | 1491 void FlowGraphCompiler::ClobberDeadTempRegisters(LocationSummary* locs) { |
1504 // Clobber temporaries that have not been manually preserved. | 1492 // Clobber temporaries that have not been manually preserved. |
1505 for (intptr_t i = 0; i < locs->temp_count(); ++i) { | 1493 for (intptr_t i = 0; i < locs->temp_count(); ++i) { |
1506 Location tmp = locs->temp(i); | 1494 Location tmp = locs->temp(i); |
1507 // TODO(zerny): clobber non-live temporary FPU registers. | 1495 // TODO(zerny): clobber non-live temporary FPU registers. |
(...skipping 415 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1923 DRegister dreg = EvenDRegisterOf(reg); | 1911 DRegister dreg = EvenDRegisterOf(reg); |
1924 __ vldrd(dreg, Address(SP, kDoubleSize, Address::PostIndex)); | 1912 __ vldrd(dreg, Address(SP, kDoubleSize, Address::PostIndex)); |
1925 } | 1913 } |
1926 | 1914 |
1927 | 1915 |
1928 #undef __ | 1916 #undef __ |
1929 | 1917 |
1930 } // namespace dart | 1918 } // namespace dart |
1931 | 1919 |
1932 #endif // defined TARGET_ARCH_ARM | 1920 #endif // defined TARGET_ARCH_ARM |
OLD | NEW |