| %def header(): |
| /* |
| * Copyright (C) 2019 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_64/asm_support_x86_64.S" |
| |
| /** |
| * x86_64 ABI general notes: |
| * |
| * Caller save set: |
| * rax, rdx, rcx, rsi, rdi, r8-r11, st(0)-st(7) |
| * Callee save set: |
| * rbx, rbp, r12-r15 |
| * Return regs: |
| * 32-bit in eax |
| * 64-bit in rax |
| * fp on xmm0 |
| * |
| * First 8 fp parameters came in xmm0-xmm7. |
| * First 6 non-fp parameters came in rdi, rsi, rdx, rcx, r8, r9. |
| * Other parameters passed on stack, pushed right-to-left. On entry to target, first |
| * param is at 8(%esp). |
| * |
| * Stack must be 16-byte aligned to support SSE in native code. |
| */ |
| |
| #define IN_ARG3 %rcx |
| #define IN_ARG2 %rdx |
| #define IN_ARG1 %rsi |
| #define IN_ARG0 %rdi |
| /* Out Args */ |
| #define OUT_ARG3 %rcx |
| #define OUT_ARG2 %rdx |
| #define OUT_ARG1 %rsi |
| #define OUT_ARG0 %rdi |
| #define OUT_32_ARG3 %ecx |
| #define OUT_32_ARG2 %edx |
| #define OUT_32_ARG1 %esi |
| #define OUT_32_ARG0 %edi |
| #define OUT_FP_ARG1 %xmm1 |
| #define OUT_FP_ARG0 %xmm0 |
| |
| /* |
| * single-purpose registers, given names for clarity |
| */ |
| #define rSELF %gs |
| #define rPC %r12 |
| #define CFI_DEX 12 // DWARF register number of the register holding dex-pc (rPC). |
| #define CFI_TMP 5 // DWARF register number of the first argument register (rdi). |
| #define rFP %r13 |
| #define rINST %ebx |
| #define rINSTq %rbx |
| #define rINSTw %bx |
| #define rINSTbh %bh |
| #define rINSTbl %bl |
| #define rIBASE %r14 |
| #define rREFS %r15 |
| #define rREFS32 %r15d |
| #define CFI_REFS 15 // DWARF register number of the reference array (r15). |
| |
| // Temporary registers while setting up a frame. |
| #define rNEW_FP %r8 |
| #define rNEW_REFS %r9 |
| #define rNEW_REFS32 %r9d |
| #define CFI_NEW_REFS 9 |
| |
| /* |
| * 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) |
| |
| // Includes the return address implictly pushed on stack by 'call'. |
| #define CALLEE_SAVES_SIZE (6 * 8 + 4 * 8 + 1 * 8) |
| |
| // +8 for the ArtMethod of the caller. |
| #define OFFSET_TO_FIRST_ARGUMENT_IN_STACK (CALLEE_SAVES_SIZE + 8) |
| |
| /* |
| * 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 |
| movzwq (rPC), rINSTq |
| .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 |
| addq rIBASE, %rcx |
| jmp *%rcx |
| .endm |
| |
| /* |
| * Advance rPC by instruction count. |
| */ |
| .macro ADVANCE_PC _count |
| leaq 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 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. */ |
| .macro GET_WIDE_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. reg is clobbered. */ |
| .macro SET_WIDE_VREG _reg _vreg |
| movq \_reg, VREG_ADDRESS(\_vreg) |
| xorq \_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 |
| |
| // An assembly entry for nterp. |
| .macro OAT_ENTRY name |
| FUNCTION_TYPE(\name) |
| ASM_HIDDEN SYMBOL(\name) |
| .global SYMBOL(\name) |
| .balign 16 |
| SYMBOL(\name): |
| .endm |
| |
| .macro ENTRY name |
| .text |
| ASM_HIDDEN SYMBOL(\name) |
| .global SYMBOL(\name) |
| FUNCTION_TYPE(\name) |
| SYMBOL(\name): |
| .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 |
| SETUP_SAVE_REFS_ONLY_FRAME |
| call \helper |
| RESTORE_SAVE_REFS_ONLY_FRAME |
| cmpq LITERAL(0), %gs:THREAD_EXCEPTION_OFFSET |
| jne nterp_deliver_pending_exception |
| ret |
| END_FUNCTION \name |
| .endm |
| |
| .macro CLEAR_VOLATILE_MARKER reg |
| andq MACRO_LITERAL(-2), \reg |
| .endm |
| |
| .macro EXPORT_PC |
| movq rPC, -16(rREFS) |
| .endm |
| |
| |
| .macro BRANCH |
| leaq (rPC, rINSTq, 2), rPC |
| // Update method counter and do a suspend check if the branch is negative or zero. |
| testq rINSTq, rINSTq |
| jle 3f |
| 2: // We use 2 and not 1 for this local label as the users of the BRANCH macro have a 1 label. |
| FETCH_INST |
| GOTO_NEXT |
| 3: |
| movq (%rsp), %rdi |
| movzwl ART_METHOD_HOTNESS_COUNT_OFFSET(%rdi), %esi |
| #if (NTERP_HOTNESS_VALUE != 0) |
| #error Expected 0 for hotness value |
| #endif |
| // If the counter is at zero, handle this in the runtime. |
| testw %si, %si |
| je NterpHandleHotnessOverflow |
| // Update counter. |
| addl $$-1, %esi |
| movw %si, ART_METHOD_HOTNESS_COUNT_OFFSET(%rdi) |
| DO_SUSPEND_CHECK continue_label=2b |
| jmp 2b |
| .endm |
| |
| // Expects: |
| // - r10, and r11 to be available. |
| // Outputs: |
| // - \registers contains the dex registers size |
| // - \outs contains the outs size |
| // - if load_ins is 1, \ins contains the ins |
| // - \code_item is replace with a pointer to the instructions |
| .macro FETCH_CODE_ITEM_INFO code_item, registers, outs, ins, load_ins |
| testq MACRO_LITERAL(1), \code_item |
| je 5f |
| andq $$-2, \code_item // Remove the extra bit that marks it's a compact dex file. |
| movzwl COMPACT_CODE_ITEM_FIELDS_OFFSET(\code_item), %r10d |
| movl %r10d, \registers |
| sarl $$COMPACT_CODE_ITEM_REGISTERS_SIZE_SHIFT, \registers |
| andl $$0xf, \registers |
| movl %r10d, \outs |
| sarl $$COMPACT_CODE_ITEM_OUTS_SIZE_SHIFT, \outs |
| andl $$0xf, \outs |
| .if \load_ins |
| movl %r10d, \ins |
| sarl $$COMPACT_CODE_ITEM_INS_SIZE_SHIFT, \ins |
| andl $$0xf, \ins |
| .else |
| movl %r10d, %r11d |
| sarl $$COMPACT_CODE_ITEM_INS_SIZE_SHIFT, %r11d |
| andl $$0xf, %r11d |
| addl %r11d, \registers |
| .endif |
| testw $$COMPACT_CODE_ITEM_REGISTERS_INS_OUTS_FLAGS, COMPACT_CODE_ITEM_FLAGS_OFFSET(\code_item) |
| je 4f |
| movq \code_item, %r11 |
| testw $$COMPACT_CODE_ITEM_INSNS_FLAG, COMPACT_CODE_ITEM_FLAGS_OFFSET(\code_item) |
| je 1f |
| subq $$4, %r11 |
| 1: |
| testw $$COMPACT_CODE_ITEM_REGISTERS_FLAG, COMPACT_CODE_ITEM_FLAGS_OFFSET(\code_item) |
| je 2f |
| subq $$2, %r11 |
| movzwl (%r11), %r10d |
| addl %r10d, \registers |
| 2: |
| testw $$COMPACT_CODE_ITEM_INS_FLAG, COMPACT_CODE_ITEM_FLAGS_OFFSET(\code_item) |
| je 3f |
| subq $$2, %r11 |
| movzwl (%r11), %r10d |
| .if \load_ins |
| addl %r10d, \ins |
| .else |
| addl %r10d, \registers |
| .endif |
| 3: |
| testw $$COMPACT_CODE_ITEM_OUTS_FLAG, COMPACT_CODE_ITEM_FLAGS_OFFSET(\code_item) |
| je 4f |
| subq $$2, %r11 |
| movzwl (%r11), %r10d |
| addl %r10d, \outs |
| 4: |
| .if \load_ins |
| addl \ins, \registers |
| .endif |
| addq $$COMPACT_CODE_ITEM_INSNS_OFFSET, \code_item |
| jmp 6f |
| 5: |
| // Fetch dex register size. |
| movzwl CODE_ITEM_REGISTERS_SIZE_OFFSET(\code_item), \registers |
| // Fetch outs size. |
| movzwl CODE_ITEM_OUTS_SIZE_OFFSET(\code_item), \outs |
| .if \load_ins |
| movzwl CODE_ITEM_INS_SIZE_OFFSET(\code_item), \ins |
| .endif |
| addq $$CODE_ITEM_INSNS_OFFSET, \code_item |
| 6: |
| .endm |
| |
| // Setup the stack to start executing the method. Expects: |
| // - rdi to contain the ArtMethod |
| // - rbx, r10, r11 to be available. |
| // |
| // Outputs |
| // - rbx contains the dex registers size |
| // - r11 contains the old stack pointer. |
| // - \code_item is replace with a pointer to the instructions |
| // - if load_ins is 1, r14 contains the ins |
| .macro SETUP_STACK_FRAME code_item, refs, refs32, fp, cfi_refs, load_ins |
| FETCH_CODE_ITEM_INFO \code_item, %ebx, \refs32, %r14d, \load_ins |
| |
| // Compute required frame size for dex registers: ((2 * ebx) + refs) |
| leaq (\refs, %rbx, 2), %r11 |
| salq $$2, %r11 |
| |
| // Compute new stack pointer in r10: add 24 for saving the previous frame, |
| // pc, and method being executed. |
| leaq -24(%rsp), %r10 |
| subq %r11, %r10 |
| // Alignment |
| // Note: There may be two pieces of alignment but there is no need to align |
| // out args to `kPointerSize` separately before aligning to kStackAlignment. |
| andq $$-16, %r10 |
| |
| // Set reference and dex registers, align to pointer size for previous frame and dex pc. |
| leaq 24 + 4(%r10, \refs, 4), \refs |
| andq LITERAL(-__SIZEOF_POINTER__), \refs |
| leaq (\refs, %rbx, 4), \fp |
| |
| // Now setup the stack pointer. |
| movq %rsp, %r11 |
| CFI_DEF_CFA_REGISTER(r11) |
| movq %r10, %rsp |
| movq %r11, -8(\refs) |
| CFI_DEF_CFA_BREG_PLUS_UCONST \cfi_refs, -8, ((6 + 4 + 1) * 8) |
| |
| // Put nulls in reference frame. |
| testl %ebx, %ebx |
| je 2f |
| movq \refs, %r10 |
| 1: |
| movl $$0, (%r10) |
| addq $$4, %r10 |
| cmpq %r10, \fp |
| jne 1b |
| 2: |
| // Save the ArtMethod. |
| movq %rdi, (%rsp) |
| .endm |
| |
| // Puts the next floating point argument into the expected register, |
| // fetching values based on a non-range invoke. |
| // Uses rax 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 // bl := *shorty |
| addq MACRO_LITERAL(1), REG_VAR(shorty) // shorty++ |
| cmpb MACRO_LITERAL(0), %al // if (al == '\0') goto finished |
| je VAR(finished) |
| cmpb MACRO_LITERAL(68), %al // if (al == 'D') goto FOUND_DOUBLE |
| je 2f |
| cmpb MACRO_LITERAL(70), %al // if (al == 'F') goto FOUND_FLOAT |
| je 3f |
| shrq MACRO_LITERAL(4), REG_VAR(inst) |
| addq MACRO_LITERAL(1), REG_VAR(arg_index) |
| // Handle extra argument in arg array taken by a long. |
| cmpb MACRO_LITERAL(74), %al // if (al != 'J') goto LOOP |
| jne 1b |
| shrq MACRO_LITERAL(4), REG_VAR(inst) |
| addq MACRO_LITERAL(1), REG_VAR(arg_index) |
| jmp 1b // goto LOOP |
| 2: // FOUND_DOUBLE |
| subq MACRO_LITERAL(8), %rsp |
| movq REG_VAR(inst), %rax |
| andq MACRO_LITERAL(0xf), %rax |
| GET_VREG %eax, %rax |
| movl %eax, (%rsp) |
| shrq MACRO_LITERAL(4), REG_VAR(inst) |
| addq MACRO_LITERAL(1), REG_VAR(arg_index) |
| cmpq MACRO_LITERAL(4), REG_VAR(arg_index) |
| je 5f |
| movq REG_VAR(inst), %rax |
| andq MACRO_LITERAL(0xf), %rax |
| shrq MACRO_LITERAL(4), REG_VAR(inst) |
| addq MACRO_LITERAL(1), REG_VAR(arg_index) |
| jmp 6f |
| 5: |
| movzbl 1(rPC), %eax |
| andq MACRO_LITERAL(0xf), %rax |
| 6: |
| GET_VREG %eax, %rax |
| movl %eax, 4(%rsp) |
| movsd (%rsp), REG_VAR(xmm_reg) |
| addq MACRO_LITERAL(8), %rsp |
| jmp 4f |
| 3: // FOUND_FLOAT |
| cmpq MACRO_LITERAL(4), REG_VAR(arg_index) |
| je 7f |
| movq REG_VAR(inst), %rax |
| andq MACRO_LITERAL(0xf), %rax |
| shrq MACRO_LITERAL(4), REG_VAR(inst) |
| addq MACRO_LITERAL(1), REG_VAR(arg_index) |
| jmp 8f |
| 7: |
| movzbl 1(rPC), %eax |
| andq MACRO_LITERAL(0xf), %rax |
| 8: |
| GET_VREG_XMMs REG_VAR(xmm_reg), %rax |
| 4: |
| .endm |
| |
| // Puts the next int/long/object argument in the expected register, |
| // fetching values based on a non-range invoke. |
| // Uses rax as temporary. |
| .macro LOOP_OVER_SHORTY_LOADING_GPRS gpr_reg64, gpr_reg32, inst, shorty, arg_index, finished |
| 1: // LOOP |
| movb (REG_VAR(shorty)), %al // al := *shorty |
| addq MACRO_LITERAL(1), REG_VAR(shorty) // shorty++ |
| cmpb MACRO_LITERAL(0), %al // if (al == '\0') goto finished |
| je VAR(finished) |
| cmpb MACRO_LITERAL(74), %al // if (al == 'J') goto FOUND_LONG |
| je 2f |
| cmpb MACRO_LITERAL(70), %al // if (al == 'F') goto SKIP_FLOAT |
| je 3f |
| cmpb MACRO_LITERAL(68), %al // if (al == 'D') goto SKIP_DOUBLE |
| je 4f |
| cmpq MACRO_LITERAL(4), REG_VAR(arg_index) |
| je 7f |
| movq REG_VAR(inst), %rax |
| andq MACRO_LITERAL(0xf), %rax |
| shrq MACRO_LITERAL(4), REG_VAR(inst) |
| addq MACRO_LITERAL(1), REG_VAR(arg_index) |
| jmp 8f |
| 7: |
| movzbl 1(rPC), %eax |
| andq MACRO_LITERAL(0xf), %rax |
| 8: |
| GET_VREG REG_VAR(gpr_reg32), %rax |
| jmp 5f |
| 2: // FOUND_LONG |
| subq MACRO_LITERAL(8), %rsp |
| movq REG_VAR(inst), %rax |
| andq MACRO_LITERAL(0xf), %rax |
| GET_VREG %eax, %rax |
| movl %eax, (%rsp) |
| shrq MACRO_LITERAL(4), REG_VAR(inst) |
| addq MACRO_LITERAL(1), REG_VAR(arg_index) |
| cmpq MACRO_LITERAL(4), REG_VAR(arg_index) |
| je 9f |
| movq REG_VAR(inst), %rax |
| andq MACRO_LITERAL(0xf), %rax |
| shrq MACRO_LITERAL(4), REG_VAR(inst) |
| addq MACRO_LITERAL(1), REG_VAR(arg_index) |
| jmp 10f |
| 9: |
| movzbl 1(rPC), %eax |
| andq MACRO_LITERAL(0xf), %rax |
| 10: |
| GET_VREG %eax, %rax |
| movl %eax, 4(%rsp) |
| movq (%rsp), REG_VAR(gpr_reg64) |
| addq MACRO_LITERAL(8), %rsp |
| jmp 5f |
| 3: // SKIP_FLOAT |
| shrq MACRO_LITERAL(4), REG_VAR(inst) |
| addq MACRO_LITERAL(1), REG_VAR(arg_index) |
| jmp 1b |
| 4: // SKIP_DOUBLE |
| shrq MACRO_LITERAL(4), REG_VAR(inst) |
| addq MACRO_LITERAL(1), REG_VAR(arg_index) |
| cmpq MACRO_LITERAL(4), REG_VAR(arg_index) |
| je 1b |
| shrq MACRO_LITERAL(4), REG_VAR(inst) |
| addq MACRO_LITERAL(1), REG_VAR(arg_index) |
| jmp 1b |
| 5: |
| .endm |
| |
| // Puts the next floating point argument into the expected register, |
| // fetching values based on a range invoke. |
| // Uses rax as temporary. |
| .macro LOOP_RANGE_OVER_SHORTY_LOADING_XMMS xmm_reg, shorty, arg_index, stack_index, finished |
| 1: // LOOP |
| movb (REG_VAR(shorty)), %al // al := *shorty |
| addq MACRO_LITERAL(1), REG_VAR(shorty) // shorty++ |
| cmpb MACRO_LITERAL(0), %al // if (al == '\0') goto finished |
| je VAR(finished) |
| cmpb MACRO_LITERAL(68), %al // if (al == 'D') goto FOUND_DOUBLE |
| je 2f |
| cmpb MACRO_LITERAL(70), %al // if (al == 'F') goto FOUND_FLOAT |
| je 3f |
| addq MACRO_LITERAL(1), REG_VAR(arg_index) |
| addq MACRO_LITERAL(1), REG_VAR(stack_index) |
| // Handle extra argument in arg array taken by a long. |
| cmpb MACRO_LITERAL(74), %al // if (al != 'J') goto LOOP |
| jne 1b |
| addq MACRO_LITERAL(1), REG_VAR(arg_index) |
| addq MACRO_LITERAL(1), REG_VAR(stack_index) |
| jmp 1b // goto LOOP |
| 2: // FOUND_DOUBLE |
| GET_VREG_XMMd REG_VAR(xmm_reg), REG_VAR(arg_index) |
| addq MACRO_LITERAL(2), REG_VAR(arg_index) |
| addq MACRO_LITERAL(2), REG_VAR(stack_index) |
| jmp 4f |
| 3: // FOUND_FLOAT |
| GET_VREG_XMMs REG_VAR(xmm_reg), REG_VAR(arg_index) |
| addq MACRO_LITERAL(1), REG_VAR(arg_index) |
| addq MACRO_LITERAL(1), REG_VAR(stack_index) |
| 4: |
| .endm |
| |
| // Puts the next floating point argument into the expected stack slot, |
| // fetching values based on a range invoke. |
| // Uses rax as temporary. |
| // |
| // TODO: We could just copy all the vregs to the stack slots in a simple loop |
| // (or REP MOVSD) without looking at the shorty at all. (We could also drop |
| // the "stack_index" from the macros for loading registers.) We could also do |
| // that conditionally if argument word count > 6; otherwise we know that all |
| // args fit into registers. |
| .macro LOOP_RANGE_OVER_FPs shorty, arg_index, stack_index, finished |
| 1: // LOOP |
| movb (REG_VAR(shorty)), %al // bl := *shorty |
| addq MACRO_LITERAL(1), REG_VAR(shorty) // shorty++ |
| cmpb MACRO_LITERAL(0), %al // if (al == '\0') goto finished |
| je VAR(finished) |
| cmpb MACRO_LITERAL(68), %al // if (al == 'D') goto FOUND_DOUBLE |
| je 2f |
| cmpb MACRO_LITERAL(70), %al // if (al == 'F') goto FOUND_FLOAT |
| je 3f |
| addq MACRO_LITERAL(1), REG_VAR(arg_index) |
| addq MACRO_LITERAL(1), REG_VAR(stack_index) |
| // Handle extra argument in arg array taken by a long. |
| cmpb MACRO_LITERAL(74), %al // if (al != 'J') goto LOOP |
| jne 1b |
| addq MACRO_LITERAL(1), REG_VAR(arg_index) |
| addq MACRO_LITERAL(1), REG_VAR(stack_index) |
| jmp 1b // goto LOOP |
| 2: // FOUND_DOUBLE |
| movq (rFP, REG_VAR(arg_index), 4), %rax |
| movq %rax, 8(%rsp, REG_VAR(stack_index), 4) |
| addq MACRO_LITERAL(2), REG_VAR(arg_index) |
| addq MACRO_LITERAL(2), REG_VAR(stack_index) |
| jmp 1b |
| 3: // FOUND_FLOAT |
| movl (rFP, REG_VAR(arg_index), 4), %eax |
| movl %eax, 8(%rsp, REG_VAR(stack_index), 4) |
| addq MACRO_LITERAL(1), REG_VAR(arg_index) |
| addq MACRO_LITERAL(1), REG_VAR(stack_index) |
| jmp 1b |
| .endm |
| |
| // Puts the next int/long/object argument in the expected register, |
| // fetching values based on a range invoke. |
| // Uses rax as temporary. |
| .macro LOOP_RANGE_OVER_SHORTY_LOADING_GPRS gpr_reg64, gpr_reg32, shorty, arg_index, stack_index, finished |
| 1: // LOOP |
| movb (REG_VAR(shorty)), %al // al := *shorty |
| addq MACRO_LITERAL(1), REG_VAR(shorty) // shorty++ |
| cmpb MACRO_LITERAL(0), %al // if (al == '\0') goto finished |
| je VAR(finished) |
| cmpb MACRO_LITERAL(74), %al // if (al == 'J') goto FOUND_LONG |
| je 2f |
| cmpb MACRO_LITERAL(70), %al // if (al == 'F') goto SKIP_FLOAT |
| je 3f |
| cmpb MACRO_LITERAL(68), %al // if (al == 'D') goto SKIP_DOUBLE |
| je 4f |
| movl (rFP, REG_VAR(arg_index), 4), REG_VAR(gpr_reg32) |
| addq MACRO_LITERAL(1), REG_VAR(arg_index) |
| addq MACRO_LITERAL(1), REG_VAR(stack_index) |
| jmp 5f |
| 2: // FOUND_LONG |
| movq (rFP, REG_VAR(arg_index), 4), REG_VAR(gpr_reg64) |
| addq MACRO_LITERAL(2), REG_VAR(arg_index) |
| addq MACRO_LITERAL(2), REG_VAR(stack_index) |
| jmp 5f |
| 3: // SKIP_FLOAT |
| addq MACRO_LITERAL(1), REG_VAR(arg_index) |
| addq MACRO_LITERAL(1), REG_VAR(stack_index) |
| jmp 1b |
| 4: // SKIP_DOUBLE |
| addq MACRO_LITERAL(2), REG_VAR(arg_index) |
| addq MACRO_LITERAL(2), REG_VAR(stack_index) |
| jmp 1b |
| 5: |
| .endm |
| |
| // Puts the next int/long/object argument in the expected stack slot, |
| // fetching values based on a range invoke. |
| // Uses rax as temporary. |
| .macro LOOP_RANGE_OVER_INTs shorty, arg_index, stack_index, finished |
| 1: // LOOP |
| movb (REG_VAR(shorty)), %al // al := *shorty |
| addq MACRO_LITERAL(1), REG_VAR(shorty) // shorty++ |
| cmpb MACRO_LITERAL(0), %al // if (al == '\0') goto finished |
| je VAR(finished) |
| cmpb MACRO_LITERAL(74), %al // if (al == 'J') goto FOUND_LONG |
| je 2f |
| cmpb MACRO_LITERAL(70), %al // if (al == 'F') goto SKIP_FLOAT |
| je 3f |
| cmpb MACRO_LITERAL(68), %al // if (al == 'D') goto SKIP_DOUBLE |
| je 4f |
| movl (rFP, REG_VAR(arg_index), 4), %eax |
| movl %eax, 8(%rsp, REG_VAR(stack_index), 4) |
| addq MACRO_LITERAL(1), REG_VAR(arg_index) |
| addq MACRO_LITERAL(1), REG_VAR(stack_index) |
| jmp 1b |
| 2: // FOUND_LONG |
| movq (rFP, REG_VAR(arg_index), 4), %rax |
| movq %rax, 8(%rsp, REG_VAR(stack_index), 4) |
| addq MACRO_LITERAL(2), REG_VAR(arg_index) |
| addq MACRO_LITERAL(2), REG_VAR(stack_index) |
| jmp 1b |
| 3: // SKIP_FLOAT |
| addq MACRO_LITERAL(1), REG_VAR(arg_index) |
| addq MACRO_LITERAL(1), REG_VAR(stack_index) |
| jmp 1b |
| 4: // SKIP_DOUBLE |
| addq MACRO_LITERAL(2), REG_VAR(arg_index) |
| addq MACRO_LITERAL(2), REG_VAR(stack_index) |
| jmp 1b |
| .endm |
| |
| // Puts the next floating point parameter passed in physical register |
| // in the expected dex register array entry. |
| // Uses rax as temporary. |
| .macro LOOP_OVER_SHORTY_STORING_XMMS xmm_reg, shorty, arg_index, fp, finished |
| 1: // LOOP |
| movb (REG_VAR(shorty)), %al // al := *shorty |
| addq MACRO_LITERAL(1), REG_VAR(shorty) // shorty++ |
| cmpb MACRO_LITERAL(0), %al // if (al == '\0') goto finished |
| je VAR(finished) |
| cmpb MACRO_LITERAL(68), %al // if (al == 'D') goto FOUND_DOUBLE |
| je 2f |
| cmpb MACRO_LITERAL(70), %al // if (al == 'F') goto FOUND_FLOAT |
| je 3f |
| addq MACRO_LITERAL(1), REG_VAR(arg_index) |
| // Handle extra argument in arg array taken by a long. |
| cmpb MACRO_LITERAL(74), %al // if (al != 'J') goto LOOP |
| jne 1b |
| addq MACRO_LITERAL(1), REG_VAR(arg_index) |
| jmp 1b // goto LOOP |
| 2: // FOUND_DOUBLE |
| movsd REG_VAR(xmm_reg),(REG_VAR(fp), REG_VAR(arg_index), 4) |
| addq MACRO_LITERAL(2), REG_VAR(arg_index) |
| jmp 4f |
| 3: // FOUND_FLOAT |
| movss REG_VAR(xmm_reg), (REG_VAR(fp), REG_VAR(arg_index), 4) |
| addq MACRO_LITERAL(1), REG_VAR(arg_index) |
| 4: |
| .endm |
| |
| // Puts the next int/long/object parameter passed in physical register |
| // in the expected dex register array entry, and in case of object in the |
| // expected reference array entry. |
| // Uses rax as temporary. |
| .macro LOOP_OVER_SHORTY_STORING_GPRS gpr_reg64, gpr_reg32, shorty, arg_index, regs, refs, finished |
| 1: // LOOP |
| movb (REG_VAR(shorty)), %al // al := *shorty |
| addq MACRO_LITERAL(1), REG_VAR(shorty) // shorty++ |
| cmpb MACRO_LITERAL(0), %al // if (al == '\0') goto finished |
| je VAR(finished) |
| cmpb MACRO_LITERAL(74), %al // if (al == 'J') goto FOUND_LONG |
| je 2f |
| cmpb MACRO_LITERAL(70), %al // if (al == 'F') goto SKIP_FLOAT |
| je 3f |
| cmpb MACRO_LITERAL(68), %al // if (al == 'D') goto SKIP_DOUBLE |
| je 4f |
| movl REG_VAR(gpr_reg32), (REG_VAR(regs), REG_VAR(arg_index), 4) |
| cmpb MACRO_LITERAL(76), %al // if (al != 'L') goto NOT_REFERENCE |
| jne 6f |
| movl REG_VAR(gpr_reg32), (REG_VAR(refs), REG_VAR(arg_index), 4) |
| 6: // NOT_REFERENCE |
| addq MACRO_LITERAL(1), REG_VAR(arg_index) |
| jmp 5f |
| 2: // FOUND_LONG |
| movq REG_VAR(gpr_reg64), (REG_VAR(regs), REG_VAR(arg_index), 4) |
| addq MACRO_LITERAL(2), REG_VAR(arg_index) |
| jmp 5f |
| 3: // SKIP_FLOAT |
| addq MACRO_LITERAL(1), REG_VAR(arg_index) |
| jmp 1b |
| 4: // SKIP_DOUBLE |
| addq MACRO_LITERAL(2), REG_VAR(arg_index) |
| jmp 1b |
| 5: |
| .endm |
| |
| // Puts the next floating point parameter passed in stack |
| // in the expected dex register array entry. |
| // Uses rax as temporary. |
| // |
| // TODO: Or we could just spill regs to the reserved slots in the caller's |
| // frame and copy all regs in a simple loop. This time, however, we would |
| // need to look at the shorty anyway to look for the references. |
| // (The trade-off is different for passing arguments and receiving them.) |
| .macro LOOP_OVER_FPs shorty, arg_index, regs, stack_ptr, finished |
| 1: // LOOP |
| movb (REG_VAR(shorty)), %al // al := *shorty |
| addq MACRO_LITERAL(1), REG_VAR(shorty) // shorty++ |
| cmpb MACRO_LITERAL(0), %al // if (al == '\0') goto finished |
| je VAR(finished) |
| cmpb MACRO_LITERAL(68), %al // if (al == 'D') goto FOUND_DOUBLE |
| je 2f |
| cmpb MACRO_LITERAL(70), %al // if (al == 'F') goto FOUND_FLOAT |
| je 3f |
| addq MACRO_LITERAL(1), REG_VAR(arg_index) |
| // Handle extra argument in arg array taken by a long. |
| cmpb MACRO_LITERAL(74), %al // if (al != 'J') goto LOOP |
| jne 1b |
| addq MACRO_LITERAL(1), REG_VAR(arg_index) |
| jmp 1b // goto LOOP |
| 2: // FOUND_DOUBLE |
| movq OFFSET_TO_FIRST_ARGUMENT_IN_STACK(REG_VAR(stack_ptr), REG_VAR(arg_index), 4), %rax |
| movq %rax, (REG_VAR(regs), REG_VAR(arg_index), 4) |
| addq MACRO_LITERAL(2), REG_VAR(arg_index) |
| jmp 1b |
| 3: // FOUND_FLOAT |
| movl OFFSET_TO_FIRST_ARGUMENT_IN_STACK(REG_VAR(stack_ptr), REG_VAR(arg_index), 4), %eax |
| movl %eax, (REG_VAR(regs), REG_VAR(arg_index), 4) |
| addq MACRO_LITERAL(1), REG_VAR(arg_index) |
| jmp 1b |
| .endm |
| |
| // Puts the next int/long/object parameter passed in stack |
| // in the expected dex register array entry, and in case of object in the |
| // expected reference array entry. |
| // Uses rax as temporary. |
| .macro LOOP_OVER_INTs shorty, arg_index, regs, refs, stack_ptr, finished |
| 1: // LOOP |
| movb (REG_VAR(shorty)), %al // al := *shorty |
| addq MACRO_LITERAL(1), REG_VAR(shorty) // shorty++ |
| cmpb MACRO_LITERAL(0), %al // if (al == '\0') goto finished |
| je VAR(finished) |
| cmpb MACRO_LITERAL(74), %al // if (al == 'J') goto FOUND_LONG |
| je 2f |
| cmpb MACRO_LITERAL(76), %al // if (al == 'L') goto FOUND_REFERENCE |
| je 6f |
| cmpb MACRO_LITERAL(70), %al // if (al == 'F') goto SKIP_FLOAT |
| je 3f |
| cmpb MACRO_LITERAL(68), %al // if (al == 'D') goto SKIP_DOUBLE |
| je 4f |
| movl OFFSET_TO_FIRST_ARGUMENT_IN_STACK(REG_VAR(stack_ptr), REG_VAR(arg_index), 4), %eax |
| movl %eax, (REG_VAR(regs), REG_VAR(arg_index), 4) |
| addq MACRO_LITERAL(1), REG_VAR(arg_index) |
| jmp 1b |
| 6: // FOUND_REFERENCE |
| movl OFFSET_TO_FIRST_ARGUMENT_IN_STACK(REG_VAR(stack_ptr), REG_VAR(arg_index), 4), %eax |
| movl %eax, (REG_VAR(regs), REG_VAR(arg_index), 4) |
| movl %eax, (REG_VAR(refs), REG_VAR(arg_index), 4) |
| addq MACRO_LITERAL(1), REG_VAR(arg_index) |
| jmp 1b |
| 2: // FOUND_LONG |
| movq OFFSET_TO_FIRST_ARGUMENT_IN_STACK(REG_VAR(stack_ptr), REG_VAR(arg_index), 4), %rax |
| movq %rax, (REG_VAR(regs), REG_VAR(arg_index), 4) |
| addq MACRO_LITERAL(2), REG_VAR(arg_index) |
| jmp 1b |
| 3: // SKIP_FLOAT |
| addq MACRO_LITERAL(1), REG_VAR(arg_index) |
| jmp 1b |
| 4: // SKIP_DOUBLE |
| addq MACRO_LITERAL(2), REG_VAR(arg_index) |
| jmp 1b |
| .endm |
| |
| // Increase method hotness and do suspend check before starting executing the method. |
| .macro START_EXECUTING_INSTRUCTIONS |
| movq (%rsp), %rdi |
| movzwl ART_METHOD_HOTNESS_COUNT_OFFSET(%rdi), %esi |
| #if (NTERP_HOTNESS_VALUE != 0) |
| #error Expected 0 for hotness value |
| #endif |
| // If the counter is at zero, handle this in the runtime. |
| testl %esi, %esi |
| je 3f |
| // Update counter. |
| addl $$-1, %esi |
| movw %si, ART_METHOD_HOTNESS_COUNT_OFFSET(%rdi) |
| 1: |
| DO_SUSPEND_CHECK continue_label=2f |
| 2: |
| FETCH_INST |
| GOTO_NEXT |
| 3: |
| CHECK_AND_UPDATE_SHARED_MEMORY_METHOD if_hot=4f, if_not_hot=1b |
| 4: |
| movq $$0, %rsi |
| movq rFP, %rdx |
| call nterp_hot_method |
| jmp 2b |
| .endm |
| |
| .macro SPILL_ALL_CALLEE_SAVES |
| PUSH r15 |
| PUSH r14 |
| PUSH r13 |
| PUSH r12 |
| PUSH rbp |
| PUSH rbx |
| SETUP_FP_CALLEE_SAVE_FRAME |
| .endm |
| |
| .macro RESTORE_ALL_CALLEE_SAVES |
| RESTORE_FP_CALLEE_SAVE_FRAME |
| POP rbx |
| POP rbp |
| POP r12 |
| POP r13 |
| POP r14 |
| POP r15 |
| .endm |
| |
| // Helper to setup the stack after doing a nterp to nterp call. This will setup: |
| // - rNEW_FP: the new pointer to dex registers |
| // - rNEW_REFS: the new pointer to references |
| // - rPC: the new PC pointer to execute |
| // - edi: number of arguments |
| // - ecx: first dex register |
| // |
| // This helper expects: |
| // - rax to contain the code item |
| .macro SETUP_STACK_FOR_INVOKE |
| // We do the same stack overflow check as the compiler. See CanMethodUseNterp |
| // in how we limit the maximum nterp frame size. |
| testq %rax, -STACK_OVERFLOW_RESERVED_BYTES(%rsp) |
| |
| // Spill all callee saves to have a consistent stack frame whether we |
| // are called by compiled code or nterp. |
| SPILL_ALL_CALLEE_SAVES |
| |
| // Setup the frame. |
| SETUP_STACK_FRAME %rax, rNEW_REFS, rNEW_REFS32, rNEW_FP, CFI_NEW_REFS, load_ins=0 |
| // Make r11 point to the top of the dex register array. |
| leaq (rNEW_FP, %rbx, 4), %r11 |
| |
| // Fetch instruction information before replacing rPC. |
| movzbl 1(rPC), %edi |
| movzwl 4(rPC), %ecx |
| |
| // Set the dex pc pointer. |
| movq %rax, rPC |
| CFI_DEFINE_DEX_PC_WITH_OFFSET(CFI_TMP, CFI_DEX, 0) |
| .endm |
| |
| // Setup arguments based on a non-range nterp to nterp call, and start executing |
| // the method. We expect: |
| // - rNEW_FP: the new pointer to dex registers |
| // - rNEW_REFS: the new pointer to references |
| // - rPC: the new PC pointer to execute |
| // - edi: number of arguments |
| // - ecx: first dex register |
| // - r11: top of dex register array |
| // - esi: receiver if non-static. |
| .macro SETUP_NON_RANGE_ARGUMENTS_AND_EXECUTE is_static=0, is_string_init=0 |
| // Now all temporary registers (except r11 containing top of registers array) |
| // are available, copy the parameters. |
| // /* op vA, vB, {vC...vG} */ |
| movl %edi, %eax |
| shrl $$4, %eax # Number of arguments |
| jz 6f # shl sets the Z flag |
| movq MACRO_LITERAL(-1), %r10 |
| cmpl MACRO_LITERAL(2), %eax |
| jl 1f |
| je 2f |
| cmpl MACRO_LITERAL(4), %eax |
| jl 3f |
| je 4f |
| |
| // We use a decrementing r10 to store references relative |
| // to rNEW_FP and dex registers relative to r11. |
| // |
| // TODO: We could set up r10 as the number of registers (this can be an additional output from |
| // SETUP_STACK_FOR_INVOKE) and then just decrement it by one before copying each arg to |
| // (rNEW_FP, r10, 4) and (rNEW_REFS, r10, 4). |
| // Maybe even introduce macros NEW_VREG_ADDRESS/NEW_VREG_REF_ADDRESS. |
| 5: |
| andq MACRO_LITERAL(15), %rdi |
| GET_VREG_OBJECT %edx, %rdi |
| movl %edx, (rNEW_FP, %r10, 4) |
| GET_VREG %edx, %rdi |
| movl %edx, (%r11, %r10, 4) |
| subq MACRO_LITERAL(1), %r10 |
| 4: |
| movl %ecx, %eax |
| shrl MACRO_LITERAL(12), %eax |
| GET_VREG_OBJECT %edx, %rax |
| movl %edx, (rNEW_FP, %r10, 4) |
| GET_VREG %edx, %rax |
| movl %edx, (%r11, %r10, 4) |
| subq MACRO_LITERAL(1), %r10 |
| 3: |
| movl %ecx, %eax |
| shrl MACRO_LITERAL(8), %eax |
| andl MACRO_LITERAL(0xf), %eax |
| GET_VREG_OBJECT %edx, %rax |
| movl %edx, (rNEW_FP, %r10, 4) |
| GET_VREG %edx, %rax |
| movl %edx, (%r11, %r10, 4) |
| subq MACRO_LITERAL(1), %r10 |
| 2: |
| movl %ecx, %eax |
| shrl MACRO_LITERAL(4), %eax |
| andl MACRO_LITERAL(0xf), %eax |
| GET_VREG_OBJECT %edx, %rax |
| movl %edx, (rNEW_FP, %r10, 4) |
| GET_VREG %edx, %rax |
| movl %edx, (%r11, %r10, 4) |
| subq MACRO_LITERAL(1), %r10 |
| 1: |
| .if \is_string_init |
| // Ignore the first argument |
| .elseif \is_static |
| movl %ecx, %eax |
| andq MACRO_LITERAL(0x000f), %rax |
| GET_VREG_OBJECT %edx, %rax |
| movl %edx, (rNEW_FP, %r10, 4) |
| GET_VREG %edx, %rax |
| movl %edx, (%r11, %r10, 4) |
| .else |
| movl %esi, (rNEW_FP, %r10, 4) |
| movl %esi, (%r11, %r10, 4) |
| .endif |
| |
| 6: |
| // Start executing the method. |
| movq rNEW_FP, rFP |
| movq rNEW_REFS, rREFS |
| CFI_DEF_CFA_BREG_PLUS_UCONST CFI_REFS, -8, ((6 + 4 + 1) * 8) |
| START_EXECUTING_INSTRUCTIONS |
| .endm |
| |
| // Setup arguments based on a range nterp to nterp call, and start executing |
| // the method. |
| .macro SETUP_RANGE_ARGUMENTS_AND_EXECUTE is_static=0, is_string_init=0 |
| // edi is number of arguments |
| // ecx is first register |
| movq MACRO_LITERAL(-4), %r10 |
| .if \is_string_init |
| // Ignore the first argument |
| subl $$1, %edi |
| addl $$1, %ecx |
| .elseif !\is_static |
| subl $$1, %edi |
| addl $$1, %ecx |
| .endif |
| |
| testl %edi, %edi |
| je 2f |
| leaq (rREFS, %rcx, 4), %rax # pointer to first argument in reference array |
| leaq (%rax, %rdi, 4), %rax # pointer to last argument in reference array |
| leaq (rFP, %rcx, 4), %rcx # pointer to first argument in register array |
| leaq (%rcx, %rdi, 4), %rdi # pointer to last argument in register array |
| // TODO: Same comment for copying arguments as in SETUP_NON_RANGE_ARGUMENTS_AND_EXECUTE. |
| 1: |
| movl -4(%rax), %edx |
| movl %edx, (rNEW_FP, %r10, 1) |
| movl -4(%rdi), %edx |
| movl %edx, (%r11, %r10, 1) |
| subq MACRO_LITERAL(4), %r10 |
| subq MACRO_LITERAL(4), %rax |
| subq MACRO_LITERAL(4), %rdi |
| cmpq %rcx, %rdi |
| jne 1b |
| |
| 2: |
| .if \is_string_init |
| // Ignore first argument |
| .elseif !\is_static |
| movl %esi, (rNEW_FP, %r10, 1) |
| movl %esi, (%r11, %r10, 1) |
| .endif |
| movq rNEW_FP, rFP |
| movq rNEW_REFS, rREFS |
| CFI_DEF_CFA_BREG_PLUS_UCONST CFI_REFS, -8, ((6 + 4 + 1) * 8) |
| START_EXECUTING_INSTRUCTIONS |
| .endm |
| |
| .macro GET_SHORTY dest, is_interface, is_polymorphic, is_custom |
| push %rdi |
| push %rsi |
| .if \is_polymorphic |
| movq 16(%rsp), %rdi |
| movq rPC, %rsi |
| call SYMBOL(NterpGetShortyFromInvokePolymorphic) |
| .elseif \is_custom |
| movq 16(%rsp), %rdi |
| movq rPC, %rsi |
| call SYMBOL(NterpGetShortyFromInvokeCustom) |
| .elseif \is_interface |
| movq 16(%rsp), %rdi |
| movzwl 2(rPC), %esi |
| call SYMBOL(NterpGetShortyFromMethodId) |
| .else |
| call SYMBOL(NterpGetShorty) |
| .endif |
| pop %rsi |
| pop %rdi |
| movq %rax, \dest |
| .endm |
| |
| .macro GET_SHORTY_SLOW_PATH dest, is_interface |
| // Save all registers that can hold arguments in the fast path. |
| push %rdi |
| push %rsi |
| push %rdx |
| subq MACRO_LITERAL(8), %rsp |
| mov %xmm0, (%rsp) |
| .if \is_interface |
| movq 32(%rsp), %rdi |
| movzwl 2(rPC), %esi |
| call SYMBOL(NterpGetShortyFromMethodId) |
| .else |
| call SYMBOL(NterpGetShorty) |
| .endif |
| mov (%rsp), %xmm0 |
| addq MACRO_LITERAL(8), %rsp |
| pop %rdx |
| pop %rsi |
| pop %rdi |
| movq %rax, \dest |
| .endm |
| |
| // Uses r9 as temporary. |
| .macro DO_ENTRY_POINT_CHECK call_compiled_code |
| // On entry, the method is %rdi, the instance is %rsi |
| leaq ExecuteNterpImpl(%rip), %r9 |
| cmpq %r9, ART_METHOD_QUICK_CODE_OFFSET_64(%rdi) |
| jne VAR(call_compiled_code) |
| |
| movq ART_METHOD_DATA_OFFSET_64(%rdi), %rax |
| .endm |
| |
| // Uses r9 and r10 as temporary |
| .macro UPDATE_REGISTERS_FOR_STRING_INIT old_value, new_value |
| movq rREFS, %r9 |
| movq rFP, %r10 |
| 1: |
| cmpl (%r9), \old_value |
| jne 2f |
| movl \new_value, (%r9) |
| movl \new_value, (%r10) |
| 2: |
| addq $$4, %r9 |
| addq $$4, %r10 |
| cmpq %r9, rFP |
| jne 1b |
| .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. |
| .elseif \is_custom |
| // We always go to compiled code for custom calls. |
| .else |
| DO_ENTRY_POINT_CHECK .Lcall_compiled_code_\suffix |
| .if \is_string_init |
| call nterp_to_nterp_string_init_non_range |
| .elseif \is_static |
| call nterp_to_nterp_static_non_range |
| .else |
| call nterp_to_nterp_instance_non_range |
| .endif |
| jmp .Ldone_return_\suffix |
| .endif |
| |
| .Lcall_compiled_code_\suffix: |
| .if \is_polymorphic |
| // No fast path for polymorphic calls. |
| .elseif \is_custom |
| // No fast path for custom calls. |
| .elseif \is_string_init |
| // No fast path for string.init. |
| .else |
| testl $$ART_METHOD_NTERP_INVOKE_FAST_PATH_FLAG, ART_METHOD_ACCESS_FLAGS_OFFSET(%rdi) |
| je .Lfast_path_with_few_args_\suffix |
| movzbl 1(rPC), %r9d |
| movl %r9d, %ebp |
| shrl MACRO_LITERAL(4), %ebp # Number of arguments |
| .if \is_static |
| jz .Linvoke_fast_path_\suffix # shl sets the Z flag |
| .else |
| cmpl MACRO_LITERAL(1), %ebp |
| je .Linvoke_fast_path_\suffix |
| .endif |
| movzwl 4(rPC), %r11d |
| cmpl MACRO_LITERAL(2), %ebp |
| .if \is_static |
| jl .Lone_arg_fast_path_\suffix |
| .endif |
| je .Ltwo_args_fast_path_\suffix |
| cmpl MACRO_LITERAL(4), %ebp |
| jl .Lthree_args_fast_path_\suffix |
| je .Lfour_args_fast_path_\suffix |
| |
| andl MACRO_LITERAL(0xf), %r9d |
| GET_VREG %r9d, %r9 |
| .Lfour_args_fast_path_\suffix: |
| movl %r11d, %r8d |
| shrl MACRO_LITERAL(12), %r8d |
| GET_VREG %r8d, %r8 |
| .Lthree_args_fast_path_\suffix: |
| movl %r11d, %ecx |
| shrl MACRO_LITERAL(8), %ecx |
| andl MACRO_LITERAL(0xf), %ecx |
| GET_VREG %ecx, %rcx |
| .Ltwo_args_fast_path_\suffix: |
| movl %r11d, %edx |
| shrl MACRO_LITERAL(4), %edx |
| andl MACRO_LITERAL(0xf), %edx |
| GET_VREG %edx, %rdx |
| .Lone_arg_fast_path_\suffix: |
| .if \is_static |
| andl MACRO_LITERAL(0xf), %r11d |
| GET_VREG %esi, %r11 |
| .else |
| // First argument already in %esi. |
| .endif |
| .Linvoke_fast_path_\suffix: |
| call *ART_METHOD_QUICK_CODE_OFFSET_64(%rdi) // Call the method. |
| 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), %r9d |
| shrl MACRO_LITERAL(4), %r9d # Number of arguments |
| .if \is_static |
| cmpl MACRO_LITERAL(1), %r9d |
| jl .Linvoke_with_few_args_\suffix |
| jne .Lget_shorty_\suffix |
| movzwl 4(rPC), %r9d |
| andl MACRO_LITERAL(0xf), %r9d // dex register of first argument |
| GET_VREG %esi, %r9 |
| movd %esi, %xmm0 |
| .else |
| cmpl MACRO_LITERAL(2), %r9d |
| jl .Linvoke_with_few_args_\suffix |
| jne .Lget_shorty_\suffix |
| movzwl 4(rPC), %r9d |
| shrl MACRO_LITERAL(4), %r9d |
| andl MACRO_LITERAL(0xf), %r9d // dex register of second argument |
| GET_VREG %edx, %r9 |
| movd %edx, %xmm0 |
| .endif |
| .Linvoke_with_few_args_\suffix: |
| // Check if the next instruction is move-result or move-result-wide. |
| // If it is, we fetch the shorty and jump to the regular invocation. |
| movzwq 6(rPC), %r9 |
| andl MACRO_LITERAL(0xfe), %r9d |
| cmpl MACRO_LITERAL(0x0a), %r9d |
| je .Lget_shorty_and_invoke_\suffix |
| call *ART_METHOD_QUICK_CODE_OFFSET_64(%rdi) // Call the method. |
| ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 |
| .Lget_shorty_and_invoke_\suffix: |
| .if \is_interface |
| // Save interface method, used for conflict resolution, in a callee-save register. |
| movq %rax, %xmm12 |
| .endif |
| GET_SHORTY_SLOW_PATH rINSTq, \is_interface |
| jmp .Lgpr_setup_finished_\suffix |
| .endif |
| |
| .Lget_shorty_\suffix: |
| .if \is_interface |
| // Save interface method, used for conflict resolution, in a callee-save register. |
| movq %rax, %xmm12 |
| .endif |
| GET_SHORTY rINSTq, \is_interface, \is_polymorphic, \is_custom |
| // From this point: |
| // - rISNTq contains shorty (in callee-save to switch over return value after call). |
| // - rdi contains method |
| // - rsi contains 'this' pointer for instance method. |
| leaq 1(rINSTq), %r9 // shorty + 1 ; ie skip return arg character |
| movzwl 4(rPC), %r11d // arguments |
| .if \is_string_init |
| shrq MACRO_LITERAL(4), %r11 |
| movq $$1, %r10 // ignore first argument |
| .elseif \is_static |
| movq $$0, %r10 // arg_index |
| .else |
| shrq MACRO_LITERAL(4), %r11 |
| movq $$1, %r10 // arg_index |
| .endif |
| LOOP_OVER_SHORTY_LOADING_XMMS xmm0, r11, r9, r10, .Lxmm_setup_finished_\suffix |
| LOOP_OVER_SHORTY_LOADING_XMMS xmm1, r11, r9, r10, .Lxmm_setup_finished_\suffix |
| LOOP_OVER_SHORTY_LOADING_XMMS xmm2, r11, r9, r10, .Lxmm_setup_finished_\suffix |
| LOOP_OVER_SHORTY_LOADING_XMMS xmm3, r11, r9, r10, .Lxmm_setup_finished_\suffix |
| LOOP_OVER_SHORTY_LOADING_XMMS xmm4, r11, r9, r10, .Lxmm_setup_finished_\suffix |
| .Lxmm_setup_finished_\suffix: |
| leaq 1(rINSTq), %r9 // shorty + 1 ; ie skip return arg character |
| movzwl 4(rPC), %r11d // arguments |
| .if \is_string_init |
| movq $$1, %r10 // ignore first argument |
| shrq MACRO_LITERAL(4), %r11 |
| LOOP_OVER_SHORTY_LOADING_GPRS rsi, esi, r11, r9, r10, .Lgpr_setup_finished_\suffix |
| .elseif \is_static |
| movq $$0, %r10 // arg_index |
| LOOP_OVER_SHORTY_LOADING_GPRS rsi, esi, r11, r9, r10, .Lgpr_setup_finished_\suffix |
| .else |
| shrq MACRO_LITERAL(4), %r11 |
| movq $$1, %r10 // arg_index |
| .endif |
| LOOP_OVER_SHORTY_LOADING_GPRS rdx, edx, r11, r9, r10, .Lgpr_setup_finished_\suffix |
| LOOP_OVER_SHORTY_LOADING_GPRS rcx, ecx, r11, r9, r10, .Lgpr_setup_finished_\suffix |
| LOOP_OVER_SHORTY_LOADING_GPRS r8, r8d, r11, r9, r10, .Lgpr_setup_finished_\suffix |
| LOOP_OVER_SHORTY_LOADING_GPRS r9, r9d, r11, r9, r10, .Lgpr_setup_finished_\suffix |
| .Lgpr_setup_finished_\suffix: |
| .if \is_polymorphic |
| call SYMBOL(art_quick_invoke_polymorphic) |
| .elseif \is_custom |
| call SYMBOL(art_quick_invoke_custom) |
| .else |
| .if \is_interface |
| movq %xmm12, %rax |
| .endif |
| call *ART_METHOD_QUICK_CODE_OFFSET_64(%rdi) // Call the method. |
| .endif |
| cmpb LITERAL(68), (rINSTq) // Test if result type char == 'D'. |
| je .Lreturn_double_\suffix |
| cmpb LITERAL(70), (rINSTq) // Test if result type char == 'F'. |
| jne .Ldone_return_\suffix |
| .Lreturn_float_\suffix: |
| movd %xmm0, %eax |
| jmp .Ldone_return_\suffix |
| .Lreturn_double_\suffix: |
| movq %xmm0, %rax |
| .Ldone_return_\suffix: |
| /* resume execution of caller */ |
| .if \is_string_init |
| movzwl 4(rPC), %r11d // arguments |
| andq $$0xf, %r11 |
| GET_VREG %esi, %r11 |
| UPDATE_REGISTERS_FOR_STRING_INIT %esi, %eax |
| .endif |
| |
| .if \is_polymorphic |
| ADVANCE_PC_FETCH_AND_GOTO_NEXT 4 |
| .else |
| ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 |
| .endif |
| .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. |
| .elseif \is_custom |
| // We always go to compiled code for custom calls. |
| .else |
| DO_ENTRY_POINT_CHECK .Lcall_compiled_code_range_\suffix |
| .if \is_string_init |
| call nterp_to_nterp_string_init_range |
| .elseif \is_static |
| call nterp_to_nterp_static_range |
| .else |
| call nterp_to_nterp_instance_range |
| .endif |
| jmp .Ldone_return_range_\suffix |
| .endif |
| |
| .Lcall_compiled_code_range_\suffix: |
| .if \is_polymorphic |
| // No fast path for polymorphic calls. |
| .elseif \is_custom |
| // No fast path for custom calls. |
| .elseif \is_string_init |
| // No fast path for string.init. |
| .else |
| testl $$ART_METHOD_NTERP_INVOKE_FAST_PATH_FLAG, ART_METHOD_ACCESS_FLAGS_OFFSET(%rdi) |
| je .Lfast_path_with_few_args_range_\suffix |
| movzbl 1(rPC), %r9d // number of arguments |
| .if \is_static |
| testl %r9d, %r9d |
| je .Linvoke_fast_path_range_\suffix |
| .else |
| cmpl MACRO_LITERAL(1), %r9d |
| je .Linvoke_fast_path_range_\suffix |
| .endif |
| movzwl 4(rPC), %r11d // dex register of first argument |
| leaq (rFP, %r11, 4), %r11 // location of first dex register value |
| cmpl MACRO_LITERAL(2), %r9d |
| .if \is_static |
| jl .Lone_arg_fast_path_range_\suffix |
| .endif |
| je .Ltwo_args_fast_path_range_\suffix |
| cmp MACRO_LITERAL(4), %r9d |
| jl .Lthree_args_fast_path_range_\suffix |
| je .Lfour_args_fast_path_range_\suffix |
| cmp MACRO_LITERAL(5), %r9d |
| je .Lfive_args_fast_path_range_\suffix |
| |
| .Lloop_over_fast_path_range_\suffix: |
| subl MACRO_LITERAL(1), %r9d |
| movl (%r11, %r9, 4), %r8d |
| movl %r8d, 8(%rsp, %r9, 4) // Add 8 for the ArtMethod |
| cmpl MACRO_LITERAL(5), %r9d |
| jne .Lloop_over_fast_path_range_\suffix |
| |
| .Lfive_args_fast_path_range_\suffix: |
| movl 16(%r11), %r9d |
| .Lfour_args_fast_path_range_\suffix: |
| movl 12(%r11), %r8d |
| .Lthree_args_fast_path_range_\suffix: |
| movl 8(%r11), %ecx |
| .Ltwo_args_fast_path_range_\suffix: |
| movl 4(%r11), %edx |
| .Lone_arg_fast_path_range_\suffix: |
| .if \is_static |
| movl 0(%r11), %esi |
| .else |
| // First argument already in %esi. |
| .endif |
| .Linvoke_fast_path_range_\suffix: |
| call *ART_METHOD_QUICK_CODE_OFFSET_64(%rdi) // Call the method. |
| 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), %r9d # Number of arguments |
| .if \is_static |
| cmpl MACRO_LITERAL(1), %r9d |
| jl .Linvoke_with_few_args_range_\suffix |
| jne .Lget_shorty_range_\suffix |
| movzwl 4(rPC), %r9d // Dex register of first argument |
| GET_VREG %esi, %r9 |
| movd %esi, %xmm0 |
| .else |
| cmpl MACRO_LITERAL(2), %r9d |
| jl .Linvoke_with_few_args_range_\suffix |
| jne .Lget_shorty_range_\suffix |
| movzwl 4(rPC), %r9d |
| addl MACRO_LITERAL(1), %r9d // dex register of second argument |
| GET_VREG %edx, %r9 |
| movd %edx, %xmm0 |
| .endif |
| .Linvoke_with_few_args_range_\suffix: |
| // Check if the next instruction is move-result or move-result-wide. |
| // If it is, we fetch the shorty and jump to the regular invocation. |
| movzwq 6(rPC), %r9 |
| and MACRO_LITERAL(0xfe), %r9d |
| cmpl MACRO_LITERAL(0x0a), %r9d |
| je .Lget_shorty_and_invoke_range_\suffix |
| call *ART_METHOD_QUICK_CODE_OFFSET_64(%rdi) // Call the method. |
| ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 |
| .Lget_shorty_and_invoke_range_\suffix: |
| .if \is_interface |
| // Save interface method, used for conflict resolution, in a callee-save register. |
| movq %rax, %xmm12 |
| .endif |
| GET_SHORTY_SLOW_PATH rINSTq, \is_interface |
| jmp .Lgpr_setup_finished_range_\suffix |
| .endif |
| |
| .Lget_shorty_range_\suffix: |
| .if \is_interface |
| // Save interface method, used for conflict resolution, in a callee-saved register. |
| movq %rax, %xmm12 |
| .endif |
| GET_SHORTY rINSTq, \is_interface, \is_polymorphic, \is_custom |
| // From this point: |
| // - rINSTq contains shorty (in callee-save to switch over return value after call). |
| // - rdi contains method |
| // - rsi contains 'this' pointer for instance method. |
| leaq 1(rINSTq), %r9 // shorty + 1 ; ie skip return arg character |
| movzwl 4(rPC), %r10d // arg start index |
| .if \is_string_init |
| addq $$1, %r10 // arg start index |
| movq $$1, %rbp // index in stack |
| .elseif \is_static |
| movq $$0, %rbp // index in stack |
| .else |
| addq $$1, %r10 // arg start index |
| movq $$1, %rbp // index in stack |
| .endif |
| LOOP_RANGE_OVER_SHORTY_LOADING_XMMS xmm0, r9, r10, rbp, .Lxmm_setup_finished_range_\suffix |
| LOOP_RANGE_OVER_SHORTY_LOADING_XMMS xmm1, r9, r10, rbp, .Lxmm_setup_finished_range_\suffix |
| LOOP_RANGE_OVER_SHORTY_LOADING_XMMS xmm2, r9, r10, rbp, .Lxmm_setup_finished_range_\suffix |
| LOOP_RANGE_OVER_SHORTY_LOADING_XMMS xmm3, r9, r10, rbp, .Lxmm_setup_finished_range_\suffix |
| LOOP_RANGE_OVER_SHORTY_LOADING_XMMS xmm4, r9, r10, rbp, .Lxmm_setup_finished_range_\suffix |
| LOOP_RANGE_OVER_SHORTY_LOADING_XMMS xmm5, r9, r10, rbp, .Lxmm_setup_finished_range_\suffix |
| LOOP_RANGE_OVER_SHORTY_LOADING_XMMS xmm6, r9, r10, rbp, .Lxmm_setup_finished_range_\suffix |
| LOOP_RANGE_OVER_SHORTY_LOADING_XMMS xmm7, r9, r10, rbp, .Lxmm_setup_finished_range_\suffix |
| LOOP_RANGE_OVER_FPs r9, r10, rbp, .Lxmm_setup_finished_range_\suffix |
| .Lxmm_setup_finished_range_\suffix: |
| leaq 1(%rbx), %r11 // shorty + 1 ; ie skip return arg character |
| movzwl 4(rPC), %r10d // arg start index |
| .if \is_string_init |
| addq $$1, %r10 // arg start index |
| movq $$1, %rbp // index in stack |
| LOOP_RANGE_OVER_SHORTY_LOADING_GPRS rsi, esi, r11, r10, rbp, .Lgpr_setup_finished_range_\suffix |
| .elseif \is_static |
| movq $$0, %rbp // index in stack |
| LOOP_RANGE_OVER_SHORTY_LOADING_GPRS rsi, esi, r11, r10, rbp, .Lgpr_setup_finished_range_\suffix |
| .else |
| addq $$1, %r10 // arg start index |
| movq $$1, %rbp // index in stack |
| .endif |
| LOOP_RANGE_OVER_SHORTY_LOADING_GPRS rdx, edx, r11, r10, rbp, .Lgpr_setup_finished_range_\suffix |
| LOOP_RANGE_OVER_SHORTY_LOADING_GPRS rcx, ecx, r11, r10, rbp, .Lgpr_setup_finished_range_\suffix |
| LOOP_RANGE_OVER_SHORTY_LOADING_GPRS r8, r8d, r11, r10, rbp, .Lgpr_setup_finished_range_\suffix |
| LOOP_RANGE_OVER_SHORTY_LOADING_GPRS r9, r9d, r11, r10, rbp, .Lgpr_setup_finished_range_\suffix |
| LOOP_RANGE_OVER_INTs r11, r10, rbp, .Lgpr_setup_finished_range_\suffix |
| |
| .Lgpr_setup_finished_range_\suffix: |
| .if \is_polymorphic |
| call SYMBOL(art_quick_invoke_polymorphic) |
| .elseif \is_custom |
| call SYMBOL(art_quick_invoke_custom) |
| .else |
| .if \is_interface |
| // Set the hidden argument for conflict resolution. |
| movq %xmm12, %rax |
| .endif |
| call *ART_METHOD_QUICK_CODE_OFFSET_64(%rdi) // Call the method. |
| .endif |
| cmpb LITERAL(68), (%rbx) // Test if result type char == 'D'. |
| je .Lreturn_range_double_\suffix |
| cmpb LITERAL(70), (%rbx) // Test if result type char == 'F'. |
| je .Lreturn_range_float_\suffix |
| /* resume execution of caller */ |
| .Ldone_return_range_\suffix: |
| .if \is_string_init |
| movzwl 4(rPC), %r11d // arguments |
| GET_VREG %esi, %r11 |
| UPDATE_REGISTERS_FOR_STRING_INIT %esi, %eax |
| .endif |
| |
| .if \is_polymorphic |
| ADVANCE_PC_FETCH_AND_GOTO_NEXT 4 |
| .else |
| ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 |
| .endif |
| .Lreturn_range_double_\suffix: |
| movq %xmm0, %rax |
| jmp .Ldone_return_range_\suffix |
| .Lreturn_range_float_\suffix: |
| movd %xmm0, %eax |
| jmp .Ldone_return_range_\suffix |
| .endm |
| |
| // Helper for static field get. |
| .macro OP_SGET load="movl", wide="0" |
| // Fast-path which gets the field from thread-local cache. |
| % fetch_from_thread_cache("%rax", miss_label="2f") |
| 1: |
| movl ART_FIELD_OFFSET_OFFSET(%rax), %edx |
| movl ART_FIELD_DECLARING_CLASS_OFFSET(%rax), %eax |
| cmpq $$0, rSELF:THREAD_READ_BARRIER_MARK_REG00_OFFSET |
| jne 3f |
| 4: |
| .if \wide |
| movq (%eax,%edx,1), %rax |
| SET_WIDE_VREG %rax, rINSTq # fp[A] <- value |
| .else |
| \load (%eax, %edx, 1), %eax |
| SET_VREG %eax, rINSTq # fp[A] <- value |
| .endif |
| ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 |
| 2: |
| EXPORT_PC |
| movq rSELF:THREAD_SELF_OFFSET, %rdi |
| movq 0(%rsp), %rsi |
| movq rPC, %rdx |
| movq $$0, %rcx |
| call nterp_get_static_field |
| // Clear the marker that we put for volatile fields. The x86 memory |
| // model doesn't require a barrier. |
| andq $$-2, %rax |
| jmp 1b |
| 3: |
| call art_quick_read_barrier_mark_reg00 |
| 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_from_thread_cache("%rax", miss_label="2f") |
| 1: |
| movl ART_FIELD_OFFSET_OFFSET(%rax), %edx |
| movl ART_FIELD_DECLARING_CLASS_OFFSET(%rax), %eax |
| cmpq $$0, rSELF:THREAD_READ_BARRIER_MARK_REG00_OFFSET |
| jne 3f |
| 4: |
| .if \wide |
| GET_WIDE_VREG rINSTq, rINSTq # rINST <- v[A] |
| .else |
| GET_VREG rINST, rINSTq # rINST <- v[A] |
| .endif |
| \store \rINST_reg, (%rax,%rdx,1) |
| ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 |
| 2: |
| EXPORT_PC |
| movq rSELF:THREAD_SELF_OFFSET, %rdi |
| movq 0(%rsp), %rsi |
| movq rPC, %rdx |
| movq $$0, %rcx |
| call nterp_get_static_field |
| testq MACRO_LITERAL(1), %rax |
| je 1b |
| // Clear the marker that we put for volatile fields. The x86 memory |
| // model doesn't require a barrier. |
| CLEAR_VOLATILE_MARKER %rax |
| movl ART_FIELD_OFFSET_OFFSET(%rax), %edx |
| movl ART_FIELD_DECLARING_CLASS_OFFSET(%rax), %eax |
| cmpq $$0, rSELF:THREAD_READ_BARRIER_MARK_REG00_OFFSET |
| jne 6f |
| 5: |
| .if \wide |
| GET_WIDE_VREG rINSTq, rINSTq # rINST <- v[A] |
| .else |
| GET_VREG rINST, rINSTq # rINST <- v[A] |
| .endif |
| \store \rINST_reg, (%rax,%rdx,1) |
| lock addl $$0, (%rsp) |
| ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 |
| 3: |
| call art_quick_read_barrier_mark_reg00 |
| jmp 4b |
| 6: |
| call art_quick_read_barrier_mark_reg00 |
| jmp 5b |
| .endm |
| |
| |
| .macro OP_IPUT_INTERNAL rINST_reg="rINST", store="movl", wide="0": |
| movzbq rINSTbl, %rcx # rcx <- BA |
| sarl $$4, %ecx # ecx <- B |
| GET_VREG %ecx, %rcx # vB (object we're operating on) |
| testl %ecx, %ecx # is object null? |
| je common_errNullObject |
| andb $$0xf, rINSTbl # rINST <- A |
| .if \wide |
| GET_WIDE_VREG rINSTq, rINSTq # rax<- fp[A]/fp[A+1] |
| .else |
| GET_VREG rINST, rINSTq # rINST <- v[A] |
| .endif |
| \store \rINST_reg, (%rcx,%rax,1) |
| .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("%rax", miss_label="2f") |
| 1: |
| OP_IPUT_INTERNAL \rINST_reg, \store, \wide |
| ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 |
| 2: |
| EXPORT_PC |
| movq rSELF:THREAD_SELF_OFFSET, %rdi |
| movq 0(%rsp), %rsi |
| movq rPC, %rdx |
| movq $$0, %rcx |
| call nterp_get_instance_field_offset |
| testl %eax, %eax |
| jns 1b |
| negl %eax |
| OP_IPUT_INTERNAL \rINST_reg, \store, \wide |
| lock addl $$0, (%rsp) |
| 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("%rax", miss_label="2f") |
| 1: |
| movl rINST, %ecx # rcx <- BA |
| sarl $$4, %ecx # ecx <- B |
| GET_VREG %ecx, %rcx # vB (object we're operating on) |
| testl %ecx, %ecx # is object null? |
| je common_errNullObject |
| andb $$0xf,rINSTbl # rINST <- A |
| .if \wide |
| movq (%rcx,%rax,1), %rax |
| SET_WIDE_VREG %rax, rINSTq # fp[A] <- value |
| .else |
| \load (%rcx,%rax,1), %eax |
| SET_VREG %eax, rINSTq # fp[A] <- value |
| .endif |
| ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 |
| 2: |
| EXPORT_PC |
| movq rSELF:THREAD_SELF_OFFSET, %rdi |
| movq 0(%rsp), %rsi |
| movq rPC, %rdx |
| movq $$0, %rcx |
| call nterp_get_instance_field_offset |
| testl %eax, %eax |
| jns 1b |
| negl %eax |
| jmp 1b |
| .endm |
| |
| .macro SETUP_REFERENCE_PARAMETER_IN_GPR gpr32, regs, refs, ins, arg_offset, finished |
| movl REG_VAR(gpr32), (REG_VAR(regs), REG_VAR(arg_offset)) |
| movl REG_VAR(gpr32), (REG_VAR(refs), REG_VAR(arg_offset)) |
| addq MACRO_LITERAL(4), REG_VAR(arg_offset) |
| subl MACRO_LITERAL(1), REG_VAR(ins) |
| je \finished |
| .endm |
| |
| // Uses eax as temporary |
| .macro SETUP_REFERENCE_PARAMETERS_IN_STACK regs, refs, ins, stack_ptr, arg_offset |
| 1: |
| movl OFFSET_TO_FIRST_ARGUMENT_IN_STACK(REG_VAR(stack_ptr), REG_VAR(arg_offset)), %eax |
| movl %eax, (REG_VAR(regs), REG_VAR(arg_offset)) |
| movl %eax, (REG_VAR(refs), REG_VAR(arg_offset)) |
| addq MACRO_LITERAL(4), REG_VAR(arg_offset) |
| subl MACRO_LITERAL(1), REG_VAR(ins) |
| jne 1b |
| .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(%rdi) |
| jz \if_hot |
| movzwl rSELF:THREAD_SHARED_METHOD_HOTNESS_OFFSET, %esi |
| testl %esi, %esi |
| je \if_hot |
| addl $$-1, %esi |
| movw %si, rSELF:THREAD_SHARED_METHOD_HOTNESS_OFFSET |
| jmp \if_not_hot |
| .endm |
| |
| .macro DO_SUSPEND_CHECK continue_label |
| testl $$(THREAD_SUSPEND_OR_CHECKPOINT_REQUEST), rSELF:THREAD_FLAGS_OFFSET |
| jz \continue_label |
| EXPORT_PC |
| call SYMBOL(art_quick_test_suspend) |
| .endm |
| |
| %def entry(): |
| /* |
| * ArtMethod entry point. |
| * |
| * On entry: |
| * rdi 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. |
| movl ART_METHOD_DECLARING_CLASS_OFFSET(%rdi), %r10d |
| cmpl $$(MIRROR_CLASS_STATUS_VISIBLY_INITIALIZED_SHIFTED), MIRROR_CLASS_STATUS_OFFSET(%r10d) |
| jae ExecuteNterpImpl |
| cmpl $$(MIRROR_CLASS_STATUS_INITIALIZING_SHIFTED), MIRROR_CLASS_STATUS_OFFSET(%r10d) |
| jb art_quick_resolution_trampoline |
| movl MIRROR_CLASS_CLINIT_THREAD_ID_OFFSET(%r10d), %r10d |
| cmpl %r10d, rSELF:THREAD_TID_OFFSET |
| je ExecuteNterpImpl |
| jmp art_quick_resolution_trampoline |
| .cfi_endproc |
| .global SYMBOL(EndExecuteNterpWithClinitImpl) |
| SYMBOL(EndExecuteNterpWithClinitImpl): |
| |
| OAT_ENTRY ExecuteNterpImpl |
| .cfi_startproc |
| .cfi_def_cfa rsp, 8 |
| testq %rax, -STACK_OVERFLOW_RESERVED_BYTES(%rsp) |
| /* Spill callee save regs */ |
| SPILL_ALL_CALLEE_SAVES |
| |
| movq ART_METHOD_DATA_OFFSET_64(%rdi), rPC |
| |
| // Setup the stack for executing the method. |
| SETUP_STACK_FRAME rPC, rREFS, rREFS32, rFP, CFI_REFS, load_ins=1 |
| |
| // Setup the parameters |
| testl %r14d, %r14d |
| je .Lxmm_setup_finished |
| |
| subq %r14, %rbx |
| salq $$2, %rbx // rbx is now the offset for inputs into the registers array. |
| |
| testl $$ART_METHOD_NTERP_ENTRY_POINT_FAST_PATH_FLAG, ART_METHOD_ACCESS_FLAGS_OFFSET(%rdi) |
| je .Lsetup_slow_path |
| leaq (rFP, %rbx, 1), %rdi |
| leaq (rREFS, %rbx, 1), %rbx |
| movq $$0, %r10 |
| |
| SETUP_REFERENCE_PARAMETER_IN_GPR esi, rdi, rbx, r14d, r10, .Lxmm_setup_finished |
| SETUP_REFERENCE_PARAMETER_IN_GPR edx, rdi, rbx, r14d, r10, .Lxmm_setup_finished |
| SETUP_REFERENCE_PARAMETER_IN_GPR ecx, rdi, rbx, r14d, r10, .Lxmm_setup_finished |
| SETUP_REFERENCE_PARAMETER_IN_GPR r8d, rdi, rbx, r14d, r10, .Lxmm_setup_finished |
| SETUP_REFERENCE_PARAMETER_IN_GPR r9d, rdi, rbx, r14d, r10, .Lxmm_setup_finished |
| SETUP_REFERENCE_PARAMETERS_IN_STACK rdi, rbx, r14d, r11, r10 |
| 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(%rdi) |
| jne .Lsetup_with_shorty |
| |
| movl %esi, (rFP, %rbx) |
| movl %esi, (rREFS, %rbx) |
| |
| cmpl $$1, %r14d |
| je .Lxmm_setup_finished |
| |
| .Lsetup_with_shorty: |
| // TODO: Get shorty in a better way and remove below |
| push %rdi |
| push %rsi |
| push %rdx |
| push %rcx |
| push %r8 |
| push %r9 |
| |
| // Save xmm registers + alignment. |
| subq MACRO_LITERAL(8 * 8 + 8), %rsp |
| movq %xmm0, 0(%rsp) |
| movq %xmm1, 8(%rsp) |
| movq %xmm2, 16(%rsp) |
| movq %xmm3, 24(%rsp) |
| movq %xmm4, 32(%rsp) |
| movq %xmm5, 40(%rsp) |
| movq %xmm6, 48(%rsp) |
| movq %xmm7, 56(%rsp) |
| |
| call SYMBOL(NterpGetShorty) |
| // Save shorty in callee-save rbp. |
| movq %rax, %rbp |
| |
| // Restore xmm registers + alignment. |
| movq 0(%rsp), %xmm0 |
| movq 8(%rsp), %xmm1 |
| movq 16(%rsp), %xmm2 |
| movq 24(%rsp), %xmm3 |
| movq 32(%rsp), %xmm4 |
| movq 40(%rsp), %xmm5 |
| movq 48(%rsp), %xmm6 |
| movq 56(%rsp), %xmm7 |
| addq MACRO_LITERAL(8 * 8 + 8), %rsp |
| |
| pop %r9 |
| pop %r8 |
| pop %rcx |
| pop %rdx |
| pop %rsi |
| pop %rdi |
| // Reload the old stack pointer, which used to be stored in %r11, which is not callee-saved. |
| movq -8(rREFS), %r11 |
| // TODO: Get shorty in a better way and remove above |
| |
| movq $$0, %r14 |
| testl $$ART_METHOD_IS_STATIC_FLAG, ART_METHOD_ACCESS_FLAGS_OFFSET(%rdi) |
| |
| // Available: rdi, r10 |
| // Note the leaq below don't change the flags. |
| leaq 1(%rbp), %r10 // shorty + 1 ; ie skip return arg character |
| leaq (rFP, %rbx, 1), %rdi |
| leaq (rREFS, %rbx, 1), %rbx |
| jne .Lhandle_static_method |
| addq $$4, %rdi |
| addq $$4, %rbx |
| addq $$4, %r11 |
| jmp .Lcontinue_setup_gprs |
| .Lhandle_static_method: |
| LOOP_OVER_SHORTY_STORING_GPRS rsi, esi, r10, r14, rdi, rbx, .Lgpr_setup_finished |
| .Lcontinue_setup_gprs: |
| LOOP_OVER_SHORTY_STORING_GPRS rdx, edx, r10, r14, rdi, rbx, .Lgpr_setup_finished |
| LOOP_OVER_SHORTY_STORING_GPRS rcx, ecx, r10, r14, rdi, rbx, .Lgpr_setup_finished |
| LOOP_OVER_SHORTY_STORING_GPRS r8, r8d, r10, r14, rdi, rbx, .Lgpr_setup_finished |
| LOOP_OVER_SHORTY_STORING_GPRS r9, r9d, r10, r14, rdi, rbx, .Lgpr_setup_finished |
| LOOP_OVER_INTs r10, r14, rdi, rbx, r11, .Lgpr_setup_finished |
| .Lgpr_setup_finished: |
| leaq 1(%rbp), %r10 // shorty + 1 ; ie skip return arg character |
| movq $$0, %r14 // reset counter |
| LOOP_OVER_SHORTY_STORING_XMMS xmm0, r10, r14, rdi, .Lxmm_setup_finished |
| LOOP_OVER_SHORTY_STORING_XMMS xmm1, r10, r14, rdi, .Lxmm_setup_finished |
| LOOP_OVER_SHORTY_STORING_XMMS xmm2, r10, r14, rdi, .Lxmm_setup_finished |
| LOOP_OVER_SHORTY_STORING_XMMS xmm3, r10, r14, rdi, .Lxmm_setup_finished |
| LOOP_OVER_SHORTY_STORING_XMMS xmm4, r10, r14, rdi, .Lxmm_setup_finished |
| LOOP_OVER_SHORTY_STORING_XMMS xmm5, r10, r14, rdi, .Lxmm_setup_finished |
| LOOP_OVER_SHORTY_STORING_XMMS xmm6, r10, r14, rdi, .Lxmm_setup_finished |
| LOOP_OVER_SHORTY_STORING_XMMS xmm7, r10, r14, rdi, .Lxmm_setup_finished |
| LOOP_OVER_FPs r10, r14, rdi, r11, .Lxmm_setup_finished |
| .Lxmm_setup_finished: |
| CFI_DEFINE_DEX_PC_WITH_OFFSET(CFI_TMP, CFI_DEX, 0) |
| |
| // Set rIBASE |
| leaq artNterpAsmInstructionStart(%rip), rIBASE |
| /* start executing the instruction at rPC */ |
| START_EXECUTING_INSTRUCTIONS |
| /* NOTE: no fallthrough */ |
| // cfi info continues, and covers the whole nterp implementation. |
| END ExecuteNterpImpl |
| |
| %def opcode_pre(): |
| |
| %def fetch_from_thread_cache(dest_reg, miss_label): |
| // Fetch some information from the thread cache. |
| // Uses rax, rdx, rcx as temporaries. |
| movq rSELF:THREAD_SELF_OFFSET, %rax |
| movq rPC, %rdx |
| salq MACRO_LITERAL(THREAD_INTERPRETER_CACHE_SIZE_SHIFT), %rdx |
| andq MACRO_LITERAL(THREAD_INTERPRETER_CACHE_SIZE_MASK), %rdx |
| cmpq THREAD_INTERPRETER_CACHE_OFFSET(%rax, %rdx, 1), rPC |
| jne ${miss_label} |
| movq __SIZEOF_POINTER__+THREAD_INTERPRETER_CACHE_OFFSET(%rax, %rdx, 1), ${dest_reg} |
| |
| %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 C compiler compiled each interpreter separately. |
| common_errDivideByZero: |
| EXPORT_PC |
| call art_quick_throw_div_zero |
| |
| // Expect array in edi, index in esi. |
| common_errArrayIndex: |
| EXPORT_PC |
| movl MIRROR_ARRAY_LENGTH_OFFSET(%edi), %eax |
| movl %esi, %edi |
| movl %eax, %esi |
| call art_quick_throw_array_bounds |
| |
| 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_string_init=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_string_init=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("%rdi", miss_label="2f") |
| cmpq $$0, rSELF:THREAD_READ_BARRIER_MARK_REG00_OFFSET |
| jne 3f |
| 4: |
| callq *rSELF:THREAD_ALLOC_OBJECT_ENTRYPOINT_OFFSET |
| 1: |
| SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value |
| ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 |
| 2: |
| movq rSELF:THREAD_SELF_OFFSET, %rdi |
| movq 0(%rsp), %rsi |
| movq rPC, %rdx |
| call nterp_allocate_object |
| jmp 1b |
| 3: |
| // 07 is %rdi |
| call art_quick_read_barrier_mark_reg07 |
| 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("%rdi", miss_label="2f") |
| cmpq $$0, rSELF:THREAD_READ_BARRIER_MARK_REG00_OFFSET |
| jne 3f |
| 1: |
| movzbl rINSTbl,%esi |
| sarl $$4,%esi # esi<- B |
| GET_VREG %esi %rsi # esi<- vB (array length) |
| andb $$0xf,rINSTbl # rINST<- A |
| callq *rSELF:THREAD_ALLOC_ARRAY_ENTRYPOINT_OFFSET |
| SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value |
| ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 |
| 2: |
| movq rSELF:THREAD_SELF_OFFSET, %rdi |
| movq 0(%rsp), %rsi |
| movq rPC, %rdx |
| call nterp_get_class |
| movq %rax, %rdi |
| jmp 1b |
| 3: |
| // 07 is %rdi |
| call art_quick_read_barrier_mark_reg07 |
| jmp 1b |
| |
| NterpPutObjectInstanceField: |
| movl rINST, %ebp # rbp <- BA |
| andl $$0xf, %ebp # rbp <- A |
| GET_VREG %ecx, %rbp # ecx <- v[A] |
| sarl $$4, rINST |
| // Fast-path which gets the field from thread-local cache. |
| % fetch_from_thread_cache("%rax", miss_label="2f") |
| 1: |
| GET_VREG rINST, rINSTq # vB (object we're operating on) |
| testl rINST, rINST # is object null? |
| je common_errNullObject |
| POISON_HEAP_REF ecx |
| movl %ecx, (rINSTq,%rax,1) |
| testl %ecx, %ecx |
| je 4f |
| movq rSELF:THREAD_CARD_TABLE_OFFSET, %rax |
| shrq $$CARD_TABLE_CARD_SHIFT, rINSTq |
| movb %al, (%rax, rINSTq, 1) |
| 4: |
| ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 |
| 2: |
| EXPORT_PC |
| movq rSELF:THREAD_SELF_OFFSET, %rdi |
| movq 0(%rsp), %rsi |
| movq rPC, %rdx |
| // %rcx is already set. |
| call nterp_get_instance_field_offset |
| // Reload the value as it may have moved. |
| GET_VREG %ecx, %rbp # ecx <- v[A] |
| testl %eax, %eax |
| jns 1b |
| GET_VREG rINST, rINSTq # vB (object we're operating on) |
| testl rINST, rINST # is object null? |
| je common_errNullObject |
| negl %eax |
| POISON_HEAP_REF ecx |
| movl %ecx, (rINSTq,%rax,1) |
| testl %ecx, %ecx |
| je 5f |
| movq rSELF:THREAD_CARD_TABLE_OFFSET, %rax |
| shrq $$CARD_TABLE_CARD_SHIFT, rINSTq |
| movb %al, (%rax, rINSTq, 1) |
| 5: |
| lock addl $$0, (%rsp) |
| ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 |
| |
| NterpGetObjectInstanceField: |
| // Fast-path which gets the field from thread-local cache. |
| % fetch_from_thread_cache("%rax", miss_label="2f") |
| 1: |
| movl rINST, %ecx # rcx <- BA |
| sarl $$4, %ecx # ecx <- B |
| GET_VREG %ecx, %rcx # 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 (%rcx,%rax,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, rINSTq # fp[A] <- value |
| ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 |
| 2: |
| EXPORT_PC |
| movq rSELF:THREAD_SELF_OFFSET, %rdi |
| movq 0(%rsp), %rsi |
| movq rPC, %rdx |
| movq $$0, %rcx |
| 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: |
| GET_VREG %ebp, rINSTq |
| // Fast-path which gets the field from thread-local cache. |
| % fetch_from_thread_cache("%rax", miss_label="2f") |
| 1: |
| movl ART_FIELD_OFFSET_OFFSET(%rax), %edx |
| movl ART_FIELD_DECLARING_CLASS_OFFSET(%rax), %eax |
| cmpq $$0, rSELF:THREAD_READ_BARRIER_MARK_REG00_OFFSET |
| jne 3f |
| 5: |
| POISON_HEAP_REF ebp |
| movl %ebp, (%eax, %edx, 1) |
| testl %ebp, %ebp |
| je 4f |
| movq rSELF:THREAD_CARD_TABLE_OFFSET, %rcx |
| shrq $$CARD_TABLE_CARD_SHIFT, %rax |
| movb %cl, (%rax, %rcx, 1) |
| 4: |
| ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 |
| 2: |
| EXPORT_PC |
| movq rSELF:THREAD_SELF_OFFSET, %rdi |
| movq 0(%rsp), %rsi |
| movq rPC, %rdx |
| movq %rbp, %rcx |
| call nterp_get_static_field |
| // Reload the value as it may have moved. |
| GET_VREG %ebp, rINSTq |
| testq MACRO_LITERAL(1), %rax |
| je 1b |
| CLEAR_VOLATILE_MARKER %rax |
| movl ART_FIELD_OFFSET_OFFSET(%rax), %edx |
| movl ART_FIELD_DECLARING_CLASS_OFFSET(%rax), %eax |
| cmpq $$0, rSELF:THREAD_READ_BARRIER_MARK_REG00_OFFSET |
| jne 7f |
| 6: |
| POISON_HEAP_REF ebp |
| movl %ebp, (%eax, %edx, 1) |
| testl %ebp, %ebp |
| je 8f |
| movq rSELF:THREAD_CARD_TABLE_OFFSET, %rcx |
| shrq $$CARD_TABLE_CARD_SHIFT, %rax |
| movb %cl, (%rax, %rcx, 1) |
| 8: |
| lock addl $$0, (%rsp) |
| ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 |
| 3: |
| call art_quick_read_barrier_mark_reg00 |
| jmp 5b |
| 7: |
| call art_quick_read_barrier_mark_reg00 |
| jmp 6b |
| |
| NterpGetObjectStaticField: |
| // Fast-path which gets the field from thread-local cache. |
| % fetch_from_thread_cache("%rax", miss_label="2f") |
| 1: |
| movl ART_FIELD_OFFSET_OFFSET(%rax), %edx |
| movl ART_FIELD_DECLARING_CLASS_OFFSET(%rax), %eax |
| cmpq $$0, rSELF:THREAD_READ_BARRIER_MARK_REG00_OFFSET |
| jne 5f |
| 6: |
| testb $$READ_BARRIER_TEST_VALUE, GRAY_BYTE_OFFSET(%eax) |
| movl (%eax, %edx, 1), %eax |
| jnz 3f |
| UNPOISON_HEAP_REF eax // Affects flags, so we cannot unpoison before the jnz. |
| 4: |
| SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value |
| ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 |
| 2: |
| EXPORT_PC |
| movq rSELF:THREAD_SELF_OFFSET, %rdi |
| movq 0(%rsp), %rsi |
| movq rPC, %rdx |
| movq $$0, %rcx |
| call nterp_get_static_field |
| andq $$-2, %rax |
| jmp 1b |
| 3: |
| UNPOISON_HEAP_REF eax |
| call art_quick_read_barrier_mark_reg00 |
| jmp 4b |
| 5: |
| call art_quick_read_barrier_mark_reg00 |
| jmp 6b |
| |
| NterpGetBooleanStaticField: |
| OP_SGET load="movsbl", 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="movq", 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=rINSTq, store="movq", 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=rINSTq, store="movq", 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="movq", wide=1 |
| |
| NterpGetInstanceField: |
| OP_IGET load="movl", wide=0 |
| |
| NterpHandleHotnessOverflow: |
| CHECK_AND_UPDATE_SHARED_MEMORY_METHOD if_hot=1f, if_not_hot=4f |
| 1: |
| movq rPC, %rsi |
| movq rFP, %rdx |
| call nterp_hot_method |
| testq %rax, %rax |
| jne 3f |
| 2: |
| FETCH_INST |
| GOTO_NEXT |
| 3: |
| // Drop the current frame. |
| movq -8(rREFS), %rsp |
| CFI_DEF_CFA(rsp, CALLEE_SAVES_SIZE) |
| |
| // Setup the new frame |
| movq OSR_DATA_FRAME_SIZE(%rax), %rcx |
| // Given stack size contains all callee saved registers, remove them. |
| subq $$CALLEE_SAVES_SIZE, %rcx |
| |
| // Remember CFA. |
| movq %rsp, %rbp |
| CFI_DEF_CFA_REGISTER(rbp) |
| |
| subq %rcx, %rsp |
| movq %rsp, %rdi // rdi := beginning of stack |
| leaq OSR_DATA_MEMORY(%rax), %rsi // rsi := memory to copy |
| rep movsb // while (rcx--) { *rdi++ = *rsi++ } |
| |
| // Fetch the native PC to jump to and save it in a callee-save register. |
| movq OSR_DATA_NATIVE_PC(%rax), %rbx |
| |
| // Free the memory holding OSR Data. |
| movq %rax, %rdi |
| call free |
| |
| // Jump to the compiled code. |
| jmp *%rbx |
| 4: |
| DO_SUSPEND_CHECK continue_label=2b |
| jmp 2b |
| |
| NterpHandleInvokeInterfaceOnObjectMethodRange: |
| shrl $$16, %eax |
| movq MIRROR_CLASS_VTABLE_OFFSET_64(%edx, %eax, 8), %rdi |
| jmp NterpCommonInvokeInstanceRange |
| |
| NterpHandleInvokeInterfaceOnObjectMethod: |
| shrl $$16, %eax |
| movq MIRROR_CLASS_VTABLE_OFFSET_64(%edx, %eax, 8), %rdi |
| 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 |
| |
| nterp_to_nterp_static_non_range: |
| .cfi_startproc |
| .cfi_def_cfa rsp, 8 |
| 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 |
| .cfi_def_cfa rsp, 8 |
| 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 |
| .cfi_def_cfa rsp, 8 |
| 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 |
| .cfi_def_cfa rsp, 8 |
| SETUP_STACK_FOR_INVOKE |
| SETUP_RANGE_ARGUMENTS_AND_EXECUTE is_static=1 |
| .cfi_endproc |
| |
| nterp_to_nterp_instance_range: |
| .cfi_startproc |
| .cfi_def_cfa rsp, 8 |
| SETUP_STACK_FOR_INVOKE |
| SETUP_RANGE_ARGUMENTS_AND_EXECUTE is_static=0 |
| .cfi_endproc |
| |
| nterp_to_nterp_string_init_range: |
| .cfi_startproc |
| .cfi_def_cfa rsp, 8 |
| SETUP_STACK_FOR_INVOKE |
| SETUP_RANGE_ARGUMENTS_AND_EXECUTE is_static=0, is_string_init=1 |
| .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 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. |
| FETCH_INST |
| GOTO_NEXT |
| |
| %def instruction_start(): |
| |
| FUNCTION_TYPE(artNterpAsmInstructionStart) |
| ASM_HIDDEN SYMBOL(artNterpAsmInstructionStart) |
| .global SYMBOL(artNterpAsmInstructionStart) |
| SYMBOL(artNterpAsmInstructionStart) = .L_op_nop |
| .text |
| |
| %def opcode_name_prefix(): |
| % return "nterp_" |
| %def opcode_start(): |
| ENTRY nterp_${opcode} |
| %def opcode_end(): |
| END nterp_${opcode} |
| // Advance to the end of this handler. Causes error if we are past that point. |
| .org nterp_${opcode} + NTERP_HANDLER_SIZE // ${opcode} handler is too big! |
| %def opcode_slow_path_start(name): |
| ENTRY ${name} |
| %def opcode_slow_path_end(name): |
| END ${name} |