Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Android/art/art/runtime/interpreter/mterp/x86ng/   (Android Betriebssystem Version 17©)  Datei vom 26.5.2026 mit Größe 75 kB image not shown  

Quelle  main.S

  Sprache: Sparc
 

/*
 * Copyright (C) 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


/*
 * This is a #include, not a %include, because we want the C pre-processor
 * to expand the macros into assembler assignment statements.
 */

#include "asm_support.h"
#include "arch/x86/asm_support_x86.S"

/**
 * x86 ABI general notes:
 *
 * Caller save set:
 *      eax, ebx, edx, ecx, st(0)-st(7)
 * Callee save set:
 *      esi, edi, ebp
 * Return regs:
 *      32-bit in eax
 *      64-bit in edx:eax (low-order 32 in eax)
 *      fp on top of fp stack st(0)
 *
 * Stack must be 16-byte aligned to support SSE in native code.
 */


#define ARG3        %ebx
#define ARG2        %edx
#define ARG1        %ecx
#define ARG0        %eax

/*
 * single-purpose registers, given names for clarity
 */

#define rSELF    %fs
#define rPC      %esi
#define CFI_DEX  6  // DWARF register number of the register holding dex-pc (esi).
#define CFI_TMP  0  // DWARF register number of the first argument register (eax).
#define rFP      %edi
#define rINST    %ebx
#define rINSTw   %bx
#define rINSTbh  %bh
#define rINSTbl  %bl
#define rIBASE   %edx
#define rREFS    %ebp
#define CFI_REFS 5 // DWARF register number of the reference array (ebp).

// Temporary registers while setting up a frame.
#define rNEW_FP   %ecx
#define rNEW_REFS %eax
#define CFI_NEW_REFS 0

#define LOCAL0 4
#define LOCAL1 8
#define LOCAL2 12

/*
 * Get/set the 32-bit value from a Dalvik register.
 */

#define VREG_ADDRESS(_vreg) (rFP,_vreg,4)
#define VREG_HIGH_ADDRESS(_vreg) 4(rFP,_vreg,4)
#define VREG_REF_ADDRESS(_vreg) (rREFS,_vreg,4)
#define VREG_REF_HIGH_ADDRESS(_vreg) 4(rREFS,_vreg,4)

.macro GET_VREG _reg _vreg
    movl    VREG_ADDRESS(\_vreg), \_reg
.endm

.macro GET_VREG_OBJECT _reg _vreg
    movl    VREG_REF_ADDRESS(\_vreg), \_reg
.endm

/* Read wide value to xmm. */
.macro GET_WIDE_FP_VREG _reg _vreg
    movq    VREG_ADDRESS(\_vreg), \_reg
.endm

.macro SET_VREG _reg _vreg
    movl    \_reg, VREG_ADDRESS(\_vreg)
    movl    MACRO_LITERAL(0), VREG_REF_ADDRESS(\_vreg)
.endm

/* Write wide value from xmm. xmm is clobbered. */
.macro SET_WIDE_FP_VREG _reg _vreg
    movq    \_reg, VREG_ADDRESS(\_vreg)
    pxor    \_reg, \_reg
    movq    \_reg, VREG_REF_ADDRESS(\_vreg)
.endm

.macro SET_VREG_OBJECT _reg _vreg
    movl    \_reg, VREG_ADDRESS(\_vreg)
    movl    \_reg, VREG_REF_ADDRESS(\_vreg)
.endm

.macro GET_VREG_HIGH _reg _vreg
    movl    VREG_HIGH_ADDRESS(\_vreg), \_reg
.endm

.macro SET_VREG_HIGH _reg _vreg
    movl    \_reg, VREG_HIGH_ADDRESS(\_vreg)
    movl    MACRO_LITERAL(0), VREG_REF_HIGH_ADDRESS(\_vreg)
.endm

.macro CLEAR_REF _vreg
    movl    MACRO_LITERAL(0), VREG_REF_ADDRESS(\_vreg)
.endm

.macro CLEAR_WIDE_REF _vreg
    movl    MACRO_LITERAL(0), VREG_REF_ADDRESS(\_vreg)
    movl    MACRO_LITERAL(0), VREG_REF_HIGH_ADDRESS(\_vreg)
.endm

.macro GET_VREG_XMMs _xmmreg _vreg
    movss VREG_ADDRESS(\_vreg), \_xmmreg
.endm
.macro GET_VREG_XMMd _xmmreg _vreg
    movsd VREG_ADDRESS(\_vreg), \_xmmreg
.endm
.macro SET_VREG_XMMs _xmmreg _vreg
    movss \_xmmreg, VREG_ADDRESS(\_vreg)
.endm
.macro SET_VREG_XMMd _xmmreg _vreg
    movsd \_xmmreg, VREG_ADDRESS(\_vreg)
.endm

// Includes the return address implicitly pushed on stack by 'call'.
#define CALLEE_SAVES_SIZE (3 * 4 + 1 * 4)

#define PARAMETERS_SAVES_SIZE (4 * 4)

// +4 for the ArtMethod of the caller.
#define OFFSET_TO_FIRST_ARGUMENT_IN_STACK (CALLEE_SAVES_SIZE + PARAMETERS_SAVES_SIZE 4)

/*
 * Refresh rINST.
 * At enter to handler rINST does not contain the opcode number.
 * However some utilities require the full value, so this macro
 * restores the opcode number.
 */

.macro REFRESH_INST _opnum
    movb    rINSTbl, rINSTbh
    movb    $$\_opnum, rINSTbl
.endm

/*
 * Fetch the next instruction from rPC into rINSTw.  Does not advance rPC.
 */

.macro FETCH_INST
    movzwl  (rPC), rINST
.endm

.macro FETCH_INST_CLEAR_OPCODE
    movzbl 1(rPC), rINST
.endm

/*
 * Remove opcode from rINST, compute the address of handler and jump to it.
 */

.macro GOTO_NEXT
    movzx   rINSTbl,%ecx
    movzbl  rINSTbh,rINST
    shll    MACRO_LITERAL(${handler_size_bits}), %ecx
    addl    rIBASE, %ecx
    jmp     *%ecx
.endm

/*
 * Advance rPC by instruction count.
 */

.macro ADVANCE_PC _count
    leal    2*\_count(rPC), rPC
.endm

/*
 * Advance rPC by instruction count, fetch instruction and jump to handler.
 */

.macro ADVANCE_PC_FETCH_AND_GOTO_NEXT _count
    ADVANCE_PC \_count
    FETCH_INST
    GOTO_NEXT
.endm

.macro NTERP_DEF_CFA cfi_reg
    CFI_DEF_CFA_BREG_PLUS_UCONST \cfi_reg, -4, CALLEE_SAVES_SIZE + PARAMETERS_SAVES_SIZE
.endm

.macro RESTORE_IBASE
    call 0f
0:
    popl rIBASE
    addl MACRO_LITERAL(SYMBOL(artNterpAsmInstructionStart) - 0b), rIBASE
.endm

.macro RESTORE_IBASE_WITH_CFA
    call 0f
0:
    CFI_ADJUST_CFA_OFFSET(4)
    popl rIBASE
    CFI_ADJUST_CFA_OFFSET(-4)
    addl MACRO_LITERAL(SYMBOL(artNterpAsmInstructionStart) - 0b), rIBASE
.endm

.macro SPILL_ALL_CORE_PARAMETERS
    PUSH_ARG eax
    PUSH_ARG ecx
    PUSH_ARG edx
    PUSH_ARG ebx
.endm

.macro RESTORE_ALL_CORE_PARAMETERS
    POP_ARG ebx
    POP_ARG edx
    POP_ARG ecx
    POP_ARG eax
.endm

.macro DROP_PARAMETERS_SAVES
    addl $$(PARAMETERS_SAVES_SIZE), %esp
.endm

.macro SAVE_WIDE_RETURN
    movl %edx, LOCAL2(%esp)
.endm

.macro LOAD_WIDE_RETURN reg
    movl LOCAL2(%esp), \reg
.endm

// An assembly entry for nterp.
.macro OAT_ENTRY name
    FUNCTION_TYPE(\name)
    ASM_HIDDEN SYMBOL(\name)
    .global SYMBOL(\name)
    .balign 16
SYMBOL(\name):
%   pass
.endm

.macro ENTRY name
    .text
    ASM_HIDDEN SYMBOL(\name)
    .global SYMBOL(\name)
    FUNCTION_TYPE(\name)
SYMBOL(\name):
%   pass
.endm

.macro END name
    SIZE(\name)
.endm

// Macro for defining entrypoints into runtime. We don't need to save registers
// (we're not holding references there), but there is no
// kDontSave runtime method. So just use the kSaveRefsOnly runtime method.
.macro NTERP_TRAMPOLINE name, helper
  DEFINE_FUNCTION \name
    movd %ebx, %xmm0
    SETUP_SAVE_REFS_ONLY_FRAME ebx
    movd %xmm0, %ebx
    PUSH_ARG ebx
    PUSH_ARG edx
    PUSH_ARG ecx
    PUSH_ARG eax
    call \helper
    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 NTERP_TRAMPOLINE_RETURN_LONG 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
    movl %edx, %ecx
    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 EXPORT_PC
    movl    rPC, -8(rREFS)
.endm

.macro FETCH_PC
    movl    -8(rREFS), rPC
.endm


.macro BRANCH
    leal    (rPC, rINST, 2), rPC
    // Update method counter and do a suspend check if the branch is negative or zero.
    testl rINST, rINST
    jle 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

    movl $$3, %eax
    cmpl $$2, \refs
    cmovle %eax, \refs

    // Compute required frame size for dex registers: ((2 * ebx) + refs)
    leal (\refs, %ebx, 2), %edx
    sall $$2, %edx

    // Compute new stack pointer in fp: add 12 for saving the previous frame,
    // pc, and method being executed.
    leal -12(%esp), \fp
    subl %edx, \fp
    // Alignment
    andl $$-16, \fp

    // Now setup the stack pointer.
    movl %esp, %edx
    CFI_DEF_CFA_REGISTER(edx)
    movl \fp, %esp

    leal 12(%esp, \refs, 4), \refs
    leal (\refs, %ebx, 4), \fp

    // Save old stack pointer.
    movl %edx, -4(\refs)
    NTERP_DEF_CFA \cfi_refs

    // Save ArtMethod.
    movl 12(%edx), %eax
    movl %eax, (%esp)

    // Put nulls in reference frame.
    testl %ebx, %ebx
    je 2f
    movl \refs, %eax
1:
    movl $$0, (%eax)
    addl $$4, %eax
    cmpl %eax, \fp
    jne 1b
2:
%   pass
.endm

// Puts the next floating point argument into the expected register,
// fetching values based on a non-range invoke.
// Uses eax as temporary.
//
// TODO: We could simplify a lot of code by loading the G argument into
// the "inst" register. Given that we enter the handler with "1(rPC)" in
// the rINST, we can just add rINST<<16 to the args and we don't even
// need to pass "arg_index" around.
.macro LOOP_OVER_SHORTY_LOADING_XMMS xmm_reg, inst, shorty, arg_index, finished
1: // LOOP
    movb (REG_VAR(shorty)), %al             // al := *shorty
    addl MACRO_LITERAL(1), REG_VAR(shorty)  // shorty++
    cmpb MACRO_LITERAL(0), %al              // if (al == '\0') goto finished
    je VAR(finished)
    cmpb MACRO_LITERAL(68), %al             // if (al == 'D') goto FOUND_DOUBLE
    je 2f
    cmpb MACRO_LITERAL(70), %al             // if (al == 'F') goto FOUND_FLOAT
    je 3f
    shrl MACRO_LITERAL(4), REG_VAR(inst)
    addl MACRO_LITERAL(1), REG_VAR(arg_index)
    //  Handle extra argument in arg array taken by a long.
    cmpb MACRO_LITERAL(74), %al   // if (al != 'J') goto LOOP
    jne 1b
    shrl MACRO_LITERAL(4), REG_VAR(inst)
    addl MACRO_LITERAL(1), REG_VAR(arg_index)
    jmp 1b                        // goto LOOP
2:  // FOUND_DOUBLE
    subl MACRO_LITERAL(8), %esp
    movl REG_VAR(inst), %eax
    andl MACRO_LITERAL(0xf), %eax
    GET_VREG %eax, %eax
    movl %eax, (%esp)
    shrl MACRO_LITERAL(4), REG_VAR(inst)
    addl MACRO_LITERAL(1), REG_VAR(arg_index)
    cmpl MACRO_LITERAL(4), REG_VAR(arg_index)
    je 5f
    movl REG_VAR(inst), %eax
    andl MACRO_LITERAL(0xf), %eax
    shrl MACRO_LITERAL(4), REG_VAR(inst)
    addl MACRO_LITERAL(1), REG_VAR(arg_index)
    jmp 6f
5:
    movzbl 1(rPC), %eax
    andl MACRO_LITERAL(0xf), %eax
6:
    GET_VREG %eax, %eax
    movl %eax, 4(%esp)
    movq (%esp), REG_VAR(xmm_reg)
    addl MACRO_LITERAL(8), %esp
    jmp 4f
3:  // FOUND_FLOAT
    cmpl MACRO_LITERAL(4), REG_VAR(arg_index)
    je 7f
    movl REG_VAR(inst), %eax
    andl MACRO_LITERAL(0xf), %eax
    shrl MACRO_LITERAL(4), REG_VAR(inst)
    addl MACRO_LITERAL(1), REG_VAR(arg_index)
    jmp 8f
7:
    movzbl 1(rPC), %eax
    andl MACRO_LITERAL(0xf), %eax
8:
    GET_VREG_XMMs REG_VAR(xmm_reg), %eax
4:
%   pass
.endm

// Puts the next int/long/object argument in the expected register,
// fetching values based on a non-range invoke.
// Uses eax as temporary.
.macro LOOP_OVER_SHORTY_LOADING_GPRS gpr_reg, gpr_long_reg, inst, shorty, arg_index, finished, if_long, is_ebx
1: // LOOP
    movb (REG_VAR(shorty)), %al   // al := *shorty
    addl MACRO_LITERAL(1), REG_VAR(shorty)  // shorty++
    cmpb MACRO_LITERAL(0), %al    // if (al == '\0') goto finished
    je  VAR(finished)
    cmpb MACRO_LITERAL(74), %al   // if (al == 'J') goto FOUND_LONG
    je 2f
    cmpb MACRO_LITERAL(70), %al   // if (al == 'F') goto SKIP_FLOAT
    je 3f
    cmpb MACRO_LITERAL(68), %al   // if (al == 'D') goto SKIP_DOUBLE
    je 4f
    cmpl MACRO_LITERAL(4), REG_VAR(arg_index)
    je 7f
    movl REG_VAR(inst), %eax
    andl MACRO_LITERAL(0xf), %eax
    shrl MACRO_LITERAL(4), REG_VAR(inst)
    addl MACRO_LITERAL(1), REG_VAR(arg_index)
    jmp 8f
7:
    // Fetch PC
    movl LOCAL1(%esp), %eax
    movl -8(%eax), %eax
    movzbl 1(%eax), %eax
    andl MACRO_LITERAL(0xf), %eax
8:
    GET_VREG REG_VAR(gpr_reg), %eax
    jmp 5f
2:  // FOUND_LONG
    .if \is_ebx
      // Put back shorty and exit
      subl MACRO_LITERAL(1), REG_VAR(shorty)
      jmp 5f
    .else
      movl REG_VAR(inst), %eax
      andl MACRO_LITERAL(0xf), %eax
      GET_VREG REG_VAR(gpr_reg), %eax
      shrl MACRO_LITERAL(4), REG_VAR(inst)
      addl MACRO_LITERAL(1), REG_VAR(arg_index)
      cmpl MACRO_LITERAL(4), REG_VAR(arg_index)
      je 9f
      movl REG_VAR(inst), %eax
      andl MACRO_LITERAL(0xf), %eax
      shrl MACRO_LITERAL(4), REG_VAR(inst)
      addl MACRO_LITERAL(1), REG_VAR(arg_index)
      jmp 10f
9:
      // Fetch PC
      movl LOCAL1(%esp), %eax
      movl -8(%eax), %eax
      movzbl 1(%eax), %eax
      andl MACRO_LITERAL(0xf), %eax
10:
      GET_VREG REG_VAR(gpr_long_reg), %eax
      jmp \if_long
    .endif
3:  // SKIP_FLOAT
    shrl MACRO_LITERAL(4), REG_VAR(inst)
    addl MACRO_LITERAL(1), REG_VAR(arg_index)
    jmp 1b
4:  // SKIP_DOUBLE
    shrl MACRO_LITERAL(8), REG_VAR(inst)
    addl MACRO_LITERAL(2), REG_VAR(arg_index)
    jmp 1b
5:
%   pass
.endm

// Puts the next int/long/object argument in the expected stack slot,
// fetching values based on a non-range invoke.
// Uses eax as temporary.
.macro LOOP_OVER_SHORTY_LOADING_INTS stack_offset, shorty, inst, arg_index, finished, is_string_init
1:  // LOOP
    movb (REG_VAR(shorty)), %al   // al := *shorty
    addl MACRO_LITERAL(1), REG_VAR(shorty)  // shorty++
    cmpb MACRO_LITERAL(0), %al    // if (al == '\0') goto finished
    je  VAR(finished)
    cmpb MACRO_LITERAL(74), %al   // if (al == 'J') goto FOUND_LONG
    je 2f
    cmpb MACRO_LITERAL(70), %al   // if (al == 'F') goto SKIP_FLOAT
    je 3f
    cmpb MACRO_LITERAL(68), %al   // if (al == 'D') goto SKIP_DOUBLE
    je 4f
    .if \is_string_init
      cmpl MACRO_LITERAL(3), REG_VAR(arg_index)
    .else
      cmpl MACRO_LITERAL(4), REG_VAR(arg_index)
    .endif
    je 7f
    movl REG_VAR(inst), %eax
    andl MACRO_LITERAL(0xf), %eax
    shrl MACRO_LITERAL(4), REG_VAR(inst)
    jmp 8f
7:
    // Fetch PC.
    movl (LOCAL1 + \stack_offset)(%esp), %eax
    movl -8(%eax), %eax
    movzbl 1(%eax), %eax
    andl MACRO_LITERAL(0xf), %eax
8:
    GET_VREG %eax, %eax
    // Add 4 for the ArtMethod.
    movl %eax, (4 + \stack_offset)(%esp, REG_VAR(arg_index), 4)
    addl MACRO_LITERAL(1), REG_VAR(arg_index)
    jmp 1b
2:  // FOUND_LONG
    movl REG_VAR(inst), %eax
    andl MACRO_LITERAL(0xf), %eax
    GET_VREG %eax, %eax
    // Add 4 for the ArtMethod.
    movl %eax, (4 + \stack_offset)(%esp, REG_VAR(arg_index), 4)
    shrl MACRO_LITERAL(4), REG_VAR(inst)
    addl MACRO_LITERAL(1), REG_VAR(arg_index)
    .if \is_string_init
      cmpl MACRO_LITERAL(3), REG_VAR(arg_index)
    .else
      cmpl MACRO_LITERAL(4), REG_VAR(arg_index)
    .endif
    je 9f
    movl REG_VAR(inst), %eax
    andl MACRO_LITERAL(0xf), %eax
    shrl MACRO_LITERAL(4), REG_VAR(inst)
    jmp 10f
9:
    // Fetch PC.
    movl (LOCAL1 + \stack_offset)(%esp), %eax
    movl -8(%eax), %eax
    movzbl 1(%eax), %eax
    andl MACRO_LITERAL(0xf), %eax
10:
    GET_VREG %eax, %eax
    // +4 for the ArtMethod.
    movl %eax, (4 + \stack_offset)(%esp, REG_VAR(arg_index), 4)
    addl MACRO_LITERAL(1), REG_VAR(arg_index)
    jmp 1b
3:  // SKIP_FLOAT
    shrl MACRO_LITERAL(4), REG_VAR(inst)
    addl MACRO_LITERAL(1), REG_VAR(arg_index)
    jmp 1b
4:  // SKIP_DOUBLE
    shrl MACRO_LITERAL(8), REG_VAR(inst)
    addl MACRO_LITERAL(2), REG_VAR(arg_index)
    jmp 1b
.endm

// Puts the next floating point argument into the expected register,
// fetching values based on a range invoke.
// Uses eax as temporary.
.macro LOOP_RANGE_OVER_SHORTY_LOADING_XMMS xmm_reg, shorty, arg_index, stack_index, finished
1: // LOOP
    movb (REG_VAR(shorty)), %al             // al := *shorty
    addl MACRO_LITERAL(1), REG_VAR(shorty)  // shorty++
    cmpb MACRO_LITERAL(0), %al              // if (al == '\0') goto finished
    je VAR(finished)
    cmpb MACRO_LITERAL(68), %al             // if (al == 'D') goto FOUND_DOUBLE
    je 2f
    cmpb MACRO_LITERAL(70), %al             // if (al == 'F') goto FOUND_FLOAT
    je 3f
    addl MACRO_LITERAL(1), REG_VAR(arg_index)
    addl MACRO_LITERAL(1), REG_VAR(stack_index)
    //  Handle extra argument in arg array taken by a long.
    cmpb MACRO_LITERAL(74), %al   // if (al != 'J') goto LOOP
    jne 1b
    addl MACRO_LITERAL(1), REG_VAR(arg_index)
    addl MACRO_LITERAL(1), REG_VAR(stack_index)
    jmp 1b                        // goto LOOP
2:  // FOUND_DOUBLE
    GET_VREG_XMMd REG_VAR(xmm_reg), REG_VAR(arg_index)
    addl MACRO_LITERAL(2), REG_VAR(arg_index)
    addl MACRO_LITERAL(2), REG_VAR(stack_index)
    jmp 4f
3:  // FOUND_FLOAT
    GET_VREG_XMMs REG_VAR(xmm_reg), REG_VAR(arg_index)
    addl MACRO_LITERAL(1), REG_VAR(arg_index)
    add MACRO_LITERAL(1), REG_VAR(stack_index)
4:
%   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 GET_SHORTY dest, is_interface, is_polymorphic, is_custom
   // Save eax (ArtMethod), ecx (potential this).
   push %eax
   push %ecx
   .if \is_polymorphic
     push rPC
     push 12(%esp)
     call SYMBOL(NterpGetShortyFromInvokePolymorphic)
     addl MACRO_LITERAL(8), %esp
   .elseif \is_custom
     push rPC
     push 12(%esp)
     call SYMBOL(NterpGetShortyFromInvokeCustom)
     addl MACRO_LITERAL(8), %esp
   .elseif \is_interface
     subl MACRO_LITERAL(16), %esp
     // Save interface method.
     movss %xmm7, (%esp)
     movzwl 2(rPC), %eax
     pushl %eax
     // Caller is at 8 (saved ArtMethod + ecx) + 16 + 4 (second argument)
     pushl 28(%esp)
     call SYMBOL(NterpGetShortyFromMethodId)
     // Restore interface method.
     movss 8(%esp), %xmm7
     addl MACRO_LITERAL(24), %esp
   .else
     subl MACRO_LITERAL(4), %esp  // Alignment
     push %eax
     call SYMBOL(NterpGetShorty)
     addl MACRO_LITERAL(8), %esp
   .endif
   movl %eax, \dest
   pop %ecx
   pop %eax
.endm

.macro GET_SHORTY_SLOW_PATH dest, is_interface
   // Save all registers that can hold arguments in the fast path.
   pushl %eax
   pushl %ecx
   pushl %edx
   subl MACRO_LITERAL(4), %esp
   movss %xmm0, (%esp)
   .if \is_interface
     // Alignment.
     subl MACRO_LITERAL(8), %esp
     movzwl 2(rPC), %eax
     pushl %eax
     // Caller is at 16 (parameters) + 8 (alignment) + 4 (second argument).
     pushl 28(%esp)
     call SYMBOL(NterpGetShortyFromMethodId)
     movl %eax, \dest
     movss 16(%esp), %xmm0
     addl MACRO_LITERAL(20), %esp
   .else
     // Alignment.
     subl MACRO_LITERAL(12), %esp
     pushl %eax
     call SYMBOL(NterpGetShorty)
     movl %eax, \dest
     movss 16(%esp), %xmm0
     addl MACRO_LITERAL(20), %esp
   .endif
   popl %edx
   popl %ecx
   popl %eax
.endm

// Uses ecx and edx as temporary
.macro UPDATE_REGISTERS_FOR_STRING_INIT old_value, new_value
   movl rREFS, %edx
   movl rFP, %ecx
1:
   cmpl (%edx), \old_value
   jne 2f
   movl \new_value, (%edx)
   movl \new_value, (%ecx)
2:
   addl $$4, %edx
   addl $$4, %ecx
   cmpl %edx, rFP
   jne 1b
.endm

.macro DO_CALL is_polymorphic, is_custom
   .if \is_polymorphic
     call SYMBOL(art_quick_invoke_polymorphic)
   .elseif \is_custom
     call SYMBOL(art_quick_invoke_custom)
   .else
     call *ART_METHOD_QUICK_CODE_OFFSET_32(%eax)
   .endif
.endm

.macro COMMON_INVOKE_NON_RANGE is_static=0, is_interface=0, suffix="", is_string_init=0, is_polymorphic=0, is_custom=0
   .if \is_polymorphic
     // No fast path for polymorphic calls.
%    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

   .if \is_polymorphic
     ADVANCE_PC_FETCH_AND_GOTO_NEXT 4
   .else
     ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
   .endif

.Linvoke_double_\suffix:
   FETCH_PC
   DO_CALL \is_polymorphic, \is_custom
   movq %xmm0, LOCAL1(%esp)
   movl LOCAL1(%esp), %eax
   jmp .Ldone_return_\suffix
.Linvoke_float_\suffix:
   FETCH_PC
   DO_CALL \is_polymorphic, \is_custom
   movd %xmm0, %eax
   jmp .Ldone_return_\suffix
.endm

.macro COMMON_INVOKE_RANGE is_static=0, is_interface=0, suffix="", is_string_init=0, is_polymorphic=0, is_custom=0
   .if \is_polymorphic
     // No fast path for polymorphic calls.
%    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_range_\suffix
     movzbl 1(rPC), %edx  // number of arguments
     .if \is_static
       testl %edx, %edx
       je .Linvoke_fast_path_range_\suffix
     .else
       cmpl MACRO_LITERAL(1), %edx
       je .Linvoke_fast_path_range_\suffix
     .endif
     movzwl 4(rPC), %ebx  // dex register of first argument
     leal (rFP, %ebx, 4), %esi  // location of first dex register value
     cmpl MACRO_LITERAL(2), %edx
     .if \is_static
       jl .Lone_arg_fast_path_range_\suffix
     .endif
     je .Ltwo_args_fast_path_range_\suffix
     cmp MACRO_LITERAL(4), %edx
     jl .Lthree_args_fast_path_range_\suffix

.Lloop_over_fast_path_range_\suffix:
     subl MACRO_LITERAL(1), %edx
     movl (%esi, %edx, 4), %ebx
     movl %ebx, 4(%esp, %edx, 4)  // Add 4 for the ArtMethod
     cmpl MACRO_LITERAL(3), %edx
     jne .Lloop_over_fast_path_range_\suffix

.Lthree_args_fast_path_range_\suffix:
     movl 8(%esi), %ebx
.Ltwo_args_fast_path_range_\suffix:
     movl 4(%esi), %edx
.Lone_arg_fast_path_range_\suffix:
     .if \is_static
       movl 0(%esi), %ecx
     .else
       // First argument already in %ecx.
%      pass
     .endif
.Linvoke_fast_path_range_\suffix:
     FETCH_PC
     call *ART_METHOD_QUICK_CODE_OFFSET_32(%eax) // Call the method.
     SAVE_WIDE_RETURN
     RESTORE_IBASE
     ADVANCE_PC_FETCH_AND_GOTO_NEXT 3

.Lfast_path_with_few_args_range_\suffix:
     // Fast path when we have zero or one argument (modulo 'this'). If there
     // is one argument, we can put it in both floating point and core register.
     movzbl 1(rPC), %ebx # Number of arguments
     .if \is_static
       cmpl MACRO_LITERAL(1), %ebx
       jl .Linvoke_with_few_args_range_\suffix
       jne .Lget_shorty_range_\suffix
       movzwl 4(rPC), %ebx  // Dex register of first argument
       GET_VREG %ecx, %ebx
       movd %ecx, %xmm0
     .else
       cmpl MACRO_LITERAL(2), %ebx
       jl .Linvoke_with_few_args_range_\suffix
       jne .Lget_shorty_range_\suffix
       movzwl 4(rPC), %ebx
       addl MACRO_LITERAL(1), %ebx  // dex register of second argument
       GET_VREG %edx, %ebx
       movd %edx, %xmm0
     .endif
.Linvoke_with_few_args_range_\suffix:
     // Check if the next instruction is move-result or move-result-wide.
     // If it is, we fetch the shorty and jump to the regular invocation.
     movzwl  6(rPC), %ebx
     and MACRO_LITERAL(0xfe), %ebx
     cmpl MACRO_LITERAL(0x0a), %ebx
     je .Lget_shorty_and_invoke_range_\suffix
     call *ART_METHOD_QUICK_CODE_OFFSET_32(%eax) // Call the method.
     RESTORE_IBASE
     ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
.Lget_shorty_and_invoke_range_\suffix:
     GET_SHORTY_SLOW_PATH %esi, \is_interface
     jmp .Lgpr_setup_finished_range_\suffix
   .endif

.Lget_shorty_range_\suffix:
   GET_SHORTY %ebx, \is_interface, \is_polymorphic, \is_custom
   movl %eax, LOCAL0(%esp)
   movl %ebp, LOCAL1(%esp)
   movl %ebx, LOCAL2(%esp)
   // From this point:
   // - ebx contains shorty (in callee-save to switch over return value after call).
   // - eax, edx, ebx, and ebp are available.
   // - ecx contains 'this' pointer for instance method.
   // TODO: ebp/rREFS is used for stack unwinding, can we find a way to preserve it?
   leal 1(%ebx), %edx  // shorty + 1  ; ie skip return arg character
   movzwl 4(rPC), %ebx // arg start index
   .if \is_string_init
     addl $$1, %ebx       // arg start index
     movl $$0, %ebp       // index in stack
   .elseif \is_static
     movl $$0, %ebp       // index in stack
   .else
     addl $$1, %ebx       // arg start index
     movl $$1, %ebp       // index in stack
   .endif
   LOOP_RANGE_OVER_SHORTY_LOADING_XMMS xmm0, edx, ebx, ebp, .Lxmm_setup_finished_range_\suffix
   LOOP_RANGE_OVER_SHORTY_LOADING_XMMS xmm1, edx, ebx, ebp, .Lxmm_setup_finished_range_\suffix
   LOOP_RANGE_OVER_SHORTY_LOADING_XMMS xmm2, edx, ebx, ebp, .Lxmm_setup_finished_range_\suffix
   LOOP_RANGE_OVER_SHORTY_LOADING_XMMS xmm3, edx, ebx, ebp, .Lxmm_setup_finished_range_\suffix
   LOOP_RANGE_OVER_FPs edx, ebx, ebp, .Lxmm_setup_finished_range_\suffix
.Lxmm_setup_finished_range_\suffix:
   // Reload rREFS for fetching the PC.
   movl LOCAL1(%esp), %ebp
   // Reload shorty
   movl LOCAL2(%esp), %ebx
   FETCH_PC
   leal 1(%ebx), %ebx  // shorty + 1  ; ie skip return arg character
   // From this point:
   // - ebx contains shorty
   // - eax and ebp are available.
   // - ecx contains 'this' pointer for instance method.
   movzwl 4(rPC), %ebp // arg start index
   // rPC (esi) is now available
   .if \is_string_init
     addl $$1, %ebp       // arg start index
     movl $$0, %esi       // index in stack
     LOOP_RANGE_OVER_SHORTY_LOADING_GPRS ecx, edx, ebx, ebp, esi, .Lrestore_saved_values_range_\suffix, .Lif_long_ebx_range_\suffix, is_ebx=0
   .elseif \is_static
     movl $$0, %esi // index in stack
     LOOP_RANGE_OVER_SHORTY_LOADING_GPRS ecx, edx, ebx, ebp, esi, .Lrestore_saved_values_range_\suffix, .Lif_long_ebx_range_\suffix, is_ebx=0
   .else
     addl $$1, %ebp // arg start index
     movl $$1, %esi // index in stack
   .endif
   // For long argument, store second half in eax to not overwrite the shorty.
   LOOP_RANGE_OVER_SHORTY_LOADING_GPRS edx, eax, ebx, ebp, esi, .Lrestore_saved_values_range_\suffix, .Lif_long_range_\suffix, is_ebx=0
.Lif_long_ebx_range_\suffix:
   // Store in eax to not overwrite the shorty.
   LOOP_RANGE_OVER_SHORTY_LOADING_GPRS eax, eax, ebx, ebp, esi, .Lrestore_saved_values_range_\suffix, .Lif_long_range_\suffix, is_ebx=1
.Lif_long_range_\suffix:
   // Save shorty, as LOOP_RANGE_OVER_SHORTY_LOADING_INTS might overwrite the LOCAL2 slot for a long argument.
   pushl LOCAL2(%esp)
   pushl %eax
   LOOP_RANGE_OVER_INTs 8, ebx, ebp, esi, .Lrestore_ebx_range_\suffix
.Lrestore_ebx_range_\suffix:
   popl %ebx
   popl %esi
   movl LOCAL0(%esp), %eax
   movl LOCAL1(%esp), %ebp
   jmp .Lgpr_setup_finished_range_\suffix

.Lrestore_saved_values_range_\suffix:
   movl LOCAL0(%esp), %eax
   movl LOCAL1(%esp), %ebp
   // Save shorty in callee-save register
   movl LOCAL2(%esp), %esi

.Lgpr_setup_finished_range_\suffix:
   cmpb LITERAL(68), (%esi)       // Test if result type char == 'D'.
   je .Lreturn_range_double_\suffix
   cmpb LITERAL(70), (%esi)       // Test if result type char == 'F'.
   je .Lreturn_range_float_\suffix

   FETCH_PC
   DO_CALL \is_polymorphic, \is_custom
   SAVE_WIDE_RETURN
.Ldone_return_range_\suffix:
   /* resume execution of caller */
   .if \is_string_init
     movzwl 4(rPC), %ecx // arguments
     GET_VREG rINST, %ecx
     UPDATE_REGISTERS_FOR_STRING_INIT rINST, %eax
   .endif
   RESTORE_IBASE
   .if \is_polymorphic
     ADVANCE_PC_FETCH_AND_GOTO_NEXT 4
   .else
     ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
   .endif
.Lreturn_range_double_\suffix:
   FETCH_PC
   DO_CALL \is_polymorphic, \is_custom
   movq %xmm0, LOCAL1(%esp)
   movl LOCAL1(%esp), %eax
   jmp .Ldone_return_range_\suffix
.Lreturn_range_float_\suffix:
   FETCH_PC
   DO_CALL \is_polymorphic, \is_custom
   movd %xmm0, %eax
   jmp .Ldone_return_range_\suffix
.endm

%def fetch_from_thread_cache(dest_reg, miss_label):
   // Fetch some information from the thread cache.
   // Uses eax, and ecx as temporaries.
   movl rSELF:THREAD_SELF_OFFSET, %eax
   movl rPC, %ecx
   andl MACRO_LITERAL(THREAD_INTERPRETER_CACHE_KEY_MASK), %ecx
   cmpl THREAD_INTERPRETER_CACHE_OFFSET(%eax, %ecx, 2), rPC
   jne  ${miss_label}
   movl __SIZEOF_POINTER__+THREAD_INTERPRETER_CACHE_OFFSET(%eax, %ecx, 2), ${dest_reg}

%def fetch_uint64_from_thread_cache_eax_ecx(miss_label):
   // Fetch some information from the thread cache.
   // Uses eax, and ecx as temporaries.
   movl rSELF:THREAD_SELF_OFFSET, %eax
   movl rPC, %ecx
   andl MACRO_LITERAL(THREAD_INTERPRETER_CACHE_KEY_MASK & (~1<<THREAD_INTERPRETER_CACHE_KEY_LOW_BIT)), %ecx
   cmpl THREAD_INTERPRETER_CACHE_OFFSET(%eax, %ecx, 2), rPC
   jne  ${miss_label}
   cmpl 2*__SIZEOF_POINTER__+THREAD_INTERPRETER_CACHE_OFFSET(%eax, %ecx, 2), rPC
   jne  ${miss_label}
   leal THREAD_INTERPRETER_CACHE_OFFSET(%eax, %ecx, 2), %ecx
   movl __SIZEOF_POINTER__(%ecx), %eax
   movl 3*__SIZEOF_POINTER__(%ecx), %ecx

%def fetch_field_from_fast_path(get_local, success_label, fail_label, is_static=False):
   movl 0(%esp), %eax
   testl $$ART_METHOD_IS_OBSOLETE_FLAG, ART_METHOD_ACCESS_FLAGS_OFFSET(%eax)
   jne ${fail_label}
   // We don't do a read barrier for faster execution.
   // See comment in `NterpGetLocalStaticFieldInternal`.
   movl ART_METHOD_DECLARING_CLASS_OFFSET(%eax), %eax
   subl MACRO_LITERAL(8), %esp
   pushl rPC
   pushl %eax
   call ${get_local}
%  if is_static:
     mov %edx, %ecx
   addl MACRO_LITERAL(16), %esp
   RESTORE_IBASE
   FETCH_INST_CLEAR_OPCODE
   cmpl MACRO_LITERAL(0), %eax
%  if is_static:
     jne ${success_label}
%  else:
     jge ${success_label}


// Helper for static field get.
.macro OP_SGET load="movl", wide="0"
   // Fast-path which gets the field from thread-local cache.
%  fetch_uint64_from_thread_cache_eax_ecx(miss_label="2f")
1:
   cmpl $$0, rSELF:THREAD_READ_BARRIER_MARK_REG00_OFFSET
   jne 3f
4:
   .if \wide
     addl %ecx, %eax
     \load (%eax), %ecx
     SET_VREG %ecx, rINST            # fp[A] <- value
     \load 4(%eax), %ecx
     SET_VREG_HIGH %ecx, rINST
   .else
     \load (%ecx, %eax, 1), %eax
     SET_VREG %eax, rINST            # fp[A] <- value
   .endif
   ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
2:
%  fetch_field_from_fast_path("NterpGetLocalStaticField", success_label="1b", fail_label="9f", is_static=True)
9:
   EXPORT_PC
   movl rSELF:THREAD_SELF_OFFSET, ARG0
   movl 0(%esp), ARG1
   movl rPC, ARG2
   movl $$0, ARG3
   call nterp_get_static_field
   testl %eax, %eax
   jns 1b
   negl %eax
   .if !\wide
     jmp 1b
   .else
     cmpl $$0, rSELF:THREAD_READ_BARRIER_MARK_REG00_OFFSET
     jne 5f
6:
     movsd (%ecx, %eax, 1), %xmm0
     SET_WIDE_FP_VREG %xmm0, rINST
     ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
5:
     call art_quick_read_barrier_mark_reg01
     jmp 6b
   .endif
3:
   call art_quick_read_barrier_mark_reg01
   jmp 4b
.endm

// Helper for static field put.
.macro OP_SPUT rINST_reg="rINST", store="movl", wide="0":
   // Fast-path which gets the field from thread-local cache.
%  fetch_uint64_from_thread_cache_eax_ecx(miss_label="2f")
1:
   cmpl $$0, rSELF:THREAD_READ_BARRIER_MARK_REG00_OFFSET
   jne 3f
4:
   .if \wide
     addl %ecx, %eax
     GET_VREG %ecx, rINST                  # rINST <- v[A]
     movl %ecx, (%eax)
     GET_VREG_HIGH %ecx, rINST
     movl %ecx, 4(%eax)
   .else
     GET_VREG rINST, rINST                  # rINST <- v[A]
     \store    \rINST_reg, (%ecx,%eax,1)
   .endif
   ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
2:
%  fetch_field_from_fast_path("NterpGetLocalStaticField", success_label="1b", fail_label="7f", is_static=True)
7:
   EXPORT_PC
   movl rSELF:THREAD_SELF_OFFSET, ARG0
   movl 0(%esp), ARG1
   movl rPC, ARG2
   movl $$0, ARG3
   call nterp_get_static_field
   testl %eax, %eax
   jns 1b
   negl %eax
   cmpl $$0, rSELF:THREAD_READ_BARRIER_MARK_REG00_OFFSET
   jne 6f
5:
   .if \wide
     addl %ecx, %eax
     GET_WIDE_FP_VREG %xmm0, rINST
     movsd %xmm0, (%eax)
   .else
     GET_VREG rINST, rINST                  # rINST <- v[A]
     \store    \rINST_reg, (%ecx,%eax,1)
   .endif
   lock addl $$0, (%esp)
   ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
3:
   call art_quick_read_barrier_mark_reg01
   jmp 4b
6:
   call art_quick_read_barrier_mark_reg01
   jmp 5b
.endm


.macro OP_IPUT_INTERNAL rINST_reg="rINST", store="movl", wide="0", volatile="0":
   movzbl  rINSTbl, %ecx                   # ecx <- BA
   sarl    $$4, %ecx                       # ecx <- B
   GET_VREG %ecx, %ecx                     # vB (object we're operating on)
   testl   %ecx, %ecx                      # is object null?
   je      common_errNullObject
   andb    $$0xf, rINSTbl                  # rINST <- A
   .if \wide
     addl %ecx, %eax
     GET_WIDE_FP_VREG %xmm0, rINST
     movsd %xmm0, (%eax)
   .else
     GET_VREG rINST, rINST                  # rINST <- v[A]
     \store \rINST_reg, (%ecx,%eax,1)
   .endif
.endm

// Helper for instance field put.
.macro OP_IPUT rINST_reg="rINST", store="movl", wide="0":
   // Fast-path which gets the field from thread-local cache.
%  fetch_from_thread_cache("%eax", miss_label="2f")
1:
   OP_IPUT_INTERNAL \rINST_reg, \store, \wide, volatile=0
   ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
2:
%  fetch_field_from_fast_path("NterpGetLocalInstanceField", success_label="1b", fail_label="3f")
3:
   EXPORT_PC
   movl rSELF:THREAD_SELF_OFFSET, ARG0
   movl 0(%esp), ARG1
   movl rPC, ARG2
   movl rFP, ARG3
   call nterp_get_instance_field_offset
   testl %eax, %eax
   jns 1b
   negl %eax
   OP_IPUT_INTERNAL \rINST_reg, \store, \wide, volatile=1
   lock addl $$0, (%esp)
   ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
.endm

// Helper for instance field get.
.macro OP_IGET load="movl", wide="0"
   // Fast-path which gets the field from thread-local cache.
%  fetch_from_thread_cache("%eax", miss_label="2f")
1:
   movl    rINST, %ecx                     # ecx <- BA
   sarl    $$4, %ecx                       # ecx <- B
   GET_VREG %ecx, %ecx                     # vB (object we're operating on)
   testl   %ecx, %ecx                      # is object null?
   je      common_errNullObject
   andb    $$0xf,rINSTbl                   # rINST <- A
   .if \wide
     addl %ecx, %eax
     \load (%eax), %ecx
     SET_VREG %ecx, rINST
     \load 4(%eax), %ecx
     SET_VREG_HIGH %ecx, rINST
   .else
     \load (%ecx,%eax,1), %eax
     SET_VREG %eax, rINST                    # fp[A] <- value
   .endif
   ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
2:
%  fetch_field_from_fast_path("NterpGetLocalInstanceField", success_label="1b", fail_label="3f")
3:
   EXPORT_PC
   movl rSELF:THREAD_SELF_OFFSET, ARG0
   movl 0(%esp), ARG1
   movl rPC, ARG2
   movl rFP, ARG3
   call nterp_get_instance_field_offset
   testl %eax, %eax
   jns 1b
   negl %eax
   .if !\wide
     jmp 1b
   .else
     movl    rINST, %ecx                     # ecx <- BA
     sarl    $$4, %ecx                       # ecx <- B
     GET_VREG %ecx, %ecx                     # vB (object we're operating on)
     testl   %ecx, %ecx                      # is object null?
     je      common_errNullObject
     andb    $$0xf,rINSTbl                   # rINST <- A
     movsd (%eax, %ecx, 1), %xmm0
     SET_WIDE_FP_VREG %xmm0, rINST
     ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
   .endif
.endm

// Store a reference parameter into our dex register frame.
// Uses xmm4 as temporary.
.macro SETUP_REFERENCE_PARAMETER_IN_GPR offset, stack_ptr, regs, refs, ins, arg_offset, finished
    movss \offset(REG_VAR(stack_ptr)), %xmm4
    movss %xmm4, (REG_VAR(regs), REG_VAR(arg_offset))
    movss %xmm4, (REG_VAR(refs), REG_VAR(arg_offset))
    addl MACRO_LITERAL(4), REG_VAR(arg_offset)
    subl MACRO_LITERAL(1), REG_VAR(ins)
    je \finished
.endm

// Store a reference parameter into our dex register frame.
// Uses xmm4 as temporary.
.macro SETUP_REFERENCE_PARAMETERS_IN_STACK stack_ptr, regs, refs, ins, arg_offset
1:
    movss OFFSET_TO_FIRST_ARGUMENT_IN_STACK(REG_VAR(stack_ptr), REG_VAR(arg_offset)), %xmm4
    movss %xmm4, (REG_VAR(regs), REG_VAR(arg_offset))
    movss %xmm4, (REG_VAR(refs), REG_VAR(arg_offset))
    addl MACRO_LITERAL(4), REG_VAR(arg_offset)
    subl MACRO_LITERAL(1), REG_VAR(ins)
    jne 1b
.endm

.macro DO_SUSPEND_CHECK continue_label
    testl   $$(THREAD_SUSPEND_OR_CHECKPOINT_REQUEST), rSELF:THREAD_FLAGS_OFFSET
    jz      \continue_label
    jmp     NterpCallSuspendAndGotoNext
.endm

.macro CHECK_AND_UPDATE_SHARED_MEMORY_METHOD if_hot, if_not_hot
    testl $$ART_METHOD_IS_MEMORY_SHARED_FLAG, ART_METHOD_ACCESS_FLAGS_OFFSET(%eax)
    jz \if_hot
    // Intrinsics are always in the boot image and considered hot.
    testl $$ART_METHOD_IS_INTRINSIC_FLAG, ART_METHOD_ACCESS_FLAGS_OFFSET(%eax)
    jnz \if_hot
    movzwl rSELF:THREAD_SHARED_METHOD_HOTNESS_OFFSET, %ecx
    testl %ecx, %ecx
    je \if_hot
    addl $$-1, %ecx
    movw %cx, rSELF:THREAD_SHARED_METHOD_HOTNESS_OFFSET
    jmp \if_not_hot
.endm


/*
 * ArtMethod entry point.
 *
 * On entry:
 *  eax   ArtMethod* callee
 *  rest  method parameters
 */


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

OAT_ENTRY ExecuteNterpImpl
    .cfi_startproc
    .cfi_def_cfa esp, 4
    testl %eax, -STACK_OVERFLOW_RESERVED_BYTES(%esp)
    // Spill callee save regs
    SPILL_ALL_CALLEE_SAVES

    // Make argument registers available.
    SPILL_ALL_CORE_PARAMETERS

    // Fetch code item.
    movl ART_METHOD_DATA_OFFSET_32(%eax), %ecx

    // Setup the stack for executing the method.
    SETUP_STACK_FRAME %ecx, rREFS, rFP, CFI_REFS, load_ins=1

    // Save the PC
    movl %ecx, -8(rREFS)

    // Setup the parameters
    testl %esi, %esi
    je .Lxmm_setup_finished

    subl %esi, %ebx
    sall $$2, %ebx // ebx is now the offset for inputs into the registers array.

    // Reload ArtMethod.
    movl (%esp), %eax
    testl $$ART_METHOD_NTERP_ENTRY_POINT_FAST_PATH_FLAG, ART_METHOD_ACCESS_FLAGS_OFFSET(%eax)
    je .Lsetup_slow_path
    leal (rREFS, %ebx, 1), %ecx
    leal (rFP, %ebx, 1), %ebx
    movl $$0, %eax

    // edx is the old stack pointer
    SETUP_REFERENCE_PARAMETER_IN_GPR 8, edx, ebx, ecx, esi, eax, .Lxmm_setup_finished
    SETUP_REFERENCE_PARAMETER_IN_GPR 4, edx, ebx, ecx, esi, eax, .Lxmm_setup_finished
    SETUP_REFERENCE_PARAMETER_IN_GPR 0, edx, ebx, ecx, esi, eax, .Lxmm_setup_finished
    SETUP_REFERENCE_PARAMETERS_IN_STACK edx, ebx, ecx, esi, eax
    jmp .Lxmm_setup_finished

.Lsetup_slow_path:
    // If the method is not static and there is one argument ('this'), we don't need to fetch the
    // shorty.
    testl $$ART_METHOD_IS_STATIC_FLAG, ART_METHOD_ACCESS_FLAGS_OFFSET(%eax)
    jne .Lsetup_with_shorty

    // Record 'this'.
    movl 8(%edx), %eax
    movl %eax, (rFP, %ebx)
    movl %eax, (rREFS, %ebx)

    cmpl $$1, %esi
    je .Lxmm_setup_finished

.Lsetup_with_shorty:
    // Save xmm registers. Core registers have already been saved.
    subl MACRO_LITERAL(4 * 8), %esp
    movq %xmm0, 0(%esp)
    movq %xmm1, 8(%esp)
    movq %xmm2, 16(%esp)
    movq %xmm3, 24(%esp)
    subl MACRO_LITERAL(12), %esp
    pushl (4 * 8 + 12)(%esp)
    call SYMBOL(NterpGetShorty)
    addl MACRO_LITERAL(16), %esp

    // Restore xmm registers
    movq 0(%esp), %xmm0
    movq 8(%esp), %xmm1
    movq 16(%esp), %xmm2
    movq 24(%esp), %xmm3
    addl MACRO_LITERAL(4 * 8), %esp

    // Reload the old stack pointer.
    movl -4(rREFS), %edx
    // TODO: Get shorty in a better way and remove above

    movl $$0, %esi
    movl (%esp), %ecx
    testl $$ART_METHOD_IS_STATIC_FLAG, ART_METHOD_ACCESS_FLAGS_OFFSET(%ecx)

    // Note the leal and movl below don't change the flags.
    leal (rFP, %ebx, 1), %ecx
    leal (rREFS, %ebx, 1), %ebx
    // Save rFP (%edi), we're using it as temporary below.
    movl rFP, LOCAL1(%esp)
    leal 1(%eax), %edi  // shorty + 1  ; ie skip return arg character
    // Save shorty + 1
    movl %edi, LOCAL2(%esp)
    jne .Lhandle_static_method
    addl $$4, %ecx
    addl $$4, %ebx
    addl $$4, %edx
    LOOP_OVER_SHORTY_STORING_GPRS 0, -4, edx, edi, esi, ecx, ebx, .Lgpr_setup_finished, .Lif_long, is_ebx=0
    LOOP_OVER_SHORTY_STORING_GPRS -40, edx, edi, esi, ecx, ebx, .Lgpr_setup_finished, .Lif_long, is_ebx=1
    jmp .Lif_long
.Lhandle_static_method:
    LOOP_OVER_SHORTY_STORING_GPRS 84, edx, edi, esi, ecx, ebx, .Lgpr_setup_finished, .Lif_long_ebx, is_ebx=0
    LOOP_OVER_SHORTY_STORING_GPRS 40, edx, edi, esi, ecx, ebx, .Lgpr_setup_finished, .Lif_long, is_ebx=0
.Lif_long_ebx:
    LOOP_OVER_SHORTY_STORING_GPRS 00, edx, edi, esi, ecx, ebx, .Lgpr_setup_finished, .Lif_long, is_ebx=1
.Lif_long:
    LOOP_OVER_INTs edi, esi, ecx, ebx, edx, .Lgpr_setup_finished
.Lgpr_setup_finished:
    // Restore shorty + 1
    movl LOCAL2(%esp), %edi
    movl $$0, %esi // reset counter
    LOOP_OVER_SHORTY_STORING_XMMS xmm0, edi, esi, ecx, .Lrestore_fp
    LOOP_OVER_SHORTY_STORING_XMMS xmm1, edi, esi, ecx, .Lrestore_fp
    LOOP_OVER_SHORTY_STORING_XMMS xmm2, edi, esi, ecx, .Lrestore_fp
    LOOP_OVER_SHORTY_STORING_XMMS xmm3, edi, esi, ecx, .Lrestore_fp
    LOOP_OVER_FPs edi, esi, ecx, edx, .Lrestore_fp
.Lrestore_fp:
    movl LOCAL1(%esp), rFP
.Lxmm_setup_finished:
    FETCH_PC
    CFI_DEFINE_DEX_PC_WITH_OFFSET(CFI_TMP, CFI_DEX, 0)
    // Set rIBASE
    RESTORE_IBASE
    /* start executing the instruction at rPC */
    START_EXECUTING_INSTRUCTIONS
    /* NOTE: no fallthrough */
    // cfi info continues, and covers the whole nterp implementation.
    END ExecuteNterpImpl

%def opcode_pre():
%   pass

%def footer():
/*
 * ===========================================================================
 *  Common subroutines and data
 * ===========================================================================
 */


  .text
  .align  2

  // Enclose all code below in a symbol (which gets printed in backtraces).
  ENTRY nterp_helper

// Note: mterp also uses the common_* names below for helpers, but that's OK
// as the assembler compiled each interpreter separately.
common_errDivideByZero:
    EXPORT_PC
    call art_quick_throw_div_zero

// Expect array in eax, index in ecx.
common_errArrayIndex:
    EXPORT_PC
    movl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %edx
    movl %ecx, %eax
    movl %edx, %ecx
    call art_quick_throw_array_bounds

common_errNullObject:
    EXPORT_PC
    call art_quick_throw_null_pointer_exception

NterpCommonInvokeStatic:
    COMMON_INVOKE_NON_RANGE is_static=1, is_interface=0, suffix="invokeStatic"

NterpCommonInvokeStaticRange:
    COMMON_INVOKE_RANGE is_static=1, is_interface=0, suffix="invokeStatic"

NterpCommonInvokeInstance:
    COMMON_INVOKE_NON_RANGE is_static=0, is_interface=0, suffix="invokeInstance"

NterpCommonInvokeInstanceRange:
    COMMON_INVOKE_RANGE is_static=0, is_interface=0, suffix="invokeInstance"

NterpCommonInvokeInterface:
    COMMON_INVOKE_NON_RANGE is_static=0, is_interface=1, suffix="invokeInterface"

NterpCommonInvokeInterfaceRange:
    COMMON_INVOKE_RANGE is_static=0, is_interface=1, suffix="invokeInterface"

NterpCommonInvokePolymorphic:
    COMMON_INVOKE_NON_RANGE is_static=0, is_interface=0, is_polymorphic=1, suffix="invokePolymorphic"

NterpCommonInvokePolymorphicRange:
    COMMON_INVOKE_RANGE is_static=0, is_interface=0, is_polymorphic=1, suffix="invokePolymorphic"

NterpCommonInvokeCustom:
    COMMON_INVOKE_NON_RANGE is_static=1, is_interface=0, is_polymorphic=0, is_custom=1, suffix="invokeCustom"

NterpCommonInvokeCustomRange:
    COMMON_INVOKE_RANGE is_static=1, is_interface=0, is_polymorphic=0, is_custom=1, suffix="invokeCustom"

NterpHandleStringInit:
    COMMON_INVOKE_NON_RANGE is_static=0, is_interface=0, is_string_init=1, suffix="stringInit"

NterpHandleStringInitRange:
    COMMON_INVOKE_RANGE is_static=0, is_interface=0, is_string_init=1, suffix="stringInit"

NterpNewInstance:
    EXPORT_PC
    // Fast-path which gets the class from thread-local cache.
%   fetch_from_thread_cache("%eax", miss_label="2f")
    cmpl $$0, rSELF:THREAD_READ_BARRIER_MARK_REG00_OFFSET
    jne 3f
4:
    call *rSELF:THREAD_ALLOC_OBJECT_ENTRYPOINT_OFFSET
    RESTORE_IBASE
    FETCH_INST_CLEAR_OPCODE
1:
    SET_VREG_OBJECT %eax, rINST             # fp[A] <- value
    ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
2:
    movl rSELF:THREAD_SELF_OFFSET, ARG0
    movl 0(%esp), ARG1
    movl rPC, ARG2
    call nterp_allocate_object
    jmp 1b
3:
    // 00 is %eax
    call art_quick_read_barrier_mark_reg00
    jmp 4b

NterpNewArray:
    /* new-array vA, vB, class@CCCC */
    EXPORT_PC
    // Fast-path which gets the class from thread-local cache.
%   fetch_from_thread_cache("%eax", miss_label="2f")
    cmpl $$0, rSELF:THREAD_READ_BARRIER_MARK_REG00_OFFSET
    jne 3f
1:
    movzbl  rINSTbl, %ecx
    sarl    $$4, %ecx                         # ecx<- B
    GET_VREG %ecx %ecx                        # ecx<- vB (array length)
    call *rSELF:THREAD_ALLOC_ARRAY_ENTRYPOINT_OFFSET
    RESTORE_IBASE
    FETCH_INST_CLEAR_OPCODE
    andb    $$0xf, rINSTbl                   # rINST<- A
    SET_VREG_OBJECT %eax, rINST              # fp[A] <- value
    ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
2:
    movl rSELF:THREAD_SELF_OFFSET, ARG0
    movl 0(%esp), ARG1
    movl rPC, ARG2
    call nterp_get_class
    jmp 1b
3:
    // 00 is %eax
    call art_quick_read_barrier_mark_reg00
    jmp 1b

NterpPutObjectInstanceField:
    // Fast-path which gets the field from thread-local cache.
%   fetch_from_thread_cache("%eax", miss_label="2f")
1:
    movl    rINST, %ecx                     # ecx <- BA
    andl    $$0xf, %ecx                     # ecx <- A
    GET_VREG %ecx, %ecx                     # ecx <- v[A]
    sarl    $$4, rINST
    GET_VREG rINST, rINST                   # vB (object we're operating on)
    testl   rINST, rINST                    # is object null?
    je      common_errNullObject
    POISON_HEAP_REF ecx
    movl %ecx, (rINST, %eax, 1)
    testl %ecx, %ecx
    je 4f
    movl rSELF:THREAD_CARD_TABLE_OFFSET, %eax
    shrl $$CARD_TABLE_CARD_SHIFT, rINST
    movb %al, (%eax, rINST, 1)
4:
    ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
2:
%   fetch_field_from_fast_path("NterpGetLocalInstanceFieldForIPutObject", success_label="1b", fail_label="6f")
6:
    EXPORT_PC
    movl rSELF:THREAD_SELF_OFFSET, ARG0
    movl 0(%esp), ARG1
    movl rPC, ARG2
    movl rFP, ARG3
    call nterp_get_instance_field_offset
    testl %eax, %eax
    jns 1b
    negl %eax
    // Reload the value as it may have moved.
    movl    rINST, %ecx                     # ecx <- BA
    andl    $$0xf, %ecx                     # ecx <- A
    GET_VREG %ecx, %ecx                     # ecx <- v[A]
    sarl    $$4, rINST
    GET_VREG rINST, rINST                   # vB (object we're operating on)
    testl   rINST, rINST                    # is object null?
    je      common_errNullObject
    POISON_HEAP_REF ecx
    movl %ecx, (rINST, %eax, 1)
    testl %ecx, %ecx
    je 5f
    movl rSELF:THREAD_CARD_TABLE_OFFSET, %eax
    shrl $$CARD_TABLE_CARD_SHIFT, rINST
    movb %al, (%eax, rINST, 1)
5:
    lock addl $$0, (%esp)
    ADVANCE_PC_FETCH_AND_GOTO_NEXT 2

NterpGetObjectInstanceField:
    // Fast-path which gets the field from thread-local cache.
%   fetch_from_thread_cache("%eax", miss_label="2f")
1:
    movl    rINST, %ecx                     # ecx <- BA
    sarl    $$4, %ecx                       # ecx <- B
    GET_VREG %ecx, %ecx                     # vB (object we're operating on)
    testl   %ecx, %ecx                      # is object null?
    je      common_errNullObject
    testb $$READ_BARRIER_TEST_VALUE, GRAY_BYTE_OFFSET(%ecx)
    movl (%ecx,%eax,1), %eax
    jnz 3f
    UNPOISON_HEAP_REF eax  // Affects flags, so we cannot unpoison before the jnz.
4:
    andb    $$0xf,rINSTbl                   # rINST <- A
    SET_VREG_OBJECT %eax, rINST             # fp[A] <- value
    ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
2:
%   fetch_field_from_fast_path("NterpGetLocalInstanceField", success_label="1b", fail_label="5f")
5:
    EXPORT_PC
    movl rSELF:THREAD_SELF_OFFSET, ARG0
    movl 0(%esp), ARG1
    movl rPC, ARG2
    movl rFP, ARG3
    call nterp_get_instance_field_offset
    testl %eax, %eax
    jns 1b
    // For volatile fields, we return a negative offset. Remove the sign
    // and no need for any barrier thanks to the memory model.
    negl %eax
    jmp 1b
3:
    UNPOISON_HEAP_REF eax
    // reg00 is eax
    call art_quick_read_barrier_mark_reg00
    jmp 4b

NterpPutObjectStaticField:
    // Fast-path which gets the field from thread-local cache.
%   fetch_uint64_from_thread_cache_eax_ecx(miss_label="2f")
1:
    GET_VREG rINST, rINST
    cmpl $$0, rSELF:THREAD_READ_BARRIER_MARK_REG00_OFFSET
    jne 3f
5:
    POISON_HEAP_REF ebx  // `rINST` is `%ebx` but we need to pass `ebx`.
    movl rINST, (%eax, %ecx, 1)
    testl rINST, rINST
    je 4f
    movl rSELF:THREAD_CARD_TABLE_OFFSET, %eax
    shrl $$CARD_TABLE_CARD_SHIFT, %ecx
    movb %al, (%ecx, %eax, 1)
4:
    ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
2:
%   fetch_field_from_fast_path("NterpGetLocalStaticFieldForSPutObject", success_label="1b", fail_label="9f", is_static=True)
9:
    EXPORT_PC
    movl rSELF:THREAD_SELF_OFFSET, ARG0
    movl 0(%esp), ARG1
    movl rPC, ARG2
    GET_VREG ARG3, rINST
    call nterp_get_static_field
    testl %eax, %eax
    jns 1b
    negl %eax
    cmpl $$0, rSELF:THREAD_READ_BARRIER_MARK_REG00_OFFSET
    jne 7f
6:
    GET_VREG rINST, rINST
    POISON_HEAP_REF ebx  // `rINST` is `%ebx` but we need to pass `ebx`.
    movl rINST, (%eax, %ecx, 1)
    testl rINST, rINST
    je 8f
    movl rSELF:THREAD_CARD_TABLE_OFFSET, %eax
    shrl $$CARD_TABLE_CARD_SHIFT, %ecx
    movb %al, (%ecx, %eax, 1)
8:
    lock addl $$0, (%esp)
    ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
3:
    call art_quick_read_barrier_mark_reg01
    jmp 5b
7:
    call art_quick_read_barrier_mark_reg01
    jmp 6b

NterpGetObjectStaticField:
    // Fast-path which gets the field from thread-local cache.
%   fetch_uint64_from_thread_cache_eax_ecx(miss_label="2f")
1:
    cmpl $$0, rSELF:THREAD_READ_BARRIER_MARK_REG00_OFFSET
    jne 5f
6:
    testb $$READ_BARRIER_TEST_VALUE, GRAY_BYTE_OFFSET(%ecx)
    movl (%ecx, %eax, 1), %eax
    jnz 3f
    UNPOISON_HEAP_REF eax  // Affects flags, so we cannot unpoison before the jnz.
4:
    SET_VREG_OBJECT %eax, rINST             # fp[A] <- value
    ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
2:
%   fetch_field_from_fast_path("NterpGetLocalStaticField", success_label="1b", fail_label="9f", is_static=True)
9:
    EXPORT_PC
    movl rSELF:THREAD_SELF_OFFSET, ARG0
    movl 0(%esp), ARG1
    movl rPC, ARG2
    movl $$0, ARG3
    call nterp_get_static_field
    testl %eax, %eax
    jns 1b
    negl %eax
    jmp 1b
3:
    UNPOISON_HEAP_REF eax
    call art_quick_read_barrier_mark_reg00
    jmp 4b
5:
    call art_quick_read_barrier_mark_reg01
    jmp 6b

NterpGetBooleanStaticField:
    OP_SGET load="movzbl", wide=0

NterpGetByteStaticField:
    OP_SGET load="movsbl", wide=0

NterpGetCharStaticField:
    OP_SGET load="movzwl", wide=0

NterpGetShortStaticField:
    OP_SGET load="movswl", wide=0

NterpGetWideStaticField:
    OP_SGET load="movl", wide=1

NterpGetIntStaticField:
    OP_SGET load="movl", wide=0

NterpPutStaticField:
    OP_SPUT rINST_reg=rINST, store="movl", wide=0

NterpPutBooleanStaticField:
NterpPutByteStaticField:
    OP_SPUT rINST_reg=rINSTbl, store="movb", wide=0

NterpPutCharStaticField:
NterpPutShortStaticField:
    OP_SPUT rINST_reg=rINSTw, store="movw", wide=0

NterpPutWideStaticField:
    OP_SPUT rINST_reg=rINST, store="movl", wide=1

NterpPutInstanceField:
    OP_IPUT rINST_reg=rINST, store="movl", wide=0

NterpPutBooleanInstanceField:
NterpPutByteInstanceField:
    OP_IPUT rINST_reg=rINSTbl, store="movb", wide=0

NterpPutCharInstanceField:
NterpPutShortInstanceField:
    OP_IPUT rINST_reg=rINSTw, store="movw", wide=0

NterpPutWideInstanceField:
    OP_IPUT rINST_reg=rINST, store="movl", wide=1

NterpGetBooleanInstanceField:
    OP_IGET load="movzbl", wide=0

NterpGetByteInstanceField:
    OP_IGET load="movsbl", wide=0

NterpGetCharInstanceField:
    OP_IGET load="movzwl", wide=0

NterpGetShortInstanceField:
    OP_IGET load="movswl", wide=0

NterpGetWideInstanceField:
    OP_IGET load="movl", wide=1

NterpGetInstanceField:
    OP_IGET load="movl", wide=0

NterpCallSuspendAndGotoNext:
    EXPORT_PC
    // Save branch offset.
    movl rINST, LOCAL0(%esp)
    call SYMBOL(art_quick_test_suspend)
    RESTORE_IBASE
    movl LOCAL0(%esp), rINST
    FETCH_INST
    GOTO_NEXT

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

    // Remember CFA.
    movl %esp, %ebp
    CFI_DEF_CFA_REGISTER(ebp)

    subl %ecx, %esp
    movl %esp, %edi               // edi := beginning of stack
    leal OSR_DATA_MEMORY(%eax), %esi  // esi := memory to copy
    rep movsb                     // while (ecx--) { *edi++ = *esi++ }

    // Fetch the native PC to jump to and save it in stack.
    pushl OSR_DATA_NATIVE_PC(%eax)
    CFI_ADJUST_CFA_OFFSET(4)

    subl MACRO_LITERAL(8), %esp
    CFI_ADJUST_CFA_OFFSET(8)
    pushl %eax
    CFI_ADJUST_CFA_OFFSET(4)
    // Free the memory holding OSR Data.
    call SYMBOL(NterpFree)
    addl MACRO_LITERAL(12), %esp
    CFI_ADJUST_CFA_OFFSET(-12)

    // Jump to the compiled code.
    ret

NterpHandleInvokeInterfaceOnObjectMethodRange:
    shrl $$16, %eax
    movl MIRROR_CLASS_VTABLE_OFFSET_32(%edx, %eax, 4), %eax
    jmp NterpCommonInvokeInstanceRange

NterpHandleInvokeInterfaceOnObjectMethod:
    shrl $$16, %eax
    movl MIRROR_CLASS_VTABLE_OFFSET_32(%edx, %eax, 4), %eax
    jmp NterpCommonInvokeInstance

    // This is the logical end of ExecuteNterpImpl, where the frame info applies.
    // EndExecuteNterpImpl includes the methods below as we want the runtime to
    // see them as part of the Nterp PCs.
    .cfi_endproc

  END nterp_helper

  // This is the end of PCs contained by the OatQuickMethodHeader created for the interpreter
  // entry point.
  FUNCTION_TYPE(EndExecuteNterpImpl)
  ASM_HIDDEN SYMBOL(EndExecuteNterpImpl)
  .global SYMBOL(EndExecuteNterpImpl)
  SYMBOL(EndExecuteNterpImpl):

  // Entrypoints into runtime.
  NTERP_TRAMPOLINE_RETURN_LONG nterp_get_static_field, NterpGetStaticField
  NTERP_TRAMPOLINE nterp_get_instance_field_offset, NterpGetInstanceFieldOffset
  NTERP_TRAMPOLINE nterp_filled_new_array, NterpFilledNewArray
  NTERP_TRAMPOLINE nterp_filled_new_array_range, NterpFilledNewArrayRange
  NTERP_TRAMPOLINE nterp_get_class, NterpGetClass
  NTERP_TRAMPOLINE nterp_allocate_object, NterpAllocateObject
  NTERP_TRAMPOLINE nterp_get_method, NterpGetMethod
  NTERP_TRAMPOLINE nterp_hot_method, NterpHotMethod
  NTERP_TRAMPOLINE nterp_load_object, NterpLoadObject

  DEFINE_FUNCTION nterp_deliver_pending_exception
      DELIVER_PENDING_EXCEPTION
  END_FUNCTION nterp_deliver_pending_exception

// gen_mterp.py will inline the following definitions
// within [ExecuteNterpImpl, EndExecuteNterpImpl).
%def instruction_end():

    FUNCTION_TYPE(artNterpAsmInstructionEnd)
    ASM_HIDDEN SYMBOL(artNterpAsmInstructionEnd)
    .global SYMBOL(artNterpAsmInstructionEnd)
SYMBOL(artNterpAsmInstructionEnd):
    // artNterpAsmInstructionEnd is used as landing pad for exception handling.
    RESTORE_IBASE
    FETCH_INST
    GOTO_NEXT

%def instruction_start():

    FUNCTION_TYPE(artNterpAsmInstructionStart)
    ASM_HIDDEN SYMBOL(artNterpAsmInstructionStart)
    .global SYMBOL(artNterpAsmInstructionStart)
    SYMBOL(artNterpAsmInstructionStart) = ${opcode_name_prefix}op_nop
    .text

%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
C=91 H=98 G=94

¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.65Angebot  (Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-06-29) ¤

*Eine klare Vorstellung vom Zielzustand






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

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.