/* single-purpose registers, given names for clarity */
#define CFI_DEX 11 // DWARF register number of the register holding dex-pc (rPC).
#define CFI_TMP 0 // DWARF register number of the first argument register (r0).
#define CFI_REFS 6
#define rFP r5
#define rREFS r6
#define rINST r7
#define rSELF r9
#define rIBASE r10
#define rPC r11
// To avoid putting ifdefs arond the use of rMR, make sure it's defined.
// IsNterpSupported returns false for configurations that don't have rMR (typically CMS).
#ifndef rMR
#define rMR r8
#endif
// Temporary registers while setting up a frame.
#define rNEW_FP r8
#define rNEW_REFS r10
#define CFI_NEW_REFS 10
#define CALLEE_SAVES_SIZE (9 * 4 + 16 * 4)
// +4 for the ArtMethod of the caller.
#define OFFSET_TO_FIRST_ARGUMENT_IN_STACK (CALLEE_SAVES_SIZE + 4)
// An assembly entry for nterp.
.macro OAT_ENTRY name
.thumb
.type \name, #function
.hidden \name
.global \name
.balign 16
\name:
% pass
.endm
.macro SIZE name
.size \name, .-\name
.endm
.macro NAME_START name
.thumb
.type \name, #function
.hidden \name // Hide this as a global symbol, so we do not incur plt calls.
.global \name /* Cache alignment for function entry */
.balign 16
\name:
% pass
.endm
.macro NAME_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
ENTRY \name
SETUP_SAVE_REFS_ONLY_FRAME ip
bl \helper
RESTORE_SAVE_REFS_ONLY_FRAME
REFRESH_MARKING_REGISTER
ldr ip, [rSELF, #THREAD_EXCEPTION_OFFSET] @ Get exception field.
cmp ip, #0
bne nterp_deliver_pending_exception
bx lr
END \name
.endm
.macro BRANCH
add rPC, rPC, rINST, lsl #1
// Update method counter and do a suspend check if the branch is negative or zero.
cmp rINST, #0
ble NterpHotnessCheck
FETCH_INST // load rINST
PREPARE_OPCODE_DISPATCH // insert opcode to rIBASE
GOTO_OPCODE // jump to next instruction
.endm
// Expects:
// - ip and lr 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.
ldrh \registers, [\code_item, #CODE_ITEM_REGISTERS_SIZE_OFFSET]
// Fetch outs size.
ldrh \outs, [\code_item, #CODE_ITEM_OUTS_SIZE_OFFSET]
.if \load_ins
ldrh \ins, [\code_item, #CODE_ITEM_INS_SIZE_OFFSET]
.endif
add \code_item, \code_item, #CODE_ITEM_INSNS_OFFSET
.endm
// Setup the stack to start executing the method. Expects:
// - r0 to contain the ArtMethod
// - \code_item to already contain the code item
// - rINST, ip, lr to be available
//
// Outputs
// - rINST contains the dex registers size
// - ip contains the old stack pointer.
// - \code_item is replaced with a pointer to the instructions
// - if load_ins is 1, r4 contains the ins
//
.macro SETUP_STACK_FRAME code_item, refs, fp, cfi_refs, load_ins
FETCH_CODE_ITEM_INFO \code_item, rINST, \refs, r4, \load_ins
// Compute required frame size: ((2 * rINST) + \refs) * 4 + 12
// 12 is for saving the previous frame, pc, and method being executed.
add ip, \refs, rINST, lsl #1
// Compute new stack pointer in lr sub lr, sp, #12 sub lr, lr, ip, lsl #2
// Alignment
and lr, lr, #-16
.macro SPILL_ALL_CALLEE_SAVES
SPILL_ALL_CALLEE_SAVE_GPRS @ 9 words (36 bytes) of callee saves.
vpush {s16-s31} @ 16 words (64 bytes) of floats.
.cfi_adjust_cfa_offset 64
.endm
.macro RESTORE_ALL_CALLEE_SAVES lr_to_pc=0
vpop {s16-s31}
.cfi_adjust_cfa_offset -64
pop {r4-r7}
.cfi_adjust_cfa_offset -16
.cfi_restore r4
.cfi_restore r5
.cfi_restore r6
.cfi_restore r7
// Don't restore r8, the marking register gets updated when coming back from runtime.
add sp, sp, #4
.cfi_adjust_cfa_offset -4
.if \lr_to_pc
pop {r9-r11, pc} @ 9 words of callee saves and args.
.cfi_adjust_cfa_offset -16
.else
pop {r9-r11, lr} @ 9 words of callee saves and args.
.cfi_adjust_cfa_offset -16
.cfi_restore r9
.cfi_restore r10
.cfi_restore r11
.cfi_restore lr
.endif
.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
// - r2: value in instruction to decode the number of arguments.
// - r3: first dex register for range invokes, up to 4 arguments for non-range invokes.
// - r4: top of dex register array
//
// The method expects:
// - r0 to contain the ArtMethod
// - r4 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. sub ip, sp, #STACK_OVERFLOW_RESERVED_BYTES
ldr ip, [ip]
// 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 r4, rNEW_REFS, rNEW_FP, CFI_NEW_REFS, load_ins=0
// Fetch instruction information before replacing rPC.
FETCH_B r2, 0, 1
FETCH r3, 2
// Set the dex pc pointer.
mov rPC, r4
// Make r4 point to the top of the dex register array.
add r4, rNEW_FP, rINST, lsl #2
// 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
// - rPC: the new PC pointer to execute
// - r2: number of arguments (bits 4-7), 5th argument if any (bits 0-3)
// - r3: up to four dex register arguments
// - r4: top of dex register array
// - r1: receiver if non-static.
//
// Uses r0 and rINST as temporaries.
.macro SETUP_NON_RANGE_ARGUMENTS_AND_EXECUTE is_static=0, is_string_init=0
// /* op vA, vB, {vC...vG} */
.if \is_static
asrs r0, r2, #4
beq 6f
.else
asr r0, r2, #4
.endif
mov rINST, #-4
cmp r0, #2
blt 1f
beq 2f
cmp r0, #4
blt 3f
beq 4f
// We use a decrementing rINST to store references relative
// to rNEW_FP and dex registers relative to r4
//
// TODO: We could set up rINST 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.
// Maybe even introduce macros NEW_VREG_ADDRESS/NEW_VREG_REF_ADDRESS. 5:
and r2, r2, #15
GET_VREG_OBJECT r0, r2
str r0, [rNEW_FP, rINST]
GET_VREG r0, r2
str r0, [r4, rINST] sub rINST, rINST, #4 4:
asr r2, r3, #12
GET_VREG_OBJECT r0, r2
str r0, [rNEW_FP, rINST]
GET_VREG r0, r2
str r0, [r4, rINST] sub rINST, rINST, #4 3:
ubfx r2, r3, #8, #4
GET_VREG_OBJECT r0, r2
str r0, [rNEW_FP, rINST]
GET_VREG r0, r2
str r0, [r4, rINST] sub rINST, rINST, #4 2:
ubfx r2, r3, #4, #4
GET_VREG_OBJECT r0, r2
str r0, [rNEW_FP, rINST]
GET_VREG r0, r2
str r0, [r4, rINST]
.if !\is_string_init sub rINST, rINST, #4
.endif 1:
.if \is_string_init
// Ignore the first argument
% pass
.elseif \is_static
and r2, r3, #0xf
GET_VREG_OBJECT r0, r2
str r0, [rNEW_FP, rINST]
GET_VREG r0, r2
str r0, [r4, rINST]
.else
str r1, [rNEW_FP, rINST]
str r1, [r4, rINST]
.endif
6:
// Start executing the method.
mov rFP, rNEW_FP
mov rREFS, rNEW_REFS
CFI_DEF_CFA_BREG_PLUS_UCONST CFI_REFS, -4, CALLEE_SAVES_SIZE
// r8 was used for setting up the frame, restore it now.
REFRESH_MARKING_REGISTER
// Branch to the main handler, which will reload rIBASE,
// that was used for setting up the frame.
b .Lexecute_instructions
.endm
// Setup arguments based on a range nterp to nterp call, and start executing
// the method.
// - rNEW_FP: the new pointer to dex registers
// - rNEW_REFS: the new pointer to references
// - rPC: the new PC pointer to execute
// - r2: number of arguments
// - r3: first dex register
// - r4: top of dex register array
// - r1: receiver if non-static.
//
// Expects r0 to be available.
.macro SETUP_RANGE_ARGUMENTS_AND_EXECUTE is_static=0, is_string_init=0
mov r0, #-4
.if \is_string_init
// Ignore the first argument sub r2, r2, #1
add r3, r3, #1
.elseif !\is_static sub r2, r2, #1
add r3, r3, #1
.endif
cmp r2, #0
beq 2f
add rREFS, rREFS, r3, lsl #2 // pointer to first argument in reference array
add rREFS, rREFS, r2, lsl #2 // pointer to last argument in reference array
add rFP, rFP, r3, lsl #2 // pointer to first argument in register array
add rFP, rFP, r2, lsl #2 // pointer to last argument in register array 1:
ldr r3, [rREFS, #-4]!
str r3, [rNEW_FP, r0]
subs r2, r2, 1
ldr r3, [rFP, #-4]!
str r3, [r4, r0] sub r0, r0, 4
bne 1b 2:
.if \is_string_init
// Ignore first argument
% pass
.elseif !\is_static
str r1, [rNEW_FP, r0]
str r1, [r4, r0]
.endif
mov rFP, rNEW_FP
mov rREFS, rNEW_REFS
CFI_DEF_CFA_BREG_PLUS_UCONST CFI_REFS, -4, CALLEE_SAVES_SIZE
// r8 was used for setting up the frame, restore it now.
REFRESH_MARKING_REGISTER
// Branch to the main handler, which will reload rIBASE,
// that was used for setting up the frame.
b .Lexecute_instructions
.endm
// Input: r0 contains the ArtMethod
// Output: r4 contains the code item
.macro GET_CODE_ITEM
ldr r4, [r0, #ART_METHOD_DATA_OFFSET_32]
.endm
.macro DO_ENTRY_POINT_CHECK call_compiled_code, name
// On entry, the method is r0, the instance is r1
ldr r2, .Lfetch_nterp_\name
.Lfetch_location_\name:
add r2, r2, pc
ldr r3, [r0, #ART_METHOD_QUICK_CODE_OFFSET_32]
cmp r2, r3
bne \call_compiled_code
.endm
// Expects ip and lr to be available.
.macro UPDATE_REGISTERS_FOR_STRING_INIT old_value, new_value
mov ip, #0 1:
GET_VREG_OBJECT lr, ip
cmp lr, \old_value
bne 2f
SET_VREG_OBJECT \new_value, ip 2:
add ip, ip, #1
add lr, rREFS, ip, lsl #2
cmp lr, rFP
bne 1b
.endm
// Puts the next floating point argument into the expected register,
// fetching values based on a non-range invoke.
// Uses ip and lr.
.macro LOOP_OVER_SHORTY_LOADING_FPS dreg, sreg, inst, shorty, arg_index, finished, if_double 1: // LOOP
ldrb ip, [\shorty], #1 // Load next character in shorty, and increment.
cmp ip, #0
beq \finished // if (ip == '\0') goto finished
cmp ip, #68 // if (ip == 'D') goto FOUND_DOUBLE
beq 2f
cmp ip, #70 // if (ip == 'F') goto FOUND_FLOAT
beq 3f
lsr \inst, \inst, #4
add \arg_index, \arg_index, #1
// Handle extra argument in arg array taken by a long.
cmp ip, #74 // if (ip != 'J') goto LOOP
bne 1b
lsr \inst, \inst, #4
add \arg_index, \arg_index, #1
b 1b // goto LOOP 2: // FOUND_DOUBLE
and ip, \inst, #0xf
GET_VREG ip, ip
lsr \inst, \inst, #4
add \arg_index, \arg_index, #1
cmp \arg_index, #4
beq 5f
and lr, \inst, #0xf
lsr \inst, \inst, #4
add \arg_index, \arg_index, #1
b 6f 5:
FETCH_B lr, 0, 1
and lr, lr, #0xf 6:
GET_VREG lr, lr
vmov \dreg, ip, lr
b \if_double 3: // FOUND_FLOAT
cmp \arg_index, #4
beq 7f
and ip, \inst, #0xf
lsr \inst, \inst, #4
add \arg_index, \arg_index, #1
b 8f 7:
FETCH_B ip, 0, 1
and ip, ip, #0xf 8:
GET_VREG ip, ip
vmov \sreg, ip
.endm
// Puts the next int/long/object argument in the expected register,
// fetching values based on a non-range invoke.
// Uses ip.
.macro LOOP_OVER_SHORTY_LOADING_GPRS gpr_reg, inst, shorty, arg_index, finished, if_long, is_r3 1: // LOOP
ldrb ip, [\shorty], #1 // Load next character in shorty, and increment.
cmp ip, #0
beq \finished // if (ip == '\0') goto finished
cmp ip, #74 // if (ip == 'J') goto FOUND_LONG
beq 2f
cmp ip, #70 // if (ip == 'F') goto SKIP_FLOAT
beq 3f
cmp ip, #68 // if (ip == 'D') goto SKIP_DOUBLE
beq 4f
cmp \arg_index, #4
beq 7f
and ip, \inst, #0xf
lsr \inst, \inst, #4
add \arg_index, \arg_index, #1
b 8f 7:
FETCH_B ip, 0, 1
and ip, ip, #0xf 8:
GET_VREG \gpr_reg, ip
b 5f 2: // FOUND_LONG
.if \is_r3
// Put back shorty and exit sub \shorty, \shorty, #1
b 5f
.endif
and ip, \inst, #0xf
GET_VREG ip, ip
// The only one possible for non-range long is r2-r3
mov r2, ip
lsr \inst, \inst, #4
add \arg_index, \arg_index, #1
cmp \arg_index, #4
beq 9f
and ip, \inst, #0xf
lsr \inst, \inst, #4
b 10f 9:
FETCH_B ip, 0, 1
and ip, ip, #0xf 10:
GET_VREG ip, ip
// The only one possible for non-range long is r2-r3
mov r3, ip
add \arg_index, \arg_index, #1
b \if_long 3: // SKIP_FLOAT
lsr \inst, \inst, #4
add \arg_index, \arg_index, #1
b 1b 4: // SKIP_DOUBLE
lsr \inst, \inst, #8
add \arg_index, \arg_index, #2
b 1b 5:
% pass
.endm
// Puts the next int/long/object argument in the expected stack slot,
// fetching values based on a non-range invoke.
// Uses ip as temporary.
.macro LOOP_OVER_SHORTY_LOADING_INTs shorty, inst, arg_index, finished, is_string_init 1: // LOOP
ldrb ip, [\shorty], #1 // Load next character in shorty, and increment.
cmp ip, #0
beq \finished // if (ip == '\0') goto finished
cmp ip, #74 // if (ip == 'J') goto FOUND_LONG
beq 2f
cmp ip, #70 // if (ip == 'F') goto SKIP_FLOAT
beq 3f
cmp ip, #68 // if (ip == 'D') goto SKIP_DOUBLE
beq 4f
.if \is_string_init
cmp \arg_index, #4
.else
cmp \arg_index, #(4+1) // +1 for ArtMethod
.endif
beq 7f
and ip, \inst, #0xf
lsr \inst, \inst, #4
b 8f 7:
FETCH_B ip, 0, 1
and ip, ip, #0xf 8:
GET_VREG ip, ip
str ip, [sp, \arg_index, lsl #2]
add \arg_index, \arg_index, #1
b 1b 2: // FOUND_LONG
and ip, \inst, #0xf
GET_VREG ip, ip
str ip, [sp, \arg_index, lsl #2]
lsr \inst, \inst, #4
add \arg_index, \arg_index, #1
.if \is_string_init
cmp \arg_index, #4
.else
cmp \arg_index, #(4+1) // +1 for ArtMethod
.endif
beq 9f
and ip, \inst, #0xf
lsr \inst, \inst, #4
b 10f 9:
FETCH_B ip, 0, 1
and ip, ip, #0xf 10:
GET_VREG ip, ip
str ip, [sp, \arg_index, lsl #2]
add \arg_index, \arg_index, #1
b 1b 3: // SKIP_FLOAT
lsr \inst, \inst, #4
add \arg_index, \arg_index, #1
b 1b 4: // SKIP_DOUBLE
lsr \inst, \inst, #8
add \arg_index, \arg_index, #2
b 1b
.endm
.macro SETUP_RETURN_VALUE shorty
ldrb ip, [\shorty]
cmp ip, #68 // Test if result type char == 'D'.
beq 1f
cmp ip, #70 // Test if result type char == 'F'.
bne 2f
vmov r0, s0
b 2f 1:
vmov r0, r1, d0 2:
% pass
.endm
.macro GET_SHORTY_SLOW_PATH dest, is_interface
// Save all registers that can hold arguments in the fast path.
vpush {s0}
push {r0-r2}
.if \is_interface
ldr r0, [sp, #16]
FETCH r1, 1
bl NterpGetShortyFromMethodId
.else
bl NterpGetShorty
.endif
mov \dest, r0
pop {r0-r2}
vpop {s0}
.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.
% pass
.elseif \is_custom
// We always go to compiled code for custom calls.
% pass
.else
DO_ENTRY_POINT_CHECK .Lcall_compiled_code_\suffix, \suffix
GET_CODE_ITEM
.if \is_string_init
bl nterp_to_nterp_string_init_non_range
.elseif \is_static
bl nterp_to_nterp_static_non_range
.else
bl nterp_to_nterp_instance_non_range
.endif
b .Ldone_return_\suffix
.Lfetch_nterp_\suffix:
.word (ExecuteNterpImpl + /* thumb mode */ 1) - (.Lfetch_location_\suffix + 4)
.endif
.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.
FETCH_B r2, 0, 1
asr r2, r2, #4 // number of arguments
.if \is_static
cmp r2, #1
blt .Linvoke_with_few_args_\suffix
bne .Lget_shorty_\suffix
FETCH r2, 2
and r2, r2, #0xf // dex register of first argument
GET_VREG r1, r2
vmov s0, r1
.else
cmp r2, #2
blt .Linvoke_with_few_args_\suffix
bne .Lget_shorty_\suffix
FETCH r2, 2
ubfx r2, r2, #4, #4 // dex register of second argument
GET_VREG r2, r2
vmov s0, r2
.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.
FETCH r3, 3
and r3, r3, #0xfe
cmp r3, #0x0a
beq .Lget_shorty_and_invoke_\suffix
.if \is_interface
// Setup hidden argument.
mov ip, r4
.endif
ldr lr, [r0, #ART_METHOD_QUICK_CODE_OFFSET_32]
blx lr
FETCH_ADVANCE_INST 3
PREPARE_OPCODE_DISPATCH
GOTO_OPCODE
.Lget_shorty_and_invoke_\suffix:
.if \is_interface
// Save hidden argument.
vmov s16, r4
.endif
GET_SHORTY_SLOW_PATH rINST, \is_interface
b .Lgpr_setup_finished_\suffix
.endif
.Lget_shorty_\suffix:
.if \is_interface
// Save hidden argument.
vmov s16, r4
.endif
GET_SHORTY rINST, \is_interface, \is_polymorphic, \is_custom
// From this point:
// - rINST contains shorty (in callee-save to switch over return value after call).
// - r0 contains method
// - r1 contains 'this' pointer for instance method.
// We need three registers.
add r3, rINST, #1 // shorty + 1 ; ie skip return arg character
FETCH r2, 2 // arguments
.if \is_string_init
lsr r2, r2, #4
mov r4, #1 // ignore first argument
.elseif \is_static
mov r4, #0 // arg_index
.else
lsr r2, r2, #4
mov r4, #1 // ignore first argument
.endif
LOOP_OVER_SHORTY_LOADING_FPS d0, s0, r2, r3, r4, .Lxmm_setup_finished_\suffix, .Ld1_s2_\suffix
.Ld1_s1_\suffix:
LOOP_OVER_SHORTY_LOADING_FPS d1, s1, r2, r3, r4, .Lxmm_setup_finished_\suffix, .Ld2_s1_\suffix
.Ld1_s2_\suffix:
LOOP_OVER_SHORTY_LOADING_FPS d1, s2, r2, r3, r4, .Lxmm_setup_finished_\suffix, .Ls4_\suffix
.Ld2_s3_\suffix:
LOOP_OVER_SHORTY_LOADING_FPS d2, s3, r2, r3, r4, .Lxmm_setup_finished_\suffix, .Lxmm_setup_finished_\suffix
b .Ls4_\suffix
.Ld2_s1_\suffix:
LOOP_OVER_SHORTY_LOADING_FPS d2, s1, r2, r3, r4, .Lxmm_setup_finished_\suffix, .Lxmm_setup_finished_\suffix
.Ls4_\suffix:
// If we arrive here, we can only have a float.
LOOP_OVER_SHORTY_LOADING_FPS d2, s4, r2, r3, r4, .Lxmm_setup_finished_\suffix, .Lxmm_setup_finished_\suffix
.Lxmm_setup_finished_\suffix:
add r4, rINST, #1 // shorty + 1 ; ie skip return arg character
FETCH r8, 2 // arguments
.if \is_string_init
lsr r8, r8, #4
mov lr, #1 // ignore first argument
LOOP_OVER_SHORTY_LOADING_GPRS r1, r8, r4, lr, .Lgpr_setup_finished_\suffix, .Lif_long_\suffix, is_r3=0
.elseif \is_static
mov lr, #0 // arg_index
LOOP_OVER_SHORTY_LOADING_GPRS r1, r8, r4, lr, .Lgpr_setup_finished_\suffix, .Lif_long_\suffix, is_r3=0
.else
lsr r8, r8, #4
mov lr, #1 // ignore first argument
.endif
LOOP_OVER_SHORTY_LOADING_GPRS r2, r8, r4, lr, .Lgpr_setup_finished_\suffix, .Lif_long_\suffix, is_r3=0
LOOP_OVER_SHORTY_LOADING_GPRS r3, r8, r4, lr, .Lgpr_setup_finished_\suffix, .Lif_long_\suffix, is_r3=1
.Lif_long_\suffix:
// Store in the outs array (stored above the ArtMethod in the stack). We only do this for non-string-init
// calls as the index is already adjusted above.
.if !\is_string_init
add lr, lr, #1
.endif
LOOP_OVER_SHORTY_LOADING_INTs r4, r8, lr, .Lgpr_setup_finished_\suffix, \is_string_init
.Lgpr_setup_finished_\suffix:
REFRESH_MARKING_REGISTER // r8 was used when setting parameters, restore it.
.if \is_polymorphic
bl art_quick_invoke_polymorphic
.elseif \is_custom
bl art_quick_invoke_custom
.else
.if \is_interface
// Setup hidden argument.
vmov ip, s16
.endif
ldr lr, [r0, #ART_METHOD_QUICK_CODE_OFFSET_32]
blx lr
.endif
SETUP_RETURN_VALUE rINST
.Ldone_return_\suffix: /* resume execution of caller */
.if \is_string_init
FETCH ip, 2 // arguments
and ip, ip, #0xf
GET_VREG r1, ip
UPDATE_REGISTERS_FOR_STRING_INIT r1, r0
.endif
.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.
FETCH_B r2, 0, 1 // number of arguments
.if \is_static
cmp r2, #1
blt .Linvoke_with_few_args_range_\suffix
bne .Lget_shorty_range_\suffix
FETCH r3, 2 // dex register of first argument
GET_VREG r1, r3
vmov s0, r1
.else
cmp r2, #2
blt .Linvoke_with_few_args_range_\suffix
bne .Lget_shorty_range_\suffix
FETCH r3, 2 // dex register of first argument
add r3, r3, #1 // Add 1 for next argument
GET_VREG r2, r3
vmov s0, r2
.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.
FETCH r3, 3
and r3, r3, #0xfe
cmp r3, #0x0a
beq .Lget_shorty_and_invoke_range_\suffix
.if \is_interface
// Setup hidden argument.
mov ip, r4
.endif
ldr lr, [r0, #ART_METHOD_QUICK_CODE_OFFSET_32]
blx lr
FETCH_ADVANCE_INST 3
PREPARE_OPCODE_DISPATCH
GOTO_OPCODE
.Lget_shorty_and_invoke_range_\suffix:
.if \is_interface
// Save hidden argument.
vmov s16, r4
.endif
GET_SHORTY_SLOW_PATH rINST, \is_interface
b .Lgpr_setup_finished_range_\suffix
.endif
.Lget_shorty_range_\suffix:
.if \is_interface
// Save hidden argument.
vmov s16, r4
.endif
GET_SHORTY rINST, \is_interface, \is_polymorphic, \is_custom
// From this point:
// - rINST contains shorty (in callee-save to switch over return value after call).
// - r0 contains method
// - r1 contains 'this' pointer for instance method.
//
// Save r0 and r1 before calling NterpSetupArm32Fprs.
push {r0, r1}
add r0, rINST, #1 // shorty + 1 ; ie skip return arg character
FETCH r1, 2 // arguments
.if \is_string_init
add r1, r1, #1 // arg start index
mov r2, #1 // index in stack
.elseif \is_static
mov r2, #0 // index in stack
.else
add r1, r1, #1 // arg start index
mov r2, #1 // index in stack
.endif
vpush {s0-s15}
mov r3, sp
// Pass the stack address for arguments, +16 for fprs, +2 for saved registers,
// +1 for ArtMethod.
add lr, sp, #((16 + 2 + 1) * 4)
push {rFP, lr}
bl NterpSetupArm32Fprs
add sp, sp, #8
vpop {s0-s15}
pop {r0, r1}
.Lxmm_setup_finished_range_\suffix:
add r8, rINST, #1 // shorty + 1 ; ie skip return arg character
FETCH lr, 2 // arguments
.if \is_string_init
add lr, lr, #1 // arg start index
mov r4, #0 // index in stack
LOOP_RANGE_OVER_SHORTY_LOADING_GPRS r1, r8, lr, r4, .Lgpr_setup_finished_range_\suffix, .Lif_long_range_\suffix, is_r3=0
.elseif \is_static
mov r4, #0 // index in stack
LOOP_RANGE_OVER_SHORTY_LOADING_GPRS r1, r8, lr, r4, .Lgpr_setup_finished_range_\suffix, .Lif_long_range_\suffix, is_r3=0
.else
add lr, lr, #1 // arg start index
mov r4, #1 // index in stack
.endif
LOOP_RANGE_OVER_SHORTY_LOADING_GPRS r2, r8, lr, r4, .Lgpr_setup_finished_range_\suffix, .Lif_long_range_\suffix, is_r3=0
LOOP_RANGE_OVER_SHORTY_LOADING_GPRS r3, r8, lr, r4, .Lgpr_setup_finished_range_\suffix, .Lif_long_range_\suffix, is_r3=1
.Lif_long_range_\suffix:
// Add 1word for the ArtMethod stored before the outs.
add r4, r4, #1
LOOP_RANGE_OVER_INTs r8, lr, r4, .Lgpr_setup_finished_range_\suffix
.Lgpr_setup_finished_range_\suffix:
REFRESH_MARKING_REGISTER // r8 was used when setting parameters, restore it.
.if \is_polymorphic
bl art_quick_invoke_polymorphic
.elseif \is_custom
bl art_quick_invoke_custom
.else
.if \is_interface
// Setup hidden argument.
vmov ip, s16
.endif
ldr lr, [r0, #ART_METHOD_QUICK_CODE_OFFSET_32]
blx lr
.endif
SETUP_RETURN_VALUE rINST
.Ldone_return_range_\suffix: /* resume execution of caller */
.if \is_string_init
FETCH ip, 2 // arguments
GET_VREG r1, ip
UPDATE_REGISTERS_FOR_STRING_INIT r1, r0
.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.
ldr r4, [r0, ART_METHOD_DECLARING_CLASS_OFFSET]
ldr ip, [r4, MIRROR_CLASS_STATUS_OFFSET]
cmp ip, #MIRROR_CLASS_STATUS_VISIBLY_INITIALIZED_SHIFTED
bcs ExecuteNterpImpl
cmp ip, #MIRROR_CLASS_STATUS_INITIALIZED_SHIFTED
blo .Linitializing_check
dmb ish
b ExecuteNterpImpl
.Linitializing_check:
cmp ip, #MIRROR_CLASS_STATUS_INITIALIZING_SHIFTED
blo art_quick_resolution_trampoline
ldr r4, [r4, #MIRROR_CLASS_CLINIT_THREAD_ID_OFFSET]
ldr ip, [rSELF, #THREAD_TID_OFFSET]
cmp r4, ip
beq ExecuteNterpImpl
b art_quick_resolution_trampoline
.cfi_endproc
.type EndExecuteNterpWithClinitImpl, #function
.hidden EndExecuteNterpWithClinitImpl
.global EndExecuteNterpWithClinitImpl
EndExecuteNterpWithClinitImpl:
% pass
OAT_ENTRY ExecuteNterpImpl
.cfi_startproc sub ip, sp, #STACK_OVERFLOW_RESERVED_BYTES
ldr ip, [ip] /* Spill callee save regs */
SPILL_ALL_CALLEE_SAVES
ldr rPC, [r0, #ART_METHOD_DATA_OFFSET_32]
// Setup the stack for executing the method.
SETUP_STACK_FRAME rPC, rREFS, rFP, CFI_REFS, load_ins=1
// Setup the parameters
cmp r4, #0
beq .Lxmm_setup_finished
sub rINST, rINST, r4
ldr r8, [r0, #ART_METHOD_ACCESS_FLAGS_OFFSET]
lsl rINST, rINST, #2 // rINST is now the offset for inputs into the registers array.
mov rIBASE, ip // rIBASE contains the old stack pointer
.Lsetup_slow_path:
// If the method is not static and there is one argument ('this'), we don't need to fetch the
// shorty.
tst r8, #ART_METHOD_IS_STATIC_FLAG
bne .Lsetup_with_shorty
str r1, [rFP, rINST]
str r1, [rREFS, rINST]
cmp r4, #1
beq .Lxmm_setup_finished
.Lsetup_with_shorty:
// Save arguments that were passed before calling into the runtime.
// No need to save r0 (ArtMethod) as we're not using it later in this code.
// Save r4 for stack aligment.
// TODO: Get shorty in a better way and remove below
push {r1-r4}
vpush {s0-s15}
bl NterpGetShorty
vpop {s0-s15}
pop {r1-r4}
mov ip, r8
add r8, rREFS, rINST
add r7, rFP, rINST
mov r4, #0
// Setup shorty, pointer to inputs in FP and pointer to inputs in REFS
add lr, r0, #1 // shorty + 1 ; ie skip return arg character
tst ip, #ART_METHOD_IS_STATIC_FLAG
bne .Lhandle_static_method
add r7, r7, #4
add r8, r8, #4
add rIBASE, rIBASE, #4
b .Lcontinue_setup_gprs
.Lhandle_static_method:
LOOP_OVER_SHORTY_STORING_GPRS r1, lr, r4, r7, r8, .Lgpr_setup_finished, .Lif_long, is_r3=0
.Lcontinue_setup_gprs:
LOOP_OVER_SHORTY_STORING_GPRS r2, lr, r4, r7, r8, .Lgpr_setup_finished, .Lif_long, is_r3=0
LOOP_OVER_SHORTY_STORING_GPRS r3, lr, r4, r7, r8, .Lgpr_setup_finished, .Lif_long, is_r3=1
.Lif_long:
LOOP_OVER_INTs lr, r4, r7, r8, rIBASE, ip, r1, .Lgpr_setup_finished
.Lgpr_setup_finished:
add r0, r0, #1 // shorty + 1 ; ie skip return arg character
mov r1, r7
add r2, rIBASE, #OFFSET_TO_FIRST_ARGUMENT_IN_STACK
vpush {s0-s15}
mov r3, sp
bl NterpStoreArm32Fprs
add sp, sp, #(16 * 4)
.Lxmm_setup_finished:
CFI_DEFINE_DEX_PC_WITH_OFFSET(CFI_TMP, CFI_DEX, 0)
// r8 was used for setting up the frame, restore it now.
REFRESH_MARKING_REGISTER
.Lexecute_instructions:
// Prepare rIBASE.
//
// A full set of opcode handlers has 20KiB. We can quickly construct the handler address by
// inserting the opcode from the low 8 bits of the 16-bit instruction into bits 13:6 of a
// register holding a 16KiB-aligned start of the handlers. However, with 4KiB pages,
// `libart.so` is loaded only on a 4KiB-aligned address. Therefore we emit four sets of nterp
// handlers, 20KiB apart, and one of them starts at a 20KiB-aligned address. We fill the 4KiB
// gaps with slow-path code. The bits 13:12 of the last handler set address tell us how many
// times we need to subtract the 20KiB distance to get the address of the aligned handler set,
// nterp3_op_nop - ((nterp3_op_nop >> 12) & 3) * 20KiB .
// To shorten the initialization sequence, we subtract only 16KiB per handler set as the
// remaining 4KiB only clears bits 13:12 which shall be replaced by the opcode anyway.
#if NTERP_HANDLER_SIZE_LOG2 != 6
#error "Unexpected handler size."
#endif
ldr rIBASE, #.LrIBASE_init_diff
.LrIBASE_init_add_pc:
add rIBASE, pc
and ip, rIBASE, #0x3000 sub rIBASE, rIBASE, ip, lsl #2 /* start executing the instruction at rPC */
START_EXECUTING_INSTRUCTIONS /* NOTE: no fallthrough */
.LrIBASE_init_diff:
.word (nterp3_op_nop + /* thumb mode */ 1) - (.LrIBASE_init_add_pc + 4)
// cfi info continues, and covers the whole nterp implementation. SIZE ExecuteNterpImpl
%def opcode_pre():
% pass
%def fetch_from_thread_cache(dest_reg, miss_label, wb=False, tmp="ip", tmp2="ip"):
// Fetch some information from the thread cache.
// Uses `tmp`, `tmp2` (must differ from `tmp` for write-back) and `lr` as temporaries.
add ${tmp2}, rSELF, #THREAD_INTERPRETER_CACHE_OFFSET // cache address
ubfx lr, rPC, #THREAD_INTERPRETER_CACHE_KEY_LOW_BIT, #THREAD_INTERPRETER_CACHE_SIZE_LOG2
// entry index
add ${tmp2}, ${tmp2}, lr, lsl #3 // entry address within the cache
% if wb:
ldmia ${tmp2}!, {${tmp}, ${dest_reg}} // 16-bit instruction if all regs are low.
% else:
ldrd ${tmp}, ${dest_reg}, [${tmp2}] // entry key (pc), value (field/method/etc.)
cmp ${tmp}, rPC
bne ${miss_label}
%def fetch_uint64_from_thread_cache(miss_label, dest_reg1="r1", dest_reg2="r3", tmp="r0", tmp2="r2"):
// Fetch some information from the thread cache.
// Uses `tmp`, `tmp2` (must differ from `tmp` for write-back) and `lr` as temporaries.
add ${tmp2}, rSELF, #THREAD_INTERPRETER_CACHE_OFFSET // cache address
ubfx lr, rPC, #THREAD_INTERPRETER_CACHE_KEY_LOW_BIT+1, #THREAD_INTERPRETER_CACHE_SIZE_LOG2-1
// entry index
add ${tmp}, ${tmp2}, lr, lsl #4 // entry address within the cache
ldm ${tmp}, {${tmp}, ${dest_reg1}, ${tmp2}, ${dest_reg2}}
cmp ${tmp}, rPC
it eq
cmpeq ${tmp2}, rPC
bne ${miss_label}
// Enclose all code below in a symbol (which gets printed in backtraces).
NAME_START 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
bl art_quick_throw_div_zero
// Expect index in r1, length in r3
common_errArrayIndex:
EXPORT_PC
mov r0, r1
mov r1, r3
bl art_quick_throw_array_bounds
// Drop the current frame.
ldr ip, [rREFS, #-4]
mov sp, ip
.cfi_def_cfa sp, CALLEE_SAVES_SIZE
// The transition frame of type SaveAllCalleeSaves saves r4, r8, and r9,
// but not managed ABI. So we need to restore callee-saves of the nterp frame,
// and save managed ABI callee saves, which will be restored by the callee upon
// return.
// Fetch the native PC to jump to and save it in a callee-save register.
ldr rFP, [r0, #OSR_DATA_NATIVE_PC]
// Free the memory holding OSR Data.
bl free
// Jump to the compiled code.
bx rFP
// 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.
.type EndExecuteNterpImpl, #function
.hidden EndExecuteNterpImpl
.global EndExecuteNterpImpl
EndExecuteNterpImpl:
/* *Convertthedoubleinr0/r1toalonginr0/r1. * *Wehavetoclipvaluestolongmin/maxperthespecification.The *expectedcommoncaseisa"reasonable"valuethatconvertsdirectly *tomodestinteger.TheEABIconvertfunctionisn'tdoingthisforus.
*/
ENTRY nterp_d2l_doconv
ubfx r2, r1, #20, #11 @ grab the exponent
movw r3, #0x43e
cmp r2, r3 @ MINLONG < x > MAXLONG?
bhs d2l_special_cases
b __aeabi_d2lz @ tail call to convert double to long
d2l_special_cases:
movw r3, #0x7ff
cmp r2, r3
beq d2l_maybeNaN @ NaN?
d2l_notNaN:
adds r1, r1, r1 @ sign bit to carry
mov r0, #0xffffffff @ assume maxlong for lsw
mov r1, #0x7fffffff @ assume maxlong for msw
adc r0, r0, #0
adc r1, r1, #0 @ convert maxlong to minlong if exp negative
bx lr @ return
d2l_maybeNaN:
orrs r3, r0, r1, lsl #12
beq d2l_notNaN @ if fraction is non-zero, it's a NaN
mov r0, #0
mov r1, #0
bx lr @ return 0 for NaN
END nterp_d2l_doconv
/* *Convertthefloatinr0toalonginr0/r1. * *Wehavetoclipvaluestolongmin/maxperthespecification.The *expectedcommoncaseisa"reasonable"valuethatconvertsdirectly *tomodestinteger.TheEABIconvertfunctionisn'tdoingthisforus.
*/
ENTRY nterp_f2l_doconv
ubfx r2, r0, #23, #8 @ grab the exponent
cmp r2, #0xbe @ MININT < x > MAXINT?
bhs f2l_special_cases
b __aeabi_f2lz @ tail call to convert float to long
f2l_special_cases:
cmp r2, #0xff @ NaN or infinity?
beq f2l_maybeNaN
f2l_notNaN:
adds r0, r0, r0 @ sign bit to carry
mov r0, #0xffffffff @ assume maxlong for lsw
mov r1, #0x7fffffff @ assume maxlong for msw
adc r0, r0, #0
adc r1, r1, #0 @ convert maxlong to minlong if exp negative
bx lr @ return
f2l_maybeNaN:
lsls r3, r0, #9
beq f2l_notNaN @ if fraction is non-zero, it's a NaN
mov r0, #0
mov r1, #0
bx lr @ return 0 for NaN
END nterp_f2l_doconv
ENTRY nterp_deliver_pending_exception
DELIVER_PENDING_EXCEPTION
END nterp_deliver_pending_exception
// gen_mterp.py will inline the following definitions
// within [ExecuteNterpImpl, EndExecuteNterpImpl).
%def instruction_end():
% prefix_number = int(re.sub(r"nterp([0-7])_", r"\1", opcode_name_prefix))
% if prefix_number == 3:
.type artNterpAsmInstructionEnd, #function
.hidden artNterpAsmInstructionEnd
.global artNterpAsmInstructionEnd
artNterpAsmInstructionEnd:
// artNterpAsmInstructionEnd is used as landing pad for exception handling.
FETCH_INST
PREPARE_OPCODE_DISPATCH
GOTO_OPCODE
%def opcode_name_prefixes():
% return ["nterp0_", "nterp1_", "nterp2_", "nterp3_"]
%def opcode_start():
NAME_START ${opcode}
# Explicitly restore CFA, just in case the previous opcode clobbered it (by .cfi_def_*).
CFI_DEF_CFA_BREG_PLUS_UCONST CFI_REFS, -4, CALLEE_SAVES_SIZE
%def opcode_end():
NAME_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):
NAME_START ${name}
% pass
%def opcode_slow_path_end(name):
NAME_END ${name}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.57 Sekunden
(vorverarbeitet am 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.