// 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 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 NterpHotnessCheck
FETCH_INST
GOTO_NEXT
.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
// 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
.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 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
// 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:
% pass
.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:
% pass
.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:
% pass
.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:
% pass
.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 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
% pass
.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
// 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
.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)
.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.
% pass
.elseif \is_custom
// We always go to compiled code for custom calls.
% pass
.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.
% pass
.elseif \is_custom
// No fast path for custom calls.
% pass
.elseif \is_string_init
// No fast path for string.init.
% pass
.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
.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
.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.
% pass
.elseif \is_custom
// We always go to compiled code for custom calls.
% pass
.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.
% pass
.elseif \is_custom
// No fast path for custom calls.
% pass
.elseif \is_string_init
// No fast path for string.init.
% pass
.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
.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
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):
% pass
.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
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
// 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
// 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
NterpHotnessCheck:
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 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
// 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
// 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):
// 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 opcode_name_prefixes():
% return ["nterp_"]
%def opcode_start():
ENTRY ${opcode}
% pass
%def opcode_end():
END ${opcode}
// Advance to the end of this handler. Causes error if we are past that point.
.org ${opcode} + NTERP_HANDLER_SIZE // ${opcode} handler is too big!
%def opcode_slow_path_start(name):
ENTRY ${name}
% pass
%def opcode_slow_path_end(name):
END ${name}
Messung V0.5 in Prozent
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.66Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-06-29)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.