products/Sources/formale Sprachen/C/LibreOffice/wizards/source/access2base/   (Android Betriebssystem Version 17©)  Datei vom 5.10.2025 mit Größe 12 kB image not shown  

Quellcode-Bibliothek main.S   Sprache: unbekannt

 
/*
 * Copyright (C) 2020 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/arm/asm_support_arm.S"

/**
 * ARM EABI general notes:
 *
 * r0-r3 hold first 4 args to a method; they are not preserved across method calls
 * r4-r8 are available for general use
 * r9 is given special treatment in some situations, but not for us
 * r10 (sl) seems to be generally available
 * r11 (fp) is used by gcc (unless -fomit-frame-pointer is set)
 * r12 (ip) is scratch -- not preserved across method calls
 * r13 (sp) should be managed carefully in case a signal arrives
 * r14 (lr) must be preserved
 * r15 (pc) can be tinkered with directly
 *
 * r0 holds returns of <= 4 bytes
 * r0-r1 hold returns of 8 bytes, low word in r0
 *
 * Callee must save/restore r4+ (except r12) if it modifies them.  If VFP
 * is present, registers s16-s31 (a/k/a d8-d15, a/k/a q4-q7) must be preserved,
 * s0-s15 (d0-d7, q0-a3) do not need to be.
 *
 * Stack is "full descending".  Only the arguments that don't fit in the first 4
 * registers are placed on the stack.  "sp" points at the first stacked argument
 * (i.e. the 5th arg).
 *
 * Native ABI uses soft-float, single-precision results are in r0,
 * double-precision results in r0-r1.
 *
 * In the EABI, "sp" must be 64-bit aligned on entry to a function, and any
 * 64-bit quantities (long long, double) must be 64-bit aligned.
 *
 * Nterp notes:
 *
 * The following registers have fixed assignments:
 *
 *   reg nick      purpose
 *   r5  rFP       interpreted frame pointer, used for accessing locals and args
 *   r6  rREFS     base of object references of dex registers
 *   r7  rINST     first 16-bit code unit of current instruction
 *   r8  rMR       marking register
 *   r9  rSELF     self (Thread) pointer
 *   r10 rIBASE    interpreted instruction handler pointer, used for computed goto;
 *                 we initialize this register roughly (except the replacable bits) with
 *                 the 32-KiB aligned address of one of the `nterp*_op_nop` symbols and
 *                 then keep inserting opcodes to bits 14:7 to form the handler address.
 *   r11 rPC       interpreted program counter, used for fetching instructions
 *
 *   r4, ip, and lr can be used as temporary
 *
 * Note that r4 is a callee-save register in ARM EABI, but not in managed code.
 *
 */


/* 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)

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

.macro FETCH_INST
    ldrh    rINST, [rPC]
.endm

/*
 * Fetch the next instruction from the specified offset.  Advances rPC
 * to point to the next instruction.  "count" is in 16-bit code units.
 *
 * Because of the limited size of immediate constants on ARM, this is only
 * suitable for small forward movements (i.e. don't try to implement "goto"
 * with this).
 *
 * This must come AFTER anything that can throw an exception, or the
 * exception catch may miss.  (This also implies that it must come after
 * EXPORT_PC.)
 */

.macro FETCH_ADVANCE_INST count
    ldrh    rINST, [rPC, #((\count)*2)]!
.endm

/*
 * Similar to FETCH_ADVANCE_INST, but does not update xPC.  Used to load
 * rINST ahead of possible exception point.  Be sure to manually advance xPC
 * later.
 */

.macro PREFETCH_INST count
    ldrh    rINST, [rPC, #((\count)*2)]
.endm

/* Advance xPC by some number of code units. */
.macro ADVANCE count
  add  rPC, #((\count)*2)
.endm

/*
 * Fetch the next instruction from an offset specified by "reg" and advance xPC.
 * xPC to point to the next instruction.  "reg" must specify the distance
 * in bytes, *not* 16-bit code units, and may be a signed value.
 */

.macro FETCH_ADVANCE_INST_RB reg
    ldrh    rINST, [rPC, \reg]!
.endm

/*
 * Fetch a half-word code unit from an offset past the current PC.  The
 * "count" value is in 16-bit code units.  Does not advance xPC.
 *
 * The "_S" variant works the same but treats the value as signed.
 */

.macro FETCH reg, count
    ldrh    \reg, [rPC, #((\count)*2)]
.endm

.macro FETCH_S reg, count
    ldrsh   \reg, [rPC, #((\count)*2)]
.endm

/*
 * Fetch one byte from an offset past the current PC.  Pass in the same
 * "count" as you would for FETCH, and an additional 0/1 indicating which
 * byte of the halfword you want (lo/hi).
 */

.macro FETCH_B reg, count, byte
    ldrb     \reg, [rPC, #((\count)*2+(\byte))]
.endm

/*
 * Insert the instruction's opcode field into the rIBASE register.
 */

.macro PREPARE_OPCODE_DISPATCH
    bfi     rIBASE, rINST, #NTERP_HANDLER_SIZE_LOG2, #8
.endm

/*
 * Begin executing the opcode.
 */

.macro GOTO_OPCODE
    bx      rIBASE
.endm

/*
 * Get/set value from a Dalvik register.
 */

.macro GET_VREG reg, vreg
    ldr     \reg, [rFP, \vreg, lsl #2]
.endm
.macro GET_VREG_OBJECT reg, vreg
    ldr     \reg, [rREFS, \vreg, lsl #2]
.endm
.macro SET_VREG reg, vreg
    str     \reg, [rFP, \vreg, lsl #2]
    mov     \reg, #0
    str     \reg, [rREFS, \vreg, lsl #2]
.endm
.macro PRESCALE_VREG scaled_vreg, vreg, s=""
    lsl\s   \scaled_vreg, \vreg, #2
.endm
.macro SET_VREG_PRESCALED reg, scaled_vreg, s=""
    str     \reg, [rFP, \scaled_vreg]
    mov\s   \reg, #0
    str     \reg, [rREFS, \scaled_vreg]
.endm
.macro SET_VREG_OBJECT reg, vreg
    str     \reg, [rFP, \vreg, lsl #2]
    str     \reg, [rREFS, \vreg, lsl #2]
.endm
.macro SET_VREG_OBJECT_PRESCALED reg, scaled_vreg
    str     \reg, [rFP, \scaled_vreg]
    str     \reg, [rREFS, \scaled_vreg]
.endm
.macro SET_VREG_FLOAT reg, vreg, tmpreg
    add     \tmpreg, rFP, \vreg, lsl #2
    vstr    \reg, [\tmpreg]
    mov     \tmpreg, #0
    str     \tmpreg, [rREFS, \vreg, lsl #2]
.endm
.macro GET_VREG_WIDE_BY_ADDR reg0, reg1, addr
    ldmia \addr, {\reg0, \reg1}
.endm
.macro SET_VREG_WIDE_BY_ADDR reg0, reg1, addr, wb=""
    stmia \addr\wb, {\reg0, \reg1}
.endm
.macro GET_VREG_FP_BY_ADDR fpreg, addr
    vldr \fpreg, [\addr]
.endm
.macro SET_VREG_FP_BY_ADDR fpreg, addr
    vstr \fpreg, [\addr]
.endm
.macro SET_VREG_SHADOW reg, vreg
    str     \reg, [rREFS, \vreg, lsl #2]
.endm
.macro CLEAR_SHADOW_PAIR vreg, tmp1, tmp2
    add     \tmp2, rREFS, \vreg, lsl #2
    mov     \tmp1, #0
    strd    \tmp1, \tmp1, [\tmp2]
.endm
.macro CLEAR_SHADOW_PAIR_PRESCALED scaled_vreg, tmp1, tmp2, s=""
    add\s   \tmp2, rREFS, \scaled_vreg
    mov\s   \tmp1, #0
    strd    \tmp1, \tmp1, [\tmp2]
.endm
.macro CLEAR_SHADOW_PAIR_PRESCALED_RZERO scaled_vreg, rzero, tmp, s=""
    add\s   \tmp, rREFS, \scaled_vreg
    strd    \rzero, \rzero, [\tmp]
.endm
.macro VREG_INDEX_TO_ADDR reg, vreg
    add     \reg, rFP, \vreg, lsl #2
.endm
.macro VREG_INDEX_TO_ADDR_PRESCALED reg, scaled_vreg, s=""
    add\s   \reg, rFP, \scaled_vreg
.endm

// 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 CLEAR_VOLATILE_MARKER reg
  rsb \reg, \reg, #0
.endm

.macro EXPORT_PC
    str    rPC, [rREFS, #-8]
.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

.macro TEST_IF_MARKING label
    cmp rMR, #0
    bne \label
.endm
.macro TEST_IF_MARKING_RZERO rzero, label
    cmp rMR, \rzero
    bne \label
.endm
.macro TEST_IF_MARKING_CBZ rmr, label
    cbnz \rmr, \label
.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

    // Set reference and dex registers.
    add \refs, lr, \refs, lsl #2
    add \refs, \refs, #12
    add \fp, \refs, rINST, lsl #2

    // Now setup the stack pointer.
    mov ip, sp
    .cfi_def_cfa_register ip
    mov sp, lr
    str ip, [\refs, #-4]
    CFI_DEF_CFA_BREG_PLUS_UCONST \cfi_refs, -4, CALLEE_SAVES_SIZE

    // Save the ArtMethod, and use r0 as a temporary.
    str r0, [sp]

    // Put nulls in reference frame.
    cmp rINST, #0
    beq 2f
    mov lr, \refs
    mov r0, #0
1:
    str r0, [lr], #4
    str r0, [lr], #4  // May clear vreg[0].
    cmp lr, \fp
    blo 1b
2:
    ldr r0, [sp]  // Reload the ArtMethod, expected by the callers.
.endm

// Increase method hotness and do suspend check before starting executing the method.
.macro START_EXECUTING_INSTRUCTIONS
    ldr r0, [sp]
    ldrh r2, [r0, #ART_METHOD_HOTNESS_COUNT_OFFSET]
    cmp r2, #NTERP_HOTNESS_VALUE
    beq 3f
    add r2, r2, #-1
    strh r2, [r0, #ART_METHOD_HOTNESS_COUNT_OFFSET]
1:
    DO_SUSPEND_CHECK continue_label=2f
2:
    FETCH_INST
    PREPARE_OPCODE_DISPATCH
    GOTO_OPCODE
3:
    CHECK_AND_UPDATE_SHARED_MEMORY_METHOD if_hot=4f, if_not_hot=1b
4:
    mov r1, #0
    mov r2, rFP
    bl nterp_hot_method
    b 2b
.endm

.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, 01
   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

   CFI_DEFINE_DEX_PC_WITH_OFFSET(CFI_TMP, CFI_DEX, 0)
.endm

// Setup arguments based on a non-range nterp to nterp call, and start executing
// the method. We expect:
// - rNEW_FP: the new pointer to dex registers
// - 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

.macro GET_SHORTY dest, is_interface, is_polymorphic, is_custom
   push {r0-r3}
   .if \is_polymorphic
     ldr r0, [sp, #16]
     mov r1, rPC
     bl NterpGetShortyFromInvokePolymorphic
   .elseif \is_custom
     ldr r0, [sp, #16]
     mov r1, rPC
     bl NterpGetShortyFromInvokeCustom
   .elseif \is_interface
     ldr r0, [sp, #16]
     FETCH r1, 1
     bl NterpGetShortyFromMethodId
   .else
     bl NterpGetShorty
   .endif
   mov \dest, r0
   pop {r0-r3}
.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, 01
    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, 01
    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, 01
    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, 01
    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, 01
    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, 01
    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

.Lcall_compiled_code_\suffix:
   .if \is_polymorphic
     // No fast path for polymorphic calls.
%    pass
   .elseif \is_custom
     // No fast path for custom calls.
%    pass
   .elseif \is_string_init
     // No fast path for string.init.
%    pass
   .else
     ldr ip, [r0, #ART_METHOD_ACCESS_FLAGS_OFFSET]
     tst ip, #ART_METHOD_NTERP_INVOKE_FAST_PATH_FLAG
     beq .Lfast_path_with_few_args_\suffix
     FETCH_B rINST, 01
     .if \is_static
       asrs lr, rINST, #4
       beq .Linvoke_fast_path_\suffix
     .else
       asr lr, rINST, #4
       cmp lr, #1
       beq .Linvoke_fast_path_\suffix
     .endif
     FETCH ip, 2
     cmp lr, #2
     .if \is_static
       blt .Lone_arg_fast_path_\suffix
     .endif
     beq .Ltwo_args_fast_path_\suffix
     cmp lr, #4
     blt .Lthree_args_fast_path_\suffix
     beq .Lfour_args_fast_path_\suffix
     and         rINST, rINST, #15
     GET_VREG    rINST, rINST
     str         rINST, [sp, #(4 + 4 * 4)]
.Lfour_args_fast_path_\suffix:
     asr         rINST, ip, #12
     GET_VREG    rINST, rINST
     str         rINST, [sp, #(4 + 3 * 4)]
.Lthree_args_fast_path_\suffix:
     ubfx        rINST, ip, #8, #4
     GET_VREG    r3, rINST
.Ltwo_args_fast_path_\suffix:
     ubfx        rINST, ip, #4, #4
     GET_VREG    r2, rINST
.Lone_arg_fast_path_\suffix:
     .if \is_static
       and         rINST, ip, #0xf
       GET_VREG    r1, rINST
     .else
       // First argument already in r1.
%      pass
     .endif
.Linvoke_fast_path_\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

.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, 01
     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

   .if \is_polymorphic
     FETCH_ADVANCE_INST 4
   .else
     FETCH_ADVANCE_INST 3
   .endif
   PREPARE_OPCODE_DISPATCH
   GOTO_OPCODE
.endm

// Puts the next int/long/object argument in the expected register,
// fetching values based on a range invoke.
// Uses ip as temporary.
.macro LOOP_RANGE_OVER_SHORTY_LOADING_GPRS reg32, shorty, arg_index, stack_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
    GET_VREG \reg32, \arg_index
    add \arg_index, \arg_index, #1
    add \stack_index, \stack_index, #1
    b 5f
2:  // FOUND_LONG
    .if \is_r3
      // Put back shorty and jump to \if_long
      sub \shorty, \shorty, #1
    .else
      GET_VREG r2, \arg_index
      add \arg_index, \arg_index, #1
      add \stack_index, \stack_index, #1
      GET_VREG r3, \arg_index
      add \arg_index, \arg_index, #1
      add \stack_index, \stack_index, #1
    .endif
    b \if_long
3:  // SKIP_FLOAT
    add \arg_index, \arg_index, #1
    add \stack_index, \stack_index, #1
    b 1b
4:  // SKIP_DOUBLE
    add \arg_index, \arg_index, #2
    add \stack_index, \stack_index, #2
    b 1b
5:
%   pass
.endm

// Puts the next int/long/object argument in the expected stack slot,
// fetching values based on a range invoke.
// Uses ip as temporary.
.macro LOOP_RANGE_OVER_INTs shorty, arg_index, stack_index, finished
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
    GET_VREG ip, \arg_index
    str ip, [sp, \stack_index, lsl #2]
    add \arg_index, \arg_index, #1
    add \stack_index, \stack_index, #1
    b 1b
2:  // FOUND_LONG
    GET_VREG ip, \arg_index
    str ip, [sp, \stack_index, lsl #2]
    add \arg_index, \arg_index, #1
    add \stack_index, \stack_index, #1
    GET_VREG ip, \arg_index
    str ip, [sp, \stack_index, lsl #2]
    add \arg_index, \arg_index, #1
    add \stack_index, \stack_index, #1
    b 1b
3:  // SKIP_FLOAT
    add \arg_index, \arg_index, #1
    add \stack_index, \stack_index, #1
    b 1b
4:  // SKIP_DOUBLE
    add \arg_index, \arg_index, #2
    add \stack_index, \stack_index, #2
    b 1b
.endm

.macro COMMON_INVOKE_RANGE is_static=0, is_interface=0, suffix="", is_string_init=0, is_polymorphic=0, is_custom=0
   .if \is_polymorphic
     // We always go to compiled code for polymorphic calls.
%    pass
   .elseif \is_custom
     // We always go to compiled code for custom calls.
%    pass
   .else
     DO_ENTRY_POINT_CHECK .Lcall_compiled_code_range_\suffix, range_\suffix
     GET_CODE_ITEM
     .if \is_string_init
       bl nterp_to_nterp_string_init_range
     .elseif \is_static
       bl nterp_to_nterp_static_range
     .else
       bl nterp_to_nterp_instance_range
     .endif
     b .Ldone_return_range_\suffix
.Lfetch_nterp_range_\suffix:
     .word   (ExecuteNterpImpl + /* thumb mode */ 1) - (.Lfetch_location_range_\suffix + 4)
   .endif

.Lcall_compiled_code_range_\suffix:
   .if \is_polymorphic
     // No fast path for polymorphic calls.
%    pass
   .elseif \is_custom
     // No fast path for custom calls.
%    pass
   .elseif \is_string_init
     // No fast path for string.init.
%    pass
   .else
     ldr ip, [r0, #ART_METHOD_ACCESS_FLAGS_OFFSET]
     tst ip, #ART_METHOD_NTERP_INVOKE_FAST_PATH_FLAG
     beq .Lfast_path_with_few_args_range_\suffix
     FETCH_B ip, 01  // Number of arguments
     .if \is_static
       cmp ip, #0
     .else
       cmp ip, #1
     .endif
     beq .Linvoke_fast_path_range_\suffix
     FETCH lr, 2  // dex register of first argument
     add lr, rFP, lr, lsl #2  // location of first dex register value.
     .if \is_static
       cmp ip, #2
       blt .Lone_arg_fast_path_range_\suffix
       beq .Ltwo_args_fast_path_range_\suffix
       cmp ip, #3
     .else
       cmp ip, #3
       blt .Ltwo_args_fast_path_range_\suffix
     .endif
     beq .Lthree_args_fast_path_range_\suffix
     add rINST, sp, #4  // Add space for the ArtMethod

.Lloop_over_fast_path_range_\suffix:
     sub ip, ip, #1
     ldr r3, [lr, ip, lsl #2]
     str r3, [rINST, ip, lsl #2]
     cmp ip, #3
     bne .Lloop_over_fast_path_range_\suffix

.Lthree_args_fast_path_range_\suffix:
     ldr r3, [lr, #8]
.Ltwo_args_fast_path_range_\suffix:
     ldr r2, [lr, #4]
.Lone_arg_fast_path_range_\suffix:
     .if \is_static
       ldr r1, [lr, #0]
     .else
       // First argument already in r1.
%      pass
     .endif
.Linvoke_fast_path_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

.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, 01 // 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 1 word 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

   .if \is_polymorphic
      FETCH_ADVANCE_INST 4
   .else
     FETCH_ADVANCE_INST 3
   .endif
   PREPARE_OPCODE_DISPATCH
   GOTO_OPCODE
.endm

.macro POISON_HEAP_REF_IF_OBJECT is_object, rRef, s=""
   .if \is_object
     POISON_HEAP_REF \rRef, \s
   .endif
.endm

.macro WRITE_BARRIER_IF_OBJECT is_object, value, holder, label, tmp, tmp2="ip", s=""
   .if \is_object
     SMART_CBZ \value, \label
     ldr     \tmp2, [rSELF, #THREAD_CARD_TABLE_OFFSET]
     lsr\s   \tmp, \holder, #CARD_TABLE_CARD_SHIFT
     strb    \tmp2, [\tmp2, \tmp]
\label:
%    pass
   .endif
.endm

.macro LDREXD_STREXD_LOOP addr, load1, load2, store1, store2, tmp, label
\label:
   ldrexd  \load1, \load2, [\addr]
   strexd  \tmp, \store1, \store2, [\addr]
   cmp     \tmp, #0
   bne     \label
.endm

.macro ATOMIC_LOAD64 addr, load1, load2, tmp, label
   LDREXD_STREXD_LOOP \addr, \load1, \load2, \load1, \load2, \tmp, \label
.endm

.macro ATOMIC_STORE64 addr, store1, store2, tmp1, tmp2, label
   LDREXD_STREXD_LOOP \addr, \tmp1, \tmp2, \store1, \store2, \tmp1, \label
.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 ip as temporary.
.macro LOOP_OVER_SHORTY_STORING_GPRS gpr_32, shorty, arg_offset, regs, refs, 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
    str \gpr_32, [\regs, \arg_offset]
    cmp ip, #76                  // if (ip != 'L') goto NOT_REFERENCE
    bne 6f
    str \gpr_32, [\refs, \arg_offset]
6:  // NOT_REFERENCE
    add \arg_offset, \arg_offset, #4
    b 5f
2:  // FOUND_LONG
    .if \is_r3
      // Put back shorty and jump to \if_long
      sub \shorty, \shorty, #1
    .else
      // A long can only be in r2, r3
      str r2, [\regs, \arg_offset]
      add \arg_offset, \arg_offset, #4
      str r3, [\regs, \arg_offset]
      add \arg_offset, \arg_offset, #4
    .endif
    b \if_long
3:  // SKIP_FLOAT
    add \arg_offset, \arg_offset, #4
    b 1b
4:  // SKIP_DOUBLE
    add \arg_offset, \arg_offset, #8
    b 1b
5:
%   pass
.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.
.macro LOOP_OVER_INTs shorty, arg_offset, regs, refs, stack_ptr, tmp1, tmp2, finished
1: // LOOP
    ldrb \tmp1, [\shorty], #1       // Load next character in shorty, and increment.
    cmp \tmp1, #0
    beq \finished                   // if (\tmp1 == '\0') goto finished
    cmp \tmp1, #74                  // if (\tmp1 == 'J') goto FOUND_LONG
    beq 2f
    cmp \tmp1, #70                  // if (\tmp1 == 'F') goto SKIP_FLOAT
    beq 3f
    cmp \tmp1, #68                  // if (\tmp1 == 'D') goto SKIP_DOUBLE
    beq 4f
    add \tmp2, \stack_ptr, \arg_offset
    ldr \tmp2, [\tmp2,  #OFFSET_TO_FIRST_ARGUMENT_IN_STACK]
    str \tmp2, [\regs, \arg_offset]
    cmp \tmp1, #76                  // if (\tmp1 != 'L') goto loop
    bne 3f
    str \tmp2, [\refs, \arg_offset]
    add \arg_offset, \arg_offset, #4
    b 1b
2:  // FOUND_LONG
    add \tmp1, \stack_ptr, \arg_offset
    ldr \tmp1, [\tmp1,  #OFFSET_TO_FIRST_ARGUMENT_IN_STACK]
    str \tmp1, [\regs, \arg_offset]
    add \arg_offset, \arg_offset, #4
    add \tmp1, \stack_ptr, \arg_offset
    ldr \tmp1, [\tmp1,  #OFFSET_TO_FIRST_ARGUMENT_IN_STACK]
    str \tmp1, [\regs, \arg_offset]
    add \arg_offset, \arg_offset, #4
    b 1b
3:  // SKIP_FLOAT
    add \arg_offset, \arg_offset, #4
    b 1b
4:  // SKIP_DOUBLE
    add \arg_offset, \arg_offset, #8
    b 1b
.endm

.macro SETUP_REFERENCE_PARAMETER_IN_GPR gpr32, regs, refs, ins, arg_offset, finished
    str \gpr32, [\regs, \arg_offset]
    subs \ins, \ins, #1
    str \gpr32, [\refs, \arg_offset]
    add \arg_offset, \arg_offset, #4
    beq \finished
.endm

.macro SETUP_REFERENCE_PARAMETERS_IN_STACK regs, refs, ins, stack_ptr, arg_offset
1:
    ldr ip, [\stack_ptr, \arg_offset]
    subs \ins, \ins, #1
    str ip, [\regs, \arg_offset]
    str ip, [\refs, \arg_offset]
    add \arg_offset, \arg_offset, #4
    bne 1b
.endm

.macro DO_SUSPEND_CHECK continue_label
    // Otherwise, do a suspend check.
    ldr ip, [rSELF, #THREAD_FLAGS_OFFSET]
    tst ip, #THREAD_SUSPEND_OR_CHECKPOINT_REQUEST
    beq \continue_label
    EXPORT_PC
    bl    art_quick_test_suspend
.endm

.macro CHECK_AND_UPDATE_SHARED_MEMORY_METHOD if_hot, if_not_hot
    ldr ip, [r0, #ART_METHOD_ACCESS_FLAGS_OFFSET]
    tst ip, #ART_METHOD_IS_MEMORY_SHARED_FLAG
    beq \if_hot
    // Intrinsics are always in the boot image and considered hot.
    tst ip, #ART_METHOD_IS_INTRINSIC_FLAG
    bne \if_hot
    ldr ip, [rSELF, #THREAD_SHARED_METHOD_HOTNESS_OFFSET]
    cmp ip, #0
    beq \if_hot
    add ip, ip, #-1
    str ip, [rSELF, #THREAD_SHARED_METHOD_HOTNESS_OFFSET]
    b \if_not_hot
.endm


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


OAT_ENTRY ExecuteNterpWithClinitImpl
    .cfi_startproc
    // For simplicity, we don't do a read barrier here, but instead rely
    // on art_quick_resolution_trampoline to always have a suspend point before
    // calling back here.
    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

    tst r8, #ART_METHOD_NTERP_ENTRY_POINT_FAST_PATH_FLAG
    beq .Lsetup_slow_path
    // Setup pointer to inputs in FP and pointer to inputs in REFS
    add lr, rFP, rINST
    add r8, rREFS, rINST
    mov r0, #0
    SETUP_REFERENCE_PARAMETER_IN_GPR r1, lr, r8, r4, r0, .Lxmm_setup_finished
    SETUP_REFERENCE_PARAMETER_IN_GPR r2, lr, r8, r4, r0, .Lxmm_setup_finished
    SETUP_REFERENCE_PARAMETER_IN_GPR r3, lr, r8, r4, r0, .Lxmm_setup_finished
    add rIBASE, rIBASE, #OFFSET_TO_FIRST_ARGUMENT_IN_STACK
    SETUP_REFERENCE_PARAMETERS_IN_STACK lr, r8, r4, rIBASE, r0
    b .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.
    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}

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


  .text
  .align  2

  // 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

common_errNullObject:
    EXPORT_PC
    bl art_quick_throw_null_pointer_exception

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

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

NterpCommonInvokeInstance:
    COMMON_INVOKE_NON_RANGE suffix="invokeInstance"

NterpCommonInvokeInstanceRange:
    COMMON_INVOKE_RANGE suffix="invokeInstance"

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

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

NterpCommonInvokePolymorphic:
    COMMON_INVOKE_NON_RANGE is_polymorphic=1, suffix="invokePolymorphic"

NterpCommonInvokePolymorphicRange:
    COMMON_INVOKE_RANGE is_polymorphic=1, suffix="invokePolymorphic"

NterpCommonInvokeCustom:
    COMMON_INVOKE_NON_RANGE is_static=1, is_custom=1, suffix="invokeCustom"

NterpCommonInvokeCustomRange:
    COMMON_INVOKE_RANGE is_static=1, is_custom=1, suffix="invokeCustom"

NterpHandleStringInit:
    COMMON_INVOKE_NON_RANGE is_string_init=1, suffix="stringInit"

NterpHandleStringInitRange:
    COMMON_INVOKE_RANGE is_string_init=1, suffix="stringInit"


NterpHotnessCheck:
    ldr r0, [sp]
    ldrh r2, [r0, #ART_METHOD_HOTNESS_COUNT_OFFSET]
    cmp r2, #NTERP_HOTNESS_VALUE
    beq 3f
    add r2, r2, #-1
    strh r2, [r0, #ART_METHOD_HOTNESS_COUNT_OFFSET]
1:
    DO_SUSPEND_CHECK continue_label=2f
2:
    FETCH_INST                          // load rINST
    PREPARE_OPCODE_DISPATCH             // insert opcode to rIBASE
    GOTO_OPCODE                         // jump to next instruction

3:
    CHECK_AND_UPDATE_SHARED_MEMORY_METHOD if_hot=4f, if_not_hot=1b
4:
    mov r1, rPC
    mov r2, rFP
    bl nterp_hot_method
    cmp r0, #0
    beq 2b

    // 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.

    RESTORE_ALL_CALLEE_SAVES
    push {r5-r7, r10-r11, lr}
    .cfi_adjust_cfa_offset 24
    .cfi_rel_offset r5, 0
    .cfi_rel_offset r6, 4
    .cfi_rel_offset r7, 8
    .cfi_rel_offset r10, 12
    .cfi_rel_offset r11, 16
    .cfi_rel_offset lr, 20
    vpush {s16-s31}
    .cfi_adjust_cfa_offset 64

    // Setup the new frame
    ldr r1, [r0, #OSR_DATA_FRAME_SIZE]
    // Given stack size contains all callee saved registers, remove them.
    sub r1, r1, #(CALLEE_SAVES_SIZE - 12)

    // We know r1 cannot be 0, as it at least contains the ArtMethod.

    // Remember CFA in a callee-save register.
    mov rINST, sp
    .cfi_def_cfa_register rINST

    sub sp, sp, r1

    add r2, r0, #OSR_DATA_MEMORY
5:
    sub r1, r1, #4
    ldr ip, [r2, r1]
    str ip, [sp, r1]
    cmp r1, #0
    bne 5b

    // 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

nterp_to_nterp_static_non_range:
    .cfi_startproc
    SETUP_STACK_FOR_INVOKE
    SETUP_NON_RANGE_ARGUMENTS_AND_EXECUTE is_static=1, is_string_init=0
    .cfi_endproc

nterp_to_nterp_string_init_non_range:
    .cfi_startproc
    SETUP_STACK_FOR_INVOKE
    SETUP_NON_RANGE_ARGUMENTS_AND_EXECUTE is_static=0, is_string_init=1
    .cfi_endproc

nterp_to_nterp_instance_non_range:
    .cfi_startproc
    SETUP_STACK_FOR_INVOKE
    SETUP_NON_RANGE_ARGUMENTS_AND_EXECUTE is_static=0, is_string_init=0
    .cfi_endproc

nterp_to_nterp_static_range:
    .cfi_startproc
    SETUP_STACK_FOR_INVOKE
    SETUP_RANGE_ARGUMENTS_AND_EXECUTE is_static=1, is_string_init=0
    .cfi_endproc

nterp_to_nterp_string_init_range:
    .cfi_startproc
    SETUP_STACK_FOR_INVOKE
    SETUP_RANGE_ARGUMENTS_AND_EXECUTE is_static=0, is_string_init=1
    .cfi_endproc

nterp_to_nterp_instance_range:
    .cfi_startproc
    SETUP_STACK_FOR_INVOKE
    SETUP_RANGE_ARGUMENTS_AND_EXECUTE is_static=0, is_string_init=0
    .cfi_endproc

  NAME_END nterp_helper

  // This is the end of PCs contained by the OatQuickMethodHeader created for the interpreter
  // entry point.
  .type EndExecuteNterpImpl, #function
  .hidden EndExecuteNterpImpl
  .global EndExecuteNterpImpl
EndExecuteNterpImpl:

  /*
   * Convert the double in r0/r1 to a long in r0/r1.
   *
   * We have to clip values to long min/max per the specification.  The
   * expected common case is a "reasonable" value that converts directly
   * to modest integer.  The EABI convert function isn't doing this for us.
   */

  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

  /*
   * Convert the float in r0 to a long in r0/r1.
   *
   * We have to clip values to long min/max per the specification.  The
   * expected common case is a "reasonable" value that converts directly
   * to modest integer.  The EABI convert function isn't doing this for us.
   */

  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

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

  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 instruction_start():
%   prefix_number = int(re.sub(r"nterp([0-7])_", r"\1", opcode_name_prefix))
%   if prefix_number == 0:
      .type artNterpAsmInstructionStart, #function
      .hidden artNterpAsmInstructionStart
      .global artNterpAsmInstructionStart
      artNterpAsmInstructionStart = nterp0_op_nop
      .text
      .balign 4096
%   else:
%     prev_op_nop = "nterp{}_op_nop".format(int(prefix_number) - 1)
      .org ${prev_op_nop} + 20480

%def add_slow_path_for_last_prefix(write_fn, *write_args, suffix="_slow_path"):
%   if opcode_name_prefix != "nterp3_":
%     return re.sub(r"nterp._", r"nterp3_", opcode) + suffix
%   else:
%     return add_slow_path(write_fn=write_fn, *write_args, suffix=suffix)

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

[Verzeichnis aufwärts0.99unsichere VerbindungÜbersetzung europäischer Sprachen durch Browser2026-06-29]