diff options
Diffstat (limited to 'vm/mterp/x86/footer.S')
-rw-r--r-- | vm/mterp/x86/footer.S | 535 |
1 files changed, 535 insertions, 0 deletions
diff --git a/vm/mterp/x86/footer.S b/vm/mterp/x86/footer.S new file mode 100644 index 0000000..d43a662 --- /dev/null +++ b/vm/mterp/x86/footer.S @@ -0,0 +1,535 @@ +/* + * 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. + */ +/* + * Common subroutines and data. + */ + +/* + * Common code when a backwards branch is taken + * + * On entry: + * ebx (a.k.a. rINST_FULL) -> PC adjustment in 16-bit words + */ +common_backwardBranch: + GET_GLUE(%ecx) + call common_periodicChecks # Note: expects rPC to be preserved + ADVANCE_PC_INDEXED(rINST_FULL) + FETCH_INST() + GOTO_NEXT + + + +/* + * Common code for method invocation with range. + * + * On entry: + * eax = Method* methodToCall + * rINST trashed, must reload + */ + +common_invokeMethodRange: +.LinvokeNewRange: + + /* + * prepare to copy args to "outs" area of current frame + */ + + movzbl 1(rPC),rINST_FULL # rINST_FULL<- AA + movzwl 4(rPC), %ecx # %ecx<- CCCC + SPILL(rPC) + SAVEAREA_FROM_FP(%edx,rFP) # %edx<- &StackSaveArea + test rINST_FULL, rINST_FULL + movl rINST_FULL, LOCAL0_OFFSET(%ebp) # LOCAL0_OFFSET(%ebp)<- AA + jz .LinvokeArgsDone # no args; jump to args done + + + /* + * %eax=methodToCall, %ecx=CCCC, LOCAL0_OFFSET(%ebp)=count, %edx=&outs (&stackSaveArea) + * (very few methods have > 10 args; could unroll for common cases) + */ + + movl %ebx, LOCAL1_OFFSET(%ebp) # LOCAL1_OFFSET(%ebp)<- save %ebx + lea (rFP, %ecx, 4), %ecx # %ecx<- &vCCCC + shll $$2, LOCAL0_OFFSET(%ebp) # LOCAL0_OFFSET(%ebp)<- offset + subl LOCAL0_OFFSET(%ebp), %edx # %edx<- update &outs + shrl $$2, LOCAL0_OFFSET(%ebp) # LOCAL0_OFFSET(%ebp)<- offset +1: + movl (%ecx), %ebx # %ebx<- vCCCC + lea 4(%ecx), %ecx # %ecx<- &vCCCC++ + subl $$1, LOCAL0_OFFSET(%ebp) # LOCAL0_OFFSET<- LOCAL0_OFFSET-- + movl %ebx, (%edx) # *outs<- vCCCC + lea 4(%edx), %edx # outs++ + jne 1b # loop if count (LOCAL0_OFFSET(%ebp)) not zero + movl LOCAL1_OFFSET(%ebp), %ebx # %ebx<- restore %ebx + jmp .LinvokeArgsDone # continue + + /* + * %eax is "Method* methodToCall", the method we're trying to call + * prepare to copy args to "outs" area of current frame + */ + +common_invokeMethodNoRange: +.LinvokeNewNoRange: + movzbl 1(rPC),rINST_FULL # rINST_FULL<- BA + SPILL(rPC) + movl rINST_FULL, LOCAL0_OFFSET(%ebp) # LOCAL0_OFFSET(%ebp)<- BA + shrl $$4, LOCAL0_OFFSET(%ebp) # LOCAL0_OFFSET(%ebp)<- B + je .LinvokeArgsDone # no args; jump to args done + movzwl 4(rPC), %ecx # %ecx<- GFED + SAVEAREA_FROM_FP(%edx,rFP) # %edx<- &StackSaveArea + + /* + * %eax=methodToCall, %ecx=GFED, LOCAL0_OFFSET(%ebp)=count, %edx=outs + */ + +.LinvokeNonRange: + cmp $$2, LOCAL0_OFFSET(%ebp) # compare LOCAL0_OFFSET(%ebp) to 2 + movl %ecx, LOCAL1_OFFSET(%ebp) # LOCAL1_OFFSET(%ebp)<- GFED + jl 1f # handle 1 arg + je 2f # handle 2 args + cmp $$4, LOCAL0_OFFSET(%ebp) # compare LOCAL0_OFFSET(%ebp) to 4 + jl 3f # handle 3 args + je 4f # handle 4 args +5: + andl $$15, rINST_FULL # rINST<- A + lea -4(%edx), %edx # %edx<- update &outs; &outs-- + movl (rFP, rINST_FULL, 4), %ecx # %ecx<- vA + movl %ecx, (%edx) # *outs<- vA + movl LOCAL1_OFFSET(%ebp), %ecx # %ecx<- GFED +4: + shr $$12, %ecx # %ecx<- G + lea -4(%edx), %edx # %edx<- update &outs; &outs-- + movl (rFP, %ecx, 4), %ecx # %ecx<- vG + movl %ecx, (%edx) # *outs<- vG + movl LOCAL1_OFFSET(%ebp), %ecx # %ecx<- GFED +3: + and $$0x0f00, %ecx # %ecx<- 0F00 + shr $$8, %ecx # %ecx<- F + lea -4(%edx), %edx # %edx<- update &outs; &outs-- + movl (rFP, %ecx, 4), %ecx # %ecx<- vF + movl %ecx, (%edx) # *outs<- vF + movl LOCAL1_OFFSET(%ebp), %ecx # %ecx<- GFED +2: + and $$0x00f0, %ecx # %ecx<- 00E0 + shr $$4, %ecx # %ecx<- E + lea -4(%edx), %edx # %edx<- update &outs; &outs-- + movl (rFP, %ecx, 4), %ecx # %ecx<- vE + movl %ecx, (%edx) # *outs<- vE + movl LOCAL1_OFFSET(%ebp), %ecx # %ecx<- GFED +1: + and $$0x000f, %ecx # %ecx<- 000D + movl (rFP, %ecx, 4), %ecx # %ecx<- vD + movl %ecx, -4(%edx) # *--outs<- vD +0: + + /* + * %eax is "Method* methodToCall", the method we're trying to call + * find space for the new stack frame, check for overflow + */ + +.LinvokeArgsDone: + movzwl offMethod_registersSize(%eax), %edx # %edx<- methodToCall->regsSize + movzwl offMethod_outsSize(%eax), %ecx # %ecx<- methodToCall->outsSize + movl %eax, LOCAL0_OFFSET(%ebp) # LOCAL0_OFFSET<- methodToCall + shl $$2, %edx # %edx<- update offset + SAVEAREA_FROM_FP(%eax,rFP) # %eax<- &StackSaveArea + subl %edx, %eax # %eax<- newFP; (old savearea - regsSize) + GET_GLUE(%edx) # %edx<- pMterpGlue + movl %eax, LOCAL1_OFFSET(%ebp) # LOCAL1_OFFSET(%ebp)<- &outs + subl $$sizeofStackSaveArea, %eax # %eax<- newSaveArea (stack save area using newFP) + movl offGlue_interpStackEnd(%edx), %edx # %edx<- glue->interpStackEnd + movl %edx, LOCAL2_OFFSET(%ebp) # LOCAL2_OFFSET<- glue->interpStackEnd + shl $$2, %ecx # %ecx<- update offset for outsSize + movl %eax, %edx # %edx<- newSaveArea + sub %ecx, %eax # %eax<- bottom; (newSaveArea - outsSize) + cmp LOCAL2_OFFSET(%ebp), %eax # compare interpStackEnd and bottom + movl LOCAL0_OFFSET(%ebp), %eax # %eax<- restore methodToCall + jl .LstackOverflow # handle frame overflow + + /* + * set up newSaveArea + */ + +#ifdef EASY_GDB + SAVEAREA_FROM_FP(%ecx,rFP) # %ecx<- &StackSaveArea + movl %ecx, offStackSaveArea_prevSave(%edx) # newSaveArea->prevSave<- &outs +#endif + movl rFP, offStackSaveArea_prevFrame(%edx) # newSaveArea->prevFrame<- rFP + movl rPC_SPILL(%ebp), %ecx + movl %ecx, offStackSaveArea_savedPc(%edx) # newSaveArea->savedPc<- rPC + testl $$ACC_NATIVE, offMethod_accessFlags(%eax) # check for native call + movl %eax, offStackSaveArea_method(%edx) # newSaveArea->method<- method to call + jne .LinvokeNative # handle native call + + /* + * Update "glue" values for the new method + * %eax=methodToCall, LOCAL1_OFFSET(%ebp)=newFp + */ + + movl offMethod_clazz(%eax), %edx # %edx<- method->clazz + GET_GLUE(%ecx) # %ecx<- pMterpGlue + movl offClassObject_pDvmDex(%edx), %edx # %edx<- method->clazz->pDvmDex + movl %eax, offGlue_method(%ecx) # glue->method<- methodToCall + movl %edx, offGlue_methodClassDex(%ecx) # glue->methodClassDex<- method->clazz->pDvmDex + movl offMethod_insns(%eax), rPC # rPC<- methodToCall->insns + movl offGlue_self(%ecx), %eax # %eax<- glue->self + movl LOCAL1_OFFSET(%ebp), rFP # rFP<- newFP + movl rFP, offThread_curFrame(%eax) # glue->self->curFrame<- newFP + FETCH_INST() + GOTO_NEXT # jump to methodToCall->insns + + /* + * Prep for the native call + * %eax=methodToCall, LOCAL1_OFFSET(%ebp)=newFP, %edx=newSaveArea + */ + +.LinvokeNative: + GET_GLUE(%ecx) # %ecx<- pMterpGlue + movl %eax, OUT_ARG1(%esp) # push parameter methodToCall + movl offGlue_self(%ecx), %ecx # %ecx<- glue->self + movl offThread_jniLocal_topCookie(%ecx), %eax # %eax<- self->localRef->... + movl %eax, offStackSaveArea_localRefCookie(%edx) # newSaveArea->localRefCookie<- top + movl %edx, OUT_ARG4(%esp) # save newSaveArea + movl LOCAL1_OFFSET(%ebp), %edx # %edx<- newFP + movl %edx, offThread_curFrame(%ecx) # glue->self->curFrame<- newFP + movl %ecx, OUT_ARG3(%esp) # save glue->self + movl %ecx, OUT_ARG2(%esp) # push parameter glue->self + GET_GLUE(%ecx) # %ecx<- pMterpGlue + movl OUT_ARG1(%esp), %eax # %eax<- methodToCall + lea offGlue_retval(%ecx), %ecx # %ecx<- &retval + movl %ecx, OUT_ARG0(%esp) # push parameter pMterpGlue + push %edx # push parameter newFP + + call *offMethod_nativeFunc(%eax) # call methodToCall->nativeFunc + lea 4(%esp), %esp + movl OUT_ARG4(%esp), %ecx # %ecx<- newSaveArea + movl OUT_ARG3(%esp), %eax # %eax<- glue->self + movl offStackSaveArea_localRefCookie(%ecx), %edx # %edx<- old top + cmp $$0, offThread_exception(%eax) # check for exception + movl rFP, offThread_curFrame(%eax) # glue->self->curFrame<- rFP + movl %edx, offThread_jniLocal_topCookie(%eax) # new top <- old top + UNSPILL(rPC) + jne common_exceptionThrown # handle exception + FETCH_INST_WORD(3) + ADVANCE_PC(3) + GOTO_NEXT # jump to next instruction + +.LstackOverflow: # eax=methodToCall + movl %eax, OUT_ARG1(%esp) # push parameter methodToCall + GET_GLUE(%eax) # %eax<- pMterpGlue + movl offGlue_self(%eax), %eax # %eax<- glue->self + movl %eax, OUT_ARG0(%esp) # push parameter self + call dvmHandleStackOverflow # call: (Thread* self, Method* meth) + UNSPILL(rPC) # return: void + jmp common_exceptionThrown # handle exception + + +/* + * Common invoke code (old-style). + * TUNING: Rewrite along lines of new armv5 code? + * + * On entry: + * eax = Method* methodToCall + * ecx = bool methodCallRange + * rINST trashed, must reload + */ +common_invokeOld: + movl %ecx,OUT_ARG1(%esp) # arg1<- methodCallRange + GET_GLUE(%ecx) + movzwl (rPC),rINST_FULL # recover rINST + movl %eax,OUT_ARG2(%esp) # arg2<- method + movzwl 4(rPC),%eax # eax<- GFED or CCCC + SAVE_PC_TO_GLUE(%ecx) + SAVE_FP_TO_GLUE(%ecx) + movzbl rINST_HI,rINST_FULL + movl rINST_FULL,OUT_ARG3(%esp)# arg3<- AA + movl %ecx,OUT_ARG0(%esp) # arg0<- GLUE + movl %eax,OUT_ARG4(%esp) # arg4<- GFED/CCCC + call dvmMterp_invokeMethod + jmp common_resumeAfterGlueCall + + +/* + * Do we need the thread to be suspended or have debugger/profiling activity? + * + * On entry: + * ebx -> PC adjustment in 16-bit words (must be preserved) + * ecx -> GLUE pointer + * reentry type, e.g. kInterpEntryInstr stored in rGLUE->entryPoint + * + * Note: A call will normally kill %eax, rPC/%edx and %ecx. To + * streamline the normal case, this routine will preserve rPC and + * %ecx in addition to the normal caller save regs. The save/restore + * is a bit ugly, but will happen in the relatively uncommon path. + * TODO: Basic-block style Jit will need a hook here as well. Fold it into + * the suspendCount check so we can get both in 1 shot. + */ +common_periodicChecks: + movl offGlue_pSelfSuspendCount(%ecx),%eax # eax <- &suspendCount + cmpl $$0,(%eax) + jne 1f + +6: + movl offGlue_pDebuggerActive(%ecx),%eax # eax <- &DebuggerActive + movl offGlue_pActiveProfilers(%ecx),%ecx # ecx <- &ActiveProfilers + testl %eax,%eax # debugger enabled? + je 2f + movzbl (%eax),%eax # get active count +2: + orl (%ecx),%eax # eax <- debuggerActive | activeProfilers + GET_GLUE(%ecx) # restore rGLUE + jne 3f # one or both active - switch interp + +5: + ret + + /* Check for suspend */ +1: + /* At this point, the return pointer to the caller of + * common_periodicChecks is on the top of stack. We need to preserve + * rPC(edx) and GLUE(ecx). We'll spill rPC, and reload GLUE. + * The outgoing profile is: + * bool dvmCheckSuspendPending(Thread* self) + * Because we reached here via a call, go ahead and build a new frame. + */ + EXPORT_PC() # need for precise GC + movl offGlue_self(%ecx),%eax # eax<- glue->self + SPILL(rPC) # save edx + push %ebp + movl %esp,%ebp + subl $$24,%esp + movl %eax,OUT_ARG0(%esp) + call dvmCheckSuspendPending + addl $$24,%esp + pop %ebp + UNSPILL(rPC) + GET_GLUE(%ecx) + + /* + * Need to check to see if debugger or profiler flags got set + * while we were suspended. + */ + jmp 6b + + /* Switch interpreters */ + /* Note: %ebx contains the 16-bit word offset to be applied to rPC to + * "complete" the interpretation of backwards branches. In effect, we + * are completing the interpretation of the branch instruction here, + * and the new interpreter will resume interpretation at the branch + * target. However, a switch request recognized during the handling + * of a return from method instruction results in an immediate abort, + * and the new interpreter will resume by re-interpreting the return + * instruction. + */ +3: + leal (rPC,%ebx,2),rPC # adjust pc to show target + GET_GLUE(%ecx) # bail expect GLUE already loaded + movl $$1,rINST_FULL # set changeInterp to true + jmp common_gotoBail + + +/* + * Common code for handling a return instruction + */ +common_returnFromMethod: + GET_GLUE(%ecx) + /* Set entry mode in case we bail */ + movb $$kInterpEntryReturn,offGlue_entryPoint(%ecx) + xorl rINST_FULL,rINST_FULL # zero offset in case we switch interps + call common_periodicChecks # Note: expects %ecx to be preserved + + SAVEAREA_FROM_FP(%eax,rFP) # eax<- saveArea (old) + movl offStackSaveArea_prevFrame(%eax),rFP # rFP<- prevFrame + movl (offStackSaveArea_method-sizeofStackSaveArea)(rFP),rINST_FULL + cmpl $$0,rINST_FULL # break? + je common_gotoBail # break frame, bail out completely + + movl offStackSaveArea_savedPc(%eax),rPC # pc<- saveArea->savedPC + movl offGlue_self(%ecx),%eax # eax<- self + movl rINST_FULL,offGlue_method(%ecx) # glue->method = newSave->meethod + movl rFP,offThread_curFrame(%eax) # self->curFrame = fp + movl offMethod_clazz(rINST_FULL),%eax # eax<- method->clazz + FETCH_INST_WORD(3) + movl offClassObject_pDvmDex(%eax),%eax # eax<- method->clazz->pDvmDex + ADVANCE_PC(3) + movl %eax,offGlue_methodClassDex(%ecx) + /* not bailing - restore entry mode to default */ + movb $$kInterpEntryInstr,offGlue_entryPoint(%ecx) + GOTO_NEXT + +/* + * Prepare to strip the current frame and "longjump" back to caller of + * dvmMterpStdRun. + * + * on entry: + * rINST_FULL holds changeInterp + * ecx holds glue pointer + * + * expected profile: dvmMterpStdBail(MterpGlue *glue, bool changeInterp) + */ +common_gotoBail: + SAVE_PC_TO_GLUE(%ecx) # export state to glue + SAVE_FP_TO_GLUE(%ecx) + movl %ecx,OUT_ARG0(%esp) # glue in arg0 + movl rINST_FULL,OUT_ARG1(%esp) # changeInterp in arg1 + call dvmMterpStdBail # bail out.... + + +/* + * After returning from a "glued" function, pull out the updated values + * and start executing at the next instruction. + */ + common_resumeAfterGlueCall: + GET_GLUE(%ecx) + LOAD_PC_FROM_GLUE(%ecx) + LOAD_FP_FROM_GLUE(%ecx) + FETCH_INST() + GOTO_NEXT + +/* + * Integer divide or mod by zero + */ +common_errDivideByZero: + EXPORT_PC() + movl $$.LstrArithmeticException,%eax + movl %eax,OUT_ARG0(%esp) + movl $$.LstrDivideByZero,%eax + movl %eax,OUT_ARG1(%esp) + SPILL(rPC) + call dvmThrowException + UNSPILL(rPC) + jmp common_exceptionThrown + +/* + * Attempt to allocate an array with a negative size. + */ +common_errNegativeArraySize: + EXPORT_PC() + movl $$.LstrNegativeArraySizeException,%eax + movl %eax,OUT_ARG0(%esp) + xorl %eax,%eax + movl %eax,OUT_ARG1(%esp) + SPILL(rPC) + call dvmThrowException + UNSPILL(rPC) + jmp common_exceptionThrown + +/* + * Attempt to allocate an array with a negative size. + */ +common_errNoSuchMethod: + + EXPORT_PC() + movl $$.LstrNoSuchMethodError,%eax + movl %eax,OUT_ARG0(%esp) + xorl %eax,%eax + movl %eax,OUT_ARG1(%esp) + SPILL(rPC) + call dvmThrowException + UNSPILL(rPC) + jmp common_exceptionThrown + +/* + * Hit a null object when we weren't expecting one. Export the PC, throw a + * NullPointerException and goto the exception processing code. + */ +common_errNullObject: + EXPORT_PC() + movl $$.LstrNullPointerException,%eax + movl %eax,OUT_ARG0(%esp) + xorl %eax,%eax + movl %eax,OUT_ARG1(%esp) + SPILL(rPC) + call dvmThrowException + UNSPILL(rPC) + jmp common_exceptionThrown + +/* + * Array index exceeds max. + */ +common_errArrayIndex: + EXPORT_PC() + movl $$.LstrArrayIndexException,%eax + movl %eax,OUT_ARG0(%esp) + xorl %eax,%eax + movl %eax,OUT_ARG1(%esp) + SPILL(rPC) + call dvmThrowException + UNSPILL(rPC) + jmp common_exceptionThrown +/* + * Invalid array value. + */ +common_errArrayStore: + EXPORT_PC() + movl $$.LstrArrayStoreException,%eax + movl %eax,OUT_ARG0(%esp) + xorl %eax,%eax + movl %eax,OUT_ARG1(%esp) + SPILL(rPC) + call dvmThrowException + UNSPILL(rPC) + jmp common_exceptionThrown + +/* + * Somebody has thrown an exception. Handle it. + * + * If the exception processing code returns to us (instead of falling + * out of the interpreter), continue with whatever the next instruction + * now happens to be. + * + * This does not return. + */ +common_exceptionThrown: + GET_GLUE(%ecx) + SAVE_PC_TO_GLUE(%ecx) + SAVE_FP_TO_GLUE(%ecx) + movl %ecx,OUT_ARG0(%esp) + call dvmMterp_exceptionThrown + jmp common_resumeAfterGlueCall + +common_abort: + movl $$0xdeadf00d,%eax + call *%eax + + +/* + * Strings + */ + + .section .rodata +.LstrNullPointerException: + .asciz "Ljava/lang/NullPointerException;" +.LstrArithmeticException: + .asciz "Ljava/lang/ArithmeticException;" +.LstrDivideByZero: + .asciz "divide by zero" +.LstrArrayIndexException: + .asciz "Ljava/lang/ArrayIndexOutOfBoundsException;" +.LstrArrayStoreException: + .asciz "Ljava/lang/ArrayStoreException;" +.LstrNegativeArraySizeException: + .asciz "Ljava/lang/NegativeArraySizeException;" +.LstrInstantiationError: + .asciz "Ljava/lang/InstantiationError;" +.LstrClassCastException: + .asciz "Ljava/lang/ClassCastException;" +.LstrNoSuchMethodError: + .asciz "Ljava/lang/NoSuchMethodError;" +.LstrInternalError: + .asciz "Ljava/lang/InternalError;" +.LstrFilledNewArrayNotImpl: + .asciz "filled-new-array only implemented for 'int'" |