| 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" | 5 #include "vm/globals.h" |
| 6 #if defined(TARGET_ARCH_ARM) | 6 #if defined(TARGET_ARCH_ARM) |
| 7 | 7 |
| 8 #include "vm/assembler.h" | 8 #include "vm/assembler.h" |
| 9 #include "vm/code_generator.h" | 9 #include "vm/code_generator.h" |
| 10 #include "vm/dart_entry.h" | 10 #include "vm/dart_entry.h" |
| 11 #include "vm/flow_graph_compiler.h" |
| 11 #include "vm/instructions.h" | 12 #include "vm/instructions.h" |
| 12 #include "vm/stack_frame.h" | 13 #include "vm/stack_frame.h" |
| 13 #include "vm/stub_code.h" | 14 #include "vm/stub_code.h" |
| 14 | 15 |
| 15 #define __ assembler-> | 16 #define __ assembler-> |
| 16 | 17 |
| 17 namespace dart { | 18 namespace dart { |
| 18 | 19 |
| 20 DEFINE_FLAG(bool, inline_alloc, true, "Inline allocation of objects."); |
| 21 DEFINE_FLAG(bool, use_slow_path, false, |
| 22 "Set to true for debugging & verifying the slow paths."); |
| 23 DECLARE_FLAG(int, optimization_counter_threshold); |
| 24 DECLARE_FLAG(bool, trace_optimized_ic_calls); |
| 25 |
| 26 |
| 19 // Input parameters: | 27 // Input parameters: |
| 20 // LR : return address. | 28 // LR : return address. |
| 21 // SP : address of last argument in argument array. | 29 // SP : address of last argument in argument array. |
| 22 // SP + 4*R4 - 4 : address of first argument in argument array. | 30 // SP + 4*R4 - 4 : address of first argument in argument array. |
| 23 // SP + 4*R4 : address of return value. | 31 // SP + 4*R4 : address of return value. |
| 24 // R5 : address of the runtime function to call. | 32 // R5 : address of the runtime function to call. |
| 25 // R4 : number of arguments to the call. | 33 // R4 : number of arguments to the call. |
| 26 void StubCode::GenerateCallToRuntimeStub(Assembler* assembler) { | 34 void StubCode::GenerateCallToRuntimeStub(Assembler* assembler) { |
| 27 const intptr_t isolate_offset = NativeArguments::isolate_offset(); | 35 const intptr_t isolate_offset = NativeArguments::isolate_offset(); |
| 28 const intptr_t argc_tag_offset = NativeArguments::argc_tag_offset(); | 36 const intptr_t argc_tag_offset = NativeArguments::argc_tag_offset(); |
| (...skipping 317 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 346 void StubCode::GenerateAllocateContextStub(Assembler* assembler) { | 354 void StubCode::GenerateAllocateContextStub(Assembler* assembler) { |
| 347 __ Unimplemented("AllocateContext stub"); | 355 __ Unimplemented("AllocateContext stub"); |
| 348 } | 356 } |
| 349 | 357 |
| 350 | 358 |
| 351 void StubCode::GenerateUpdateStoreBufferStub(Assembler* assembler) { | 359 void StubCode::GenerateUpdateStoreBufferStub(Assembler* assembler) { |
| 352 __ Unimplemented("UpdateStoreBuffer stub"); | 360 __ Unimplemented("UpdateStoreBuffer stub"); |
| 353 } | 361 } |
| 354 | 362 |
| 355 | 363 |
| 364 // Called for inline allocation of objects. |
| 365 // Input parameters: |
| 366 // LR : return address. |
| 367 // SP + 4 : type arguments object (only if class is parameterized). |
| 368 // SP + 0 : type arguments of instantiator (only if class is parameterized). |
| 356 void StubCode::GenerateAllocationStubForClass(Assembler* assembler, | 369 void StubCode::GenerateAllocationStubForClass(Assembler* assembler, |
| 357 const Class& cls) { | 370 const Class& cls) { |
| 358 __ Unimplemented("AllocateObject stub"); | 371 // The generated code is different if the class is parameterized. |
| 372 const bool is_cls_parameterized = |
| 373 cls.type_arguments_field_offset() != Class::kNoTypeArguments; |
| 374 // kInlineInstanceSize is a constant used as a threshold for determining |
| 375 // when the object initialization should be done as a loop or as |
| 376 // straight line code. |
| 377 const int kInlineInstanceSize = 12; |
| 378 const intptr_t instance_size = cls.instance_size(); |
| 379 ASSERT(instance_size > 0); |
| 380 const intptr_t type_args_size = InstantiatedTypeArguments::InstanceSize(); |
| 381 if (FLAG_inline_alloc && |
| 382 PageSpace::IsPageAllocatableSize(instance_size + type_args_size)) { |
| 383 Label slow_case; |
| 384 Heap* heap = Isolate::Current()->heap(); |
| 385 __ LoadImmediate(R5, heap->TopAddress()); |
| 386 __ ldr(R2, Address(R5, 0)); |
| 387 __ AddImmediate(R3, R2, instance_size); |
| 388 if (is_cls_parameterized) { |
| 389 __ ldm(IA, SP, (1 << R0) | (1 << R1)); |
| 390 __ mov(R4, ShifterOperand(R3)); |
| 391 // A new InstantiatedTypeArguments object only needs to be allocated if |
| 392 // the instantiator is provided (not kNoInstantiator, but may be null). |
| 393 __ CompareImmediate(R0, Smi::RawValue(StubCode::kNoInstantiator)); |
| 394 __ AddImmediate(R3, type_args_size, NE); |
| 395 // R4: potential new object end and, if R4 != R3, potential new |
| 396 // InstantiatedTypeArguments object start. |
| 397 } |
| 398 // Check if the allocation fits into the remaining space. |
| 399 // R2: potential new object start. |
| 400 // R3: potential next object start. |
| 401 if (FLAG_use_slow_path) { |
| 402 __ b(&slow_case); |
| 403 } else { |
| 404 __ LoadImmediate(IP, heap->EndAddress()); |
| 405 __ cmp(R3, ShifterOperand(IP)); |
| 406 __ b(&slow_case, CS); // Branch if unsigned higher or equal. |
| 407 } |
| 408 |
| 409 // Successfully allocated the object(s), now update top to point to |
| 410 // next object start and initialize the object. |
| 411 __ str(R3, Address(R5, 0)); |
| 412 |
| 413 if (is_cls_parameterized) { |
| 414 // Initialize the type arguments field in the object. |
| 415 // R2: new object start. |
| 416 // R4: potential new object end and, if R4 != R3, potential new |
| 417 // InstantiatedTypeArguments object start. |
| 418 // R3: next object start. |
| 419 Label type_arguments_ready; |
| 420 __ cmp(R4, ShifterOperand(R3)); |
| 421 __ b(&type_arguments_ready, EQ); |
| 422 // Initialize InstantiatedTypeArguments object at R4. |
| 423 __ str(R1, Address(R4, |
| 424 InstantiatedTypeArguments::uninstantiated_type_arguments_offset())); |
| 425 __ str(R0, Address(R4, |
| 426 InstantiatedTypeArguments::instantiator_type_arguments_offset())); |
| 427 const Class& ita_cls = |
| 428 Class::ZoneHandle(Object::instantiated_type_arguments_class()); |
| 429 // Set the tags. |
| 430 uword tags = 0; |
| 431 tags = RawObject::SizeTag::update(type_args_size, tags); |
| 432 tags = RawObject::ClassIdTag::update(ita_cls.id(), tags); |
| 433 __ LoadImmediate(R0, tags); |
| 434 __ str(R0, Address(R4, Instance::tags_offset())); |
| 435 // Set the new InstantiatedTypeArguments object (R4) as the type |
| 436 // arguments (R1) of the new object (R2). |
| 437 __ add(R1, R4, ShifterOperand(kHeapObjectTag)); |
| 438 // Set R3 to new object end. |
| 439 __ mov(R3, ShifterOperand(R4)); |
| 440 __ Bind(&type_arguments_ready); |
| 441 // R2: new object. |
| 442 // R1: new object type arguments. |
| 443 } |
| 444 |
| 445 // R2: new object start. |
| 446 // R3: next object start. |
| 447 // R1: new object type arguments (if is_cls_parameterized). |
| 448 // Set the tags. |
| 449 uword tags = 0; |
| 450 tags = RawObject::SizeTag::update(instance_size, tags); |
| 451 ASSERT(cls.id() != kIllegalCid); |
| 452 tags = RawObject::ClassIdTag::update(cls.id(), tags); |
| 453 __ LoadImmediate(R0, tags); |
| 454 __ str(R0, Address(R2, Instance::tags_offset())); |
| 455 |
| 456 // Initialize the remaining words of the object. |
| 457 __ LoadImmediate(R0, reinterpret_cast<intptr_t>(Object::null())); |
| 458 |
| 459 // R0: raw null. |
| 460 // R2: new object start. |
| 461 // R3: next object start. |
| 462 // R1: new object type arguments (if is_cls_parameterized). |
| 463 // First try inlining the initialization without a loop. |
| 464 if (instance_size < (kInlineInstanceSize * kWordSize)) { |
| 465 // Check if the object contains any non-header fields. |
| 466 // Small objects are initialized using a consecutive set of writes. |
| 467 for (intptr_t current_offset = sizeof(RawObject); |
| 468 current_offset < instance_size; |
| 469 current_offset += kWordSize) { |
| 470 __ StoreToOffset(kStoreWord, R0, R2, current_offset); |
| 471 } |
| 472 } else { |
| 473 __ add(R4, R2, ShifterOperand(sizeof(RawObject))); |
| 474 // Loop until the whole object is initialized. |
| 475 // R0: raw null. |
| 476 // R2: new object. |
| 477 // R3: next object start. |
| 478 // R4: next word to be initialized. |
| 479 // R1: new object type arguments (if is_cls_parameterized). |
| 480 Label init_loop; |
| 481 Label done; |
| 482 __ Bind(&init_loop); |
| 483 __ cmp(R4, ShifterOperand(R3)); |
| 484 __ b(&done, CS); |
| 485 __ str(R0, Address(R4, 0)); |
| 486 __ AddImmediate(R4, kWordSize); |
| 487 __ b(&init_loop); |
| 488 __ Bind(&done); |
| 489 } |
| 490 if (is_cls_parameterized) { |
| 491 // R1: new object type arguments. |
| 492 // Set the type arguments in the new object. |
| 493 __ StoreToOffset(kStoreWord, R1, R2, cls.type_arguments_field_offset()); |
| 494 } |
| 495 // Done allocating and initializing the instance. |
| 496 // R2: new object still missing its heap tag. |
| 497 __ add(R0, R2, ShifterOperand(kHeapObjectTag)); |
| 498 __ Ret(); |
| 499 |
| 500 __ Bind(&slow_case); |
| 501 } |
| 502 if (is_cls_parameterized) { |
| 503 __ ldm(IA, SP, (1 << R0) | (1 << R1)); |
| 504 } |
| 505 // Create a stub frame as we are pushing some objects on the stack before |
| 506 // calling into the runtime. |
| 507 __ EnterStubFrame(true); // Uses pool pointer to pass cls to runtime. |
| 508 __ LoadImmediate(R2, reinterpret_cast<intptr_t>(Object::null())); |
| 509 __ Push(R2); // Setup space on stack for return value. |
| 510 __ PushObject(cls); // Push class of object to be allocated. |
| 511 if (is_cls_parameterized) { |
| 512 // Push type arguments of object to be allocated and of instantiator. |
| 513 __ PushList((1 << R0) | (1 << R1)); |
| 514 } else { |
| 515 // Push null type arguments and kNoInstantiator. |
| 516 __ LoadImmediate(R1, Smi::RawValue(StubCode::kNoInstantiator)); |
| 517 __ PushList((1 << R1) | (1 << R2)); |
| 518 } |
| 519 __ CallRuntime(kAllocateObjectRuntimeEntry); // Allocate object. |
| 520 __ Drop(3); // Pop arguments. |
| 521 __ Pop(R0); // Pop result (newly allocated object). |
| 522 // R0: new object |
| 523 // Restore the frame pointer. |
| 524 __ LeaveStubFrame(true); |
| 525 __ Ret(); |
| 359 } | 526 } |
| 360 | 527 |
| 361 | 528 |
| 362 void StubCode::GenerateAllocationStubForClosure(Assembler* assembler, | 529 void StubCode::GenerateAllocationStubForClosure(Assembler* assembler, |
| 363 const Function& func) { | 530 const Function& func) { |
| 364 __ Unimplemented("AllocateClosure stub"); | 531 __ Unimplemented("AllocateClosure stub"); |
| 365 } | 532 } |
| 366 | 533 |
| 367 | 534 |
| 368 void StubCode::GenerateCallNoSuchMethodFunctionStub(Assembler* assembler) { | 535 void StubCode::GenerateCallNoSuchMethodFunctionStub(Assembler* assembler) { |
| 369 __ Unimplemented("CallNoSuchMethodFunction stub"); | 536 __ Unimplemented("CallNoSuchMethodFunction stub"); |
| 370 } | 537 } |
| 371 | 538 |
| 372 | 539 |
| 373 void StubCode::GenerateOptimizedUsageCounterIncrement(Assembler* assembler) { | 540 void StubCode::GenerateOptimizedUsageCounterIncrement(Assembler* assembler) { |
| 374 __ Unimplemented("OptimizedUsageCounterIncrement stub"); | 541 __ Unimplemented("OptimizedUsageCounterIncrement stub"); |
| 375 } | 542 } |
| 376 | 543 |
| 377 | 544 |
| 545 // Loads function into 'temp_reg'. |
| 378 void StubCode::GenerateUsageCounterIncrement(Assembler* assembler, | 546 void StubCode::GenerateUsageCounterIncrement(Assembler* assembler, |
| 379 Register temp_reg) { | 547 Register temp_reg) { |
| 380 __ Unimplemented("UsageCounterIncrement stub"); | 548 Register ic_reg = R5; |
| 381 } | 549 Register func_reg = temp_reg; |
| 382 | 550 ASSERT(temp_reg == R6); |
| 383 | 551 __ ldr(func_reg, FieldAddress(ic_reg, ICData::function_offset())); |
| 552 __ ldr(R7, FieldAddress(func_reg, Function::usage_counter_offset())); |
| 553 Label is_hot; |
| 554 if (FlowGraphCompiler::CanOptimize()) { |
| 555 ASSERT(FLAG_optimization_counter_threshold > 1); |
| 556 // The usage_counter is always less than FLAG_optimization_counter_threshold |
| 557 // except when the function gets optimized. |
| 558 __ CompareImmediate(R7, FLAG_optimization_counter_threshold); |
| 559 __ b(&is_hot, EQ); |
| 560 // As long as VM has no OSR do not optimize in the middle of the function |
| 561 // but only at exit so that we have collected all type feedback before |
| 562 // optimizing. |
| 563 } |
| 564 __ add(R7, R7, ShifterOperand(1)); |
| 565 __ str(R7, FieldAddress(func_reg, Function::usage_counter_offset())); |
| 566 __ Bind(&is_hot); |
| 567 } |
| 568 |
| 569 |
| 570 // Generate inline cache check for 'num_args'. |
| 571 // LR: return address |
| 572 // R5: Inline cache data object. |
| 573 // R4: Arguments descriptor array. |
| 574 // Control flow: |
| 575 // - If receiver is null -> jump to IC miss. |
| 576 // - If receiver is Smi -> load Smi class. |
| 577 // - If receiver is not-Smi -> load receiver's class. |
| 578 // - Check if 'num_args' (including receiver) match any IC data group. |
| 579 // - Match found -> jump to target. |
| 580 // - Match not found -> jump to IC miss. |
| 384 void StubCode::GenerateNArgsCheckInlineCacheStub(Assembler* assembler, | 581 void StubCode::GenerateNArgsCheckInlineCacheStub(Assembler* assembler, |
| 385 intptr_t num_args) { | 582 intptr_t num_args) { |
| 386 __ Unimplemented("NArgsCheckInlineCache stub"); | 583 ASSERT(num_args > 0); |
| 387 } | 584 #if defined(DEBUG) |
| 388 | 585 { Label ok; |
| 389 | 586 // Check that the IC data array has NumberOfArgumentsChecked() == num_args. |
| 587 // 'num_args_tested' is stored as an untagged int. |
| 588 __ ldr(R6, FieldAddress(R5, ICData::num_args_tested_offset())); |
| 589 __ CompareImmediate(R6, num_args); |
| 590 __ b(&ok, EQ); |
| 591 __ Stop("Incorrect stub for IC data"); |
| 592 __ Bind(&ok); |
| 593 } |
| 594 #endif // DEBUG |
| 595 |
| 596 // Preserve return address, since LR is needed for subroutine call. |
| 597 __ mov(R8, ShifterOperand(LR)); |
| 598 // Loop that checks if there is an IC data match. |
| 599 Label loop, update, test, found, get_class_id_as_smi; |
| 600 // R5: IC data object (preserved). |
| 601 __ ldr(R6, FieldAddress(R5, ICData::ic_data_offset())); |
| 602 // R6: ic_data_array with check entries: classes and target functions. |
| 603 __ AddImmediate(R6, R6, Array::data_offset() - kHeapObjectTag); |
| 604 // R6: points directly to the first ic data array element. |
| 605 |
| 606 // Get the receiver's class ID (first read number of arguments from |
| 607 // arguments descriptor array and then access the receiver from the stack). |
| 608 __ ldr(R7, FieldAddress(R4, ArgumentsDescriptor::count_offset())); |
| 609 __ sub(R7, R7, ShifterOperand(Smi::RawValue(1))); |
| 610 __ ldr(R0, Address(SP, R7, LSL, 1)); // R7 (argument_count - 1) is smi. |
| 611 __ bl(&get_class_id_as_smi); |
| 612 // R7: argument_count - 1 (smi). |
| 613 // R0: receiver's class ID (smi). |
| 614 __ ldr(R1, Address(R6, 0)); // First class id (smi) to check. |
| 615 __ b(&test); |
| 616 |
| 617 __ Bind(&loop); |
| 618 for (int i = 0; i < num_args; i++) { |
| 619 if (i > 0) { |
| 620 // If not the first, load the next argument's class ID. |
| 621 __ AddImmediate(R0, R7, Smi::RawValue(-i)); |
| 622 __ ldr(R0, Address(SP, R0, LSL, 1)); |
| 623 __ bl(&get_class_id_as_smi); |
| 624 // R0: next argument class ID (smi). |
| 625 __ LoadFromOffset(kLoadWord, R1, R6, i * kWordSize); |
| 626 // R1: next class ID to check (smi). |
| 627 } |
| 628 __ cmp(R0, ShifterOperand(R1)); // Class id match? |
| 629 if (i < (num_args - 1)) { |
| 630 __ b(&update, NE); // Continue. |
| 631 } else { |
| 632 // Last check, all checks before matched. |
| 633 __ mov(LR, ShifterOperand(R8), EQ); // Restore return address if found. |
| 634 __ b(&found, EQ); // Break. |
| 635 } |
| 636 } |
| 637 __ Bind(&update); |
| 638 // Reload receiver class ID. It has not been destroyed when num_args == 1. |
| 639 if (num_args > 1) { |
| 640 __ ldr(R0, Address(SP, R7, LSL, 1)); |
| 641 __ bl(&get_class_id_as_smi); |
| 642 } |
| 643 |
| 644 const intptr_t entry_size = ICData::TestEntryLengthFor(num_args) * kWordSize; |
| 645 __ AddImmediate(R6, entry_size); // Next entry. |
| 646 __ ldr(R1, Address(R6, 0)); // Next class ID. |
| 647 |
| 648 __ Bind(&test); |
| 649 __ CompareImmediate(R1, Smi::RawValue(kIllegalCid)); // Done? |
| 650 __ b(&loop, NE); |
| 651 |
| 652 // IC miss. |
| 653 // Restore return address. |
| 654 __ mov(LR, ShifterOperand(R8)); |
| 655 |
| 656 // Compute address of arguments (first read number of arguments from |
| 657 // arguments descriptor array and then compute address on the stack). |
| 658 // R7: argument_count - 1 (smi). |
| 659 __ add(R7, SP, ShifterOperand(R7, LSL, 1)); // R7 is Smi. |
| 660 // R7: address of receiver. |
| 661 // Create a stub frame as we are pushing some objects on the stack before |
| 662 // calling into the runtime. |
| 663 __ EnterStubFrame(); |
| 664 __ LoadImmediate(R0, reinterpret_cast<intptr_t>(Object::null())); |
| 665 // Preserve IC data object and arguments descriptor array and |
| 666 // setup space on stack for result (target code object). |
| 667 __ PushList((1 << R0) | (1 << R4) | (1 << R5)); |
| 668 // Push call arguments. |
| 669 for (intptr_t i = 0; i < num_args; i++) { |
| 670 __ LoadFromOffset(kLoadWord, IP, R7, -i * kWordSize); |
| 671 __ Push(IP); |
| 672 } |
| 673 // Pass IC data object and arguments descriptor array. |
| 674 __ PushList((1 << R4) | (1 << R5)); |
| 675 |
| 676 if (num_args == 1) { |
| 677 __ CallRuntime(kInlineCacheMissHandlerOneArgRuntimeEntry); |
| 678 } else if (num_args == 2) { |
| 679 __ CallRuntime(kInlineCacheMissHandlerTwoArgsRuntimeEntry); |
| 680 } else if (num_args == 3) { |
| 681 __ CallRuntime(kInlineCacheMissHandlerThreeArgsRuntimeEntry); |
| 682 } else { |
| 683 UNIMPLEMENTED(); |
| 684 } |
| 685 // Remove the call arguments pushed earlier, including the IC data object |
| 686 // and the arguments descriptor array. |
| 687 __ Drop(num_args + 2); |
| 688 // Pop returned code object into R0 (null if not found). |
| 689 // Restore arguments descriptor array and IC data array. |
| 690 __ PopList((1 << R0) | (1 << R4) | (1 << R5)); |
| 691 __ LeaveStubFrame(); |
| 692 Label call_target_function; |
| 693 __ CompareImmediate(R0, reinterpret_cast<intptr_t>(Object::null())); |
| 694 __ b(&call_target_function, NE); |
| 695 // NoSuchMethod or closure. |
| 696 // Mark IC call that it may be a closure call that does not collect |
| 697 // type feedback. |
| 698 __ mov(IP, ShifterOperand(1)); |
| 699 __ strb(IP, FieldAddress(R5, ICData::is_closure_call_offset())); |
| 700 __ Branch(&StubCode::InstanceFunctionLookupLabel()); |
| 701 |
| 702 __ Bind(&found); |
| 703 // R6: Pointer to an IC data check group. |
| 704 const intptr_t target_offset = ICData::TargetIndexFor(num_args) * kWordSize; |
| 705 const intptr_t count_offset = ICData::CountIndexFor(num_args) * kWordSize; |
| 706 __ LoadFromOffset(kLoadWord, R0, R6, target_offset); |
| 707 __ LoadFromOffset(kLoadWord, R1, R6, count_offset); |
| 708 __ adds(R1, R1, ShifterOperand(Smi::RawValue(1))); |
| 709 __ StoreToOffset(kStoreWord, R1, R6, count_offset); |
| 710 __ b(&call_target_function, VC); // No overflow. |
| 711 __ LoadImmediate(R1, Smi::RawValue(Smi::kMaxValue)); |
| 712 __ StoreToOffset(kStoreWord, R1, R6, count_offset); |
| 713 |
| 714 __ Bind(&call_target_function); |
| 715 // R0: Target function. |
| 716 __ ldr(R0, FieldAddress(R0, Function::code_offset())); |
| 717 __ ldr(R0, FieldAddress(R0, Code::instructions_offset())); |
| 718 __ AddImmediate(R0, Instructions::HeaderSize() - kHeapObjectTag); |
| 719 __ bx(R0); |
| 720 |
| 721 // Instance in R0, return its class-id in R0 as Smi. |
| 722 __ Bind(&get_class_id_as_smi); |
| 723 Label not_smi; |
| 724 // Test if Smi -> load Smi class for comparison. |
| 725 __ tst(R0, ShifterOperand(kSmiTagMask)); |
| 726 __ mov(R0, ShifterOperand(Smi::RawValue(kSmiCid)), EQ); |
| 727 __ bx(LR, EQ); |
| 728 __ LoadClassId(R0, R0); |
| 729 __ SmiTag(R0); |
| 730 __ bx(LR); |
| 731 } |
| 732 |
| 733 |
| 734 // Use inline cache data array to invoke the target or continue in inline |
| 735 // cache miss handler. Stub for 1-argument check (receiver class). |
| 736 // LR: Return address. |
| 737 // R5: Inline cache data object. |
| 738 // R4: Arguments descriptor array. |
| 739 // Inline cache data object structure: |
| 740 // 0: function-name |
| 741 // 1: N, number of arguments checked. |
| 742 // 2 .. (length - 1): group of checks, each check containing: |
| 743 // - N classes. |
| 744 // - 1 target function. |
| 390 void StubCode::GenerateOneArgCheckInlineCacheStub(Assembler* assembler) { | 745 void StubCode::GenerateOneArgCheckInlineCacheStub(Assembler* assembler) { |
| 391 __ Unimplemented("GenerateOneArgCheckInlineCacheStub stub"); | 746 GenerateUsageCounterIncrement(assembler, R6); |
| 747 GenerateNArgsCheckInlineCacheStub(assembler, 1); |
| 392 } | 748 } |
| 393 | 749 |
| 394 | 750 |
| 395 void StubCode::GenerateTwoArgsCheckInlineCacheStub(Assembler* assembler) { | 751 void StubCode::GenerateTwoArgsCheckInlineCacheStub(Assembler* assembler) { |
| 396 __ Unimplemented("GenerateTwoArgsCheckInlineCacheStub stub"); | 752 GenerateUsageCounterIncrement(assembler, R6); |
| 753 GenerateNArgsCheckInlineCacheStub(assembler, 2); |
| 397 } | 754 } |
| 398 | 755 |
| 399 | 756 |
| 400 void StubCode::GenerateThreeArgsCheckInlineCacheStub(Assembler* assembler) { | 757 void StubCode::GenerateThreeArgsCheckInlineCacheStub(Assembler* assembler) { |
| 401 __ Unimplemented("GenerateThreeArgsCheckInlineCacheStub stub"); | 758 GenerateUsageCounterIncrement(assembler, R6); |
| 402 } | 759 GenerateNArgsCheckInlineCacheStub(assembler, 3); |
| 403 | 760 } |
| 404 | 761 |
| 762 |
| 405 void StubCode::GenerateOneArgOptimizedCheckInlineCacheStub( | 763 void StubCode::GenerateOneArgOptimizedCheckInlineCacheStub( |
| 406 Assembler* assembler) { | 764 Assembler* assembler) { |
| 407 GenerateOptimizedUsageCounterIncrement(assembler); | 765 GenerateOptimizedUsageCounterIncrement(assembler); |
| 408 GenerateNArgsCheckInlineCacheStub(assembler, 1); | 766 GenerateNArgsCheckInlineCacheStub(assembler, 1); |
| 409 } | 767 } |
| 410 | 768 |
| 411 | 769 |
| 412 void StubCode::GenerateTwoArgsOptimizedCheckInlineCacheStub( | 770 void StubCode::GenerateTwoArgsOptimizedCheckInlineCacheStub( |
| 413 Assembler* assembler) { | 771 Assembler* assembler) { |
| 414 GenerateOptimizedUsageCounterIncrement(assembler); | 772 GenerateOptimizedUsageCounterIncrement(assembler); |
| (...skipping 26 matching lines...) Expand all Loading... |
| 441 void StubCode::GenerateBreakpointReturnStub(Assembler* assembler) { | 799 void StubCode::GenerateBreakpointReturnStub(Assembler* assembler) { |
| 442 __ Unimplemented("BreakpointReturn stub"); | 800 __ Unimplemented("BreakpointReturn stub"); |
| 443 } | 801 } |
| 444 | 802 |
| 445 | 803 |
| 446 void StubCode::GenerateBreakpointDynamicStub(Assembler* assembler) { | 804 void StubCode::GenerateBreakpointDynamicStub(Assembler* assembler) { |
| 447 __ Unimplemented("BreakpointDynamic stub"); | 805 __ Unimplemented("BreakpointDynamic stub"); |
| 448 } | 806 } |
| 449 | 807 |
| 450 | 808 |
| 451 void StubCode::GenerateSubtype1TestCacheStub(Assembler* assembler) { | 809 // Used to check class and type arguments. Arguments passed in registers: |
| 452 __ Unimplemented("Subtype1TestCache Stub"); | 810 // LR: return address. |
| 811 // R0: instance (must be preserved). |
| 812 // R1: instantiator type arguments or NULL. |
| 813 // R2: cache array. |
| 814 // Result in R1: null -> not found, otherwise result (true or false). |
| 815 static void GenerateSubtypeNTestCacheStub(Assembler* assembler, int n) { |
| 816 ASSERT((1 <= n) && (n <= 3)); |
| 817 if (n > 1) { |
| 818 // Get instance type arguments. |
| 819 __ LoadClass(R3, R0, R4); |
| 820 // Compute instance type arguments into R4. |
| 821 Label has_no_type_arguments; |
| 822 __ LoadImmediate(R4, reinterpret_cast<intptr_t>(Object::null())); |
| 823 __ ldr(R5, FieldAddress(R3, |
| 824 Class::type_arguments_field_offset_in_words_offset())); |
| 825 __ CompareImmediate(R5, Class::kNoTypeArguments); |
| 826 __ b(&has_no_type_arguments, EQ); |
| 827 __ add(R5, R0, ShifterOperand(R5, LSL, 2)); |
| 828 __ ldr(R4, FieldAddress(R5, 0)); |
| 829 __ Bind(&has_no_type_arguments); |
| 830 } |
| 831 __ LoadClassId(R3, R0); |
| 832 // R0: instance. |
| 833 // R1: instantiator type arguments or NULL. |
| 834 // R2: SubtypeTestCache. |
| 835 // R3: instance class id. |
| 836 // R4: instance type arguments (null if none), used only if n > 1. |
| 837 __ ldr(R2, FieldAddress(R2, SubtypeTestCache::cache_offset())); |
| 838 __ AddImmediate(R2, Array::data_offset() - kHeapObjectTag); |
| 839 |
| 840 Label loop, found, not_found, next_iteration; |
| 841 // R2: Entry start. |
| 842 // R3: instance class id. |
| 843 // R4: instance type arguments. |
| 844 __ SmiTag(R3); |
| 845 __ Bind(&loop); |
| 846 __ ldr(R5, Address(R2, kWordSize * SubtypeTestCache::kInstanceClassId)); |
| 847 __ CompareImmediate(R5, reinterpret_cast<intptr_t>(Object::null())); |
| 848 __ b(¬_found, EQ); |
| 849 __ cmp(R5, ShifterOperand(R3)); |
| 850 if (n == 1) { |
| 851 __ b(&found, EQ); |
| 852 } else { |
| 853 __ b(&next_iteration, NE); |
| 854 __ ldr(R5, |
| 855 Address(R2, kWordSize * SubtypeTestCache::kInstanceTypeArguments)); |
| 856 __ cmp(R5, ShifterOperand(R4)); |
| 857 if (n == 2) { |
| 858 __ b(&found, EQ); |
| 859 } else { |
| 860 __ b(&next_iteration, NE); |
| 861 __ ldr(R5, Address(R2, kWordSize * |
| 862 SubtypeTestCache::kInstantiatorTypeArguments)); |
| 863 __ cmp(R5, ShifterOperand(R1)); |
| 864 __ b(&found, EQ); |
| 865 } |
| 866 } |
| 867 __ Bind(&next_iteration); |
| 868 __ AddImmediate(R2, kWordSize * SubtypeTestCache::kTestEntryLength); |
| 869 __ b(&loop); |
| 870 // Fall through to not found. |
| 871 __ Bind(¬_found); |
| 872 __ LoadImmediate(R1, reinterpret_cast<intptr_t>(Object::null())); |
| 873 __ Ret(); |
| 874 |
| 875 __ Bind(&found); |
| 876 __ ldr(R1, Address(R2, kWordSize * SubtypeTestCache::kTestResult)); |
| 877 __ Ret(); |
| 453 } | 878 } |
| 454 | 879 |
| 455 | 880 |
| 456 void StubCode::GenerateSubtype2TestCacheStub(Assembler* assembler) { | 881 // Used to check class and type arguments. Arguments passed in registers: |
| 457 __ Unimplemented("Subtype2TestCache Stub"); | 882 // LR: return address. |
| 883 // R0: instance (must be preserved). |
| 884 // R1: instantiator type arguments or NULL. |
| 885 // R2: cache array. |
| 886 // Result in R1: null -> not found, otherwise result (true or false). |
| 887 void StubCode::GenerateSubtype1TestCacheStub(Assembler* assembler) { |
| 888 GenerateSubtypeNTestCacheStub(assembler, 1); |
| 458 } | 889 } |
| 459 | 890 |
| 460 | 891 |
| 461 void StubCode::GenerateSubtype3TestCacheStub(Assembler* assembler) { | 892 // Used to check class and type arguments. Arguments passed in registers: |
| 462 __ Unimplemented("Subtype3TestCache Stub"); | 893 // LR: return address. |
| 894 // R0: instance (must be preserved). |
| 895 // R1: instantiator type arguments or NULL. |
| 896 // R2: cache array. |
| 897 // Result in R1: null -> not found, otherwise result (true or false). |
| 898 void StubCode::GenerateSubtype2TestCacheStub(Assembler* assembler) { |
| 899 GenerateSubtypeNTestCacheStub(assembler, 2); |
| 463 } | 900 } |
| 464 | 901 |
| 465 | 902 |
| 903 // Used to check class and type arguments. Arguments passed in registers: |
| 904 // LR: return address. |
| 905 // R0: instance (must be preserved). |
| 906 // R1: instantiator type arguments or NULL. |
| 907 // R2: cache array. |
| 908 // Result in R1: null -> not found, otherwise result (true or false). |
| 909 void StubCode::GenerateSubtype3TestCacheStub(Assembler* assembler) { |
| 910 GenerateSubtypeNTestCacheStub(assembler, 3); |
| 911 } |
| 912 |
| 913 |
| 466 // Return the current stack pointer address, used to stack alignment | 914 // Return the current stack pointer address, used to stack alignment |
| 467 // checks. | 915 // checks. |
| 468 void StubCode::GenerateGetStackPointerStub(Assembler* assembler) { | 916 void StubCode::GenerateGetStackPointerStub(Assembler* assembler) { |
| 469 __ Unimplemented("GetStackPointer Stub"); | 917 __ Unimplemented("GetStackPointer Stub"); |
| 470 } | 918 } |
| 471 | 919 |
| 472 | 920 |
| 473 // Jump to the exception handler. | 921 // Jump to the exception handler. |
| 474 // No Result. | 922 // No Result. |
| 475 void StubCode::GenerateJumpToExceptionHandlerStub(Assembler* assembler) { | 923 void StubCode::GenerateJumpToExceptionHandlerStub(Assembler* assembler) { |
| 476 __ Unimplemented("JumpToExceptionHandler Stub"); | 924 __ Unimplemented("JumpToExceptionHandler Stub"); |
| 477 } | 925 } |
| 478 | 926 |
| 479 | 927 |
| 480 // Jump to the error handler. | 928 // Jump to the error handler. |
| 481 // No Result. | 929 // No Result. |
| 482 void StubCode::GenerateJumpToErrorHandlerStub(Assembler* assembler) { | 930 void StubCode::GenerateJumpToErrorHandlerStub(Assembler* assembler) { |
| 483 __ Unimplemented("JumpToErrorHandler Stub"); | 931 __ Unimplemented("JumpToErrorHandler Stub"); |
| 484 } | 932 } |
| 485 | 933 |
| 486 | 934 |
| 487 void StubCode::GenerateEqualityWithNullArgStub(Assembler* assembler) { | 935 void StubCode::GenerateEqualityWithNullArgStub(Assembler* assembler) { |
| 488 __ Unimplemented("EqualityWithNullArg stub"); | 936 __ Unimplemented("EqualityWithNullArg Stub"); |
| 489 } | 937 } |
| 490 | 938 |
| 491 | 939 |
| 492 void StubCode::GenerateOptimizeFunctionStub(Assembler* assembler) { | 940 void StubCode::GenerateOptimizeFunctionStub(Assembler* assembler) { |
| 493 __ Unimplemented("OptimizeFunction stub"); | 941 __ Unimplemented("OptimizeFunction stub"); |
| 494 } | 942 } |
| 495 | 943 |
| 496 | 944 |
| 945 DECLARE_LEAF_RUNTIME_ENTRY(intptr_t, |
| 946 BigintCompare, |
| 947 RawBigint* left, |
| 948 RawBigint* right); |
| 949 |
| 950 |
| 951 // Does identical check (object references are equal or not equal) with special |
| 952 // checks for boxed numbers. |
| 953 // LR: return address. |
| 954 // SP + 4: left operand. |
| 955 // SP + 0: right operand. |
| 956 // Return Zero condition flag set if equal. |
| 957 // Note: A Mint cannot contain a value that would fit in Smi, a Bigint |
| 958 // cannot contain a value that fits in Mint or Smi. |
| 497 void StubCode::GenerateIdenticalWithNumberCheckStub(Assembler* assembler) { | 959 void StubCode::GenerateIdenticalWithNumberCheckStub(Assembler* assembler) { |
| 498 __ Unimplemented("IdenticalWithNumberCheck stub"); | 960 const Register temp = R2; |
| 961 const Register left = R1; |
| 962 const Register right = R0; |
| 963 // Preserve left, right and temp. |
| 964 __ PushList((1 << R0) | (1 << R1) | (1 << R2)); |
| 965 // TOS + 4: left argument. |
| 966 // TOS + 3: right argument. |
| 967 // TOS + 2: saved temp |
| 968 // TOS + 1: saved left |
| 969 // TOS + 0: saved right |
| 970 __ ldr(left, Address(SP, 4 * kWordSize)); |
| 971 __ ldr(right, Address(SP, 3 * kWordSize)); |
| 972 Label reference_compare, done, check_mint, check_bigint; |
| 973 // If any of the arguments is Smi do reference compare. |
| 974 __ tst(left, ShifterOperand(kSmiTagMask)); |
| 975 __ b(&reference_compare, EQ); |
| 976 __ tst(right, ShifterOperand(kSmiTagMask)); |
| 977 __ b(&reference_compare, EQ); |
| 978 |
| 979 // Value compare for two doubles. |
| 980 __ CompareClassId(left, kDoubleCid, temp); |
| 981 __ b(&check_mint, NE); |
| 982 __ CompareClassId(right, kDoubleCid, temp); |
| 983 __ b(&done, NE); |
| 984 |
| 985 // Double values bitwise compare. |
| 986 __ ldr(temp, FieldAddress(left, Double::value_offset() + 0 * kWordSize)); |
| 987 __ ldr(IP, FieldAddress(right, Double::value_offset() + 0 * kWordSize)); |
| 988 __ cmp(temp, ShifterOperand(IP)); |
| 989 __ b(&done, NE); |
| 990 __ ldr(temp, FieldAddress(left, Double::value_offset() + 1 * kWordSize)); |
| 991 __ ldr(IP, FieldAddress(right, Double::value_offset() + 1 * kWordSize)); |
| 992 __ cmp(temp, ShifterOperand(IP)); |
| 993 __ b(&done); |
| 994 |
| 995 __ Bind(&check_mint); |
| 996 __ CompareClassId(left, kMintCid, temp); |
| 997 __ b(&check_bigint, NE); |
| 998 __ CompareClassId(right, kMintCid, temp); |
| 999 __ b(&done, NE); |
| 1000 __ ldr(temp, FieldAddress(left, Mint::value_offset() + 0 * kWordSize)); |
| 1001 __ ldr(IP, FieldAddress(right, Mint::value_offset() + 0 * kWordSize)); |
| 1002 __ cmp(temp, ShifterOperand(IP)); |
| 1003 __ b(&done, NE); |
| 1004 __ ldr(temp, FieldAddress(left, Mint::value_offset() + 1 * kWordSize)); |
| 1005 __ ldr(IP, FieldAddress(right, Mint::value_offset() + 1 * kWordSize)); |
| 1006 __ cmp(temp, ShifterOperand(IP)); |
| 1007 __ b(&done); |
| 1008 |
| 1009 __ Bind(&check_bigint); |
| 1010 __ CompareClassId(left, kBigintCid, temp); |
| 1011 __ b(&reference_compare, NE); |
| 1012 __ CompareClassId(right, kBigintCid, temp); |
| 1013 __ b(&done, NE); |
| 1014 __ EnterStubFrame(0); |
| 1015 __ ReserveAlignedFrameSpace(2 * kWordSize); |
| 1016 __ stm(IA, SP, (1 << R0) | (1 << R1)); |
| 1017 __ CallRuntime(kBigintCompareRuntimeEntry); |
| 1018 // Result in R0, 0 means equal. |
| 1019 __ LeaveStubFrame(); |
| 1020 __ cmp(R0, ShifterOperand(0)); |
| 1021 __ b(&done); |
| 1022 |
| 1023 __ Bind(&reference_compare); |
| 1024 __ cmp(left, ShifterOperand(right)); |
| 1025 __ Bind(&done); |
| 1026 __ PopList((1 << R0) | (1 << R1) | (1 << R2)); |
| 1027 __ Ret(); |
| 499 } | 1028 } |
| 500 | 1029 |
| 501 } // namespace dart | 1030 } // namespace dart |
| 502 | 1031 |
| 503 #endif // defined TARGET_ARCH_ARM | 1032 #endif // defined TARGET_ARCH_ARM |
| OLD | NEW |