/* * 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'"