Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011, 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" | 5 #include "vm/globals.h" |
| 6 #if defined(TARGET_ARCH_X64) | 6 #if defined(TARGET_ARCH_X64) |
| 7 | 7 |
| 8 #include "vm/code_generator.h" | 8 #include "vm/code_generator.h" |
| 9 #include "vm/compiler.h" | 9 #include "vm/compiler.h" |
| 10 #include "vm/ic_data.h" | 10 #include "vm/ic_data.h" |
| 11 #include "vm/object_store.h" | 11 #include "vm/object_store.h" |
| (...skipping 256 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 268 // Remove the stub frame as we are about to jump to the dart function. | 268 // Remove the stub frame as we are about to jump to the dart function. |
| 269 __ LeaveFrame(); | 269 __ LeaveFrame(); |
| 270 __ movq(RAX, FieldAddress(RBX, Function::code_offset())); | 270 __ movq(RAX, FieldAddress(RBX, Function::code_offset())); |
| 271 | 271 |
| 272 __ movq(RBX, FieldAddress(RAX, Code::instructions_offset())); | 272 __ movq(RBX, FieldAddress(RAX, Code::instructions_offset())); |
| 273 __ addq(RBX, Immediate(Instructions::HeaderSize() - kHeapObjectTag)); | 273 __ addq(RBX, Immediate(Instructions::HeaderSize() - kHeapObjectTag)); |
| 274 __ jmp(RBX); | 274 __ jmp(RBX); |
| 275 } | 275 } |
| 276 | 276 |
| 277 | 277 |
| 278 // Called when number of invocations exceeds | |
| 279 // --optimization_invocation_threshold. | |
| 280 // RAX: target function. | |
| 281 // R10: arguments descriptor array (num_args is first Smi element). | |
| 278 void StubCode::GenerateOptimizeInvokedFunctionStub(Assembler* assembler) { | 282 void StubCode::GenerateOptimizeInvokedFunctionStub(Assembler* assembler) { |
| 279 __ Unimplemented("OptimizeInvokedFunction stub"); | 283 __ Untested("OptimizeInvokedFunction stub"); |
|
Ivan Posva
2012/01/13 06:27:40
I have not reviewed any of the untested stubs in t
regis
2012/01/13 18:57:31
Fair enough.
| |
| 284 __ EnterFrame(0); | |
| 285 __ pushq(R10); // Preserve arguments descriptor array. | |
| 286 __ pushq(RAX); // Preserve target function. | |
| 287 __ pushq(RAX); // Target function. | |
| 288 __ CallRuntimeFromStub(kOptimizeInvokedFunctionRuntimeEntry); | |
| 289 __ popq(RAX); // discard argument. | |
| 290 __ popq(RAX); // Restore function. | |
| 291 __ popq(R10); // Restore arguments descriptor array. | |
| 292 __ movq(RAX, FieldAddress(RAX, Function::code_offset())); | |
| 293 __ movq(RAX, FieldAddress(RAX, Code::instructions_offset())); | |
| 294 __ addq(RAX, Immediate(Instructions::HeaderSize() - kHeapObjectTag)); | |
| 295 __ LeaveFrame(); | |
| 296 __ jmp(RAX); | |
| 297 __ int3(); | |
| 280 } | 298 } |
| 281 | 299 |
| 282 | 300 |
| 301 // Called from a static call only when an invalid code has been entered | |
| 302 // (invalid because its function was optimized or deoptimized). | |
| 303 // RBX: function object. | |
| 304 // R10: arguments descriptor array (num_args is first Smi element). | |
| 283 void StubCode::GenerateFixCallersTargetStub(Assembler* assembler) { | 305 void StubCode::GenerateFixCallersTargetStub(Assembler* assembler) { |
| 284 __ Unimplemented("FixCallersTarget stub"); | 306 __ Untested("FixCallersTarget stub"); |
| 307 __ EnterFrame(0); | |
| 308 __ pushq(R10); // Preserve arguments descriptor array. | |
| 309 __ pushq(RBX); // Preserve target function. | |
| 310 __ pushq(RBX); // Target function. | |
| 311 __ CallRuntimeFromStub(kFixCallersTargetRuntimeEntry); | |
| 312 __ popq(RAX); // discard argument. | |
| 313 __ popq(RAX); // Restore function. | |
| 314 __ popq(R10); // Restore arguments descriptor array. | |
| 315 __ movq(RAX, FieldAddress(RAX, Function::code_offset())); | |
| 316 __ movq(RAX, FieldAddress(RAX, Code::instructions_offset())); | |
| 317 __ addq(RAX, Immediate(Instructions::HeaderSize() - kHeapObjectTag)); | |
| 318 __ LeaveFrame(); | |
| 319 __ jmp(RAX); | |
| 320 __ int3(); | |
| 285 } | 321 } |
| 286 | 322 |
| 287 | 323 |
| 288 // Lookup for [function-name, arg count] in 'functions_map_'. | 324 // Lookup for [function-name, arg count] in 'functions_map_'. |
| 289 // Input parameters (to be treated as read only, unless calling to target!): | 325 // Input parameters (to be treated as read only, unless calling to target!): |
| 290 // RBX: ic-data array. | 326 // RBX: ic-data array. |
| 291 // R10: arguments descriptor array (num_args is first Smi element). | 327 // R10: arguments descriptor array (num_args is first Smi element). |
| 292 // Stack: return address, arguments. | 328 // Stack: return address, arguments. |
| 293 // If the lookup succeeds we jump to the target method from here, otherwise | 329 // If the lookup succeeds we jump to the target method from here, otherwise |
| 294 // we continue in code generated by the caller of 'MegamorphicLookup'. | 330 // we continue in code generated by the caller of 'MegamorphicLookup'. |
| (...skipping 291 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 586 __ popq(RAX); | 622 __ popq(RAX); |
| 587 __ popq(RAX); // Get result into RAX. | 623 __ popq(RAX); // Get result into RAX. |
| 588 | 624 |
| 589 // Remove the stub frame as we are about to return. | 625 // Remove the stub frame as we are about to return. |
| 590 __ LeaveFrame(); | 626 __ LeaveFrame(); |
| 591 __ ret(); | 627 __ ret(); |
| 592 } | 628 } |
| 593 | 629 |
| 594 | 630 |
| 595 void StubCode::GenerateDeoptimizeStub(Assembler* assembler) { | 631 void StubCode::GenerateDeoptimizeStub(Assembler* assembler) { |
| 596 __ Unimplemented("Deoptimize stub"); | 632 __ Untested("Deoptimize stub"); |
| 633 __ EnterFrame(0); | |
| 634 // RAX: deoptimization reason id. | |
| 635 // Stack at this point: | |
| 636 // TOS + 0: Saved EBP of function frame that will be deoptimized. <== EBP | |
| 637 // TOS + 1: Deoptimization point (return address), will be patched. | |
| 638 // TOS + 2: top-of-stack at deoptimization point (all arguments on stack). | |
| 639 __ pushq(RAX); | |
| 640 __ CallRuntimeFromStub(kDeoptimizeRuntimeEntry); | |
| 641 __ popq(RAX); | |
| 642 __ LeaveFrame(); | |
| 643 __ ret(); | |
| 597 } | 644 } |
| 598 | 645 |
| 599 | 646 |
| 600 // Called for inline allocation of arrays. | 647 // Called for inline allocation of arrays. |
| 601 // Input parameters: | 648 // Input parameters: |
| 602 // R10 : Array length as Smi. | 649 // R10 : Array length as Smi. |
| 603 // RBX : array element type (either NULL or an instantiated type). | 650 // RBX : array element type (either NULL or an instantiated type). |
| 604 // NOTE: R10 cannot be clobbered here as the caller relies on it being saved. | 651 // NOTE: R10 cannot be clobbered here as the caller relies on it being saved. |
| 605 // The newly allocated object is returned in RAX. | 652 // The newly allocated object is returned in RAX. |
| 606 void StubCode::GenerateAllocateArrayStub(Assembler* assembler) { | 653 void StubCode::GenerateAllocateArrayStub(Assembler* assembler) { |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 669 __ movq(RBX, FieldAddress(CTX, Context::isolate_offset())); | 716 __ movq(RBX, FieldAddress(CTX, Context::isolate_offset())); |
| 670 __ movq(RBX, Address(RBX, Isolate::object_store_offset())); | 717 __ movq(RBX, Address(RBX, Isolate::object_store_offset())); |
| 671 __ movq(RBX, Address(RBX, ObjectStore::array_class_offset())); | 718 __ movq(RBX, Address(RBX, ObjectStore::array_class_offset())); |
| 672 __ StoreIntoObject(RAX, FieldAddress(RAX, Array::class_offset()), RBX); | 719 __ StoreIntoObject(RAX, FieldAddress(RAX, Array::class_offset()), RBX); |
| 673 // Calculate the size tag. | 720 // Calculate the size tag. |
| 674 // RAX: new object start as a tagged pointer. | 721 // RAX: new object start as a tagged pointer. |
| 675 // R12: new object end address. | 722 // R12: new object end address. |
| 676 // R10: Array length as Smi. | 723 // R10: Array length as Smi. |
| 677 { | 724 { |
| 678 Label size_tag_overflow, done; | 725 Label size_tag_overflow, done; |
| 679 __ leaq(RBX, Address(R10, TIMES_2, fixed_size)); // R10 is Smi. | 726 __ leaq(RBX, Address(R10, TIMES_4, fixed_size)); // R10 is Smi. |
| 680 ASSERT(kSmiTagShift == 1); | 727 ASSERT(kSmiTagShift == 1); |
| 681 __ andq(RBX, Immediate(-kObjectAlignment)); | 728 __ andq(RBX, Immediate(-kObjectAlignment)); |
| 682 __ cmpq(RBX, Immediate(RawObject::SizeTag::kMaxSizeTag)); | 729 __ cmpq(RBX, Immediate(RawObject::SizeTag::kMaxSizeTag)); |
| 683 __ j(ABOVE, &size_tag_overflow, Assembler::kNearJump); | 730 __ j(ABOVE, &size_tag_overflow, Assembler::kNearJump); |
| 684 __ shlq(RBX, Immediate(RawObject::kSizeTagBit - kObjectAlignmentLog2)); | 731 __ shlq(RBX, Immediate(RawObject::kSizeTagBit - kObjectAlignmentLog2)); |
| 685 __ movq(FieldAddress(RAX, Array::tags_offset()), RBX); | 732 __ movq(FieldAddress(RAX, Array::tags_offset()), RBX); |
| 686 __ jmp(&done); | 733 __ jmp(&done); |
| 687 | 734 |
| 688 __ Bind(&size_tag_overflow); | 735 __ Bind(&size_tag_overflow); |
| 689 __ movq(FieldAddress(RAX, Array::tags_offset()), Immediate(0)); | 736 __ movq(FieldAddress(RAX, Array::tags_offset()), Immediate(0)); |
| 690 __ Bind(&done); | 737 __ Bind(&done); |
| 691 } | 738 } |
| 692 | 739 |
| 693 // Initialize all array elements to raw_null. | 740 // Initialize all array elements to raw_null. |
| 694 // RAX: new object start as a tagged pointer. | 741 // RAX: new object start as a tagged pointer. |
| 695 // R12: new object end address. | 742 // R12: new object end address. |
| 743 __ leaq(RBX, FieldAddress(RAX, Array::data_offset())); | |
| 696 // RBX: iterator which initially points to the start of the variable | 744 // RBX: iterator which initially points to the start of the variable |
| 697 // data area to be initialized. | 745 // data area to be initialized. |
| 698 __ leaq(RBX, FieldAddress(RAX, Array::data_offset())); | |
| 699 Label done; | 746 Label done; |
| 700 Label init_loop; | 747 Label init_loop; |
| 701 __ Bind(&init_loop); | 748 __ Bind(&init_loop); |
| 702 __ cmpq(RBX, R12); | 749 __ cmpq(RBX, R12); |
| 703 __ j(ABOVE_EQUAL, &done, Assembler::kNearJump); | 750 __ j(ABOVE_EQUAL, &done, Assembler::kNearJump); |
| 704 __ movq(Address(RBX, 0), raw_null); | 751 __ movq(Address(RBX, 0), raw_null); |
| 705 __ addq(RBX, Immediate(kWordSize)); | 752 __ addq(RBX, Immediate(kWordSize)); |
| 706 __ jmp(&init_loop, Assembler::kNearJump); | 753 __ jmp(&init_loop, Assembler::kNearJump); |
| 707 __ Bind(&done); | 754 __ Bind(&done); |
| 708 | 755 |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 723 __ CallRuntimeFromStub(kAllocateArrayRuntimeEntry); | 770 __ CallRuntimeFromStub(kAllocateArrayRuntimeEntry); |
| 724 __ popq(RAX); // Pop instantiator. | 771 __ popq(RAX); // Pop instantiator. |
| 725 __ popq(RAX); // Pop element type argument. | 772 __ popq(RAX); // Pop element type argument. |
| 726 __ popq(R10); // Pop array length argument. | 773 __ popq(R10); // Pop array length argument. |
| 727 __ popq(RAX); // Pop return value from return slot. | 774 __ popq(RAX); // Pop return value from return slot. |
| 728 __ LeaveFrame(); | 775 __ LeaveFrame(); |
| 729 __ ret(); | 776 __ ret(); |
| 730 } | 777 } |
| 731 | 778 |
| 732 | 779 |
| 780 // Input parameters: | |
| 781 // R10: Arguments descriptor array (num_args is first Smi element, closure | |
| 782 // object is not included in num_args). | |
| 783 // Note: The closure object is pushed before the first argument to the function | |
| 784 // being called, the stub accesses the closure from this location directly | |
| 785 // when setting up the context and resolving the entry point. | |
| 786 // Uses R13. | |
|
Ivan Posva
2012/01/13 06:27:40
Also uses RAX, RBX.
regis
2012/01/13 18:57:31
Removed comment. See below.
| |
| 733 void StubCode::GenerateCallClosureFunctionStub(Assembler* assembler) { | 787 void StubCode::GenerateCallClosureFunctionStub(Assembler* assembler) { |
| 734 __ Unimplemented("CallClosureFunction stub"); | 788 const Immediate raw_null = |
| 789 Immediate(reinterpret_cast<intptr_t>(Object::null())); | |
| 790 | |
| 791 // Total number of args is the first Smi in args descriptor array (R10). | |
| 792 __ movq(RAX, FieldAddress(R10, Array::data_offset())); // Load num_args. | |
| 793 // Load closure object in R13. | |
| 794 __ movq(R13, Address(RSP, RAX, TIMES_4, kWordSize)); // RAX is a Smi. | |
| 795 | |
| 796 // Verify that R13 is a closure by checking its class. | |
| 797 Label not_closure; | |
| 798 __ cmpq(R13, raw_null); | |
| 799 // Not a closure, but null object. | |
| 800 __ j(EQUAL, ¬_closure, Assembler::kNearJump); | |
| 801 __ testq(R13, Immediate(kSmiTagMask)); | |
| 802 __ j(ZERO, ¬_closure, Assembler::kNearJump); // Not a closure, but a smi. | |
| 803 // Verify that the class of the object is a closure class by checking that | |
| 804 // class.signature_function() is not null. | |
| 805 __ movq(RAX, FieldAddress(R13, Object::class_offset())); | |
| 806 __ movq(RAX, FieldAddress(RAX, Class::signature_function_offset())); | |
| 807 __ cmpq(RAX, raw_null); | |
| 808 // Actual class is not a closure class. | |
| 809 __ j(EQUAL, ¬_closure, Assembler::kNearJump); | |
| 810 | |
| 811 // RAX is just the signature function. Load the actual closure function. | |
| 812 __ movq(RBX, FieldAddress(R13, Closure::function_offset())); | |
| 813 | |
| 814 // Load closure context in CTX; note that CTX has already been preserved. | |
| 815 __ movq(CTX, FieldAddress(R13, Closure::context_offset())); | |
| 816 | |
| 817 // Load closure function code in RAX. | |
| 818 __ movq(RAX, FieldAddress(RBX, Function::code_offset())); | |
| 819 __ cmpq(RAX, raw_null); | |
| 820 Label function_compiled; | |
| 821 __ j(NOT_EQUAL, &function_compiled, Assembler::kNearJump); | |
| 822 | |
| 823 // Create a stub frame as we are pushing some objects on the stack before | |
| 824 // calling into the runtime. | |
| 825 __ EnterFrame(0); | |
| 826 | |
| 827 __ pushq(R10); // Preserve arguments descriptor array. | |
| 828 __ pushq(RBX); | |
|
Ivan Posva
2012/01/13 06:27:40
What is in RBX?
regis
2012/01/13 18:57:31
Added comment.
| |
| 829 __ CallRuntimeFromStub(kCompileFunctionRuntimeEntry); | |
| 830 __ popq(RBX); // Restore read-only function object argument in RBX. | |
| 831 __ popq(R10); // Restore arguments descriptor array. | |
| 832 // Restore RAX. | |
| 833 __ movq(RAX, FieldAddress(RBX, Function::code_offset())); | |
| 834 | |
| 835 // Remove the stub frame as we are about to jump to the closure function. | |
| 836 __ LeaveFrame(); | |
| 837 | |
| 838 __ Bind(&function_compiled); | |
| 839 // RAX: Code. | |
| 840 // RBX: Function. | |
| 841 // R10: Arguments descriptor array (num_args is first Smi element). | |
| 842 | |
| 843 __ movq(RBX, FieldAddress(RAX, Code::instructions_offset())); | |
| 844 __ addq(RBX, Immediate(Instructions::HeaderSize() - kHeapObjectTag)); | |
| 845 __ jmp(RBX); | |
| 846 | |
| 847 __ Bind(¬_closure); | |
| 848 // Call runtime to report that a closure call was attempted on a non-closure | |
| 849 // object, passing the non-closure object and its arguments array. | |
| 850 // R13: non-closure object. | |
| 851 // R10: arguments descriptor array (num_args is first Smi element, closure | |
| 852 // object is not included in num_args). | |
| 853 | |
| 854 // Create a stub frame as we are pushing some objects on the stack before | |
| 855 // calling into the runtime. | |
| 856 __ EnterFrame(0); | |
| 857 | |
| 858 __ pushq(raw_null); // Setup space on stack for result from error reporting. | |
| 859 __ pushq(R13); // Non-closure object. | |
| 860 // Total number of args is the first Smi in args descriptor array (R10). | |
| 861 __ movq(R13, FieldAddress(R10, Array::data_offset())); // Load num_args. | |
| 862 __ SmiUntag(R13); | |
| 863 // See stack layout below explaining "wordSize * 4" offset. | |
| 864 PushArgumentsArray(assembler, (kWordSize * 4)); | |
| 865 | |
| 866 // Stack: | |
| 867 // TOS + 0: Argument array. | |
| 868 // TOS + 1: Non-closure object. | |
| 869 // TOS + 2: Place for result from reporting the error. | |
| 870 // TOS + 3: Saved RBP of previous frame. <== RBP | |
| 871 // TOS + 4: Dart code return address | |
| 872 // TOS + 5: Last argument of caller. | |
| 873 // .... | |
| 874 __ CallRuntimeFromStub(kReportObjectNotClosureRuntimeEntry); | |
| 875 __ Stop("runtime call throws an exception"); | |
| 735 } | 876 } |
| 736 | 877 |
| 737 | 878 |
| 738 // Called when invoking Dart code from C++ (VM code). | 879 // Called when invoking Dart code from C++ (VM code). |
| 739 // Input parameters: | 880 // Input parameters: |
| 740 // RSP : points to return address. | 881 // RSP : points to return address. |
| 741 // RDI : entrypoint of the Dart function to call. | 882 // RDI : entrypoint of the Dart function to call. |
| 742 // RSI : arguments descriptor array. | 883 // RSI : arguments descriptor array. |
| 743 // RDX : pointer to the argument array. | 884 // RDX : pointer to the argument array. |
| 744 // RCX : new context containing the current isolate pointer. | 885 // RCX : new context containing the current isolate pointer. |
| (...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 846 | 987 |
| 847 // Restore the frame pointer. | 988 // Restore the frame pointer. |
| 848 __ LeaveFrame(); | 989 __ LeaveFrame(); |
| 849 | 990 |
| 850 __ ret(); | 991 __ ret(); |
| 851 } | 992 } |
| 852 | 993 |
| 853 | 994 |
| 854 // Called for inline allocation of contexts. | 995 // Called for inline allocation of contexts. |
| 855 // Input: | 996 // Input: |
| 856 // RDX: number of context variables. | 997 // R10: number of context variables. |
| 857 // Output: | 998 // Output: |
| 858 // RAX: new allocated RawContext object. | 999 // RAX: new allocated RawContext object. |
| 859 // RBX and RDX are destroyed. | |
|
Ivan Posva
2012/01/13 06:27:40
What is the convention regarding these comments. S
regis
2012/01/13 18:57:31
Personally, I think these comments are a bad idea.
| |
| 860 void StubCode::GenerateAllocateContextStub(Assembler* assembler) { | 1000 void StubCode::GenerateAllocateContextStub(Assembler* assembler) { |
| 861 const Immediate raw_null = | 1001 const Immediate raw_null = |
| 862 Immediate(reinterpret_cast<intptr_t>(Object::null())); | 1002 Immediate(reinterpret_cast<intptr_t>(Object::null())); |
| 863 if (false) { | 1003 if (FLAG_inline_alloc) { |
| 864 // TODO(regis): Implement fast inline allocation of contexts. | 1004 const Class& context_class = Class::ZoneHandle(Object::context_class()); |
| 865 __ Unimplemented("AllocateContext stub - inline allocation"); | 1005 Label slow_case; |
| 1006 Heap* heap = Isolate::Current()->heap(); | |
| 1007 // First compute the rounded instance size. | |
| 1008 // R10: number of context variables. | |
| 1009 intptr_t fixed_size = (sizeof(RawContext) + kObjectAlignment - 1); | |
| 1010 __ leaq(R13, Address(R10, TIMES_8, fixed_size)); | |
| 1011 __ andq(R13, Immediate(-kObjectAlignment)); | |
| 1012 | |
| 1013 // Now allocate the object. | |
| 1014 // R10: number of context variables. | |
| 1015 __ movq(RAX, Immediate(heap->TopAddress())); | |
| 1016 __ movq(RAX, Address(RAX, 0)); | |
| 1017 __ addq(R13, RAX); | |
| 1018 // Check if the allocation fits into the remaining space. | |
| 1019 // RAX: potential new object. | |
| 1020 // R13: potential next object start. | |
| 1021 // R10: number of context variables. | |
| 1022 __ movq(RDI, Immediate(heap->EndAddress())); | |
| 1023 __ cmpq(R13, Address(RDI, 0)); | |
| 1024 if (FLAG_use_slow_path) { | |
| 1025 __ jmp(&slow_case); | |
| 1026 } else { | |
| 1027 __ j(ABOVE_EQUAL, &slow_case); | |
| 1028 } | |
| 1029 | |
| 1030 // Successfully allocated the object, now update top to point to | |
| 1031 // next object start and initialize the object. | |
| 1032 // RAX: new object. | |
| 1033 // R13: next object start. | |
| 1034 // R10: number of context variables. | |
| 1035 __ movq(RDI, Immediate(heap->TopAddress())); | |
| 1036 __ movq(Address(RDI, 0), R13); | |
| 1037 __ addq(RAX, Immediate(kHeapObjectTag)); | |
| 1038 | |
| 1039 // Initialize the class field in the context object. | |
| 1040 // RAX: new object. | |
| 1041 // R10: number of context variables. | |
| 1042 __ LoadObject(R13, context_class); // Load up class field of context. | |
| 1043 __ StoreIntoObject(RAX, | |
| 1044 FieldAddress(RAX, Context::class_offset()), | |
| 1045 R13); | |
| 1046 // Calculate the size tag. | |
| 1047 // RAX: new object. | |
| 1048 // R10: number of context variables. | |
| 1049 { | |
| 1050 Label size_tag_overflow, done; | |
| 1051 __ leaq(R13, Address(R10, TIMES_8, fixed_size)); | |
| 1052 __ andq(R13, Immediate(-kObjectAlignment)); | |
| 1053 __ cmpq(R13, Immediate(RawObject::SizeTag::kMaxSizeTag)); | |
| 1054 __ j(ABOVE, &size_tag_overflow, Assembler::kNearJump); | |
| 1055 __ shlq(R13, Immediate(RawObject::kSizeTagBit - kObjectAlignmentLog2)); | |
| 1056 __ movq(FieldAddress(RAX, Context::tags_offset()), R13); // Tags. | |
| 1057 __ jmp(&done); | |
| 1058 | |
| 1059 __ Bind(&size_tag_overflow); | |
| 1060 // Set overflow size tag value. | |
| 1061 __ movq(FieldAddress(RAX, Context::tags_offset()), Immediate(0)); | |
| 1062 __ Bind(&done); | |
| 1063 } | |
| 1064 | |
| 1065 // Setup up number of context variables field. | |
| 1066 // RAX: new object. | |
| 1067 // R10: number of context variables as integer value (not object). | |
| 1068 __ movq(FieldAddress(RAX, Context::num_variables_offset()), R10); | |
| 1069 | |
| 1070 // Setup isolate field. | |
| 1071 // Load Isolate pointer from Context structure into R13. | |
| 1072 // RAX: new object. | |
| 1073 // R10: number of context variables. | |
| 1074 __ movq(R13, FieldAddress(CTX, Context::isolate_offset())); | |
| 1075 // R13: Isolate, not an object. | |
| 1076 __ movq(FieldAddress(RAX, Context::isolate_offset()), R13); | |
| 1077 | |
| 1078 const Immediate raw_null = | |
| 1079 Immediate(reinterpret_cast<intptr_t>(Object::null())); | |
| 1080 // Setup the parent field. | |
| 1081 // RAX: new object. | |
| 1082 // R10: number of context variables. | |
| 1083 __ movq(FieldAddress(RAX, Context::parent_offset()), raw_null); | |
| 1084 | |
| 1085 // Initialize the context variables. | |
| 1086 // RAX: new object. | |
| 1087 // R10: number of context variables. | |
| 1088 { | |
| 1089 Label loop, entry; | |
| 1090 __ leaq(R13, FieldAddress(RAX, Context::variable_offset(0))); | |
| 1091 | |
| 1092 __ jmp(&entry, Assembler::kNearJump); | |
| 1093 __ Bind(&loop); | |
| 1094 __ decq(R10); | |
| 1095 __ movq(Address(R13, R10, TIMES_8, 0), raw_null); | |
| 1096 __ Bind(&entry); | |
| 1097 __ cmpq(R10, Immediate(0)); | |
| 1098 __ j(NOT_EQUAL, &loop, Assembler::kNearJump); | |
| 1099 } | |
| 1100 | |
| 1101 // Done allocating and initializing the context. | |
| 1102 // RAX: new object. | |
| 1103 __ ret(); | |
| 1104 | |
| 1105 __ Bind(&slow_case); | |
| 866 } | 1106 } |
| 867 // Create the stub frame. | 1107 // Create a stub frame. |
| 868 __ EnterFrame(0); | 1108 __ EnterFrame(0); |
| 869 __ pushq(raw_null); // Setup space on stack for the return value. | 1109 __ pushq(raw_null); // Setup space on stack for the return value. |
| 870 __ SmiTag(RDX); | 1110 __ SmiTag(R10); |
| 871 __ pushq(RDX); // Push number of context variables. | 1111 __ pushq(R10); // Push number of context variables. |
| 872 __ CallRuntimeFromStub(kAllocateContextRuntimeEntry); // Allocate context. | 1112 __ CallRuntimeFromStub(kAllocateContextRuntimeEntry); // Allocate context. |
| 873 __ popq(RAX); // Pop number of context variables argument. | 1113 __ popq(RAX); // Pop number of context variables argument. |
| 874 __ popq(RAX); // Pop the new context object. | 1114 __ popq(RAX); // Pop the new context object. |
| 875 // RAX: new object | 1115 // RAX: new object |
| 876 // Restore the frame pointer. | 1116 // Restore the frame pointer. |
| 877 __ LeaveFrame(); | 1117 __ LeaveFrame(); |
| 878 __ ret(); | 1118 __ ret(); |
| 879 } | 1119 } |
| 880 | 1120 |
| 881 | 1121 |
| 882 | |
| 883 | |
| 884 // Called for inline allocation of objects. | 1122 // Called for inline allocation of objects. |
| 885 // Input parameters: | 1123 // Input parameters: |
| 886 // RSP + 16 : type arguments object (only if class is parameterized). | 1124 // RSP + 16 : type arguments object (only if class is parameterized). |
| 887 // RSP + 8 : type arguments of instantiator (only if class is parameterized). | 1125 // RSP + 8 : type arguments of instantiator (only if class is parameterized). |
| 888 // RSP : points to return address. | 1126 // RSP : points to return address. |
| 889 // Uses RAX, RBX, RCX, RDX, RDI as temporary registers. | 1127 // Uses RAX, RBX, RCX, RDX, RDI as temporary registers. |
| 890 void StubCode::GenerateAllocationStubForClass(Assembler* assembler, | 1128 void StubCode::GenerateAllocationStubForClass(Assembler* assembler, |
| 891 const Class& cls) { | 1129 const Class& cls) { |
| 892 const intptr_t kObjectTypeArgumentsOffset = 2 * kWordSize; | 1130 const intptr_t kObjectTypeArgumentsOffset = 2 * kWordSize; |
| 893 const intptr_t kInstantiatorTypeArgumentsOffset = 1 * kWordSize; | 1131 const intptr_t kInstantiatorTypeArgumentsOffset = 1 * kWordSize; |
| (...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1076 } | 1314 } |
| 1077 | 1315 |
| 1078 | 1316 |
| 1079 // Called for inline allocation of closures. | 1317 // Called for inline allocation of closures. |
| 1080 // Input parameters: | 1318 // Input parameters: |
| 1081 // If the signature class is not parameterized, the receiver, if any, will be | 1319 // If the signature class is not parameterized, the receiver, if any, will be |
| 1082 // at RSP + 8 instead of RSP + 16, since no type arguments are passed. | 1320 // at RSP + 8 instead of RSP + 16, since no type arguments are passed. |
| 1083 // RSP + 16 (or RSP + 8): receiver (only if implicit instance closure). | 1321 // RSP + 16 (or RSP + 8): receiver (only if implicit instance closure). |
| 1084 // RSP + 8 : type arguments object (only if signature class is parameterized). | 1322 // RSP + 8 : type arguments object (only if signature class is parameterized). |
| 1085 // RSP : points to return address. | 1323 // RSP : points to return address. |
| 1086 // Uses RAX, RCX as temporary registers. | 1324 // Uses RAX, RCX as temporary registers. |
|
Ivan Posva
2012/01/13 06:27:40
At least R13, RDI are also touched.
regis
2012/01/13 18:57:31
Removed comment. See above.
| |
| 1087 void StubCode::GenerateAllocationStubForClosure(Assembler* assembler, | 1325 void StubCode::GenerateAllocationStubForClosure(Assembler* assembler, |
| 1088 const Function& func) { | 1326 const Function& func) { |
| 1089 const Immediate raw_null = | 1327 const Immediate raw_null = |
| 1090 Immediate(reinterpret_cast<intptr_t>(Object::null())); | 1328 Immediate(reinterpret_cast<intptr_t>(Object::null())); |
| 1091 ASSERT(func.IsClosureFunction()); | 1329 ASSERT(func.IsClosureFunction()); |
| 1092 const bool is_implicit_static_closure = | 1330 const bool is_implicit_static_closure = |
| 1093 func.IsImplicitStaticClosureFunction(); | 1331 func.IsImplicitStaticClosureFunction(); |
| 1094 const bool is_implicit_instance_closure = | 1332 const bool is_implicit_instance_closure = |
| 1095 func.IsImplicitInstanceClosureFunction(); | 1333 func.IsImplicitInstanceClosureFunction(); |
| 1096 const Class& cls = Class::ZoneHandle(func.signature_class()); | 1334 const Class& cls = Class::ZoneHandle(func.signature_class()); |
| 1097 const bool has_type_arguments = cls.HasTypeArguments(); | 1335 const bool has_type_arguments = cls.HasTypeArguments(); |
| 1098 const intptr_t kTypeArgumentsOffset = 1 * kWordSize; | 1336 const intptr_t kTypeArgumentsOffset = 1 * kWordSize; |
| 1099 const intptr_t kReceiverOffset = (has_type_arguments ? 2 : 1) * kWordSize; | 1337 const intptr_t kReceiverOffset = (has_type_arguments ? 2 : 1) * kWordSize; |
| 1100 if (false) { | 1338 const intptr_t closure_size = Closure::InstanceSize(); |
| 1101 // TODO(regis): Implement inline context allocation. | 1339 const intptr_t context_size = Context::InstanceSize(1); // Captured receiver. |
| 1102 __ Unimplemented("AllocateClosure stub - inline allocation"); | 1340 if (FLAG_inline_alloc && |
| 1341 PageSpace::IsPageAllocatableSize(closure_size + context_size)) { | |
| 1342 Label slow_case; | |
| 1343 Heap* heap = Isolate::Current()->heap(); | |
| 1344 __ movq(RAX, Immediate(heap->TopAddress())); | |
| 1345 __ movq(RAX, Address(RAX, 0)); | |
| 1346 __ leaq(R13, Address(RAX, closure_size)); | |
| 1347 if (is_implicit_instance_closure) { | |
| 1348 __ movq(RBX, R13); // RBX: new context address. | |
| 1349 __ addq(R13, Immediate(context_size)); | |
| 1350 } | |
| 1351 // Check if the allocation fits into the remaining space. | |
| 1352 // RAX: potential new closure object. | |
| 1353 // RBX: potential new context object (only if is_implicit_closure). | |
| 1354 // R13: potential next object start. | |
| 1355 __ movq(RDI, Immediate(heap->EndAddress())); | |
| 1356 __ cmpq(R13, Address(RDI, 0)); | |
| 1357 if (FLAG_use_slow_path) { | |
| 1358 __ jmp(&slow_case); | |
| 1359 } else { | |
| 1360 __ j(ABOVE_EQUAL, &slow_case); | |
| 1361 } | |
| 1362 | |
| 1363 // Successfully allocated the object, now update top to point to | |
| 1364 // next object start and initialize the object. | |
| 1365 __ movq(RDI, Immediate(heap->TopAddress())); | |
| 1366 __ movq(Address(RDI, 0), R13); | |
| 1367 | |
| 1368 // Initialize the class field in the object. | |
| 1369 // RAX: new closure object. | |
| 1370 // RBX: new context object (only if is_implicit_closure). | |
| 1371 __ LoadObject(R10, cls); // Load signature class of closure. | |
| 1372 __ movq(Address(RAX, Closure::class_offset()), R10); | |
| 1373 // Set the tags. | |
| 1374 __ movq(Address(RAX, Closure::tags_offset()), | |
| 1375 Immediate(RawObject::SizeTag::encode(closure_size))); | |
| 1376 | |
| 1377 // Initialize the function field in the object. | |
| 1378 // RAX: new closure object. | |
| 1379 // RBX: new context object (only if is_implicit_closure). | |
| 1380 // R13: next object start. | |
| 1381 __ LoadObject(R10, func); // Load function of closure to be allocated. | |
| 1382 __ movq(Address(RAX, Closure::function_offset()), R10); | |
| 1383 | |
| 1384 // Setup the context for this closure. | |
| 1385 if (is_implicit_static_closure) { | |
| 1386 ObjectStore* object_store = Isolate::Current()->object_store(); | |
| 1387 ASSERT(object_store != NULL); | |
| 1388 const Context& empty_context = | |
| 1389 Context::ZoneHandle(object_store->empty_context()); | |
| 1390 __ LoadObject(R10, empty_context); | |
| 1391 __ movq(Address(RAX, Closure::context_offset()), R10); | |
| 1392 } else if (is_implicit_instance_closure) { | |
| 1393 // Initialize the new context capturing the receiver. | |
| 1394 | |
| 1395 // Set the class field to the Context class. | |
| 1396 __ LoadObject(R13, Class::ZoneHandle(Object::context_class())); | |
| 1397 __ movq(Address(RBX, Context::class_offset()), R13); | |
| 1398 // Set the tags. | |
| 1399 __ movq(Address(RBX, Context::tags_offset()), | |
| 1400 Immediate(RawObject::SizeTag::encode(context_size))); | |
| 1401 | |
| 1402 // Set number of variables field to 1 (for captured receiver). | |
| 1403 __ movq(Address(RBX, Context::num_variables_offset()), Immediate(1)); | |
| 1404 | |
| 1405 // Set isolate field to isolate of current context. | |
| 1406 __ movq(R10, FieldAddress(CTX, Context::isolate_offset())); | |
| 1407 __ movq(Address(RBX, Context::isolate_offset()), R10); | |
| 1408 | |
| 1409 // Set the parent field to null. | |
| 1410 __ movq(Address(RBX, Context::parent_offset()), raw_null); | |
| 1411 | |
| 1412 // Initialize the context variable to the receiver. | |
| 1413 __ movq(R10, Address(RSP, kReceiverOffset)); | |
| 1414 __ movq(Address(RBX, Context::variable_offset(0)), R10); | |
| 1415 | |
| 1416 // Set the newly allocated context in the newly allocated closure. | |
| 1417 __ addq(RBX, Immediate(kHeapObjectTag)); | |
| 1418 __ movq(Address(RAX, Closure::context_offset()), RBX); | |
| 1419 } else { | |
| 1420 __ movq(Address(RAX, Closure::context_offset()), CTX); | |
| 1421 } | |
| 1422 | |
| 1423 // Set the type arguments field in the newly allocated closure. | |
| 1424 if (has_type_arguments) { | |
| 1425 ASSERT(!is_implicit_static_closure); | |
| 1426 // Use the passed-in type arguments. | |
| 1427 __ movq(R10, Address(RSP, kTypeArgumentsOffset)); | |
| 1428 __ movq(Address(RAX, Closure::type_arguments_offset()), R10); | |
| 1429 } else { | |
| 1430 // Set to null. | |
| 1431 __ movq(Address(RAX, Closure::type_arguments_offset()), raw_null); | |
| 1432 } | |
| 1433 | |
| 1434 __ movq(Address(RAX, Closure::smrck_offset()), raw_null); | |
| 1435 | |
| 1436 // Done allocating and initializing the instance. | |
| 1437 // RAX: new object. | |
| 1438 __ addq(RAX, Immediate(kHeapObjectTag)); | |
| 1439 __ ret(); | |
| 1440 | |
| 1441 __ Bind(&slow_case); | |
| 1103 } | 1442 } |
| 1104 if (has_type_arguments) { | 1443 if (has_type_arguments) { |
| 1105 __ movq(RCX, Address(RSP, kTypeArgumentsOffset)); | 1444 __ movq(RCX, Address(RSP, kTypeArgumentsOffset)); |
| 1106 } | 1445 } |
| 1107 if (is_implicit_instance_closure) { | 1446 if (is_implicit_instance_closure) { |
| 1108 __ movq(RAX, Address(RSP, kReceiverOffset)); | 1447 __ movq(RAX, Address(RSP, kReceiverOffset)); |
| 1109 } | 1448 } |
| 1110 // Create the stub frame. | 1449 // Create the stub frame. |
| 1111 __ EnterFrame(0); | 1450 __ EnterFrame(0); |
| 1112 __ pushq(raw_null); // Setup space on stack for the return value. | 1451 __ pushq(raw_null); // Setup space on stack for the return value. |
| (...skipping 21 matching lines...) Expand all Loading... | |
| 1134 } | 1473 } |
| 1135 __ popq(RAX); // Pop the function object. | 1474 __ popq(RAX); // Pop the function object. |
| 1136 __ popq(RAX); // Pop the result. | 1475 __ popq(RAX); // Pop the result. |
| 1137 // RAX: New closure object. | 1476 // RAX: New closure object. |
| 1138 // Restore the calling frame. | 1477 // Restore the calling frame. |
| 1139 __ LeaveFrame(); | 1478 __ LeaveFrame(); |
| 1140 __ ret(); | 1479 __ ret(); |
| 1141 } | 1480 } |
| 1142 | 1481 |
| 1143 | 1482 |
| 1483 // Called for invoking noSuchMethod function from the entry code of a dart | |
| 1484 // function after an error in passed named arguments is detected. | |
| 1485 // Input parameters: | |
| 1486 // RBP : points to previous frame pointer. | |
| 1487 // RBP + 8 : points to return address. | |
| 1488 // RBP + 16 : address of last argument (arg n-1). | |
| 1489 // RBP + 16 + 8*(n-1) : address of first argument (arg 0). | |
| 1490 // RBX : ic-data array. | |
| 1491 // R10 : arguments descriptor array. | |
| 1144 void StubCode::GenerateCallNoSuchMethodFunctionStub(Assembler* assembler) { | 1492 void StubCode::GenerateCallNoSuchMethodFunctionStub(Assembler* assembler) { |
| 1145 __ Unimplemented("CallNoSuchMethodFunction stub"); | 1493 // The target function was not found, so invoke method |
| 1494 // "void noSuchMethod(function_name, Array arguments)". | |
| 1495 // TODO(regis): For now, we simply pass the actual arguments, both positional | |
| 1496 // and named, as the argument array. This is not correct if out-of-order | |
| 1497 // named arguments were passed. | |
| 1498 // The signature of the "noSuchMethod" method has to change from | |
| 1499 // noSuchMethod(String name, Array arguments) to something like | |
| 1500 // noSuchMethod(InvocationMirror call). | |
| 1501 // Also, the class NoSuchMethodException has to be modified accordingly. | |
| 1502 // Total number of args is the first Smi in args descriptor array (R10). | |
| 1503 const Immediate raw_null = | |
| 1504 Immediate(reinterpret_cast<intptr_t>(Object::null())); | |
| 1505 __ movq(R13, FieldAddress(R10, Array::data_offset())); | |
| 1506 __ SmiUntag(R13); | |
| 1507 __ movq(RAX, Address(RBP, R13, TIMES_8, kWordSize)); // Get receiver. | |
| 1508 | |
| 1509 __ EnterFrame(0); | |
| 1510 __ pushq(raw_null); // Setup space on stack for result from noSuchMethod. | |
| 1511 __ pushq(RAX); // Receiver. | |
| 1512 __ pushq(RBX); // IC data array. | |
| 1513 __ pushq(R10); // Arguments descriptor array. | |
| 1514 __ subq(R13, Immediate(1)); // Arguments array length, minus the receiver. | |
| 1515 // See stack layout below explaining "wordSize * 8" offset. | |
| 1516 PushArgumentsArray(assembler, (kWordSize * 8)); | |
| 1517 | |
| 1518 // Stack: | |
| 1519 // TOS + 0: Argument array. | |
| 1520 // TOS + 1: Arguments descriptor array. | |
| 1521 // TOS + 2: Ic-data array. | |
| 1522 // TOS + 3: Receiver. | |
| 1523 // TOS + 4: Place for result from noSuchMethod. | |
| 1524 // TOS + 5: Saved RBP of previous frame. <== RBP | |
| 1525 // TOS + 6: Dart callee (or stub) code return address | |
| 1526 // TOS + 7: Saved RBP of dart caller frame. | |
| 1527 // TOS + 8: Dart caller code return address | |
| 1528 // TOS + 9: Last argument of caller. | |
| 1529 // .... | |
| 1530 __ CallRuntimeFromStub(kInvokeNoSuchMethodFunctionRuntimeEntry); | |
| 1531 // Remove arguments. | |
| 1532 __ popq(RAX); | |
| 1533 __ popq(RAX); | |
| 1534 __ popq(RAX); | |
| 1535 __ popq(RAX); | |
| 1536 __ popq(RAX); // Get result into RAX. | |
| 1537 | |
| 1538 // Remove the stub frame as we are about to return. | |
| 1539 __ LeaveFrame(); | |
| 1540 __ ret(); | |
| 1146 } | 1541 } |
| 1147 | 1542 |
| 1148 | 1543 |
| 1544 | |
| 1149 // Generate inline cache check for 'num_args'. | 1545 // Generate inline cache check for 'num_args'. |
| 1150 // RBX: Inline cache data array. | 1546 // RBX: Inline cache data array. |
| 1151 // R10: Arguments array. | 1547 // R10: Arguments array. |
| 1152 // TOS(0): return address | 1548 // TOS(0): return address |
| 1153 // Control flow: | 1549 // Control flow: |
| 1154 // - If receiver is null -> jump to IC miss. | 1550 // - If receiver is null -> jump to IC miss. |
| 1155 // - If receiver is Smi -> load Smi class. | 1551 // - If receiver is Smi -> load Smi class. |
| 1156 // - If receiver is not-Smi -> load receiver's class. | 1552 // - If receiver is not-Smi -> load receiver's class. |
| 1157 // - Check if 'num_args' (including receiver) match any IC data group. | 1553 // - Check if 'num_args' (including receiver) match any IC data group. |
| 1158 // - Match found -> jump to target. | 1554 // - Match found -> jump to target. |
| (...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1300 void StubCode::GenerateOneArgCheckInlineCacheStub(Assembler* assembler) { | 1696 void StubCode::GenerateOneArgCheckInlineCacheStub(Assembler* assembler) { |
| 1301 return GenerateNArgsCheckInlineCacheStub(assembler, 1); | 1697 return GenerateNArgsCheckInlineCacheStub(assembler, 1); |
| 1302 } | 1698 } |
| 1303 | 1699 |
| 1304 | 1700 |
| 1305 void StubCode::GenerateTwoArgsCheckInlineCacheStub(Assembler* assembler) { | 1701 void StubCode::GenerateTwoArgsCheckInlineCacheStub(Assembler* assembler) { |
| 1306 return GenerateNArgsCheckInlineCacheStub(assembler, 2); | 1702 return GenerateNArgsCheckInlineCacheStub(assembler, 2); |
| 1307 } | 1703 } |
| 1308 | 1704 |
| 1309 | 1705 |
| 1706 // RBX: Function object. | |
| 1707 // R10: Arguments array. | |
| 1708 // TOS(0): return address (Dart code). | |
| 1310 void StubCode::GenerateBreakpointStaticStub(Assembler* assembler) { | 1709 void StubCode::GenerateBreakpointStaticStub(Assembler* assembler) { |
| 1311 __ Unimplemented("BreakpointStatic stub"); | 1710 __ Untested("BreakpointStatic stub"); |
| 1711 __ EnterFrame(0); | |
| 1712 __ pushq(R10); | |
| 1713 __ pushq(RBX); | |
| 1714 __ CallRuntimeFromStub(kBreakpointStaticHandlerRuntimeEntry); | |
| 1715 __ popq(RBX); | |
| 1716 __ popq(R10); | |
| 1717 __ LeaveFrame(); | |
| 1718 | |
| 1719 // Now call the static function. The breakpoint handler function | |
| 1720 // ensures that the call target is compiled. | |
| 1721 __ movq(RAX, FieldAddress(RBX, Function::code_offset())); | |
| 1722 __ movq(RBX, FieldAddress(RAX, Code::instructions_offset())); | |
| 1723 __ addq(RBX, Immediate(Instructions::HeaderSize() - kHeapObjectTag)); | |
| 1724 __ jmp(RBX); | |
| 1312 } | 1725 } |
| 1313 | 1726 |
| 1314 | 1727 |
| 1728 // RBX: Inline cache data array. | |
| 1729 // R10: Arguments array. | |
| 1730 // TOS(0): return address (Dart code). | |
| 1315 void StubCode::GenerateBreakpointDynamicStub(Assembler* assembler) { | 1731 void StubCode::GenerateBreakpointDynamicStub(Assembler* assembler) { |
| 1316 __ Unimplemented("BreakpointDynamic stub"); | 1732 __ Untested("BreakpointDynamic stub"); |
| 1733 __ EnterFrame(0); | |
| 1734 __ pushq(RBX); | |
| 1735 __ pushq(R10); | |
| 1736 __ CallRuntimeFromStub(kBreakpointDynamicHandlerRuntimeEntry); | |
| 1737 __ popq(R10); | |
| 1738 __ popq(RBX); | |
| 1739 __ LeaveFrame(); | |
| 1740 // Now call the dynamic function. | |
| 1741 __ jmp(&StubCode::OneArgCheckInlineCacheLabel()); | |
| 1317 } | 1742 } |
| 1318 | 1743 |
| 1319 } // namespace dart | 1744 } // namespace dart |
| 1320 | 1745 |
| 1321 #endif // defined TARGET_ARCH_X64 | 1746 #endif // defined TARGET_ARCH_X64 |
| OLD | NEW |