blob: f346cb873d1003e494e8a63884b3fd978e7cea5e [file] [log] [blame]
%def header():
/*
* Copyright (C) 2019 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_64/asm_support_x86_64.S"
/**
* x86_64 ABI general notes:
*
* Caller save set:
* rax, rdx, rcx, rsi, rdi, r8-r11, st(0)-st(7)
* Callee save set:
* rbx, rbp, r12-r15
* Return regs:
* 32-bit in eax
* 64-bit in rax
* fp on xmm0
*
* First 8 fp parameters came in xmm0-xmm7.
* First 6 non-fp parameters came in rdi, rsi, rdx, rcx, r8, r9.
* Other parameters passed on stack, pushed right-to-left. On entry to target, first
* param is at 8(%esp).
*
* Stack must be 16-byte aligned to support SSE in native code.
*/
#define IN_ARG3 %rcx
#define IN_ARG2 %rdx
#define IN_ARG1 %rsi
#define IN_ARG0 %rdi
/* Out Args */
#define OUT_ARG3 %rcx
#define OUT_ARG2 %rdx
#define OUT_ARG1 %rsi
#define OUT_ARG0 %rdi
#define OUT_32_ARG3 %ecx
#define OUT_32_ARG2 %edx
#define OUT_32_ARG1 %esi
#define OUT_32_ARG0 %edi
#define OUT_FP_ARG1 %xmm1
#define OUT_FP_ARG0 %xmm0
/*
* single-purpose registers, given names for clarity
*/
#define rSELF %gs
#define rPC %r12
#define CFI_DEX 12 // DWARF register number of the register holding dex-pc (rPC).
#define CFI_TMP 5 // DWARF register number of the first argument register (rdi).
#define rFP %r13
#define rINST %ebx
#define rINSTq %rbx
#define rINSTw %bx
#define rINSTbh %bh
#define rINSTbl %bl
#define rIBASE %r14
#define rREFS %r15
#define rREFS32 %r15d
#define CFI_REFS 15 // DWARF register number of the reference array (r15).
// Temporary registers while setting up a frame.
#define rNEW_FP %r8
#define rNEW_REFS %r9
#define rNEW_REFS32 %r9d
#define CFI_NEW_REFS 9
/*
* 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)
// Includes the return address implictly pushed on stack by 'call'.
#define CALLEE_SAVES_SIZE (6 * 8 + 4 * 8 + 1 * 8)
// +8 for the ArtMethod of the caller.
#define OFFSET_TO_FIRST_ARGUMENT_IN_STACK (CALLEE_SAVES_SIZE + 8)
/*
* 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
movzwq (rPC), rINSTq
.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
addq rIBASE, %rcx
jmp *%rcx
.endm
/*
* Advance rPC by instruction count.
*/
.macro ADVANCE_PC _count
leaq 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 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. */
.macro GET_WIDE_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. reg is clobbered. */
.macro SET_WIDE_VREG _reg _vreg
movq \_reg, VREG_ADDRESS(\_vreg)
xorq \_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
// An assembly entry for nterp.
.macro OAT_ENTRY name
FUNCTION_TYPE(\name)
ASM_HIDDEN SYMBOL(\name)
.global SYMBOL(\name)
.balign 16
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
SETUP_SAVE_REFS_ONLY_FRAME
call \helper
RESTORE_SAVE_REFS_ONLY_FRAME
cmpq LITERAL(0), %gs:THREAD_EXCEPTION_OFFSET
jne nterp_deliver_pending_exception
ret
END_FUNCTION \name
.endm
.macro CLEAR_VOLATILE_MARKER reg
andq MACRO_LITERAL(-2), \reg
.endm
.macro EXPORT_PC
movq rPC, -16(rREFS)
.endm
.macro BRANCH
leaq (rPC, rINSTq, 2), rPC
// Update method counter and do a suspend check if the branch is negative or zero.
testq rINSTq, rINSTq
jle 3f
2: // We use 2 and not 1 for this local label as the users of the BRANCH macro have a 1 label.
FETCH_INST
GOTO_NEXT
3:
movq (%rsp), %rdi
movzwl ART_METHOD_HOTNESS_COUNT_OFFSET(%rdi), %esi
#if (NTERP_HOTNESS_VALUE != 0)
#error Expected 0 for hotness value
#endif
// If the counter is at zero, handle this in the runtime.
testw %si, %si
je NterpHandleHotnessOverflow
// Update counter.
addl $$-1, %esi
movw %si, ART_METHOD_HOTNESS_COUNT_OFFSET(%rdi)
DO_SUSPEND_CHECK continue_label=2b
jmp 2b
.endm
// Expects:
// - r10, and r11 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 replace with a pointer to the instructions
.macro FETCH_CODE_ITEM_INFO code_item, registers, outs, ins, load_ins
testq MACRO_LITERAL(1), \code_item
je 5f
andq $$-2, \code_item // Remove the extra bit that marks it's a compact dex file.
movzwl COMPACT_CODE_ITEM_FIELDS_OFFSET(\code_item), %r10d
movl %r10d, \registers
sarl $$COMPACT_CODE_ITEM_REGISTERS_SIZE_SHIFT, \registers
andl $$0xf, \registers
movl %r10d, \outs
sarl $$COMPACT_CODE_ITEM_OUTS_SIZE_SHIFT, \outs
andl $$0xf, \outs
.if \load_ins
movl %r10d, \ins
sarl $$COMPACT_CODE_ITEM_INS_SIZE_SHIFT, \ins
andl $$0xf, \ins
.else
movl %r10d, %r11d
sarl $$COMPACT_CODE_ITEM_INS_SIZE_SHIFT, %r11d
andl $$0xf, %r11d
addl %r11d, \registers
.endif
testw $$COMPACT_CODE_ITEM_REGISTERS_INS_OUTS_FLAGS, COMPACT_CODE_ITEM_FLAGS_OFFSET(\code_item)
je 4f
movq \code_item, %r11
testw $$COMPACT_CODE_ITEM_INSNS_FLAG, COMPACT_CODE_ITEM_FLAGS_OFFSET(\code_item)
je 1f
subq $$4, %r11
1:
testw $$COMPACT_CODE_ITEM_REGISTERS_FLAG, COMPACT_CODE_ITEM_FLAGS_OFFSET(\code_item)
je 2f
subq $$2, %r11
movzwl (%r11), %r10d
addl %r10d, \registers
2:
testw $$COMPACT_CODE_ITEM_INS_FLAG, COMPACT_CODE_ITEM_FLAGS_OFFSET(\code_item)
je 3f
subq $$2, %r11
movzwl (%r11), %r10d
.if \load_ins
addl %r10d, \ins
.else
addl %r10d, \registers
.endif
3:
testw $$COMPACT_CODE_ITEM_OUTS_FLAG, COMPACT_CODE_ITEM_FLAGS_OFFSET(\code_item)
je 4f
subq $$2, %r11
movzwl (%r11), %r10d
addl %r10d, \outs
4:
.if \load_ins
addl \ins, \registers
.endif
addq $$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
addq $$CODE_ITEM_INSNS_OFFSET, \code_item
6:
.endm
// Setup the stack to start executing the method. Expects:
// - rdi to contain the ArtMethod
// - rbx, r10, r11 to be available.
//
// Outputs
// - rbx contains the dex registers size
// - r11 contains the old stack pointer.
// - \code_item is replace with a pointer to the instructions
// - if load_ins is 1, r14 contains the ins
.macro SETUP_STACK_FRAME code_item, refs, refs32, fp, cfi_refs, load_ins
FETCH_CODE_ITEM_INFO \code_item, %ebx, \refs32, %r14d, \load_ins
// Compute required frame size for dex registers: ((2 * ebx) + refs)
leaq (\refs, %rbx, 2), %r11
salq $$2, %r11
// Compute new stack pointer in r10: add 24 for saving the previous frame,
// pc, and method being executed.
leaq -24(%rsp), %r10
subq %r11, %r10
// Alignment
// Note: There may be two pieces of alignment but there is no need to align
// out args to `kPointerSize` separately before aligning to kStackAlignment.
andq $$-16, %r10
// Set reference and dex registers, align to pointer size for previous frame and dex pc.
leaq 24 + 4(%r10, \refs, 4), \refs
andq LITERAL(-__SIZEOF_POINTER__), \refs
leaq (\refs, %rbx, 4), \fp
// Now setup the stack pointer.
movq %rsp, %r11
CFI_DEF_CFA_REGISTER(r11)
movq %r10, %rsp
movq %r11, -8(\refs)
CFI_DEF_CFA_BREG_PLUS_UCONST \cfi_refs, -8, ((6 + 4 + 1) * 8)
// Put nulls in reference frame.
testl %ebx, %ebx
je 2f
movq \refs, %r10
1:
movl $$0, (%r10)
addq $$4, %r10
cmpq %r10, \fp
jne 1b
2:
// Save the ArtMethod.
movq %rdi, (%rsp)
.endm
// Puts the next floating point argument into the expected register,
// fetching values based on a non-range invoke.
// Uses rax 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 // bl := *shorty
addq 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
shrq MACRO_LITERAL(4), REG_VAR(inst)
addq 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
shrq MACRO_LITERAL(4), REG_VAR(inst)
addq MACRO_LITERAL(1), REG_VAR(arg_index)
jmp 1b // goto LOOP
2: // FOUND_DOUBLE
subq MACRO_LITERAL(8), %rsp
movq REG_VAR(inst), %rax
andq MACRO_LITERAL(0xf), %rax
GET_VREG %eax, %rax
movl %eax, (%rsp)
shrq MACRO_LITERAL(4), REG_VAR(inst)
addq MACRO_LITERAL(1), REG_VAR(arg_index)
cmpq MACRO_LITERAL(4), REG_VAR(arg_index)
je 5f
movq REG_VAR(inst), %rax
andq MACRO_LITERAL(0xf), %rax
shrq MACRO_LITERAL(4), REG_VAR(inst)
addq MACRO_LITERAL(1), REG_VAR(arg_index)
jmp 6f
5:
movzbl 1(rPC), %eax
andq MACRO_LITERAL(0xf), %rax
6:
GET_VREG %eax, %rax
movl %eax, 4(%rsp)
movsd (%rsp), REG_VAR(xmm_reg)
addq MACRO_LITERAL(8), %rsp
jmp 4f
3: // FOUND_FLOAT
cmpq MACRO_LITERAL(4), REG_VAR(arg_index)
je 7f
movq REG_VAR(inst), %rax
andq MACRO_LITERAL(0xf), %rax
shrq MACRO_LITERAL(4), REG_VAR(inst)
addq MACRO_LITERAL(1), REG_VAR(arg_index)
jmp 8f
7:
movzbl 1(rPC), %eax
andq MACRO_LITERAL(0xf), %rax
8:
GET_VREG_XMMs REG_VAR(xmm_reg), %rax
4:
.endm
// Puts the next int/long/object argument in the expected register,
// fetching values based on a non-range invoke.
// Uses rax as temporary.
.macro LOOP_OVER_SHORTY_LOADING_GPRS gpr_reg64, gpr_reg32, inst, shorty, arg_index, finished
1: // LOOP
movb (REG_VAR(shorty)), %al // al := *shorty
addq 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
cmpq MACRO_LITERAL(4), REG_VAR(arg_index)
je 7f
movq REG_VAR(inst), %rax
andq MACRO_LITERAL(0xf), %rax
shrq MACRO_LITERAL(4), REG_VAR(inst)
addq MACRO_LITERAL(1), REG_VAR(arg_index)
jmp 8f
7:
movzbl 1(rPC), %eax
andq MACRO_LITERAL(0xf), %rax
8:
GET_VREG REG_VAR(gpr_reg32), %rax
jmp 5f
2: // FOUND_LONG
subq MACRO_LITERAL(8), %rsp
movq REG_VAR(inst), %rax
andq MACRO_LITERAL(0xf), %rax
GET_VREG %eax, %rax
movl %eax, (%rsp)
shrq MACRO_LITERAL(4), REG_VAR(inst)
addq MACRO_LITERAL(1), REG_VAR(arg_index)
cmpq MACRO_LITERAL(4), REG_VAR(arg_index)
je 9f
movq REG_VAR(inst), %rax
andq MACRO_LITERAL(0xf), %rax
shrq MACRO_LITERAL(4), REG_VAR(inst)
addq MACRO_LITERAL(1), REG_VAR(arg_index)
jmp 10f
9:
movzbl 1(rPC), %eax
andq MACRO_LITERAL(0xf), %rax
10:
GET_VREG %eax, %rax
movl %eax, 4(%rsp)
movq (%rsp), REG_VAR(gpr_reg64)
addq MACRO_LITERAL(8), %rsp
jmp 5f
3: // SKIP_FLOAT
shrq MACRO_LITERAL(4), REG_VAR(inst)
addq MACRO_LITERAL(1), REG_VAR(arg_index)
jmp 1b
4: // SKIP_DOUBLE
shrq MACRO_LITERAL(4), REG_VAR(inst)
addq MACRO_LITERAL(1), REG_VAR(arg_index)
cmpq MACRO_LITERAL(4), REG_VAR(arg_index)
je 1b
shrq MACRO_LITERAL(4), REG_VAR(inst)
addq MACRO_LITERAL(1), REG_VAR(arg_index)
jmp 1b
5:
.endm
// Puts the next floating point argument into the expected register,
// fetching values based on a range invoke.
// Uses rax 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
addq 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
addq MACRO_LITERAL(1), REG_VAR(arg_index)
addq 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
addq MACRO_LITERAL(1), REG_VAR(arg_index)
addq 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)
addq MACRO_LITERAL(2), REG_VAR(arg_index)
addq MACRO_LITERAL(2), REG_VAR(stack_index)
jmp 4f
3: // FOUND_FLOAT
GET_VREG_XMMs REG_VAR(xmm_reg), REG_VAR(arg_index)
addq MACRO_LITERAL(1), REG_VAR(arg_index)
addq 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 rax 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 > 6; 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
addq 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
addq MACRO_LITERAL(1), REG_VAR(arg_index)
addq 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
addq MACRO_LITERAL(1), REG_VAR(arg_index)
addq MACRO_LITERAL(1), REG_VAR(stack_index)
jmp 1b // goto LOOP
2: // FOUND_DOUBLE
movq (rFP, REG_VAR(arg_index), 4), %rax
movq %rax, 8(%rsp, REG_VAR(stack_index), 4)
addq MACRO_LITERAL(2), REG_VAR(arg_index)
addq MACRO_LITERAL(2), REG_VAR(stack_index)
jmp 1b
3: // FOUND_FLOAT
movl (rFP, REG_VAR(arg_index), 4), %eax
movl %eax, 8(%rsp, REG_VAR(stack_index), 4)
addq MACRO_LITERAL(1), REG_VAR(arg_index)
addq 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 rax as temporary.
.macro LOOP_RANGE_OVER_SHORTY_LOADING_GPRS gpr_reg64, gpr_reg32, shorty, arg_index, stack_index, finished
1: // LOOP
movb (REG_VAR(shorty)), %al // al := *shorty
addq 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_reg32)
addq MACRO_LITERAL(1), REG_VAR(arg_index)
addq MACRO_LITERAL(1), REG_VAR(stack_index)
jmp 5f
2: // FOUND_LONG
movq (rFP, REG_VAR(arg_index), 4), REG_VAR(gpr_reg64)
addq MACRO_LITERAL(2), REG_VAR(arg_index)
addq MACRO_LITERAL(2), REG_VAR(stack_index)
jmp 5f
3: // SKIP_FLOAT
addq MACRO_LITERAL(1), REG_VAR(arg_index)
addq MACRO_LITERAL(1), REG_VAR(stack_index)
jmp 1b
4: // SKIP_DOUBLE
addq MACRO_LITERAL(2), REG_VAR(arg_index)
addq 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 rax as temporary.
.macro LOOP_RANGE_OVER_INTs shorty, arg_index, stack_index, finished
1: // LOOP
movb (REG_VAR(shorty)), %al // al := *shorty
addq 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
movl %eax, 8(%rsp, REG_VAR(stack_index), 4)
addq MACRO_LITERAL(1), REG_VAR(arg_index)
addq MACRO_LITERAL(1), REG_VAR(stack_index)
jmp 1b
2: // FOUND_LONG
movq (rFP, REG_VAR(arg_index), 4), %rax
movq %rax, 8(%rsp, REG_VAR(stack_index), 4)
addq MACRO_LITERAL(2), REG_VAR(arg_index)
addq MACRO_LITERAL(2), REG_VAR(stack_index)
jmp 1b
3: // SKIP_FLOAT
addq MACRO_LITERAL(1), REG_VAR(arg_index)
addq MACRO_LITERAL(1), REG_VAR(stack_index)
jmp 1b
4: // SKIP_DOUBLE
addq MACRO_LITERAL(2), REG_VAR(arg_index)
addq 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 rax as temporary.
.macro LOOP_OVER_SHORTY_STORING_XMMS xmm_reg, shorty, arg_index, fp, finished
1: // LOOP
movb (REG_VAR(shorty)), %al // al := *shorty
addq 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
addq 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
addq MACRO_LITERAL(1), REG_VAR(arg_index)
jmp 1b // goto LOOP
2: // FOUND_DOUBLE
movsd REG_VAR(xmm_reg),(REG_VAR(fp), REG_VAR(arg_index), 4)
addq 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)
addq 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 rax as temporary.
.macro LOOP_OVER_SHORTY_STORING_GPRS gpr_reg64, gpr_reg32, shorty, arg_index, regs, refs, finished
1: // LOOP
movb (REG_VAR(shorty)), %al // al := *shorty
addq 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 REG_VAR(gpr_reg32), (REG_VAR(regs), REG_VAR(arg_index), 4)
cmpb MACRO_LITERAL(76), %al // if (al != 'L') goto NOT_REFERENCE
jne 6f
movl REG_VAR(gpr_reg32), (REG_VAR(refs), REG_VAR(arg_index), 4)
6: // NOT_REFERENCE
addq MACRO_LITERAL(1), REG_VAR(arg_index)
jmp 5f
2: // FOUND_LONG
movq REG_VAR(gpr_reg64), (REG_VAR(regs), REG_VAR(arg_index), 4)
addq MACRO_LITERAL(2), REG_VAR(arg_index)
jmp 5f
3: // SKIP_FLOAT
addq MACRO_LITERAL(1), REG_VAR(arg_index)
jmp 1b
4: // SKIP_DOUBLE
addq MACRO_LITERAL(2), REG_VAR(arg_index)
jmp 1b
5:
.endm
// Puts the next floating point parameter passed in stack
// in the expected dex register array entry.
// Uses rax 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
addq 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
addq 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
addq 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), %rax
movq %rax, (REG_VAR(regs), REG_VAR(arg_index), 4)
addq 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)
addq 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 rax as temporary.
.macro LOOP_OVER_INTs shorty, arg_index, regs, refs, stack_ptr, finished
1: // LOOP
movb (REG_VAR(shorty)), %al // al := *shorty
addq 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)
addq 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)
addq MACRO_LITERAL(1), REG_VAR(arg_index)
jmp 1b
2: // FOUND_LONG
movq OFFSET_TO_FIRST_ARGUMENT_IN_STACK(REG_VAR(stack_ptr), REG_VAR(arg_index), 4), %rax
movq %rax, (REG_VAR(regs), REG_VAR(arg_index), 4)
addq MACRO_LITERAL(2), REG_VAR(arg_index)
jmp 1b
3: // SKIP_FLOAT
addq MACRO_LITERAL(1), REG_VAR(arg_index)
jmp 1b
4: // SKIP_DOUBLE
addq 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
movq (%rsp), %rdi
movzwl ART_METHOD_HOTNESS_COUNT_OFFSET(%rdi), %esi
#if (NTERP_HOTNESS_VALUE != 0)
#error Expected 0 for hotness value
#endif
// If the counter is at zero, handle this in the runtime.
testl %esi, %esi
je 3f
// Update counter.
addl $$-1, %esi
movw %si, ART_METHOD_HOTNESS_COUNT_OFFSET(%rdi)
1:
DO_SUSPEND_CHECK continue_label=2f
2:
FETCH_INST
GOTO_NEXT
3:
CHECK_AND_UPDATE_SHARED_MEMORY_METHOD if_hot=4f, if_not_hot=1b
4:
movq $$0, %rsi
movq rFP, %rdx
call nterp_hot_method
jmp 2b
.endm
.macro SPILL_ALL_CALLEE_SAVES
PUSH r15
PUSH r14
PUSH r13
PUSH r12
PUSH rbp
PUSH rbx
SETUP_FP_CALLEE_SAVE_FRAME
.endm
.macro RESTORE_ALL_CALLEE_SAVES
RESTORE_FP_CALLEE_SAVE_FRAME
POP rbx
POP rbp
POP r12
POP r13
POP r14
POP r15
.endm
// Helper to setup the stack after doing a nterp to nterp call. This will setup:
// - rNEW_FP: the new pointer to dex registers
// - rNEW_REFS: the new pointer to references
// - rPC: the new PC pointer to execute
// - edi: number of arguments
// - ecx: first dex register
//
// This helper expects:
// - rax to contain the code item
.macro SETUP_STACK_FOR_INVOKE
// We do the same stack overflow check as the compiler. See CanMethodUseNterp
// in how we limit the maximum nterp frame size.
testq %rax, -STACK_OVERFLOW_RESERVED_BYTES(%rsp)
// Spill all callee saves to have a consistent stack frame whether we
// are called by compiled code or nterp.
SPILL_ALL_CALLEE_SAVES
// Setup the frame.
SETUP_STACK_FRAME %rax, rNEW_REFS, rNEW_REFS32, rNEW_FP, CFI_NEW_REFS, load_ins=0
// Make r11 point to the top of the dex register array.
leaq (rNEW_FP, %rbx, 4), %r11
// Fetch instruction information before replacing rPC.
movzbl 1(rPC), %edi
movzwl 4(rPC), %ecx
// Set the dex pc pointer.
movq %rax, rPC
CFI_DEFINE_DEX_PC_WITH_OFFSET(CFI_TMP, CFI_DEX, 0)
.endm
// Setup arguments based on a non-range nterp to nterp call, and start executing
// the method. We expect:
// - rNEW_FP: the new pointer to dex registers
// - rNEW_REFS: the new pointer to references
// - rPC: the new PC pointer to execute
// - edi: number of arguments
// - ecx: first dex register
// - r11: top of dex register array
// - esi: receiver if non-static.
.macro SETUP_NON_RANGE_ARGUMENTS_AND_EXECUTE is_static=0, is_string_init=0
// Now all temporary registers (except r11 containing top of registers array)
// are available, copy the parameters.
// /* op vA, vB, {vC...vG} */
movl %edi, %eax
shrl $$4, %eax # Number of arguments
jz 6f # shl sets the Z flag
movq MACRO_LITERAL(-1), %r10
cmpl MACRO_LITERAL(2), %eax
jl 1f
je 2f
cmpl MACRO_LITERAL(4), %eax
jl 3f
je 4f
// We use a decrementing r10 to store references relative
// to rNEW_FP and dex registers relative to r11.
//
// TODO: We could set up r10 as the number of registers (this can be an additional output from
// SETUP_STACK_FOR_INVOKE) and then just decrement it by one before copying each arg to
// (rNEW_FP, r10, 4) and (rNEW_REFS, r10, 4).
// Maybe even introduce macros NEW_VREG_ADDRESS/NEW_VREG_REF_ADDRESS.
5:
andq MACRO_LITERAL(15), %rdi
GET_VREG_OBJECT %edx, %rdi
movl %edx, (rNEW_FP, %r10, 4)
GET_VREG %edx, %rdi
movl %edx, (%r11, %r10, 4)
subq MACRO_LITERAL(1), %r10
4:
movl %ecx, %eax
shrl MACRO_LITERAL(12), %eax
GET_VREG_OBJECT %edx, %rax
movl %edx, (rNEW_FP, %r10, 4)
GET_VREG %edx, %rax
movl %edx, (%r11, %r10, 4)
subq MACRO_LITERAL(1), %r10
3:
movl %ecx, %eax
shrl MACRO_LITERAL(8), %eax
andl MACRO_LITERAL(0xf), %eax
GET_VREG_OBJECT %edx, %rax
movl %edx, (rNEW_FP, %r10, 4)
GET_VREG %edx, %rax
movl %edx, (%r11, %r10, 4)
subq MACRO_LITERAL(1), %r10
2:
movl %ecx, %eax
shrl MACRO_LITERAL(4), %eax
andl MACRO_LITERAL(0xf), %eax
GET_VREG_OBJECT %edx, %rax
movl %edx, (rNEW_FP, %r10, 4)
GET_VREG %edx, %rax
movl %edx, (%r11, %r10, 4)
subq MACRO_LITERAL(1), %r10
1:
.if \is_string_init
// Ignore the first argument
.elseif \is_static
movl %ecx, %eax
andq MACRO_LITERAL(0x000f), %rax
GET_VREG_OBJECT %edx, %rax
movl %edx, (rNEW_FP, %r10, 4)
GET_VREG %edx, %rax
movl %edx, (%r11, %r10, 4)
.else
movl %esi, (rNEW_FP, %r10, 4)
movl %esi, (%r11, %r10, 4)
.endif
6:
// Start executing the method.
movq rNEW_FP, rFP
movq rNEW_REFS, rREFS
CFI_DEF_CFA_BREG_PLUS_UCONST CFI_REFS, -8, ((6 + 4 + 1) * 8)
START_EXECUTING_INSTRUCTIONS
.endm
// Setup arguments based on a range nterp to nterp call, and start executing
// the method.
.macro SETUP_RANGE_ARGUMENTS_AND_EXECUTE is_static=0, is_string_init=0
// edi is number of arguments
// ecx is first register
movq MACRO_LITERAL(-4), %r10
.if \is_string_init
// Ignore the first argument
subl $$1, %edi
addl $$1, %ecx
.elseif !\is_static
subl $$1, %edi
addl $$1, %ecx
.endif
testl %edi, %edi
je 2f
leaq (rREFS, %rcx, 4), %rax # pointer to first argument in reference array
leaq (%rax, %rdi, 4), %rax # pointer to last argument in reference array
leaq (rFP, %rcx, 4), %rcx # pointer to first argument in register array
leaq (%rcx, %rdi, 4), %rdi # pointer to last argument in register array
// TODO: Same comment for copying arguments as in SETUP_NON_RANGE_ARGUMENTS_AND_EXECUTE.
1:
movl -4(%rax), %edx
movl %edx, (rNEW_FP, %r10, 1)
movl -4(%rdi), %edx
movl %edx, (%r11, %r10, 1)
subq MACRO_LITERAL(4), %r10
subq MACRO_LITERAL(4), %rax
subq MACRO_LITERAL(4), %rdi
cmpq %rcx, %rdi
jne 1b
2:
.if \is_string_init
// Ignore first argument
.elseif !\is_static
movl %esi, (rNEW_FP, %r10, 1)
movl %esi, (%r11, %r10, 1)
.endif
movq rNEW_FP, rFP
movq rNEW_REFS, rREFS
CFI_DEF_CFA_BREG_PLUS_UCONST CFI_REFS, -8, ((6 + 4 + 1) * 8)
START_EXECUTING_INSTRUCTIONS
.endm
.macro GET_SHORTY dest, is_interface, is_polymorphic, is_custom
push %rdi
push %rsi
.if \is_polymorphic
movq 16(%rsp), %rdi
movq rPC, %rsi
call SYMBOL(NterpGetShortyFromInvokePolymorphic)
.elseif \is_custom
movq 16(%rsp), %rdi
movq rPC, %rsi
call SYMBOL(NterpGetShortyFromInvokeCustom)
.elseif \is_interface
movq 16(%rsp), %rdi
movzwl 2(rPC), %esi
call SYMBOL(NterpGetShortyFromMethodId)
.else
call SYMBOL(NterpGetShorty)
.endif
pop %rsi
pop %rdi
movq %rax, \dest
.endm
.macro GET_SHORTY_SLOW_PATH dest, is_interface
// Save all registers that can hold arguments in the fast path.
push %rdi
push %rsi
push %rdx
subq MACRO_LITERAL(8), %rsp
mov %xmm0, (%rsp)
.if \is_interface
movq 32(%rsp), %rdi
movzwl 2(rPC), %esi
call SYMBOL(NterpGetShortyFromMethodId)
.else
call SYMBOL(NterpGetShorty)
.endif
mov (%rsp), %xmm0
addq MACRO_LITERAL(8), %rsp
pop %rdx
pop %rsi
pop %rdi
movq %rax, \dest
.endm
// Uses r9 as temporary.
.macro DO_ENTRY_POINT_CHECK call_compiled_code
// On entry, the method is %rdi, the instance is %rsi
leaq ExecuteNterpImpl(%rip), %r9
cmpq %r9, ART_METHOD_QUICK_CODE_OFFSET_64(%rdi)
jne VAR(call_compiled_code)
movq ART_METHOD_DATA_OFFSET_64(%rdi), %rax
.endm
// Uses r9 and r10 as temporary
.macro UPDATE_REGISTERS_FOR_STRING_INIT old_value, new_value
movq rREFS, %r9
movq rFP, %r10
1:
cmpl (%r9), \old_value
jne 2f
movl \new_value, (%r9)
movl \new_value, (%r10)
2:
addq $$4, %r9
addq $$4, %r10
cmpq %r9, rFP
jne 1b
.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
// We always go to compiled code for polymorphic calls.
.elseif \is_custom
// We always go to compiled code for custom calls.
.else
DO_ENTRY_POINT_CHECK .Lcall_compiled_code_\suffix
.if \is_string_init
call nterp_to_nterp_string_init_non_range
.elseif \is_static
call nterp_to_nterp_static_non_range
.else
call nterp_to_nterp_instance_non_range
.endif
jmp .Ldone_return_\suffix
.endif
.Lcall_compiled_code_\suffix:
.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(%rdi)
je .Lfast_path_with_few_args_\suffix
movzbl 1(rPC), %r9d
movl %r9d, %ebp
shrl MACRO_LITERAL(4), %ebp # Number of arguments
.if \is_static
jz .Linvoke_fast_path_\suffix # shl sets the Z flag
.else
cmpl MACRO_LITERAL(1), %ebp
je .Linvoke_fast_path_\suffix
.endif
movzwl 4(rPC), %r11d
cmpl MACRO_LITERAL(2), %ebp
.if \is_static
jl .Lone_arg_fast_path_\suffix
.endif
je .Ltwo_args_fast_path_\suffix
cmpl MACRO_LITERAL(4), %ebp
jl .Lthree_args_fast_path_\suffix
je .Lfour_args_fast_path_\suffix
andl MACRO_LITERAL(0xf), %r9d
GET_VREG %r9d, %r9
.Lfour_args_fast_path_\suffix:
movl %r11d, %r8d
shrl MACRO_LITERAL(12), %r8d
GET_VREG %r8d, %r8
.Lthree_args_fast_path_\suffix:
movl %r11d, %ecx
shrl MACRO_LITERAL(8), %ecx
andl MACRO_LITERAL(0xf), %ecx
GET_VREG %ecx, %rcx
.Ltwo_args_fast_path_\suffix:
movl %r11d, %edx
shrl MACRO_LITERAL(4), %edx
andl MACRO_LITERAL(0xf), %edx
GET_VREG %edx, %rdx
.Lone_arg_fast_path_\suffix:
.if \is_static
andl MACRO_LITERAL(0xf), %r11d
GET_VREG %esi, %r11
.else
// First argument already in %esi.
.endif
.Linvoke_fast_path_\suffix:
call *ART_METHOD_QUICK_CODE_OFFSET_64(%rdi) // Call the method.
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), %r9d
shrl MACRO_LITERAL(4), %r9d # Number of arguments
.if \is_static
cmpl MACRO_LITERAL(1), %r9d
jl .Linvoke_with_few_args_\suffix
jne .Lget_shorty_\suffix
movzwl 4(rPC), %r9d
andl MACRO_LITERAL(0xf), %r9d // dex register of first argument
GET_VREG %esi, %r9
movd %esi, %xmm0
.else
cmpl MACRO_LITERAL(2), %r9d
jl .Linvoke_with_few_args_\suffix
jne .Lget_shorty_\suffix
movzwl 4(rPC), %r9d
shrl MACRO_LITERAL(4), %r9d
andl MACRO_LITERAL(0xf), %r9d // dex register of second argument
GET_VREG %edx, %r9
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.
movzwq 6(rPC), %r9
andl MACRO_LITERAL(0xfe), %r9d
cmpl MACRO_LITERAL(0x0a), %r9d
je .Lget_shorty_and_invoke_\suffix
call *ART_METHOD_QUICK_CODE_OFFSET_64(%rdi) // Call the method.
ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
.Lget_shorty_and_invoke_\suffix:
.if \is_interface
// Save interface method, used for conflict resolution, in a callee-save register.
movq %rax, %xmm12
.endif
GET_SHORTY_SLOW_PATH rINSTq, \is_interface
jmp .Lgpr_setup_finished_\suffix
.endif
.Lget_shorty_\suffix:
.if \is_interface
// Save interface method, used for conflict resolution, in a callee-save register.
movq %rax, %xmm12
.endif
GET_SHORTY rINSTq, \is_interface, \is_polymorphic, \is_custom
// From this point:
// - rISNTq contains shorty (in callee-save to switch over return value after call).
// - rdi contains method
// - rsi contains 'this' pointer for instance method.
leaq 1(rINSTq), %r9 // shorty + 1 ; ie skip return arg character
movzwl 4(rPC), %r11d // arguments
.if \is_string_init
shrq MACRO_LITERAL(4), %r11
movq $$1, %r10 // ignore first argument
.elseif \is_static
movq $$0, %r10 // arg_index
.else
shrq MACRO_LITERAL(4), %r11
movq $$1, %r10 // arg_index
.endif
LOOP_OVER_SHORTY_LOADING_XMMS xmm0, r11, r9, r10, .Lxmm_setup_finished_\suffix
LOOP_OVER_SHORTY_LOADING_XMMS xmm1, r11, r9, r10, .Lxmm_setup_finished_\suffix
LOOP_OVER_SHORTY_LOADING_XMMS xmm2, r11, r9, r10, .Lxmm_setup_finished_\suffix
LOOP_OVER_SHORTY_LOADING_XMMS xmm3, r11, r9, r10, .Lxmm_setup_finished_\suffix
LOOP_OVER_SHORTY_LOADING_XMMS xmm4, r11, r9, r10, .Lxmm_setup_finished_\suffix
.Lxmm_setup_finished_\suffix:
leaq 1(rINSTq), %r9 // shorty + 1 ; ie skip return arg character
movzwl 4(rPC), %r11d // arguments
.if \is_string_init
movq $$1, %r10 // ignore first argument
shrq MACRO_LITERAL(4), %r11
LOOP_OVER_SHORTY_LOADING_GPRS rsi, esi, r11, r9, r10, .Lgpr_setup_finished_\suffix
.elseif \is_static
movq $$0, %r10 // arg_index
LOOP_OVER_SHORTY_LOADING_GPRS rsi, esi, r11, r9, r10, .Lgpr_setup_finished_\suffix
.else
shrq MACRO_LITERAL(4), %r11
movq $$1, %r10 // arg_index
.endif
LOOP_OVER_SHORTY_LOADING_GPRS rdx, edx, r11, r9, r10, .Lgpr_setup_finished_\suffix
LOOP_OVER_SHORTY_LOADING_GPRS rcx, ecx, r11, r9, r10, .Lgpr_setup_finished_\suffix
LOOP_OVER_SHORTY_LOADING_GPRS r8, r8d, r11, r9, r10, .Lgpr_setup_finished_\suffix
LOOP_OVER_SHORTY_LOADING_GPRS r9, r9d, r11, r9, r10, .Lgpr_setup_finished_\suffix
.Lgpr_setup_finished_\suffix:
.if \is_polymorphic
call SYMBOL(art_quick_invoke_polymorphic)
.elseif \is_custom
call SYMBOL(art_quick_invoke_custom)
.else
.if \is_interface
movq %xmm12, %rax
.endif
call *ART_METHOD_QUICK_CODE_OFFSET_64(%rdi) // Call the method.
.endif
cmpb LITERAL(68), (rINSTq) // Test if result type char == 'D'.
je .Lreturn_double_\suffix
cmpb LITERAL(70), (rINSTq) // Test if result type char == 'F'.
jne .Ldone_return_\suffix
.Lreturn_float_\suffix:
movd %xmm0, %eax
jmp .Ldone_return_\suffix
.Lreturn_double_\suffix:
movq %xmm0, %rax
.Ldone_return_\suffix:
/* resume execution of caller */
.if \is_string_init
movzwl 4(rPC), %r11d // arguments
andq $$0xf, %r11
GET_VREG %esi, %r11
UPDATE_REGISTERS_FOR_STRING_INIT %esi, %eax
.endif
.if \is_polymorphic
ADVANCE_PC_FETCH_AND_GOTO_NEXT 4
.else
ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
.endif
.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
// We always go to compiled code for polymorphic calls.
.elseif \is_custom
// We always go to compiled code for custom calls.
.else
DO_ENTRY_POINT_CHECK .Lcall_compiled_code_range_\suffix
.if \is_string_init
call nterp_to_nterp_string_init_range
.elseif \is_static
call nterp_to_nterp_static_range
.else
call nterp_to_nterp_instance_range
.endif
jmp .Ldone_return_range_\suffix
.endif
.Lcall_compiled_code_range_\suffix:
.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(%rdi)
je .Lfast_path_with_few_args_range_\suffix
movzbl 1(rPC), %r9d // number of arguments
.if \is_static
testl %r9d, %r9d
je .Linvoke_fast_path_range_\suffix
.else
cmpl MACRO_LITERAL(1), %r9d
je .Linvoke_fast_path_range_\suffix
.endif
movzwl 4(rPC), %r11d // dex register of first argument
leaq (rFP, %r11, 4), %r11 // location of first dex register value
cmpl MACRO_LITERAL(2), %r9d
.if \is_static
jl .Lone_arg_fast_path_range_\suffix
.endif
je .Ltwo_args_fast_path_range_\suffix
cmp MACRO_LITERAL(4), %r9d
jl .Lthree_args_fast_path_range_\suffix
je .Lfour_args_fast_path_range_\suffix
cmp MACRO_LITERAL(5), %r9d
je .Lfive_args_fast_path_range_\suffix
.Lloop_over_fast_path_range_\suffix:
subl MACRO_LITERAL(1), %r9d
movl (%r11, %r9, 4), %r8d
movl %r8d, 8(%rsp, %r9, 4) // Add 8 for the ArtMethod
cmpl MACRO_LITERAL(5), %r9d
jne .Lloop_over_fast_path_range_\suffix
.Lfive_args_fast_path_range_\suffix:
movl 16(%r11), %r9d
.Lfour_args_fast_path_range_\suffix:
movl 12(%r11), %r8d
.Lthree_args_fast_path_range_\suffix:
movl 8(%r11), %ecx
.Ltwo_args_fast_path_range_\suffix:
movl 4(%r11), %edx
.Lone_arg_fast_path_range_\suffix:
.if \is_static
movl 0(%r11), %esi
.else
// First argument already in %esi.
.endif
.Linvoke_fast_path_range_\suffix:
call *ART_METHOD_QUICK_CODE_OFFSET_64(%rdi) // Call the method.
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), %r9d # Number of arguments
.if \is_static
cmpl MACRO_LITERAL(1), %r9d
jl .Linvoke_with_few_args_range_\suffix
jne .Lget_shorty_range_\suffix
movzwl 4(rPC), %r9d // Dex register of first argument
GET_VREG %esi, %r9
movd %esi, %xmm0
.else
cmpl MACRO_LITERAL(2), %r9d
jl .Linvoke_with_few_args_range_\suffix
jne .Lget_shorty_range_\suffix
movzwl 4(rPC), %r9d
addl MACRO_LITERAL(1), %r9d // dex register of second argument
GET_VREG %edx, %r9
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.
movzwq 6(rPC), %r9
and MACRO_LITERAL(0xfe), %r9d
cmpl MACRO_LITERAL(0x0a), %r9d
je .Lget_shorty_and_invoke_range_\suffix
call *ART_METHOD_QUICK_CODE_OFFSET_64(%rdi) // Call the method.
ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
.Lget_shorty_and_invoke_range_\suffix:
.if \is_interface
// Save interface method, used for conflict resolution, in a callee-save register.
movq %rax, %xmm12
.endif
GET_SHORTY_SLOW_PATH rINSTq, \is_interface
jmp .Lgpr_setup_finished_range_\suffix
.endif
.Lget_shorty_range_\suffix:
.if \is_interface
// Save interface method, used for conflict resolution, in a callee-saved register.
movq %rax, %xmm12
.endif
GET_SHORTY rINSTq, \is_interface, \is_polymorphic, \is_custom
// From this point:
// - rINSTq contains shorty (in callee-save to switch over return value after call).
// - rdi contains method
// - rsi contains 'this' pointer for instance method.
leaq 1(rINSTq), %r9 // shorty + 1 ; ie skip return arg character
movzwl 4(rPC), %r10d // arg start index
.if \is_string_init
addq $$1, %r10 // arg start index
movq $$1, %rbp // index in stack
.elseif \is_static
movq $$0, %rbp // index in stack
.else
addq $$1, %r10 // arg start index
movq $$1, %rbp // index in stack
.endif
LOOP_RANGE_OVER_SHORTY_LOADING_XMMS xmm0, r9, r10, rbp, .Lxmm_setup_finished_range_\suffix
LOOP_RANGE_OVER_SHORTY_LOADING_XMMS xmm1, r9, r10, rbp, .Lxmm_setup_finished_range_\suffix
LOOP_RANGE_OVER_SHORTY_LOADING_XMMS xmm2, r9, r10, rbp, .Lxmm_setup_finished_range_\suffix
LOOP_RANGE_OVER_SHORTY_LOADING_XMMS xmm3, r9, r10, rbp, .Lxmm_setup_finished_range_\suffix
LOOP_RANGE_OVER_SHORTY_LOADING_XMMS xmm4, r9, r10, rbp, .Lxmm_setup_finished_range_\suffix
LOOP_RANGE_OVER_SHORTY_LOADING_XMMS xmm5, r9, r10, rbp, .Lxmm_setup_finished_range_\suffix
LOOP_RANGE_OVER_SHORTY_LOADING_XMMS xmm6, r9, r10, rbp, .Lxmm_setup_finished_range_\suffix
LOOP_RANGE_OVER_SHORTY_LOADING_XMMS xmm7, r9, r10, rbp, .Lxmm_setup_finished_range_\suffix
LOOP_RANGE_OVER_FPs r9, r10, rbp, .Lxmm_setup_finished_range_\suffix
.Lxmm_setup_finished_range_\suffix:
leaq 1(%rbx), %r11 // shorty + 1 ; ie skip return arg character
movzwl 4(rPC), %r10d // arg start index
.if \is_string_init
addq $$1, %r10 // arg start index
movq $$1, %rbp // index in stack
LOOP_RANGE_OVER_SHORTY_LOADING_GPRS rsi, esi, r11, r10, rbp, .Lgpr_setup_finished_range_\suffix
.elseif \is_static
movq $$0, %rbp // index in stack
LOOP_RANGE_OVER_SHORTY_LOADING_GPRS rsi, esi, r11, r10, rbp, .Lgpr_setup_finished_range_\suffix
.else
addq $$1, %r10 // arg start index
movq $$1, %rbp // index in stack
.endif
LOOP_RANGE_OVER_SHORTY_LOADING_GPRS rdx, edx, r11, r10, rbp, .Lgpr_setup_finished_range_\suffix
LOOP_RANGE_OVER_SHORTY_LOADING_GPRS rcx, ecx, r11, r10, rbp, .Lgpr_setup_finished_range_\suffix
LOOP_RANGE_OVER_SHORTY_LOADING_GPRS r8, r8d, r11, r10, rbp, .Lgpr_setup_finished_range_\suffix
LOOP_RANGE_OVER_SHORTY_LOADING_GPRS r9, r9d, r11, r10, rbp, .Lgpr_setup_finished_range_\suffix
LOOP_RANGE_OVER_INTs r11, r10, rbp, .Lgpr_setup_finished_range_\suffix
.Lgpr_setup_finished_range_\suffix:
.if \is_polymorphic
call SYMBOL(art_quick_invoke_polymorphic)
.elseif \is_custom
call SYMBOL(art_quick_invoke_custom)
.else
.if \is_interface
// Set the hidden argument for conflict resolution.
movq %xmm12, %rax
.endif
call *ART_METHOD_QUICK_CODE_OFFSET_64(%rdi) // Call the method.
.endif
cmpb LITERAL(68), (%rbx) // Test if result type char == 'D'.
je .Lreturn_range_double_\suffix
cmpb LITERAL(70), (%rbx) // Test if result type char == 'F'.
je .Lreturn_range_float_\suffix
/* resume execution of caller */
.Ldone_return_range_\suffix:
.if \is_string_init
movzwl 4(rPC), %r11d // arguments
GET_VREG %esi, %r11
UPDATE_REGISTERS_FOR_STRING_INIT %esi, %eax
.endif
.if \is_polymorphic
ADVANCE_PC_FETCH_AND_GOTO_NEXT 4
.else
ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
.endif
.Lreturn_range_double_\suffix:
movq %xmm0, %rax
jmp .Ldone_return_range_\suffix
.Lreturn_range_float_\suffix:
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("%rax", miss_label="2f")
1:
movl ART_FIELD_OFFSET_OFFSET(%rax), %edx
movl ART_FIELD_DECLARING_CLASS_OFFSET(%rax), %eax
cmpq $$0, rSELF:THREAD_READ_BARRIER_MARK_REG00_OFFSET
jne 3f
4:
.if \wide
movq (%eax,%edx,1), %rax
SET_WIDE_VREG %rax, rINSTq # fp[A] <- value
.else
\load (%eax, %edx, 1), %eax
SET_VREG %eax, rINSTq # fp[A] <- value
.endif
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
2:
EXPORT_PC
movq rSELF:THREAD_SELF_OFFSET, %rdi
movq 0(%rsp), %rsi
movq rPC, %rdx
movq $$0, %rcx
call nterp_get_static_field
// Clear the marker that we put for volatile fields. The x86 memory
// model doesn't require a barrier.
andq $$-2, %rax
jmp 1b
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("%rax", miss_label="2f")
1:
movl ART_FIELD_OFFSET_OFFSET(%rax), %edx
movl ART_FIELD_DECLARING_CLASS_OFFSET(%rax), %eax
cmpq $$0, rSELF:THREAD_READ_BARRIER_MARK_REG00_OFFSET
jne 3f
4:
.if \wide
GET_WIDE_VREG rINSTq, rINSTq # rINST <- v[A]
.else
GET_VREG rINST, rINSTq # rINST <- v[A]
.endif
\store \rINST_reg, (%rax,%rdx,1)
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
2:
EXPORT_PC
movq rSELF:THREAD_SELF_OFFSET, %rdi
movq 0(%rsp), %rsi
movq rPC, %rdx
movq $$0, %rcx
call nterp_get_static_field
testq MACRO_LITERAL(1), %rax
je 1b
// Clear the marker that we put for volatile fields. The x86 memory
// model doesn't require a barrier.
CLEAR_VOLATILE_MARKER %rax
movl ART_FIELD_OFFSET_OFFSET(%rax), %edx
movl ART_FIELD_DECLARING_CLASS_OFFSET(%rax), %eax
cmpq $$0, rSELF:THREAD_READ_BARRIER_MARK_REG00_OFFSET
jne 6f
5:
.if \wide
GET_WIDE_VREG rINSTq, rINSTq # rINST <- v[A]
.else
GET_VREG rINST, rINSTq # rINST <- v[A]
.endif
\store \rINST_reg, (%rax,%rdx,1)
lock addl $$0, (%rsp)
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":
movzbq rINSTbl, %rcx # rcx <- BA
sarl $$4, %ecx # ecx <- B
GET_VREG %ecx, %rcx # vB (object we're operating on)
testl %ecx, %ecx # is object null?
je common_errNullObject
andb $$0xf, rINSTbl # rINST <- A
.if \wide
GET_WIDE_VREG rINSTq, rINSTq # rax<- fp[A]/fp[A+1]
.else
GET_VREG rINST, rINSTq # rINST <- v[A]
.endif
\store \rINST_reg, (%rcx,%rax,1)
.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("%rax", miss_label="2f")
1:
OP_IPUT_INTERNAL \rINST_reg, \store, \wide
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
2:
EXPORT_PC
movq rSELF:THREAD_SELF_OFFSET, %rdi
movq 0(%rsp), %rsi
movq rPC, %rdx
movq $$0, %rcx
call nterp_get_instance_field_offset
testl %eax, %eax
jns 1b
negl %eax
OP_IPUT_INTERNAL \rINST_reg, \store, \wide
lock addl $$0, (%rsp)
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("%rax", miss_label="2f")
1:
movl rINST, %ecx # rcx <- BA
sarl $$4, %ecx # ecx <- B
GET_VREG %ecx, %rcx # vB (object we're operating on)
testl %ecx, %ecx # is object null?
je common_errNullObject
andb $$0xf,rINSTbl # rINST <- A
.if \wide
movq (%rcx,%rax,1), %rax
SET_WIDE_VREG %rax, rINSTq # fp[A] <- value
.else
\load (%rcx,%rax,1), %eax
SET_VREG %eax, rINSTq # fp[A] <- value
.endif
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
2:
EXPORT_PC
movq rSELF:THREAD_SELF_OFFSET, %rdi
movq 0(%rsp), %rsi
movq rPC, %rdx
movq $$0, %rcx
call nterp_get_instance_field_offset
testl %eax, %eax
jns 1b
negl %eax
jmp 1b
.endm
.macro SETUP_REFERENCE_PARAMETER_IN_GPR gpr32, regs, refs, ins, arg_offset, finished
movl REG_VAR(gpr32), (REG_VAR(regs), REG_VAR(arg_offset))
movl REG_VAR(gpr32), (REG_VAR(refs), REG_VAR(arg_offset))
addq MACRO_LITERAL(4), REG_VAR(arg_offset)
subl MACRO_LITERAL(1), REG_VAR(ins)
je \finished
.endm
// Uses eax as temporary
.macro SETUP_REFERENCE_PARAMETERS_IN_STACK regs, refs, ins, stack_ptr, arg_offset
1:
movl OFFSET_TO_FIRST_ARGUMENT_IN_STACK(REG_VAR(stack_ptr), REG_VAR(arg_offset)), %eax
movl %eax, (REG_VAR(regs), REG_VAR(arg_offset))
movl %eax, (REG_VAR(refs), REG_VAR(arg_offset))
addq MACRO_LITERAL(4), REG_VAR(arg_offset)
subl MACRO_LITERAL(1), REG_VAR(ins)
jne 1b
.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(%rdi)
jz \if_hot
movzwl rSELF:THREAD_SHARED_METHOD_HOTNESS_OFFSET, %esi
testl %esi, %esi
je \if_hot
addl $$-1, %esi
movw %si, rSELF:THREAD_SHARED_METHOD_HOTNESS_OFFSET
jmp \if_not_hot
.endm
.macro DO_SUSPEND_CHECK continue_label
testl $$(THREAD_SUSPEND_OR_CHECKPOINT_REQUEST), rSELF:THREAD_FLAGS_OFFSET
jz \continue_label
EXPORT_PC
call SYMBOL(art_quick_test_suspend)
.endm
%def entry():
/*
* ArtMethod entry point.
*
* On entry:
* rdi ArtMethod* callee
* rest method parameters
*/
OAT_ENTRY ExecuteNterpWithClinitImpl
.cfi_startproc
// For simplicity, we don't do a read barrier here, but instead rely
// on art_quick_resolution_trampoline to always have a suspend point before
// calling back here.
movl ART_METHOD_DECLARING_CLASS_OFFSET(%rdi), %r10d
cmpl $$(MIRROR_CLASS_STATUS_VISIBLY_INITIALIZED_SHIFTED), MIRROR_CLASS_STATUS_OFFSET(%r10d)
jae ExecuteNterpImpl
cmpl $$(MIRROR_CLASS_STATUS_INITIALIZING_SHIFTED), MIRROR_CLASS_STATUS_OFFSET(%r10d)
jb art_quick_resolution_trampoline
movl MIRROR_CLASS_CLINIT_THREAD_ID_OFFSET(%r10d), %r10d
cmpl %r10d, rSELF:THREAD_TID_OFFSET
je ExecuteNterpImpl
jmp art_quick_resolution_trampoline
.cfi_endproc
.global SYMBOL(EndExecuteNterpWithClinitImpl)
SYMBOL(EndExecuteNterpWithClinitImpl):
OAT_ENTRY ExecuteNterpImpl
.cfi_startproc
.cfi_def_cfa rsp, 8
testq %rax, -STACK_OVERFLOW_RESERVED_BYTES(%rsp)
/* Spill callee save regs */
SPILL_ALL_CALLEE_SAVES
movq ART_METHOD_DATA_OFFSET_64(%rdi), rPC
// Setup the stack for executing the method.
SETUP_STACK_FRAME rPC, rREFS, rREFS32, rFP, CFI_REFS, load_ins=1
// Setup the parameters
testl %r14d, %r14d
je .Lxmm_setup_finished
subq %r14, %rbx
salq $$2, %rbx // rbx is now the offset for inputs into the registers array.
testl $$ART_METHOD_NTERP_ENTRY_POINT_FAST_PATH_FLAG, ART_METHOD_ACCESS_FLAGS_OFFSET(%rdi)
je .Lsetup_slow_path
leaq (rFP, %rbx, 1), %rdi
leaq (rREFS, %rbx, 1), %rbx
movq $$0, %r10
SETUP_REFERENCE_PARAMETER_IN_GPR esi, rdi, rbx, r14d, r10, .Lxmm_setup_finished
SETUP_REFERENCE_PARAMETER_IN_GPR edx, rdi, rbx, r14d, r10, .Lxmm_setup_finished
SETUP_REFERENCE_PARAMETER_IN_GPR ecx, rdi, rbx, r14d, r10, .Lxmm_setup_finished
SETUP_REFERENCE_PARAMETER_IN_GPR r8d, rdi, rbx, r14d, r10, .Lxmm_setup_finished
SETUP_REFERENCE_PARAMETER_IN_GPR r9d, rdi, rbx, r14d, r10, .Lxmm_setup_finished
SETUP_REFERENCE_PARAMETERS_IN_STACK rdi, rbx, r14d, r11, r10
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(%rdi)
jne .Lsetup_with_shorty
movl %esi, (rFP, %rbx)
movl %esi, (rREFS, %rbx)
cmpl $$1, %r14d
je .Lxmm_setup_finished
.Lsetup_with_shorty:
// TODO: Get shorty in a better way and remove below
push %rdi
push %rsi
push %rdx
push %rcx
push %r8
push %r9
// Save xmm registers + alignment.
subq MACRO_LITERAL(8 * 8 + 8), %rsp
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)
call SYMBOL(NterpGetShorty)
// Save shorty in callee-save rbp.
movq %rax, %rbp
// Restore xmm registers + alignment.
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
addq MACRO_LITERAL(8 * 8 + 8), %rsp
pop %r9
pop %r8
pop %rcx
pop %rdx
pop %rsi
pop %rdi
// Reload the old stack pointer, which used to be stored in %r11, which is not callee-saved.
movq -8(rREFS), %r11
// TODO: Get shorty in a better way and remove above
movq $$0, %r14
testl $$ART_METHOD_IS_STATIC_FLAG, ART_METHOD_ACCESS_FLAGS_OFFSET(%rdi)
// Available: rdi, r10
// Note the leaq below don't change the flags.
leaq 1(%rbp), %r10 // shorty + 1 ; ie skip return arg character
leaq (rFP, %rbx, 1), %rdi
leaq (rREFS, %rbx, 1), %rbx
jne .Lhandle_static_method
addq $$4, %rdi
addq $$4, %rbx
addq $$4, %r11
jmp .Lcontinue_setup_gprs
.Lhandle_static_method:
LOOP_OVER_SHORTY_STORING_GPRS rsi, esi, r10, r14, rdi, rbx, .Lgpr_setup_finished
.Lcontinue_setup_gprs:
LOOP_OVER_SHORTY_STORING_GPRS rdx, edx, r10, r14, rdi, rbx, .Lgpr_setup_finished
LOOP_OVER_SHORTY_STORING_GPRS rcx, ecx, r10, r14, rdi, rbx, .Lgpr_setup_finished
LOOP_OVER_SHORTY_STORING_GPRS r8, r8d, r10, r14, rdi, rbx, .Lgpr_setup_finished
LOOP_OVER_SHORTY_STORING_GPRS r9, r9d, r10, r14, rdi, rbx, .Lgpr_setup_finished
LOOP_OVER_INTs r10, r14, rdi, rbx, r11, .Lgpr_setup_finished
.Lgpr_setup_finished:
leaq 1(%rbp), %r10 // shorty + 1 ; ie skip return arg character
movq $$0, %r14 // reset counter
LOOP_OVER_SHORTY_STORING_XMMS xmm0, r10, r14, rdi, .Lxmm_setup_finished
LOOP_OVER_SHORTY_STORING_XMMS xmm1, r10, r14, rdi, .Lxmm_setup_finished
LOOP_OVER_SHORTY_STORING_XMMS xmm2, r10, r14, rdi, .Lxmm_setup_finished
LOOP_OVER_SHORTY_STORING_XMMS xmm3, r10, r14, rdi, .Lxmm_setup_finished
LOOP_OVER_SHORTY_STORING_XMMS xmm4, r10, r14, rdi, .Lxmm_setup_finished
LOOP_OVER_SHORTY_STORING_XMMS xmm5, r10, r14, rdi, .Lxmm_setup_finished
LOOP_OVER_SHORTY_STORING_XMMS xmm6, r10, r14, rdi, .Lxmm_setup_finished
LOOP_OVER_SHORTY_STORING_XMMS xmm7, r10, r14, rdi, .Lxmm_setup_finished
LOOP_OVER_FPs r10, r14, rdi, r11, .Lxmm_setup_finished
.Lxmm_setup_finished:
CFI_DEFINE_DEX_PC_WITH_OFFSET(CFI_TMP, CFI_DEX, 0)
// Set rIBASE
leaq artNterpAsmInstructionStart(%rip), rIBASE
/* 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 rax, rdx, rcx as temporaries.
movq rSELF:THREAD_SELF_OFFSET, %rax
movq rPC, %rdx
salq MACRO_LITERAL(THREAD_INTERPRETER_CACHE_SIZE_SHIFT), %rdx
andq MACRO_LITERAL(THREAD_INTERPRETER_CACHE_SIZE_MASK), %rdx
cmpq THREAD_INTERPRETER_CACHE_OFFSET(%rax, %rdx, 1), rPC
jne ${miss_label}
movq __SIZEOF_POINTER__+THREAD_INTERPRETER_CACHE_OFFSET(%rax, %rdx, 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 C compiler compiled each interpreter separately.
common_errDivideByZero:
EXPORT_PC
call art_quick_throw_div_zero
// Expect array in edi, index in esi.
common_errArrayIndex:
EXPORT_PC
movl MIRROR_ARRAY_LENGTH_OFFSET(%edi), %eax
movl %esi, %edi
movl %eax, %esi
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_string_init=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_string_init=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("%rdi", miss_label="2f")
cmpq $$0, rSELF:THREAD_READ_BARRIER_MARK_REG00_OFFSET
jne 3f
4:
callq *rSELF:THREAD_ALLOC_OBJECT_ENTRYPOINT_OFFSET
1:
SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
2:
movq rSELF:THREAD_SELF_OFFSET, %rdi
movq 0(%rsp), %rsi
movq rPC, %rdx
call nterp_allocate_object
jmp 1b
3:
// 07 is %rdi
call art_quick_read_barrier_mark_reg07
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("%rdi", miss_label="2f")
cmpq $$0, rSELF:THREAD_READ_BARRIER_MARK_REG00_OFFSET
jne 3f
1:
movzbl rINSTbl,%esi
sarl $$4,%esi # esi<- B
GET_VREG %esi %rsi # esi<- vB (array length)
andb $$0xf,rINSTbl # rINST<- A
callq *rSELF:THREAD_ALLOC_ARRAY_ENTRYPOINT_OFFSET
SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
2:
movq rSELF:THREAD_SELF_OFFSET, %rdi
movq 0(%rsp), %rsi
movq rPC, %rdx
call nterp_get_class
movq %rax, %rdi
jmp 1b
3:
// 07 is %rdi
call art_quick_read_barrier_mark_reg07
jmp 1b
NterpPutObjectInstanceField:
movl rINST, %ebp # rbp <- BA
andl $$0xf, %ebp # rbp <- A
GET_VREG %ecx, %rbp # ecx <- v[A]
sarl $$4, rINST
// Fast-path which gets the field from thread-local cache.
% fetch_from_thread_cache("%rax", miss_label="2f")
1:
GET_VREG rINST, rINSTq # vB (object we're operating on)
testl rINST, rINST # is object null?
je common_errNullObject
POISON_HEAP_REF ecx
movl %ecx, (rINSTq,%rax,1)
testl %ecx, %ecx
je 4f
movq rSELF:THREAD_CARD_TABLE_OFFSET, %rax
shrq $$CARD_TABLE_CARD_SHIFT, rINSTq
movb %al, (%rax, rINSTq, 1)
4:
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
2:
EXPORT_PC
movq rSELF:THREAD_SELF_OFFSET, %rdi
movq 0(%rsp), %rsi
movq rPC, %rdx
// %rcx is already set.
call nterp_get_instance_field_offset
// Reload the value as it may have moved.
GET_VREG %ecx, %rbp # ecx <- v[A]
testl %eax, %eax
jns 1b
GET_VREG rINST, rINSTq # vB (object we're operating on)
testl rINST, rINST # is object null?
je common_errNullObject
negl %eax
POISON_HEAP_REF ecx
movl %ecx, (rINSTq,%rax,1)
testl %ecx, %ecx
je 5f
movq rSELF:THREAD_CARD_TABLE_OFFSET, %rax
shrq $$CARD_TABLE_CARD_SHIFT, rINSTq
movb %al, (%rax, rINSTq, 1)
5:
lock addl $$0, (%rsp)
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
NterpGetObjectInstanceField:
// Fast-path which gets the field from thread-local cache.
% fetch_from_thread_cache("%rax", miss_label="2f")
1:
movl rINST, %ecx # rcx <- BA
sarl $$4, %ecx # ecx <- B
GET_VREG %ecx, %rcx # 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 (%rcx,%rax,1), %eax
jnz 3f
UNPOISON_HEAP_REF eax // Affects flags, so we cannot unpoison before the jnz.
4:
andb $$0xf,rINSTbl # rINST <- A
SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
2:
EXPORT_PC
movq rSELF:THREAD_SELF_OFFSET, %rdi
movq 0(%rsp), %rsi
movq rPC, %rdx
movq $$0, %rcx
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:
UNPOISON_HEAP_REF eax
// reg00 is eax
call art_quick_read_barrier_mark_reg00
jmp 4b
NterpPutObjectStaticField:
GET_VREG %ebp, rINSTq
// Fast-path which gets the field from thread-local cache.
% fetch_from_thread_cache("%rax", miss_label="2f")
1:
movl ART_FIELD_OFFSET_OFFSET(%rax), %edx
movl ART_FIELD_DECLARING_CLASS_OFFSET(%rax), %eax
cmpq $$0, rSELF:THREAD_READ_BARRIER_MARK_REG00_OFFSET
jne 3f
5:
POISON_HEAP_REF ebp
movl %ebp, (%eax, %edx, 1)
testl %ebp, %ebp
je 4f
movq rSELF:THREAD_CARD_TABLE_OFFSET, %rcx
shrq $$CARD_TABLE_CARD_SHIFT, %rax
movb %cl, (%rax, %rcx, 1)
4:
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
2:
EXPORT_PC
movq rSELF:THREAD_SELF_OFFSET, %rdi
movq 0(%rsp), %rsi
movq rPC, %rdx
movq %rbp, %rcx
call nterp_get_static_field
// Reload the value as it may have moved.
GET_VREG %ebp, rINSTq
testq MACRO_LITERAL(1), %rax
je 1b
CLEAR_VOLATILE_MARKER %rax
movl ART_FIELD_OFFSET_OFFSET(%rax), %edx
movl ART_FIELD_DECLARING_CLASS_OFFSET(%rax), %eax
cmpq $$0, rSELF:THREAD_READ_BARRIER_MARK_REG00_OFFSET
jne 7f
6:
POISON_HEAP_REF ebp
movl %ebp, (%eax, %edx, 1)
testl %ebp, %ebp
je 8f
movq rSELF:THREAD_CARD_TABLE_OFFSET, %rcx
shrq $$CARD_TABLE_CARD_SHIFT, %rax
movb %cl, (%rax, %rcx, 1)
8:
lock addl $$0, (%rsp)
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("%rax", miss_label="2f")
1:
movl ART_FIELD_OFFSET_OFFSET(%rax), %edx
movl ART_FIELD_DECLARING_CLASS_OFFSET(%rax), %eax
cmpq $$0, rSELF:THREAD_READ_BARRIER_MARK_REG00_OFFSET
jne 5f
6:
testb $$READ_BARRIER_TEST_VALUE, GRAY_BYTE_OFFSET(%eax)
movl (%eax, %edx, 1), %eax
jnz 3f
UNPOISON_HEAP_REF eax // Affects flags, so we cannot unpoison before the jnz.
4:
SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
2:
EXPORT_PC
movq rSELF:THREAD_SELF_OFFSET, %rdi
movq 0(%rsp), %rsi
movq rPC, %rdx
movq $$0, %rcx
call nterp_get_static_field
andq $$-2, %rax
jmp 1b
3:
UNPOISON_HEAP_REF eax
call art_quick_read_barrier_mark_reg00
jmp 4b
5:
call art_quick_read_barrier_mark_reg00
jmp 6b
NterpGetBooleanStaticField:
OP_SGET load="movsbl", 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="movq", 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=rINSTq, store="movq", 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=rINSTq, store="movq", 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="movq", wide=1
NterpGetInstanceField:
OP_IGET load="movl", wide=0
NterpHandleHotnessOverflow:
CHECK_AND_UPDATE_SHARED_MEMORY_METHOD if_hot=1f, if_not_hot=4f
1:
movq rPC, %rsi
movq rFP, %rdx
call nterp_hot_method
testq %rax, %rax
jne 3f
2:
FETCH_INST
GOTO_NEXT
3:
// Drop the current frame.
movq -8(rREFS), %rsp
CFI_DEF_CFA(rsp, CALLEE_SAVES_SIZE)
// Setup the new frame
movq OSR_DATA_FRAME_SIZE(%rax), %rcx
// Given stack size contains all callee saved registers, remove them.
subq $$CALLEE_SAVES_SIZE, %rcx
// Remember CFA.
movq %rsp, %rbp
CFI_DEF_CFA_REGISTER(rbp)
subq %rcx, %rsp
movq %rsp, %rdi // rdi := beginning of stack
leaq OSR_DATA_MEMORY(%rax), %rsi // rsi := memory to copy
rep movsb // while (rcx--) { *rdi++ = *rsi++ }
// Fetch the native PC to jump to and save it in a callee-save register.
movq OSR_DATA_NATIVE_PC(%rax), %rbx
// Free the memory holding OSR Data.
movq %rax, %rdi
call free
// Jump to the compiled code.
jmp *%rbx
4:
DO_SUSPEND_CHECK continue_label=2b
jmp 2b
NterpHandleInvokeInterfaceOnObjectMethodRange:
shrl $$16, %eax
movq MIRROR_CLASS_VTABLE_OFFSET_64(%edx, %eax, 8), %rdi
jmp NterpCommonInvokeInstanceRange
NterpHandleInvokeInterfaceOnObjectMethod:
shrl $$16, %eax
movq MIRROR_CLASS_VTABLE_OFFSET_64(%edx, %eax, 8), %rdi
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
nterp_to_nterp_static_non_range:
.cfi_startproc
.cfi_def_cfa rsp, 8
SETUP_STACK_FOR_INVOKE
SETUP_NON_RANGE_ARGUMENTS_AND_EXECUTE is_static=1, is_string_init=0
.cfi_endproc
nterp_to_nterp_string_init_non_range:
.cfi_startproc
.cfi_def_cfa rsp, 8
SETUP_STACK_FOR_INVOKE
SETUP_NON_RANGE_ARGUMENTS_AND_EXECUTE is_static=0, is_string_init=1
.cfi_endproc
nterp_to_nterp_instance_non_range:
.cfi_startproc
.cfi_def_cfa rsp, 8
SETUP_STACK_FOR_INVOKE
SETUP_NON_RANGE_ARGUMENTS_AND_EXECUTE is_static=0, is_string_init=0
.cfi_endproc
nterp_to_nterp_static_range:
.cfi_startproc
.cfi_def_cfa rsp, 8
SETUP_STACK_FOR_INVOKE
SETUP_RANGE_ARGUMENTS_AND_EXECUTE is_static=1
.cfi_endproc
nterp_to_nterp_instance_range:
.cfi_startproc
.cfi_def_cfa rsp, 8
SETUP_STACK_FOR_INVOKE
SETUP_RANGE_ARGUMENTS_AND_EXECUTE is_static=0
.cfi_endproc
nterp_to_nterp_string_init_range:
.cfi_startproc
.cfi_def_cfa rsp, 8
SETUP_STACK_FOR_INVOKE
SETUP_RANGE_ARGUMENTS_AND_EXECUTE is_static=0, is_string_init=1
.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, NterpGetClass
NTERP_TRAMPOLINE nterp_allocate_object, NterpAllocateObject
NTERP_TRAMPOLINE nterp_get_method, NterpGetMethod
NTERP_TRAMPOLINE nterp_hot_method, NterpHotMethod
NTERP_TRAMPOLINE nterp_load_object, NterpLoadObject
DEFINE_FUNCTION nterp_deliver_pending_exception
DELIVER_PENDING_EXCEPTION
END_FUNCTION nterp_deliver_pending_exception
// 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.
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}