2197 lines
		
	
	
		
			91 KiB
		
	
	
	
		
			ArmAsm
		
	
	
	
			
		
		
	
	
			2197 lines
		
	
	
		
			91 KiB
		
	
	
	
		
			ArmAsm
		
	
	
	
| /*
 | |
|  * Copyright (C) 2012 The Android Open Source Project
 | |
|  *
 | |
|  * Licensed under the Apache License, Version 2.0 (the "License");
 | |
|  * you may not use this file except in compliance with the License.
 | |
|  * You may obtain a copy of the License at
 | |
|  *
 | |
|  *      http://www.apache.org/licenses/LICENSE-2.0
 | |
|  *
 | |
|  * Unless required by applicable law or agreed to in writing, software
 | |
|  * distributed under the License is distributed on an "AS IS" BASIS,
 | |
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
|  * See the License for the specific language governing permissions and
 | |
|  * limitations under the License.
 | |
|  */
 | |
| 
 | |
| #include "asm_support_x86_64.S"
 | |
| #include "interpreter/cfi_asm_support.h"
 | |
| 
 | |
| #include "arch/quick_alloc_entrypoints.S"
 | |
| 
 | |
| MACRO0(ASSERT_USE_READ_BARRIER)
 | |
| #if !defined(USE_READ_BARRIER)
 | |
|     int3
 | |
|     int3
 | |
| #endif
 | |
| END_MACRO
 | |
| 
 | |
| // For x86, the CFA is esp+4, the address above the pushed return address on the stack.
 | |
| 
 | |
| 
 | |
|     /*
 | |
|      * Macro that sets up the callee save frame to conform with
 | |
|      * Runtime::CreateCalleeSaveMethod(kSaveRefsAndArgs)
 | |
|      */
 | |
| MACRO0(SETUP_SAVE_REFS_AND_ARGS_FRAME)
 | |
| #if defined(__APPLE__)
 | |
|     int3
 | |
|     int3
 | |
| #else
 | |
|     SETUP_SAVE_REFS_AND_ARGS_FRAME_REGISTERS_ONLY
 | |
|     // R10 := Runtime::Current()
 | |
|     LOAD_RUNTIME_INSTANCE r10
 | |
|     // R10 := ArtMethod* for ref and args callee save frame method.
 | |
|     movq RUNTIME_SAVE_REFS_AND_ARGS_METHOD_OFFSET(%r10), %r10
 | |
|     // Store ArtMethod* to bottom of stack.
 | |
|     movq %r10, 0(%rsp)
 | |
|     // Store rsp as the top quick frame.
 | |
|     movq %rsp, %gs:THREAD_TOP_QUICK_FRAME_OFFSET
 | |
| #endif  // __APPLE__
 | |
| END_MACRO
 | |
| 
 | |
| MACRO0(SETUP_SAVE_REFS_AND_ARGS_FRAME_WITH_METHOD_IN_RDI)
 | |
|     SETUP_SAVE_REFS_AND_ARGS_FRAME_REGISTERS_ONLY
 | |
|     // Store ArtMethod to bottom of stack.
 | |
|     movq %rdi, 0(%rsp)
 | |
|     // Store rsp as the stop quick frame.
 | |
|     movq %rsp, %gs:THREAD_TOP_QUICK_FRAME_OFFSET
 | |
| END_MACRO
 | |
| 
 | |
|     /*
 | |
|      * Macro that sets up the callee save frame to conform with
 | |
|      * Runtime::CreateCalleeSaveMethod(kSaveEverything)
 | |
|      * when R14 and R15 are already saved.
 | |
|      */
 | |
| MACRO1(SETUP_SAVE_EVERYTHING_FRAME_R14_R15_SAVED, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET)
 | |
| #if defined(__APPLE__)
 | |
|     int3
 | |
|     int3
 | |
| #else
 | |
|     // Save core registers from highest to lowest to agree with core spills bitmap.
 | |
|     // R14 and R15, or at least placeholders for them, are already on the stack.
 | |
|     PUSH r13
 | |
|     PUSH r12
 | |
|     PUSH r11
 | |
|     PUSH r10
 | |
|     PUSH r9
 | |
|     PUSH r8
 | |
|     PUSH rdi
 | |
|     PUSH rsi
 | |
|     PUSH rbp
 | |
|     PUSH rbx
 | |
|     PUSH rdx
 | |
|     PUSH rcx
 | |
|     PUSH rax
 | |
|     // Create space for FPRs and stack alignment padding.
 | |
|     subq MACRO_LITERAL(8 + 16 * 8), %rsp
 | |
|     CFI_ADJUST_CFA_OFFSET(8 + 16 * 8)
 | |
|     // R10 := Runtime::Current()
 | |
|     LOAD_RUNTIME_INSTANCE r10
 | |
|     // Save FPRs.
 | |
|     movq %xmm0, 8(%rsp)
 | |
|     movq %xmm1, 16(%rsp)
 | |
|     movq %xmm2, 24(%rsp)
 | |
|     movq %xmm3, 32(%rsp)
 | |
|     movq %xmm4, 40(%rsp)
 | |
|     movq %xmm5, 48(%rsp)
 | |
|     movq %xmm6, 56(%rsp)
 | |
|     movq %xmm7, 64(%rsp)
 | |
|     movq %xmm8, 72(%rsp)
 | |
|     movq %xmm9, 80(%rsp)
 | |
|     movq %xmm10, 88(%rsp)
 | |
|     movq %xmm11, 96(%rsp)
 | |
|     movq %xmm12, 104(%rsp)
 | |
|     movq %xmm13, 112(%rsp)
 | |
|     movq %xmm14, 120(%rsp)
 | |
|     movq %xmm15, 128(%rsp)
 | |
|     // Push ArtMethod* for save everything frame method.
 | |
|     pushq \runtime_method_offset(%r10)
 | |
|     CFI_ADJUST_CFA_OFFSET(8)
 | |
|     // Store rsp as the top quick frame.
 | |
|     movq %rsp, %gs:THREAD_TOP_QUICK_FRAME_OFFSET
 | |
| 
 | |
|     // Ugly compile-time check, but we only have the preprocessor.
 | |
|     // Last +8: implicit return address pushed on stack when caller made call.
 | |
| #if (FRAME_SIZE_SAVE_EVERYTHING != 15 * 8 + 16 * 8 + 16 + 8)
 | |
| #error "FRAME_SIZE_SAVE_EVERYTHING(X86_64) size not as expected."
 | |
| #endif
 | |
| #endif  // __APPLE__
 | |
| END_MACRO
 | |
| 
 | |
|     /*
 | |
|      * Macro that sets up the callee save frame to conform with
 | |
|      * Runtime::CreateCalleeSaveMethod(kSaveEverything)
 | |
|      * when R15 is already saved.
 | |
|      */
 | |
| MACRO1(SETUP_SAVE_EVERYTHING_FRAME_R15_SAVED, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET)
 | |
|     PUSH r14
 | |
|     SETUP_SAVE_EVERYTHING_FRAME_R14_R15_SAVED \runtime_method_offset
 | |
| END_MACRO
 | |
| 
 | |
|     /*
 | |
|      * Macro that sets up the callee save frame to conform with
 | |
|      * Runtime::CreateCalleeSaveMethod(kSaveEverything)
 | |
|      */
 | |
| MACRO1(SETUP_SAVE_EVERYTHING_FRAME, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET)
 | |
|     PUSH r15
 | |
|     SETUP_SAVE_EVERYTHING_FRAME_R15_SAVED \runtime_method_offset
 | |
| END_MACRO
 | |
| 
 | |
| MACRO0(RESTORE_SAVE_EVERYTHING_FRAME_FRPS)
 | |
|     // Restore FPRs. Method and padding is still on the stack.
 | |
|     movq 16(%rsp), %xmm0
 | |
|     movq 24(%rsp), %xmm1
 | |
|     movq 32(%rsp), %xmm2
 | |
|     movq 40(%rsp), %xmm3
 | |
|     movq 48(%rsp), %xmm4
 | |
|     movq 56(%rsp), %xmm5
 | |
|     movq 64(%rsp), %xmm6
 | |
|     movq 72(%rsp), %xmm7
 | |
|     movq 80(%rsp), %xmm8
 | |
|     movq 88(%rsp), %xmm9
 | |
|     movq 96(%rsp), %xmm10
 | |
|     movq 104(%rsp), %xmm11
 | |
|     movq 112(%rsp), %xmm12
 | |
|     movq 120(%rsp), %xmm13
 | |
|     movq 128(%rsp), %xmm14
 | |
|     movq 136(%rsp), %xmm15
 | |
| END_MACRO
 | |
| 
 | |
| MACRO0(RESTORE_SAVE_EVERYTHING_FRAME_GPRS_EXCEPT_RAX)
 | |
|     // Restore callee and GPR args (except RAX), mixed together to agree with core spills bitmap.
 | |
|     POP rcx
 | |
|     POP rdx
 | |
|     POP rbx
 | |
|     POP rbp
 | |
|     POP rsi
 | |
|     POP rdi
 | |
|     POP r8
 | |
|     POP r9
 | |
|     POP r10
 | |
|     POP r11
 | |
|     POP r12
 | |
|     POP r13
 | |
|     POP r14
 | |
|     POP r15
 | |
| END_MACRO
 | |
| 
 | |
| MACRO0(RESTORE_SAVE_EVERYTHING_FRAME)
 | |
|     RESTORE_SAVE_EVERYTHING_FRAME_FRPS
 | |
| 
 | |
|     // Remove save everything callee save method, stack alignment padding and FPRs.
 | |
|     addq MACRO_LITERAL(16 + 16 * 8), %rsp
 | |
|     CFI_ADJUST_CFA_OFFSET(-(16 + 16 * 8))
 | |
| 
 | |
|     POP rax
 | |
|     RESTORE_SAVE_EVERYTHING_FRAME_GPRS_EXCEPT_RAX
 | |
| END_MACRO
 | |
| 
 | |
| MACRO0(RESTORE_SAVE_EVERYTHING_FRAME_KEEP_RAX)
 | |
|     RESTORE_SAVE_EVERYTHING_FRAME_FRPS
 | |
| 
 | |
|     // Remove save everything callee save method, stack alignment padding and FPRs, skip RAX.
 | |
|     addq MACRO_LITERAL(16 + 16 * 8 + 8), %rsp
 | |
|     CFI_ADJUST_CFA_OFFSET(-(16 + 16 * 8 + 8))
 | |
| 
 | |
|     RESTORE_SAVE_EVERYTHING_FRAME_GPRS_EXCEPT_RAX
 | |
| END_MACRO
 | |
| 
 | |
| MACRO2(NO_ARG_RUNTIME_EXCEPTION, c_name, cxx_name)
 | |
|     DEFINE_FUNCTION VAR(c_name)
 | |
|     SETUP_SAVE_ALL_CALLEE_SAVES_FRAME  // save all registers as basis for long jump context
 | |
|     // Outgoing argument set up
 | |
|     movq %gs:THREAD_SELF_OFFSET, %rdi  // pass Thread::Current()
 | |
|     call CALLVAR(cxx_name)             // cxx_name(Thread*)
 | |
|     UNREACHABLE
 | |
|     END_FUNCTION VAR(c_name)
 | |
| END_MACRO
 | |
| 
 | |
| MACRO2(NO_ARG_RUNTIME_EXCEPTION_SAVE_EVERYTHING, c_name, cxx_name)
 | |
|     DEFINE_FUNCTION VAR(c_name)
 | |
|     SETUP_SAVE_EVERYTHING_FRAME        // save all registers as basis for long jump context
 | |
|     // Outgoing argument set up
 | |
|     movq %gs:THREAD_SELF_OFFSET, %rdi  // pass Thread::Current()
 | |
|     call CALLVAR(cxx_name)             // cxx_name(Thread*)
 | |
|     UNREACHABLE
 | |
|     END_FUNCTION VAR(c_name)
 | |
| END_MACRO
 | |
| 
 | |
| MACRO2(ONE_ARG_RUNTIME_EXCEPTION, c_name, cxx_name)
 | |
|     DEFINE_FUNCTION VAR(c_name)
 | |
|     SETUP_SAVE_ALL_CALLEE_SAVES_FRAME  // save all registers as basis for long jump context
 | |
|     // Outgoing argument set up
 | |
|     movq %gs:THREAD_SELF_OFFSET, %rsi  // pass Thread::Current()
 | |
|     call CALLVAR(cxx_name)             // cxx_name(arg1, Thread*)
 | |
|     UNREACHABLE
 | |
|     END_FUNCTION VAR(c_name)
 | |
| END_MACRO
 | |
| 
 | |
| MACRO2(TWO_ARG_RUNTIME_EXCEPTION_SAVE_EVERYTHING, c_name, cxx_name)
 | |
|     DEFINE_FUNCTION VAR(c_name)
 | |
|     SETUP_SAVE_EVERYTHING_FRAME        // save all registers as basis for long jump context
 | |
|     // Outgoing argument set up
 | |
|     movq %gs:THREAD_SELF_OFFSET, %rdx  // pass Thread::Current()
 | |
|     call CALLVAR(cxx_name)             // cxx_name(Thread*)
 | |
|     UNREACHABLE
 | |
|     END_FUNCTION VAR(c_name)
 | |
| END_MACRO
 | |
| 
 | |
|     /*
 | |
|      * Called by managed code to create and deliver a NullPointerException.
 | |
|      */
 | |
| NO_ARG_RUNTIME_EXCEPTION_SAVE_EVERYTHING art_quick_throw_null_pointer_exception, artThrowNullPointerExceptionFromCode
 | |
| 
 | |
|     /*
 | |
|      * Call installed by a signal handler to create and deliver a NullPointerException.
 | |
|      */
 | |
| DEFINE_FUNCTION_CUSTOM_CFA art_quick_throw_null_pointer_exception_from_signal, 2 * __SIZEOF_POINTER__
 | |
|     // Fault address and return address were saved by the fault handler.
 | |
|     // Save all registers as basis for long jump context; R15 will replace fault address later.
 | |
|     SETUP_SAVE_EVERYTHING_FRAME_R15_SAVED
 | |
|     // Retrieve fault address and save R15.
 | |
|     movq (FRAME_SIZE_SAVE_EVERYTHING - 2 * __SIZEOF_POINTER__)(%rsp), %rdi
 | |
|     movq %r15, (FRAME_SIZE_SAVE_EVERYTHING - 2 * __SIZEOF_POINTER__)(%rsp)
 | |
|     CFI_REL_OFFSET(%r15, (FRAME_SIZE_SAVE_EVERYTHING - 2 * __SIZEOF_POINTER__))
 | |
|     // Outgoing argument set up; RDI already contains the fault address.
 | |
|     movq %gs:THREAD_SELF_OFFSET, %rsi  // pass Thread::Current()
 | |
|     call SYMBOL(artThrowNullPointerExceptionFromSignal)  // (addr, self)
 | |
|     UNREACHABLE
 | |
| END_FUNCTION art_quick_throw_null_pointer_exception_from_signal
 | |
| 
 | |
|     /*
 | |
|      * Called by managed code to create and deliver an ArithmeticException.
 | |
|      */
 | |
| NO_ARG_RUNTIME_EXCEPTION_SAVE_EVERYTHING art_quick_throw_div_zero, artThrowDivZeroFromCode
 | |
| 
 | |
|     /*
 | |
|      * Called by managed code to create and deliver a StackOverflowError.
 | |
|      */
 | |
| NO_ARG_RUNTIME_EXCEPTION art_quick_throw_stack_overflow, artThrowStackOverflowFromCode
 | |
| 
 | |
|     /*
 | |
|      * Called by managed code, saves callee saves and then calls artThrowException
 | |
|      * that will place a mock Method* at the bottom of the stack. Arg1 holds the exception.
 | |
|      */
 | |
| ONE_ARG_RUNTIME_EXCEPTION art_quick_deliver_exception, artDeliverExceptionFromCode
 | |
| 
 | |
|     /*
 | |
|      * Called by managed code to create and deliver an ArrayIndexOutOfBoundsException. Arg1 holds
 | |
|      * index, arg2 holds limit.
 | |
|      */
 | |
| TWO_ARG_RUNTIME_EXCEPTION_SAVE_EVERYTHING art_quick_throw_array_bounds, artThrowArrayBoundsFromCode
 | |
| 
 | |
|     /*
 | |
|      * Called by managed code to create and deliver a StringIndexOutOfBoundsException
 | |
|      * as if thrown from a call to String.charAt(). Arg1 holds index, arg2 holds limit.
 | |
|      */
 | |
| TWO_ARG_RUNTIME_EXCEPTION_SAVE_EVERYTHING art_quick_throw_string_bounds, artThrowStringBoundsFromCode
 | |
| 
 | |
|     /*
 | |
|      * All generated callsites for interface invokes and invocation slow paths will load arguments
 | |
|      * as usual - except instead of loading arg0/rdi with the target Method*, arg0/rdi will contain
 | |
|      * the method_idx.  This wrapper will save arg1-arg3, and call the appropriate C helper.
 | |
|      * NOTE: "this" is first visible argument of the target, and so can be found in arg1/rsi.
 | |
|      *
 | |
|      * The helper will attempt to locate the target and return a 128-bit result in rax/rdx consisting
 | |
|      * of the target Method* in rax and method->code_ in rdx.
 | |
|      *
 | |
|      * If unsuccessful, the helper will return null/????. There will be a pending exception in the
 | |
|      * thread and we branch to another stub to deliver it.
 | |
|      *
 | |
|      * On success this wrapper will restore arguments and *jump* to the target, leaving the return
 | |
|      * location on the stack.
 | |
|      *
 | |
|      * Adapted from x86 code.
 | |
|      */
 | |
| MACRO1(INVOKE_TRAMPOLINE_BODY, cxx_name)
 | |
|     SETUP_SAVE_REFS_AND_ARGS_FRAME  // save callee saves in case allocation triggers GC
 | |
|     // Helper signature is always
 | |
|     // (method_idx, *this_object, *caller_method, *self, sp)
 | |
| 
 | |
|     movq %gs:THREAD_SELF_OFFSET, %rdx                      // pass Thread
 | |
|     movq %rsp, %rcx                                        // pass SP
 | |
| 
 | |
|     call CALLVAR(cxx_name)                                 // cxx_name(arg1, arg2, Thread*, SP)
 | |
|                                                            // save the code pointer
 | |
|     movq %rax, %rdi
 | |
|     movq %rdx, %rax
 | |
|     RESTORE_SAVE_REFS_AND_ARGS_FRAME
 | |
| 
 | |
|     testq %rdi, %rdi
 | |
|     jz 1f
 | |
| 
 | |
|     // Tail call to intended method.
 | |
|     jmp *%rax
 | |
| 1:
 | |
|     DELIVER_PENDING_EXCEPTION
 | |
| END_MACRO
 | |
| MACRO2(INVOKE_TRAMPOLINE, c_name, cxx_name)
 | |
|     DEFINE_FUNCTION VAR(c_name)
 | |
|     INVOKE_TRAMPOLINE_BODY RAW_VAR(cxx_name)
 | |
|     END_FUNCTION VAR(c_name)
 | |
| END_MACRO
 | |
| 
 | |
| INVOKE_TRAMPOLINE art_quick_invoke_interface_trampoline_with_access_check, artInvokeInterfaceTrampolineWithAccessCheck
 | |
| 
 | |
| INVOKE_TRAMPOLINE art_quick_invoke_static_trampoline_with_access_check, artInvokeStaticTrampolineWithAccessCheck
 | |
| INVOKE_TRAMPOLINE art_quick_invoke_direct_trampoline_with_access_check, artInvokeDirectTrampolineWithAccessCheck
 | |
| INVOKE_TRAMPOLINE art_quick_invoke_super_trampoline_with_access_check, artInvokeSuperTrampolineWithAccessCheck
 | |
| INVOKE_TRAMPOLINE art_quick_invoke_virtual_trampoline_with_access_check, artInvokeVirtualTrampolineWithAccessCheck
 | |
| 
 | |
| 
 | |
|     /*
 | |
|      * Helper for quick invocation stub to set up XMM registers. Assumes r10 == shorty,
 | |
|      * r11 == arg_array. Clobbers r10, r11 and al. Branches to xmm_setup_finished if it encounters
 | |
|      * the end of the shorty.
 | |
|      */
 | |
| MACRO2(LOOP_OVER_SHORTY_LOADING_XMMS, xmm_reg, finished)
 | |
| 1: // LOOP
 | |
|     movb (%r10), %al              // al := *shorty
 | |
|     addq MACRO_LITERAL(1), %r10   // shorty++
 | |
|     cmpb MACRO_LITERAL(0), %al    // if (al == '\0') goto xmm_setup_finished
 | |
|     je VAR(finished)
 | |
|     cmpb MACRO_LITERAL(68), %al   // if (al == 'D') goto FOUND_DOUBLE
 | |
|     je 2f
 | |
|     cmpb MACRO_LITERAL(70), %al   // if (al == 'F') goto FOUND_FLOAT
 | |
|     je 3f
 | |
|     addq MACRO_LITERAL(4), %r11   // arg_array++
 | |
|     //  Handle extra space in arg array taken by a long.
 | |
|     cmpb MACRO_LITERAL(74), %al   // if (al != 'J') goto LOOP
 | |
|     jne 1b
 | |
|     addq MACRO_LITERAL(4), %r11   // arg_array++
 | |
|     jmp 1b                        // goto LOOP
 | |
| 2:  // FOUND_DOUBLE
 | |
|     movsd (%r11), REG_VAR(xmm_reg)
 | |
|     addq MACRO_LITERAL(8), %r11   // arg_array+=2
 | |
|     jmp 4f
 | |
| 3:  // FOUND_FLOAT
 | |
|     movss (%r11), REG_VAR(xmm_reg)
 | |
|     addq MACRO_LITERAL(4), %r11   // arg_array++
 | |
| 4:
 | |
| END_MACRO
 | |
| 
 | |
|     /*
 | |
|      * Helper for quick invocation stub to set up GPR registers. Assumes r10 == shorty,
 | |
|      * r11 == arg_array. Clobbers r10, r11 and al. Branches to gpr_setup_finished if it encounters
 | |
|      * the end of the shorty.
 | |
|      */
 | |
| MACRO3(LOOP_OVER_SHORTY_LOADING_GPRS, gpr_reg64, gpr_reg32, finished)
 | |
| 1: // LOOP
 | |
|     movb (%r10), %al              // al := *shorty
 | |
|     addq MACRO_LITERAL(1), %r10   // shorty++
 | |
|     cmpb MACRO_LITERAL(0), %al    // if (al == '\0') goto gpr_setup_finished
 | |
|     je  VAR(finished)
 | |
|     cmpb MACRO_LITERAL(74), %al   // if (al == 'J') goto FOUND_LONG
 | |
|     je 2f
 | |
|     cmpb MACRO_LITERAL(70), %al   // if (al == 'F') goto SKIP_FLOAT
 | |
|     je 3f
 | |
|     cmpb MACRO_LITERAL(68), %al   // if (al == 'D') goto SKIP_DOUBLE
 | |
|     je 4f
 | |
|     movl (%r11), REG_VAR(gpr_reg32)
 | |
|     addq MACRO_LITERAL(4), %r11   // arg_array++
 | |
|     jmp 5f
 | |
| 2:  // FOUND_LONG
 | |
|     movq (%r11), REG_VAR(gpr_reg64)
 | |
|     addq MACRO_LITERAL(8), %r11   // arg_array+=2
 | |
|     jmp 5f
 | |
| 3:  // SKIP_FLOAT
 | |
|     addq MACRO_LITERAL(4), %r11   // arg_array++
 | |
|     jmp 1b
 | |
| 4:  // SKIP_DOUBLE
 | |
|     addq MACRO_LITERAL(8), %r11   // arg_array+=2
 | |
|     jmp 1b
 | |
| 5:
 | |
| END_MACRO
 | |
| 
 | |
|     /*
 | |
|      * Quick invocation stub.
 | |
|      * On entry:
 | |
|      *   [sp] = return address
 | |
|      *   rdi = method pointer
 | |
|      *   rsi = argument array that must at least contain the this pointer.
 | |
|      *   rdx = size of argument array in bytes
 | |
|      *   rcx = (managed) thread pointer
 | |
|      *   r8 = JValue* result
 | |
|      *   r9 = char* shorty
 | |
|      */
 | |
| DEFINE_FUNCTION art_quick_invoke_stub
 | |
| #if defined(__APPLE__)
 | |
|     int3
 | |
|     int3
 | |
| #else
 | |
|     // Set up argument XMM registers.
 | |
|     leaq 1(%r9), %r10             // R10 := shorty + 1  ; ie skip return arg character.
 | |
|     leaq 4(%rsi), %r11            // R11 := arg_array + 4 ; ie skip this pointer.
 | |
|     LOOP_OVER_SHORTY_LOADING_XMMS xmm0, .Lxmm_setup_finished
 | |
|     LOOP_OVER_SHORTY_LOADING_XMMS xmm1, .Lxmm_setup_finished
 | |
|     LOOP_OVER_SHORTY_LOADING_XMMS xmm2, .Lxmm_setup_finished
 | |
|     LOOP_OVER_SHORTY_LOADING_XMMS xmm3, .Lxmm_setup_finished
 | |
|     LOOP_OVER_SHORTY_LOADING_XMMS xmm4, .Lxmm_setup_finished
 | |
|     LOOP_OVER_SHORTY_LOADING_XMMS xmm5, .Lxmm_setup_finished
 | |
|     LOOP_OVER_SHORTY_LOADING_XMMS xmm6, .Lxmm_setup_finished
 | |
|     LOOP_OVER_SHORTY_LOADING_XMMS xmm7, .Lxmm_setup_finished
 | |
|     .balign 16
 | |
| .Lxmm_setup_finished:
 | |
|     PUSH rbp                      // Save rbp.
 | |
|     PUSH r8                       // Save r8/result*.
 | |
|     PUSH r9                       // Save r9/shorty*.
 | |
|     PUSH rbx                      // Save native callee save rbx
 | |
|     PUSH r12                      // Save native callee save r12
 | |
|     PUSH r13                      // Save native callee save r13
 | |
|     PUSH r14                      // Save native callee save r14
 | |
|     PUSH r15                      // Save native callee save r15
 | |
|     movq %rsp, %rbp               // Copy value of stack pointer into base pointer.
 | |
|     CFI_DEF_CFA_REGISTER(rbp)
 | |
| 
 | |
|     movl %edx, %r10d
 | |
|     addl LITERAL(100), %edx        // Reserve space for return addr, StackReference<method>, rbp,
 | |
|                                    // r8, r9, rbx, r12, r13, r14, and r15 in frame.
 | |
|     andl LITERAL(0xFFFFFFF0), %edx // Align frame size to 16 bytes.
 | |
|     subl LITERAL(72), %edx         // Remove space for return address, rbp, r8, r9, rbx, r12,
 | |
|                                    // r13, r14, and r15
 | |
|     subq %rdx, %rsp                // Reserve stack space for argument array.
 | |
| 
 | |
| #if (STACK_REFERENCE_SIZE != 4)
 | |
| #error "STACK_REFERENCE_SIZE(X86_64) size not as expected."
 | |
| #endif
 | |
|     movq LITERAL(0), (%rsp)       // Store null for method*
 | |
| 
 | |
|     movl %r10d, %ecx              // Place size of args in rcx.
 | |
|     movq %rdi, %rax               // rax := method to be called
 | |
|     movq %rsi, %r11               // r11 := arg_array
 | |
|     leaq 8(%rsp), %rdi            // rdi is pointing just above the ArtMethod* in the stack
 | |
|                                   // arguments.
 | |
|     // Copy arg array into stack.
 | |
|     rep movsb                     // while (rcx--) { *rdi++ = *rsi++ }
 | |
|     leaq 1(%r9), %r10             // r10 := shorty + 1  ; ie skip return arg character
 | |
|     movq %rax, %rdi               // rdi := method to be called
 | |
|     movl (%r11), %esi             // rsi := this pointer
 | |
|     addq LITERAL(4), %r11         // arg_array++
 | |
|     LOOP_OVER_SHORTY_LOADING_GPRS rdx, edx, .Lgpr_setup_finished
 | |
|     LOOP_OVER_SHORTY_LOADING_GPRS rcx, ecx, .Lgpr_setup_finished
 | |
|     LOOP_OVER_SHORTY_LOADING_GPRS r8, r8d, .Lgpr_setup_finished
 | |
|     LOOP_OVER_SHORTY_LOADING_GPRS r9, r9d, .Lgpr_setup_finished
 | |
| .Lgpr_setup_finished:
 | |
|     call *ART_METHOD_QUICK_CODE_OFFSET_64(%rdi) // Call the method.
 | |
|     movq %rbp, %rsp               // Restore stack pointer.
 | |
|     POP r15                       // Pop r15
 | |
|     POP r14                       // Pop r14
 | |
|     POP r13                       // Pop r13
 | |
|     POP r12                       // Pop r12
 | |
|     POP rbx                       // Pop rbx
 | |
|     POP r9                        // Pop r9 - shorty*
 | |
|     POP r8                        // Pop r8 - result*.
 | |
|     POP rbp                       // Pop rbp
 | |
|     cmpb LITERAL(68), (%r9)       // Test if result type char == 'D'.
 | |
|     je .Lreturn_double_quick
 | |
|     cmpb LITERAL(70), (%r9)       // Test if result type char == 'F'.
 | |
|     je .Lreturn_float_quick
 | |
|     movq %rax, (%r8)              // Store the result assuming its a long, int or Object*
 | |
|     ret
 | |
| .Lreturn_double_quick:
 | |
|     movsd %xmm0, (%r8)            // Store the double floating point result.
 | |
|     ret
 | |
| .Lreturn_float_quick:
 | |
|     movss %xmm0, (%r8)            // Store the floating point result.
 | |
|     ret
 | |
| #endif  // __APPLE__
 | |
| END_FUNCTION art_quick_invoke_stub
 | |
| 
 | |
|     /*
 | |
|      * Quick invocation stub.
 | |
|      * On entry:
 | |
|      *   [sp] = return address
 | |
|      *   rdi = method pointer
 | |
|      *   rsi = argument array or null if no arguments.
 | |
|      *   rdx = size of argument array in bytes
 | |
|      *   rcx = (managed) thread pointer
 | |
|      *   r8 = JValue* result
 | |
|      *   r9 = char* shorty
 | |
|      */
 | |
| DEFINE_FUNCTION art_quick_invoke_static_stub
 | |
| #if defined(__APPLE__)
 | |
|     int3
 | |
|     int3
 | |
| #else
 | |
|     // Set up argument XMM registers.
 | |
|     leaq 1(%r9), %r10             // R10 := shorty + 1  ; ie skip return arg character
 | |
|     movq %rsi, %r11               // R11 := arg_array
 | |
|     LOOP_OVER_SHORTY_LOADING_XMMS xmm0, .Lxmm_setup_finished2
 | |
|     LOOP_OVER_SHORTY_LOADING_XMMS xmm1, .Lxmm_setup_finished2
 | |
|     LOOP_OVER_SHORTY_LOADING_XMMS xmm2, .Lxmm_setup_finished2
 | |
|     LOOP_OVER_SHORTY_LOADING_XMMS xmm3, .Lxmm_setup_finished2
 | |
|     LOOP_OVER_SHORTY_LOADING_XMMS xmm4, .Lxmm_setup_finished2
 | |
|     LOOP_OVER_SHORTY_LOADING_XMMS xmm5, .Lxmm_setup_finished2
 | |
|     LOOP_OVER_SHORTY_LOADING_XMMS xmm6, .Lxmm_setup_finished2
 | |
|     LOOP_OVER_SHORTY_LOADING_XMMS xmm7, .Lxmm_setup_finished2
 | |
|     .balign 16
 | |
| .Lxmm_setup_finished2:
 | |
|     PUSH rbp                      // Save rbp.
 | |
|     PUSH r8                       // Save r8/result*.
 | |
|     PUSH r9                       // Save r9/shorty*.
 | |
|     PUSH rbx                      // Save rbx
 | |
|     PUSH r12                      // Save r12
 | |
|     PUSH r13                      // Save r13
 | |
|     PUSH r14                      // Save r14
 | |
|     PUSH r15                      // Save r15
 | |
|     movq %rsp, %rbp               // Copy value of stack pointer into base pointer.
 | |
|     CFI_DEF_CFA_REGISTER(rbp)
 | |
| 
 | |
|     movl %edx, %r10d
 | |
|     addl LITERAL(100), %edx        // Reserve space for return addr, StackReference<method>, rbp,
 | |
|                                    // r8, r9, r12, r13, r14, and r15 in frame.
 | |
|     andl LITERAL(0xFFFFFFF0), %edx // Align frame size to 16 bytes.
 | |
|     subl LITERAL(72), %edx         // Remove space for return address, rbp, r8, r9, rbx, r12,
 | |
|                                    // r13, r14, and r15.
 | |
|     subq %rdx, %rsp                // Reserve stack space for argument array.
 | |
| 
 | |
| #if (STACK_REFERENCE_SIZE != 4)
 | |
| #error "STACK_REFERENCE_SIZE(X86_64) size not as expected."
 | |
| #endif
 | |
|     movq LITERAL(0), (%rsp)        // Store null for method*
 | |
| 
 | |
|     movl %r10d, %ecx               // Place size of args in rcx.
 | |
|     movq %rdi, %rax                // rax := method to be called
 | |
|     movq %rsi, %r11                // r11 := arg_array
 | |
|     leaq 8(%rsp), %rdi             // rdi is pointing just above the ArtMethod* in the
 | |
|                                    // stack arguments.
 | |
|     // Copy arg array into stack.
 | |
|     rep movsb                      // while (rcx--) { *rdi++ = *rsi++ }
 | |
|     leaq 1(%r9), %r10              // r10 := shorty + 1  ; ie skip return arg character
 | |
|     movq %rax, %rdi                // rdi := method to be called
 | |
|     LOOP_OVER_SHORTY_LOADING_GPRS rsi, esi, .Lgpr_setup_finished2
 | |
|     LOOP_OVER_SHORTY_LOADING_GPRS rdx, edx, .Lgpr_setup_finished2
 | |
|     LOOP_OVER_SHORTY_LOADING_GPRS rcx, ecx, .Lgpr_setup_finished2
 | |
|     LOOP_OVER_SHORTY_LOADING_GPRS r8, r8d, .Lgpr_setup_finished2
 | |
|     LOOP_OVER_SHORTY_LOADING_GPRS r9, r9d, .Lgpr_setup_finished2
 | |
| .Lgpr_setup_finished2:
 | |
|     call *ART_METHOD_QUICK_CODE_OFFSET_64(%rdi) // Call the method.
 | |
|     movq %rbp, %rsp                // Restore stack pointer.
 | |
|     POP r15                        // Pop r15
 | |
|     POP r14                        // Pop r14
 | |
|     POP r13                        // Pop r13
 | |
|     POP r12                        // Pop r12
 | |
|     POP rbx                        // Pop rbx
 | |
|     POP r9                         // Pop r9 - shorty*.
 | |
|     POP r8                         // Pop r8 - result*.
 | |
|     POP rbp                        // Pop rbp
 | |
|     cmpb LITERAL(68), (%r9)        // Test if result type char == 'D'.
 | |
|     je .Lreturn_double_quick2
 | |
|     cmpb LITERAL(70), (%r9)        // Test if result type char == 'F'.
 | |
|     je .Lreturn_float_quick2
 | |
|     movq %rax, (%r8)               // Store the result assuming its a long, int or Object*
 | |
|     ret
 | |
| .Lreturn_double_quick2:
 | |
|     movsd %xmm0, (%r8)             // Store the double floating point result.
 | |
|     ret
 | |
| .Lreturn_float_quick2:
 | |
|     movss %xmm0, (%r8)             // Store the floating point result.
 | |
|     ret
 | |
| #endif  // __APPLE__
 | |
| END_FUNCTION art_quick_invoke_static_stub
 | |
| 
 | |
|     /*
 | |
|      * Long jump stub.
 | |
|      * On entry:
 | |
|      *   rdi = gprs
 | |
|      *   rsi = fprs
 | |
|      */
 | |
| DEFINE_FUNCTION art_quick_do_long_jump
 | |
| #if defined(__APPLE__)
 | |
|     int3
 | |
|     int3
 | |
| #else
 | |
|     // Restore FPRs.
 | |
|     movq 0(%rsi), %xmm0
 | |
|     movq 8(%rsi), %xmm1
 | |
|     movq 16(%rsi), %xmm2
 | |
|     movq 24(%rsi), %xmm3
 | |
|     movq 32(%rsi), %xmm4
 | |
|     movq 40(%rsi), %xmm5
 | |
|     movq 48(%rsi), %xmm6
 | |
|     movq 56(%rsi), %xmm7
 | |
|     movq 64(%rsi), %xmm8
 | |
|     movq 72(%rsi), %xmm9
 | |
|     movq 80(%rsi), %xmm10
 | |
|     movq 88(%rsi), %xmm11
 | |
|     movq 96(%rsi), %xmm12
 | |
|     movq 104(%rsi), %xmm13
 | |
|     movq 112(%rsi), %xmm14
 | |
|     movq 120(%rsi), %xmm15
 | |
|     // Restore FPRs.
 | |
|     movq %rdi, %rsp   // RSP points to gprs.
 | |
|     // Load all registers except RSP and RIP with values in gprs.
 | |
|     popq %r15
 | |
|     popq %r14
 | |
|     popq %r13
 | |
|     popq %r12
 | |
|     popq %r11
 | |
|     popq %r10
 | |
|     popq %r9
 | |
|     popq %r8
 | |
|     popq %rdi
 | |
|     popq %rsi
 | |
|     popq %rbp
 | |
|     addq LITERAL(8), %rsp   // Skip rsp
 | |
|     popq %rbx
 | |
|     popq %rdx
 | |
|     popq %rcx
 | |
|     popq %rax
 | |
|     popq %rsp      // Load stack pointer.
 | |
|     ret            // From higher in the stack pop rip.
 | |
| #endif  // __APPLE__
 | |
| END_FUNCTION art_quick_do_long_jump
 | |
| 
 | |
| MACRO3(ONE_ARG_DOWNCALL, c_name, cxx_name, return_macro)
 | |
|     DEFINE_FUNCTION VAR(c_name)
 | |
|     SETUP_SAVE_REFS_ONLY_FRAME           // save ref containing registers for GC
 | |
|     // Outgoing argument set up
 | |
|     movq %gs:THREAD_SELF_OFFSET, %rsi    // pass Thread::Current()
 | |
|     call CALLVAR(cxx_name)               // cxx_name(arg0, Thread*)
 | |
|     RESTORE_SAVE_REFS_ONLY_FRAME         // restore frame up to return address
 | |
|     CALL_MACRO(return_macro)             // return or deliver exception
 | |
|     END_FUNCTION VAR(c_name)
 | |
| END_MACRO
 | |
| 
 | |
| MACRO3(TWO_ARG_DOWNCALL, c_name, cxx_name, return_macro)
 | |
|     DEFINE_FUNCTION VAR(c_name)
 | |
|     SETUP_SAVE_REFS_ONLY_FRAME           // save ref containing registers for GC
 | |
|     // Outgoing argument set up
 | |
|     movq %gs:THREAD_SELF_OFFSET, %rdx    // pass Thread::Current()
 | |
|     call CALLVAR(cxx_name)               // cxx_name(arg0, arg1, Thread*)
 | |
|     RESTORE_SAVE_REFS_ONLY_FRAME         // restore frame up to return address
 | |
|     CALL_MACRO(return_macro)             // return or deliver exception
 | |
|     END_FUNCTION VAR(c_name)
 | |
| END_MACRO
 | |
| 
 | |
| MACRO3(THREE_ARG_DOWNCALL, c_name, cxx_name, return_macro)
 | |
|     DEFINE_FUNCTION VAR(c_name)
 | |
|     SETUP_SAVE_REFS_ONLY_FRAME          // save ref containing registers for GC
 | |
|     // Outgoing argument set up
 | |
|     movq %gs:THREAD_SELF_OFFSET, %rcx   // pass Thread::Current()
 | |
|     call CALLVAR(cxx_name)              // cxx_name(arg0, arg1, arg2, Thread*)
 | |
|     RESTORE_SAVE_REFS_ONLY_FRAME        // restore frame up to return address
 | |
|     CALL_MACRO(return_macro)            // return or deliver exception
 | |
|     END_FUNCTION VAR(c_name)
 | |
| END_MACRO
 | |
| 
 | |
| MACRO3(FOUR_ARG_DOWNCALL, c_name, cxx_name, return_macro)
 | |
|     DEFINE_FUNCTION VAR(c_name)
 | |
|     SETUP_SAVE_REFS_ONLY_FRAME          // save ref containing registers for GC
 | |
|     // Outgoing argument set up
 | |
|     movq %gs:THREAD_SELF_OFFSET, %r8    // pass Thread::Current()
 | |
|     call CALLVAR(cxx_name)              // cxx_name(arg1, arg2, arg3, arg4, Thread*)
 | |
|     RESTORE_SAVE_REFS_ONLY_FRAME        // restore frame up to return address
 | |
|     CALL_MACRO(return_macro)            // return or deliver exception
 | |
|     END_FUNCTION VAR(c_name)
 | |
| END_MACRO
 | |
| 
 | |
| MACRO3(ONE_ARG_REF_DOWNCALL, c_name, cxx_name, return_macro)
 | |
|     DEFINE_FUNCTION VAR(c_name)
 | |
|     SETUP_SAVE_REFS_ONLY_FRAME
 | |
|                                         // arg0 is in rdi
 | |
|     movq %gs:THREAD_SELF_OFFSET, %rsi   // pass Thread::Current()
 | |
|     call CALLVAR(cxx_name)              // cxx_name(arg0, Thread*)
 | |
|     RESTORE_SAVE_REFS_ONLY_FRAME        // restore frame up to return address
 | |
|     CALL_MACRO(return_macro)
 | |
|     END_FUNCTION VAR(c_name)
 | |
| END_MACRO
 | |
| 
 | |
| MACRO3(TWO_ARG_REF_DOWNCALL, c_name, cxx_name, return_macro)
 | |
|     DEFINE_FUNCTION VAR(c_name)
 | |
|     SETUP_SAVE_REFS_ONLY_FRAME
 | |
|                                         // arg0 and arg1 are in rdi/rsi
 | |
|     movq %gs:THREAD_SELF_OFFSET, %rdx   // pass Thread::Current()
 | |
|     call CALLVAR(cxx_name)              // (arg0, arg1, Thread*)
 | |
|     RESTORE_SAVE_REFS_ONLY_FRAME        // restore frame up to return address
 | |
|     CALL_MACRO(return_macro)
 | |
|     END_FUNCTION VAR(c_name)
 | |
| END_MACRO
 | |
| 
 | |
| MACRO3(THREE_ARG_REF_DOWNCALL, c_name, cxx_name, return_macro)
 | |
|     DEFINE_FUNCTION VAR(c_name)
 | |
|     SETUP_SAVE_REFS_ONLY_FRAME
 | |
|                                         // arg0, arg1, and arg2 are in rdi/rsi/rdx
 | |
|     movq %gs:THREAD_SELF_OFFSET, %rcx   // pass Thread::Current()
 | |
|     call CALLVAR(cxx_name)              // cxx_name(arg0, arg1, arg2, Thread*)
 | |
|     RESTORE_SAVE_REFS_ONLY_FRAME        // restore frame up to return address
 | |
|     CALL_MACRO(return_macro)            // return or deliver exception
 | |
|     END_FUNCTION VAR(c_name)
 | |
| END_MACRO
 | |
| 
 | |
|     /*
 | |
|      * Macro for resolution and initialization of indexed DEX file
 | |
|      * constants such as classes and strings.
 | |
|      */
 | |
| MACRO3(ONE_ARG_SAVE_EVERYTHING_DOWNCALL, c_name, cxx_name, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET)
 | |
|     DEFINE_FUNCTION VAR(c_name)
 | |
|     SETUP_SAVE_EVERYTHING_FRAME \runtime_method_offset  // save everything for GC
 | |
|     // Outgoing argument set up
 | |
|     movl %eax, %edi                               // pass the index of the constant as arg0
 | |
|     movq %gs:THREAD_SELF_OFFSET, %rsi             // pass Thread::Current()
 | |
|     call CALLVAR(cxx_name)                        // cxx_name(arg0, Thread*)
 | |
|     testl %eax, %eax                              // If result is null, deliver the OOME.
 | |
|     jz 1f
 | |
|     CFI_REMEMBER_STATE
 | |
|     RESTORE_SAVE_EVERYTHING_FRAME_KEEP_RAX        // restore frame up to return address
 | |
|     ret
 | |
|     CFI_RESTORE_STATE_AND_DEF_CFA rsp, FRAME_SIZE_SAVE_EVERYTHING
 | |
| 1:
 | |
|     DELIVER_PENDING_EXCEPTION_FRAME_READY
 | |
|     END_FUNCTION VAR(c_name)
 | |
| END_MACRO
 | |
| 
 | |
| MACRO2(ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT, c_name, cxx_name)
 | |
|     ONE_ARG_SAVE_EVERYTHING_DOWNCALL \c_name, \cxx_name, RUNTIME_SAVE_EVERYTHING_FOR_CLINIT_METHOD_OFFSET
 | |
| END_MACRO
 | |
| 
 | |
| MACRO0(RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER)
 | |
|     testq %rax, %rax               // rax == 0 ?
 | |
|     jz  1f                         // if rax == 0 goto 1
 | |
|     ret                            // return
 | |
| 1:                                 // deliver exception on current thread
 | |
|     DELIVER_PENDING_EXCEPTION
 | |
| END_MACRO
 | |
| 
 | |
| MACRO0(RETURN_IF_EAX_ZERO)
 | |
|     testl %eax, %eax               // eax == 0 ?
 | |
|     jnz  1f                        // if eax != 0 goto 1
 | |
|     ret                            // return
 | |
| 1:                                 // deliver exception on current thread
 | |
|     DELIVER_PENDING_EXCEPTION
 | |
| END_MACRO
 | |
| 
 | |
| // Generate the allocation entrypoints for each allocator.
 | |
| GENERATE_ALLOC_ENTRYPOINTS_FOR_NON_TLAB_ALLOCATORS
 | |
| 
 | |
| // Comment out allocators that have x86_64 specific asm.
 | |
| // Region TLAB:
 | |
| // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB)
 | |
| // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region_tlab, RegionTLAB)
 | |
| GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB)
 | |
| GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_OBJECT(_region_tlab, RegionTLAB)
 | |
| // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_tlab, RegionTLAB)
 | |
| // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED8(_region_tlab, RegionTLAB)
 | |
| // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED16(_region_tlab, RegionTLAB)
 | |
| // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED32(_region_tlab, RegionTLAB)
 | |
| // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED64(_region_tlab, RegionTLAB)
 | |
| GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region_tlab, RegionTLAB)
 | |
| GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region_tlab, RegionTLAB)
 | |
| GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_tlab, RegionTLAB)
 | |
| // Normal TLAB:
 | |
| // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_tlab, TLAB)
 | |
| // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_tlab, TLAB)
 | |
| GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_tlab, TLAB)
 | |
| GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_OBJECT(_tlab, TLAB)
 | |
| // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_tlab, TLAB)
 | |
| // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED8(_tlab, TLAB)
 | |
| // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED16(_tlab, TLAB)
 | |
| // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED32(_tlab, TLAB)
 | |
| // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED64(_tlab, TLAB)
 | |
| GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_tlab, TLAB)
 | |
| GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_tlab, TLAB)
 | |
| GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_tlab, TLAB)
 | |
| 
 | |
| 
 | |
| // A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_rosalloc, RosAlloc).
 | |
| MACRO2(ART_QUICK_ALLOC_OBJECT_ROSALLOC, c_name, cxx_name)
 | |
|     DEFINE_FUNCTION VAR(c_name)
 | |
|     // Fast path rosalloc allocation.
 | |
|     // RDI: mirror::Class*, RAX: return value
 | |
|     // RSI, RDX, RCX, R8, R9: free.
 | |
|                                                            // Check if the thread local
 | |
|                                                            // allocation stack has room.
 | |
|     movq   %gs:THREAD_SELF_OFFSET, %r8                     // r8 = thread
 | |
|     movq   THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET(%r8), %rcx  // rcx = alloc stack top.
 | |
|     cmpq   THREAD_LOCAL_ALLOC_STACK_END_OFFSET(%r8), %rcx
 | |
|     jae    .Lslow_path\c_name
 | |
|                                                            // Load the object size
 | |
|     movl   MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET(%rdi), %eax
 | |
|                                                            // Check if the size is for a thread
 | |
|                                                            // local allocation. Also does the
 | |
|                                                            // initialized and finalizable checks.
 | |
|     cmpl   LITERAL(ROSALLOC_MAX_THREAD_LOCAL_BRACKET_SIZE), %eax
 | |
|     ja     .Lslow_path\c_name
 | |
|                                                            // Compute the rosalloc bracket index
 | |
|                                                            // from the size.
 | |
|     shrq   LITERAL(ROSALLOC_BRACKET_QUANTUM_SIZE_SHIFT), %rax
 | |
|                                                            // Load the rosalloc run (r9)
 | |
|                                                            // Subtract __SIZEOF_POINTER__ to
 | |
|                                                            // subtract one from edi as there is no
 | |
|                                                            // 0 byte run and the size is already
 | |
|                                                            // aligned.
 | |
|     movq   (THREAD_ROSALLOC_RUNS_OFFSET - __SIZEOF_POINTER__)(%r8, %rax, __SIZEOF_POINTER__), %r9
 | |
|                                                            // Load the free list head (rax). This
 | |
|                                                            // will be the return val.
 | |
|     movq   (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_HEAD_OFFSET)(%r9), %rax
 | |
|     testq  %rax, %rax
 | |
|     jz     .Lslow_path\c_name
 | |
|     // "Point of no slow path". Won't go to the slow path from here on. OK to clobber rdi and rsi.
 | |
|                                                            // Push the new object onto the thread
 | |
|                                                            // local allocation stack and
 | |
|                                                            // increment the thread local
 | |
|                                                            // allocation stack top.
 | |
|     movl   %eax, (%rcx)
 | |
|     addq   LITERAL(COMPRESSED_REFERENCE_SIZE), %rcx
 | |
|     movq   %rcx, THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET(%r8)
 | |
|                                                            // Load the next pointer of the head
 | |
|                                                            // and update the list head with the
 | |
|                                                            // next pointer.
 | |
|     movq   ROSALLOC_SLOT_NEXT_OFFSET(%rax), %rcx
 | |
|     movq   %rcx, (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_HEAD_OFFSET)(%r9)
 | |
|                                                            // Store the class pointer in the
 | |
|                                                            // header. This also overwrites the
 | |
|                                                            // next pointer. The offsets are
 | |
|                                                            // asserted to match.
 | |
| #if ROSALLOC_SLOT_NEXT_OFFSET != MIRROR_OBJECT_CLASS_OFFSET
 | |
| #error "Class pointer needs to overwrite next pointer."
 | |
| #endif
 | |
|     POISON_HEAP_REF edi
 | |
|     movl   %edi, MIRROR_OBJECT_CLASS_OFFSET(%rax)
 | |
|                                                            // Decrement the size of the free list
 | |
|     decl   (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_SIZE_OFFSET)(%r9)
 | |
|                                                            // No fence necessary for x86.
 | |
|     ret
 | |
| .Lslow_path\c_name:
 | |
|     SETUP_SAVE_REFS_ONLY_FRAME                             // save ref containing registers for GC
 | |
|     // Outgoing argument set up
 | |
|     movq %gs:THREAD_SELF_OFFSET, %rsi                      // pass Thread::Current()
 | |
|     call CALLVAR(cxx_name)                                 // cxx_name(arg0, Thread*)
 | |
|     RESTORE_SAVE_REFS_ONLY_FRAME                           // restore frame up to return address
 | |
|     RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER                // return or deliver exception
 | |
|     END_FUNCTION VAR(c_name)
 | |
| END_MACRO
 | |
| 
 | |
| ART_QUICK_ALLOC_OBJECT_ROSALLOC art_quick_alloc_object_resolved_rosalloc, artAllocObjectFromCodeResolvedRosAlloc
 | |
| ART_QUICK_ALLOC_OBJECT_ROSALLOC art_quick_alloc_object_initialized_rosalloc, artAllocObjectFromCodeInitializedRosAlloc
 | |
| 
 | |
| // The common fast path code for art_quick_alloc_object_resolved_region_tlab.
 | |
| // TODO: delete ALLOC_OBJECT_RESOLVED_TLAB_FAST_PATH since it is the same as
 | |
| // ALLOC_OBJECT_INITIALIZED_TLAB_FAST_PATH.
 | |
| //
 | |
| // RDI: the class, RAX: return value.
 | |
| // RCX, RSI, RDX: scratch, r8: Thread::Current().
 | |
| MACRO1(ALLOC_OBJECT_RESOLVED_TLAB_FAST_PATH, slowPathLabel)
 | |
|     ALLOC_OBJECT_INITIALIZED_TLAB_FAST_PATH(RAW_VAR(slowPathLabel))
 | |
| END_MACRO
 | |
| 
 | |
| // The fast path code for art_quick_alloc_object_initialized_region_tlab.
 | |
| //
 | |
| // RDI: the class, RSI: ArtMethod*, RAX: return value.
 | |
| // RCX, RSI, RDX: scratch, r8: Thread::Current().
 | |
| MACRO1(ALLOC_OBJECT_INITIALIZED_TLAB_FAST_PATH, slowPathLabel)
 | |
|     movq %gs:THREAD_SELF_OFFSET, %r8                           // r8 = thread
 | |
|     movl MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET(%rdi), %ecx // Load the object size.
 | |
|     movq THREAD_LOCAL_POS_OFFSET(%r8), %rax
 | |
|     addq %rax, %rcx                                            // Add size to pos, note that these
 | |
|                                                                // are both 32 bit ints, overflow
 | |
|                                                                // will cause the add to be past the
 | |
|                                                                // end of the thread local region.
 | |
|     cmpq THREAD_LOCAL_END_OFFSET(%r8), %rcx                    // Check if it fits.
 | |
|     ja   RAW_VAR(slowPathLabel)
 | |
|     movq %rcx, THREAD_LOCAL_POS_OFFSET(%r8)                    // Update thread_local_pos.
 | |
|     incq THREAD_LOCAL_OBJECTS_OFFSET(%r8)                      // Increase thread_local_objects.
 | |
|                                                                // Store the class pointer in the
 | |
|                                                                // header.
 | |
|                                                                // No fence needed for x86.
 | |
|     POISON_HEAP_REF edi
 | |
|     movl %edi, MIRROR_OBJECT_CLASS_OFFSET(%rax)
 | |
|     ret                                                        // Fast path succeeded.
 | |
| END_MACRO
 | |
| 
 | |
| // The fast path code for art_quick_alloc_array_region_tlab.
 | |
| // Inputs: RDI: the class, RSI: int32_t component_count, R9: total_size
 | |
| // Free temps: RCX, RDX, R8
 | |
| // Output: RAX: return value.
 | |
| MACRO1(ALLOC_ARRAY_TLAB_FAST_PATH_RESOLVED_WITH_SIZE, slowPathLabel)
 | |
|     movq %gs:THREAD_SELF_OFFSET, %rcx                          // rcx = thread
 | |
|     // Mask out the unaligned part to make sure we are 8 byte aligned.
 | |
|     andq LITERAL(OBJECT_ALIGNMENT_MASK_TOGGLED64), %r9
 | |
|     movq THREAD_LOCAL_POS_OFFSET(%rcx), %rax
 | |
|     addq %rax, %r9
 | |
|     cmpq THREAD_LOCAL_END_OFFSET(%rcx), %r9                    // Check if it fits.
 | |
|     ja   RAW_VAR(slowPathLabel)
 | |
|     movq %r9, THREAD_LOCAL_POS_OFFSET(%rcx)                    // Update thread_local_pos.
 | |
|     addq LITERAL(1), THREAD_LOCAL_OBJECTS_OFFSET(%rcx)         // Increase thread_local_objects.
 | |
|                                                                // Store the class pointer in the
 | |
|                                                                // header.
 | |
|                                                                // No fence needed for x86.
 | |
|     POISON_HEAP_REF edi
 | |
|     movl %edi, MIRROR_OBJECT_CLASS_OFFSET(%rax)
 | |
|     movl %esi, MIRROR_ARRAY_LENGTH_OFFSET(%rax)
 | |
|     ret                                                        // Fast path succeeded.
 | |
| END_MACRO
 | |
| 
 | |
| // The common slow path code for art_quick_alloc_object_{resolved, initialized}_tlab
 | |
| // and art_quick_alloc_object_{resolved, initialized}_region_tlab.
 | |
| MACRO1(ALLOC_OBJECT_TLAB_SLOW_PATH, cxx_name)
 | |
|     SETUP_SAVE_REFS_ONLY_FRAME                             // save ref containing registers for GC
 | |
|     // Outgoing argument set up
 | |
|     movq %gs:THREAD_SELF_OFFSET, %rsi                      // pass Thread::Current()
 | |
|     call CALLVAR(cxx_name)                                 // cxx_name(arg0, Thread*)
 | |
|     RESTORE_SAVE_REFS_ONLY_FRAME                           // restore frame up to return address
 | |
|     RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER                // return or deliver exception
 | |
| END_MACRO
 | |
| 
 | |
| // A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_tlab, TLAB). May be
 | |
| // called with CC if the GC is not active.
 | |
| DEFINE_FUNCTION art_quick_alloc_object_resolved_tlab
 | |
|     // RDI: mirror::Class* klass
 | |
|     // RDX, RSI, RCX, R8, R9: free. RAX: return val.
 | |
|     ALLOC_OBJECT_RESOLVED_TLAB_FAST_PATH .Lart_quick_alloc_object_resolved_tlab_slow_path
 | |
| .Lart_quick_alloc_object_resolved_tlab_slow_path:
 | |
|     ALLOC_OBJECT_TLAB_SLOW_PATH artAllocObjectFromCodeResolvedTLAB
 | |
| END_FUNCTION art_quick_alloc_object_resolved_tlab
 | |
| 
 | |
| // A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_tlab, TLAB).
 | |
| // May be called with CC if the GC is not active.
 | |
| DEFINE_FUNCTION art_quick_alloc_object_initialized_tlab
 | |
|     // RDI: mirror::Class* klass
 | |
|     // RDX, RSI, RCX, R8, R9: free. RAX: return val.
 | |
|     ALLOC_OBJECT_INITIALIZED_TLAB_FAST_PATH .Lart_quick_alloc_object_initialized_tlab_slow_path
 | |
| .Lart_quick_alloc_object_initialized_tlab_slow_path:
 | |
|     ALLOC_OBJECT_TLAB_SLOW_PATH artAllocObjectFromCodeInitializedTLAB
 | |
| END_FUNCTION art_quick_alloc_object_initialized_tlab
 | |
| 
 | |
| MACRO0(COMPUTE_ARRAY_SIZE_UNKNOWN)
 | |
|     movl MIRROR_CLASS_COMPONENT_TYPE_OFFSET(%rdi), %ecx        // Load component type.
 | |
|     UNPOISON_HEAP_REF ecx
 | |
|     movl MIRROR_CLASS_OBJECT_PRIMITIVE_TYPE_OFFSET(%rcx), %ecx // Load primitive type.
 | |
|     shrq MACRO_LITERAL(PRIMITIVE_TYPE_SIZE_SHIFT_SHIFT), %rcx        // Get component size shift.
 | |
|     movq %rsi, %r9
 | |
|     salq %cl, %r9                                              // Calculate array count shifted.
 | |
|     // Add array header + alignment rounding.
 | |
|     addq MACRO_LITERAL(MIRROR_INT_ARRAY_DATA_OFFSET + OBJECT_ALIGNMENT_MASK), %r9
 | |
|     // Add 4 extra bytes if we are doing a long array.
 | |
|     addq MACRO_LITERAL(1), %rcx
 | |
|     andq MACRO_LITERAL(4), %rcx
 | |
| #if MIRROR_LONG_ARRAY_DATA_OFFSET != MIRROR_INT_ARRAY_DATA_OFFSET + 4
 | |
| #error Long array data offset must be 4 greater than int array data offset.
 | |
| #endif
 | |
|     addq %rcx, %r9
 | |
| END_MACRO
 | |
| 
 | |
| MACRO0(COMPUTE_ARRAY_SIZE_8)
 | |
|     // RDI: mirror::Class* klass, RSI: int32_t component_count
 | |
|     // RDX, RCX, R8, R9: free. RAX: return val.
 | |
|     movq %rsi, %r9
 | |
|     // Add array header + alignment rounding.
 | |
|     addq MACRO_LITERAL(MIRROR_INT_ARRAY_DATA_OFFSET + OBJECT_ALIGNMENT_MASK), %r9
 | |
| END_MACRO
 | |
| 
 | |
| MACRO0(COMPUTE_ARRAY_SIZE_16)
 | |
|     // RDI: mirror::Class* klass, RSI: int32_t component_count
 | |
|     // RDX, RCX, R8, R9: free. RAX: return val.
 | |
|     movq %rsi, %r9
 | |
|     salq MACRO_LITERAL(1), %r9
 | |
|     // Add array header + alignment rounding.
 | |
|     addq MACRO_LITERAL(MIRROR_INT_ARRAY_DATA_OFFSET + OBJECT_ALIGNMENT_MASK), %r9
 | |
| END_MACRO
 | |
| 
 | |
| MACRO0(COMPUTE_ARRAY_SIZE_32)
 | |
|     // RDI: mirror::Class* klass, RSI: int32_t component_count
 | |
|     // RDX, RCX, R8, R9: free. RAX: return val.
 | |
|     movq %rsi, %r9
 | |
|     salq MACRO_LITERAL(2), %r9
 | |
|     // Add array header + alignment rounding.
 | |
|     addq MACRO_LITERAL(MIRROR_INT_ARRAY_DATA_OFFSET + OBJECT_ALIGNMENT_MASK), %r9
 | |
| END_MACRO
 | |
| 
 | |
| MACRO0(COMPUTE_ARRAY_SIZE_64)
 | |
|     // RDI: mirror::Class* klass, RSI: int32_t component_count
 | |
|     // RDX, RCX, R8, R9: free. RAX: return val.
 | |
|     movq %rsi, %r9
 | |
|     salq MACRO_LITERAL(3), %r9
 | |
|     // Add array header + alignment rounding.
 | |
|     addq MACRO_LITERAL(MIRROR_WIDE_ARRAY_DATA_OFFSET + OBJECT_ALIGNMENT_MASK), %r9
 | |
| END_MACRO
 | |
| 
 | |
| MACRO3(GENERATE_ALLOC_ARRAY_TLAB, c_entrypoint, cxx_name, size_setup)
 | |
|     DEFINE_FUNCTION VAR(c_entrypoint)
 | |
|     // RDI: mirror::Class* klass, RSI: int32_t component_count
 | |
|     // RDX, RCX, R8, R9: free. RAX: return val.
 | |
|     CALL_MACRO(size_setup)
 | |
|     ALLOC_ARRAY_TLAB_FAST_PATH_RESOLVED_WITH_SIZE .Lslow_path\c_entrypoint
 | |
| .Lslow_path\c_entrypoint:
 | |
|     SETUP_SAVE_REFS_ONLY_FRAME                                 // save ref containing registers for GC
 | |
|     // Outgoing argument set up
 | |
|     movq %gs:THREAD_SELF_OFFSET, %rdx                          // pass Thread::Current()
 | |
|     call CALLVAR(cxx_name)                                     // cxx_name(arg0, arg1, Thread*)
 | |
|     RESTORE_SAVE_REFS_ONLY_FRAME                               // restore frame up to return address
 | |
|     RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER                    // return or deliver exception
 | |
|     END_FUNCTION VAR(c_entrypoint)
 | |
| END_MACRO
 | |
| 
 | |
| 
 | |
| GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved_region_tlab, artAllocArrayFromCodeResolvedRegionTLAB, COMPUTE_ARRAY_SIZE_UNKNOWN
 | |
| GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved8_region_tlab, artAllocArrayFromCodeResolvedRegionTLAB, COMPUTE_ARRAY_SIZE_8
 | |
| GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved16_region_tlab, artAllocArrayFromCodeResolvedRegionTLAB, COMPUTE_ARRAY_SIZE_16
 | |
| GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved32_region_tlab, artAllocArrayFromCodeResolvedRegionTLAB, COMPUTE_ARRAY_SIZE_32
 | |
| GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved64_region_tlab, artAllocArrayFromCodeResolvedRegionTLAB, COMPUTE_ARRAY_SIZE_64
 | |
| 
 | |
| GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved_tlab, artAllocArrayFromCodeResolvedTLAB, COMPUTE_ARRAY_SIZE_UNKNOWN
 | |
| GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved8_tlab, artAllocArrayFromCodeResolvedTLAB, COMPUTE_ARRAY_SIZE_8
 | |
| GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved16_tlab, artAllocArrayFromCodeResolvedTLAB, COMPUTE_ARRAY_SIZE_16
 | |
| GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved32_tlab, artAllocArrayFromCodeResolvedTLAB, COMPUTE_ARRAY_SIZE_32
 | |
| GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved64_tlab, artAllocArrayFromCodeResolvedTLAB, COMPUTE_ARRAY_SIZE_64
 | |
| 
 | |
| // A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB).
 | |
| DEFINE_FUNCTION art_quick_alloc_object_resolved_region_tlab
 | |
|     // Fast path region tlab allocation.
 | |
|     // RDI: mirror::Class* klass
 | |
|     // RDX, RSI, RCX, R8, R9: free. RAX: return val.
 | |
|     ASSERT_USE_READ_BARRIER
 | |
|     ALLOC_OBJECT_RESOLVED_TLAB_FAST_PATH .Lart_quick_alloc_object_resolved_region_tlab_slow_path
 | |
| .Lart_quick_alloc_object_resolved_region_tlab_slow_path:
 | |
|     ALLOC_OBJECT_TLAB_SLOW_PATH artAllocObjectFromCodeResolvedRegionTLAB
 | |
| END_FUNCTION art_quick_alloc_object_resolved_region_tlab
 | |
| 
 | |
| // A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region_tlab, RegionTLAB).
 | |
| DEFINE_FUNCTION art_quick_alloc_object_initialized_region_tlab
 | |
|     // Fast path region tlab allocation.
 | |
|     // RDI: mirror::Class* klass
 | |
|     // RDX, RSI, RCX, R8, R9: free. RAX: return val.
 | |
|     ASSERT_USE_READ_BARRIER
 | |
|     // No read barrier since the caller is responsible for that.
 | |
|     ALLOC_OBJECT_INITIALIZED_TLAB_FAST_PATH .Lart_quick_alloc_object_initialized_region_tlab_slow_path
 | |
| .Lart_quick_alloc_object_initialized_region_tlab_slow_path:
 | |
|     ALLOC_OBJECT_TLAB_SLOW_PATH artAllocObjectFromCodeInitializedRegionTLAB
 | |
| END_FUNCTION art_quick_alloc_object_initialized_region_tlab
 | |
| 
 | |
| ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_static_storage, artInitializeStaticStorageFromCode
 | |
| ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_resolve_type, artResolveTypeFromCode
 | |
| ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_type_and_verify_access, artResolveTypeAndVerifyAccessFromCode
 | |
| ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_handle, artResolveMethodHandleFromCode
 | |
| ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_type, artResolveMethodTypeFromCode
 | |
| ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_string, artResolveStringFromCode
 | |
| 
 | |
| TWO_ARG_REF_DOWNCALL art_quick_handle_fill_data, artHandleFillArrayDataFromCode, RETURN_IF_EAX_ZERO
 | |
| 
 | |
|     /*
 | |
|      * Entry from managed code that tries to lock the object in a fast path and
 | |
|      * calls `artLockObjectFromCode()` for the difficult cases, may block for GC.
 | |
|      * RDI holds the possibly null object to lock.
 | |
|      */
 | |
| DEFINE_FUNCTION art_quick_lock_object
 | |
|     testq %rdi, %rdi                      // Null check object.
 | |
|     jz   art_quick_lock_object_no_inline
 | |
|     LOCK_OBJECT_FAST_PATH rdi, ecx, art_quick_lock_object_no_inline
 | |
| END_FUNCTION art_quick_lock_object
 | |
| 
 | |
|     /*
 | |
|      * Entry from managed code that calls `artLockObjectFromCode()`, may block for GC.
 | |
|      * RDI holds the possibly null object to lock.
 | |
|      */
 | |
| DEFINE_FUNCTION art_quick_lock_object_no_inline
 | |
|     // This is also the slow path for art_quick_lock_object.
 | |
|     SETUP_SAVE_REFS_ONLY_FRAME
 | |
|     movq %gs:THREAD_SELF_OFFSET, %rsi     // pass Thread::Current()
 | |
|     call SYMBOL(artLockObjectFromCode)    // artLockObjectFromCode(object, Thread*)
 | |
|     RESTORE_SAVE_REFS_ONLY_FRAME          // restore frame up to return address
 | |
|     RETURN_IF_EAX_ZERO
 | |
| END_FUNCTION art_quick_lock_object_no_inline
 | |
| 
 | |
|     /*
 | |
|      * Entry from managed code that tries to unlock the object in a fast path and calls
 | |
|      * `artUnlockObjectFromCode()` for the difficult cases and delivers exception on failure.
 | |
|      * RDI holds the possibly null object to unlock.
 | |
|      */
 | |
| DEFINE_FUNCTION art_quick_unlock_object
 | |
|     testq %rdi, %rdi                      // Null check object.
 | |
|     jz   art_quick_lock_object_no_inline
 | |
|     UNLOCK_OBJECT_FAST_PATH rdi, ecx, /*saved_rax*/ none, art_quick_unlock_object_no_inline
 | |
| END_FUNCTION art_quick_unlock_object
 | |
| 
 | |
|     /*
 | |
|      * Entry from managed code that calls `artUnlockObjectFromCode()`
 | |
|      * and delivers exception on failure.
 | |
|      * RDI holds the possibly null object to unlock.
 | |
|      */
 | |
| DEFINE_FUNCTION art_quick_unlock_object_no_inline
 | |
|     // This is also the slow path for art_quick_unlock_object.
 | |
|     SETUP_SAVE_REFS_ONLY_FRAME
 | |
|     movq %gs:THREAD_SELF_OFFSET, %rsi     // pass Thread::Current()
 | |
|     call SYMBOL(artUnlockObjectFromCode)  // artUnlockObjectFromCode(object, Thread*)
 | |
|     RESTORE_SAVE_REFS_ONLY_FRAME          // restore frame up to return address
 | |
|     RETURN_IF_EAX_ZERO
 | |
| END_FUNCTION art_quick_unlock_object_no_inline
 | |
| 
 | |
| DEFINE_FUNCTION art_quick_check_instance_of
 | |
|     // Type check using the bit string passes null as the target class. In that case just throw.
 | |
|     testl %esi, %esi
 | |
|     jz .Lthrow_class_cast_exception_for_bitstring_check
 | |
| 
 | |
|     // We could check the super classes here but that is usually already checked in the caller.
 | |
|     PUSH rdi                          // Save args for exc
 | |
|     PUSH rsi
 | |
|     subq LITERAL(8), %rsp             // Alignment padding.
 | |
|     CFI_ADJUST_CFA_OFFSET(8)
 | |
|     SETUP_FP_CALLEE_SAVE_FRAME
 | |
|     call SYMBOL(artInstanceOfFromCode)  // (Object* obj, Class* ref_klass)
 | |
|     testq %rax, %rax
 | |
|     jz .Lthrow_class_cast_exception   // jump forward if not assignable
 | |
|     CFI_REMEMBER_STATE
 | |
|     RESTORE_FP_CALLEE_SAVE_FRAME
 | |
|     addq LITERAL(24), %rsp            // pop arguments
 | |
|     CFI_ADJUST_CFA_OFFSET(-24)
 | |
|     ret
 | |
|     CFI_RESTORE_STATE_AND_DEF_CFA rsp, 64  // Reset unwind info so following code unwinds.
 | |
| 
 | |
| .Lthrow_class_cast_exception:
 | |
|     RESTORE_FP_CALLEE_SAVE_FRAME
 | |
|     addq LITERAL(8), %rsp             // pop padding
 | |
|     CFI_ADJUST_CFA_OFFSET(-8)
 | |
|     POP rsi                           // Pop arguments
 | |
|     POP rdi
 | |
| 
 | |
| .Lthrow_class_cast_exception_for_bitstring_check:
 | |
|     SETUP_SAVE_ALL_CALLEE_SAVES_FRAME // save all registers as basis for long jump context
 | |
|     mov %gs:THREAD_SELF_OFFSET, %rdx  // pass Thread::Current()
 | |
|     call SYMBOL(artThrowClassCastExceptionForObject)  // (Object* src, Class* dest, Thread*)
 | |
|     UNREACHABLE
 | |
| END_FUNCTION art_quick_check_instance_of
 | |
| 
 | |
| 
 | |
| // Restore reg's value if reg is not the same as exclude_reg, otherwise just adjust stack.
 | |
| MACRO2(POP_REG_NE, reg, exclude_reg)
 | |
|     .ifc RAW_VAR(reg), RAW_VAR(exclude_reg)
 | |
|       addq MACRO_LITERAL(8), %rsp
 | |
|       CFI_ADJUST_CFA_OFFSET(-8)
 | |
|     .else
 | |
|       POP RAW_VAR(reg)
 | |
|     .endif
 | |
| END_MACRO
 | |
| 
 | |
|     /*
 | |
|      * Macro to insert read barrier, used in art_quick_aput_obj.
 | |
|      * obj_reg and dest_reg{32|64} are registers, offset is a defined literal such as
 | |
|      * MIRROR_OBJECT_CLASS_OFFSET. dest_reg needs two versions to handle the mismatch between
 | |
|      * 64b PUSH/POP and 32b argument.
 | |
|      * TODO: When read barrier has a fast path, add heap unpoisoning support for the fast path.
 | |
|      *
 | |
|      * As with art_quick_aput_obj function, the 64b versions are in comments.
 | |
|      */
 | |
| MACRO4(READ_BARRIER, obj_reg, offset, dest_reg32, dest_reg64)
 | |
| #ifdef USE_READ_BARRIER
 | |
|     PUSH rax                            // save registers that might be used
 | |
|     PUSH rdi
 | |
|     PUSH rsi
 | |
|     PUSH rdx
 | |
|     PUSH rcx
 | |
|     SETUP_FP_CALLEE_SAVE_FRAME
 | |
|     // Outgoing argument set up
 | |
|     // movl REG_VAR(ref_reg32), %edi    // pass ref, no-op for now since parameter ref is unused
 | |
|     // // movq REG_VAR(ref_reg64), %rdi
 | |
|     movl REG_VAR(obj_reg), %esi         // pass obj_reg
 | |
|     // movq REG_VAR(obj_reg), %rsi
 | |
|     movl MACRO_LITERAL((RAW_VAR(offset))), %edx // pass offset, double parentheses are necessary
 | |
|     // movq MACRO_LITERAL((RAW_VAR(offset))), %rdx
 | |
|     call SYMBOL(artReadBarrierSlow)     // artReadBarrierSlow(ref, obj_reg, offset)
 | |
|     // No need to unpoison return value in rax, artReadBarrierSlow() would do the unpoisoning.
 | |
|     .ifnc RAW_VAR(dest_reg32), eax
 | |
|     // .ifnc RAW_VAR(dest_reg64), rax
 | |
|       movl %eax, REG_VAR(dest_reg32)    // save loaded ref in dest_reg
 | |
|       // movq %rax, REG_VAR(dest_reg64)
 | |
|     .endif
 | |
|     RESTORE_FP_CALLEE_SAVE_FRAME
 | |
|     POP_REG_NE rcx, RAW_VAR(dest_reg64) // Restore registers except dest_reg
 | |
|     POP_REG_NE rdx, RAW_VAR(dest_reg64)
 | |
|     POP_REG_NE rsi, RAW_VAR(dest_reg64)
 | |
|     POP_REG_NE rdi, RAW_VAR(dest_reg64)
 | |
|     POP_REG_NE rax, RAW_VAR(dest_reg64)
 | |
| #else
 | |
|     movl RAW_VAR(offset)(REG_VAR(obj_reg)), REG_VAR(dest_reg32)
 | |
|     // movq RAW_VAR(offset)(REG_VAR(obj_reg)), REG_VAR(dest_reg64)
 | |
|     UNPOISON_HEAP_REF RAW_VAR(dest_reg32) // UNPOISON_HEAP_REF only takes a 32b register
 | |
| #endif  // USE_READ_BARRIER
 | |
| END_MACRO
 | |
| 
 | |
| DEFINE_FUNCTION art_quick_aput_obj
 | |
|     testl %edx, %edx                // store of null
 | |
| //  test %rdx, %rdx
 | |
|     jz .Ldo_aput_null
 | |
|     READ_BARRIER edi, MIRROR_OBJECT_CLASS_OFFSET, ecx, rcx
 | |
|     // READ_BARRIER rdi, MIRROR_OBJECT_CLASS_OFFSET, ecx, rcx
 | |
|     READ_BARRIER ecx, MIRROR_CLASS_COMPONENT_TYPE_OFFSET, ecx, rcx
 | |
|     // READ_BARRIER rcx, MIRROR_CLASS_COMPONENT_TYPE_OFFSET, ecx, rcx
 | |
| #if defined(USE_HEAP_POISONING) || defined(USE_READ_BARRIER)
 | |
|     READ_BARRIER edx, MIRROR_OBJECT_CLASS_OFFSET, eax, rax  // rax is free.
 | |
|     // READ_BARRIER rdx, MIRROR_OBJECT_CLASS_OFFSET, eax, rax
 | |
|     cmpl %eax, %ecx  // value's type == array's component type - trivial assignability
 | |
| #else
 | |
|     cmpl MIRROR_OBJECT_CLASS_OFFSET(%edx), %ecx // value's type == array's component type - trivial assignability
 | |
| //  cmpq MIRROR_CLASS_OFFSET(%rdx), %rcx
 | |
| #endif
 | |
|     jne .Lcheck_assignability
 | |
| .Ldo_aput:
 | |
|     POISON_HEAP_REF edx
 | |
|     movl %edx, MIRROR_OBJECT_ARRAY_DATA_OFFSET(%edi, %esi, 4)
 | |
| //  movq %rdx, MIRROR_OBJECT_ARRAY_DATA_OFFSET(%rdi, %rsi, 4)
 | |
|     movq %gs:THREAD_CARD_TABLE_OFFSET, %rdx
 | |
|     shrl LITERAL(CARD_TABLE_CARD_SHIFT), %edi
 | |
| //  shrl LITERAL(CARD_TABLE_CARD_SHIFT), %rdi
 | |
|     movb %dl, (%rdx, %rdi)                       // Note: this assumes that top 32b of %rdi are zero
 | |
|     ret
 | |
| .Ldo_aput_null:
 | |
|     movl %edx, MIRROR_OBJECT_ARRAY_DATA_OFFSET(%edi, %esi, 4)
 | |
| //  movq %rdx, MIRROR_OBJECT_ARRAY_DATA_OFFSET(%rdi, %rsi, 4)
 | |
|     ret
 | |
| .Lcheck_assignability:
 | |
|     // Save arguments.
 | |
|     PUSH rdi
 | |
|     PUSH rsi
 | |
|     PUSH rdx
 | |
|     SETUP_FP_CALLEE_SAVE_FRAME
 | |
| 
 | |
| #if defined(USE_HEAP_POISONING) || defined(USE_READ_BARRIER)
 | |
|     // The load of MIRROR_OBJECT_CLASS_OFFSET(%edx) is redundant, eax still holds the value.
 | |
|     movl %eax, %esi               // Pass arg2 = value's class.
 | |
|     // movq %rax, %rsi
 | |
| #else
 | |
|                                      // "Uncompress" = do nothing, as already zero-extended on load.
 | |
|     movl MIRROR_OBJECT_CLASS_OFFSET(%edx), %esi  // Pass arg2 = value's class.
 | |
| #endif
 | |
|     movq %rcx, %rdi               // Pass arg1 = array's component type.
 | |
| 
 | |
|     call SYMBOL(artIsAssignableFromCode)  // (Class* a, Class* b)
 | |
| 
 | |
|     // Exception?
 | |
|     testq %rax, %rax
 | |
|     jz   .Lthrow_array_store_exception
 | |
| 
 | |
|     RESTORE_FP_CALLEE_SAVE_FRAME
 | |
|     // Restore arguments.
 | |
|     POP  rdx
 | |
|     POP  rsi
 | |
|     POP  rdi
 | |
| 
 | |
|     POISON_HEAP_REF edx
 | |
|     movl %edx, MIRROR_OBJECT_ARRAY_DATA_OFFSET(%edi, %esi, 4)
 | |
| //  movq %rdx, MIRROR_OBJECT_ARRAY_DATA_OFFSET(%rdi, %rsi, 4)
 | |
|     movq %gs:THREAD_CARD_TABLE_OFFSET, %rdx
 | |
|     shrl LITERAL(CARD_TABLE_CARD_SHIFT), %edi
 | |
| //  shrl LITERAL(CARD_TABLE_CARD_SHIFT), %rdi
 | |
|     movb %dl, (%rdx, %rdi)                       // Note: this assumes that top 32b of %rdi are zero
 | |
| //  movb %dl, (%rdx, %rdi)
 | |
|     ret
 | |
|     CFI_ADJUST_CFA_OFFSET(24 + 4 * 8)  // Reset unwind info so following code unwinds.
 | |
| .Lthrow_array_store_exception:
 | |
|     RESTORE_FP_CALLEE_SAVE_FRAME
 | |
|     // Restore arguments.
 | |
|     POP  rdx
 | |
|     POP  rsi
 | |
|     POP  rdi
 | |
| 
 | |
|     SETUP_SAVE_ALL_CALLEE_SAVES_FRAME  // Save all registers as basis for long jump context.
 | |
| 
 | |
|     // Outgoing argument set up.
 | |
|     movq %rdx, %rsi                         // Pass arg 2 = value.
 | |
|     movq %gs:THREAD_SELF_OFFSET, %rdx       // Pass arg 3 = Thread::Current().
 | |
|                                             // Pass arg 1 = array.
 | |
|     call SYMBOL(artThrowArrayStoreException) // (array, value, Thread*)
 | |
|     UNREACHABLE
 | |
| END_FUNCTION art_quick_aput_obj
 | |
| 
 | |
| // TODO: This is quite silly on X86_64 now.
 | |
| DEFINE_FUNCTION art_quick_memcpy
 | |
|     call PLT_SYMBOL(memcpy)       // (void*, const void*, size_t)
 | |
|     ret
 | |
| END_FUNCTION art_quick_memcpy
 | |
| 
 | |
| DEFINE_FUNCTION art_quick_test_suspend
 | |
|     SETUP_SAVE_EVERYTHING_FRAME RUNTIME_SAVE_EVERYTHING_FOR_SUSPEND_CHECK_METHOD_OFFSET  // save everything for GC
 | |
|     // Outgoing argument set up
 | |
|     movq %gs:THREAD_SELF_OFFSET, %rdi           // pass Thread::Current()
 | |
|     call SYMBOL(artTestSuspendFromCode)         // (Thread*)
 | |
|     RESTORE_SAVE_EVERYTHING_FRAME               // restore frame up to return address
 | |
|     ret
 | |
| END_FUNCTION art_quick_test_suspend
 | |
| 
 | |
| UNIMPLEMENTED art_quick_ldiv
 | |
| UNIMPLEMENTED art_quick_lmod
 | |
| UNIMPLEMENTED art_quick_lmul
 | |
| UNIMPLEMENTED art_quick_lshl
 | |
| UNIMPLEMENTED art_quick_lshr
 | |
| UNIMPLEMENTED art_quick_lushr
 | |
| 
 | |
| // Note: Functions `art{Get,Set}<Kind>{Static,Instance}FromCompiledCode` are
 | |
| // defined with a macro in runtime/entrypoints/quick/quick_field_entrypoints.cc.
 | |
| 
 | |
| THREE_ARG_REF_DOWNCALL art_quick_set8_instance, artSet8InstanceFromCompiledCode, RETURN_IF_EAX_ZERO
 | |
| THREE_ARG_REF_DOWNCALL art_quick_set16_instance, artSet16InstanceFromCompiledCode, RETURN_IF_EAX_ZERO
 | |
| THREE_ARG_REF_DOWNCALL art_quick_set32_instance, artSet32InstanceFromCompiledCode, RETURN_IF_EAX_ZERO
 | |
| THREE_ARG_REF_DOWNCALL art_quick_set64_instance, artSet64InstanceFromCompiledCode, RETURN_IF_EAX_ZERO
 | |
| THREE_ARG_REF_DOWNCALL art_quick_set_obj_instance, artSetObjInstanceFromCompiledCode, RETURN_IF_EAX_ZERO
 | |
| 
 | |
| TWO_ARG_REF_DOWNCALL art_quick_get_byte_instance, artGetByteInstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
 | |
| TWO_ARG_REF_DOWNCALL art_quick_get_boolean_instance, artGetBooleanInstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
 | |
| TWO_ARG_REF_DOWNCALL art_quick_get_short_instance, artGetShortInstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
 | |
| TWO_ARG_REF_DOWNCALL art_quick_get_char_instance, artGetCharInstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
 | |
| TWO_ARG_REF_DOWNCALL art_quick_get32_instance, artGet32InstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
 | |
| TWO_ARG_REF_DOWNCALL art_quick_get64_instance, artGet64InstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
 | |
| TWO_ARG_REF_DOWNCALL art_quick_get_obj_instance, artGetObjInstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
 | |
| 
 | |
| TWO_ARG_REF_DOWNCALL art_quick_set8_static, artSet8StaticFromCompiledCode, RETURN_IF_EAX_ZERO
 | |
| TWO_ARG_REF_DOWNCALL art_quick_set16_static, artSet16StaticFromCompiledCode, RETURN_IF_EAX_ZERO
 | |
| TWO_ARG_REF_DOWNCALL art_quick_set32_static, artSet32StaticFromCompiledCode, RETURN_IF_EAX_ZERO
 | |
| TWO_ARG_REF_DOWNCALL art_quick_set64_static, artSet64StaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
 | |
| TWO_ARG_REF_DOWNCALL art_quick_set_obj_static, artSetObjStaticFromCompiledCode, RETURN_IF_EAX_ZERO
 | |
| 
 | |
| ONE_ARG_REF_DOWNCALL art_quick_get_byte_static, artGetByteStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
 | |
| ONE_ARG_REF_DOWNCALL art_quick_get_boolean_static, artGetBooleanStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
 | |
| ONE_ARG_REF_DOWNCALL art_quick_get_short_static, artGetShortStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
 | |
| ONE_ARG_REF_DOWNCALL art_quick_get_char_static, artGetCharStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
 | |
| ONE_ARG_REF_DOWNCALL art_quick_get32_static, artGet32StaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
 | |
| ONE_ARG_REF_DOWNCALL art_quick_get64_static, artGet64StaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
 | |
| ONE_ARG_REF_DOWNCALL art_quick_get_obj_static, artGetObjStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
 | |
| 
 | |
| DEFINE_FUNCTION art_quick_proxy_invoke_handler
 | |
|     SETUP_SAVE_REFS_AND_ARGS_FRAME_WITH_METHOD_IN_RDI
 | |
| 
 | |
|     movq %gs:THREAD_SELF_OFFSET, %rdx       // Pass Thread::Current().
 | |
|     movq %rsp, %rcx                         // Pass SP.
 | |
|     call SYMBOL(artQuickProxyInvokeHandler) // (proxy method, receiver, Thread*, SP)
 | |
|     RESTORE_SAVE_REFS_AND_ARGS_FRAME
 | |
|     movq %rax, %xmm0                        // Copy return value in case of float returns.
 | |
|     RETURN_OR_DELIVER_PENDING_EXCEPTION
 | |
| END_FUNCTION art_quick_proxy_invoke_handler
 | |
| 
 | |
|     /*
 | |
|      * Called to resolve an imt conflict.
 | |
|      * rdi is the conflict ArtMethod.
 | |
|      * rax is a hidden argument that holds the target interface method.
 | |
|      *
 | |
|      * Note that this stub writes to rdi.
 | |
|      */
 | |
| DEFINE_FUNCTION art_quick_imt_conflict_trampoline
 | |
| #if defined(__APPLE__)
 | |
|     int3
 | |
|     int3
 | |
| #else
 | |
|     movq ART_METHOD_JNI_OFFSET_64(%rdi), %rdi  // Load ImtConflictTable
 | |
| .Limt_table_iterate:
 | |
|     cmpq %rax, 0(%rdi)
 | |
|     jne .Limt_table_next_entry
 | |
|     // We successfully hit an entry in the table. Load the target method
 | |
|     // and jump to it.
 | |
|     movq __SIZEOF_POINTER__(%rdi), %rdi
 | |
|     jmp *ART_METHOD_QUICK_CODE_OFFSET_64(%rdi)
 | |
| .Limt_table_next_entry:
 | |
|     // If the entry is null, the interface method is not in the ImtConflictTable.
 | |
|     cmpq LITERAL(0), 0(%rdi)
 | |
|     jz .Lconflict_trampoline
 | |
|     // Iterate over the entries of the ImtConflictTable.
 | |
|     addq LITERAL(2 * __SIZEOF_POINTER__), %rdi
 | |
|     jmp .Limt_table_iterate
 | |
| .Lconflict_trampoline:
 | |
|     // Call the runtime stub to populate the ImtConflictTable and jump to the
 | |
|     // resolved method.
 | |
|     movq %rax, %rdi  // Load interface method
 | |
|     INVOKE_TRAMPOLINE_BODY artInvokeInterfaceTrampoline
 | |
| #endif  // __APPLE__
 | |
| END_FUNCTION art_quick_imt_conflict_trampoline
 | |
| 
 | |
| DEFINE_FUNCTION art_quick_resolution_trampoline
 | |
|     SETUP_SAVE_REFS_AND_ARGS_FRAME
 | |
|     movq %gs:THREAD_SELF_OFFSET, %rdx
 | |
|     movq %rsp, %rcx
 | |
|     call SYMBOL(artQuickResolutionTrampoline) // (called, receiver, Thread*, SP)
 | |
|     movq %rax, %r10               // Remember returned code pointer in R10.
 | |
|     movq (%rsp), %rdi             // Load called method into RDI.
 | |
|     RESTORE_SAVE_REFS_AND_ARGS_FRAME
 | |
|     testq %r10, %r10              // If code pointer is null goto deliver pending exception.
 | |
|     jz 1f
 | |
|     jmp *%r10                     // Tail call into method.
 | |
| 1:
 | |
|     DELIVER_PENDING_EXCEPTION
 | |
| END_FUNCTION art_quick_resolution_trampoline
 | |
| 
 | |
| /* Generic JNI frame layout:
 | |
|  *
 | |
|  * #-------------------#
 | |
|  * |                   |
 | |
|  * | caller method...  |
 | |
|  * #-------------------#    <--- SP on entry
 | |
|  *
 | |
|  *          |
 | |
|  *          V
 | |
|  *
 | |
|  * #-------------------#
 | |
|  * | caller method...  |
 | |
|  * #-------------------#
 | |
|  * | Return            |
 | |
|  * | R15               |    callee save
 | |
|  * | R14               |    callee save
 | |
|  * | R13               |    callee save
 | |
|  * | R12               |    callee save
 | |
|  * | R9                |    arg5
 | |
|  * | R8                |    arg4
 | |
|  * | RSI/R6            |    arg1
 | |
|  * | RBP/R5            |    callee save
 | |
|  * | RBX/R3            |    callee save
 | |
|  * | RDX/R2            |    arg2
 | |
|  * | RCX/R1            |    arg3
 | |
|  * | XMM7              |    float arg 8
 | |
|  * | XMM6              |    float arg 7
 | |
|  * | XMM5              |    float arg 6
 | |
|  * | XMM4              |    float arg 5
 | |
|  * | XMM3              |    float arg 4
 | |
|  * | XMM2              |    float arg 3
 | |
|  * | XMM1              |    float arg 2
 | |
|  * | XMM0              |    float arg 1
 | |
|  * | RDI/Method*       |  <- sp
 | |
|  * #-------------------#
 | |
|  * | Scratch Alloca    |    5K scratch space
 | |
|  * #---------#---------#
 | |
|  * |         | sp*     |
 | |
|  * | Tramp.  #---------#
 | |
|  * | args    | thread  |
 | |
|  * | Tramp.  #---------#
 | |
|  * |         | method  |
 | |
|  * #-------------------#    <--- SP on artQuickGenericJniTrampoline
 | |
|  *
 | |
|  *           |
 | |
|  *           v              artQuickGenericJniTrampoline
 | |
|  *
 | |
|  * #-------------------#
 | |
|  * | caller method...  |
 | |
|  * #-------------------#
 | |
|  * | Return PC         |
 | |
|  * | Callee-Saves      |
 | |
|  * | padding           | // 8B
 | |
|  * | Method*           |    <--- (1)
 | |
|  * #-------------------#
 | |
|  * | local ref cookie  | // 4B
 | |
|  * | padding           | // 0B or 4B to align handle scope on 8B address
 | |
|  * | handle scope      | // Size depends on number of references; multiple of 4B.
 | |
|  * #-------------------#
 | |
|  * | JNI Stack Args    | // Empty if all args fit into registers.
 | |
|  * #-------------------#    <--- SP on native call (1)
 | |
|  * | Free scratch      |
 | |
|  * #-------------------#
 | |
|  * | SP for JNI call   | // Pointer to (1).
 | |
|  * #-------------------#
 | |
|  * | Hidden arg        | // For @CriticalNative
 | |
|  * #-------------------#
 | |
|  * |                   |
 | |
|  * | Stack for Regs    |    The trampoline assembly will pop these values
 | |
|  * |                   |    into registers for native call
 | |
|  * #-------------------#
 | |
|  */
 | |
|     /*
 | |
|      * Called to do a generic JNI down-call
 | |
|      */
 | |
| DEFINE_FUNCTION art_quick_generic_jni_trampoline
 | |
|     SETUP_SAVE_REFS_AND_ARGS_FRAME_WITH_METHOD_IN_RDI
 | |
| 
 | |
|     movq %rsp, %rbp                 // save SP at (old) callee-save frame
 | |
|     CFI_DEF_CFA_REGISTER(rbp)
 | |
| 
 | |
|     //
 | |
|     // reserve a lot of space
 | |
|     //
 | |
|     //      4    local state ref
 | |
|     //      4    padding
 | |
|     //   4196    4k scratch space, enough for 2x 256 8-byte parameters (TODO: handle scope overhead?)
 | |
|     //     16    handle scope member fields ?
 | |
|     // +  112    14x 8-byte stack-2-register space
 | |
|     // ------
 | |
|     //   4332
 | |
|     // 16-byte aligned: 4336
 | |
|     // Note: 14x8 = 7*16, so the stack stays aligned for the native call...
 | |
|     //       Also means: the padding is somewhere in the middle
 | |
|     //
 | |
|     //
 | |
|     // New test: use 5K and release
 | |
|     // 5k = 5120
 | |
|     subq LITERAL(5120), %rsp
 | |
|     // prepare for artQuickGenericJniTrampoline call
 | |
|     // (Thread*, managed_sp, reserved_area)
 | |
|     //    rdi       rsi           rdx   <= C calling convention
 | |
|     //  gs:...      rbp           rsp   <= where they are
 | |
|     movq %gs:THREAD_SELF_OFFSET, %rdi  // Pass Thread::Current().
 | |
|     movq %rbp, %rsi                    // Pass managed frame SP.
 | |
|     movq %rsp, %rdx                    // Pass reserved area.
 | |
|     call SYMBOL(artQuickGenericJniTrampoline)  // (Thread*, sp)
 | |
| 
 | |
|     // The C call will have registered the complete save-frame on success.
 | |
|     // The result of the call is:
 | |
|     //     %rax: pointer to native code, 0 on error.
 | |
|     //     The bottom of the reserved area contains values for arg registers,
 | |
|     //     hidden arg register and SP for out args for the call.
 | |
| 
 | |
|     // Check for error (class init check or locking for synchronized native method can throw).
 | |
|     test %rax, %rax
 | |
|     jz .Lexception_in_native
 | |
| 
 | |
|     // pop from the register-passing alloca region
 | |
|     // what's the right layout?
 | |
|     popq %rdi
 | |
|     popq %rsi
 | |
|     popq %rdx
 | |
|     popq %rcx
 | |
|     popq %r8
 | |
|     popq %r9
 | |
|     // TODO: skip floating point if unused, some flag.
 | |
|     movq 0(%rsp), %xmm0
 | |
|     movq 8(%rsp), %xmm1
 | |
|     movq 16(%rsp), %xmm2
 | |
|     movq 24(%rsp), %xmm3
 | |
|     movq 32(%rsp), %xmm4
 | |
|     movq 40(%rsp), %xmm5
 | |
|     movq 48(%rsp), %xmm6
 | |
|     movq 56(%rsp), %xmm7
 | |
| 
 | |
|     // Save call target in scratch register.
 | |
|     movq %rax, %r11
 | |
| 
 | |
|     // Load hidden arg (rax) for @CriticalNative.
 | |
|     movq 64(%rsp), %rax
 | |
|     // Load SP for out args, releasing unneeded reserved area.
 | |
|     movq 72(%rsp), %rsp
 | |
| 
 | |
|     // native call
 | |
|     call *%r11
 | |
| 
 | |
|     // result sign extension is handled in C code
 | |
|     // prepare for artQuickGenericJniEndTrampoline call
 | |
|     // (Thread*,  result, result_f)
 | |
|     //   rdi      rsi   rdx       <= C calling convention
 | |
|     //  gs:...    rax   xmm0      <= where they are
 | |
|     movq %gs:THREAD_SELF_OFFSET, %rdi
 | |
|     movq %rax, %rsi
 | |
|     movq %xmm0, %rdx
 | |
|     call SYMBOL(artQuickGenericJniEndTrampoline)
 | |
| 
 | |
|     // Pending exceptions possible.
 | |
|     // TODO: use cmpq, needs direct encoding because of gas bug
 | |
|     movq %gs:THREAD_EXCEPTION_OFFSET, %rcx
 | |
|     test %rcx, %rcx
 | |
|     jnz .Lexception_in_native
 | |
| 
 | |
|     // Tear down the alloca.
 | |
|     movq %rbp, %rsp
 | |
|     CFI_REMEMBER_STATE
 | |
|     CFI_DEF_CFA_REGISTER(rsp)
 | |
| 
 | |
|     // Tear down the callee-save frame.
 | |
|     // Load FPRs.
 | |
|     // movq %xmm0, 16(%rsp)         // doesn't make sense!!!
 | |
|     movq 24(%rsp), %xmm1            // neither does this!!!
 | |
|     movq 32(%rsp), %xmm2
 | |
|     movq 40(%rsp), %xmm3
 | |
|     movq 48(%rsp), %xmm4
 | |
|     movq 56(%rsp), %xmm5
 | |
|     movq 64(%rsp), %xmm6
 | |
|     movq 72(%rsp), %xmm7
 | |
|     movq 80(%rsp), %xmm12
 | |
|     movq 88(%rsp), %xmm13
 | |
|     movq 96(%rsp), %xmm14
 | |
|     movq 104(%rsp), %xmm15
 | |
|     // was 80 bytes
 | |
|     addq LITERAL(80 + 4*8), %rsp
 | |
|     CFI_ADJUST_CFA_OFFSET(-80 - 4*8)
 | |
|     // Save callee and GPR args, mixed together to agree with core spills bitmap.
 | |
|     POP rcx  // Arg.
 | |
|     POP rdx  // Arg.
 | |
|     POP rbx  // Callee save.
 | |
|     POP rbp  // Callee save.
 | |
|     POP rsi  // Arg.
 | |
|     POP r8   // Arg.
 | |
|     POP r9   // Arg.
 | |
|     POP r12  // Callee save.
 | |
|     POP r13  // Callee save.
 | |
|     POP r14  // Callee save.
 | |
|     POP r15  // Callee save.
 | |
|     // store into fpr, for when it's a fpr return...
 | |
|     movq %rax, %xmm0
 | |
|     ret
 | |
| 
 | |
|     // Undo the unwinding information from above since it doesn't apply below.
 | |
|     CFI_RESTORE_STATE_AND_DEF_CFA rbp, 208
 | |
| .Lexception_in_native:
 | |
|     pushq %gs:THREAD_TOP_QUICK_FRAME_OFFSET
 | |
|     addq LITERAL(-1), (%rsp)  // Remove the GenericJNI tag.
 | |
|     movq (%rsp), %rsp
 | |
|     call art_deliver_pending_exception
 | |
| END_FUNCTION art_quick_generic_jni_trampoline
 | |
| 
 | |
| DEFINE_FUNCTION art_deliver_pending_exception
 | |
|     // This will create a new save-all frame, required by the runtime.
 | |
|     DELIVER_PENDING_EXCEPTION
 | |
| END_FUNCTION art_deliver_pending_exception
 | |
| 
 | |
|     /*
 | |
|      * Called to bridge from the quick to interpreter ABI. On entry the arguments match those
 | |
|      * of a quick call:
 | |
|      * RDI = method being called / to bridge to.
 | |
|      * RSI, RDX, RCX, R8, R9 are arguments to that method.
 | |
|      */
 | |
| DEFINE_FUNCTION art_quick_to_interpreter_bridge
 | |
|     SETUP_SAVE_REFS_AND_ARGS_FRAME     // Set up frame and save arguments.
 | |
|     movq %gs:THREAD_SELF_OFFSET, %rsi  // RSI := Thread::Current()
 | |
|     movq %rsp, %rdx                    // RDX := sp
 | |
|     call SYMBOL(artQuickToInterpreterBridge)  // (method, Thread*, SP)
 | |
|     RESTORE_SAVE_REFS_AND_ARGS_FRAME   // TODO: no need to restore arguments in this case.
 | |
|     movq %rax, %xmm0                   // Place return value also into floating point return value.
 | |
|     RETURN_OR_DELIVER_PENDING_EXCEPTION    // return or deliver exception
 | |
| END_FUNCTION art_quick_to_interpreter_bridge
 | |
| 
 | |
|     /*
 | |
|      * Called to catch an attempt to invoke an obsolete method.
 | |
|      * RDI = method being called.
 | |
|      */
 | |
| ONE_ARG_RUNTIME_EXCEPTION art_invoke_obsolete_method_stub, artInvokeObsoleteMethod
 | |
| 
 | |
|     /*
 | |
|      * Routine that intercepts method calls and returns.
 | |
|      */
 | |
| DEFINE_FUNCTION art_quick_instrumentation_entry
 | |
| #if defined(__APPLE__)
 | |
|     int3
 | |
|     int3
 | |
| #else
 | |
|     SETUP_SAVE_REFS_AND_ARGS_FRAME
 | |
| 
 | |
|     movq %rdi, %r12               // Preserve method pointer in a callee-save.
 | |
| 
 | |
|     movq %gs:THREAD_SELF_OFFSET, %rdx   // Pass thread.
 | |
|     movq %rsp, %rcx                     // Pass SP.
 | |
| 
 | |
|     call SYMBOL(artInstrumentationMethodEntryFromCode) // (Method*, Object*, Thread*, SP)
 | |
| 
 | |
|                                   // %rax = result of call.
 | |
|     testq %rax, %rax
 | |
|     jz 1f
 | |
| 
 | |
|     movq %r12, %rdi               // Reload method pointer.
 | |
|     leaq art_quick_instrumentation_exit(%rip), %r12   // Set up return through instrumentation
 | |
|     movq %r12, FRAME_SIZE_SAVE_REFS_AND_ARGS-8(%rsp)  // exit.
 | |
| 
 | |
|     RESTORE_SAVE_REFS_AND_ARGS_FRAME
 | |
| 
 | |
|     jmp *%rax                     // Tail call to intended method.
 | |
| 1:
 | |
|     RESTORE_SAVE_REFS_AND_ARGS_FRAME
 | |
|     DELIVER_PENDING_EXCEPTION
 | |
| #endif  // __APPLE__
 | |
| END_FUNCTION art_quick_instrumentation_entry
 | |
| 
 | |
| DEFINE_FUNCTION_CUSTOM_CFA art_quick_instrumentation_exit, 0
 | |
|     pushq LITERAL(0)          // Push a fake return PC as there will be none on the stack.
 | |
|     CFI_ADJUST_CFA_OFFSET(8)
 | |
| 
 | |
|     SETUP_SAVE_EVERYTHING_FRAME
 | |
| 
 | |
|     leaq 16(%rsp), %rcx       // Pass floating-point result pointer, in kSaveEverything frame.
 | |
|     leaq 144(%rsp), %rdx      // Pass integer result pointer, in kSaveEverything frame.
 | |
|     movq %rsp, %rsi           // Pass SP.
 | |
|     movq %gs:THREAD_SELF_OFFSET, %rdi  // Pass Thread.
 | |
| 
 | |
|     call SYMBOL(artInstrumentationMethodExitFromCode)   // (Thread*, SP, gpr_res*, fpr_res*)
 | |
| 
 | |
|     testq %rax, %rax          // Check if we have a return-pc to go to. If we don't then there was
 | |
|                               // an exception
 | |
|     jz .Ldo_deliver_instrumentation_exception
 | |
|     testq %rdx, %rdx
 | |
|     jnz .Ldeoptimize
 | |
|     // Normal return.
 | |
|     movq %rax, FRAME_SIZE_SAVE_EVERYTHING-8(%rsp)  // Set return pc.
 | |
|     RESTORE_SAVE_EVERYTHING_FRAME
 | |
|     ret
 | |
| .Ldeoptimize:
 | |
|     movq %rdx, FRAME_SIZE_SAVE_EVERYTHING-8(%rsp)  // Set return pc.
 | |
|     RESTORE_SAVE_EVERYTHING_FRAME
 | |
|     // Jump to art_quick_deoptimize.
 | |
|     jmp SYMBOL(art_quick_deoptimize)
 | |
| .Ldo_deliver_instrumentation_exception:
 | |
|     DELIVER_PENDING_EXCEPTION_FRAME_READY
 | |
| END_FUNCTION art_quick_instrumentation_exit
 | |
| 
 | |
|     /*
 | |
|      * Instrumentation has requested that we deoptimize into the interpreter. The deoptimization
 | |
|      * will long jump to the upcall with a special exception of -1.
 | |
|      */
 | |
| DEFINE_FUNCTION art_quick_deoptimize
 | |
|     SETUP_SAVE_EVERYTHING_FRAME        // Stack should be aligned now.
 | |
|     movq %gs:THREAD_SELF_OFFSET, %rdi  // Pass Thread.
 | |
|     call SYMBOL(artDeoptimize)         // (Thread*)
 | |
|     UNREACHABLE
 | |
| END_FUNCTION art_quick_deoptimize
 | |
| 
 | |
|     /*
 | |
|      * Compiled code has requested that we deoptimize into the interpreter. The deoptimization
 | |
|      * will long jump to the interpreter bridge.
 | |
|      */
 | |
| DEFINE_FUNCTION art_quick_deoptimize_from_compiled_code
 | |
|     SETUP_SAVE_EVERYTHING_FRAME
 | |
|                                                 // Stack should be aligned now.
 | |
|     movq %gs:THREAD_SELF_OFFSET, %rsi           // Pass Thread.
 | |
|     call SYMBOL(artDeoptimizeFromCompiledCode)  // (DeoptimizationKind, Thread*)
 | |
|     UNREACHABLE
 | |
| END_FUNCTION art_quick_deoptimize_from_compiled_code
 | |
| 
 | |
|     /*
 | |
|      * String's compareTo.
 | |
|      *
 | |
|      * On entry:
 | |
|      *    rdi:   this string object (known non-null)
 | |
|      *    rsi:   comp string object (known non-null)
 | |
|      */
 | |
| DEFINE_FUNCTION art_quick_string_compareto
 | |
|     movl MIRROR_STRING_COUNT_OFFSET(%edi), %r8d
 | |
|     movl MIRROR_STRING_COUNT_OFFSET(%esi), %r9d
 | |
|     /* Build pointers to the start of string data */
 | |
|     leal MIRROR_STRING_VALUE_OFFSET(%edi), %edi
 | |
|     leal MIRROR_STRING_VALUE_OFFSET(%esi), %esi
 | |
| #if (STRING_COMPRESSION_FEATURE)
 | |
|     /* Differ cases */
 | |
|     shrl    LITERAL(1), %r8d
 | |
|     jnc     .Lstring_compareto_this_is_compressed
 | |
|     shrl    LITERAL(1), %r9d
 | |
|     jnc     .Lstring_compareto_that_is_compressed
 | |
|     jmp     .Lstring_compareto_both_not_compressed
 | |
| .Lstring_compareto_this_is_compressed:
 | |
|     shrl    LITERAL(1), %r9d
 | |
|     jnc     .Lstring_compareto_both_compressed
 | |
|     /* Comparison this (8-bit) and that (16-bit) */
 | |
|     mov     %r8d, %eax
 | |
|     subl    %r9d, %eax
 | |
|     mov     %r8d, %ecx
 | |
|     cmovg   %r9d, %ecx
 | |
|     /* Going into loop to compare each character */
 | |
|     jecxz   .Lstring_compareto_keep_length1     // check loop counter (if 0 then stop)
 | |
| .Lstring_compareto_loop_comparison_this_compressed:
 | |
|     movzbl  (%edi), %r8d                        // move *(this_cur_char) byte to long
 | |
|     movzwl  (%esi), %r9d                        // move *(that_cur_char) word to long
 | |
|     addl    LITERAL(1), %edi                    // ++this_cur_char (8-bit)
 | |
|     addl    LITERAL(2), %esi                    // ++that_cur_char (16-bit)
 | |
|     subl    %r9d, %r8d
 | |
|     loope   .Lstring_compareto_loop_comparison_this_compressed
 | |
|     cmovne  %r8d, %eax                          // return eax = *(this_cur_char) - *(that_cur_char)
 | |
| .Lstring_compareto_keep_length1:
 | |
|     ret
 | |
| .Lstring_compareto_that_is_compressed:
 | |
|     movl    %r8d, %eax
 | |
|     subl    %r9d, %eax
 | |
|     mov     %r8d, %ecx
 | |
|     cmovg   %r9d, %ecx
 | |
|     /* Comparison this (8-bit) and that (16-bit) */
 | |
|     jecxz   .Lstring_compareto_keep_length2     // check loop counter (if 0, don't compare)
 | |
| .Lstring_compareto_loop_comparison_that_compressed:
 | |
|     movzwl  (%edi), %r8d                        // move *(this_cur_char) word to long
 | |
|     movzbl  (%esi), %r9d                        // move *(that_cur_chat) byte to long
 | |
|     addl    LITERAL(2), %edi                    // ++this_cur_char (16-bit)
 | |
|     addl    LITERAL(1), %esi                    // ++that_cur_char (8-bit)
 | |
|     subl    %r9d, %r8d
 | |
|     loope   .Lstring_compareto_loop_comparison_that_compressed
 | |
|     cmovne  %r8d, %eax                          // return eax = *(this_cur_char) - *(that_cur_char)
 | |
| .Lstring_compareto_keep_length2:
 | |
|     ret
 | |
| .Lstring_compareto_both_compressed:
 | |
|     /* Calculate min length and count diff */
 | |
|     movl    %r8d, %ecx
 | |
|     movl    %r8d, %eax
 | |
|     subl    %r9d, %eax
 | |
|     cmovg   %r9d, %ecx
 | |
|     jecxz   .Lstring_compareto_keep_length3
 | |
|     repe    cmpsb
 | |
|     je      .Lstring_compareto_keep_length3
 | |
|     movzbl  -1(%edi), %eax        // get last compared char from this string (8-bit)
 | |
|     movzbl  -1(%esi), %ecx        // get last compared char from comp string (8-bit)
 | |
|     jmp     .Lstring_compareto_count_difference
 | |
| #endif // STRING_COMPRESSION_FEATURE
 | |
| .Lstring_compareto_both_not_compressed:
 | |
|     /* Calculate min length and count diff */
 | |
|     movl    %r8d, %ecx
 | |
|     movl    %r8d, %eax
 | |
|     subl    %r9d, %eax
 | |
|     cmovg   %r9d, %ecx
 | |
|     /*
 | |
|      * At this point we have:
 | |
|      *   eax: value to return if first part of strings are equal
 | |
|      *   ecx: minimum among the lengths of the two strings
 | |
|      *   esi: pointer to comp string data
 | |
|      *   edi: pointer to this string data
 | |
|      */
 | |
|     jecxz .Lstring_compareto_keep_length3
 | |
|     repe  cmpsw                   // find nonmatching chars in [%esi] and [%edi], up to length %ecx
 | |
|     je    .Lstring_compareto_keep_length3
 | |
|     movzwl  -2(%edi), %eax        // get last compared char from this string (16-bit)
 | |
|     movzwl  -2(%esi), %ecx        // get last compared char from comp string (16-bit)
 | |
| .Lstring_compareto_count_difference:
 | |
|     subl  %ecx, %eax              // return the difference
 | |
| .Lstring_compareto_keep_length3:
 | |
|     ret
 | |
| END_FUNCTION art_quick_string_compareto
 | |
| 
 | |
| UNIMPLEMENTED art_quick_memcmp16
 | |
| 
 | |
| DEFINE_FUNCTION art_quick_instance_of
 | |
|     SETUP_FP_CALLEE_SAVE_FRAME
 | |
|     subq LITERAL(8), %rsp                      // Alignment padding.
 | |
|     CFI_ADJUST_CFA_OFFSET(8)
 | |
|     call SYMBOL(artInstanceOfFromCode)         // (mirror::Object*, mirror::Class*)
 | |
|     addq LITERAL(8), %rsp
 | |
|     CFI_ADJUST_CFA_OFFSET(-8)
 | |
|     RESTORE_FP_CALLEE_SAVE_FRAME
 | |
|     ret
 | |
| END_FUNCTION art_quick_instance_of
 | |
| 
 | |
| DEFINE_FUNCTION art_quick_string_builder_append
 | |
|     SETUP_SAVE_REFS_ONLY_FRAME                // save ref containing registers for GC
 | |
|     // Outgoing argument set up
 | |
|     leaq FRAME_SIZE_SAVE_REFS_ONLY + __SIZEOF_POINTER__(%rsp), %rsi  // pass args
 | |
|     movq %gs:THREAD_SELF_OFFSET, %rdx         // pass Thread::Current()
 | |
|     call artStringBuilderAppend               // (uint32_t, const unit32_t*, Thread*)
 | |
|     RESTORE_SAVE_REFS_ONLY_FRAME              // restore frame up to return address
 | |
|     RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER   // return or deliver exception
 | |
| END_FUNCTION art_quick_string_builder_append
 | |
| 
 | |
| // Create a function `name` calling the ReadBarrier::Mark routine,
 | |
| // getting its argument and returning its result through register
 | |
| // `reg`, saving and restoring all caller-save registers.
 | |
| //
 | |
| // The generated function follows a non-standard runtime calling
 | |
| // convention:
 | |
| // - register `reg` (which may be different from RDI) is used to pass
 | |
| //   the (sole) argument of this function;
 | |
| // - register `reg` (which may be different from RAX) is used to return
 | |
| //   the result of this function (instead of RAX);
 | |
| // - if `reg` is different from `rdi`, RDI is treated like a normal
 | |
| //   (non-argument) caller-save register;
 | |
| // - if `reg` is different from `rax`, RAX is treated like a normal
 | |
| //   (non-result) caller-save register;
 | |
| // - everything else is the same as in the standard runtime calling
 | |
| //   convention (e.g. standard callee-save registers are preserved).
 | |
| MACRO2(READ_BARRIER_MARK_REG, name, reg)
 | |
|     DEFINE_FUNCTION VAR(name)
 | |
|     // Null check so that we can load the lock word.
 | |
|     testq REG_VAR(reg), REG_VAR(reg)
 | |
|     jz .Lret_rb_\name
 | |
| .Lnot_null_\name:
 | |
|     // Check the mark bit, if it is 1 return.
 | |
|     testl LITERAL(LOCK_WORD_MARK_BIT_MASK_SHIFTED), MIRROR_OBJECT_LOCK_WORD_OFFSET(REG_VAR(reg))
 | |
|     jz .Lslow_rb_\name
 | |
|     ret
 | |
| .Lslow_rb_\name:
 | |
|     PUSH rax
 | |
|     movl MIRROR_OBJECT_LOCK_WORD_OFFSET(REG_VAR(reg)), %eax
 | |
|     addl LITERAL(LOCK_WORD_STATE_FORWARDING_ADDRESS_OVERFLOW), %eax
 | |
|     // Jump if the addl caused eax to unsigned overflow. The only case where it overflows is the
 | |
|     // forwarding address one.
 | |
|     // Taken ~25% of the time.
 | |
|     jnae .Lret_forwarding_address\name
 | |
| 
 | |
|     // Save all potentially live caller-save core registers.
 | |
|     movq 0(%rsp), %rax
 | |
|     PUSH rcx
 | |
|     PUSH rdx
 | |
|     PUSH rsi
 | |
|     PUSH rdi
 | |
|     PUSH r8
 | |
|     PUSH r9
 | |
|     PUSH r10
 | |
|     PUSH r11
 | |
|     // Create space for caller-save floating-point registers.
 | |
|     subq MACRO_LITERAL(12 * 8), %rsp
 | |
|     CFI_ADJUST_CFA_OFFSET(12 * 8)
 | |
|     // Save all potentially live caller-save floating-point registers.
 | |
|     movq %xmm0, 0(%rsp)
 | |
|     movq %xmm1, 8(%rsp)
 | |
|     movq %xmm2, 16(%rsp)
 | |
|     movq %xmm3, 24(%rsp)
 | |
|     movq %xmm4, 32(%rsp)
 | |
|     movq %xmm5, 40(%rsp)
 | |
|     movq %xmm6, 48(%rsp)
 | |
|     movq %xmm7, 56(%rsp)
 | |
|     movq %xmm8, 64(%rsp)
 | |
|     movq %xmm9, 72(%rsp)
 | |
|     movq %xmm10, 80(%rsp)
 | |
|     movq %xmm11, 88(%rsp)
 | |
|     SETUP_FP_CALLEE_SAVE_FRAME
 | |
| 
 | |
|     .ifnc RAW_VAR(reg), rdi
 | |
|       movq REG_VAR(reg), %rdi       // Pass arg1 - obj from `reg`.
 | |
|     .endif
 | |
|     call SYMBOL(artReadBarrierMark) // artReadBarrierMark(obj)
 | |
|     .ifnc RAW_VAR(reg), rax
 | |
|       movq %rax, REG_VAR(reg)       // Return result into `reg`.
 | |
|     .endif
 | |
| 
 | |
|     RESTORE_FP_CALLEE_SAVE_FRAME
 | |
|     // Restore floating-point registers.
 | |
|     movq 0(%rsp), %xmm0
 | |
|     movq 8(%rsp), %xmm1
 | |
|     movq 16(%rsp), %xmm2
 | |
|     movq 24(%rsp), %xmm3
 | |
|     movq 32(%rsp), %xmm4
 | |
|     movq 40(%rsp), %xmm5
 | |
|     movq 48(%rsp), %xmm6
 | |
|     movq 56(%rsp), %xmm7
 | |
|     movq 64(%rsp), %xmm8
 | |
|     movq 72(%rsp), %xmm9
 | |
|     movq 80(%rsp), %xmm10
 | |
|     movq 88(%rsp), %xmm11
 | |
|     // Remove floating-point registers.
 | |
|     addq MACRO_LITERAL(12 * 8), %rsp
 | |
|     CFI_ADJUST_CFA_OFFSET(-(12 * 8))
 | |
|     // Restore core regs, except `reg`, as it is used to return the
 | |
|     // result of this function (simply remove it from the stack instead).
 | |
|     POP_REG_NE r11, RAW_VAR(reg)
 | |
|     POP_REG_NE r10, RAW_VAR(reg)
 | |
|     POP_REG_NE r9, RAW_VAR(reg)
 | |
|     POP_REG_NE r8, RAW_VAR(reg)
 | |
|     POP_REG_NE rdi, RAW_VAR(reg)
 | |
|     POP_REG_NE rsi, RAW_VAR(reg)
 | |
|     POP_REG_NE rdx, RAW_VAR(reg)
 | |
|     POP_REG_NE rcx, RAW_VAR(reg)
 | |
|     POP_REG_NE rax, RAW_VAR(reg)
 | |
| .Lret_rb_\name:
 | |
|     ret
 | |
| .Lret_forwarding_address\name:
 | |
|     // The overflow cleared the top bits.
 | |
|     sall LITERAL(LOCK_WORD_STATE_FORWARDING_ADDRESS_SHIFT), %eax
 | |
|     movq %rax, REG_VAR(reg)
 | |
|     POP_REG_NE rax, RAW_VAR(reg)
 | |
|     ret
 | |
|     END_FUNCTION VAR(name)
 | |
| END_MACRO
 | |
| 
 | |
| READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg00, rax
 | |
| READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg01, rcx
 | |
| READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg02, rdx
 | |
| READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg03, rbx
 | |
| // Note: There is no art_quick_read_barrier_mark_reg04, as register 4 (RSP)
 | |
| // cannot be used to pass arguments.
 | |
| READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg05, rbp
 | |
| READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg06, rsi
 | |
| READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg07, rdi
 | |
| READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg08, r8
 | |
| READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg09, r9
 | |
| READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg10, r10
 | |
| READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg11, r11
 | |
| READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg12, r12
 | |
| READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg13, r13
 | |
| READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg14, r14
 | |
| READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg15, r15
 | |
| 
 | |
| DEFINE_FUNCTION art_quick_read_barrier_slow
 | |
|     SETUP_FP_CALLEE_SAVE_FRAME
 | |
|     subq LITERAL(8), %rsp           // Alignment padding.
 | |
|     CFI_ADJUST_CFA_OFFSET(8)
 | |
|     call SYMBOL(artReadBarrierSlow) // artReadBarrierSlow(ref, obj, offset)
 | |
|     addq LITERAL(8), %rsp
 | |
|     CFI_ADJUST_CFA_OFFSET(-8)
 | |
|     RESTORE_FP_CALLEE_SAVE_FRAME
 | |
|     ret
 | |
| END_FUNCTION art_quick_read_barrier_slow
 | |
| 
 | |
| DEFINE_FUNCTION art_quick_read_barrier_for_root_slow
 | |
|     SETUP_FP_CALLEE_SAVE_FRAME
 | |
|     subq LITERAL(8), %rsp                  // Alignment padding.
 | |
|     CFI_ADJUST_CFA_OFFSET(8)
 | |
|     call SYMBOL(artReadBarrierForRootSlow) // artReadBarrierForRootSlow(root)
 | |
|     addq LITERAL(8), %rsp
 | |
|     CFI_ADJUST_CFA_OFFSET(-8)
 | |
|     RESTORE_FP_CALLEE_SAVE_FRAME
 | |
|     ret
 | |
| END_FUNCTION art_quick_read_barrier_for_root_slow
 | |
| 
 | |
|     /*
 | |
|      * On stack replacement stub.
 | |
|      * On entry:
 | |
|      *   [sp] = return address
 | |
|      *   rdi = stack to copy
 | |
|      *   rsi = size of stack
 | |
|      *   rdx = pc to call
 | |
|      *   rcx = JValue* result
 | |
|      *   r8 = shorty
 | |
|      *   r9 = thread
 | |
|      *
 | |
|      * Note that the native C ABI already aligned the stack to 16-byte.
 | |
|      */
 | |
| DEFINE_FUNCTION art_quick_osr_stub
 | |
|     // Save the non-volatiles.
 | |
|     PUSH rbp                      // Save rbp.
 | |
|     PUSH rcx                      // Save rcx/result*.
 | |
|     PUSH r8                       // Save r8/shorty*.
 | |
| 
 | |
|     // Save callee saves.
 | |
|     PUSH rbx
 | |
|     PUSH r12
 | |
|     PUSH r13
 | |
|     PUSH r14
 | |
|     PUSH r15
 | |
| 
 | |
|     pushq LITERAL(0)              // Push null for ArtMethod*.
 | |
|     CFI_ADJUST_CFA_OFFSET(8)
 | |
|     movl %esi, %ecx               // rcx := size of stack
 | |
|     movq %rdi, %rsi               // rsi := stack to copy
 | |
|     movq %rsp, %rbp               // Save stack pointer to RBP for CFI use in .Losr_entry.
 | |
|     call .Losr_entry
 | |
|     CFI_REMEMBER_STATE
 | |
| 
 | |
|     // Restore stack and callee-saves.
 | |
|     addq LITERAL(8), %rsp
 | |
|     CFI_ADJUST_CFA_OFFSET(-8)
 | |
|     POP r15
 | |
|     POP r14
 | |
|     POP r13
 | |
|     POP r12
 | |
|     POP rbx
 | |
|     POP r8
 | |
|     POP rcx
 | |
|     POP rbp
 | |
|     movq %rax, (%rcx)              // Store the result.
 | |
|     ret
 | |
| .Losr_entry:
 | |
|     CFI_RESTORE_STATE_AND_DEF_CFA rsp, 80
 | |
|     // Since the call has pushed the return address we need to switch the CFA register to RBP.
 | |
|     CFI_DEF_CFA_REGISTER(rbp)
 | |
| 
 | |
|     subl LITERAL(8), %ecx         // Given stack size contains pushed frame pointer, substract it.
 | |
|     subq %rcx, %rsp
 | |
|     movq %rsp, %rdi               // rdi := beginning of stack
 | |
|     rep movsb                     // while (rcx--) { *rdi++ = *rsi++ }
 | |
|     jmp *%rdx
 | |
| END_FUNCTION art_quick_osr_stub
 | |
| 
 | |
| DEFINE_FUNCTION art_quick_invoke_polymorphic
 | |
|                                                    // On entry: RDI := unused, RSI := receiver
 | |
|     SETUP_SAVE_REFS_AND_ARGS_FRAME                 // save callee saves
 | |
|     movq %rsi, %rdi                                // RDI := receiver
 | |
|     movq %gs:THREAD_SELF_OFFSET, %rsi              // RSI := Thread (self)
 | |
|     movq %rsp, %rdx                                // RDX := pass SP
 | |
|     call SYMBOL(artInvokePolymorphic)              // invoke with (receiver, self, SP)
 | |
|                                                    // save the code pointer
 | |
|     RESTORE_SAVE_REFS_AND_ARGS_FRAME
 | |
|     movq %rax, %xmm0                               // Result is in RAX. Copy to FP result register.
 | |
|     RETURN_OR_DELIVER_PENDING_EXCEPTION
 | |
| END_FUNCTION art_quick_invoke_polymorphic
 | |
| 
 | |
| DEFINE_FUNCTION art_quick_invoke_custom
 | |
|     SETUP_SAVE_REFS_AND_ARGS_FRAME                 // save callee saves
 | |
|                                                    // RDI := call_site_index
 | |
|     movq %gs:THREAD_SELF_OFFSET, %rsi              // RSI := Thread::Current()
 | |
|     movq %rsp, %rdx                                // RDX := SP
 | |
|     call SYMBOL(artInvokeCustom)                   // artInvokeCustom(Thread*, SP)
 | |
|     RESTORE_SAVE_REFS_AND_ARGS_FRAME
 | |
|     movq %rax, %xmm0                               // Result is in RAX. Copy to FP result register.
 | |
|     RETURN_OR_DELIVER_PENDING_EXCEPTION
 | |
| END_FUNCTION art_quick_invoke_custom
 | |
| 
 | |
| // Wrap ExecuteSwitchImpl in assembly method which specifies DEX PC for unwinding.
 | |
| //  Argument 0: RDI: The context pointer for ExecuteSwitchImpl.
 | |
| //  Argument 1: RSI: Pointer to the templated ExecuteSwitchImpl to call.
 | |
| //  Argument 2: RDX: The value of DEX PC (memory address of the methods bytecode).
 | |
| DEFINE_FUNCTION ExecuteSwitchImplAsm
 | |
|     PUSH rbx                 // Spill RBX
 | |
|     movq %rdx, %rbx          // RBX = DEX PC (callee save register)
 | |
|     CFI_DEFINE_DEX_PC_WITH_OFFSET(0 /* RAX */, 3 /* RBX */, 0)
 | |
| 
 | |
|     call *%rsi               // Call the wrapped function
 | |
| 
 | |
|     POP rbx                  // Restore RBX
 | |
|     ret
 | |
| END_FUNCTION ExecuteSwitchImplAsm
 | |
| 
 | |
| // On entry: edi is the class, r11 is the inline cache. r10 and rax are available.
 | |
| DEFINE_FUNCTION art_quick_update_inline_cache
 | |
| #if (INLINE_CACHE_SIZE != 5)
 | |
| #error "INLINE_CACHE_SIZE not as expected."
 | |
| #endif
 | |
|     // Don't update the cache if we are marking.
 | |
|     cmpl LITERAL(0), %gs:THREAD_IS_GC_MARKING_OFFSET
 | |
|     jnz .Ldone
 | |
| .Lentry1:
 | |
|     movl INLINE_CACHE_CLASSES_OFFSET(%r11), %eax
 | |
|     cmpl %edi, %eax
 | |
|     je .Ldone
 | |
|     cmpl LITERAL(0), %eax
 | |
|     jne .Lentry2
 | |
|     lock cmpxchg %edi, INLINE_CACHE_CLASSES_OFFSET(%r11)
 | |
|     jz .Ldone
 | |
|     jmp .Lentry1
 | |
| .Lentry2:
 | |
|     movl (INLINE_CACHE_CLASSES_OFFSET+4)(%r11), %eax
 | |
|     cmpl %edi, %eax
 | |
|     je .Ldone
 | |
|     cmpl LITERAL(0), %eax
 | |
|     jne .Lentry3
 | |
|     lock cmpxchg %edi, (INLINE_CACHE_CLASSES_OFFSET+4)(%r11)
 | |
|     jz .Ldone
 | |
|     jmp .Lentry2
 | |
| .Lentry3:
 | |
|     movl (INLINE_CACHE_CLASSES_OFFSET+8)(%r11), %eax
 | |
|     cmpl %edi, %eax
 | |
|     je .Ldone
 | |
|     cmpl LITERAL(0), %eax
 | |
|     jne .Lentry4
 | |
|     lock cmpxchg %edi, (INLINE_CACHE_CLASSES_OFFSET+8)(%r11)
 | |
|     jz .Ldone
 | |
|     jmp .Lentry3
 | |
| .Lentry4:
 | |
|     movl (INLINE_CACHE_CLASSES_OFFSET+12)(%r11), %eax
 | |
|     cmpl %edi, %eax
 | |
|     je .Ldone
 | |
|     cmpl LITERAL(0), %eax
 | |
|     jne .Lentry5
 | |
|     lock cmpxchg %edi, (INLINE_CACHE_CLASSES_OFFSET+12)(%r11)
 | |
|     jz .Ldone
 | |
|     jmp .Lentry4
 | |
| .Lentry5:
 | |
|     // Unconditionally store, the cache is megamorphic.
 | |
|     movl %edi, (INLINE_CACHE_CLASSES_OFFSET+16)(%r11)
 | |
| .Ldone:
 | |
|     ret
 | |
| END_FUNCTION art_quick_update_inline_cache
 | |
| 
 | |
| // On entry, method is at the bottom of the stack.
 | |
| DEFINE_FUNCTION art_quick_compile_optimized
 | |
|     SETUP_SAVE_EVERYTHING_FRAME
 | |
|     movq FRAME_SIZE_SAVE_EVERYTHING(%rsp), %rdi // pass ArtMethod
 | |
|     movq %gs:THREAD_SELF_OFFSET, %rsi           // pass Thread::Current()
 | |
|     call SYMBOL(artCompileOptimized)            // (ArtMethod*, Thread*)
 | |
|     RESTORE_SAVE_EVERYTHING_FRAME               // restore frame up to return address
 | |
|     ret
 | |
| END_FUNCTION art_quick_compile_optimized
 | |
| 
 | |
| // On entry, method is at the bottom of the stack.
 | |
| DEFINE_FUNCTION art_quick_method_entry_hook
 | |
|     SETUP_SAVE_EVERYTHING_FRAME
 | |
| 
 | |
|     movq FRAME_SIZE_SAVE_EVERYTHING(%rsp), %rdi // pass ArtMethod
 | |
|     movq %gs:THREAD_SELF_OFFSET, %rsi           // pass Thread::Current()
 | |
| 
 | |
|     call SYMBOL(artMethodEntryHook)              // (ArtMethod*, Thread*)
 | |
| 
 | |
|     RESTORE_SAVE_EVERYTHING_FRAME
 | |
|     ret
 | |
| END_FUNCTION art_quick_method_entry_hook
 | |
| 
 | |
| // On entry, method is at the bottom of the stack.
 | |
| // and r8 has should_deopt_frame value.
 | |
| DEFINE_FUNCTION art_quick_method_exit_hook
 | |
|     SETUP_SAVE_EVERYTHING_FRAME
 | |
| 
 | |
|     leaq 16(%rsp), %rcx                         // floating-point result pointer in kSaveEverything
 | |
|                                                 // frame
 | |
|     leaq 144(%rsp), %rdx                        // integer result pointer in kSaveEverything frame
 | |
|     movq FRAME_SIZE_SAVE_EVERYTHING(%rsp), %rsi // ArtMethod
 | |
|     movq %gs:THREAD_SELF_OFFSET, %rdi           // Thread::Current
 | |
|     call SYMBOL(artMethodExitHook)              // (Thread*, SP, gpr_res*, fpr_res*)
 | |
| 
 | |
|     cmpq LITERAL(1), %rax
 | |
|     CFI_REMEMBER_STATE
 | |
|     je .Ldo_deliver_instrumentation_exception_exit
 | |
| 
 | |
|     // Normal return.
 | |
|     RESTORE_SAVE_EVERYTHING_FRAME
 | |
|     ret
 | |
| .Ldo_deliver_instrumentation_exception_exit:
 | |
|     CFI_RESTORE_STATE_AND_DEF_CFA rsp, FRAME_SIZE_SAVE_EVERYTHING
 | |
|     DELIVER_PENDING_EXCEPTION_FRAME_READY
 | |
| END_FUNCTION art_quick_method_entry_hook
 |