| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, 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_IA32. | 5 #include "vm/globals.h" // Needed here to get TARGET_ARCH_IA32. |
| 6 #if defined(TARGET_ARCH_IA32) | 6 #if defined(TARGET_ARCH_IA32) |
| 7 | 7 |
| 8 #include "vm/flow_graph_compiler.h" | 8 #include "vm/flow_graph_compiler.h" |
| 9 | 9 |
| 10 #include "lib/error.h" | 10 #include "lib/error.h" |
| (...skipping 20 matching lines...) Expand all Loading... |
| 31 __ Bind(entry_label()); | 31 __ Bind(entry_label()); |
| 32 for (intptr_t i = 0; i < registers_.length(); i++) { | 32 for (intptr_t i = 0; i < registers_.length(); i++) { |
| 33 if (registers_[i] != kNoRegister) { | 33 if (registers_[i] != kNoRegister) { |
| 34 __ pushl(registers_[i]); | 34 __ pushl(registers_[i]); |
| 35 } | 35 } |
| 36 } | 36 } |
| 37 __ movl(EAX, Immediate(Smi::RawValue(reason_))); | 37 __ movl(EAX, Immediate(Smi::RawValue(reason_))); |
| 38 __ call(&StubCode::DeoptimizeLabel()); | 38 __ call(&StubCode::DeoptimizeLabel()); |
| 39 compiler->AddCurrentDescriptor(PcDescriptors::kOther, | 39 compiler->AddCurrentDescriptor(PcDescriptors::kOther, |
| 40 deopt_id_, | 40 deopt_id_, |
| 41 deopt_token_index_, | 41 deopt_token_pos_, |
| 42 try_index_); | 42 try_index_); |
| 43 #undef __ | 43 #undef __ |
| 44 } | 44 } |
| 45 | 45 |
| 46 | 46 |
| 47 | 47 |
| 48 #define __ assembler()-> | 48 #define __ assembler()-> |
| 49 | 49 |
| 50 void FlowGraphCompiler::GenerateInlinedGetter(intptr_t offset) { | 50 void FlowGraphCompiler::GenerateInlinedGetter(intptr_t offset) { |
| 51 // TOS: return address. | 51 // TOS: return address. |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 89 __ Drop(1); | 89 __ Drop(1); |
| 90 __ jmp(done); | 90 __ jmp(done); |
| 91 __ Bind(&smi_to_double); | 91 __ Bind(&smi_to_double); |
| 92 __ SmiUntag(EAX); | 92 __ SmiUntag(EAX); |
| 93 __ cvtsi2sd(XMM1, EAX); | 93 __ cvtsi2sd(XMM1, EAX); |
| 94 __ jmp(&double_op); | 94 __ jmp(&double_op); |
| 95 __ Bind(&call_method); | 95 __ Bind(&call_method); |
| 96 } | 96 } |
| 97 | 97 |
| 98 | 98 |
| 99 void FlowGraphCompiler::GenerateCall(intptr_t token_index, | 99 void FlowGraphCompiler::GenerateCall(intptr_t token_pos, |
| 100 intptr_t try_index, | 100 intptr_t try_index, |
| 101 const ExternalLabel* label, | 101 const ExternalLabel* label, |
| 102 PcDescriptors::Kind kind) { | 102 PcDescriptors::Kind kind) { |
| 103 ASSERT(frame_register_allocator()->IsSpilled()); | 103 ASSERT(frame_register_allocator()->IsSpilled()); |
| 104 __ call(label); | 104 __ call(label); |
| 105 AddCurrentDescriptor(kind, AstNode::kNoId, token_index, try_index); | 105 AddCurrentDescriptor(kind, AstNode::kNoId, token_pos, try_index); |
| 106 } | 106 } |
| 107 | 107 |
| 108 | 108 |
| 109 void FlowGraphCompiler::GenerateCallRuntime(intptr_t cid, | 109 void FlowGraphCompiler::GenerateCallRuntime(intptr_t cid, |
| 110 intptr_t token_index, | 110 intptr_t token_pos, |
| 111 intptr_t try_index, | 111 intptr_t try_index, |
| 112 const RuntimeEntry& entry) { | 112 const RuntimeEntry& entry) { |
| 113 ASSERT(frame_register_allocator()->IsSpilled()); | 113 ASSERT(frame_register_allocator()->IsSpilled()); |
| 114 __ CallRuntime(entry); | 114 __ CallRuntime(entry); |
| 115 AddCurrentDescriptor(PcDescriptors::kOther, cid, token_index, try_index); | 115 AddCurrentDescriptor(PcDescriptors::kOther, cid, token_pos, try_index); |
| 116 } | 116 } |
| 117 | 117 |
| 118 | 118 |
| 119 void FlowGraphCompiler::CopyParameters() { | 119 void FlowGraphCompiler::CopyParameters() { |
| 120 const Function& function = parsed_function().function(); | 120 const Function& function = parsed_function().function(); |
| 121 const bool is_native_instance_closure = | 121 const bool is_native_instance_closure = |
| 122 function.is_native() && function.IsImplicitInstanceClosureFunction(); | 122 function.is_native() && function.IsImplicitInstanceClosureFunction(); |
| 123 LocalScope* scope = parsed_function().node_sequence()->scope(); | 123 LocalScope* scope = parsed_function().node_sequence()->scope(); |
| 124 const int num_fixed_params = function.num_fixed_parameters(); | 124 const int num_fixed_params = function.num_fixed_parameters(); |
| 125 const int num_opt_params = function.num_optional_parameters(); | 125 const int num_opt_params = function.num_optional_parameters(); |
| (...skipping 223 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 349 #endif | 349 #endif |
| 350 if (check_arguments) { | 350 if (check_arguments) { |
| 351 // Check that num_fixed <= argc <= num_params. | 351 // Check that num_fixed <= argc <= num_params. |
| 352 Label argc_in_range; | 352 Label argc_in_range; |
| 353 // Total number of args is the first Smi in args descriptor array (EDX). | 353 // Total number of args is the first Smi in args descriptor array (EDX). |
| 354 __ movl(EAX, FieldAddress(EDX, Array::data_offset())); | 354 __ movl(EAX, FieldAddress(EDX, Array::data_offset())); |
| 355 __ cmpl(EAX, Immediate(Smi::RawValue(parameter_count))); | 355 __ cmpl(EAX, Immediate(Smi::RawValue(parameter_count))); |
| 356 __ j(EQUAL, &argc_in_range, Assembler::kNearJump); | 356 __ j(EQUAL, &argc_in_range, Assembler::kNearJump); |
| 357 if (function.IsClosureFunction()) { | 357 if (function.IsClosureFunction()) { |
| 358 GenerateCallRuntime(AstNode::kNoId, | 358 GenerateCallRuntime(AstNode::kNoId, |
| 359 function.token_index(), | 359 function.token_pos(), |
| 360 CatchClauseNode::kInvalidTryIndex, | 360 CatchClauseNode::kInvalidTryIndex, |
| 361 kClosureArgumentMismatchRuntimeEntry); | 361 kClosureArgumentMismatchRuntimeEntry); |
| 362 } else { | 362 } else { |
| 363 __ Stop("Wrong number of arguments"); | 363 __ Stop("Wrong number of arguments"); |
| 364 } | 364 } |
| 365 __ Bind(&argc_in_range); | 365 __ Bind(&argc_in_range); |
| 366 } | 366 } |
| 367 } else { | 367 } else { |
| 368 CopyParameters(); | 368 CopyParameters(); |
| 369 } | 369 } |
| 370 // Initialize (non-argument) stack allocated locals to null. | 370 // Initialize (non-argument) stack allocated locals to null. |
| 371 if (local_count > 0) { | 371 if (local_count > 0) { |
| 372 const Immediate raw_null = | 372 const Immediate raw_null = |
| 373 Immediate(reinterpret_cast<intptr_t>(Object::null())); | 373 Immediate(reinterpret_cast<intptr_t>(Object::null())); |
| 374 __ movl(EAX, raw_null); | 374 __ movl(EAX, raw_null); |
| 375 const int base = parsed_function().first_stack_local_index(); | 375 const int base = parsed_function().first_stack_local_index(); |
| 376 for (int i = 0; i < local_count; ++i) { | 376 for (int i = 0; i < local_count; ++i) { |
| 377 // Subtract index i (locals lie at lower addresses than EBP). | 377 // Subtract index i (locals lie at lower addresses than EBP). |
| 378 __ movl(Address(EBP, (base - i) * kWordSize), EAX); | 378 __ movl(Address(EBP, (base - i) * kWordSize), EAX); |
| 379 } | 379 } |
| 380 } | 380 } |
| 381 | 381 |
| 382 // Generate stack overflow check. | 382 // Generate stack overflow check. |
| 383 __ cmpl(ESP, | 383 __ cmpl(ESP, |
| 384 Address::Absolute(Isolate::Current()->stack_limit_address())); | 384 Address::Absolute(Isolate::Current()->stack_limit_address())); |
| 385 Label no_stack_overflow; | 385 Label no_stack_overflow; |
| 386 __ j(ABOVE, &no_stack_overflow, Assembler::kNearJump); | 386 __ j(ABOVE, &no_stack_overflow, Assembler::kNearJump); |
| 387 GenerateCallRuntime(AstNode::kNoId, | 387 GenerateCallRuntime(AstNode::kNoId, |
| 388 function.token_index(), | 388 function.token_pos(), |
| 389 CatchClauseNode::kInvalidTryIndex, | 389 CatchClauseNode::kInvalidTryIndex, |
| 390 kStackOverflowRuntimeEntry); | 390 kStackOverflowRuntimeEntry); |
| 391 __ Bind(&no_stack_overflow); | 391 __ Bind(&no_stack_overflow); |
| 392 | 392 |
| 393 if (FLAG_print_scopes) { | 393 if (FLAG_print_scopes) { |
| 394 // Print the function scope (again) after generating the prologue in order | 394 // Print the function scope (again) after generating the prologue in order |
| 395 // to see annotations such as allocation indices of locals. | 395 // to see annotations such as allocation indices of locals. |
| 396 if (FLAG_print_ast) { | 396 if (FLAG_print_ast) { |
| 397 // Second printing. | 397 // Second printing. |
| 398 OS::Print("Annotated "); | 398 OS::Print("Annotated "); |
| (...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 497 } | 497 } |
| 498 | 498 |
| 499 | 499 |
| 500 // Jumps to labels 'is_instance' or 'is_not_instance' respectively, if | 500 // Jumps to labels 'is_instance' or 'is_not_instance' respectively, if |
| 501 // type test is conclusive, otherwise fallthrough if a type test could not | 501 // type test is conclusive, otherwise fallthrough if a type test could not |
| 502 // be completed. | 502 // be completed. |
| 503 // EAX: instance (must survive), clobbers ECX, EDI | 503 // EAX: instance (must survive), clobbers ECX, EDI |
| 504 RawSubtypeTestCache* | 504 RawSubtypeTestCache* |
| 505 FlowGraphCompiler::GenerateInstantiatedTypeWithArgumentsTest( | 505 FlowGraphCompiler::GenerateInstantiatedTypeWithArgumentsTest( |
| 506 intptr_t cid, | 506 intptr_t cid, |
| 507 intptr_t token_index, | 507 intptr_t token_pos, |
| 508 const AbstractType& type, | 508 const AbstractType& type, |
| 509 Label* is_instance_lbl, | 509 Label* is_instance_lbl, |
| 510 Label* is_not_instance_lbl) { | 510 Label* is_not_instance_lbl) { |
| 511 ASSERT(type.IsInstantiated()); | 511 ASSERT(type.IsInstantiated()); |
| 512 const Class& type_class = Class::ZoneHandle(type.type_class()); | 512 const Class& type_class = Class::ZoneHandle(type.type_class()); |
| 513 ASSERT(type_class.HasTypeArguments()); | 513 ASSERT(type_class.HasTypeArguments()); |
| 514 const Register kInstanceReg = EAX; | 514 const Register kInstanceReg = EAX; |
| 515 // A Smi object cannot be the instance of a parameterized class. | 515 // A Smi object cannot be the instance of a parameterized class. |
| 516 __ testl(kInstanceReg, Immediate(kSmiTagMask)); | 516 __ testl(kInstanceReg, Immediate(kSmiTagMask)); |
| 517 __ j(ZERO, is_not_instance_lbl); | 517 __ j(ZERO, is_not_instance_lbl); |
| 518 const AbstractTypeArguments& type_arguments = | 518 const AbstractTypeArguments& type_arguments = |
| 519 AbstractTypeArguments::ZoneHandle(type.arguments()); | 519 AbstractTypeArguments::ZoneHandle(type.arguments()); |
| 520 const bool is_raw_type = type_arguments.IsNull() || | 520 const bool is_raw_type = type_arguments.IsNull() || |
| 521 type_arguments.IsRaw(type_arguments.Length()); | 521 type_arguments.IsRaw(type_arguments.Length()); |
| 522 if (is_raw_type) { | 522 if (is_raw_type) { |
| 523 const Register kClassIdReg = ECX; | 523 const Register kClassIdReg = ECX; |
| 524 // Dynamic type argument, check only classes. | 524 // Dynamic type argument, check only classes. |
| 525 __ LoadClassId(kClassIdReg, kInstanceReg); | 525 __ LoadClassId(kClassIdReg, kInstanceReg); |
| 526 if (!type_class.is_interface()) { | 526 if (!type_class.is_interface()) { |
| 527 __ cmpl(kClassIdReg, Immediate(type_class.id())); | 527 __ cmpl(kClassIdReg, Immediate(type_class.id())); |
| 528 __ j(EQUAL, is_instance_lbl); | 528 __ j(EQUAL, is_instance_lbl); |
| 529 } | 529 } |
| 530 if (type.IsListInterface()) { | 530 if (type.IsListInterface()) { |
| 531 GenerateListTypeCheck(kClassIdReg, is_instance_lbl); | 531 GenerateListTypeCheck(kClassIdReg, is_instance_lbl); |
| 532 } | 532 } |
| 533 return GenerateSubtype1TestCacheLookup( | 533 return GenerateSubtype1TestCacheLookup( |
| 534 cid, token_index, type_class, is_instance_lbl, is_not_instance_lbl); | 534 cid, token_pos, type_class, is_instance_lbl, is_not_instance_lbl); |
| 535 } | 535 } |
| 536 // If one type argument only, check if type argument is Object or Dynamic. | 536 // If one type argument only, check if type argument is Object or Dynamic. |
| 537 if (type_arguments.Length() == 1) { | 537 if (type_arguments.Length() == 1) { |
| 538 const AbstractType& tp_argument = AbstractType::ZoneHandle( | 538 const AbstractType& tp_argument = AbstractType::ZoneHandle( |
| 539 type_arguments.TypeAt(0)); | 539 type_arguments.TypeAt(0)); |
| 540 ASSERT(!tp_argument.IsMalformed()); | 540 ASSERT(!tp_argument.IsMalformed()); |
| 541 if (tp_argument.IsType()) { | 541 if (tp_argument.IsType()) { |
| 542 ASSERT(tp_argument.HasResolvedTypeClass()); | 542 ASSERT(tp_argument.HasResolvedTypeClass()); |
| 543 // Check if type argument is dynamic or Object. | 543 // Check if type argument is dynamic or Object. |
| 544 const Type& object_type = | 544 const Type& object_type = |
| 545 Type::Handle(Isolate::Current()->object_store()->object_type()); | 545 Type::Handle(Isolate::Current()->object_store()->object_type()); |
| 546 Error& malformed_error = Error::Handle(); | 546 Error& malformed_error = Error::Handle(); |
| 547 if (object_type.IsSubtypeOf(tp_argument, &malformed_error)) { | 547 if (object_type.IsSubtypeOf(tp_argument, &malformed_error)) { |
| 548 // Instance class test only necessary. | 548 // Instance class test only necessary. |
| 549 return GenerateSubtype1TestCacheLookup( | 549 return GenerateSubtype1TestCacheLookup( |
| 550 cid, token_index, type_class, is_instance_lbl, is_not_instance_lbl); | 550 cid, token_pos, type_class, is_instance_lbl, is_not_instance_lbl); |
| 551 } | 551 } |
| 552 } | 552 } |
| 553 } | 553 } |
| 554 // Regular subtype test cache involving instance's type arguments. | 554 // Regular subtype test cache involving instance's type arguments. |
| 555 const Register kTypeArgumentsReg = kNoRegister; | 555 const Register kTypeArgumentsReg = kNoRegister; |
| 556 const Register kTempReg = EDI; | 556 const Register kTempReg = EDI; |
| 557 return GenerateCallSubtypeTestStub(kTestTypeTwoArgs, | 557 return GenerateCallSubtypeTestStub(kTestTypeTwoArgs, |
| 558 kInstanceReg, | 558 kInstanceReg, |
| 559 kTypeArgumentsReg, | 559 kTypeArgumentsReg, |
| 560 kTempReg, | 560 kTempReg, |
| (...skipping 12 matching lines...) Expand all Loading... |
| 573 } | 573 } |
| 574 __ jmp(is_not_equal_lbl); | 574 __ jmp(is_not_equal_lbl); |
| 575 } | 575 } |
| 576 | 576 |
| 577 | 577 |
| 578 // Testing against an instantiated type with no arguments, without | 578 // Testing against an instantiated type with no arguments, without |
| 579 // SubtypeTestCache. | 579 // SubtypeTestCache. |
| 580 // EAX: instance to test against (preserved). Clobbers ECX, EDI. | 580 // EAX: instance to test against (preserved). Clobbers ECX, EDI. |
| 581 void FlowGraphCompiler::GenerateInstantiatedTypeNoArgumentsTest( | 581 void FlowGraphCompiler::GenerateInstantiatedTypeNoArgumentsTest( |
| 582 intptr_t cid, | 582 intptr_t cid, |
| 583 intptr_t token_index, | 583 intptr_t token_pos, |
| 584 const AbstractType& type, | 584 const AbstractType& type, |
| 585 Label* is_instance_lbl, | 585 Label* is_instance_lbl, |
| 586 Label* is_not_instance_lbl) { | 586 Label* is_not_instance_lbl) { |
| 587 ASSERT(type.IsInstantiated()); | 587 ASSERT(type.IsInstantiated()); |
| 588 const Class& type_class = Class::Handle(type.type_class()); | 588 const Class& type_class = Class::Handle(type.type_class()); |
| 589 ASSERT(!type_class.HasTypeArguments()); | 589 ASSERT(!type_class.HasTypeArguments()); |
| 590 | 590 |
| 591 const Register kInstanceReg = EAX; | 591 const Register kInstanceReg = EAX; |
| 592 Label compare_classes; | 592 Label compare_classes; |
| 593 __ testl(kInstanceReg, Immediate(kSmiTagMask)); | 593 __ testl(kInstanceReg, Immediate(kSmiTagMask)); |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 644 GenerateStringTypeCheck(kClassIdReg, is_instance_lbl, is_not_instance_lbl); | 644 GenerateStringTypeCheck(kClassIdReg, is_instance_lbl, is_not_instance_lbl); |
| 645 return; | 645 return; |
| 646 } | 646 } |
| 647 } | 647 } |
| 648 | 648 |
| 649 | 649 |
| 650 // Generates inlined check if 'type' is a type parameter or type itsef | 650 // Generates inlined check if 'type' is a type parameter or type itsef |
| 651 // EAX: instance (preserved). Clobbers EDX, EDI, ECX. | 651 // EAX: instance (preserved). Clobbers EDX, EDI, ECX. |
| 652 RawSubtypeTestCache* FlowGraphCompiler::GenerateUninstantiatedTypeTest( | 652 RawSubtypeTestCache* FlowGraphCompiler::GenerateUninstantiatedTypeTest( |
| 653 intptr_t cid, | 653 intptr_t cid, |
| 654 intptr_t token_index, | 654 intptr_t token_pos, |
| 655 const AbstractType& type, | 655 const AbstractType& type, |
| 656 Label* is_instance_lbl, | 656 Label* is_instance_lbl, |
| 657 Label* is_not_instance_lbl) { | 657 Label* is_not_instance_lbl) { |
| 658 ASSERT(!type.IsInstantiated()); | 658 ASSERT(!type.IsInstantiated()); |
| 659 // Skip check if destination is a dynamic type. | 659 // Skip check if destination is a dynamic type. |
| 660 const Immediate raw_null = | 660 const Immediate raw_null = |
| 661 Immediate(reinterpret_cast<intptr_t>(Object::null())); | 661 Immediate(reinterpret_cast<intptr_t>(Object::null())); |
| 662 if (type.IsTypeParameter()) { | 662 if (type.IsTypeParameter()) { |
| 663 // Load instantiator (or null) and instantiator type arguments on stack. | 663 // Load instantiator (or null) and instantiator type arguments on stack. |
| 664 __ movl(EDX, Address(ESP, 0)); // Get instantiator type arguments. | 664 __ movl(EDX, Address(ESP, 0)); // Get instantiator type arguments. |
| (...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 734 | 734 |
| 735 | 735 |
| 736 // Uses SubtypeTestCache to store instance class and result. | 736 // Uses SubtypeTestCache to store instance class and result. |
| 737 // EAX: instance to test. Clobbers EDI, ECX. | 737 // EAX: instance to test. Clobbers EDI, ECX. |
| 738 // Immediate class test already done. | 738 // Immediate class test already done. |
| 739 // TODO(srdjan): Implement a quicker subtype check, as type test | 739 // TODO(srdjan): Implement a quicker subtype check, as type test |
| 740 // arrays can grow too high, but they may be useful when optimizing | 740 // arrays can grow too high, but they may be useful when optimizing |
| 741 // code (type-feedback). | 741 // code (type-feedback). |
| 742 RawSubtypeTestCache* FlowGraphCompiler::GenerateSubtype1TestCacheLookup( | 742 RawSubtypeTestCache* FlowGraphCompiler::GenerateSubtype1TestCacheLookup( |
| 743 intptr_t cid, | 743 intptr_t cid, |
| 744 intptr_t token_index, | 744 intptr_t token_pos, |
| 745 const Class& type_class, | 745 const Class& type_class, |
| 746 Label* is_instance_lbl, | 746 Label* is_instance_lbl, |
| 747 Label* is_not_instance_lbl) { | 747 Label* is_not_instance_lbl) { |
| 748 const Register kInstanceReg = EAX; | 748 const Register kInstanceReg = EAX; |
| 749 __ LoadClass(ECX, kInstanceReg, EDI); | 749 __ LoadClass(ECX, kInstanceReg, EDI); |
| 750 // ECX: instance class. | 750 // ECX: instance class. |
| 751 // Check immediate superclass equality. | 751 // Check immediate superclass equality. |
| 752 __ movl(EDI, FieldAddress(ECX, Class::super_type_offset())); | 752 __ movl(EDI, FieldAddress(ECX, Class::super_type_offset())); |
| 753 __ movl(EDI, FieldAddress(EDI, Type::type_class_offset())); | 753 __ movl(EDI, FieldAddress(EDI, Type::type_class_offset())); |
| 754 __ CompareObject(EDI, type_class); | 754 __ CompareObject(EDI, type_class); |
| (...skipping 13 matching lines...) Expand all Loading... |
| 768 // Inputs: | 768 // Inputs: |
| 769 // - EAX: instance to test against (preserved). | 769 // - EAX: instance to test against (preserved). |
| 770 // - EDX: optional instantiator type arguments (preserved). | 770 // - EDX: optional instantiator type arguments (preserved). |
| 771 // Returns: | 771 // Returns: |
| 772 // - preserved instance in EAX and optional instantiator type arguments in EDX. | 772 // - preserved instance in EAX and optional instantiator type arguments in EDX. |
| 773 // Note that this inlined code must be followed by the runtime_call code, as it | 773 // Note that this inlined code must be followed by the runtime_call code, as it |
| 774 // may fall through to it. Otherwise, this inline code will jump to the label | 774 // may fall through to it. Otherwise, this inline code will jump to the label |
| 775 // is_instance or to the label is_not_instance. | 775 // is_instance or to the label is_not_instance. |
| 776 RawSubtypeTestCache* FlowGraphCompiler::GenerateInlineInstanceof( | 776 RawSubtypeTestCache* FlowGraphCompiler::GenerateInlineInstanceof( |
| 777 intptr_t cid, | 777 intptr_t cid, |
| 778 intptr_t token_index, | 778 intptr_t token_pos, |
| 779 const AbstractType& type, | 779 const AbstractType& type, |
| 780 Label* is_instance_lbl, | 780 Label* is_instance_lbl, |
| 781 Label* is_not_instance_lbl) { | 781 Label* is_not_instance_lbl) { |
| 782 if (type.IsInstantiated()) { | 782 if (type.IsInstantiated()) { |
| 783 const Class& type_class = Class::ZoneHandle(type.type_class()); | 783 const Class& type_class = Class::ZoneHandle(type.type_class()); |
| 784 // A Smi object cannot be the instance of a parameterized class. | 784 // A Smi object cannot be the instance of a parameterized class. |
| 785 // A class equality check is only applicable with a dst type of a | 785 // A class equality check is only applicable with a dst type of a |
| 786 // non-parameterized class or with a raw dst type of a parameterized class. | 786 // non-parameterized class or with a raw dst type of a parameterized class. |
| 787 if (type_class.HasTypeArguments()) { | 787 if (type_class.HasTypeArguments()) { |
| 788 return GenerateInstantiatedTypeWithArgumentsTest(cid, | 788 return GenerateInstantiatedTypeWithArgumentsTest(cid, |
| 789 token_index, | 789 token_pos, |
| 790 type, | 790 type, |
| 791 is_instance_lbl, | 791 is_instance_lbl, |
| 792 is_not_instance_lbl); | 792 is_not_instance_lbl); |
| 793 // Fall through to runtime call. | 793 // Fall through to runtime call. |
| 794 } else { | 794 } else { |
| 795 GenerateInstantiatedTypeNoArgumentsTest(cid, | 795 GenerateInstantiatedTypeNoArgumentsTest(cid, |
| 796 token_index, | 796 token_pos, |
| 797 type, | 797 type, |
| 798 is_instance_lbl, | 798 is_instance_lbl, |
| 799 is_not_instance_lbl); | 799 is_not_instance_lbl); |
| 800 // If test non-conclusive so far, try the inlined type-test cache. | 800 // If test non-conclusive so far, try the inlined type-test cache. |
| 801 // 'type' is known at compile time. | 801 // 'type' is known at compile time. |
| 802 return GenerateSubtype1TestCacheLookup( | 802 return GenerateSubtype1TestCacheLookup( |
| 803 cid, token_index, type_class, | 803 cid, token_pos, type_class, |
| 804 is_instance_lbl, is_not_instance_lbl); | 804 is_instance_lbl, is_not_instance_lbl); |
| 805 } | 805 } |
| 806 } else { | 806 } else { |
| 807 return GenerateUninstantiatedTypeTest(cid, | 807 return GenerateUninstantiatedTypeTest(cid, |
| 808 token_index, | 808 token_pos, |
| 809 type, | 809 type, |
| 810 is_instance_lbl, | 810 is_instance_lbl, |
| 811 is_not_instance_lbl); | 811 is_not_instance_lbl); |
| 812 } | 812 } |
| 813 return SubtypeTestCache::null(); | 813 return SubtypeTestCache::null(); |
| 814 } | 814 } |
| 815 | 815 |
| 816 | 816 |
| 817 // If instanceof type test cannot be performed successfully at compile time and | 817 // If instanceof type test cannot be performed successfully at compile time and |
| 818 // therefore eliminated, optimize it by adding inlined tests for: | 818 // therefore eliminated, optimize it by adding inlined tests for: |
| 819 // - NULL -> return false. | 819 // - NULL -> return false. |
| 820 // - Smi -> compile time subtype check (only if dst class is not parameterized). | 820 // - Smi -> compile time subtype check (only if dst class is not parameterized). |
| 821 // - Class equality (only if class is not parameterized). | 821 // - Class equality (only if class is not parameterized). |
| 822 // Inputs: | 822 // Inputs: |
| 823 // - EAX: object. | 823 // - EAX: object. |
| 824 // - EDX: instantiator type arguments or raw_null. | 824 // - EDX: instantiator type arguments or raw_null. |
| 825 // - ECX: instantiator or raw_null. | 825 // - ECX: instantiator or raw_null. |
| 826 // Returns: | 826 // Returns: |
| 827 // - true or false in EAX. | 827 // - true or false in EAX. |
| 828 void FlowGraphCompiler::GenerateInstanceOf(intptr_t cid, | 828 void FlowGraphCompiler::GenerateInstanceOf(intptr_t cid, |
| 829 intptr_t token_index, | 829 intptr_t token_pos, |
| 830 intptr_t try_index, | 830 intptr_t try_index, |
| 831 const AbstractType& type, | 831 const AbstractType& type, |
| 832 bool negate_result) { | 832 bool negate_result) { |
| 833 ASSERT(type.IsFinalized() && !type.IsMalformed()); | 833 ASSERT(type.IsFinalized() && !type.IsMalformed()); |
| 834 | 834 |
| 835 const Immediate raw_null = | 835 const Immediate raw_null = |
| 836 Immediate(reinterpret_cast<intptr_t>(Object::null())); | 836 Immediate(reinterpret_cast<intptr_t>(Object::null())); |
| 837 Label is_instance, is_not_instance; | 837 Label is_instance, is_not_instance; |
| 838 __ pushl(ECX); // Store instantiator on stack. | 838 __ pushl(ECX); // Store instantiator on stack. |
| 839 __ pushl(EDX); // Store instantiator type arguments. | 839 __ pushl(EDX); // Store instantiator type arguments. |
| 840 // If type is instantiated and non-parameterized, we can inline code | 840 // If type is instantiated and non-parameterized, we can inline code |
| 841 // checking whether the tested instance is a Smi. | 841 // checking whether the tested instance is a Smi. |
| 842 if (type.IsInstantiated()) { | 842 if (type.IsInstantiated()) { |
| 843 // A null object is only an instance of Object and Dynamic, which has | 843 // A null object is only an instance of Object and Dynamic, which has |
| 844 // already been checked above (if the type is instantiated). So we can | 844 // already been checked above (if the type is instantiated). So we can |
| 845 // return false here if the instance is null (and if the type is | 845 // return false here if the instance is null (and if the type is |
| 846 // instantiated). | 846 // instantiated). |
| 847 // We can only inline this null check if the type is instantiated at compile | 847 // We can only inline this null check if the type is instantiated at compile |
| 848 // time, since an uninstantiated type at compile time could be Object or | 848 // time, since an uninstantiated type at compile time could be Object or |
| 849 // Dynamic at run time. | 849 // Dynamic at run time. |
| 850 __ cmpl(EAX, raw_null); | 850 __ cmpl(EAX, raw_null); |
| 851 __ j(EQUAL, &is_not_instance); | 851 __ j(EQUAL, &is_not_instance); |
| 852 } | 852 } |
| 853 // TODO(srdjan): Enable inlined checks. | 853 // TODO(srdjan): Enable inlined checks. |
| 854 // Generate inline instanceof test. | 854 // Generate inline instanceof test. |
| 855 SubtypeTestCache& test_cache = SubtypeTestCache::ZoneHandle(); | 855 SubtypeTestCache& test_cache = SubtypeTestCache::ZoneHandle(); |
| 856 test_cache = GenerateInlineInstanceof(cid, token_index, type, | 856 test_cache = GenerateInlineInstanceof(cid, token_pos, type, |
| 857 &is_instance, &is_not_instance); | 857 &is_instance, &is_not_instance); |
| 858 | 858 |
| 859 // Generate runtime call. | 859 // Generate runtime call. |
| 860 __ movl(EDX, Address(ESP, 0)); // Get instantiator type arguments. | 860 __ movl(EDX, Address(ESP, 0)); // Get instantiator type arguments. |
| 861 __ movl(ECX, Address(ESP, kWordSize)); // Get instantiator. | 861 __ movl(ECX, Address(ESP, kWordSize)); // Get instantiator. |
| 862 __ PushObject(Object::ZoneHandle()); // Make room for the result. | 862 __ PushObject(Object::ZoneHandle()); // Make room for the result. |
| 863 __ pushl(Immediate(Smi::RawValue(token_index))); // Source location. | 863 __ pushl(Immediate(Smi::RawValue(token_pos))); // Source location. |
| 864 __ pushl(Immediate(Smi::RawValue(cid))); // Computation id. | 864 __ pushl(Immediate(Smi::RawValue(cid))); // Computation id. |
| 865 __ pushl(EAX); // Push the instance. | 865 __ pushl(EAX); // Push the instance. |
| 866 __ PushObject(type); // Push the type. | 866 __ PushObject(type); // Push the type. |
| 867 __ pushl(ECX); // TODO(srdjan): Pass instantiator instead of null. | 867 __ pushl(ECX); // TODO(srdjan): Pass instantiator instead of null. |
| 868 __ pushl(EDX); // Instantiator type arguments. | 868 __ pushl(EDX); // Instantiator type arguments. |
| 869 __ LoadObject(EAX, test_cache); | 869 __ LoadObject(EAX, test_cache); |
| 870 __ pushl(EAX); | 870 __ pushl(EAX); |
| 871 GenerateCallRuntime(cid, token_index, try_index, kInstanceofRuntimeEntry); | 871 GenerateCallRuntime(cid, token_pos, try_index, kInstanceofRuntimeEntry); |
| 872 // Pop the two parameters supplied to the runtime entry. The result of the | 872 // Pop the two parameters supplied to the runtime entry. The result of the |
| 873 // instanceof runtime call will be left as the result of the operation. | 873 // instanceof runtime call will be left as the result of the operation. |
| 874 __ Drop(7); | 874 __ Drop(7); |
| 875 Label done; | 875 Label done; |
| 876 if (negate_result) { | 876 if (negate_result) { |
| 877 __ popl(EDX); | 877 __ popl(EDX); |
| 878 __ LoadObject(EAX, bool_true()); | 878 __ LoadObject(EAX, bool_true()); |
| 879 __ cmpl(EDX, EAX); | 879 __ cmpl(EDX, EAX); |
| 880 __ j(NOT_EQUAL, &done, Assembler::kNearJump); | 880 __ j(NOT_EQUAL, &done, Assembler::kNearJump); |
| 881 __ LoadObject(EAX, bool_false()); | 881 __ LoadObject(EAX, bool_false()); |
| (...skipping 20 matching lines...) Expand all Loading... |
| 902 // - Class equality (only if class is not parameterized). | 902 // - Class equality (only if class is not parameterized). |
| 903 // Inputs: | 903 // Inputs: |
| 904 // - EAX: object. | 904 // - EAX: object. |
| 905 // - EDX: instantiator type arguments or raw_null. | 905 // - EDX: instantiator type arguments or raw_null. |
| 906 // - ECX: instantiator or raw_null. | 906 // - ECX: instantiator or raw_null. |
| 907 // Returns: | 907 // Returns: |
| 908 // - object in EAX for successful assignable check (or throws TypeError). | 908 // - object in EAX for successful assignable check (or throws TypeError). |
| 909 // Performance notes: positive checks must be quick, negative checks can be slow | 909 // Performance notes: positive checks must be quick, negative checks can be slow |
| 910 // as they throw an exception. | 910 // as they throw an exception. |
| 911 void FlowGraphCompiler::GenerateAssertAssignable(intptr_t cid, | 911 void FlowGraphCompiler::GenerateAssertAssignable(intptr_t cid, |
| 912 intptr_t token_index, | 912 intptr_t token_pos, |
| 913 intptr_t try_index, | 913 intptr_t try_index, |
| 914 const AbstractType& dst_type, | 914 const AbstractType& dst_type, |
| 915 const String& dst_name) { | 915 const String& dst_name) { |
| 916 ASSERT(FLAG_enable_type_checks); | 916 ASSERT(FLAG_enable_type_checks); |
| 917 ASSERT(token_index >= 0); | 917 ASSERT(token_pos >= 0); |
| 918 ASSERT(!dst_type.IsNull()); | 918 ASSERT(!dst_type.IsNull()); |
| 919 ASSERT(dst_type.IsFinalized()); | 919 ASSERT(dst_type.IsFinalized()); |
| 920 // Assignable check is skipped in FlowGraphBuilder, not here. | 920 // Assignable check is skipped in FlowGraphBuilder, not here. |
| 921 ASSERT(dst_type.IsMalformed() || | 921 ASSERT(dst_type.IsMalformed() || |
| 922 (!dst_type.IsDynamicType() && !dst_type.IsObjectType())); | 922 (!dst_type.IsDynamicType() && !dst_type.IsObjectType())); |
| 923 ASSERT(!dst_type.IsVoidType()); | 923 ASSERT(!dst_type.IsVoidType()); |
| 924 __ pushl(ECX); // Store instantiator. | 924 __ pushl(ECX); // Store instantiator. |
| 925 __ pushl(EDX); // Store instantiator type arguments. | 925 __ pushl(EDX); // Store instantiator type arguments. |
| 926 // A null object is always assignable and is returned as result. | 926 // A null object is always assignable and is returned as result. |
| 927 const Immediate raw_null = | 927 const Immediate raw_null = |
| 928 Immediate(reinterpret_cast<intptr_t>(Object::null())); | 928 Immediate(reinterpret_cast<intptr_t>(Object::null())); |
| 929 Label is_assignable, runtime_call; | 929 Label is_assignable, runtime_call; |
| 930 __ cmpl(EAX, raw_null); | 930 __ cmpl(EAX, raw_null); |
| 931 __ j(EQUAL, &is_assignable); | 931 __ j(EQUAL, &is_assignable); |
| 932 | 932 |
| 933 // Generate throw new TypeError() if the type is malformed. | 933 // Generate throw new TypeError() if the type is malformed. |
| 934 if (dst_type.IsMalformed()) { | 934 if (dst_type.IsMalformed()) { |
| 935 const Error& error = Error::Handle(dst_type.malformed_error()); | 935 const Error& error = Error::Handle(dst_type.malformed_error()); |
| 936 const String& error_message = String::ZoneHandle( | 936 const String& error_message = String::ZoneHandle( |
| 937 String::NewSymbol(error.ToErrorCString())); | 937 String::NewSymbol(error.ToErrorCString())); |
| 938 __ PushObject(Object::ZoneHandle()); // Make room for the result. | 938 __ PushObject(Object::ZoneHandle()); // Make room for the result. |
| 939 __ pushl(Immediate(Smi::RawValue(token_index))); // Source location. | 939 __ pushl(Immediate(Smi::RawValue(token_pos))); // Source location. |
| 940 __ pushl(EAX); // Push the source object. | 940 __ pushl(EAX); // Push the source object. |
| 941 __ PushObject(dst_name); // Push the name of the destination. | 941 __ PushObject(dst_name); // Push the name of the destination. |
| 942 __ PushObject(error_message); | 942 __ PushObject(error_message); |
| 943 GenerateCallRuntime(cid, | 943 GenerateCallRuntime(cid, |
| 944 token_index, | 944 token_pos, |
| 945 try_index, | 945 try_index, |
| 946 kMalformedTypeErrorRuntimeEntry); | 946 kMalformedTypeErrorRuntimeEntry); |
| 947 // We should never return here. | 947 // We should never return here. |
| 948 __ int3(); | 948 __ int3(); |
| 949 | 949 |
| 950 __ Bind(&is_assignable); // For a null object. | 950 __ Bind(&is_assignable); // For a null object. |
| 951 return; | 951 return; |
| 952 } | 952 } |
| 953 | 953 |
| 954 // TODO(srdjan): Enable subtype test cache. | 954 // TODO(srdjan): Enable subtype test cache. |
| 955 // Generate inline type check, linking to runtime call if not assignable. | 955 // Generate inline type check, linking to runtime call if not assignable. |
| 956 SubtypeTestCache& test_cache = SubtypeTestCache::ZoneHandle(); | 956 SubtypeTestCache& test_cache = SubtypeTestCache::ZoneHandle(); |
| 957 test_cache = GenerateInlineInstanceof(cid, token_index, dst_type, | 957 test_cache = GenerateInlineInstanceof(cid, token_pos, dst_type, |
| 958 &is_assignable, &runtime_call); | 958 &is_assignable, &runtime_call); |
| 959 | 959 |
| 960 __ Bind(&runtime_call); | 960 __ Bind(&runtime_call); |
| 961 __ movl(EDX, Address(ESP, 0)); // Get instantiator type arguments. | 961 __ movl(EDX, Address(ESP, 0)); // Get instantiator type arguments. |
| 962 __ movl(ECX, Address(ESP, kWordSize)); // Get instantiator. | 962 __ movl(ECX, Address(ESP, kWordSize)); // Get instantiator. |
| 963 __ PushObject(Object::ZoneHandle()); // Make room for the result. | 963 __ PushObject(Object::ZoneHandle()); // Make room for the result. |
| 964 __ pushl(Immediate(Smi::RawValue(token_index))); // Source location. | 964 __ pushl(Immediate(Smi::RawValue(token_pos))); // Source location. |
| 965 __ pushl(Immediate(Smi::RawValue(cid))); // Computation id. | 965 __ pushl(Immediate(Smi::RawValue(cid))); // Computation id. |
| 966 __ pushl(EAX); // Push the source object. | 966 __ pushl(EAX); // Push the source object. |
| 967 __ PushObject(dst_type); // Push the type of the destination. | 967 __ PushObject(dst_type); // Push the type of the destination. |
| 968 __ pushl(ECX); // Instantiator. | 968 __ pushl(ECX); // Instantiator. |
| 969 __ pushl(EDX); // Instantiator type arguments. | 969 __ pushl(EDX); // Instantiator type arguments. |
| 970 __ PushObject(dst_name); // Push the name of the destination. | 970 __ PushObject(dst_name); // Push the name of the destination. |
| 971 __ LoadObject(EAX, test_cache); | 971 __ LoadObject(EAX, test_cache); |
| 972 __ pushl(EAX); | 972 __ pushl(EAX); |
| 973 GenerateCallRuntime(cid, | 973 GenerateCallRuntime(cid, |
| 974 token_index, | 974 token_pos, |
| 975 try_index, | 975 try_index, |
| 976 kTypeCheckRuntimeEntry); | 976 kTypeCheckRuntimeEntry); |
| 977 // Pop the parameters supplied to the runtime entry. The result of the | 977 // Pop the parameters supplied to the runtime entry. The result of the |
| 978 // type check runtime call is the checked value. | 978 // type check runtime call is the checked value. |
| 979 __ Drop(8); | 979 __ Drop(8); |
| 980 __ popl(EAX); | 980 __ popl(EAX); |
| 981 | 981 |
| 982 __ Bind(&is_assignable); | 982 __ Bind(&is_assignable); |
| 983 __ popl(EDX); // Remove pushed instantiator type arguments.. | 983 __ popl(EDX); // Remove pushed instantiator type arguments.. |
| 984 __ popl(ECX); // Remove pushed instantiator. | 984 __ popl(ECX); // Remove pushed instantiator. |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1046 __ cvtsi2sd(result, temp); | 1046 __ cvtsi2sd(result, temp); |
| 1047 __ Bind(&done); | 1047 __ Bind(&done); |
| 1048 } | 1048 } |
| 1049 | 1049 |
| 1050 | 1050 |
| 1051 #undef __ | 1051 #undef __ |
| 1052 | 1052 |
| 1053 } // namespace dart | 1053 } // namespace dart |
| 1054 | 1054 |
| 1055 #endif // defined TARGET_ARCH_IA32 | 1055 #endif // defined TARGET_ARCH_IA32 |
| OLD | NEW |