2380 lines
		
	
	
		
			73 KiB
		
	
	
	
		
			ArmAsm
		
	
	
	
			
		
		
	
	
			2380 lines
		
	
	
		
			73 KiB
		
	
	
	
		
			ArmAsm
		
	
	
	
| %def header():
 | |
| /*
 | |
|  * Copyright (C) 2021 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.
 | |
|  */
 | |
| 
 | |
| /*
 | |
|  * This is a #include, not a %include, because we want the C pre-processor
 | |
|  * to expand the macros into assembler assignment statements.
 | |
|  */
 | |
| #include "asm_support.h"
 | |
| #include "arch/x86/asm_support_x86.S"
 | |
| 
 | |
| /**
 | |
|  * x86 ABI general notes:
 | |
|  *
 | |
|  * Caller save set:
 | |
|  *      eax, ebx, edx, ecx, st(0)-st(7)
 | |
|  * Callee save set:
 | |
|  *      esi, edi, ebp
 | |
|  * Return regs:
 | |
|  *      32-bit in eax
 | |
|  *      64-bit in edx:eax (low-order 32 in eax)
 | |
|  *      fp on top of fp stack st(0)
 | |
|  *
 | |
|  * Stack must be 16-byte aligned to support SSE in native code.
 | |
|  */
 | |
| 
 | |
| #define ARG3        %ebx
 | |
| #define ARG2        %edx
 | |
| #define ARG1        %ecx
 | |
| #define ARG0        %eax
 | |
| 
 | |
| /*
 | |
|  * single-purpose registers, given names for clarity
 | |
|  */
 | |
| #define rSELF    %fs
 | |
| #define rPC      %esi
 | |
| #define CFI_DEX  6  // DWARF register number of the register holding dex-pc (esi).
 | |
| #define CFI_TMP  0  // DWARF register number of the first argument register (eax).
 | |
| #define rFP      %edi
 | |
| #define rINST    %ebx
 | |
| #define rINSTw   %bx
 | |
| #define rINSTbh  %bh
 | |
| #define rINSTbl  %bl
 | |
| #define rIBASE   %edx
 | |
| #define rREFS    %ebp
 | |
| #define CFI_REFS 5 // DWARF register number of the reference array (ebp).
 | |
| 
 | |
| // Temporary registers while setting up a frame.
 | |
| #define rNEW_FP   %ecx
 | |
| #define rNEW_REFS %eax
 | |
| #define CFI_NEW_REFS 0
 | |
| 
 | |
| #define LOCAL0 4
 | |
| #define LOCAL1 8
 | |
| #define LOCAL2 12
 | |
| 
 | |
| /*
 | |
|  * Get/set the 32-bit value from a Dalvik register.
 | |
|  */
 | |
| #define VREG_ADDRESS(_vreg) (rFP,_vreg,4)
 | |
| #define VREG_HIGH_ADDRESS(_vreg) 4(rFP,_vreg,4)
 | |
| #define VREG_REF_ADDRESS(_vreg) (rREFS,_vreg,4)
 | |
| #define VREG_REF_HIGH_ADDRESS(_vreg) 4(rREFS,_vreg,4)
 | |
| 
 | |
| .macro GET_VREG _reg _vreg
 | |
|     movl    VREG_ADDRESS(\_vreg), \_reg
 | |
| .endm
 | |
| 
 | |
| .macro GET_VREG_OBJECT _reg _vreg
 | |
|     movl    VREG_REF_ADDRESS(\_vreg), \_reg
 | |
| .endm
 | |
| 
 | |
| /* Read wide value to xmm. */
 | |
| .macro GET_WIDE_FP_VREG _reg _vreg
 | |
|     movq    VREG_ADDRESS(\_vreg), \_reg
 | |
| .endm
 | |
| 
 | |
| .macro SET_VREG _reg _vreg
 | |
|     movl    \_reg, VREG_ADDRESS(\_vreg)
 | |
|     movl    MACRO_LITERAL(0), VREG_REF_ADDRESS(\_vreg)
 | |
| .endm
 | |
| 
 | |
| /* Write wide value from xmm. xmm is clobbered. */
 | |
| .macro SET_WIDE_FP_VREG _reg _vreg
 | |
|     movq    \_reg, VREG_ADDRESS(\_vreg)
 | |
|     pxor    \_reg, \_reg
 | |
|     movq    \_reg, VREG_REF_ADDRESS(\_vreg)
 | |
| .endm
 | |
| 
 | |
| .macro SET_VREG_OBJECT _reg _vreg
 | |
|     movl    \_reg, VREG_ADDRESS(\_vreg)
 | |
|     movl    \_reg, VREG_REF_ADDRESS(\_vreg)
 | |
| .endm
 | |
| 
 | |
| .macro GET_VREG_HIGH _reg _vreg
 | |
|     movl    VREG_HIGH_ADDRESS(\_vreg), \_reg
 | |
| .endm
 | |
| 
 | |
| .macro SET_VREG_HIGH _reg _vreg
 | |
|     movl    \_reg, VREG_HIGH_ADDRESS(\_vreg)
 | |
|     movl    MACRO_LITERAL(0), VREG_REF_HIGH_ADDRESS(\_vreg)
 | |
| .endm
 | |
| 
 | |
| .macro CLEAR_REF _vreg
 | |
|     movl    MACRO_LITERAL(0), VREG_REF_ADDRESS(\_vreg)
 | |
| .endm
 | |
| 
 | |
| .macro CLEAR_WIDE_REF _vreg
 | |
|     movl    MACRO_LITERAL(0), VREG_REF_ADDRESS(\_vreg)
 | |
|     movl    MACRO_LITERAL(0), VREG_REF_HIGH_ADDRESS(\_vreg)
 | |
| .endm
 | |
| 
 | |
| .macro GET_VREG_XMMs _xmmreg _vreg
 | |
|     movss VREG_ADDRESS(\_vreg), \_xmmreg
 | |
| .endm
 | |
| .macro GET_VREG_XMMd _xmmreg _vreg
 | |
|     movsd VREG_ADDRESS(\_vreg), \_xmmreg
 | |
| .endm
 | |
| .macro SET_VREG_XMMs _xmmreg _vreg
 | |
|     movss \_xmmreg, VREG_ADDRESS(\_vreg)
 | |
| .endm
 | |
| .macro SET_VREG_XMMd _xmmreg _vreg
 | |
|     movsd \_xmmreg, VREG_ADDRESS(\_vreg)
 | |
| .endm
 | |
| 
 | |
| // Includes the return address implicitly pushed on stack by 'call'.
 | |
| #define CALLEE_SAVES_SIZE (3 * 4 + 1 * 4)
 | |
| 
 | |
| #define PARAMETERS_SAVES_SIZE (4 * 4)
 | |
| 
 | |
| // +4 for the ArtMethod of the caller.
 | |
| #define OFFSET_TO_FIRST_ARGUMENT_IN_STACK (CALLEE_SAVES_SIZE + PARAMETERS_SAVES_SIZE + 4)
 | |
| 
 | |
| /*
 | |
|  * Refresh rINST.
 | |
|  * At enter to handler rINST does not contain the opcode number.
 | |
|  * However some utilities require the full value, so this macro
 | |
|  * restores the opcode number.
 | |
|  */
 | |
| .macro REFRESH_INST _opnum
 | |
|     movb    rINSTbl, rINSTbh
 | |
|     movb    $$\_opnum, rINSTbl
 | |
| .endm
 | |
| 
 | |
| /*
 | |
|  * Fetch the next instruction from rPC into rINSTw.  Does not advance rPC.
 | |
|  */
 | |
| .macro FETCH_INST
 | |
|     movzwl  (rPC), rINST
 | |
| .endm
 | |
| 
 | |
| .macro FETCH_INST_CLEAR_OPCODE
 | |
|     movzbl 1(rPC), rINST
 | |
| .endm
 | |
| 
 | |
| /*
 | |
|  * Remove opcode from rINST, compute the address of handler and jump to it.
 | |
|  */
 | |
| .macro GOTO_NEXT
 | |
|     movzx   rINSTbl,%ecx
 | |
|     movzbl  rINSTbh,rINST
 | |
|     shll    MACRO_LITERAL(${handler_size_bits}), %ecx
 | |
|     addl    rIBASE, %ecx
 | |
|     jmp     *%ecx
 | |
| .endm
 | |
| 
 | |
| /*
 | |
|  * Advance rPC by instruction count.
 | |
|  */
 | |
| .macro ADVANCE_PC _count
 | |
|     leal    2*\_count(rPC), rPC
 | |
| .endm
 | |
| 
 | |
| /*
 | |
|  * Advance rPC by instruction count, fetch instruction and jump to handler.
 | |
|  */
 | |
| .macro ADVANCE_PC_FETCH_AND_GOTO_NEXT _count
 | |
|     ADVANCE_PC \_count
 | |
|     FETCH_INST
 | |
|     GOTO_NEXT
 | |
| .endm
 | |
| 
 | |
| .macro NTERP_DEF_CFA cfi_reg
 | |
|     CFI_DEF_CFA_BREG_PLUS_UCONST \cfi_reg, -4, CALLEE_SAVES_SIZE + PARAMETERS_SAVES_SIZE
 | |
| .endm
 | |
| 
 | |
| .macro RESTORE_IBASE
 | |
|     call 0f
 | |
| 0:
 | |
|     popl rIBASE
 | |
|     addl MACRO_LITERAL(SYMBOL(artNterpAsmInstructionStart) - 0b), rIBASE
 | |
| .endm
 | |
| 
 | |
| .macro SPILL_ALL_CORE_PARAMETERS
 | |
|     PUSH_ARG eax
 | |
|     PUSH_ARG ecx
 | |
|     PUSH_ARG edx
 | |
|     PUSH_ARG ebx
 | |
| .endm
 | |
| 
 | |
| .macro RESTORE_ALL_CORE_PARAMETERS
 | |
|     POP_ARG ebx
 | |
|     POP_ARG edx
 | |
|     POP_ARG ecx
 | |
|     POP_ARG eax
 | |
| .endm
 | |
| 
 | |
| .macro DROP_PARAMETERS_SAVES
 | |
|     addl $$(PARAMETERS_SAVES_SIZE), %esp
 | |
| .endm
 | |
| 
 | |
| .macro SAVE_WIDE_RETURN
 | |
|     movl %edx, LOCAL2(%esp)
 | |
| .endm
 | |
| 
 | |
| .macro LOAD_WIDE_RETURN reg
 | |
|     movl LOCAL2(%esp), \reg
 | |
| .endm
 | |
| 
 | |
| // An assembly entry that has a OatQuickMethodHeader prefix.
 | |
| .macro OAT_ENTRY name, end
 | |
|     FUNCTION_TYPE(\name)
 | |
|     ASM_HIDDEN SYMBOL(\name)
 | |
|     .global SYMBOL(\name)
 | |
|     .balign 16
 | |
|     // Padding of 3 * 4 bytes to get 16 bytes alignment of code entry.
 | |
|     .long 0
 | |
|     .long 0
 | |
|     .long 0
 | |
|     // OatQuickMethodHeader. Note that the top two bits must be clear.
 | |
|     .long (SYMBOL(\end) - SYMBOL(\name))
 | |
| SYMBOL(\name):
 | |
| .endm
 | |
| 
 | |
| .macro ENTRY name
 | |
|     .text
 | |
|     ASM_HIDDEN SYMBOL(\name)
 | |
|     .global SYMBOL(\name)
 | |
|     FUNCTION_TYPE(\name)
 | |
| SYMBOL(\name):
 | |
| .endm
 | |
| 
 | |
| .macro END name
 | |
|     SIZE(\name)
 | |
| .endm
 | |
| 
 | |
| // Macro for defining entrypoints into runtime. We don't need to save registers
 | |
| // (we're not holding references there), but there is no
 | |
| // kDontSave runtime method. So just use the kSaveRefsOnly runtime method.
 | |
| .macro NTERP_TRAMPOLINE name, helper
 | |
| DEFINE_FUNCTION \name
 | |
|   movd %ebx, %xmm0
 | |
|   SETUP_SAVE_REFS_ONLY_FRAME ebx
 | |
|   movd %xmm0, %ebx
 | |
|   PUSH_ARG ebx
 | |
|   PUSH_ARG edx
 | |
|   PUSH_ARG ecx
 | |
|   PUSH_ARG eax
 | |
|   call \helper
 | |
|   addl MACRO_LITERAL(16), %esp
 | |
|   CFI_ADJUST_CFA_OFFSET(-16)
 | |
|   RESTORE_IBASE
 | |
|   FETCH_INST_CLEAR_OPCODE
 | |
|   RESTORE_SAVE_REFS_ONLY_FRAME
 | |
|   RETURN_OR_DELIVER_PENDING_EXCEPTION
 | |
| END_FUNCTION \name
 | |
| .endm
 | |
| 
 | |
| .macro CLEAR_VOLATILE_MARKER reg
 | |
|   andl MACRO_LITERAL(-2), \reg
 | |
| .endm
 | |
| 
 | |
| .macro EXPORT_PC
 | |
|     movl    rPC, -8(rREFS)
 | |
| .endm
 | |
| 
 | |
| .macro FETCH_PC
 | |
|     movl    -8(rREFS), rPC
 | |
| .endm
 | |
| 
 | |
| 
 | |
| .macro BRANCH
 | |
|     leal    (rPC, rINST, 2), rPC
 | |
|     // Update method counter and do a suspend check if the branch is negative or zero.
 | |
|     testl rINST, rINST
 | |
|     jle 3f
 | |
| 2:
 | |
|     FETCH_INST
 | |
|     GOTO_NEXT
 | |
| 3:
 | |
|     movl (%esp), %eax
 | |
|     movzwl ART_METHOD_HOTNESS_COUNT_OFFSET(%eax), %ecx
 | |
| #if (NTERP_HOTNESS_VALUE != 0)
 | |
| #error Expected 0 for hotness value
 | |
| #endif
 | |
|     // If the counter is at zero, handle this in the runtime.
 | |
|     testw %cx, %cx
 | |
|     je NterpHandleHotnessOverflow
 | |
|     // Update counter.
 | |
|     addl $$-1, %ecx
 | |
|     movw %cx, ART_METHOD_HOTNESS_COUNT_OFFSET(%eax)
 | |
|     DO_SUSPEND_CHECK continue_label=2b
 | |
| .endm
 | |
| 
 | |
| // Expects:
 | |
| // - edx, and eax to be available.
 | |
| // Outputs:
 | |
| // - \registers contains the dex registers size
 | |
| // - \outs contains the outs size
 | |
| // - if load_ins is 1, \ins contains the ins
 | |
| // - \code_item is replaced with a pointer to the instructions
 | |
| .macro FETCH_CODE_ITEM_INFO code_item, registers, outs, ins, load_ins
 | |
|     testl MACRO_LITERAL(1), \code_item
 | |
|     je 5f
 | |
|     andl $$-2, \code_item  // Remove the extra bit that marks it's a compact dex file.
 | |
|     movzwl COMPACT_CODE_ITEM_FIELDS_OFFSET(\code_item), %edx
 | |
|     movl %edx, \registers
 | |
|     sarl $$COMPACT_CODE_ITEM_REGISTERS_SIZE_SHIFT, \registers
 | |
|     andl $$0xf, \registers
 | |
|     movl %edx, \outs
 | |
|     sarl $$COMPACT_CODE_ITEM_OUTS_SIZE_SHIFT, \outs
 | |
|     andl $$0xf, \outs
 | |
|     .if \load_ins
 | |
|     movl %edx, \ins
 | |
|     sarl $$COMPACT_CODE_ITEM_INS_SIZE_SHIFT, \ins
 | |
|     andl $$0xf, \ins
 | |
|     .else
 | |
|     movl %edx, %eax
 | |
|     sarl $$COMPACT_CODE_ITEM_INS_SIZE_SHIFT, %eax
 | |
|     andl $$0xf, %eax
 | |
|     addl %eax, \registers
 | |
|     .endif
 | |
|     testw $$COMPACT_CODE_ITEM_REGISTERS_INS_OUTS_FLAGS, COMPACT_CODE_ITEM_FLAGS_OFFSET(\code_item)
 | |
|     je 4f
 | |
|     movl \code_item, %eax
 | |
|     testw $$COMPACT_CODE_ITEM_INSNS_FLAG, COMPACT_CODE_ITEM_FLAGS_OFFSET(\code_item)
 | |
|     je 1f
 | |
|     subl $$4, %eax
 | |
| 1:
 | |
|     testw $$COMPACT_CODE_ITEM_REGISTERS_FLAG, COMPACT_CODE_ITEM_FLAGS_OFFSET(\code_item)
 | |
|     je 2f
 | |
|     subl $$2, %eax
 | |
|     movzwl (%eax), %edx
 | |
|     addl %edx, \registers
 | |
| 2:
 | |
|     testw $$COMPACT_CODE_ITEM_INS_FLAG, COMPACT_CODE_ITEM_FLAGS_OFFSET(\code_item)
 | |
|     je 3f
 | |
|     subl $$2, %eax
 | |
|     movzwl (%eax), %edx
 | |
|     .if \load_ins
 | |
|     addl %edx, \ins
 | |
|     .else
 | |
|     addl %edx, \registers
 | |
|     .endif
 | |
| 3:
 | |
|     testw $$COMPACT_CODE_ITEM_OUTS_FLAG, COMPACT_CODE_ITEM_FLAGS_OFFSET(\code_item)
 | |
|     je 4f
 | |
|     subl $$2, %eax
 | |
|     movzwl (%eax), %edx
 | |
|     addl %edx, \outs
 | |
| 4:
 | |
|     .if \load_ins
 | |
|     addl \ins, \registers
 | |
|     .endif
 | |
|     addl $$COMPACT_CODE_ITEM_INSNS_OFFSET, \code_item
 | |
|     jmp 6f
 | |
| 5:
 | |
|     // Fetch dex register size.
 | |
|     movzwl CODE_ITEM_REGISTERS_SIZE_OFFSET(\code_item), \registers
 | |
|     // Fetch outs size.
 | |
|     movzwl CODE_ITEM_OUTS_SIZE_OFFSET(\code_item), \outs
 | |
|     .if \load_ins
 | |
|     movzwl CODE_ITEM_INS_SIZE_OFFSET(\code_item), \ins
 | |
|     .endif
 | |
|     addl $$CODE_ITEM_INSNS_OFFSET, \code_item
 | |
| 6:
 | |
| .endm
 | |
| 
 | |
| // Setup the stack to start executing the method. Expects:
 | |
| // - eax, edx, and ebx to be available.
 | |
| //
 | |
| // Inputs
 | |
| // - code_item: where the code item is
 | |
| // - refs: register where the pointer to dex references will be
 | |
| // - fp: register where the pointer to dex values will be
 | |
| // - cfi_refs: CFI register number of refs
 | |
| // - load_ins: whether to store the 'ins' value of the code item in esi
 | |
| //
 | |
| // Outputs
 | |
| // - ebx contains the dex registers size
 | |
| // - edx contains the old stack pointer.
 | |
| // - \code_item is replace with a pointer to the instructions
 | |
| // - if load_ins is 1, esi contains the ins
 | |
| .macro SETUP_STACK_FRAME code_item, refs, fp, cfi_refs, load_ins
 | |
|     FETCH_CODE_ITEM_INFO \code_item, %ebx, \refs, %esi, \load_ins
 | |
| 
 | |
|     movl $$3, %eax
 | |
|     cmpl $$2, \refs
 | |
|     cmovle %eax, \refs
 | |
| 
 | |
|     // Compute required frame size for dex registers: ((2 * ebx) + refs)
 | |
|     leal (\refs, %ebx, 2), %edx
 | |
|     sall $$2, %edx
 | |
| 
 | |
|     // Compute new stack pointer in fp: add 12 for saving the previous frame,
 | |
|     // pc, and method being executed.
 | |
|     leal -12(%esp), \fp
 | |
|     subl %edx, \fp
 | |
|     // Alignment
 | |
|     andl $$-16, \fp
 | |
| 
 | |
|     // Now setup the stack pointer.
 | |
|     movl %esp, %edx
 | |
|     CFI_DEF_CFA_REGISTER(edx)
 | |
|     movl \fp, %esp
 | |
| 
 | |
|     leal 12(%esp, \refs, 4), \refs
 | |
|     leal (\refs, %ebx, 4), \fp
 | |
| 
 | |
|     // Save old stack pointer.
 | |
|     movl %edx, -4(\refs)
 | |
|     NTERP_DEF_CFA \cfi_refs
 | |
| 
 | |
|     // Save ArtMethod.
 | |
|     movl 12(%edx), %eax
 | |
|     movl %eax, (%esp)
 | |
| 
 | |
|     // Put nulls in reference frame.
 | |
|     testl %ebx, %ebx
 | |
|     je 2f
 | |
|     movl \refs, %eax
 | |
| 1:
 | |
|     movl $$0, (%eax)
 | |
|     addl $$4, %eax
 | |
|     cmpl %eax, \fp
 | |
|     jne 1b
 | |
| 2:
 | |
| .endm
 | |
| 
 | |
| // Puts the next floating point argument into the expected register,
 | |
| // fetching values based on a non-range invoke.
 | |
| // Uses eax as temporary.
 | |
| //
 | |
| // TODO: We could simplify a lot of code by loading the G argument into
 | |
| // the "inst" register. Given that we enter the handler with "1(rPC)" in
 | |
| // the rINST, we can just add rINST<<16 to the args and we don't even
 | |
| // need to pass "arg_index" around.
 | |
| .macro LOOP_OVER_SHORTY_LOADING_XMMS xmm_reg, inst, shorty, arg_index, finished
 | |
| 1: // LOOP
 | |
|     movb (REG_VAR(shorty)), %al             // al := *shorty
 | |
|     addl MACRO_LITERAL(1), REG_VAR(shorty)  // shorty++
 | |
|     cmpb MACRO_LITERAL(0), %al              // if (al == '\0') goto 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
 | |
|     shrl MACRO_LITERAL(4), REG_VAR(inst)
 | |
|     addl MACRO_LITERAL(1), REG_VAR(arg_index)
 | |
|     //  Handle extra argument in arg array taken by a long.
 | |
|     cmpb MACRO_LITERAL(74), %al   // if (al != 'J') goto LOOP
 | |
|     jne 1b
 | |
|     shrl MACRO_LITERAL(4), REG_VAR(inst)
 | |
|     addl MACRO_LITERAL(1), REG_VAR(arg_index)
 | |
|     jmp 1b                        // goto LOOP
 | |
| 2:  // FOUND_DOUBLE
 | |
|     subl MACRO_LITERAL(8), %esp
 | |
|     movl REG_VAR(inst), %eax
 | |
|     andl MACRO_LITERAL(0xf), %eax
 | |
|     GET_VREG %eax, %eax
 | |
|     movl %eax, (%esp)
 | |
|     shrl MACRO_LITERAL(4), REG_VAR(inst)
 | |
|     addl MACRO_LITERAL(1), REG_VAR(arg_index)
 | |
|     cmpl MACRO_LITERAL(4), REG_VAR(arg_index)
 | |
|     je 5f
 | |
|     movl REG_VAR(inst), %eax
 | |
|     andl MACRO_LITERAL(0xf), %eax
 | |
|     shrl MACRO_LITERAL(4), REG_VAR(inst)
 | |
|     addl MACRO_LITERAL(1), REG_VAR(arg_index)
 | |
|     jmp 6f
 | |
| 5:
 | |
|     movzbl 1(rPC), %eax
 | |
|     andl MACRO_LITERAL(0xf), %eax
 | |
| 6:
 | |
|     GET_VREG %eax, %eax
 | |
|     movl %eax, 4(%esp)
 | |
|     movq (%esp), REG_VAR(xmm_reg)
 | |
|     addl MACRO_LITERAL(8), %esp
 | |
|     jmp 4f
 | |
| 3:  // FOUND_FLOAT
 | |
|     cmpl MACRO_LITERAL(4), REG_VAR(arg_index)
 | |
|     je 7f
 | |
|     movl REG_VAR(inst), %eax
 | |
|     andl MACRO_LITERAL(0xf), %eax
 | |
|     shrl MACRO_LITERAL(4), REG_VAR(inst)
 | |
|     addl MACRO_LITERAL(1), REG_VAR(arg_index)
 | |
|     jmp 8f
 | |
| 7:
 | |
|     movzbl 1(rPC), %eax
 | |
|     andl MACRO_LITERAL(0xf), %eax
 | |
| 8:
 | |
|     GET_VREG_XMMs REG_VAR(xmm_reg), %eax
 | |
| 4:
 | |
| .endm
 | |
| 
 | |
| // Puts the next int/long/object argument in the expected register,
 | |
| // fetching values based on a non-range invoke.
 | |
| // Uses eax as temporary.
 | |
| .macro LOOP_OVER_SHORTY_LOADING_GPRS gpr_reg, gpr_long_reg, inst, shorty, arg_index, finished, if_long, is_ebx
 | |
| 1: // LOOP
 | |
|     movb (REG_VAR(shorty)), %al   // al := *shorty
 | |
|     addl MACRO_LITERAL(1), REG_VAR(shorty)  // shorty++
 | |
|     cmpb MACRO_LITERAL(0), %al    // if (al == '\0') goto 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
 | |
|     cmpl MACRO_LITERAL(4), REG_VAR(arg_index)
 | |
|     je 7f
 | |
|     movl REG_VAR(inst), %eax
 | |
|     andl MACRO_LITERAL(0xf), %eax
 | |
|     shrl MACRO_LITERAL(4), REG_VAR(inst)
 | |
|     addl MACRO_LITERAL(1), REG_VAR(arg_index)
 | |
|     jmp 8f
 | |
| 7:
 | |
|     // Fetch PC
 | |
|     movl LOCAL1(%esp), %eax
 | |
|     movl -8(%eax), %eax
 | |
|     movzbl 1(%eax), %eax
 | |
|     andl MACRO_LITERAL(0xf), %eax
 | |
| 8:
 | |
|     GET_VREG REG_VAR(gpr_reg), %eax
 | |
|     jmp 5f
 | |
| 2:  // FOUND_LONG
 | |
|     .if \is_ebx
 | |
|     // Put back shorty and exit
 | |
|     subl MACRO_LITERAL(1), REG_VAR(shorty)
 | |
|     jmp 5f
 | |
|     .else
 | |
|     movl REG_VAR(inst), %eax
 | |
|     andl MACRO_LITERAL(0xf), %eax
 | |
|     GET_VREG REG_VAR(gpr_reg), %eax
 | |
|     shrl MACRO_LITERAL(4), REG_VAR(inst)
 | |
|     addl MACRO_LITERAL(1), REG_VAR(arg_index)
 | |
|     cmpl MACRO_LITERAL(4), REG_VAR(arg_index)
 | |
|     je 9f
 | |
|     movl REG_VAR(inst), %eax
 | |
|     andl MACRO_LITERAL(0xf), %eax
 | |
|     shrl MACRO_LITERAL(4), REG_VAR(inst)
 | |
|     addl MACRO_LITERAL(1), REG_VAR(arg_index)
 | |
|     jmp 10f
 | |
| 9:
 | |
|     // Fetch PC
 | |
|     movl LOCAL1(%esp), %eax
 | |
|     movl -8(%eax), %eax
 | |
|     movzbl 1(%eax), %eax
 | |
|     andl MACRO_LITERAL(0xf), %eax
 | |
| 10:
 | |
|     GET_VREG REG_VAR(gpr_long_reg), %eax
 | |
|     jmp \if_long
 | |
|     .endif
 | |
| 3:  // SKIP_FLOAT
 | |
|     shrl MACRO_LITERAL(4), REG_VAR(inst)
 | |
|     addl MACRO_LITERAL(1), REG_VAR(arg_index)
 | |
|     jmp 1b
 | |
| 4:  // SKIP_DOUBLE
 | |
|     shrl MACRO_LITERAL(8), REG_VAR(inst)
 | |
|     addl MACRO_LITERAL(2), REG_VAR(arg_index)
 | |
|     jmp 1b
 | |
| 5:
 | |
| .endm
 | |
| 
 | |
| // Puts the next int/long/object argument in the expected stack slot,
 | |
| // fetching values based on a non-range invoke.
 | |
| // Uses eax as temporary.
 | |
| .macro LOOP_OVER_SHORTY_LOADING_INTS stack_offset, shorty, inst, arg_index, finished, is_string_init
 | |
| 1:  // LOOP
 | |
|     movb (REG_VAR(shorty)), %al   // al := *shorty
 | |
|     addl MACRO_LITERAL(1), REG_VAR(shorty)  // shorty++
 | |
|     cmpb MACRO_LITERAL(0), %al    // if (al == '\0') goto 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
 | |
|     .if \is_string_init
 | |
|     cmpl MACRO_LITERAL(3), REG_VAR(arg_index)
 | |
|     .else
 | |
|     cmpl MACRO_LITERAL(4), REG_VAR(arg_index)
 | |
|     .endif
 | |
|     je 7f
 | |
|     movl REG_VAR(inst), %eax
 | |
|     andl MACRO_LITERAL(0xf), %eax
 | |
|     shrl MACRO_LITERAL(4), REG_VAR(inst)
 | |
|     jmp 8f
 | |
| 7:
 | |
|     // Fetch PC.
 | |
|     movl (LOCAL1 + \stack_offset)(%esp), %eax
 | |
|     movl -8(%eax), %eax
 | |
|     movzbl 1(%eax), %eax
 | |
|     andl MACRO_LITERAL(0xf), %eax
 | |
| 8:
 | |
|     GET_VREG %eax, %eax
 | |
|     // Add 4 for the ArtMethod.
 | |
|     movl %eax, (4 + \stack_offset)(%esp, REG_VAR(arg_index), 4)
 | |
|     addl MACRO_LITERAL(1), REG_VAR(arg_index)
 | |
|     jmp 1b
 | |
| 2:  // FOUND_LONG
 | |
|     movl REG_VAR(inst), %eax
 | |
|     andl MACRO_LITERAL(0xf), %eax
 | |
|     GET_VREG %eax, %eax
 | |
|     // Add 4 for the ArtMethod.
 | |
|     movl %eax, (4 + \stack_offset)(%esp, REG_VAR(arg_index), 4)
 | |
|     shrl MACRO_LITERAL(4), REG_VAR(inst)
 | |
|     addl MACRO_LITERAL(1), REG_VAR(arg_index)
 | |
|     .if \is_string_init
 | |
|     cmpl MACRO_LITERAL(3), REG_VAR(arg_index)
 | |
|     .else
 | |
|     cmpl MACRO_LITERAL(4), REG_VAR(arg_index)
 | |
|     .endif
 | |
|     je 9f
 | |
|     movl REG_VAR(inst), %eax
 | |
|     andl MACRO_LITERAL(0xf), %eax
 | |
|     shrl MACRO_LITERAL(4), REG_VAR(inst)
 | |
|     jmp 10f
 | |
| 9:
 | |
|     // Fetch PC.
 | |
|     movl (LOCAL1 + \stack_offset)(%esp), %eax
 | |
|     movl -8(%eax), %eax
 | |
|     movzbl 1(%eax), %eax
 | |
|     andl MACRO_LITERAL(0xf), %eax
 | |
| 10:
 | |
|     GET_VREG %eax, %eax
 | |
|     // +4 for the ArtMethod.
 | |
|     movl %eax, (4 + \stack_offset)(%esp, REG_VAR(arg_index), 4)
 | |
|     addl MACRO_LITERAL(1), REG_VAR(arg_index)
 | |
|     jmp 1b
 | |
| 3:  // SKIP_FLOAT
 | |
|     shrl MACRO_LITERAL(4), REG_VAR(inst)
 | |
|     addl MACRO_LITERAL(1), REG_VAR(arg_index)
 | |
|     jmp 1b
 | |
| 4:  // SKIP_DOUBLE
 | |
|     shrl MACRO_LITERAL(8), REG_VAR(inst)
 | |
|     addl MACRO_LITERAL(2), REG_VAR(arg_index)
 | |
|     jmp 1b
 | |
| .endm
 | |
| 
 | |
| // Puts the next floating point argument into the expected register,
 | |
| // fetching values based on a range invoke.
 | |
| // Uses eax as temporary.
 | |
| .macro LOOP_RANGE_OVER_SHORTY_LOADING_XMMS xmm_reg, shorty, arg_index, stack_index, finished
 | |
| 1: // LOOP
 | |
|     movb (REG_VAR(shorty)), %al             // al := *shorty
 | |
|     addl MACRO_LITERAL(1), REG_VAR(shorty)  // shorty++
 | |
|     cmpb MACRO_LITERAL(0), %al              // if (al == '\0') goto 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
 | |
|     addl MACRO_LITERAL(1), REG_VAR(arg_index)
 | |
|     addl MACRO_LITERAL(1), REG_VAR(stack_index)
 | |
|     //  Handle extra argument in arg array taken by a long.
 | |
|     cmpb MACRO_LITERAL(74), %al   // if (al != 'J') goto LOOP
 | |
|     jne 1b
 | |
|     addl MACRO_LITERAL(1), REG_VAR(arg_index)
 | |
|     addl MACRO_LITERAL(1), REG_VAR(stack_index)
 | |
|     jmp 1b                        // goto LOOP
 | |
| 2:  // FOUND_DOUBLE
 | |
|     GET_VREG_XMMd REG_VAR(xmm_reg), REG_VAR(arg_index)
 | |
|     addl MACRO_LITERAL(2), REG_VAR(arg_index)
 | |
|     addl MACRO_LITERAL(2), REG_VAR(stack_index)
 | |
|     jmp 4f
 | |
| 3:  // FOUND_FLOAT
 | |
|     GET_VREG_XMMs REG_VAR(xmm_reg), REG_VAR(arg_index)
 | |
|     addl MACRO_LITERAL(1), REG_VAR(arg_index)
 | |
|     add MACRO_LITERAL(1), REG_VAR(stack_index)
 | |
| 4:
 | |
| .endm
 | |
| 
 | |
| // Puts the next floating point argument into the expected stack slot,
 | |
| // fetching values based on a range invoke.
 | |
| // Uses eax as temporary.
 | |
| //
 | |
| // TODO: We could just copy all the vregs to the stack slots in a simple loop
 | |
| // (or REP MOVSD) without looking at the shorty at all. (We could also drop
 | |
| // the "stack_index" from the macros for loading registers.) We could also do
 | |
| // that conditionally if argument word count > 3; otherwise we know that all
 | |
| // args fit into registers.
 | |
| .macro LOOP_RANGE_OVER_FPs shorty, arg_index, stack_index, finished
 | |
| 1: // LOOP
 | |
|     movb (REG_VAR(shorty)), %al             // bl := *shorty
 | |
|     addl MACRO_LITERAL(1), REG_VAR(shorty)  // shorty++
 | |
|     cmpb MACRO_LITERAL(0), %al              // if (al == '\0') goto 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
 | |
|     addl MACRO_LITERAL(1), REG_VAR(arg_index)
 | |
|     addl MACRO_LITERAL(1), REG_VAR(stack_index)
 | |
|     //  Handle extra argument in arg array taken by a long.
 | |
|     cmpb MACRO_LITERAL(74), %al   // if (al != 'J') goto LOOP
 | |
|     jne 1b
 | |
|     addl MACRO_LITERAL(1), REG_VAR(arg_index)
 | |
|     addl MACRO_LITERAL(1), REG_VAR(stack_index)
 | |
|     jmp 1b                        // goto LOOP
 | |
| 2:  // FOUND_DOUBLE
 | |
|     movq (rFP, REG_VAR(arg_index), 4), %xmm4
 | |
|     movq %xmm4, 4(%esp, REG_VAR(stack_index), 4)
 | |
|     addl MACRO_LITERAL(2), REG_VAR(arg_index)
 | |
|     addl MACRO_LITERAL(2), REG_VAR(stack_index)
 | |
|     jmp 1b
 | |
| 3:  // FOUND_FLOAT
 | |
|     movl (rFP, REG_VAR(arg_index), 4), %eax
 | |
|     movl %eax, 4(%esp, REG_VAR(stack_index), 4)
 | |
|     addl MACRO_LITERAL(1), REG_VAR(arg_index)
 | |
|     addl MACRO_LITERAL(1), REG_VAR(stack_index)
 | |
|     jmp 1b
 | |
| .endm
 | |
| 
 | |
| // Puts the next int/long/object argument in the expected register,
 | |
| // fetching values based on a range invoke.
 | |
| // Uses eax as temporary.
 | |
| .macro LOOP_RANGE_OVER_SHORTY_LOADING_GPRS gpr_reg, gpr_long_reg, shorty, arg_index, stack_index, finished, if_long, is_ebx
 | |
| 1: // LOOP
 | |
|     movb (REG_VAR(shorty)), %al             // al := *shorty
 | |
|     addl MACRO_LITERAL(1), REG_VAR(shorty)  // shorty++
 | |
|     cmpb MACRO_LITERAL(0), %al    // if (al == '\0') goto 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       (rFP, REG_VAR(arg_index), 4), REG_VAR(gpr_reg)
 | |
|     addl MACRO_LITERAL(1), REG_VAR(arg_index)
 | |
|     addl MACRO_LITERAL(1), REG_VAR(stack_index)
 | |
|     jmp 5f
 | |
| 2:  // FOUND_LONG
 | |
|     .if \is_ebx
 | |
|     // Put back shorty and exit
 | |
|     subl MACRO_LITERAL(1), REG_VAR(shorty)
 | |
|     .else
 | |
|     movl (rFP, REG_VAR(arg_index), 4), REG_VAR(gpr_reg)
 | |
|     movl 4(rFP, REG_VAR(arg_index), 4), REG_VAR(gpr_long_reg)
 | |
|     addl MACRO_LITERAL(2), REG_VAR(arg_index)
 | |
|     addl MACRO_LITERAL(2), REG_VAR(stack_index)
 | |
|     .endif
 | |
|     jmp \if_long
 | |
| 3:  // SKIP_FLOAT
 | |
|     addl MACRO_LITERAL(1), REG_VAR(arg_index)
 | |
|     addl MACRO_LITERAL(1), REG_VAR(stack_index)
 | |
|     jmp 1b
 | |
| 4:  // SKIP_DOUBLE
 | |
|     addl MACRO_LITERAL(2), REG_VAR(arg_index)
 | |
|     addl MACRO_LITERAL(2), REG_VAR(stack_index)
 | |
|     jmp 1b
 | |
| 5:
 | |
| .endm
 | |
| 
 | |
| // Puts the next int/long/object argument in the expected stack slot,
 | |
| // fetching values based on a range invoke.
 | |
| // Uses eax as temporary.
 | |
| .macro LOOP_RANGE_OVER_INTs offset, shorty, arg_index, stack_index, finished
 | |
| 1: // LOOP
 | |
|     movb (REG_VAR(shorty)), %al             // al := *shorty
 | |
|     addl MACRO_LITERAL(1), REG_VAR(shorty)  // shorty++
 | |
|     cmpb MACRO_LITERAL(0), %al    // if (al == '\0') goto 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 (rFP, REG_VAR(arg_index), 4), %eax
 | |
|     // Add 4 for the ArtMethod.
 | |
|     movl %eax, (4 + \offset)(%esp, REG_VAR(stack_index), 4)
 | |
| 3:  // SKIP_FLOAT
 | |
|     addl MACRO_LITERAL(1), REG_VAR(arg_index)
 | |
|     addl MACRO_LITERAL(1), REG_VAR(stack_index)
 | |
|     jmp 1b
 | |
| 2:  // FOUND_LONG
 | |
|     movl (rFP, REG_VAR(arg_index), 4), %eax
 | |
|     // Add 4 for the ArtMethod.
 | |
|     movl %eax, (4 + \offset)(%esp, REG_VAR(stack_index), 4)
 | |
|     movl 4(rFP, REG_VAR(arg_index), 4), %eax
 | |
|     // Add 4 for the ArtMethod and 4 for other half.
 | |
|     movl %eax, (4 + 4 + \offset)(%esp, REG_VAR(stack_index), 4)
 | |
| 4:  // SKIP_DOUBLE
 | |
|     addl MACRO_LITERAL(2), REG_VAR(arg_index)
 | |
|     addl MACRO_LITERAL(2), REG_VAR(stack_index)
 | |
|     jmp 1b
 | |
| .endm
 | |
| 
 | |
| // Puts the next floating point parameter passed in physical register
 | |
| // in the expected dex register array entry.
 | |
| // Uses eax as temporary.
 | |
| .macro LOOP_OVER_SHORTY_STORING_XMMS xmm_reg, shorty, arg_index, fp, finished
 | |
| 1: // LOOP
 | |
|     movb (REG_VAR(shorty)), %al             // al := *shorty
 | |
|     addl MACRO_LITERAL(1), REG_VAR(shorty)  // shorty++
 | |
|     cmpb MACRO_LITERAL(0), %al              // if (al == '\0') goto 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
 | |
|     addl MACRO_LITERAL(1), REG_VAR(arg_index)
 | |
|     //  Handle extra argument in arg array taken by a long.
 | |
|     cmpb MACRO_LITERAL(74), %al   // if (al != 'J') goto LOOP
 | |
|     jne 1b
 | |
|     addl MACRO_LITERAL(1), REG_VAR(arg_index)
 | |
|     jmp 1b                        // goto LOOP
 | |
| 2:  // FOUND_DOUBLE
 | |
|     movq REG_VAR(xmm_reg),(REG_VAR(fp), REG_VAR(arg_index), 4)
 | |
|     addl MACRO_LITERAL(2), REG_VAR(arg_index)
 | |
|     jmp 4f
 | |
| 3:  // FOUND_FLOAT
 | |
|     movss REG_VAR(xmm_reg), (REG_VAR(fp), REG_VAR(arg_index), 4)
 | |
|     addl MACRO_LITERAL(1), REG_VAR(arg_index)
 | |
| 4:
 | |
| .endm
 | |
| 
 | |
| // Puts the next int/long/object parameter passed in physical register
 | |
| // in the expected dex register array entry, and in case of object in the
 | |
| // expected reference array entry.
 | |
| // Uses eax as temporary.
 | |
| .macro LOOP_OVER_SHORTY_STORING_GPRS offset, offset_long, stack_ptr, shorty, arg_index, regs, refs, finished, if_long, is_ebx
 | |
| 1: // LOOP
 | |
|     movb (REG_VAR(shorty)), %al             // al := *shorty
 | |
|     addl MACRO_LITERAL(1), REG_VAR(shorty)  // shorty++
 | |
|     cmpb MACRO_LITERAL(0), %al    // if (al == '\0') goto 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
 | |
|     cmpb MACRO_LITERAL(76), %al   // if (al != 'L') goto NOT_REFERENCE
 | |
|     jne 6f
 | |
|     movl \offset(REG_VAR(stack_ptr)), %eax
 | |
|     movl %eax, (REG_VAR(regs), REG_VAR(arg_index), 4)
 | |
|     movl %eax, (REG_VAR(refs), REG_VAR(arg_index), 4)
 | |
|     addl MACRO_LITERAL(1), REG_VAR(arg_index)
 | |
|     jmp 5f
 | |
| 2:  // FOUND_LONG
 | |
|     .if \is_ebx
 | |
|     // Put back shorty and jump to \if_long
 | |
|     subl MACRO_LITERAL(1), REG_VAR(shorty)
 | |
|     .else
 | |
|     movl \offset(REG_VAR(stack_ptr)), %eax
 | |
|     movl %eax, (REG_VAR(regs), REG_VAR(arg_index), 4)
 | |
|     movl \offset_long(REG_VAR(stack_ptr)), %eax
 | |
|     movl %eax, 4(REG_VAR(regs), REG_VAR(arg_index), 4)
 | |
|     addl MACRO_LITERAL(2), REG_VAR(arg_index)
 | |
|     .endif
 | |
|     jmp \if_long
 | |
| 3:  // SKIP_FLOAT
 | |
|     addl MACRO_LITERAL(1), REG_VAR(arg_index)
 | |
|     jmp 1b
 | |
| 4:  // SKIP_DOUBLE
 | |
|     addl MACRO_LITERAL(2), REG_VAR(arg_index)
 | |
|     jmp 1b
 | |
| 6:  // NOT_REFERENCE
 | |
|     movl \offset(REG_VAR(stack_ptr)), %eax
 | |
|     movl %eax, (REG_VAR(regs), REG_VAR(arg_index), 4)
 | |
|     addl MACRO_LITERAL(1), REG_VAR(arg_index)
 | |
| 5:
 | |
| .endm
 | |
| 
 | |
| // Puts the next floating point parameter passed in stack
 | |
| // in the expected dex register array entry.
 | |
| // Uses eax as temporary.
 | |
| //
 | |
| // TODO: Or we could just spill regs to the reserved slots in the caller's
 | |
| // frame and copy all regs in a simple loop. This time, however, we would
 | |
| // need to look at the shorty anyway to look for the references.
 | |
| // (The trade-off is different for passing arguments and receiving them.)
 | |
| .macro LOOP_OVER_FPs shorty, arg_index, regs, stack_ptr, finished
 | |
| 1: // LOOP
 | |
|     movb (REG_VAR(shorty)), %al             // al := *shorty
 | |
|     addl MACRO_LITERAL(1), REG_VAR(shorty)  // shorty++
 | |
|     cmpb MACRO_LITERAL(0), %al              // if (al == '\0') goto 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
 | |
|     addl MACRO_LITERAL(1), REG_VAR(arg_index)
 | |
|     //  Handle extra argument in arg array taken by a long.
 | |
|     cmpb MACRO_LITERAL(74), %al   // if (al != 'J') goto LOOP
 | |
|     jne 1b
 | |
|     addl MACRO_LITERAL(1), REG_VAR(arg_index)
 | |
|     jmp 1b                        // goto LOOP
 | |
| 2:  // FOUND_DOUBLE
 | |
|     movq OFFSET_TO_FIRST_ARGUMENT_IN_STACK(REG_VAR(stack_ptr), REG_VAR(arg_index), 4), %xmm4
 | |
|     movq %xmm4, (REG_VAR(regs), REG_VAR(arg_index), 4)
 | |
|     addl MACRO_LITERAL(2), REG_VAR(arg_index)
 | |
|     jmp 1b
 | |
| 3:  // FOUND_FLOAT
 | |
|     movl OFFSET_TO_FIRST_ARGUMENT_IN_STACK(REG_VAR(stack_ptr), REG_VAR(arg_index), 4), %eax
 | |
|     movl %eax, (REG_VAR(regs), REG_VAR(arg_index), 4)
 | |
|     addl MACRO_LITERAL(1), REG_VAR(arg_index)
 | |
|     jmp 1b
 | |
| .endm
 | |
| 
 | |
| // Puts the next int/long/object parameter passed in stack
 | |
| // in the expected dex register array entry, and in case of object in the
 | |
| // expected reference array entry.
 | |
| // Uses eax as temporary.
 | |
| .macro LOOP_OVER_INTs shorty, arg_index, regs, refs, stack_ptr, finished
 | |
| 1: // LOOP
 | |
|     movb (REG_VAR(shorty)), %al             // al := *shorty
 | |
|     addl MACRO_LITERAL(1), REG_VAR(shorty)  // shorty++
 | |
|     cmpb MACRO_LITERAL(0), %al    // if (al == '\0') goto finished
 | |
|     je  VAR(finished)
 | |
|     cmpb MACRO_LITERAL(74), %al   // if (al == 'J') goto FOUND_LONG
 | |
|     je 2f
 | |
|     cmpb MACRO_LITERAL(76), %al   // if (al == 'L') goto FOUND_REFERENCE
 | |
|     je 6f
 | |
|     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 OFFSET_TO_FIRST_ARGUMENT_IN_STACK(REG_VAR(stack_ptr), REG_VAR(arg_index), 4), %eax
 | |
|     movl %eax, (REG_VAR(regs), REG_VAR(arg_index), 4)
 | |
|     addl MACRO_LITERAL(1), REG_VAR(arg_index)
 | |
|     jmp 1b
 | |
| 6:  // FOUND_REFERENCE
 | |
|     movl OFFSET_TO_FIRST_ARGUMENT_IN_STACK(REG_VAR(stack_ptr), REG_VAR(arg_index), 4), %eax
 | |
|     movl %eax, (REG_VAR(regs), REG_VAR(arg_index), 4)
 | |
|     movl %eax, (REG_VAR(refs), REG_VAR(arg_index), 4)
 | |
| 3:  // SKIP_FLOAT
 | |
|     addl MACRO_LITERAL(1), REG_VAR(arg_index)
 | |
|     jmp 1b
 | |
| 2:  // FOUND_LONG
 | |
|     movl OFFSET_TO_FIRST_ARGUMENT_IN_STACK(REG_VAR(stack_ptr), REG_VAR(arg_index), 4), %eax
 | |
|     movl %eax, (REG_VAR(regs), REG_VAR(arg_index), 4)
 | |
|     movl (OFFSET_TO_FIRST_ARGUMENT_IN_STACK+4)(REG_VAR(stack_ptr), REG_VAR(arg_index), 4), %eax
 | |
|     movl %eax, 4(REG_VAR(regs), REG_VAR(arg_index), 4)
 | |
| 4:  // SKIP_DOUBLE
 | |
|     addl MACRO_LITERAL(2), REG_VAR(arg_index)
 | |
|     jmp 1b
 | |
| .endm
 | |
| 
 | |
| // Increase method hotness and do suspend check before starting executing the method.
 | |
| .macro START_EXECUTING_INSTRUCTIONS
 | |
|    movl (%esp), %eax
 | |
|    movzwl ART_METHOD_HOTNESS_COUNT_OFFSET(%eax), %ecx
 | |
| #if (NTERP_HOTNESS_VALUE != 0)
 | |
| #error Expected 0 for hotness value
 | |
| #endif
 | |
|    // If the counter is at zero, handle this in the runtime.
 | |
|    testl %ecx, %ecx
 | |
|    je 3f
 | |
|    // Update counter.
 | |
|    addl $$-1, %ecx
 | |
|    movw %cx, ART_METHOD_HOTNESS_COUNT_OFFSET(%eax)
 | |
| 1:
 | |
|    testl $$(THREAD_SUSPEND_OR_CHECKPOINT_REQUEST), rSELF:THREAD_FLAGS_OFFSET
 | |
|    jz 2f
 | |
|    EXPORT_PC
 | |
|    call SYMBOL(art_quick_test_suspend)
 | |
|    RESTORE_IBASE
 | |
| 2:
 | |
|    FETCH_INST
 | |
|    GOTO_NEXT
 | |
| 3:
 | |
|    CHECK_AND_UPDATE_SHARED_MEMORY_METHOD if_hot=4f, if_not_hot=1b
 | |
| 4:
 | |
|    movl $$0, ARG1
 | |
|    movl rFP, ARG2
 | |
|    call nterp_hot_method
 | |
|    jmp 2b
 | |
| .endm
 | |
| 
 | |
| .macro SPILL_ALL_CALLEE_SAVES
 | |
|     PUSH edi
 | |
|     PUSH esi
 | |
|     PUSH ebp
 | |
| .endm
 | |
| 
 | |
| .macro RESTORE_ALL_CALLEE_SAVES
 | |
|     POP ebp
 | |
|     POP esi
 | |
|     POP edi
 | |
| .endm
 | |
| 
 | |
| .macro GET_SHORTY dest, is_interface, is_polymorphic, is_custom
 | |
|    // Save eax (ArtMethod), ecx (potential this).
 | |
|    push %eax
 | |
|    push %ecx
 | |
|    .if \is_polymorphic
 | |
|    push rPC
 | |
|    push 12(%esp)
 | |
|    call SYMBOL(NterpGetShortyFromInvokePolymorphic)
 | |
|    addl MACRO_LITERAL(8), %esp
 | |
|    .elseif \is_custom
 | |
|    push rPC
 | |
|    push 12(%esp)
 | |
|    call SYMBOL(NterpGetShortyFromInvokeCustom)
 | |
|    addl MACRO_LITERAL(8), %esp
 | |
|    .elseif \is_interface
 | |
|    subl MACRO_LITERAL(16), %esp
 | |
|    // Save interface method.
 | |
|    movss %xmm7, (%esp)
 | |
|    movzwl 2(rPC), %eax
 | |
|    pushl %eax
 | |
|    // Caller is at 8 (saved ArtMethod + ecx) + 16 + 4 (second argument)
 | |
|    pushl 28(%esp)
 | |
|    call SYMBOL(NterpGetShortyFromMethodId)
 | |
|    // Restore interface method.
 | |
|    movss 8(%esp), %xmm7
 | |
|    addl MACRO_LITERAL(24), %esp
 | |
|    .else
 | |
|    subl MACRO_LITERAL(4), %esp  // Alignment
 | |
|    push %eax
 | |
|    call SYMBOL(NterpGetShorty)
 | |
|    addl MACRO_LITERAL(8), %esp
 | |
|    .endif
 | |
|    movl %eax, \dest
 | |
|    pop %ecx
 | |
|    pop %eax
 | |
| .endm
 | |
| 
 | |
| .macro GET_SHORTY_SLOW_PATH dest, is_interface
 | |
|    // Save all registers that can hold arguments in the fast path.
 | |
|    pushl %eax
 | |
|    pushl %ecx
 | |
|    pushl %edx
 | |
|    subl MACRO_LITERAL(4), %esp
 | |
|    movss %xmm0, (%esp)
 | |
|    .if \is_interface
 | |
|    // Alignment.
 | |
|    subl MACRO_LITERAL(8), %esp
 | |
|    movzwl 2(rPC), %eax
 | |
|    pushl %eax
 | |
|    // Caller is at 16 (parameters) + 8 (alignment) + 4 (second argument).
 | |
|    pushl 28(%esp)
 | |
|    call SYMBOL(NterpGetShortyFromMethodId)
 | |
|    movl %eax, \dest
 | |
|    movss 16(%esp), %xmm0
 | |
|    addl MACRO_LITERAL(20), %esp
 | |
|    .else
 | |
|    // Alignment.
 | |
|    subl MACRO_LITERAL(12), %esp
 | |
|    pushl %eax
 | |
|    call SYMBOL(NterpGetShorty)
 | |
|    movl %eax, \dest
 | |
|    movss 16(%esp), %xmm0
 | |
|    addl MACRO_LITERAL(20), %esp
 | |
|    .endif
 | |
|    popl %edx
 | |
|    popl %ecx
 | |
|    popl %eax
 | |
| .endm
 | |
| 
 | |
| // Uses ecx and edx as temporary
 | |
| .macro UPDATE_REGISTERS_FOR_STRING_INIT old_value, new_value
 | |
|    movl rREFS, %edx
 | |
|    movl rFP, %ecx
 | |
| 1:
 | |
|    cmpl (%edx), \old_value
 | |
|    jne 2f
 | |
|    movl \new_value, (%edx)
 | |
|    movl \new_value, (%ecx)
 | |
| 2:
 | |
|    addl $$4, %edx
 | |
|    addl $$4, %ecx
 | |
|    cmpl %edx, rFP
 | |
|    jne 1b
 | |
| .endm
 | |
| 
 | |
| .macro DO_CALL is_polymorphic, is_custom
 | |
|    .if \is_polymorphic
 | |
|    call SYMBOL(art_quick_invoke_polymorphic)
 | |
|    .elseif \is_custom
 | |
|    call SYMBOL(art_quick_invoke_custom)
 | |
|    .else
 | |
|    call *ART_METHOD_QUICK_CODE_OFFSET_32(%eax)
 | |
|    .endif
 | |
| .endm
 | |
| 
 | |
| .macro COMMON_INVOKE_NON_RANGE is_static=0, is_interface=0, suffix="", is_string_init=0, is_polymorphic=0, is_custom=0
 | |
|    .if \is_polymorphic
 | |
|    // No fast path for polymorphic calls.
 | |
|    .elseif \is_custom
 | |
|    // No fast path for custom calls.
 | |
|    .elseif \is_string_init
 | |
|    // No fast path for string.init.
 | |
|    .else
 | |
|      testl $$ART_METHOD_NTERP_INVOKE_FAST_PATH_FLAG, ART_METHOD_ACCESS_FLAGS_OFFSET(%eax)
 | |
|      je .Lfast_path_with_few_args_\suffix
 | |
|      movzbl 1(rPC), %edx
 | |
|      movl %edx, %ebx
 | |
|      shrl MACRO_LITERAL(4), %ebx # Number of arguments
 | |
|      .if \is_static
 | |
|      jz .Linvoke_fast_path_\suffix  # shl sets the Z flag
 | |
|      .else
 | |
|      cmpl MACRO_LITERAL(1), %ebx
 | |
|      je .Linvoke_fast_path_\suffix
 | |
|      .endif
 | |
|      movzwl 4(rPC), %esi
 | |
|      cmpl MACRO_LITERAL(2), %ebx
 | |
|      .if \is_static
 | |
|      jl .Lone_arg_fast_path_\suffix
 | |
|      .endif
 | |
|      je .Ltwo_args_fast_path_\suffix
 | |
|      cmpl MACRO_LITERAL(4), %ebx
 | |
|      jl .Lthree_args_fast_path_\suffix
 | |
|      je .Lfour_args_fast_path_\suffix
 | |
| 
 | |
|      andl        MACRO_LITERAL(0xf), %edx
 | |
|      GET_VREG    %edx, %edx
 | |
|      movl        %edx, (4 + 4 * 4)(%esp)
 | |
| .Lfour_args_fast_path_\suffix:
 | |
|      movl        %esi, %edx
 | |
|      shrl        MACRO_LITERAL(12), %edx
 | |
|      GET_VREG    %edx, %edx
 | |
|      movl        %edx, (4 + 3 * 4)(%esp)
 | |
| .Lthree_args_fast_path_\suffix:
 | |
|      movl        %esi, %ebx
 | |
|      shrl        MACRO_LITERAL(8), %ebx
 | |
|      andl        MACRO_LITERAL(0xf), %ebx
 | |
|      GET_VREG    %ebx, %ebx
 | |
| .Ltwo_args_fast_path_\suffix:
 | |
|      movl        %esi, %edx
 | |
|      shrl        MACRO_LITERAL(4), %edx
 | |
|      andl        MACRO_LITERAL(0xf), %edx
 | |
|      GET_VREG    %edx, %edx
 | |
| .Lone_arg_fast_path_\suffix:
 | |
|      .if \is_static
 | |
|      andl        MACRO_LITERAL(0xf), %esi
 | |
|      GET_VREG    %ecx, %esi
 | |
|      .else
 | |
|      // First argument already in %ecx.
 | |
|      .endif
 | |
| .Linvoke_fast_path_\suffix:
 | |
|      // Fetch PC before calling for proper stack unwinding.
 | |
|      FETCH_PC
 | |
|      call *ART_METHOD_QUICK_CODE_OFFSET_32(%eax) // Call the method.
 | |
|      // In case of a long return, save the high half into LOCAL0
 | |
|      SAVE_WIDE_RETURN
 | |
|      RESTORE_IBASE
 | |
|      ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
 | |
| 
 | |
| .Lfast_path_with_few_args_\suffix:
 | |
|      // Fast path when we have zero or one argument (modulo 'this'). If there
 | |
|      // is one argument, we can put it in both floating point and core register.
 | |
|      movzbl 1(rPC), %edx
 | |
|      shrl MACRO_LITERAL(4), %edx # Number of arguments
 | |
|      .if \is_static
 | |
|      cmpl MACRO_LITERAL(1), %edx
 | |
|      jl .Linvoke_with_few_args_\suffix
 | |
|      jne .Lget_shorty_\suffix
 | |
|      movzwl 4(rPC), %ecx
 | |
|      andl MACRO_LITERAL(0xf), %ecx  // dex register of first argument
 | |
|      GET_VREG %ecx, %ecx
 | |
|      movd %ecx, %xmm0
 | |
|      .else
 | |
|      cmpl MACRO_LITERAL(2), %edx
 | |
|      jl .Linvoke_with_few_args_\suffix
 | |
|      jne .Lget_shorty_\suffix
 | |
|      movzwl 4(rPC), %edx
 | |
|      shrl MACRO_LITERAL(4), %edx
 | |
|      andl MACRO_LITERAL(0xf), %edx  // dex register of second argument
 | |
|      GET_VREG %edx, %edx
 | |
|      movd %edx, %xmm0
 | |
|      .endif
 | |
| .Linvoke_with_few_args_\suffix:
 | |
|      // Check if the next instruction is move-result or move-result-wide.
 | |
|      // If it is, we fetch the shorty and jump to the regular invocation.
 | |
|      movzwl  6(rPC), %ebx
 | |
|      andl MACRO_LITERAL(0xfe), %ebx
 | |
|      cmpl MACRO_LITERAL(0x0a), %ebx
 | |
|      je .Lget_shorty_and_invoke_\suffix
 | |
|      call *ART_METHOD_QUICK_CODE_OFFSET_32(%eax) // Call the method.
 | |
|      RESTORE_IBASE
 | |
|      ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
 | |
| .Lget_shorty_and_invoke_\suffix:
 | |
|      GET_SHORTY_SLOW_PATH %esi, \is_interface
 | |
|      jmp .Lgpr_setup_finished_\suffix
 | |
|    .endif
 | |
| 
 | |
| .Lget_shorty_\suffix:
 | |
|    GET_SHORTY %ebx, \is_interface, \is_polymorphic, \is_custom
 | |
|    movl %eax, LOCAL0(%esp)
 | |
|    movl %ebp, LOCAL1(%esp)
 | |
|    movl %ebx, LOCAL2(%esp)
 | |
|    // From this point:
 | |
|    // - ebx contains shorty (in callee-save to switch over return value after call).
 | |
|    // - eax, edx, and ebp are available
 | |
|    // - ecx contains 'this' pointer for instance method.
 | |
|    // TODO: ebp/rREFS is used for stack unwinding, can we find a way to preserve it?
 | |
|    leal 1(%ebx), %edx  // shorty + 1  ; ie skip return arg character
 | |
|    movzwl 4(rPC), %ebx // arguments
 | |
|    .if \is_string_init
 | |
|    shrl MACRO_LITERAL(4), %ebx
 | |
|    movl $$1, %ebp       // ignore first argument
 | |
|    .elseif \is_static
 | |
|    movl $$0, %ebp       // arg_index
 | |
|    .else
 | |
|    shrl MACRO_LITERAL(4), %ebx
 | |
|    movl $$1, %ebp       // arg_index
 | |
|    .endif
 | |
|    LOOP_OVER_SHORTY_LOADING_XMMS xmm0, ebx, edx, ebp, .Lxmm_setup_finished_\suffix
 | |
|    LOOP_OVER_SHORTY_LOADING_XMMS xmm1, ebx, edx, ebp, .Lxmm_setup_finished_\suffix
 | |
|    LOOP_OVER_SHORTY_LOADING_XMMS xmm2, ebx, edx, ebp, .Lxmm_setup_finished_\suffix
 | |
|    LOOP_OVER_SHORTY_LOADING_XMMS xmm3, ebx, edx, ebp, .Lxmm_setup_finished_\suffix
 | |
|    // We know this can only be a float.
 | |
|    movb (%edx), %al                        // al := *shorty
 | |
|    cmpb MACRO_LITERAL(70), %al             // if (al != 'F') goto finished
 | |
|    jne .Lxmm_setup_finished_\suffix
 | |
|    movzbl 1(rPC), %eax
 | |
|    andl MACRO_LITERAL(0xf), %eax
 | |
|    GET_VREG %eax, %eax
 | |
|    // Add four for the ArtMethod.
 | |
|    movl %eax, 4(%esp, %ebp, 4)
 | |
|    // We know there is no more argument, jump to the call.
 | |
|    jmp .Lrestore_saved_values_\suffix
 | |
| .Lxmm_setup_finished_\suffix:
 | |
|    // Reload rREFS for fetching the PC.
 | |
|    movl LOCAL1(%esp), %ebp
 | |
|    // Reload shorty
 | |
|    movl LOCAL2(%esp), %ebx
 | |
|    FETCH_PC
 | |
|    leal 1(%ebx), %ebx  // shorty + 1  ; ie skip return arg character
 | |
|    movzwl 4(rPC), %esi // arguments
 | |
|    .if \is_string_init
 | |
|    movl $$0, %ebp       // arg_index
 | |
|    shrl MACRO_LITERAL(4), %esi
 | |
|    LOOP_OVER_SHORTY_LOADING_GPRS ecx, edx, esi, ebx, ebp, .Lrestore_saved_values_\suffix, .Lif_long_ebx_\suffix, is_ebx=0
 | |
|    .elseif \is_static
 | |
|    movl $$0, %ebp       // arg_index
 | |
|    LOOP_OVER_SHORTY_LOADING_GPRS ecx, edx, esi, ebx, ebp, .Lrestore_saved_values_\suffix, .Lif_long_ebx_\suffix, is_ebx=0
 | |
|    .else
 | |
|    shrl MACRO_LITERAL(4), %esi
 | |
|    movl $$1, %ebp       // arg_index
 | |
|    .endif
 | |
|    // For long argument, store second half in eax to not overwrite the shorty.
 | |
|    LOOP_OVER_SHORTY_LOADING_GPRS edx, eax, esi, ebx, ebp, .Lrestore_saved_values_\suffix, .Lif_long_\suffix, is_ebx=0
 | |
| .Lif_long_ebx_\suffix:
 | |
|    // Store in eax to not overwrite the shorty.
 | |
|    LOOP_OVER_SHORTY_LOADING_GPRS eax, eax, esi, ebx, ebp, .Lrestore_saved_values_\suffix, .Lif_long_\suffix, is_ebx=1
 | |
| .Lif_long_\suffix:
 | |
|    // Save shorty, as LOOP_OVER_SHORTY_LOADING_INTS might overwrite the LOCAL2 slot for a long argument.
 | |
|    pushl LOCAL2(%esp)
 | |
|    pushl %eax
 | |
|    LOOP_OVER_SHORTY_LOADING_INTS 8, ebx, esi, ebp, .Lrestore_ebx_\suffix, \is_string_init
 | |
| .Lrestore_ebx_\suffix:
 | |
|    popl %ebx
 | |
|    popl %esi
 | |
|    movl LOCAL0(%esp), %eax
 | |
|    movl LOCAL1(%esp), %ebp
 | |
|    jmp .Lgpr_setup_finished_\suffix
 | |
| .Lrestore_saved_values_\suffix:
 | |
|    movl LOCAL0(%esp), %eax
 | |
|    movl LOCAL1(%esp), %ebp
 | |
|    movl LOCAL2(%esp), %esi
 | |
| .Lgpr_setup_finished_\suffix:
 | |
|    // Look at the shorty now, as we'll want %esi to have the PC for proper stack unwinding
 | |
|    // and we're running out of callee-save registers.
 | |
|    cmpb LITERAL(68), (%esi)       // Test if result type char == 'D'.
 | |
|    je .Linvoke_double_\suffix
 | |
|    cmpb LITERAL(70), (%esi)       // Test if result type char == 'F'.
 | |
|    je .Linvoke_float_\suffix
 | |
|    FETCH_PC
 | |
|    DO_CALL \is_polymorphic, \is_custom
 | |
|    SAVE_WIDE_RETURN
 | |
| .Ldone_return_\suffix:
 | |
|    /* resume execution of caller */
 | |
|    .if \is_string_init
 | |
|    movzwl 4(rPC), %ecx // arguments
 | |
|    andl $$0xf, %ecx
 | |
|    GET_VREG rINST, %ecx
 | |
|    UPDATE_REGISTERS_FOR_STRING_INIT rINST, %eax
 | |
|    .endif
 | |
|    RESTORE_IBASE
 | |
| 
 | |
|    .if \is_polymorphic
 | |
|    ADVANCE_PC_FETCH_AND_GOTO_NEXT 4
 | |
|    .else
 | |
|    ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
 | |
|    .endif
 | |
| 
 | |
| .Linvoke_double_\suffix:
 | |
|    FETCH_PC
 | |
|    DO_CALL \is_polymorphic, \is_custom
 | |
|    movq %xmm0, LOCAL1(%esp)
 | |
|    movl LOCAL1(%esp), %eax
 | |
|    jmp .Ldone_return_\suffix
 | |
| .Linvoke_float_\suffix:
 | |
|    FETCH_PC
 | |
|    DO_CALL \is_polymorphic, \is_custom
 | |
|    movd %xmm0, %eax
 | |
|    jmp .Ldone_return_\suffix
 | |
| .endm
 | |
| 
 | |
| .macro COMMON_INVOKE_RANGE is_static=0, is_interface=0, suffix="", is_string_init=0, is_polymorphic=0, is_custom=0
 | |
|    .if \is_polymorphic
 | |
|    // No fast path for polymorphic calls.
 | |
|    .elseif \is_custom
 | |
|    // No fast path for custom calls.
 | |
|    .elseif \is_string_init
 | |
|    // No fast path for string.init.
 | |
|    .else
 | |
|      testl $$ART_METHOD_NTERP_INVOKE_FAST_PATH_FLAG, ART_METHOD_ACCESS_FLAGS_OFFSET(%eax)
 | |
|      je .Lfast_path_with_few_args_range_\suffix
 | |
|      movzbl 1(rPC), %edx  // number of arguments
 | |
|      .if \is_static
 | |
|      testl %edx, %edx
 | |
|      je .Linvoke_fast_path_range_\suffix
 | |
|      .else
 | |
|      cmpl MACRO_LITERAL(1), %edx
 | |
|      je .Linvoke_fast_path_range_\suffix
 | |
|      .endif
 | |
|      movzwl 4(rPC), %ebx  // dex register of first argument
 | |
|      leal (rFP, %ebx, 4), %esi  // location of first dex register value
 | |
|      cmpl MACRO_LITERAL(2), %edx
 | |
|      .if \is_static
 | |
|      jl .Lone_arg_fast_path_range_\suffix
 | |
|      .endif
 | |
|      je .Ltwo_args_fast_path_range_\suffix
 | |
|      cmp MACRO_LITERAL(4), %edx
 | |
|      jl .Lthree_args_fast_path_range_\suffix
 | |
| 
 | |
| .Lloop_over_fast_path_range_\suffix:
 | |
|      subl MACRO_LITERAL(1), %edx
 | |
|      movl (%esi, %edx, 4), %ebx
 | |
|      movl %ebx, 4(%esp, %edx, 4)  // Add 4 for the ArtMethod
 | |
|      cmpl MACRO_LITERAL(3), %edx
 | |
|      jne .Lloop_over_fast_path_range_\suffix
 | |
| 
 | |
| .Lthree_args_fast_path_range_\suffix:
 | |
|      movl 8(%esi), %ebx
 | |
| .Ltwo_args_fast_path_range_\suffix:
 | |
|      movl 4(%esi), %edx
 | |
| .Lone_arg_fast_path_range_\suffix:
 | |
|      .if \is_static
 | |
|      movl 0(%esi), %ecx
 | |
|      .else
 | |
|      // First argument already in %ecx.
 | |
|      .endif
 | |
| .Linvoke_fast_path_range_\suffix:
 | |
|      FETCH_PC
 | |
|      call *ART_METHOD_QUICK_CODE_OFFSET_32(%eax) // Call the method.
 | |
|      SAVE_WIDE_RETURN
 | |
|      RESTORE_IBASE
 | |
|      ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
 | |
| 
 | |
| .Lfast_path_with_few_args_range_\suffix:
 | |
|      // Fast path when we have zero or one argument (modulo 'this'). If there
 | |
|      // is one argument, we can put it in both floating point and core register.
 | |
|      movzbl 1(rPC), %ebx # Number of arguments
 | |
|      .if \is_static
 | |
|      cmpl MACRO_LITERAL(1), %ebx
 | |
|      jl .Linvoke_with_few_args_range_\suffix
 | |
|      jne .Lget_shorty_range_\suffix
 | |
|      movzwl 4(rPC), %ebx  // Dex register of first argument
 | |
|      GET_VREG %ecx, %ebx
 | |
|      movd %ecx, %xmm0
 | |
|      .else
 | |
|      cmpl MACRO_LITERAL(2), %ebx
 | |
|      jl .Linvoke_with_few_args_range_\suffix
 | |
|      jne .Lget_shorty_range_\suffix
 | |
|      movzwl 4(rPC), %ebx
 | |
|      addl MACRO_LITERAL(1), %ebx  // dex register of second argument
 | |
|      GET_VREG %edx, %ebx
 | |
|      movd %edx, %xmm0
 | |
|      .endif
 | |
| .Linvoke_with_few_args_range_\suffix:
 | |
|      // Check if the next instruction is move-result or move-result-wide.
 | |
|      // If it is, we fetch the shorty and jump to the regular invocation.
 | |
|      movzwl  6(rPC), %ebx
 | |
|      and MACRO_LITERAL(0xfe), %ebx
 | |
|      cmpl MACRO_LITERAL(0x0a), %ebx
 | |
|      je .Lget_shorty_and_invoke_range_\suffix
 | |
|      call *ART_METHOD_QUICK_CODE_OFFSET_32(%eax) // Call the method.
 | |
|      RESTORE_IBASE
 | |
|      ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
 | |
| .Lget_shorty_and_invoke_range_\suffix:
 | |
|      GET_SHORTY_SLOW_PATH %esi, \is_interface
 | |
|      jmp .Lgpr_setup_finished_range_\suffix
 | |
|    .endif
 | |
| 
 | |
| .Lget_shorty_range_\suffix:
 | |
|    GET_SHORTY %ebx, \is_interface, \is_polymorphic, \is_custom
 | |
|    movl %eax, LOCAL0(%esp)
 | |
|    movl %ebp, LOCAL1(%esp)
 | |
|    movl %ebx, LOCAL2(%esp)
 | |
|    // From this point:
 | |
|    // - ebx contains shorty (in callee-save to switch over return value after call).
 | |
|    // - eax, edx, ebx, and ebp are available.
 | |
|    // - ecx contains 'this' pointer for instance method.
 | |
|    // TODO: ebp/rREFS is used for stack unwinding, can we find a way to preserve it?
 | |
|    leal 1(%ebx), %edx  // shorty + 1  ; ie skip return arg character
 | |
|    movzwl 4(rPC), %ebx // arg start index
 | |
|    .if \is_string_init
 | |
|    addl $$1, %ebx       // arg start index
 | |
|    movl $$0, %ebp       // index in stack
 | |
|    .elseif \is_static
 | |
|    movl $$0, %ebp       // index in stack
 | |
|    .else
 | |
|    addl $$1, %ebx       // arg start index
 | |
|    movl $$1, %ebp       // index in stack
 | |
|    .endif
 | |
|    LOOP_RANGE_OVER_SHORTY_LOADING_XMMS xmm0, edx, ebx, ebp, .Lxmm_setup_finished_range_\suffix
 | |
|    LOOP_RANGE_OVER_SHORTY_LOADING_XMMS xmm1, edx, ebx, ebp, .Lxmm_setup_finished_range_\suffix
 | |
|    LOOP_RANGE_OVER_SHORTY_LOADING_XMMS xmm2, edx, ebx, ebp, .Lxmm_setup_finished_range_\suffix
 | |
|    LOOP_RANGE_OVER_SHORTY_LOADING_XMMS xmm3, edx, ebx, ebp, .Lxmm_setup_finished_range_\suffix
 | |
|    LOOP_RANGE_OVER_FPs edx, ebx, ebp, .Lxmm_setup_finished_range_\suffix
 | |
| .Lxmm_setup_finished_range_\suffix:
 | |
|    // Reload rREFS for fetching the PC.
 | |
|    movl LOCAL1(%esp), %ebp
 | |
|    // Reload shorty
 | |
|    movl LOCAL2(%esp), %ebx
 | |
|    FETCH_PC
 | |
|    leal 1(%ebx), %ebx  // shorty + 1  ; ie skip return arg character
 | |
|    // From this point:
 | |
|    // - ebx contains shorty
 | |
|    // - eax and ebp are available.
 | |
|    // - ecx contains 'this' pointer for instance method.
 | |
|    movzwl 4(rPC), %ebp // arg start index
 | |
|    // rPC (esi) is now available
 | |
|    .if \is_string_init
 | |
|    addl $$1, %ebp       // arg start index
 | |
|    movl $$0, %esi       // index in stack
 | |
|    LOOP_RANGE_OVER_SHORTY_LOADING_GPRS ecx, edx, ebx, ebp, esi, .Lrestore_saved_values_range_\suffix, .Lif_long_ebx_range_\suffix, is_ebx=0
 | |
|    .elseif \is_static
 | |
|    movl $$0, %esi // index in stack
 | |
|    LOOP_RANGE_OVER_SHORTY_LOADING_GPRS ecx, edx, ebx, ebp, esi, .Lrestore_saved_values_range_\suffix, .Lif_long_ebx_range_\suffix, is_ebx=0
 | |
|    .else
 | |
|    addl $$1, %ebp // arg start index
 | |
|    movl $$1, %esi // index in stack
 | |
|    .endif
 | |
|    // For long argument, store second half in eax to not overwrite the shorty.
 | |
|    LOOP_RANGE_OVER_SHORTY_LOADING_GPRS edx, eax, ebx, ebp, esi, .Lrestore_saved_values_range_\suffix, .Lif_long_range_\suffix, is_ebx=0
 | |
| .Lif_long_ebx_range_\suffix:
 | |
|    // Store in eax to not overwrite the shorty.
 | |
|    LOOP_RANGE_OVER_SHORTY_LOADING_GPRS eax, eax, ebx, ebp, esi, .Lrestore_saved_values_range_\suffix, .Lif_long_range_\suffix, is_ebx=1
 | |
| .Lif_long_range_\suffix:
 | |
|    // Save shorty, as LOOP_RANGE_OVER_SHORTY_LOADING_INTS might overwrite the LOCAL2 slot for a long argument.
 | |
|    pushl LOCAL2(%esp)
 | |
|    pushl %eax
 | |
|    LOOP_RANGE_OVER_INTs 8, ebx, ebp, esi, .Lrestore_ebx_range_\suffix
 | |
| .Lrestore_ebx_range_\suffix:
 | |
|    popl %ebx
 | |
|    popl %esi
 | |
|    movl LOCAL0(%esp), %eax
 | |
|    movl LOCAL1(%esp), %ebp
 | |
|    jmp .Lgpr_setup_finished_range_\suffix
 | |
| 
 | |
| .Lrestore_saved_values_range_\suffix:
 | |
|    movl LOCAL0(%esp), %eax
 | |
|    movl LOCAL1(%esp), %ebp
 | |
|    // Save shorty in callee-save register
 | |
|    movl LOCAL2(%esp), %esi
 | |
| 
 | |
| .Lgpr_setup_finished_range_\suffix:
 | |
|    cmpb LITERAL(68), (%esi)       // Test if result type char == 'D'.
 | |
|    je .Lreturn_range_double_\suffix
 | |
|    cmpb LITERAL(70), (%esi)       // Test if result type char == 'F'.
 | |
|    je .Lreturn_range_float_\suffix
 | |
| 
 | |
|    FETCH_PC
 | |
|    DO_CALL \is_polymorphic, \is_custom
 | |
|    SAVE_WIDE_RETURN
 | |
| .Ldone_return_range_\suffix:
 | |
|    /* resume execution of caller */
 | |
|    .if \is_string_init
 | |
|    movzwl 4(rPC), %ecx // arguments
 | |
|    GET_VREG rINST, %ecx
 | |
|    UPDATE_REGISTERS_FOR_STRING_INIT rINST, %eax
 | |
|    .endif
 | |
|    RESTORE_IBASE
 | |
|    .if \is_polymorphic
 | |
|    ADVANCE_PC_FETCH_AND_GOTO_NEXT 4
 | |
|    .else
 | |
|    ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
 | |
|    .endif
 | |
| .Lreturn_range_double_\suffix:
 | |
|    FETCH_PC
 | |
|    DO_CALL \is_polymorphic, \is_custom
 | |
|    movq %xmm0, LOCAL1(%esp)
 | |
|    movl LOCAL1(%esp), %eax
 | |
|    jmp .Ldone_return_range_\suffix
 | |
| .Lreturn_range_float_\suffix:
 | |
|    FETCH_PC
 | |
|    DO_CALL \is_polymorphic, \is_custom
 | |
|    movd %xmm0, %eax
 | |
|    jmp .Ldone_return_range_\suffix
 | |
| .endm
 | |
| 
 | |
| // Helper for static field get.
 | |
| .macro OP_SGET load="movl", wide="0"
 | |
|    // Fast-path which gets the field from thread-local cache.
 | |
| %  fetch_from_thread_cache("%eax", miss_label="2f")
 | |
| 1:
 | |
|    movl ART_FIELD_OFFSET_OFFSET(%eax), %ecx
 | |
|    movl ART_FIELD_DECLARING_CLASS_OFFSET(%eax), %eax
 | |
|    cmpl $$0, rSELF:THREAD_READ_BARRIER_MARK_REG00_OFFSET
 | |
|    jne 3f
 | |
| 4:
 | |
|    .if \wide
 | |
|    addl %ecx, %eax
 | |
|    \load (%eax), %ecx
 | |
|    SET_VREG %ecx, rINST            # fp[A] <- value
 | |
|    \load 4(%eax), %ecx
 | |
|    SET_VREG_HIGH %ecx, rINST
 | |
|    .else
 | |
|    \load (%eax, %ecx, 1), %eax
 | |
|    SET_VREG %eax, rINST            # fp[A] <- value
 | |
|    .endif
 | |
|    ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
 | |
| 2:
 | |
|    EXPORT_PC
 | |
|    movl rSELF:THREAD_SELF_OFFSET, ARG0
 | |
|    movl 0(%esp), ARG1
 | |
|    movl rPC, ARG2
 | |
|    movl $$0, ARG3
 | |
|    call nterp_get_static_field
 | |
|    .if !\wide
 | |
|    CLEAR_VOLATILE_MARKER %eax
 | |
|    jmp 1b
 | |
|    .else
 | |
|    testl MACRO_LITERAL(1), %eax
 | |
|    je 1b
 | |
|    CLEAR_VOLATILE_MARKER %eax
 | |
|    movl ART_FIELD_OFFSET_OFFSET(%eax), %ecx
 | |
|    movl ART_FIELD_DECLARING_CLASS_OFFSET(%eax), %eax
 | |
|    cmpl $$0, rSELF:THREAD_READ_BARRIER_MARK_REG00_OFFSET
 | |
|    jne 5f
 | |
| 6:
 | |
|    movsd (%eax, %ecx, 1), %xmm0
 | |
|    SET_WIDE_FP_VREG %xmm0, rINST
 | |
|    ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
 | |
| 5:
 | |
|    call art_quick_read_barrier_mark_reg00
 | |
|    jmp 6b
 | |
|    .endif
 | |
| 3:
 | |
|    call art_quick_read_barrier_mark_reg00
 | |
|    jmp 4b
 | |
| .endm
 | |
| 
 | |
| // Helper for static field put.
 | |
| .macro OP_SPUT rINST_reg="rINST", store="movl", wide="0":
 | |
|    // Fast-path which gets the field from thread-local cache.
 | |
| %  fetch_from_thread_cache("%eax", miss_label="2f")
 | |
| 1:
 | |
|    movl ART_FIELD_OFFSET_OFFSET(%eax), %ecx
 | |
|    movl ART_FIELD_DECLARING_CLASS_OFFSET(%eax), %eax
 | |
|    cmpl $$0, rSELF:THREAD_READ_BARRIER_MARK_REG00_OFFSET
 | |
|    jne 3f
 | |
| 4:
 | |
|    .if \wide
 | |
|    addl %ecx, %eax
 | |
|    GET_VREG %ecx, rINST                  # rINST <- v[A]
 | |
|    movl %ecx, (%eax)
 | |
|    GET_VREG_HIGH %ecx, rINST
 | |
|    movl %ecx, 4(%eax)
 | |
|    .else
 | |
|    GET_VREG rINST, rINST                  # rINST <- v[A]
 | |
|    \store    \rINST_reg, (%eax,%ecx,1)
 | |
|    .endif
 | |
|    ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
 | |
| 2:
 | |
|    EXPORT_PC
 | |
|    movl rSELF:THREAD_SELF_OFFSET, ARG0
 | |
|    movl 0(%esp), ARG1
 | |
|    movl rPC, ARG2
 | |
|    movl $$0, ARG3
 | |
|    call nterp_get_static_field
 | |
|    testl MACRO_LITERAL(1), %eax
 | |
|    je 1b
 | |
|    // Clear the marker that we put for volatile fields.
 | |
|    CLEAR_VOLATILE_MARKER %eax
 | |
|    movl ART_FIELD_OFFSET_OFFSET(%eax), %ecx
 | |
|    movl ART_FIELD_DECLARING_CLASS_OFFSET(%eax), %eax
 | |
|    cmpl $$0, rSELF:THREAD_READ_BARRIER_MARK_REG00_OFFSET
 | |
|    jne 6f
 | |
| 5:
 | |
|    .if \wide
 | |
|    addl %ecx, %eax
 | |
|    GET_WIDE_FP_VREG %xmm0, rINST
 | |
|    movsd %xmm0, (%eax)
 | |
|    .else
 | |
|    GET_VREG rINST, rINST                  # rINST <- v[A]
 | |
|    \store    \rINST_reg, (%eax,%ecx,1)
 | |
|    .endif
 | |
|    lock addl $$0, (%esp)
 | |
|    ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
 | |
| 3:
 | |
|    call art_quick_read_barrier_mark_reg00
 | |
|    jmp 4b
 | |
| 6:
 | |
|    call art_quick_read_barrier_mark_reg00
 | |
|    jmp 5b
 | |
| .endm
 | |
| 
 | |
| 
 | |
| .macro OP_IPUT_INTERNAL rINST_reg="rINST", store="movl", wide="0", volatile="0":
 | |
|    movzbl  rINSTbl, %ecx                   # ecx <- BA
 | |
|    sarl    $$4, %ecx                       # ecx <- B
 | |
|    GET_VREG %ecx, %ecx                     # vB (object we're operating on)
 | |
|    testl   %ecx, %ecx                      # is object null?
 | |
|    je      common_errNullObject
 | |
|    andb    $$0xf, rINSTbl                  # rINST <- A
 | |
|    .if \wide
 | |
|    addl %ecx, %eax
 | |
|    GET_WIDE_FP_VREG %xmm0, rINST
 | |
|    movsd %xmm0, (%eax)
 | |
|    .else
 | |
|    GET_VREG rINST, rINST                  # rINST <- v[A]
 | |
|    \store \rINST_reg, (%ecx,%eax,1)
 | |
|    .endif
 | |
| .endm
 | |
| 
 | |
| // Helper for instance field put.
 | |
| .macro OP_IPUT rINST_reg="rINST", store="movl", wide="0":
 | |
|    // Fast-path which gets the field from thread-local cache.
 | |
| %  fetch_from_thread_cache("%eax", miss_label="2f")
 | |
| 1:
 | |
|    OP_IPUT_INTERNAL \rINST_reg, \store, \wide, volatile=0
 | |
|    ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
 | |
| 2:
 | |
|    EXPORT_PC
 | |
|    movl rSELF:THREAD_SELF_OFFSET, ARG0
 | |
|    movl 0(%esp), ARG1
 | |
|    movl rPC, ARG2
 | |
|    movl $$0, ARG3
 | |
|    call nterp_get_instance_field_offset
 | |
|    testl %eax, %eax
 | |
|    jns 1b
 | |
|    negl %eax
 | |
|    OP_IPUT_INTERNAL \rINST_reg, \store, \wide, volatile=1
 | |
|    lock addl $$0, (%esp)
 | |
|    ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
 | |
| .endm
 | |
| 
 | |
| // Helper for instance field get.
 | |
| .macro OP_IGET load="movl", wide="0"
 | |
|    // Fast-path which gets the field from thread-local cache.
 | |
| %  fetch_from_thread_cache("%eax", miss_label="2f")
 | |
| 1:
 | |
|    movl    rINST, %ecx                     # ecx <- BA
 | |
|    sarl    $$4, %ecx                       # ecx <- B
 | |
|    GET_VREG %ecx, %ecx                     # vB (object we're operating on)
 | |
|    testl   %ecx, %ecx                      # is object null?
 | |
|    je      common_errNullObject
 | |
|    andb    $$0xf,rINSTbl                   # rINST <- A
 | |
|    .if \wide
 | |
|    addl %ecx, %eax
 | |
|    \load (%eax), %ecx
 | |
|    SET_VREG %ecx, rINST
 | |
|    \load 4(%eax), %ecx
 | |
|    SET_VREG_HIGH %ecx, rINST
 | |
|    .else
 | |
|    \load (%ecx,%eax,1), %eax
 | |
|    SET_VREG %eax, rINST                    # fp[A] <- value
 | |
|    .endif
 | |
|    ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
 | |
| 2:
 | |
|    EXPORT_PC
 | |
|    movl rSELF:THREAD_SELF_OFFSET, ARG0
 | |
|    movl 0(%esp), ARG1
 | |
|    movl rPC, ARG2
 | |
|    movl $$0, ARG3
 | |
|    call nterp_get_instance_field_offset
 | |
|    testl %eax, %eax
 | |
|    jns 1b
 | |
|    negl %eax
 | |
|    .if !\wide
 | |
|    jmp 1b
 | |
|    .else
 | |
|    movl    rINST, %ecx                     # ecx <- BA
 | |
|    sarl    $$4, %ecx                       # ecx <- B
 | |
|    GET_VREG %ecx, %ecx                     # vB (object we're operating on)
 | |
|    testl   %ecx, %ecx                      # is object null?
 | |
|    je      common_errNullObject
 | |
|    andb    $$0xf,rINSTbl                   # rINST <- A
 | |
|    movsd (%eax, %ecx, 1), %xmm0
 | |
|    SET_WIDE_FP_VREG %xmm0, rINST
 | |
|    ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
 | |
|    .endif
 | |
| .endm
 | |
| 
 | |
| // Store a reference parameter into our dex register frame.
 | |
| // Uses xmm4 as temporary.
 | |
| .macro SETUP_REFERENCE_PARAMETER_IN_GPR offset, stack_ptr, regs, refs, ins, arg_offset, finished
 | |
|     movss \offset(REG_VAR(stack_ptr)), %xmm4
 | |
|     movss %xmm4, (REG_VAR(regs), REG_VAR(arg_offset))
 | |
|     movss %xmm4, (REG_VAR(refs), REG_VAR(arg_offset))
 | |
|     addl MACRO_LITERAL(4), REG_VAR(arg_offset)
 | |
|     subl MACRO_LITERAL(1), REG_VAR(ins)
 | |
|     je \finished
 | |
| .endm
 | |
| 
 | |
| // Store a reference parameter into our dex register frame.
 | |
| // Uses xmm4 as temporary.
 | |
| .macro SETUP_REFERENCE_PARAMETERS_IN_STACK stack_ptr, regs, refs, ins, arg_offset
 | |
| 1:
 | |
|     movss OFFSET_TO_FIRST_ARGUMENT_IN_STACK(REG_VAR(stack_ptr), REG_VAR(arg_offset)), %xmm4
 | |
|     movss %xmm4, (REG_VAR(regs), REG_VAR(arg_offset))
 | |
|     movss %xmm4, (REG_VAR(refs), REG_VAR(arg_offset))
 | |
|     addl MACRO_LITERAL(4), REG_VAR(arg_offset)
 | |
|     subl MACRO_LITERAL(1), REG_VAR(ins)
 | |
|     jne 1b
 | |
| .endm
 | |
| 
 | |
| .macro DO_SUSPEND_CHECK continue_label
 | |
|     testl   $$(THREAD_SUSPEND_OR_CHECKPOINT_REQUEST), rSELF:THREAD_FLAGS_OFFSET
 | |
|     jz      \continue_label
 | |
|     jmp     NterpCallSuspendAndGotoNext
 | |
| .endm
 | |
| 
 | |
| .macro CHECK_AND_UPDATE_SHARED_MEMORY_METHOD if_hot, if_not_hot
 | |
|     testl $$ART_METHOD_IS_MEMORY_SHARED_FLAG, ART_METHOD_ACCESS_FLAGS_OFFSET(%eax)
 | |
|     jz \if_hot
 | |
|     movzwl rSELF:THREAD_SHARED_METHOD_HOTNESS_OFFSET, %ecx
 | |
|     testl %ecx, %ecx
 | |
|     je \if_hot
 | |
|     addl $$-1, %ecx
 | |
|     movw %cx, rSELF:THREAD_SHARED_METHOD_HOTNESS_OFFSET
 | |
|     jmp \if_not_hot
 | |
| .endm
 | |
| 
 | |
| 
 | |
| %def entry():
 | |
| /*
 | |
|  * ArtMethod entry point.
 | |
|  *
 | |
|  * On entry:
 | |
|  *  eax   ArtMethod* callee
 | |
|  *  rest  method parameters
 | |
|  */
 | |
| 
 | |
| OAT_ENTRY ExecuteNterpImpl, EndExecuteNterpImpl
 | |
|     .cfi_startproc
 | |
|     .cfi_def_cfa esp, 4
 | |
|     testl %eax, -STACK_OVERFLOW_RESERVED_BYTES(%esp)
 | |
|     // Spill callee save regs
 | |
|     SPILL_ALL_CALLEE_SAVES
 | |
| 
 | |
|     // Make argument registers available.
 | |
|     SPILL_ALL_CORE_PARAMETERS
 | |
| 
 | |
|     // Fetch code item.
 | |
|     movl ART_METHOD_DATA_OFFSET_32(%eax), %ecx
 | |
| 
 | |
|     // Setup the stack for executing the method.
 | |
|     SETUP_STACK_FRAME %ecx, rREFS, rFP, CFI_REFS, load_ins=1
 | |
| 
 | |
|     // Save the PC
 | |
|     movl %ecx, -8(rREFS)
 | |
| 
 | |
|     // Setup the parameters
 | |
|     testl %esi, %esi
 | |
|     je .Lxmm_setup_finished
 | |
| 
 | |
|     subl %esi, %ebx
 | |
|     sall $$2, %ebx // ebx is now the offset for inputs into the registers array.
 | |
| 
 | |
|     // Reload ArtMethod.
 | |
|     movl (%esp), %eax
 | |
|     testl $$ART_METHOD_NTERP_ENTRY_POINT_FAST_PATH_FLAG, ART_METHOD_ACCESS_FLAGS_OFFSET(%eax)
 | |
|     je .Lsetup_slow_path
 | |
|     leal (rREFS, %ebx, 1), %ecx
 | |
|     leal (rFP, %ebx, 1), %ebx
 | |
|     movl $$0, %eax
 | |
| 
 | |
|     // edx is the old stack pointer
 | |
|     SETUP_REFERENCE_PARAMETER_IN_GPR 8, edx, ebx, ecx, esi, eax, .Lxmm_setup_finished
 | |
|     SETUP_REFERENCE_PARAMETER_IN_GPR 4, edx, ebx, ecx, esi, eax, .Lxmm_setup_finished
 | |
|     SETUP_REFERENCE_PARAMETER_IN_GPR 0, edx, ebx, ecx, esi, eax, .Lxmm_setup_finished
 | |
|     SETUP_REFERENCE_PARAMETERS_IN_STACK edx, ebx, ecx, esi, eax
 | |
|     jmp .Lxmm_setup_finished
 | |
| 
 | |
| .Lsetup_slow_path:
 | |
|     // If the method is not static and there is one argument ('this'), we don't need to fetch the
 | |
|     // shorty.
 | |
|     testl $$ART_METHOD_IS_STATIC_FLAG, ART_METHOD_ACCESS_FLAGS_OFFSET(%eax)
 | |
|     jne .Lsetup_with_shorty
 | |
| 
 | |
|     // Record 'this'.
 | |
|     movl 8(%edx), %eax
 | |
|     movl %eax, (rFP, %ebx)
 | |
|     movl %eax, (rREFS, %ebx)
 | |
| 
 | |
|     cmpl $$1, %esi
 | |
|     je .Lxmm_setup_finished
 | |
| 
 | |
| .Lsetup_with_shorty:
 | |
|     // Save xmm registers. Core registers have already been saved.
 | |
|     subl MACRO_LITERAL(4 * 8), %esp
 | |
|     movq %xmm0, 0(%esp)
 | |
|     movq %xmm1, 8(%esp)
 | |
|     movq %xmm2, 16(%esp)
 | |
|     movq %xmm3, 24(%esp)
 | |
|     subl MACRO_LITERAL(12), %esp
 | |
|     pushl (4 * 8 + 12)(%esp)
 | |
|     call SYMBOL(NterpGetShorty)
 | |
|     addl MACRO_LITERAL(16), %esp
 | |
| 
 | |
|     // Restore xmm registers
 | |
|     movq 0(%esp), %xmm0
 | |
|     movq 8(%esp), %xmm1
 | |
|     movq 16(%esp), %xmm2
 | |
|     movq 24(%esp), %xmm3
 | |
|     addl MACRO_LITERAL(4 * 8), %esp
 | |
| 
 | |
|     // Reload the old stack pointer.
 | |
|     movl -4(rREFS), %edx
 | |
|     // TODO: Get shorty in a better way and remove above
 | |
| 
 | |
|     movl $$0, %esi
 | |
|     movl (%esp), %ecx
 | |
|     testl $$ART_METHOD_IS_STATIC_FLAG, ART_METHOD_ACCESS_FLAGS_OFFSET(%ecx)
 | |
| 
 | |
|     // Note the leal and movl below don't change the flags.
 | |
|     leal (rFP, %ebx, 1), %ecx
 | |
|     leal (rREFS, %ebx, 1), %ebx
 | |
|     // Save rFP (%edi), we're using it as temporary below.
 | |
|     movl rFP, LOCAL1(%esp)
 | |
|     leal 1(%eax), %edi  // shorty + 1  ; ie skip return arg character
 | |
|     // Save shorty + 1
 | |
|     movl %edi, LOCAL2(%esp)
 | |
|     jne .Lhandle_static_method
 | |
|     addl $$4, %ecx
 | |
|     addl $$4, %ebx
 | |
|     addl $$4, %edx
 | |
|     LOOP_OVER_SHORTY_STORING_GPRS 0, -4, edx, edi, esi, ecx, ebx, .Lgpr_setup_finished, .Lif_long, is_ebx=0
 | |
|     LOOP_OVER_SHORTY_STORING_GPRS -4, 0, edx, edi, esi, ecx, ebx, .Lgpr_setup_finished, .Lif_long, is_ebx=1
 | |
|     jmp .Lif_long
 | |
| .Lhandle_static_method:
 | |
|     LOOP_OVER_SHORTY_STORING_GPRS 8, 4, edx, edi, esi, ecx, ebx, .Lgpr_setup_finished, .Lif_long_ebx, is_ebx=0
 | |
|     LOOP_OVER_SHORTY_STORING_GPRS 4, 0, edx, edi, esi, ecx, ebx, .Lgpr_setup_finished, .Lif_long, is_ebx=0
 | |
| .Lif_long_ebx:
 | |
|     LOOP_OVER_SHORTY_STORING_GPRS 0, 0, edx, edi, esi, ecx, ebx, .Lgpr_setup_finished, .Lif_long, is_ebx=1
 | |
| .Lif_long:
 | |
|     LOOP_OVER_INTs edi, esi, ecx, ebx, edx, .Lgpr_setup_finished
 | |
| .Lgpr_setup_finished:
 | |
|     // Restore shorty + 1
 | |
|     movl LOCAL2(%esp), %edi
 | |
|     movl $$0, %esi // reset counter
 | |
|     LOOP_OVER_SHORTY_STORING_XMMS xmm0, edi, esi, ecx, .Lrestore_fp
 | |
|     LOOP_OVER_SHORTY_STORING_XMMS xmm1, edi, esi, ecx, .Lrestore_fp
 | |
|     LOOP_OVER_SHORTY_STORING_XMMS xmm2, edi, esi, ecx, .Lrestore_fp
 | |
|     LOOP_OVER_SHORTY_STORING_XMMS xmm3, edi, esi, ecx, .Lrestore_fp
 | |
|     LOOP_OVER_FPs edi, esi, ecx, edx, .Lrestore_fp
 | |
| .Lrestore_fp:
 | |
|     movl LOCAL1(%esp), rFP
 | |
| .Lxmm_setup_finished:
 | |
|     FETCH_PC
 | |
|     CFI_DEFINE_DEX_PC_WITH_OFFSET(CFI_TMP, CFI_DEX, 0)
 | |
|     // Set rIBASE
 | |
|     RESTORE_IBASE
 | |
|     /* start executing the instruction at rPC */
 | |
|     START_EXECUTING_INSTRUCTIONS
 | |
|     /* NOTE: no fallthrough */
 | |
|     // cfi info continues, and covers the whole nterp implementation.
 | |
|     END ExecuteNterpImpl
 | |
| 
 | |
| %def opcode_pre():
 | |
| 
 | |
| %def fetch_from_thread_cache(dest_reg, miss_label):
 | |
|    // Fetch some information from the thread cache.
 | |
|    // Uses eax, and ecx as temporaries.
 | |
|    movl rSELF:THREAD_SELF_OFFSET, %eax
 | |
|    movl rPC, %ecx
 | |
|    sall MACRO_LITERAL(THREAD_INTERPRETER_CACHE_SIZE_SHIFT), %ecx
 | |
|    andl MACRO_LITERAL(THREAD_INTERPRETER_CACHE_SIZE_MASK), %ecx
 | |
|    cmpl THREAD_INTERPRETER_CACHE_OFFSET(%eax, %ecx, 1), rPC
 | |
|    jne  ${miss_label}
 | |
|    movl __SIZEOF_POINTER__+THREAD_INTERPRETER_CACHE_OFFSET(%eax, %ecx, 1), ${dest_reg}
 | |
| 
 | |
| %def footer():
 | |
| /*
 | |
|  * ===========================================================================
 | |
|  *  Common subroutines and data
 | |
|  * ===========================================================================
 | |
|  */
 | |
| 
 | |
|     .text
 | |
|     .align  2
 | |
| 
 | |
| // Enclose all code below in a symbol (which gets printed in backtraces).
 | |
| ENTRY nterp_helper
 | |
| 
 | |
| // Note: mterp also uses the common_* names below for helpers, but that's OK
 | |
| // as the assembler compiled each interpreter separately.
 | |
| common_errDivideByZero:
 | |
|     EXPORT_PC
 | |
|     call art_quick_throw_div_zero
 | |
| 
 | |
| // Expect array in eax, index in ecx.
 | |
| common_errArrayIndex:
 | |
|     EXPORT_PC
 | |
|     movl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %edx
 | |
|     movl %ecx, %eax
 | |
|     movl %edx, %ecx
 | |
|     call art_quick_throw_array_bounds
 | |
| 
 | |
| common_errNullObject:
 | |
|     EXPORT_PC
 | |
|     call art_quick_throw_null_pointer_exception
 | |
| 
 | |
| NterpCommonInvokeStatic:
 | |
|     COMMON_INVOKE_NON_RANGE is_static=1, is_interface=0, suffix="invokeStatic"
 | |
| 
 | |
| NterpCommonInvokeStaticRange:
 | |
|     COMMON_INVOKE_RANGE is_static=1, is_interface=0, suffix="invokeStatic"
 | |
| 
 | |
| NterpCommonInvokeInstance:
 | |
|     COMMON_INVOKE_NON_RANGE is_static=0, is_interface=0, suffix="invokeInstance"
 | |
| 
 | |
| NterpCommonInvokeInstanceRange:
 | |
|     COMMON_INVOKE_RANGE is_static=0, is_interface=0, suffix="invokeInstance"
 | |
| 
 | |
| NterpCommonInvokeInterface:
 | |
|     COMMON_INVOKE_NON_RANGE is_static=0, is_interface=1, suffix="invokeInterface"
 | |
| 
 | |
| NterpCommonInvokeInterfaceRange:
 | |
|     COMMON_INVOKE_RANGE is_static=0, is_interface=1, suffix="invokeInterface"
 | |
| 
 | |
| NterpCommonInvokePolymorphic:
 | |
|     COMMON_INVOKE_NON_RANGE is_static=0, is_interface=0, is_polymorphic=1, suffix="invokePolymorphic"
 | |
| 
 | |
| NterpCommonInvokePolymorphicRange:
 | |
|     COMMON_INVOKE_RANGE is_static=0, is_interface=0, is_polymorphic=1, suffix="invokePolymorphic"
 | |
| 
 | |
| NterpCommonInvokeCustom:
 | |
|     COMMON_INVOKE_NON_RANGE is_static=1, is_interface=0, is_polymorphic=0, is_custom=1, suffix="invokeCustom"
 | |
| 
 | |
| NterpCommonInvokeCustomRange:
 | |
|     COMMON_INVOKE_RANGE is_static=1, is_interface=0, is_polymorphic=0, is_custom=1, suffix="invokeCustom"
 | |
| 
 | |
| NterpHandleStringInit:
 | |
|    COMMON_INVOKE_NON_RANGE is_static=0, is_interface=0, is_string_init=1, suffix="stringInit"
 | |
| 
 | |
| NterpHandleStringInitRange:
 | |
|    COMMON_INVOKE_RANGE is_static=0, is_interface=0, is_string_init=1, suffix="stringInit"
 | |
| 
 | |
| NterpNewInstance:
 | |
|    EXPORT_PC
 | |
|    // Fast-path which gets the class from thread-local cache.
 | |
| %  fetch_from_thread_cache("%eax", miss_label="2f")
 | |
|    cmpl $$0, rSELF:THREAD_READ_BARRIER_MARK_REG00_OFFSET
 | |
|    jne 3f
 | |
| 4:
 | |
|    call *rSELF:THREAD_ALLOC_OBJECT_ENTRYPOINT_OFFSET
 | |
|    RESTORE_IBASE
 | |
|    FETCH_INST_CLEAR_OPCODE
 | |
| 1:
 | |
|    SET_VREG_OBJECT %eax, rINST             # fp[A] <- value
 | |
|    ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
 | |
| 2:
 | |
|    movl rSELF:THREAD_SELF_OFFSET, ARG0
 | |
|    movl 0(%esp), ARG1
 | |
|    movl rPC, ARG2
 | |
|    call nterp_get_class_or_allocate_object
 | |
|    jmp 1b
 | |
| 3:
 | |
|    // 00 is %eax
 | |
|    call art_quick_read_barrier_mark_reg00
 | |
|    jmp 4b
 | |
| 
 | |
| NterpNewArray:
 | |
|    /* new-array vA, vB, class@CCCC */
 | |
|    EXPORT_PC
 | |
|    // Fast-path which gets the class from thread-local cache.
 | |
| %  fetch_from_thread_cache("%eax", miss_label="2f")
 | |
|    cmpl $$0, rSELF:THREAD_READ_BARRIER_MARK_REG00_OFFSET
 | |
|    jne 3f
 | |
| 1:
 | |
|    movzbl  rINSTbl, %ecx
 | |
|    sarl    $$4, %ecx                         # ecx<- B
 | |
|    GET_VREG %ecx %ecx                        # ecx<- vB (array length)
 | |
|    call *rSELF:THREAD_ALLOC_ARRAY_ENTRYPOINT_OFFSET
 | |
|    RESTORE_IBASE
 | |
|    FETCH_INST_CLEAR_OPCODE
 | |
|    andb    $$0xf, rINSTbl                   # rINST<- A
 | |
|    SET_VREG_OBJECT %eax, rINST              # fp[A] <- value
 | |
|    ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
 | |
| 2:
 | |
|    movl rSELF:THREAD_SELF_OFFSET, ARG0
 | |
|    movl 0(%esp), ARG1
 | |
|    movl rPC, ARG2
 | |
|    call nterp_get_class_or_allocate_object
 | |
|    jmp 1b
 | |
| 3:
 | |
|    // 00 is %eax
 | |
|    call art_quick_read_barrier_mark_reg00
 | |
|    jmp 1b
 | |
| 
 | |
| NterpPutObjectInstanceField:
 | |
|    // Fast-path which gets the field from thread-local cache.
 | |
| %  fetch_from_thread_cache("%eax", miss_label="2f")
 | |
| 1:
 | |
|    movl    rINST, %ecx                     # ecx <- BA
 | |
|    andl    $$0xf, %ecx                     # ecx <- A
 | |
|    GET_VREG %ecx, %ecx                     # ecx <- v[A]
 | |
|    sarl    $$4, rINST
 | |
|    GET_VREG rINST, rINST                   # vB (object we're operating on)
 | |
|    testl   rINST, rINST                    # is object null?
 | |
|    je      common_errNullObject
 | |
|    movl %ecx, (rINST, %eax, 1)
 | |
|    testl %ecx, %ecx
 | |
|    je 4f
 | |
|    movl rSELF:THREAD_CARD_TABLE_OFFSET, %eax
 | |
|    shrl $$CARD_TABLE_CARD_SHIFT, rINST
 | |
|    movb %al, (%eax, rINST, 1)
 | |
| 4:
 | |
|    ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
 | |
| 2:
 | |
|    EXPORT_PC
 | |
|    // Fetch the value, needed by nterp_get_instance_field_offset.
 | |
|    movl    rINST, %ecx                     # ecx <- BA
 | |
|    andl    $$0xf, %ecx                     # ecx <- A
 | |
|    GET_VREG ARG3, %ecx                     # ecx <- v[A]
 | |
|    movl rSELF:THREAD_SELF_OFFSET, ARG0
 | |
|    movl 0(%esp), ARG1
 | |
|    movl rPC, ARG2
 | |
|    call nterp_get_instance_field_offset
 | |
|    testl %eax, %eax
 | |
|    jns 1b
 | |
|    negl %eax
 | |
|    // Reload the value as it may have moved.
 | |
|    movl    rINST, %ecx                     # ecx <- BA
 | |
|    andl    $$0xf, %ecx                     # ecx <- A
 | |
|    GET_VREG %ecx, %ecx                     # ecx <- v[A]
 | |
|    sarl    $$4, rINST
 | |
|    GET_VREG rINST, rINST                   # vB (object we're operating on)
 | |
|    testl   rINST, rINST                    # is object null?
 | |
|    je      common_errNullObject
 | |
|    movl %ecx, (rINST, %eax, 1)
 | |
|    testl %ecx, %ecx
 | |
|    je 5f
 | |
|    movl rSELF:THREAD_CARD_TABLE_OFFSET, %eax
 | |
|    shrl $$CARD_TABLE_CARD_SHIFT, rINST
 | |
|    movb %al, (%eax, rINST, 1)
 | |
| 5:
 | |
|    lock addl $$0, (%esp)
 | |
|    ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
 | |
| 
 | |
| NterpGetObjectInstanceField:
 | |
|    // Fast-path which gets the field from thread-local cache.
 | |
| %  fetch_from_thread_cache("%eax", miss_label="2f")
 | |
| 1:
 | |
|    movl    rINST, %ecx                     # ecx <- BA
 | |
|    sarl    $$4, %ecx                       # ecx <- B
 | |
|    GET_VREG %ecx, %ecx                     # vB (object we're operating on)
 | |
|    testl   %ecx, %ecx                      # is object null?
 | |
|    je      common_errNullObject
 | |
|    testb $$READ_BARRIER_TEST_VALUE, GRAY_BYTE_OFFSET(%ecx)
 | |
|    movl (%ecx,%eax,1), %eax
 | |
|    jnz 3f
 | |
| 4:
 | |
|    andb    $$0xf,rINSTbl                   # rINST <- A
 | |
|    SET_VREG_OBJECT %eax, rINST             # fp[A] <- value
 | |
|    ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
 | |
| 2:
 | |
|    EXPORT_PC
 | |
|    movl rSELF:THREAD_SELF_OFFSET, ARG0
 | |
|    movl 0(%esp), ARG1
 | |
|    movl rPC, ARG2
 | |
|    movl $$0, ARG3
 | |
|    call nterp_get_instance_field_offset
 | |
|    testl %eax, %eax
 | |
|    jns 1b
 | |
|    // For volatile fields, we return a negative offset. Remove the sign
 | |
|    // and no need for any barrier thanks to the memory model.
 | |
|    negl %eax
 | |
|    jmp 1b
 | |
| 3:
 | |
|    // reg00 is eax
 | |
|    call art_quick_read_barrier_mark_reg00
 | |
|    jmp 4b
 | |
| 
 | |
| NterpPutObjectStaticField:
 | |
|    GET_VREG rINST, rINST
 | |
|    // Fast-path which gets the field from thread-local cache.
 | |
| %  fetch_from_thread_cache("%eax", miss_label="2f")
 | |
| 1:
 | |
|    movl ART_FIELD_OFFSET_OFFSET(%eax), %ecx
 | |
|    movl ART_FIELD_DECLARING_CLASS_OFFSET(%eax), %eax
 | |
|    cmpl $$0, rSELF:THREAD_READ_BARRIER_MARK_REG00_OFFSET
 | |
|    jne 3f
 | |
| 5:
 | |
|    movl rINST, (%eax, %ecx, 1)
 | |
|    testl rINST, rINST
 | |
|    je 4f
 | |
|    movl rSELF:THREAD_CARD_TABLE_OFFSET, %ecx
 | |
|    shrl $$CARD_TABLE_CARD_SHIFT, %eax
 | |
|    movb %cl, (%ecx, %eax, 1)
 | |
| 4:
 | |
|    ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
 | |
| 2:
 | |
|    EXPORT_PC
 | |
|    movl rSELF:THREAD_SELF_OFFSET, ARG0
 | |
|    movl 0(%esp), ARG1
 | |
|    movl rPC, ARG2
 | |
|    movl rINST, ARG3
 | |
|    call nterp_get_static_field
 | |
|    // Reload the value as it may have moved.
 | |
|    GET_VREG rINST, rINST
 | |
|    testl MACRO_LITERAL(1), %eax
 | |
|    je 1b
 | |
|    CLEAR_VOLATILE_MARKER %eax
 | |
|    movl ART_FIELD_OFFSET_OFFSET(%eax), %ecx
 | |
|    movl ART_FIELD_DECLARING_CLASS_OFFSET(%eax), %eax
 | |
|    cmpl $$0, rSELF:THREAD_READ_BARRIER_MARK_REG00_OFFSET
 | |
|    jne 7f
 | |
| 6:
 | |
|    movl rINST, (%eax, %ecx, 1)
 | |
|    testl rINST, rINST
 | |
|    je 8f
 | |
|    movl rSELF:THREAD_CARD_TABLE_OFFSET, %ecx
 | |
|    shrl $$CARD_TABLE_CARD_SHIFT, %eax
 | |
|    movb %cl, (%ecx, %eax, 1)
 | |
| 8:
 | |
|    lock addl $$0, (%esp)
 | |
|    ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
 | |
| 3:
 | |
|    call art_quick_read_barrier_mark_reg00
 | |
|    jmp 5b
 | |
| 7:
 | |
|    call art_quick_read_barrier_mark_reg00
 | |
|    jmp 6b
 | |
| 
 | |
| NterpGetObjectStaticField:
 | |
|    // Fast-path which gets the field from thread-local cache.
 | |
| %  fetch_from_thread_cache("%eax", miss_label="2f")
 | |
| 1:
 | |
|    movl ART_FIELD_OFFSET_OFFSET(%eax), %ecx
 | |
|    movl ART_FIELD_DECLARING_CLASS_OFFSET(%eax), %eax
 | |
|    cmpl $$0, rSELF:THREAD_READ_BARRIER_MARK_REG00_OFFSET
 | |
|    jne 5f
 | |
| 6:
 | |
|    testb $$READ_BARRIER_TEST_VALUE, GRAY_BYTE_OFFSET(%eax)
 | |
|    movl (%eax, %ecx, 1), %eax
 | |
|    jnz 3f
 | |
| 4:
 | |
|    SET_VREG_OBJECT %eax, rINST             # fp[A] <- value
 | |
|    ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
 | |
| 2:
 | |
|    EXPORT_PC
 | |
|    movl rSELF:THREAD_SELF_OFFSET, ARG0
 | |
|    movl 0(%esp), ARG1
 | |
|    movl rPC, ARG2
 | |
|    movl $$0, ARG3
 | |
|    call nterp_get_static_field
 | |
|    CLEAR_VOLATILE_MARKER %eax
 | |
|    jmp 1b
 | |
| 3:
 | |
|    call art_quick_read_barrier_mark_reg00
 | |
|    jmp 4b
 | |
| 5:
 | |
|    call art_quick_read_barrier_mark_reg00
 | |
|    jmp 6b
 | |
| 
 | |
| NterpGetBooleanStaticField:
 | |
|   OP_SGET load="movzbl", wide=0
 | |
| 
 | |
| NterpGetByteStaticField:
 | |
|   OP_SGET load="movsbl", wide=0
 | |
| 
 | |
| NterpGetCharStaticField:
 | |
|   OP_SGET load="movzwl", wide=0
 | |
| 
 | |
| NterpGetShortStaticField:
 | |
|   OP_SGET load="movswl", wide=0
 | |
| 
 | |
| NterpGetWideStaticField:
 | |
|   OP_SGET load="movl", wide=1
 | |
| 
 | |
| NterpGetIntStaticField:
 | |
|   OP_SGET load="movl", wide=0
 | |
| 
 | |
| NterpPutStaticField:
 | |
|   OP_SPUT rINST_reg=rINST, store="movl", wide=0
 | |
| 
 | |
| NterpPutBooleanStaticField:
 | |
| NterpPutByteStaticField:
 | |
|   OP_SPUT rINST_reg=rINSTbl, store="movb", wide=0
 | |
| 
 | |
| NterpPutCharStaticField:
 | |
| NterpPutShortStaticField:
 | |
|   OP_SPUT rINST_reg=rINSTw, store="movw", wide=0
 | |
| 
 | |
| NterpPutWideStaticField:
 | |
|   OP_SPUT rINST_reg=rINST, store="movl", wide=1
 | |
| 
 | |
| NterpPutInstanceField:
 | |
|   OP_IPUT rINST_reg=rINST, store="movl", wide=0
 | |
| 
 | |
| NterpPutBooleanInstanceField:
 | |
| NterpPutByteInstanceField:
 | |
|   OP_IPUT rINST_reg=rINSTbl, store="movb", wide=0
 | |
| 
 | |
| NterpPutCharInstanceField:
 | |
| NterpPutShortInstanceField:
 | |
|   OP_IPUT rINST_reg=rINSTw, store="movw", wide=0
 | |
| 
 | |
| NterpPutWideInstanceField:
 | |
|   OP_IPUT rINST_reg=rINST, store="movl", wide=1
 | |
| 
 | |
| NterpGetBooleanInstanceField:
 | |
|   OP_IGET load="movzbl", wide=0
 | |
| 
 | |
| NterpGetByteInstanceField:
 | |
|   OP_IGET load="movsbl", wide=0
 | |
| 
 | |
| NterpGetCharInstanceField:
 | |
|   OP_IGET load="movzwl", wide=0
 | |
| 
 | |
| NterpGetShortInstanceField:
 | |
|   OP_IGET load="movswl", wide=0
 | |
| 
 | |
| NterpGetWideInstanceField:
 | |
|   OP_IGET load="movl", wide=1
 | |
| 
 | |
| NterpGetInstanceField:
 | |
|   OP_IGET load="movl", wide=0
 | |
| 
 | |
| NterpCallSuspendAndGotoNext:
 | |
|     EXPORT_PC
 | |
|     // Save branch offset.
 | |
|     movl rINST, LOCAL0(%esp)
 | |
|     call SYMBOL(art_quick_test_suspend)
 | |
|     RESTORE_IBASE
 | |
|     movl LOCAL0(%esp), rINST
 | |
|     FETCH_INST
 | |
|     GOTO_NEXT
 | |
| 
 | |
| NterpHandleHotnessOverflow:
 | |
|     CHECK_AND_UPDATE_SHARED_MEMORY_METHOD if_hot=1f, if_not_hot=4f
 | |
| 1:
 | |
|     movl rPC, %ecx
 | |
|     movl rFP, ARG2
 | |
|     // Save next PC.
 | |
|     movl %ecx, LOCAL0(%esp)
 | |
|     call nterp_hot_method
 | |
|     testl %eax, %eax
 | |
|     jne 3f
 | |
|     // Fetch next PC.
 | |
|     mov LOCAL0(%esp), rPC
 | |
| 2:
 | |
|     FETCH_INST
 | |
|     GOTO_NEXT
 | |
| 3:
 | |
|     // Drop the current frame.
 | |
|     movl -4(rREFS), %esp
 | |
|     CFI_DEF_CFA(esp, PARAMETERS_SAVES_SIZE+CALLEE_SAVES_SIZE)
 | |
|     DROP_PARAMETERS_SAVES
 | |
|     CFI_DEF_CFA(esp, CALLEE_SAVES_SIZE)
 | |
| 
 | |
|     // Setup the new frame
 | |
|     movl OSR_DATA_FRAME_SIZE(%eax), %ecx
 | |
|     // Given stack size contains all callee saved registers, remove them.
 | |
|     subl $$CALLEE_SAVES_SIZE, %ecx
 | |
| 
 | |
|     // Remember CFA.
 | |
|     movl %esp, %ebp
 | |
|     CFI_DEF_CFA_REGISTER(ebp)
 | |
| 
 | |
|     subl %ecx, %esp
 | |
|     movl %esp, %edi               // edi := beginning of stack
 | |
|     leal OSR_DATA_MEMORY(%eax), %esi  // esi := memory to copy
 | |
|     rep movsb                     // while (ecx--) { *edi++ = *esi++ }
 | |
| 
 | |
|     // Fetch the native PC to jump to and save it in stack.
 | |
|     pushl OSR_DATA_NATIVE_PC(%eax)
 | |
|     CFI_ADJUST_CFA_OFFSET(4)
 | |
| 
 | |
|     subl MACRO_LITERAL(8), %esp
 | |
|     CFI_ADJUST_CFA_OFFSET(8)
 | |
|     pushl %eax
 | |
|     CFI_ADJUST_CFA_OFFSET(4)
 | |
|     // Free the memory holding OSR Data.
 | |
|     call SYMBOL(NterpFree)
 | |
|     addl MACRO_LITERAL(12), %esp
 | |
|     CFI_ADJUST_CFA_OFFSET(-12)
 | |
| 
 | |
|     // Jump to the compiled code.
 | |
|     ret
 | |
| 4:
 | |
|     DO_SUSPEND_CHECK continue_label=2b
 | |
| 
 | |
| 
 | |
| NterpHandleInvokeInterfaceOnObjectMethodRange:
 | |
|    shrl $$16, %eax
 | |
|    movl MIRROR_CLASS_VTABLE_OFFSET_32(%edx, %eax, 4), %eax
 | |
|    jmp NterpCommonInvokeInstanceRange
 | |
| 
 | |
| NterpHandleInvokeInterfaceOnObjectMethod:
 | |
|    shrl $$16, %eax
 | |
|    movl MIRROR_CLASS_VTABLE_OFFSET_32(%edx, %eax, 4), %eax
 | |
|    jmp NterpCommonInvokeInstance
 | |
| 
 | |
| // This is the logical end of ExecuteNterpImpl, where the frame info applies.
 | |
| // EndExecuteNterpImpl includes the methods below as we want the runtime to
 | |
| // see them as part of the Nterp PCs.
 | |
| .cfi_endproc
 | |
| 
 | |
| END nterp_helper
 | |
| 
 | |
| // This is the end of PCs contained by the OatQuickMethodHeader created for the interpreter
 | |
| // entry point.
 | |
|     FUNCTION_TYPE(EndExecuteNterpImpl)
 | |
|     ASM_HIDDEN SYMBOL(EndExecuteNterpImpl)
 | |
|     .global SYMBOL(EndExecuteNterpImpl)
 | |
| SYMBOL(EndExecuteNterpImpl):
 | |
| 
 | |
| // Entrypoints into runtime.
 | |
| NTERP_TRAMPOLINE nterp_get_static_field, NterpGetStaticField
 | |
| NTERP_TRAMPOLINE nterp_get_instance_field_offset, NterpGetInstanceFieldOffset
 | |
| NTERP_TRAMPOLINE nterp_filled_new_array, NterpFilledNewArray
 | |
| NTERP_TRAMPOLINE nterp_filled_new_array_range, NterpFilledNewArrayRange
 | |
| NTERP_TRAMPOLINE nterp_get_class_or_allocate_object, NterpGetClassOrAllocateObject
 | |
| NTERP_TRAMPOLINE nterp_get_method, NterpGetMethod
 | |
| NTERP_TRAMPOLINE nterp_hot_method, NterpHotMethod
 | |
| NTERP_TRAMPOLINE nterp_load_object, NterpLoadObject
 | |
| 
 | |
| // gen_mterp.py will inline the following definitions
 | |
| // within [ExecuteNterpImpl, EndExecuteNterpImpl).
 | |
| %def instruction_end():
 | |
| 
 | |
|     FUNCTION_TYPE(artNterpAsmInstructionEnd)
 | |
|     ASM_HIDDEN SYMBOL(artNterpAsmInstructionEnd)
 | |
|     .global SYMBOL(artNterpAsmInstructionEnd)
 | |
| SYMBOL(artNterpAsmInstructionEnd):
 | |
|     // artNterpAsmInstructionEnd is used as landing pad for exception handling.
 | |
|     RESTORE_IBASE
 | |
|     FETCH_INST
 | |
|     GOTO_NEXT
 | |
| 
 | |
| %def instruction_start():
 | |
| 
 | |
|     FUNCTION_TYPE(artNterpAsmInstructionStart)
 | |
|     ASM_HIDDEN SYMBOL(artNterpAsmInstructionStart)
 | |
|     .global SYMBOL(artNterpAsmInstructionStart)
 | |
| SYMBOL(artNterpAsmInstructionStart) = .L_op_nop
 | |
|     .text
 | |
| 
 | |
| %def opcode_name_prefix():
 | |
| %   return "nterp_"
 | |
| %def opcode_start():
 | |
|     ENTRY nterp_${opcode}
 | |
| %def opcode_end():
 | |
|     END nterp_${opcode}
 | |
|     // Advance to the end of this handler. Causes error if we are past that point.
 | |
|     .org nterp_${opcode} + NTERP_HANDLER_SIZE  // ${opcode} handler is too big!
 | |
| %def opcode_slow_path_start(name):
 | |
|     ENTRY ${name}
 | |
| %def opcode_slow_path_end(name):
 | |
|     END ${name}
 |