| 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 |