/* 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: footer.S */ .text .align 2 /* * Check to see if the thread needs to be suspended or debugger/profiler * activity has begun. * * On entry: * %ecx is reentry type, e.g. kInterpEntryInstr * %edx is PC adjustment in bytes */ common_periodicChecks: movl %edx, -8(%esp) # save pc adjustments movl rGLUE, %edx # %edx<- pMterpGlue movl %ebx, -4(%esp) # save %ebx to the stack movl offGlue_pSelfSuspendCount(%edx), %ebx # %ebx<- pSuspendCount (int) 4: movl offGlue_pDebuggerActive(%edx), %eax # %eax<- pDebuggerActive testl %eax, %eax je 5f movzbl (%eax), %eax # %eax<- get debuggerActive (boolean) 5: cmp $$0, (%ebx) # check if suspend is pending jne 2f # handle suspend movl offGlue_pActiveProfilers(%edx), %ebx # %ebx<- activeProfilers (int) orl (%ebx), %eax # %eax<- merge activeProfilers and debuggerActive movl -8(%esp), %edx # %edx<- restore %edx jne 3f # debugger or profiler active; switch interp movl -4(%esp), %ebx # %ebx<- restore %ebx ret # return 2: # check suspended EXPORT_PC movl offGlue_self(%edx), %eax # %eax<- glue->self movl %eax, -12(%esp) # push parameter boolean lea -12(%esp), %esp call dvmCheckSuspendPending # call: (Thread* self) # return: bool movl 4(%esp), %edx # %edx<- restore %edx movl 8(%esp), %ebx # %ebx<- restore %ebx lea 12(%esp), %esp ret 3: # debugger/profiler enabled, bail out leal (rPC, %edx, 2), rPC # adjust pc to show target movl rGLUE, %ecx # %ecx<- pMterpGlue movb $$kInterpEntryInstr, offGlue_entryPoint(%ecx) movl $$1, %edx # switch interpreter jmp common_gotoBail # bail /* * Check to see if the thread needs to be suspended or debugger/profiler * activity has begun. With this variant, the reentry type is hard coded * as kInterpEntryInstr. * * On entry: * %edx is PC adjustment in bytes */ common_periodicChecks_backwardBranch: EXPORT_PC movl rGLUE, %ecx # %ecx<- pMterpGlue movl offGlue_pSelfSuspendCount(%ecx), rINST # %ebx<- pSuspendCount (int) 4: movl offGlue_pDebuggerActive(%ecx), %eax # %eax<- pDebuggerActive testl %eax, %eax # test for NULL pointer je 5f movzbl (%eax), %eax # %eax<- get debuggerActive count 5: cmp $$0, (rINST) # check if suspend is pending jne 2f # handle suspend movl offGlue_pActiveProfilers(%ecx), rINST # %edx<- activeProfilers (int) orl (rINST), %eax # %eax<- merge activeProfilers and debuggerActive jne 3f # debugger or profiler active; switch interp FINISH_RB %edx, %ecx # jump to next instruction 2: # check suspended movl offGlue_self(%ecx), %eax# %eax<- glue->self movl %edx, rINST movl %eax, -12(%esp) # push parameter boolean lea -12(%esp), %esp call dvmCheckSuspendPending # call: (Thread* self) # return: bool movl rINST, %edx # %edx<- restore %edx lea 12(%esp), %esp FINISH_RB %edx, %ecx 3: # debugger/profiler enabled, bail out leal (rPC, %edx, 2), rPC # adjust pc to show target movb $$kInterpEntryInstr, offGlue_entryPoint(%ecx) movl $$1, %edx # switch interpreter jmp common_gotoBail # bail /* * The equivalent of "goto bail", this calls through the "bail handler". * State registers will be saved to the "glue" area before bailing. * * On entry: * %edx is "bool changeInterp", indicating if we want to switch to the * other interpreter or just bail all the way out */ common_gotoBail: SAVE_PC_FP_TO_GLUE %ecx # save program counter and frame pointer /* * Inlined dvmMterpStdBail */ lea 40(%ebp), %esp movl %edx, %eax movl 24(%ebp), %edi movl 28(%ebp), %esi movl 32(%ebp), %ebx movl 36(%ebp), %ebp ret /* * Common code for method invocation with range. * * On entry: * %ecx is "Method* methodToCall", the method we're trying to call */ common_invokeMethodRange: .LinvokeNewRange: /* * prepare to copy args to "outs" area of current frame */ SAVEAREA_FROM_FP %eax # %eax<- &outs; &StackSaveArea test rINST, rINST # test for no args movl rINST, sReg0 # sReg0<- AA jz .LinvokeArgsDone # no args; jump to args done FETCH 2, %edx # %edx<- CCCC /* * %ecx=methodToCall, %edx=CCCC, sReg0=count, %eax=&outs (&stackSaveArea) * (very few methods have > 10 args; could unroll for common cases) */ movl %ebx, sReg1 # sReg1<- save %ebx lea (rFP, %edx, 4), %edx # %edx<- &vCCCC shll $$2, sReg0 # sReg0<- offset subl sReg0, %eax # %eax<- update &outs shrl $$2, sReg0 # sReg0<- offset 1: movl (%edx), %ebx # %ebx<- vCCCC lea 4(%edx), %edx # %edx<- &vCCCC++ subl $$1, sReg0 # sReg<- sReg-- movl %ebx, (%eax) # *outs<- vCCCC lea 4(%eax), %eax # outs++ jne 1b # loop if count (sReg0) not zero movl sReg1, %ebx # %ebx<- restore %ebx jmp .LinvokeArgsDone # continue /* * %ecx is "Method* methodToCall", the method we're trying to call * prepare to copy args to "outs" area of current frame */ common_invokeMethodNoRange: .LinvokeNewNoRange: movl rINST, sReg0 # sReg0<- BA shrl $$4, sReg0 # sReg0<- B je .LinvokeArgsDone # no args; jump to args done SAVEAREA_FROM_FP %eax # %eax<- &outs; &StackSaveArea FETCH 2, %edx # %edx<- GFED /* * %ecx=methodToCall, %edx=GFED, sReg0=count, %eax=outs */ .LinvokeNonRange: cmp $$2, sReg0 # compare sReg0 to 2 movl %edx, sReg1 # sReg1<- GFED jl 1f # handle 1 arg je 2f # handle 2 args cmp $$4, sReg0 # compare sReg0 to 4 jl 3f # handle 3 args je 4f # handle 4 args 5: andl $$15, rINST # rINST<- A lea -4(%eax), %eax # %eax<- update &outs; &outs-- movl (rFP, rINST, 4), %edx # %edx<- vA movl %edx, (%eax) # *outs<- vA movl sReg1, %edx # %edx<- GFED 4: shr $$12, %edx # %edx<- G lea -4(%eax), %eax # %eax<- update &outs; &outs-- movl (rFP, %edx, 4), %edx # %edx<- vG movl %edx, (%eax) # *outs<- vG movl sReg1, %edx # %edx<- GFED 3: and $$0x0f00, %edx # %edx<- 0F00 shr $$6, %edx # %edx<- F at correct offset lea -4(%eax), %eax # %eax<- update &outs; &outs-- movl (rFP, %edx), %edx # %edx<- vF movl %edx, (%eax) # *outs<- vF movl sReg1, %edx # %edx<- GFED 2: and $$0x00f0, %edx # %edx<- 00E0 shr $$2, %edx # %edx<- E at correct offset lea -4(%eax), %eax # %eax<- update &outs; &outs-- movl (rFP, %edx), %edx # %edx<- vE movl %edx, (%eax) # *outs<- vE movl sReg1, %edx # %edx<- GFED 1: and $$0x000f, %edx # %edx<- 000D movl (rFP, %edx, 4), %edx # %edx<- vD movl %edx, -4(%eax) # *--outs<- vD 0: /* * %ecx is "Method* methodToCall", the method we're trying to call * find space for the new stack frame, check for overflow */ .LinvokeArgsDone: movzwl offMethod_registersSize(%ecx), %eax # %eax<- methodToCall->regsSize movzwl offMethod_outsSize(%ecx), %edx # %edx<- methodToCall->outsSize movl %ecx, sReg0 # sReg<- methodToCall shl $$2, %eax # %eax<- update offset SAVEAREA_FROM_FP %ecx # %ecx<- &outs; &StackSaveArea subl %eax, %ecx # %ecx<- newFP; (old savearea - regsSize) movl rGLUE, %eax # %eax<- pMterpGlue movl %ecx, sReg1 # sReg1<- &outs subl $$sizeofStackSaveArea, %ecx # %ecx<- newSaveArea (stack save area using newFP) movl offGlue_interpStackEnd(%eax), %eax # %eax<- glue->interpStackEnd movl %eax, sReg2 # sReg2<- glue->interpStackEnd shl $$2, %edx # %edx<- update offset for outsSize movl %ecx, %eax # %eax<- newSaveArea sub %edx, %ecx # %ecx<- bottom; (newSaveArea - outsSize) cmp sReg2, %ecx # compare interpStackEnd and bottom movl sReg0, %ecx # %ecx<- restore methodToCall jl .LstackOverflow # handle frame overflow /* * set up newSaveArea */ #ifdef EASY_GDB SAVEAREA_FROM_FP %edx # %edx<- &outs; &StackSaveArea movl %edx, offStackSaveArea_prevSave(%eax) # newSaveArea->prevSave<- &outs #endif movl rFP, offStackSaveArea_prevFrame(%eax) # newSaveArea->prevFrame<- rFP movl rPC, offStackSaveArea_savedPc(%eax) # newSaveArea->savedPc<- rPC testl $$ACC_NATIVE, offMethod_accessFlags(%ecx) # check for native call movl %ecx, offStackSaveArea_method(%eax) # newSaveArea->method<- method to call jne .LinvokeNative # handle native call /* * Update "glue" values for the new method * %ecx=methodToCall, sReg1=newFp */ movl offMethod_clazz(%ecx), %edx # %edx<- method->clazz movl rGLUE, %eax # %eax<- pMterpGlue movl %ecx, offGlue_method(%eax) # glue->method<- methodToCall movl offClassObject_pDvmDex(%edx), %edx # %edx<- method->clazz->pDvmDex movl offMethod_insns(%ecx), rPC # rPC<- methodToCall->insns movl %edx, offGlue_methodClassDex(%eax) # glue->methodClassDex<- method->clazz->pDvmDex movl offGlue_self(%eax), %ecx # %ecx<- glue->self movl sReg1, rFP # rFP<- newFP movl rFP, offThread_curFrame(%ecx) # glue->self->curFrame<- newFP FINISH_A # jump to methodToCall->insns /* * Prep for the native call * %ecx=methodToCall, sReg1=newFP, %eax=newSaveArea */ .LinvokeNative: movl rGLUE, %edx # %edx<- pMterpGlue movl %ecx, -20(%esp) # push parameter methodToCall movl offGlue_self(%edx), %edx # %edx<- glue->self movl offThread_jniLocal_topCookie(%edx), %ecx # %ecx<- glue->self->thread->refNext movl %ecx, offStackSaveArea_localRefCookie(%eax) # newSaveArea->localRefCookie<- refNext movl %eax, -4(%esp) # save newSaveArea movl sReg1, %eax # %eax<- newFP movl %eax, offThread_curFrame(%edx) # glue->self->curFrame<- newFP movl %edx, -8(%esp) # save glue->self movl %edx, -16(%esp) # push parameter glue->self movl rGLUE, %edx # %edx<- pMterpGlue movl -20(%esp), %ecx # %ecx<- methodToCall lea offGlue_retval(%edx), %edx # %edx<- &retval movl %edx, -24(%esp) # push parameter pMterpGlue movl %eax, -28(%esp) # push parameter newFP lea -28(%esp), %esp #ifdef ASSIST_DEBUGGER jmp .Lskip .type dalvik_mterp, %function dalvik_mterp: MTERP_ENTRY .Lskip: #endif call *offMethod_nativeFunc(%ecx) # call methodToCall->nativeFunc lea 28(%esp), %esp movl -4(%esp), %edx # %edx<- newSaveArea movl -8(%esp), %ecx # %ecx<- glue->self movl offStackSaveArea_localRefCookie(%edx), %eax # %eax<- newSaveArea->localRefCookie FFETCH_ADV 3, %edx # %edx<- next instruction hi; fetch, advance cmp $$0, offThread_exception(%ecx) # check for exception movl rFP, offThread_curFrame(%ecx) # glue->self->curFrame<- rFP movl %eax, offThread_jniLocal_topCookie(%ecx) # glue->self<- newSaveArea->localRefCookie jne common_exceptionThrown # handle exception FGETOP_JMP 3, %edx # jump to next instruction; getop, jmp .LstackOverflow: movl %ecx, -4(%esp) # push method to call movl rGLUE, %ecx # %ecx<- pMterpGlue movl offGlue_self(%ecx), %ecx # %ecx<- glue->self movl %ecx, -8(%esp) # push parameter self lea -8(%esp), %esp call dvmHandleStackOverflow # call: (Thread* self, Method *meth) # return: void lea 8(%esp), %esp jmp common_exceptionThrown # handle exception #ifdef ASSIST_DEBUGGER #endif /* * Common code for handling a return instruction. * * This does not return. */ common_returnFromMethod: .LreturnNew: /* * Inline common periodic checks */ movl rGLUE, rINST # %ecx<- pMterpGlue movl offGlue_pSelfSuspendCount(rINST), %edx # %ebx<- pSuspendCount (int) movl offGlue_pDebuggerActive(rINST), %eax # %eax<- pDebuggerActive movl (%eax), %eax # %eax<- get debuggerActive (boolean) and $$7, %eax # %eax<- mask for boolean (just how many bits does it take?) cmp $$0, (%edx) # check if suspend is pending jne 2f # handle suspend movl offGlue_pActiveProfilers(rINST), %edx # %edx<- activeProfilers (int) or (%edx), %eax # %eax<- merge activeProfilers and debuggerActive cmp $$0, %eax # check for debuggerActive jne 3f # debugger or profiler active; switch interp jmp 4f 2: # check suspended movl offGlue_self(rINST), %eax# %eax<- glue->self movl %eax, -12(%esp) # push parameter boolean lea -12(%esp), %esp call dvmCheckSuspendPending # call: (Thread* self) # return: bool lea 12(%esp), %esp jmp 4f 3: # debugger/profiler enabled, bail out movl $$kInterpEntryInstr, offGlue_entryPoint(rINST) # glue->entryPoint<- reentry type movl $$1, %edx # switch to interp<- true jmp common_gotoBail # bail /* * Get save area; rGLUE is %ebx, rFP is %eax */ 4: SAVEAREA_FROM_FP %ecx # %ecx<- saveArea(old) movl offStackSaveArea_prevFrame(%ecx), rFP # rFP<- saveArea->PrevFrame movl (offStackSaveArea_method - sizeofStackSaveArea)(rFP), %edx # %edx<- method we are returning to cmpl $$0, %edx # check for break frame je common_gotoBail # bail if break frame movl offStackSaveArea_savedPc(%ecx), rPC # rPC<- saveAreaOld->savedPc movl offGlue_self(rINST), %ecx # %eax<- glue->self movl %edx, offGlue_method(rINST) # glue->method<- newSave->method movl offMethod_clazz(%edx), %edx # %edx<- method->clazz FFETCH_ADV 3, %eax # %ecx<- next instruction hi; fetch, advance movl rFP, offThread_curFrame(%ecx) # glue->self->curFrame<- rFP movl offClassObject_pDvmDex(%edx), %edx # %edx<- method->clazz->pDvmDex movl %edx, offGlue_methodClassDex(rINST) # glue->pDvmDex<- method->clazz->pDvmDex FGETOP_JMP 3, %eax # jump to next instruction; getop, jmp /* * Handle thrown an exception. 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: .LexceptionNew: movl $$kInterpEntryThrow, %ecx # %ecx<- reentry type movl $$0, %edx # %edx<- pc adjustment call common_periodicChecks movl rGLUE, %eax # %eax<- pMterpGlue movl offGlue_self(%eax), %edx # %edx<- glue->self movl offThread_exception(%edx), %ecx # %ecx<- pMterpGlue->self->exception movl %edx, -4(%esp) # push parameter self movl %ecx, -8(%esp) # push parameter obj lea -8(%esp), %esp call dvmAddTrackedAlloc # don't allow the exception to be GC'd # call: (Object* obj, Thread* self) # return: void movl 4(%esp), %edx # %edx<- glue->self movl $$0, offThread_exception(%edx) # glue->self->exception<- NULL /* * set up args and a local for &fp */ movl rFP, -4(%esp) # move fp to stack lea -4(%esp), %esp # update %esp movl %esp, -4(%esp) # push parameter 4<- &fp movl $$0, -8(%esp) # push parameter 3<- false movl 4(%esp), %edx movl %edx, -12(%esp) # push parameter 2<- glue->self->exception movl rGLUE, %eax # %eax<- pMterpGlue movl offGlue_method(%eax), %edx # %edx<- glue->method movl offMethod_insns(%edx), %edx # %edx<- glue->method->insns movl rPC, %ecx # %ecx<- rPC subl %edx, %ecx # %ecx<- pc - glue->method->insns sar $$1, %ecx # %ecx<- adjust %ecx for offset movl %ecx, -16(%esp) # push parameter 1<- glue->method->insns movl 8(%esp), %edx movl %edx, -20(%esp) # push parameter 0<- glue->self lea -20(%esp), %esp /* * call dvmFindCatchBlock, %eax gets catchRelPc (a code-unit offset) */ call dvmFindCatchBlock # call: (Thread* self, int relPc, Object* exception, # bool doUnroll, void** newFrame) # return: int lea 32(%esp), %esp movl -12(%esp), rFP # rFP<- updated rFP cmp $$0, %eax # check for catchRelPc < 0 jl .LnotCaughtLocally # handle not caught locally /* * fix stack overflow if necessary */ movl -4(%esp), %ecx # %ecx<- glue->self cmp $$0, offThread_stackOverflowed(%ecx) je 1f movl %eax, -4(%esp) # save %eax for later movl %ecx, -12(%esp) # push parameter 2 glue->self lea -12(%esp), %esp call dvmCleanupStackOverflow # call: (Thread* self, Object* exception) # return: void lea 12(%esp), %esp movl -4(%esp), %eax # %eax<- restore %eax jmp 2f 1: movl %ecx, -12(%esp) # push parameter 2 glue->self 2: /* * adjust locals to match self->curFrame and updated PC * */ SAVEAREA_FROM_FP %edx # %edx<- get newSaveArea movl rGLUE, %ecx # %ecx<- pMterpGlue movl offStackSaveArea_method(%edx), rPC # rPC<- newMethod movl rPC, offGlue_method(%ecx) # glue->method<- newMethod movl offMethod_clazz(rPC), %edx # %edx<- method->clazz movl offMethod_insns(rPC), rPC # rPC<- method->insns movl offClassObject_pDvmDex(%edx), %edx # %edx<- method->clazz->pDvmDex lea (rPC, %eax, 2), rPC # rPC<- method->insns + catchRelPc movl %edx, offGlue_methodClassDex(%ecx) # glue->pDvmDex<- method->clazz->pDvmDex movl -8(%esp), %eax movl %eax, -16(%esp) # push parameter 1 obj lea -16(%esp), %esp call dvmReleaseTrackedAlloc # call: (Object* obj, Thread* self) # return: void lea 16(%esp), %esp FINISH_FETCH %eax cmp $$OP_MOVE_EXCEPTION, %eax # is it a move exception jne 1f movl -12(%esp), %edx # %edx<- glue->self movl -8(%esp), %ecx # %ecx<- exception movl %ecx, offThread_exception(%edx) # restore the exception 1: FINISH_JMP %eax /* * -8(%esp) = exception, -4(%esp) = self */ .LnotCaughtLocally: movl -4(%esp), %edx # %edx<- glue->self movzb offThread_stackOverflowed(%edx), %eax # %eax<- self->stackOverflowed cmp $$0, %eax # check for stack overflow; # maybe should use cmpb je 1f # movl %edx, -12(%esp) # push parameter 1 glue->self lea -12(%esp), %esp call dvmCleanupStackOverflow # call: (Thread* self, Object* exception) # return: void lea 12(%esp), %esp /* * Release the exception * -8(%esp) = exception, -4(%esp) = self */ 1: movl -8(%esp), %ecx # %ecx<- exception movl -4(%esp), %edx # %edx<- glue->self movl %ecx, offThread_exception(%edx) # glue->self<- exception lea -8(%esp), %esp call dvmReleaseTrackedAlloc # call: (Object* obj, Thread* self) # return: void lea 8(%esp), %esp movl $$0, %edx # switch to interp<- false jmp common_gotoBail # bail /* * After returning from a "glued" function, pull out the updated * values and start executing at the next instruction. */ common_resumeAfterGlueCall: LOAD_PC_FP_FROM_GLUE # pull rPC and rFP out of glue FINISH_A # jump to next instruction /* * For debugging, cause an immediate fault. */ common_abort: jmp .LdeadFood .LdeadFood: .int 0xdeadf00d /* * Invalid array index. */ common_errArrayIndex: EXPORT_PC movl $$.LstrArrayIndexException, -8(%esp) # push parameter description movl $$0, -4(%esp) # push parameter msg paramter lea -8(%esp), %esp call dvmThrowException # call: (const char* exceptionDescriptor, const char* msg) # return: void lea 8(%esp), %esp jmp common_exceptionThrown # handle exception /* * Invalid array value. */ common_errArrayStore: EXPORT_PC movl $$.LstrArrayStoreException, -8(%esp) # push parameter description movl $$0, -4(%esp) # push parameter msg paramter lea -8(%esp), %esp call dvmThrowException # call: (const char* exceptionDescriptor, const char* msg) # return: void lea 8(%esp), %esp jmp common_exceptionThrown # handle exception /* * Integer divide or mod by zero. */ common_errDivideByZero: EXPORT_PC movl $$.LstrArithmeticException, -8(%esp) # push parameter description movl $$.LstrDivideByZero, -4(%esp) # push parameter msg paramter lea -8(%esp), %esp call dvmThrowException # call: (const char* exceptionDescriptor, const char* msg) # return: void lea 8(%esp), %esp jmp common_exceptionThrown # handle exception /* * Attempt to allocate an array with a negative size. */ common_errNegativeArraySize: EXPORT_PC movl $$.LstrNegativeArraySizeException, -8(%esp) # push parameter description movl $$0, -4(%esp) # push parameter msg paramter lea -8(%esp), %esp call dvmThrowException # call: (const char* exceptionDescriptor, const char* msg) # return: void lea 8(%esp), %esp jmp common_exceptionThrown # handle exception /* * Invocation of a non-existent method. */ common_errNoSuchMethod: EXPORT_PC movl $$.LstrNoSuchMethodError, -8(%esp) # push parameter description movl $$0, -4(%esp) # push parameter msg paramter lea -8(%esp), %esp call dvmThrowException # call: (const char* exceptionDescriptor, const char* msg) # return: void lea 8(%esp), %esp jmp common_exceptionThrown # handle exception /* * Unexpected null object. */ common_errNullObject: EXPORT_PC movl $$.LstrNullPointerException, -8(%esp) # push parameter description movl $$0, -4(%esp) # push parameter msg paramter lea -8(%esp), %esp call dvmThrowException # call: (const char* exceptionDescriptor, const char* msg) # return: void lea 8(%esp), %esp jmp common_exceptionThrown # handle exception /* * String references */ .align 4 .section .rodata .LstrArithmeticException: .asciz "Ljava/lang/ArithmeticException;" .LstrArrayIndexException: .asciz "Ljava/lang/ArrayIndexOutOfBoundsException;" .LstrArrayStoreException: .asciz "Ljava/lang/ArrayStoreException;" .LstrDivideByZero: .asciz "divide by zero" .LstrInstantiationError: .asciz "Ljava/lang/InstantiationError;" .LstrNegativeArraySizeException: .asciz "Ljava/lang/NegativeArraySizeException;" .LstrNoSuchMethodError: .asciz "Ljava/lang/NoSuchMethodError;" .LstrNullPointerException: .asciz "Ljava/lang/NullPointerException;" .LstrExceptionNotCaughtLocally: .asciz "Exception %s from %s:%d not caught locally\n"