// Macro for defining entrypoints into runtime. We don't need to save registers
// (we're not holding references there), but there is no
// kDontSave runtime method. So just use the kSaveRefsOnly runtime method.
.macro NTERP_TRAMPOLINE name, helper
DEFINE_FUNCTION \name
movd %ebx, %xmm0
SETUP_SAVE_REFS_ONLY_FRAME ebx
movd %xmm0, %ebx
PUSH_ARG ebx
PUSH_ARG edx
PUSH_ARG ecx
PUSH_ARG eax
call \helper
DECREASE_FRAME 16
RESTORE_IBASE_WITH_CFA
FETCH_INST_CLEAR_OPCODE
RESTORE_SAVE_REFS_ONLY_FRAME
cmpl LITERAL(0), %fs:THREAD_EXCEPTION_OFFSET
jne nterp_deliver_pending_exception
ret
END_FUNCTION \name
.endm
.macro BRANCH
leal (rPC, rINST, 2), rPC
// Update method counter and do a suspend check if the branch is negative or zero.
testl rINST, rINST
jle NterpHotnessCheck
FETCH_INST
GOTO_NEXT
.endm
// Expects:
// - edx, and eax to be available.
// Outputs:
// - \registers contains the dex registers size
// - \outs contains the outs size
// - if load_ins is 1, \ins contains the ins
// - \code_item is replaced with a pointer to the instructions
.macro FETCH_CODE_ITEM_INFO code_item, registers, outs, ins, load_ins
// Fetch dex register size.
movzwl CODE_ITEM_REGISTERS_SIZE_OFFSET(\code_item), \registers
// Fetch outs size.
movzwl CODE_ITEM_OUTS_SIZE_OFFSET(\code_item), \outs
.if \load_ins
movzwl CODE_ITEM_INS_SIZE_OFFSET(\code_item), \ins
.endif
addl $$CODE_ITEM_INSNS_OFFSET, \code_item
.endm
// Setup the stack to start executing the method. Expects:
// - eax, edx, and ebx to be available.
//
// Inputs
// - code_item: where the code item is
// - refs: register where the pointer to dex references will be
// - fp: register where the pointer to dex values will be
// - cfi_refs: CFI register number of refs
// - load_ins: whether to store the 'ins' value of the code item in esi
//
// Outputs
// - ebx contains the dex registers size
// - edx contains the old stack pointer.
// - \code_item is replace with a pointer to the instructions
// - if load_ins is 1, esi contains the ins
.macro SETUP_STACK_FRAME code_item, refs, fp, cfi_refs, load_ins
FETCH_CODE_ITEM_INFO \code_item, %ebx, \refs, %esi, \load_ins
// Puts the next floating point argument into the expected register,
// fetching values based on a range invoke.
// Uses eax as temporary.
.macro LOOP_RANGE_OVER_SHORTY_LOADING_XMMS xmm_reg, shorty, arg_index, stack_index, finished 1: // LOOP
movb (REG_VAR(shorty)), %al // al := *shorty
addl MACRO_LITERAL(1), REG_VAR(shorty) // shorty++
cmpb MACRO_LITERAL(0), %al // if (al == '\0') goto finished
je VAR(finished)
cmpb MACRO_LITERAL(68), %al // if (al == 'D') goto FOUND_DOUBLE
je 2f
cmpb MACRO_LITERAL(70), %al // if (al == 'F') goto FOUND_FLOAT
je 3f
addl MACRO_LITERAL(1), REG_VAR(arg_index)
addl MACRO_LITERAL(1), REG_VAR(stack_index)
// Handle extra argument in arg array taken by a long.
cmpb MACRO_LITERAL(74), %al // if (al != 'J') goto LOOP
jne 1b
addl MACRO_LITERAL(1), REG_VAR(arg_index)
addl MACRO_LITERAL(1), REG_VAR(stack_index)
jmp 1b // goto LOOP 2: // FOUND_DOUBLE
GET_VREG_XMMd REG_VAR(xmm_reg), REG_VAR(arg_index)
addl MACRO_LITERAL(2), REG_VAR(arg_index)
addl MACRO_LITERAL(2), REG_VAR(stack_index)
jmp 4f 3: // FOUND_FLOAT
GET_VREG_XMMs REG_VAR(xmm_reg), REG_VAR(arg_index)
addl MACRO_LITERAL(1), REG_VAR(arg_index)
add MACRO_LITERAL(1), REG_VAR(stack_index) 4:
% pass
.endm
// Puts the next floating point argument into the expected stack slot,
// fetching values based on a range invoke.
// Uses eax as temporary.
//
// TODO: We could just copy all the vregs to the stack slots in a simple loop
// (or REP MOVSD) without looking at the shorty at all. (We could also drop
// the "stack_index" from the macros for loading registers.) We could also do
// that conditionally if argument word count > 3; otherwise we know that all
// args fit into registers.
.macro LOOP_RANGE_OVER_FPs shorty, arg_index, stack_index, finished 1: // LOOP
movb (REG_VAR(shorty)), %al // bl := *shorty
addl MACRO_LITERAL(1), REG_VAR(shorty) // shorty++
cmpb MACRO_LITERAL(0), %al // if (al == '\0') goto finished
je VAR(finished)
cmpb MACRO_LITERAL(68), %al // if (al == 'D') goto FOUND_DOUBLE
je 2f
cmpb MACRO_LITERAL(70), %al // if (al == 'F') goto FOUND_FLOAT
je 3f
addl MACRO_LITERAL(1), REG_VAR(arg_index)
addl MACRO_LITERAL(1), REG_VAR(stack_index)
// Handle extra argument in arg array taken by a long.
cmpb MACRO_LITERAL(74), %al // if (al != 'J') goto LOOP
jne 1b
addl MACRO_LITERAL(1), REG_VAR(arg_index)
addl MACRO_LITERAL(1), REG_VAR(stack_index)
jmp 1b // goto LOOP 2: // FOUND_DOUBLE
movq (rFP, REG_VAR(arg_index), 4), %xmm4
movq %xmm4, 4(%esp, REG_VAR(stack_index), 4)
addl MACRO_LITERAL(2), REG_VAR(arg_index)
addl MACRO_LITERAL(2), REG_VAR(stack_index)
jmp 1b 3: // FOUND_FLOAT
movl (rFP, REG_VAR(arg_index), 4), %eax
movl %eax, 4(%esp, REG_VAR(stack_index), 4)
addl MACRO_LITERAL(1), REG_VAR(arg_index)
addl MACRO_LITERAL(1), REG_VAR(stack_index)
jmp 1b
.endm
// Puts the next int/long/object argument in the expected register,
// fetching values based on a range invoke.
// Uses eax as temporary.
.macro LOOP_RANGE_OVER_SHORTY_LOADING_GPRS gpr_reg, gpr_long_reg, shorty, arg_index, stack_index, finished, if_long, is_ebx 1: // LOOP
movb (REG_VAR(shorty)), %al // al := *shorty
addl MACRO_LITERAL(1), REG_VAR(shorty) // shorty++
cmpb MACRO_LITERAL(0), %al // if (al == '\0') goto finished
je VAR(finished)
cmpb MACRO_LITERAL(74), %al // if (al == 'J') goto FOUND_LONG
je 2f
cmpb MACRO_LITERAL(70), %al // if (al == 'F') goto SKIP_FLOAT
je 3f
cmpb MACRO_LITERAL(68), %al // if (al == 'D') goto SKIP_DOUBLE
je 4f
movl (rFP, REG_VAR(arg_index), 4), REG_VAR(gpr_reg)
addl MACRO_LITERAL(1), REG_VAR(arg_index)
addl MACRO_LITERAL(1), REG_VAR(stack_index)
jmp 5f 2: // FOUND_LONG
.if \is_ebx
// Put back shorty and exit
subl MACRO_LITERAL(1), REG_VAR(shorty)
.else
movl (rFP, REG_VAR(arg_index), 4), REG_VAR(gpr_reg)
movl 4(rFP, REG_VAR(arg_index), 4), REG_VAR(gpr_long_reg)
addl MACRO_LITERAL(2), REG_VAR(arg_index)
addl MACRO_LITERAL(2), REG_VAR(stack_index)
.endif
jmp \if_long 3: // SKIP_FLOAT
addl MACRO_LITERAL(1), REG_VAR(arg_index)
addl MACRO_LITERAL(1), REG_VAR(stack_index)
jmp 1b 4: // SKIP_DOUBLE
addl MACRO_LITERAL(2), REG_VAR(arg_index)
addl MACRO_LITERAL(2), REG_VAR(stack_index)
jmp 1b 5:
% pass
.endm
// Puts the next int/long/object argument in the expected stack slot,
// fetching values based on a range invoke.
// Uses eax as temporary.
.macro LOOP_RANGE_OVER_INTs offset, shorty, arg_index, stack_index, finished 1: // LOOP
movb (REG_VAR(shorty)), %al // al := *shorty
addl MACRO_LITERAL(1), REG_VAR(shorty) // shorty++
cmpb MACRO_LITERAL(0), %al // if (al == '\0') goto finished
je VAR(finished)
cmpb MACRO_LITERAL(74), %al // if (al == 'J') goto FOUND_LONG
je 2f
cmpb MACRO_LITERAL(70), %al // if (al == 'F') goto SKIP_FLOAT
je 3f
cmpb MACRO_LITERAL(68), %al // if (al == 'D') goto SKIP_DOUBLE
je 4f
movl (rFP, REG_VAR(arg_index), 4), %eax
// Add 4 for the ArtMethod.
movl %eax, (4 + \offset)(%esp, REG_VAR(stack_index), 4) 3: // SKIP_FLOAT
addl MACRO_LITERAL(1), REG_VAR(arg_index)
addl MACRO_LITERAL(1), REG_VAR(stack_index)
jmp 1b 2: // FOUND_LONG
movl (rFP, REG_VAR(arg_index), 4), %eax
// Add 4 for the ArtMethod.
movl %eax, (4 + \offset)(%esp, REG_VAR(stack_index), 4)
movl 4(rFP, REG_VAR(arg_index), 4), %eax
// Add 4 for the ArtMethod and 4 for other half.
movl %eax, (4 + 4 + \offset)(%esp, REG_VAR(stack_index), 4) 4: // SKIP_DOUBLE
addl MACRO_LITERAL(2), REG_VAR(arg_index)
addl MACRO_LITERAL(2), REG_VAR(stack_index)
jmp 1b
.endm
// Puts the next floating point parameter passed in physical register
// in the expected dex register array entry.
// Uses eax as temporary.
.macro LOOP_OVER_SHORTY_STORING_XMMS xmm_reg, shorty, arg_index, fp, finished 1: // LOOP
movb (REG_VAR(shorty)), %al // al := *shorty
addl MACRO_LITERAL(1), REG_VAR(shorty) // shorty++
cmpb MACRO_LITERAL(0), %al // if (al == '\0') goto finished
je VAR(finished)
cmpb MACRO_LITERAL(68), %al // if (al == 'D') goto FOUND_DOUBLE
je 2f
cmpb MACRO_LITERAL(70), %al // if (al == 'F') goto FOUND_FLOAT
je 3f
addl MACRO_LITERAL(1), REG_VAR(arg_index)
// Handle extra argument in arg array taken by a long.
cmpb MACRO_LITERAL(74), %al // if (al != 'J') goto LOOP
jne 1b
addl MACRO_LITERAL(1), REG_VAR(arg_index)
jmp 1b // goto LOOP 2: // FOUND_DOUBLE
movq REG_VAR(xmm_reg),(REG_VAR(fp), REG_VAR(arg_index), 4)
addl MACRO_LITERAL(2), REG_VAR(arg_index)
jmp 4f 3: // FOUND_FLOAT
movss REG_VAR(xmm_reg), (REG_VAR(fp), REG_VAR(arg_index), 4)
addl MACRO_LITERAL(1), REG_VAR(arg_index) 4:
% 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 eax as temporary.
.macro LOOP_OVER_SHORTY_STORING_GPRS offset, offset_long, stack_ptr, shorty, arg_index, regs, refs, finished, if_long, is_ebx 1: // LOOP
movb (REG_VAR(shorty)), %al // al := *shorty
addl MACRO_LITERAL(1), REG_VAR(shorty) // shorty++
cmpb MACRO_LITERAL(0), %al // if (al == '\0') goto finished
je VAR(finished)
cmpb MACRO_LITERAL(74), %al // if (al == 'J') goto FOUND_LONG
je 2f
cmpb MACRO_LITERAL(70), %al // if (al == 'F') goto SKIP_FLOAT
je 3f
cmpb MACRO_LITERAL(68), %al // if (al == 'D') goto SKIP_DOUBLE
je 4f
cmpb MACRO_LITERAL(76), %al // if (al != 'L') goto NOT_REFERENCE
jne 6f
movl \offset(REG_VAR(stack_ptr)), %eax
movl %eax, (REG_VAR(regs), REG_VAR(arg_index), 4)
movl %eax, (REG_VAR(refs), REG_VAR(arg_index), 4)
addl MACRO_LITERAL(1), REG_VAR(arg_index)
jmp 5f 2: // FOUND_LONG
.if \is_ebx
// Put back shorty and jump to \if_long
subl MACRO_LITERAL(1), REG_VAR(shorty)
.else
movl \offset(REG_VAR(stack_ptr)), %eax
movl %eax, (REG_VAR(regs), REG_VAR(arg_index), 4)
movl \offset_long(REG_VAR(stack_ptr)), %eax
movl %eax, 4(REG_VAR(regs), REG_VAR(arg_index), 4)
addl MACRO_LITERAL(2), REG_VAR(arg_index)
.endif
jmp \if_long 3: // SKIP_FLOAT
addl MACRO_LITERAL(1), REG_VAR(arg_index)
jmp 1b 4: // SKIP_DOUBLE
addl MACRO_LITERAL(2), REG_VAR(arg_index)
jmp 1b 6: // NOT_REFERENCE
movl \offset(REG_VAR(stack_ptr)), %eax
movl %eax, (REG_VAR(regs), REG_VAR(arg_index), 4)
addl MACRO_LITERAL(1), REG_VAR(arg_index) 5:
% pass
.endm
// Puts the next floating point parameter passed in stack
// in the expected dex register array entry.
// Uses eax as temporary.
//
// TODO: Or we could just spill regs to the reserved slots in the caller's
// frame and copy all regs in a simple loop. This time, however, we would
// need to look at the shorty anyway to look for the references.
// (The trade-off is different for passing arguments and receiving them.)
.macro LOOP_OVER_FPs shorty, arg_index, regs, stack_ptr, finished 1: // LOOP
movb (REG_VAR(shorty)), %al // al := *shorty
addl MACRO_LITERAL(1), REG_VAR(shorty) // shorty++
cmpb MACRO_LITERAL(0), %al // if (al == '\0') goto finished
je VAR(finished)
cmpb MACRO_LITERAL(68), %al // if (al == 'D') goto FOUND_DOUBLE
je 2f
cmpb MACRO_LITERAL(70), %al // if (al == 'F') goto FOUND_FLOAT
je 3f
addl MACRO_LITERAL(1), REG_VAR(arg_index)
// Handle extra argument in arg array taken by a long.
cmpb MACRO_LITERAL(74), %al // if (al != 'J') goto LOOP
jne 1b
addl MACRO_LITERAL(1), REG_VAR(arg_index)
jmp 1b // goto LOOP 2: // FOUND_DOUBLE
movq OFFSET_TO_FIRST_ARGUMENT_IN_STACK(REG_VAR(stack_ptr), REG_VAR(arg_index), 4), %xmm4
movq %xmm4, (REG_VAR(regs), REG_VAR(arg_index), 4)
addl MACRO_LITERAL(2), REG_VAR(arg_index)
jmp 1b 3: // FOUND_FLOAT
movl OFFSET_TO_FIRST_ARGUMENT_IN_STACK(REG_VAR(stack_ptr), REG_VAR(arg_index), 4), %eax
movl %eax, (REG_VAR(regs), REG_VAR(arg_index), 4)
addl MACRO_LITERAL(1), REG_VAR(arg_index)
jmp 1b
.endm
// Puts the next int/long/object parameter passed in stack
// in the expected dex register array entry, and in case of object in the
// expected reference array entry.
// Uses eax as temporary.
.macro LOOP_OVER_INTs shorty, arg_index, regs, refs, stack_ptr, finished 1: // LOOP
movb (REG_VAR(shorty)), %al // al := *shorty
addl MACRO_LITERAL(1), REG_VAR(shorty) // shorty++
cmpb MACRO_LITERAL(0), %al // if (al == '\0') goto finished
je VAR(finished)
cmpb MACRO_LITERAL(74), %al // if (al == 'J') goto FOUND_LONG
je 2f
cmpb MACRO_LITERAL(76), %al // if (al == 'L') goto FOUND_REFERENCE
je 6f
cmpb MACRO_LITERAL(70), %al // if (al == 'F') goto SKIP_FLOAT
je 3f
cmpb MACRO_LITERAL(68), %al // if (al == 'D') goto SKIP_DOUBLE
je 4f
movl OFFSET_TO_FIRST_ARGUMENT_IN_STACK(REG_VAR(stack_ptr), REG_VAR(arg_index), 4), %eax
movl %eax, (REG_VAR(regs), REG_VAR(arg_index), 4)
addl MACRO_LITERAL(1), REG_VAR(arg_index)
jmp 1b 6: // FOUND_REFERENCE
movl OFFSET_TO_FIRST_ARGUMENT_IN_STACK(REG_VAR(stack_ptr), REG_VAR(arg_index), 4), %eax
movl %eax, (REG_VAR(regs), REG_VAR(arg_index), 4)
movl %eax, (REG_VAR(refs), REG_VAR(arg_index), 4) 3: // SKIP_FLOAT
addl MACRO_LITERAL(1), REG_VAR(arg_index)
jmp 1b 2: // FOUND_LONG
movl OFFSET_TO_FIRST_ARGUMENT_IN_STACK(REG_VAR(stack_ptr), REG_VAR(arg_index), 4), %eax
movl %eax, (REG_VAR(regs), REG_VAR(arg_index), 4)
movl (OFFSET_TO_FIRST_ARGUMENT_IN_STACK+4)(REG_VAR(stack_ptr), REG_VAR(arg_index), 4), %eax
movl %eax, 4(REG_VAR(regs), REG_VAR(arg_index), 4) 4: // SKIP_DOUBLE
addl MACRO_LITERAL(2), REG_VAR(arg_index)
jmp 1b
.endm
// Increase method hotness and do suspend check before starting executing the method.
.macro START_EXECUTING_INSTRUCTIONS
movl (%esp), %eax
movzwl ART_METHOD_HOTNESS_COUNT_OFFSET(%eax), %ecx
#if (NTERP_HOTNESS_VALUE != 0)
#error Expected 0 for hotness value
#endif
// If the counter is at zero, handle this in the runtime.
testl %ecx, %ecx
je 3f
// Update counter.
addl $$-1, %ecx
movw %cx, ART_METHOD_HOTNESS_COUNT_OFFSET(%eax) 1:
testl $$(THREAD_SUSPEND_OR_CHECKPOINT_REQUEST), rSELF:THREAD_FLAGS_OFFSET
jz 2f
EXPORT_PC
call SYMBOL(art_quick_test_suspend)
RESTORE_IBASE 2:
FETCH_INST
GOTO_NEXT 3:
CHECK_AND_UPDATE_SHARED_MEMORY_METHOD if_hot=4f, if_not_hot=1b 4:
movl $$0, ARG1
movl rFP, ARG2
call nterp_hot_method
jmp 2b
.endm
.macro SPILL_ALL_CALLEE_SAVES
PUSH edi
PUSH esi
PUSH ebp
.endm
.macro RESTORE_ALL_CALLEE_SAVES
POP ebp
POP esi
POP edi
.endm
.macro COMMON_INVOKE_NON_RANGE is_static=0, is_interface=0, suffix="", is_string_init=0, is_polymorphic=0, is_custom=0
.if \is_polymorphic
// No fast path for polymorphic calls.
% 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(%eax)
je .Lfast_path_with_few_args_\suffix
movzbl 1(rPC), %edx
movl %edx, %ebx
shrl MACRO_LITERAL(4), %ebx # Number of arguments
.if \is_static
jz .Linvoke_fast_path_\suffix # shl sets the Z flag
.else
cmpl MACRO_LITERAL(1), %ebx
je .Linvoke_fast_path_\suffix
.endif
movzwl 4(rPC), %esi
cmpl MACRO_LITERAL(2), %ebx
.if \is_static
jl .Lone_arg_fast_path_\suffix
.endif
je .Ltwo_args_fast_path_\suffix
cmpl MACRO_LITERAL(4), %ebx
jl .Lthree_args_fast_path_\suffix
je .Lfour_args_fast_path_\suffix
andl MACRO_LITERAL(0xf), %edx
GET_VREG %edx, %edx
movl %edx, (4 + 4 * 4)(%esp)
.Lfour_args_fast_path_\suffix:
movl %esi, %edx
shrl MACRO_LITERAL(12), %edx
GET_VREG %edx, %edx
movl %edx, (4 + 3 * 4)(%esp)
.Lthree_args_fast_path_\suffix:
movl %esi, %ebx
shrl MACRO_LITERAL(8), %ebx
andl MACRO_LITERAL(0xf), %ebx
GET_VREG %ebx, %ebx
.Ltwo_args_fast_path_\suffix:
movl %esi, %edx
shrl MACRO_LITERAL(4), %edx
andl MACRO_LITERAL(0xf), %edx
GET_VREG %edx, %edx
.Lone_arg_fast_path_\suffix:
.if \is_static
andl MACRO_LITERAL(0xf), %esi
GET_VREG %ecx, %esi
.else
// First argument already in %ecx.
% pass
.endif
.Linvoke_fast_path_\suffix:
// Fetch PC before calling for proper stack unwinding.
FETCH_PC
call *ART_METHOD_QUICK_CODE_OFFSET_32(%eax) // Call the method.
// In case of a long return, save the high half into LOCAL0
SAVE_WIDE_RETURN
RESTORE_IBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
.Lfast_path_with_few_args_\suffix:
// Fast path when we have zero or one argument (modulo 'this'). If there
// is one argument, we can put it in both floating point and core register.
movzbl 1(rPC), %edx
shrl MACRO_LITERAL(4), %edx # Number of arguments
.if \is_static
cmpl MACRO_LITERAL(1), %edx
jl .Linvoke_with_few_args_\suffix
jne .Lget_shorty_\suffix
movzwl 4(rPC), %ecx
andl MACRO_LITERAL(0xf), %ecx // dex register of first argument
GET_VREG %ecx, %ecx
movd %ecx, %xmm0
.else
cmpl MACRO_LITERAL(2), %edx
jl .Linvoke_with_few_args_\suffix
jne .Lget_shorty_\suffix
movzwl 4(rPC), %edx
shrl MACRO_LITERAL(4), %edx
andl MACRO_LITERAL(0xf), %edx // dex register of second argument
GET_VREG %edx, %edx
movd %edx, %xmm0
.endif
.Linvoke_with_few_args_\suffix:
// Check if the next instruction is move-result or move-result-wide.
// If it is, we fetch the shorty and jump to the regular invocation.
movzwl 6(rPC), %ebx
andl MACRO_LITERAL(0xfe), %ebx
cmpl MACRO_LITERAL(0x0a), %ebx
je .Lget_shorty_and_invoke_\suffix
call *ART_METHOD_QUICK_CODE_OFFSET_32(%eax) // Call the method.
RESTORE_IBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
.Lget_shorty_and_invoke_\suffix:
GET_SHORTY_SLOW_PATH %esi, \is_interface
jmp .Lgpr_setup_finished_\suffix
.endif
.Lget_shorty_\suffix:
GET_SHORTY %ebx, \is_interface, \is_polymorphic, \is_custom
movl %eax, LOCAL0(%esp)
movl %ebp, LOCAL1(%esp)
movl %ebx, LOCAL2(%esp)
// From this point:
// - ebx contains shorty (in callee-save to switch over return value after call).
// - eax, edx, and ebp are available
// - ecx contains 'this' pointer for instance method.
// TODO: ebp/rREFS is used for stack unwinding, can we find a way to preserve it?
leal 1(%ebx), %edx // shorty + 1 ; ie skip return arg character
movzwl 4(rPC), %ebx // arguments
.if \is_string_init
shrl MACRO_LITERAL(4), %ebx
movl $$1, %ebp // ignore first argument
.elseif \is_static
movl $$0, %ebp // arg_index
.else
shrl MACRO_LITERAL(4), %ebx
movl $$1, %ebp // arg_index
.endif
LOOP_OVER_SHORTY_LOADING_XMMS xmm0, ebx, edx, ebp, .Lxmm_setup_finished_\suffix
LOOP_OVER_SHORTY_LOADING_XMMS xmm1, ebx, edx, ebp, .Lxmm_setup_finished_\suffix
LOOP_OVER_SHORTY_LOADING_XMMS xmm2, ebx, edx, ebp, .Lxmm_setup_finished_\suffix
LOOP_OVER_SHORTY_LOADING_XMMS xmm3, ebx, edx, ebp, .Lxmm_setup_finished_\suffix
// We know this can only be a float.
movb (%edx), %al // al := *shorty
cmpb MACRO_LITERAL(70), %al // if (al != 'F') goto finished
jne .Lxmm_setup_finished_\suffix
movzbl 1(rPC), %eax
andl MACRO_LITERAL(0xf), %eax
GET_VREG %eax, %eax
// Add four for the ArtMethod.
movl %eax, 4(%esp, %ebp, 4)
// We know there is no more argument, jump to the call.
jmp .Lrestore_saved_values_\suffix
.Lxmm_setup_finished_\suffix:
// Reload rREFS for fetching the PC.
movl LOCAL1(%esp), %ebp
// Reload shorty
movl LOCAL2(%esp), %ebx
FETCH_PC
leal 1(%ebx), %ebx // shorty + 1 ; ie skip return arg character
movzwl 4(rPC), %esi // arguments
.if \is_string_init
movl $$0, %ebp // arg_index
shrl MACRO_LITERAL(4), %esi
LOOP_OVER_SHORTY_LOADING_GPRS ecx, edx, esi, ebx, ebp, .Lrestore_saved_values_\suffix, .Lif_long_ebx_\suffix, is_ebx=0
.elseif \is_static
movl $$0, %ebp // arg_index
LOOP_OVER_SHORTY_LOADING_GPRS ecx, edx, esi, ebx, ebp, .Lrestore_saved_values_\suffix, .Lif_long_ebx_\suffix, is_ebx=0
.else
shrl MACRO_LITERAL(4), %esi
movl $$1, %ebp // arg_index
.endif
// For long argument, store second half in eax to not overwrite the shorty.
LOOP_OVER_SHORTY_LOADING_GPRS edx, eax, esi, ebx, ebp, .Lrestore_saved_values_\suffix, .Lif_long_\suffix, is_ebx=0
.Lif_long_ebx_\suffix:
// Store in eax to not overwrite the shorty.
LOOP_OVER_SHORTY_LOADING_GPRS eax, eax, esi, ebx, ebp, .Lrestore_saved_values_\suffix, .Lif_long_\suffix, is_ebx=1
.Lif_long_\suffix:
// Save shorty, as LOOP_OVER_SHORTY_LOADING_INTS might overwrite the LOCAL2 slot for a long argument.
pushl LOCAL2(%esp)
pushl %eax
LOOP_OVER_SHORTY_LOADING_INTS 8, ebx, esi, ebp, .Lrestore_ebx_\suffix, \is_string_init
.Lrestore_ebx_\suffix:
popl %ebx
popl %esi
movl LOCAL0(%esp), %eax
movl LOCAL1(%esp), %ebp
jmp .Lgpr_setup_finished_\suffix
.Lrestore_saved_values_\suffix:
movl LOCAL0(%esp), %eax
movl LOCAL1(%esp), %ebp
movl LOCAL2(%esp), %esi
.Lgpr_setup_finished_\suffix:
// Look at the shorty now, as we'll want %esi to have the PC for proper stack unwinding
// and we're running out of callee-save registers.
cmpb LITERAL(68), (%esi) // Test if result type char == 'D'.
je .Linvoke_double_\suffix
cmpb LITERAL(70), (%esi) // Test if result type char == 'F'.
je .Linvoke_float_\suffix
FETCH_PC
DO_CALL \is_polymorphic, \is_custom
SAVE_WIDE_RETURN
.Ldone_return_\suffix: /* resume execution of caller */
.if \is_string_init
movzwl 4(rPC), %ecx // arguments
andl $$0xf, %ecx
GET_VREG rINST, %ecx
UPDATE_REGISTERS_FOR_STRING_INIT rINST, %eax
.endif
RESTORE_IBASE
.Lfast_path_with_few_args_range_\suffix:
// Fast path when we have zero or one argument (modulo 'this'). If there
// is one argument, we can put it in both floating point and core register.
movzbl 1(rPC), %ebx # Number of arguments
.if \is_static
cmpl MACRO_LITERAL(1), %ebx
jl .Linvoke_with_few_args_range_\suffix
jne .Lget_shorty_range_\suffix
movzwl 4(rPC), %ebx // Dex register of first argument
GET_VREG %ecx, %ebx
movd %ecx, %xmm0
.else
cmpl MACRO_LITERAL(2), %ebx
jl .Linvoke_with_few_args_range_\suffix
jne .Lget_shorty_range_\suffix
movzwl 4(rPC), %ebx
addl MACRO_LITERAL(1), %ebx // dex register of second argument
GET_VREG %edx, %ebx
movd %edx, %xmm0
.endif
.Linvoke_with_few_args_range_\suffix:
// Check if the next instruction is move-result or move-result-wide.
// If it is, we fetch the shorty and jump to the regular invocation.
movzwl 6(rPC), %ebx
and MACRO_LITERAL(0xfe), %ebx
cmpl MACRO_LITERAL(0x0a), %ebx
je .Lget_shorty_and_invoke_range_\suffix
call *ART_METHOD_QUICK_CODE_OFFSET_32(%eax) // Call the method.
RESTORE_IBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
.Lget_shorty_and_invoke_range_\suffix:
GET_SHORTY_SLOW_PATH %esi, \is_interface
jmp .Lgpr_setup_finished_range_\suffix
.endif
.Lget_shorty_range_\suffix:
GET_SHORTY %ebx, \is_interface, \is_polymorphic, \is_custom
movl %eax, LOCAL0(%esp)
movl %ebp, LOCAL1(%esp)
movl %ebx, LOCAL2(%esp)
// From this point:
// - ebx contains shorty (in callee-save to switch over return value after call).
// - eax, edx, ebx, and ebp are available.
// - ecx contains 'this' pointer for instance method.
// TODO: ebp/rREFS is used for stack unwinding, can we find a way to preserve it?
leal 1(%ebx), %edx // shorty + 1 ; ie skip return arg character
movzwl 4(rPC), %ebx // arg start index
.if \is_string_init
addl $$1, %ebx // arg start index
movl $$0, %ebp // index in stack
.elseif \is_static
movl $$0, %ebp // index in stack
.else
addl $$1, %ebx // arg start index
movl $$1, %ebp // index in stack
.endif
LOOP_RANGE_OVER_SHORTY_LOADING_XMMS xmm0, edx, ebx, ebp, .Lxmm_setup_finished_range_\suffix
LOOP_RANGE_OVER_SHORTY_LOADING_XMMS xmm1, edx, ebx, ebp, .Lxmm_setup_finished_range_\suffix
LOOP_RANGE_OVER_SHORTY_LOADING_XMMS xmm2, edx, ebx, ebp, .Lxmm_setup_finished_range_\suffix
LOOP_RANGE_OVER_SHORTY_LOADING_XMMS xmm3, edx, ebx, ebp, .Lxmm_setup_finished_range_\suffix
LOOP_RANGE_OVER_FPs edx, ebx, ebp, .Lxmm_setup_finished_range_\suffix
.Lxmm_setup_finished_range_\suffix:
// Reload rREFS for fetching the PC.
movl LOCAL1(%esp), %ebp
// Reload shorty
movl LOCAL2(%esp), %ebx
FETCH_PC
leal 1(%ebx), %ebx // shorty + 1 ; ie skip return arg character
// From this point:
// - ebx contains shorty
// - eax and ebp are available.
// - ecx contains 'this' pointer for instance method.
movzwl 4(rPC), %ebp // arg start index
// rPC (esi) is now available
.if \is_string_init
addl $$1, %ebp // arg start index
movl $$0, %esi // index in stack
LOOP_RANGE_OVER_SHORTY_LOADING_GPRS ecx, edx, ebx, ebp, esi, .Lrestore_saved_values_range_\suffix, .Lif_long_ebx_range_\suffix, is_ebx=0
.elseif \is_static
movl $$0, %esi // index in stack
LOOP_RANGE_OVER_SHORTY_LOADING_GPRS ecx, edx, ebx, ebp, esi, .Lrestore_saved_values_range_\suffix, .Lif_long_ebx_range_\suffix, is_ebx=0
.else
addl $$1, %ebp // arg start index
movl $$1, %esi // index in stack
.endif
// For long argument, store second half in eax to not overwrite the shorty.
LOOP_RANGE_OVER_SHORTY_LOADING_GPRS edx, eax, ebx, ebp, esi, .Lrestore_saved_values_range_\suffix, .Lif_long_range_\suffix, is_ebx=0
.Lif_long_ebx_range_\suffix:
// Store in eax to not overwrite the shorty.
LOOP_RANGE_OVER_SHORTY_LOADING_GPRS eax, eax, ebx, ebp, esi, .Lrestore_saved_values_range_\suffix, .Lif_long_range_\suffix, is_ebx=1
.Lif_long_range_\suffix:
// Save shorty, as LOOP_RANGE_OVER_SHORTY_LOADING_INTS might overwrite the LOCAL2 slot for a long argument.
pushl LOCAL2(%esp)
pushl %eax
LOOP_RANGE_OVER_INTs 8, ebx, ebp, esi, .Lrestore_ebx_range_\suffix
.Lrestore_ebx_range_\suffix:
popl %ebx
popl %esi
movl LOCAL0(%esp), %eax
movl LOCAL1(%esp), %ebp
jmp .Lgpr_setup_finished_range_\suffix
.Lrestore_saved_values_range_\suffix:
movl LOCAL0(%esp), %eax
movl LOCAL1(%esp), %ebp
// Save shorty in callee-save register
movl LOCAL2(%esp), %esi
.Lgpr_setup_finished_range_\suffix:
cmpb LITERAL(68), (%esi) // Test if result type char == 'D'.
je .Lreturn_range_double_\suffix
cmpb LITERAL(70), (%esi) // Test if result type char == 'F'.
je .Lreturn_range_float_\suffix
OAT_ENTRY ExecuteNterpWithClinitImpl
.cfi_startproc
PUSH_ARG esi
// 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(%eax), %esi
cmpl $$(MIRROR_CLASS_STATUS_VISIBLY_INITIALIZED_SHIFTED), MIRROR_CLASS_STATUS_OFFSET(%esi)
jae .Lcontinue_execute_nterp
cmpl $$(MIRROR_CLASS_STATUS_INITIALIZING_SHIFTED), MIRROR_CLASS_STATUS_OFFSET(%esi)
jb .Linvoke_trampoline
movl MIRROR_CLASS_CLINIT_THREAD_ID_OFFSET(%esi), %esi
cmpl %esi, rSELF:THREAD_TID_OFFSET
CFI_REMEMBER_STATE
je .Lcontinue_execute_nterp
.Linvoke_trampoline:
POP_ARG esi
jmp art_quick_resolution_trampoline
.Lcontinue_execute_nterp:
CFI_RESTORE_STATE_AND_DEF_CFA esp, 8
POP_ARG esi
jmp ExecuteNterpImpl
.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(%eax)
jne .Lsetup_with_shorty
// Enclose all code below in a symbol (which gets printed in backtraces).
ENTRY nterp_helper
// Note: mterp also uses the common_* names below for helpers, but that's OK
// as the assembler compiled each interpreter separately.
common_errDivideByZero:
EXPORT_PC
call art_quick_throw_div_zero
// Expect array in eax, index in ecx.
common_errArrayIndex:
EXPORT_PC
movl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %edx
movl %ecx, %eax
movl %edx, %ecx
call art_quick_throw_array_bounds
NterpHotnessCheck:
movl (%esp), %eax
movzwl ART_METHOD_HOTNESS_COUNT_OFFSET(%eax), %ecx
#if (NTERP_HOTNESS_VALUE != 0)
#error Expected 0 for hotness value
#endif
// If the counter is at zero, handle this in the runtime.
testw %cx, %cx
je 3f
// Update counter.
addl $$-1, %ecx
movw %cx, ART_METHOD_HOTNESS_COUNT_OFFSET(%eax) 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:
movl rPC, %ecx
movl rFP, ARG2
// Save next PC around the call.
movl %ecx, LOCAL0(%esp)
call nterp_hot_method
mov LOCAL0(%esp), rPC
testl %eax, %eax
je 2b
// Drop the current frame.
movl -4(rREFS), %esp
CFI_DEF_CFA(esp, PARAMETERS_SAVES_SIZE+CALLEE_SAVES_SIZE)
DROP_PARAMETERS_SAVES
CFI_DEF_CFA(esp, CALLEE_SAVES_SIZE)
// Setup the new frame
movl OSR_DATA_FRAME_SIZE(%eax), %ecx
// Given stack size contains all callee saved registers, remove them.
subl $$CALLEE_SAVES_SIZE, %ecx
// This is the logical end of ExecuteNterpImpl, where the frame info applies.
// EndExecuteNterpImpl includes the methods below as we want the runtime to
// see them as part of the Nterp PCs.
.cfi_endproc
END nterp_helper
// This is the end of PCs contained by the OatQuickMethodHeader created for the interpreter
// entry point.
FUNCTION_TYPE(EndExecuteNterpImpl)
ASM_HIDDEN SYMBOL(EndExecuteNterpImpl)
.global SYMBOL(EndExecuteNterpImpl)
SYMBOL(EndExecuteNterpImpl):
// gen_mterp.py will inline the following definitions
// within [ExecuteNterpImpl, EndExecuteNterpImpl).
%def instruction_end():
FUNCTION_TYPE(artNterpAsmInstructionEnd)
ASM_HIDDEN SYMBOL(artNterpAsmInstructionEnd)
.global SYMBOL(artNterpAsmInstructionEnd)
SYMBOL(artNterpAsmInstructionEnd):
// artNterpAsmInstructionEnd is used as landing pad for exception handling.
RESTORE_IBASE
FETCH_INST
GOTO_NEXT
%def 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.19Angebot
(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.