| /* Copyright (C) 2008 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. |
| */ |
| |
| /* |
| * File: header.S |
| */ |
| |
| /* |
| * IA32 calling convention and general notes: |
| * |
| * EAX, ECX, EDX - general purpose scratch registers (caller-saved); |
| * |
| * The stack (%esp) - used to pass arguments to functions |
| * |
| * EAX - holds the first 4 bytes of a return |
| * EDX - holds the second 4 bytes of a return |
| * |
| * EBX, ESI, EDI, EBP - are callee saved |
| * |
| * CS, DS, SS - are segment registers |
| * ES, FS, GS - are segment registers. We will try to avoid using these registers |
| * |
| * The stack is "full descending". Only the arguments that do not fit * in the first two arg registers are placed on the stack. |
| * "%esp" points to the first stacked argument (i.e. the 3rd arg). |
| */ |
| |
| /* |
| * Mterp and IA32 notes |
| * |
| * mem nick purpose |
| * (%ebp) rGLUE InterpState base pointer (A.K.A. MterpGlue Pointer) |
| * %esi rPC interpreted program counter, used for fetching |
| * instructions |
| * %ebx rINST first 16-bit code unit of current instruction |
| * %edi rFP interpreted frame pointer, used for accessing |
| * locals and args |
| */ |
| |
| /* |
| * Includes |
| */ |
| |
| #include "../common/asm-constants.h" |
| |
| /* |
| * Reserved registers |
| */ |
| |
| #define rGLUE (%ebp) |
| #define rINST %ebx |
| #define rINSTbl %bl |
| #define rINSTbh %bh |
| #define rINSTw %bx |
| #define rPC %esi |
| #define rFP %edi |
| |
| /* |
| * Temporary register used when finishing an opcode |
| */ |
| |
| #define rFinish %edx |
| |
| /* |
| * Stack locations used for temporary data. For convenience. |
| */ |
| |
| #define sReg0 4(%ebp) |
| #define sReg1 8(%ebp) |
| #define sReg2 12(%ebp) |
| #define sReg3 16(%ebp) |
| |
| /* |
| * Save the PC and FP to the glue struct |
| */ |
| |
| .macro SAVE_PC_FP_TO_GLUE _reg |
| movl rGLUE, \_reg |
| movl rPC, offGlue_pc(\_reg) |
| movl rFP, offGlue_fp(\_reg) |
| .endm |
| |
| /* |
| * Restore the PC and FP from the glue struct |
| */ |
| |
| .macro LOAD_PC_FP_FROM_GLUE |
| movl rGLUE, rFP |
| movl offGlue_pc(rFP), rPC |
| movl offGlue_fp(rFP), rFP |
| .endm |
| |
| /* |
| * "Export" the PC to the stack frame, f/b/o future exception objects. This must |
| * be done *before* something calls dvmThrowException. |
| * |
| * In C this is "SAVEAREA_FROM_FP(fp)->xtra.currentPc = pc", i.e. |
| * fp - sizeof(StackSaveArea) + offsetof(SaveArea, xtra.currentPc) |
| * |
| * It's okay to do this more than once. |
| */ |
| |
| .macro EXPORT_PC |
| movl rPC, (-sizeofStackSaveArea + offStackSaveArea_currentPc)(rFP) |
| .endm |
| |
| /* |
| * Given a frame pointer, find the stack save area. |
| * In C this is "((StackSaveArea*)(_fp) -1)". |
| */ |
| |
| .macro SAVEAREA_FROM_FP _reg |
| lea -sizeofStackSaveArea(rFP), \_reg |
| .endm |
| |
| /* |
| * Get the 32-bit value from a dalvik register. |
| */ |
| |
| .macro GET_VREG _vreg |
| movl (rFP,\_vreg, 4), \_vreg |
| .endm |
| |
| /* |
| * Set the 32-bit value from a dalvik register. |
| */ |
| |
| .macro SET_VREG _reg _vreg |
| movl \_reg, (rFP,\_vreg, 4) |
| .endm |
| |
| /* |
| * Fetch the next instruction from rPC into rINST. Does not advance rPC. |
| */ |
| |
| .macro FETCH_INST |
| movzwl (rPC), rINST |
| .endm |
| |
| /* |
| * Fetch the next instruction from the specified offset. Advances rPC |
| * to point to the next instruction. "_count" is in 16-bit code units. |
| * |
| * 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 |
| add $$(\_count*2), rPC |
| movzwl (rPC), rINST |
| .endm |
| |
| /* |
| * Fetch the next instruction from an offset specified by _reg. Updates |
| * rPC 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 |
| addl \_reg, rPC |
| movzwl (rPC), rINST |
| .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 rPC. |
| * For example, given instruction of format: AA|op BBBB, it |
| * fetches BBBB. |
| */ |
| |
| .macro FETCH _count _reg |
| movzwl (\_count*2)(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 rPC. |
| * This variant treats the value as signed. |
| */ |
| |
| .macro FETCHs _count _reg |
| movswl (\_count*2)(rPC), \_reg |
| .endm |
| |
| /* |
| * Fetch the first byte from an offset past the current PC. The |
| * "_count" value is in 16-bit code units. Does not advance rPC. |
| * For example, given instruction of format: AA|op CC|BB, it |
| * fetches BB. |
| */ |
| |
| .macro FETCH_BB _count _reg |
| movzbl (\_count*2)(rPC), \_reg |
| .endm |
| |
| /* |
| * Fetch the second byte from an offset past the current PC. The |
| * "_count" value is in 16-bit code units. Does not advance rPC. |
| * For example, given instruction of format: AA|op CC|BB, it |
| * fetches CC. |
| */ |
| |
| .macro FETCH_CC _count _reg |
| movzbl (\_count*2 + 1)(rPC), \_reg |
| .endm |
| |
| /* |
| * Fetch the second byte from an offset past the current PC. The |
| * "_count" value is in 16-bit code units. Does not advance rPC. |
| * This variant treats the value as signed. |
| */ |
| |
| .macro FETCH_CCs _count _reg |
| movsbl (\_count*2 + 1)(rPC), \_reg |
| .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 |
| movzbl (\_count*2+\_byte)(rPC), \_reg |
| .endm |
| |
| /* |
| * Put the instruction's opcode field into the specified register. |
| */ |
| |
| .macro GET_INST_OPCODE _reg |
| movzbl rINSTbl, \_reg |
| .endm |
| |
| /* |
| * Begin executing the opcode in _reg. |
| */ |
| |
| .macro GOTO_OPCODE _reg |
| shl $$${handler_size_bits}, \_reg |
| addl $$dvmAsmInstructionStart,\_reg |
| jmp *\_reg |
| .endm |
| |
| |
| |
| /* |
| * Macros pair attempts to speed up FETCH_INST, GET_INST_OPCODE and GOTO_OPCODE |
| * by using a jump table. _rFinish should must be the same register for |
| * both macros. |
| */ |
| |
| .macro FFETCH _rFinish |
| movzbl (rPC), \_rFinish |
| .endm |
| |
| .macro FGETOP_JMPa _rFinish |
| movzbl 1(rPC), rINST |
| jmp *dvmAsmInstructionJmpTable(,\_rFinish, 4) |
| .endm |
| |
| /* |
| * Macro pair attempts to speed up FETCH_INST, GET_INST_OPCODE and GOTO_OPCODE |
| * by using a jump table. _rFinish and _count should must be the same register for |
| * both macros. |
| */ |
| |
| .macro FFETCH_ADV _count _rFinish |
| movzbl (\_count*2)(rPC), \_rFinish |
| .endm |
| |
| .macro FGETOP_JMP _count _rFinish |
| movzbl (\_count*2 + 1)(rPC), rINST |
| addl $$(\_count*2), rPC |
| jmp *dvmAsmInstructionJmpTable(,\_rFinish, 4) |
| .endm |
| |
| .macro FGETOP_JMP2 _rFinish |
| movzbl 1(rPC), rINST |
| jmp *dvmAsmInstructionJmpTable(,\_rFinish, 4) |
| .endm |
| |
| .macro OLD_JMP_1 _count _rFinish |
| movzbl (\_count*2)(rPC), \_rFinish |
| shl $$${handler_size_bits}, \_rFinish |
| .endm |
| |
| .macro OLD_JMP_2 _rFinish |
| addl $$dvmAsmInstructionStart,\_rFinish |
| .endm |
| |
| .macro OLD_JMP_3 _count |
| addl $$(\_count*2), rPC |
| .endm |
| |
| .macro OLD_JMP_4 _rFinish |
| movzbl 1(rPC), rINST |
| jmp *\_rFinish |
| .endm |
| |
| .macro OLD_JMP_A_1 _reg _rFinish |
| movzbl (rPC, \_reg), \_rFinish |
| shl $$${handler_size_bits}, \_rFinish |
| .endm |
| |
| .macro OLD_JMP_A_2 _rFinish |
| addl $$dvmAsmInstructionStart,\_rFinish |
| .endm |
| |
| .macro OLD_JMP_A_3 _reg _rFinish |
| addl \_reg, rPC |
| movzbl 1(rPC, \_reg), rINST |
| jmp *\_rFinish |
| .endm |
| |
| /* |
| * Macro pair attempts to speed up FETCH_INST, GET_INST_OPCODE and GOTO_OPCODE |
| * by using a jump table. _rFinish and _reg should must be the same register for |
| * both macros. |
| */ |
| |
| .macro FFETCH_ADV_RB _reg _rFinish |
| movzbl (\_reg, rPC), \_rFinish |
| .endm |
| |
| .macro FGETOP_RB_JMP _reg _rFinish |
| movzbl 1(\_reg, rPC), rINST |
| addl \_reg, rPC |
| jmp *dvmAsmInstructionJmpTable(,\_rFinish, 4) |
| .endm |
| |
| /* |
| * Attempts to speed up FETCH_INST, GET_INST_OPCODE using |
| * a jump table. This macro should be called before FINISH_JMP where |
| * rFinish should be the same register containing the opcode value. |
| * This is an attempt to split up FINISH in order to reduce or remove |
| * potential stalls due to the wait for rFINISH. |
| */ |
| |
| .macro FINISH_FETCH _rFinish |
| movzbl (rPC), \_rFinish |
| movzbl 1(rPC), rINST |
| .endm |
| |
| |
| /* |
| * Attempts to speed up FETCH_ADVANCE_INST, GET_INST_OPCODE using |
| * a jump table. This macro should be called before FINISH_JMP where |
| * rFinish should be the same register containing the opcode value. |
| * This is an attempt to split up FINISH in order to reduce or remove |
| * potential stalls due to the wait for rFINISH. |
| */ |
| |
| .macro FINISH_FETCH_ADVANCE _count _rFinish |
| movzbl (\_count*2)(rPC), \_rFinish |
| movzbl (\_count*2 + 1)(rPC), rINST |
| addl $$(\_count*2), rPC |
| .endm |
| |
| /* |
| * Attempts to speed up FETCH_ADVANCE_INST_RB, GET_INST_OPCODE using |
| * a jump table. This macro should be called before FINISH_JMP where |
| * rFinish should be the same register containing the opcode value. |
| * This is an attempt to split up FINISH in order to reduce or remove |
| * potential stalls due to the wait for rFINISH. |
| */ |
| |
| .macro FINISH_FETCH_ADVANCE_RB _reg _rFinish |
| movzbl (\_reg, rPC), \_rFinish |
| movzbl 1(\_reg, rPC), rINST |
| addl \_reg, rPC |
| .endm |
| |
| /* |
| * Attempts to speed up GOTO_OPCODE using a jump table. This macro should |
| * be called after a FINISH_FETCH* instruction where rFinish should be the |
| * same register containing the opcode value. This is an attempt to split up |
| * FINISH in order to reduce or remove potential stalls due to the wait for rFINISH. |
| */ |
| |
| .macro FINISH_JMP _rFinish |
| jmp *dvmAsmInstructionJmpTable(,\_rFinish, 4) |
| .endm |
| |
| /* |
| * Attempts to speed up FETCH_INST, GET_INST_OPCODE, GOTO_OPCODE by using |
| * a jump table. Uses a single macro - but it should be faster if we |
| * split up the fetch for rFinish and the jump using rFinish. |
| */ |
| |
| .macro FINISH_A |
| movzbl (rPC), rFinish |
| movzbl 1(rPC), rINST |
| jmp *dvmAsmInstructionJmpTable(,rFinish, 4) |
| .endm |
| |
| /* |
| * Attempts to speed up FETCH_ADVANCE_INST, GET_INST_OPCODE, |
| * GOTO_OPCODE by using a jump table. Uses a single macro - |
| * but it should be faster if we split up the fetch for rFinish |
| * and the jump using rFinish. |
| */ |
| |
| .macro FINISH _count |
| movzbl (\_count*2)(rPC), rFinish |
| movzbl (\_count*2 + 1)(rPC), rINST |
| addl $$(\_count*2), rPC |
| jmp *dvmAsmInstructionJmpTable(,rFinish, 4) |
| .endm |
| |
| /* |
| * Attempts to speed up FETCH_ADVANCE_INST_RB, GET_INST_OPCODE, |
| * GOTO_OPCODE by using a jump table. Uses a single macro - |
| * but it should be faster if we split up the fetch for rFinish |
| * and the jump using rFinish. |
| */ |
| |
| .macro FINISH_RB _reg _rFinish |
| movzbl (\_reg, rPC), \_rFinish |
| movzbl 1(\_reg, rPC), rINST |
| addl \_reg, rPC |
| jmp *dvmAsmInstructionJmpTable(,\_rFinish, 4) |
| .endm |
| |
| /* |
| * Hard coded helper values. |
| */ |
| |
| .balign 16 |
| |
| .LdoubNeg: |
| .quad 0x8000000000000000 |
| |
| .L64bits: |
| .quad 0xFFFFFFFFFFFFFFFF |
| |
| .LshiftMask2: |
| .quad 0x0000000000000000 |
| .LshiftMask: |
| .quad 0x000000000000003F |
| |
| .Lvalue64: |
| .quad 0x0000000000000040 |
| |
| .LvaluePosInfLong: |
| .quad 0x7FFFFFFFFFFFFFFF |
| |
| .LvalueNegInfLong: |
| .quad 0x8000000000000000 |
| |
| .LvalueNanLong: |
| .quad 0x0000000000000000 |
| |
| .LintMin: |
| .long 0x80000000 |
| |
| .LintMax: |
| .long 0x7FFFFFFF |