blob: 3b5c79e704e12125832d8475525087799594baa9 [file] [log] [blame]
/*
* 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.
*/
#if defined(WITH_JIT)
/*
* JIT-related re-entries into the interpreter. In general, if the
* exit from a translation can at some point be chained, the entry
* here requires that control arrived via a call, and that the "rp"
* on TOS is actually a pointer to a 32-bit cell containing the Dalvik PC
* of the next insn to handle. If no chaining will happen, the entry
* should be reached via a direct jump and rPC set beforehand.
*/
.global dvmJitToInterpPunt
/*
* The compiler will generate a jump to this entry point when it is
* having difficulty translating a Dalvik instruction. We must skip
* the code cache lookup & prevent chaining to avoid bouncing between
* the interpreter and code cache. rPC must be set on entry.
*/
dvmJitToInterpPunt:
GET_PC
#if defined(WITH_JIT_TUNING)
movl rPC, OUT_ARG0(%esp)
call dvmBumpPunt
#endif
movl rSELF, %ecx
movl offThread_curHandlerTable(%ecx),rIBASE
movl $$0, offThread_inJitCodeCache(%ecx)
FETCH_INST_R %ecx
GOTO_NEXT_R %ecx
.global dvmJitToInterpSingleStep
/*
* Return to the interpreter to handle a single instruction.
* Should be reached via a call.
* On entry:
* 0(%esp) <= native return address within trace
* rPC <= Dalvik PC of this instruction
* OUT_ARG0+4(%esp) <= Dalvik PC of next instruction
*/
dvmJitToInterpSingleStep:
/* TODO */
call dvmAbort
#if 0
pop %eax
movl rSELF, %ecx
movl OUT_ARG0(%esp), %edx
movl %eax,offThread_jitResumeNPC(%ecx)
movl %edx,offThread_jitResumeDPC(%ecx)
movl $$kInterpEntryInstr,offThread_entryPoint(%ecx)
movl $$1,rINST # changeInterp <= true
jmp common_gotoBail
#endif
.global dvmJitToInterpNoChainNoProfile
/*
* Return from the translation cache to the interpreter to do method
* invocation. Check if the translation exists for the callee, but don't
* chain to it. rPC must be set on entry.
*/
dvmJitToInterpNoChainNoProfile:
#if defined(WITH_JIT_TUNING)
call dvmBumpNoChain
#endif
movl %eax, rPC
movl rSELF, %eax
movl rPC,OUT_ARG0(%esp)
movl %eax,OUT_ARG1(%esp)
call dvmJitGetTraceAddrThread # (pc, self)
movl rSELF,%ecx # ecx <- self
movl %eax,offThread_inJitCodeCache(%ecx) # set inJitCodeCache flag
cmpl $$0, %eax
jz 1f
jmp *%eax # exec translation if we've got one
# won't return
1:
EXPORT_PC
movl rSELF, %ecx
movl offThread_curHandlerTable(%ecx),rIBASE
FETCH_INST_R %ecx
GOTO_NEXT_R %ecx
/*
* Return from the translation cache and immediately request a
* translation from the exit target, but don't attempt to chain.
* rPC set on entry.
*/
.global dvmJitToInterpTraceSelectNoChain
dvmJitToInterpTraceSelectNoChain:
#if defined(WITH_JIT_TUNING)
call dvmBumpNoChain
#endif
movl %ebx, rPC
lea 4(%esp), %esp #to recover the esp update due to function call
movl rSELF, %eax
movl rPC,OUT_ARG0(%esp)
movl %eax,OUT_ARG1(%esp)
call dvmJitGetTraceAddrThread # (pc, self)
movl rSELF,%ecx
cmpl $$0,%eax
movl %eax,offThread_inJitCodeCache(%ecx) # set inJitCodeCache flag
jz 1f
jmp *%eax # jump to tranlation
# won't return
/* No Translation - request one */
1:
GET_JIT_PROF_TABLE %ecx %eax
cmpl $$0, %eax # JIT enabled?
jnz 2f # Request one if so
movl rSELF, %ecx
movl offThread_curHandlerTable(%ecx),rIBASE
FETCH_INST_R %ecx # Continue interpreting if not
GOTO_NEXT_R %ecx
2:
## Looks like an EXPORT_PC is needed here. Now jmp to common_selectTrace2
movl $$kJitTSelectRequestHot,%eax # ask for trace select
jmp common_selectTrace
/*
* Return from the translation cache and immediately request a
* translation for the exit target. Reached via a call, and
* (TOS)->rPC.
*/
.global dvmJitToInterpTraceSelect
dvmJitToInterpTraceSelect:
movl 0(%esp), %eax # get return address
movl %ebx, rPC # get first argument (target rPC)
## TODO, need to clean up stack manipulation ... this isn't signal safe and
## doesn't use the calling conventions of header.S
lea 4(%esp), %esp #to recover the esp update due to function call
## An additional 5B instruction "jump 0" was added for a thread-safe
## chaining cell update in JIT code cache. So the offset is now -17=-12-5.
lea -17(%eax), %ebx #$$JIT_OFFSET_CHAIN_START(%eax), %ebx
lea -4(%esp), %esp
movl rSELF, %eax
movl rPC,OUT_ARG0(%esp)
movl %eax,OUT_ARG1(%esp)
call dvmJitGetTraceAddrThread # (pc, self)
lea 4(%esp), %esp
cmpl $$0,%eax
movl rSELF, %ecx
movl %eax,offThread_inJitCodeCache(%ecx) # set inJitCodeCache flag
jz 1b # no - ask for one
movl %eax,OUT_ARG0(%esp)
movl rINST,OUT_ARG1(%esp)
call dvmJitChain # Attempt dvmJitChain(codeAddr,chainAddr)
cmpl $$0,%eax # Success?
jz toInterpreter # didn't chain - interpret
jmp *%eax
# won't return
/*
* Placeholder entries for x86 JIT
*/
.global dvmJitToInterpBackwardBranch
dvmJitToInterpBackwardBranch:
.global dvmJitToExceptionThrown
dvmJitToExceptionThrown: //rPC in
movl rSELF, %edx
GET_PC
movl $$0, offThread_inJitCodeCache(%edx)
jmp common_exceptionThrown
.global dvmJitToInterpNormal
dvmJitToInterpNormal:
/* one input: the target rPC value */
movl 0(%esp), %eax # get return address
movl %ebx, rPC # get first argument (target rPC)
## TODO, need to clean up stack manipulation ... this isn't signal safe and
## doesn't use the calling conventions of header.S
## An additional 5B instruction "jump 0" was added for a thread-safe
## chaining cell update in JIT code cache. So the offset is now -17=-12-5.
lea -17(%eax), %ebx #$$JIT_OFFSET_CHAIN_START(%eax), %ebx
lea 4(%esp), %esp
movl rPC, OUT_ARG0(%esp)
movl rSELF, %ecx
movl %ecx, OUT_ARG1(%esp)
call dvmJitGetTraceAddrThread
## Here is the change from using rGLUE to rSELF for accessing the
## JIT code cache flag
movl rSELF, %ecx
movl %eax, offThread_inJitCodeCache(%ecx) # set inJitCodeCache flag
#lea 4(%esp), %esp
cmp $$0, %eax
je toInterpreter
#lea -8(%esp), %esp
movl %ebx, OUT_ARG1(%esp) # %ebx live thorugh dvmJitGetTraceAddrThread
movl %eax, OUT_ARG0(%esp) # first argument
call dvmJitChain
#lea 8(%esp), %esp
cmp $$0, %eax
je toInterpreter
jmp *%eax #to native address
.global dvmJitToInterpNoChain
dvmJitToInterpNoChain:
dvmJitToInterpNoChain: #rPC in eax
## TODO, need to clean up stack manipulation ... this isn't signal safe and
## doesn't use the calling conventions of header.S
movl %eax, rPC
movl rPC, OUT_ARG0(%esp)
movl rSELF, %ecx
movl %ecx, OUT_ARG1(%esp)
call dvmJitGetTraceAddrThread
## Here is the change from using rGLUE to rSELF for accessing the
## JIT code cache flag
movl rSELF, %ecx
movl %eax, offThread_inJitCodeCache(%ecx) # set inJitCodeCache flag
cmp $$0, %eax
je toInterpreter
jmp *%eax #to native address
toInterpreter:
EXPORT_PC
movl rSELF, %ecx
movl offThread_curHandlerTable(%ecx), rIBASE
FETCH_INST
movl offThread_pJitProfTable(%ecx), %eax
#Fallthrough
/* ebx holds the pointer to the jit profile table
edx has the opCode */
common_testUpdateProfile:
cmp $$0, %eax
je 4f
/* eax holds the pointer to the jit profile table
edx has the opCode
rPC points to the next bytecode */
common_updateProfile:
# quick & dirty hash
movl rPC, %ecx
shrl $$12, %ecx
xorl rPC, %ecx
andl $$((1<<JIT_PROF_SIZE_LOG_2)-1), %ecx
decb (%ecx,%eax)
#jmp 1f # remove
jz 2f
1:
GOTO_NEXT
2:
common_Profile:
/*
* Here, we switch to the debug interpreter to request
* trace selection. First, though, check to see if there
* is already a native translation in place (and, if so,
* jump to it now.
*/
SPILL(rIBASE)
SPILL_TMP1(rINST)
movl rSELF, rIBASE
GET_JIT_THRESHOLD rIBASE rINST # leaves rSELF in %ecx
EXPORT_PC
movb rINSTbl,(%ecx,%eax) # reset counter
movl rIBASE,rINST # preserve rSELF
movl rSELF, %eax
movl rPC,OUT_ARG0(%esp)
movl rIBASE,OUT_ARG1(%esp)
call dvmJitGetTraceAddrThread # (pc, self)
UNSPILL(rIBASE)
movl %eax,offThread_inJitCodeCache(rINST) # set the inJitCodeCache flag
UNSPILL_TMP1(rINST)
cmpl $$0,%eax
#jmp 1f # remove
jz 1f
jmp *%eax # TODO: decide call vs/ jmp!. No return either way
1:
movl $$kJitTSelectRequest,%eax
# On entry, eax<- jitState, rPC valid
common_selectTrace:
mov %ebx, EBX_SPILL(%ebp)
movl rSELF, %ebx
movzwl offThread_subMode(%ebx), %ecx
and $$(kSubModeJitTraceBuild | kSubModeJitSV), %ecx
jne 3f # already doing JIT work, continue
movl %eax, offThread_jitState(%ebx)
movl rSELF, %eax
movl %eax, OUT_ARG0(%esp)
/*
* Call out to validate trace-building request. If successful, rIBASE will be swapped
* to send us into single-steppign trace building mode, so we need to refresh before
* we continue.
*/
EXPORT_PC
SAVE_PC_FP_TO_SELF %ecx
call dvmJitCheckTraceRequest
3:
mov EBX_SPILL(%ebp), %ebx
FETCH_INST
movl rSELF, %ecx
movl offThread_curHandlerTable(%ecx), rIBASE
4:
GOTO_NEXT
common_selectTrace2:
mov %ebx, EBX_SPILL(%ebp)
movl rSELF, %ebx
movl %ebx, OUT_ARG0(%esp)
movl %eax, offThread_jitState(%ebx)
movzwl offThread_subMode(%ebx), %ecx
mov EBX_SPILL(%ebp), %ebx
and (kSubModeJitTraceBuild | kSubModeJitSV), %ecx
jne 3f # already doing JIT work, continue
/*
* Call out to validate trace-building request. If successful, rIBASE will be swapped
* to send us into single-steppign trace building mode, so we need to refresh before
* we continue.
*/
EXPORT_PC
SAVE_PC_FP_TO_SELF %ecx
call dvmJitCheckTraceRequest
3:
FETCH_INST
movl rSELF, %ecx
movl offThread_curHandlerTable(%ecx), rIBASE
4:
GOTO_NEXT
#endif
/*
* For the invoke codes we need to know what register holds the "this" pointer. However
* it seems the this pointer is assigned consistently most times it is in %ecx but other
* times it is in OP_INVOKE_INTERFACE, OP_INVOKE_SUPER_QUICK, or OP_INVOKE_VIRTUAL_QUICK.
*/
/*
* Common code for method invocation with range.
*
* On entry:
* eax = Method* methodToCall
* ecx = "this"
* rINSTw trashed, must reload
* rIBASE trashed, must reload before resuming interpreter
*/
common_invokeMethodRange:
.LinvokeNewRange:
#if defined(WITH_JIT)
SPILL_TMP1(%edx)
SPILL_TMP2(%ebx)
movl rSELF, %edx
movzwl offThread_subMode(%edx), %ebx
and $$kSubModeJitTraceBuild, %ebx
jz 6f
call save_callsiteinfo
6:
UNSPILL_TMP2(%ebx)
UNSPILL_TMP1(%edx)
#endif
/*
* prepare to copy args to "outs" area of current frame
*/
movzbl 1(rPC),rINST # rINST<- AA
movzwl 4(rPC), %ecx # %ecx<- CCCC
SAVEAREA_FROM_FP %edx # %edx<- &StackSaveArea
test rINST, rINST
movl rINST, 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)
*/
.LinvokeRangeArgs:
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
* rIBASE trashed, must reload before resuming interpreter
*/
common_invokeMethodNoRange:
#if defined(WITH_JIT)
SPILL_TMP1(%edx)
SPILL_TMP2(%ebx)
movl rSELF, %edx
movzwl offThread_subMode(%edx), %ebx
and $$kSubModeJitTraceBuild, %ebx
jz 6f
call save_callsiteinfo
6:
UNSPILL_TMP2(%ebx)
UNSPILL_TMP1(%edx)
#endif
.LinvokeNewNoRange:
movzbl 1(rPC),rINST # rINST<- BA
movl rINST, 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 # %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 # rINSTw<- A
lea -4(%edx), %edx # %edx<- update &outs; &outs--
movl (rFP, rINST, 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 # %eax<- &StackSaveArea
subl %edx, %eax # %eax<- newFP; (old savearea - regsSize)
movl rSELF,%edx # %edx<- pthread
movl %eax, LOCAL1_OFFSET(%ebp) # LOCAL1_OFFSET(%ebp)<- &outs
subl $$sizeofStackSaveArea, %eax # %eax<- newSaveArea (stack save area using newFP)
movl offThread_interpStackEnd(%edx), %edx # %edx<- self->interpStackEnd
movl %edx, TMP_SPILL1(%ebp) # spill self->interpStackEnd
shl $$2, %ecx # %ecx<- update offset for outsSize
movl %eax, %edx # %edx<- newSaveArea
sub %ecx, %eax # %eax<- bottom; (newSaveArea - outsSize)
cmp TMP_SPILL1(%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 # %ecx<- &StackSaveArea
movl %ecx, offStackSaveArea_prevSave(%edx) # newSaveArea->prevSave<- &outs
#endif
movl rSELF,%ecx # %ecx<- pthread
movl rFP, offStackSaveArea_prevFrame(%edx) # newSaveArea->prevFrame<- rFP
movl rPC, offStackSaveArea_savedPc(%edx) # newSaveArea->savedPc<- rPC
#if defined(WITH_JIT)
movl $$0, offStackSaveArea_returnAddr(%edx)
#endif
/* Any special actions to take? */
cmpw $$0, offThread_subMode(%ecx)
jne 2f # Yes - handle them
1:
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 "self" values for the new method
* %eax=methodToCall, LOCAL1_OFFSET(%ebp)=newFp
*/
movl offMethod_clazz(%eax), %edx # %edx<- method->clazz
movl offClassObject_pDvmDex(%edx), %edx # %edx<- method->clazz->pDvmDex
movl %eax, offThread_method(%ecx) # self->method<- methodToCall
movl %edx, offThread_methodClassDex(%ecx) # self->methodClassDex<- method->clazz->pDvmDex
movl offMethod_insns(%eax), rPC # rPC<- methodToCall->insns
movl $$1, offThread_debugIsMethodEntry(%ecx)
movl LOCAL1_OFFSET(%ebp), rFP # rFP<- newFP
movl rFP, offThread_curFrame(%ecx) # curFrame<-newFP
movl offThread_curHandlerTable(%ecx),rIBASE
FETCH_INST
#if defined(WITH_JIT)
/* rPC is already updated */
GET_JIT_PROF_TABLE %ecx %eax
cmp $$0, %eax
jne common_updateProfile # set up %ebx & %edx & rPC
#endif
GOTO_NEXT # jump to methodToCall->insns
2:
/*
* On entry, preserve all:
* %eax: method
* %ecx: self
* %edx: new save area
*/
SPILL_TMP1(%eax) # preserve methodToCall
SPILL_TMP2(%edx) # preserve newSaveArea
movl rPC, offThread_pc(%ecx) # update interpSave.pc
movl %ecx, OUT_ARG0(%esp)
movl %eax, OUT_ARG1(%esp)
call dvmReportInvoke # (self, method)
UNSPILL_TMP1(%eax)
UNSPILL_TMP2(%edx)
movl rSELF,%ecx # restore rSELF
jmp 1b
/*
* Prep for the native call
* %eax=methodToCall, LOCAL1_OFFSET(%ebp)=newFP, %edx=newSaveArea, %ecx=self
*/
.LinvokeNative:
movl offThread_jniLocal_topCookie(%ecx), rINST # rINST<- self->localRef->...
movl rINST, offStackSaveArea_localRefCookie(%edx) # newSaveArea->localRefCookie<- top
movl %edx, LOCAL2_OFFSET(%ebp) # save newSaveArea
movl LOCAL1_OFFSET(%ebp), rINST # rINST<- newFP
movl rINST, offThread_curFrame(%ecx) # curFrame<- newFP
cmpw $$0, offThread_subMode(%ecx) # Anything special going on?
jne 11f # yes - handle it
movl %ecx, OUT_ARG3(%esp) # push parameter self
movl %eax, OUT_ARG2(%esp) # push parameter methodToCall
lea offThread_retval(%ecx), %ecx # %ecx<- &retval
movl %ecx, OUT_ARG1(%esp) # push parameter &retval
movl rINST, OUT_ARG0(%esp) # push parameter newFP
call *offMethod_nativeFunc(%eax) # call methodToCall->nativeFunc
7:
movl LOCAL2_OFFSET(%ebp), %ecx # %ecx<- newSaveArea
movl rSELF, %eax # %eax<- self
movl offStackSaveArea_localRefCookie(%ecx), %edx # %edx<- old top
cmp $$0, offThread_exception(%eax) # check for exception
movl rFP, offThread_curFrame(%eax) # curFrame<- rFP
movl %edx, offThread_jniLocal_topCookie(%eax) # new top <- old top
jne common_exceptionThrown # handle exception
movl offThread_curHandlerTable(%eax),rIBASE
FETCH_INST_OPCODE 3 %ecx
ADVANCE_PC 3
GOTO_NEXT_R %ecx # jump to next instruction
11:
/*
* Handle any special subMode actions
* %eax=methodToCall, rINST=newFP, %ecx=self
*/
SPILL_TMP1(%eax) # save methodTocall
movl rPC, offThread_pc(%ecx)
movl %ecx, OUT_ARG1(%esp)
movl %eax, OUT_ARG0(%esp)
movl rFP, OUT_ARG2(%esp)
call dvmReportPreNativeInvoke # (methodToCall, self, fp)
UNSPILL_TMP1(%eax) # restore methodToCall
movl rSELF,%ecx # restore self
/* Do the native call */
movl %ecx, OUT_ARG3(%esp) # push parameter self
lea offThread_retval(%ecx), %ecx # %ecx<- &retval
movl %eax, OUT_ARG2(%esp) # push parameter methodToCall
movl %ecx, OUT_ARG1(%esp) # push parameter &retval
movl rINST, OUT_ARG0(%esp) # push parameter newFP
call *offMethod_nativeFunc(%eax) # call methodToCall->nativeFunc
UNSPILL_TMP1(%eax) # restore methodToCall
movl rSELF, %ecx
movl %ecx, OUT_ARG1(%esp)
movl %eax, OUT_ARG0(%esp)
movl rFP, OUT_ARG2(%esp)
call dvmReportPostNativeInvoke # (methodToCall, self, fp)
jmp 7b # rejoin
.LstackOverflow: # eax=methodToCall
movl %eax, OUT_ARG1(%esp) # push parameter methodToCall
movl rSELF,%eax # %eax<- self
movl %eax, OUT_ARG0(%esp) # push parameter self
call dvmHandleStackOverflow # call: (Thread* self, Method* meth)
jmp common_exceptionThrown # handle exception
/*
* Common code for handling a return instruction
*/
common_returnFromMethod:
movl rSELF, %ecx
SAVEAREA_FROM_FP %eax # %eax<- saveArea(old)
cmpw $$0, offThread_subMode(%ecx) # special action needed?
jne 19f # go if so
14:
movl offStackSaveArea_prevFrame(%eax), rFP # rFP<- saveArea->PrevFrame
movl (offStackSaveArea_method - sizeofStackSaveArea)(rFP), rINST # rINST<- method we are returning to
cmpl $$0, rINST # check for break frame
je common_gotoBail # bail if break frame
movl offThread_curHandlerTable(%ecx),rIBASE
movl offStackSaveArea_savedPc(%eax), rPC # rPC<- saveAreaOld->savedPc
#if defined(WITH_JIT)
movl offStackSaveArea_returnAddr(%eax), %ecx
#endif
movl rSELF, %eax
movl rINST, offThread_method(%eax) # glue->method<- newSave->method
movl offMethod_clazz(rINST), rINST # rINST<- method->clazz
movl rFP, offThread_curFrame(%eax) # glue->self->curFrame<- rFP
#if defined(WITH_JIT)
//update self->offThread_inJitCodeCache
movl %ecx, offThread_inJitCodeCache(%eax)
#endif
movl offClassObject_pDvmDex(rINST), rINST # rINST<- method->clazz->pDvmDex
movl rINST, offThread_methodClassDex(%eax) # glue->pDvmDex<- method->clazz->pDvmDex
#if defined(WITH_JIT)
cmp $$0, %ecx
je .returnToBC
movl %ecx, %eax
jmp *%eax
#endif
.returnToBC:
#if defined(WITH_JIT)
FETCH_INST_OPCODE 3, %ecx # %eax<- next instruction hi; fetch, advance
// %ecx has the opcode
addl $$6, rPC # 3*2 = 6
SPILL_TMP1 (%ecx)
movl rSELF, %ecx
FETCH_INST
UNSPILL_TMP1 (%ecx)
movzbl 1(rPC), rINST
jmp *(rIBASE,%ecx,4)
#else
FETCH_INST_WORD 3
ADVANCE_PC 3
GOTO_NEXT
#endif
19:
/*
* Handle special subMode actions
* On entry, rFP: prevFP, %ecx: self, %eax: saveArea
*/
SPILL_TMP1(%ebx)
movl offStackSaveArea_prevFrame(%eax), %ebx # %ebx<- saveArea->PrevFrame
movl rPC, offThread_pc(%ecx) # update interpSave.pc
movl %ebx, offThread_curFrame(%ecx) # update interpSave.curFrame
movl %ecx, OUT_ARG0(%esp) # parameter self
call dvmReportReturn # (self)
UNSPILL_TMP1(%ebx)
movl rSELF, %ecx # restore self
SAVEAREA_FROM_FP %eax # restore saveArea
jmp 14b
/*
* Prepare to strip the current frame and "longjump" back to caller of
* dvmMterpStdRun.
*
* on entry:
* rINST holds changeInterp
* ecx holds self pointer
*
* expected profile: dvmMterpStdBail(Thread *self, bool changeInterp)
*/
common_gotoBail:
movl rPC,offThread_pc(%ecx) # export state to self
movl rFP,offThread_curFrame(%ecx)
movl %ecx,OUT_ARG0(%esp) # self in arg0
movl rINST,OUT_ARG1(%esp) # changeInterp in arg1
call dvmMterpStdBail # bail out....
/*
* The JIT's invoke method needs to remember the callsite class and
* target pair. Save them here so that they are available to
* dvmCheckJit following the interpretation of this invoke.
*
* eax = Method* methodToCall
* ecx = "this"
* edx = rSELF
* ebx = free to use
*/
#if defined(WITH_JIT)
save_callsiteinfo:
cmp $$0, %ecx
je 2f
movl offObject_clazz(%ecx), %ecx
2:
movl rSELF, %ebx
movl %eax, offThread_methodToCall(%ebx)
movl %ecx, offThread_callsiteClass(%ebx)
ret
#endif
#if defined(WITH_JIT)
/*
* If the JIT is actively building a trace we need to make sure
* that the field is fully resolved before including the current
* instruction.
*
* On entry:
* %ecx: &dvmDex->pResFields[field]
* %eax: field pointer (must preserve)
*/
common_verifyField:
movl %ebx, TMP_SPILL1(%ebp)
movl rSELF, %ebx
movzwl offThread_subMode(%ebx), %ebx
andl $$kSubModeJitTraceBuild, %ebx
movl TMP_SPILL1(%ebp), %ebx
jne 1f
ret
1:
movl (%ecx), %ecx
cmp $$0, %ecx
je 1f
ret
1:
SPILL_TMP1(%eax)
SPILL_TMP2(%edx)
movl rSELF, %ecx
# Because we call into this helper from a bytecode, we have
# to be careful not to write over the return address when using
# the OUT_ARG macros
lea -8(%esp), %esp
movl %ecx, OUT_ARG0(%esp)
movl rPC, OUT_ARG1(%esp)
call dvmJitEndTraceSelect
lea 8(%esp), %esp
UNSPILL_TMP2(%edx)
UNSPILL_TMP1(%eax)
ret
#endif
/*
* After returning from a "selfd" function, pull out the updated values
* and start executing at the next instruction.
*/
common_resumeAfterGlueCall:
movl rSELF, %eax
movl offThread_pc(%eax),rPC
movl offThread_curFrame(%eax),rFP
movl offThread_curHandlerTable(%eax),rIBASE
FETCH_INST
GOTO_NEXT
/*
* Integer divide or mod by zero
*/
common_errDivideByZero:
EXPORT_PC
movl $$.LstrDivideByZero,%eax
movl %eax,OUT_ARG0(%esp)
call dvmThrowArithmeticException
jmp common_exceptionThrown
/*
* Attempt to allocate an array with a negative size.
* On entry, len in eax
*/
common_errNegativeArraySize:
EXPORT_PC
movl %eax,OUT_ARG0(%esp) # arg0<- len
call dvmThrowNegativeArraySizeException # (len)
jmp common_exceptionThrown
/*
* Attempt to allocate an array with a negative size.
* On entry, method name in eax
*/
common_errNoSuchMethod:
EXPORT_PC
movl %eax,OUT_ARG0(%esp)
call dvmThrowNoSuchMethodError
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
xorl %eax,%eax
movl %eax,OUT_ARG0(%esp)
call dvmThrowNullPointerException
jmp common_exceptionThrown
/*
* Array index exceeds max.
* On entry:
* eax <- array object
* ecx <- index
*/
common_errArrayIndex:
EXPORT_PC
movl offArrayObject_length(%eax), %eax
movl %eax,OUT_ARG0(%esp)
movl %ecx,OUT_ARG1(%esp)
call dvmThrowArrayIndexOutOfBoundsException # args (length, index)
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.
*
* NOTE: special subMode handling done in dvmMterp_exceptionThrown
*
* This does not return.
*/
common_exceptionThrown:
.LexceptionNew:
EXPORT_PC
movl rSELF, %ecx
movl %ecx, OUT_ARG0(%esp)
call dvmCheckSuspendPending
movl rSELF, %ecx
movl offThread_exception(%ecx), %edx # %edx <- self->exception
movl %edx, OUT_ARG0(%esp)
movl %ecx, OUT_ARG1(%esp)
SPILL_TMP1(%edx)
call dvmAddTrackedAlloc # don't let the exception be GCed
UNSPILL_TMP1(%edx)
movl rSELF, %ecx
movl offThread_subMode(%ecx), %eax # get subMode flags
movl $$0, offThread_exception(%ecx)
# Special subMode?
cmpl $$0, %eax # any special subMode handling needed?
je 8f # go if so
# Manage debugger bookkeeping
movl rPC, offThread_pc(%ecx) # update interpSave.pc
movl rFP, offThread_curFrame(%ecx) # update interpSave.curFrame
movl %ecx, OUT_ARG0(%esp)
movl %edx, OUT_ARG1(%esp)
SPILL_TMP1(%edx)
call dvmReportExceptionThrow # (self, exception)
UNSPILL_TMP1(%edx)
movl rSELF, %ecx
8:
/*
* set up args and a local for &fp
*/
lea 20(%esp), %esp # raise %esp
movl rFP, (%esp) # save fp
movl %esp, %eax # %eax = &fp
lea -20(%esp), %esp # reset %esp
movl %eax, OUT_ARG4(%esp) # Arg 4 = &fp
movl $$0, OUT_ARG3(%esp) # Arg 3 = false
movl %edx, OUT_ARG2(%esp) # Arg 2 = exception
movl %ecx, OUT_ARG0(%esp) # Arg 0 = self
movl offThread_method(%ecx), %eax # %eax = self->method
movl offMethod_insns(%eax), %eax # %eax = self->method->insn
movl rPC, %ecx
subl %eax, %ecx # %ecx = pc - self->method->insn
sar $$1, %ecx # adjust %ecx for code offset
movl %ecx, OUT_ARG1(%esp) # Arg 1 = %ecx
/* call, %eax gets catchRelPc (a code-unit offset) */
SPILL_TMP1(%edx) # save exception
call dvmFindCatchBlock # call(self, relPc, exc, scan?, &fp)
UNSPILL_TMP1(%edx) # restore exception
/* fix earlier stack overflow if necessary; may trash rFP */
movl rSELF, %ecx
cmpl $$0, offThread_stackOverflowed(%ecx) # did we overflow?
je 1f # no, skip ahead
movl %eax, rFP # save relPc result in rFP
movl %ecx, OUT_ARG0(%esp) # Arg 0 = self
movl %edx, OUT_ARG1(%esp) # Arg 1 = exception
SPILL_TMP1(%edx)
call dvmCleanupStackOverflow # call(self, exception)
UNSPILL_TMP1(%edx)
movl rFP, %eax # restore result
movl rSELF, %ecx
1:
/* update frame pointer and check result from dvmFindCatchBlock */
movl 20(%esp), rFP # retrieve the updated rFP
cmpl $$0, %eax # is catchRelPc < 0?
jl .LnotCaughtLocally
/* adjust locals to match self->interpSave.curFrame and updated PC */
SAVEAREA_FROM_FP rINST # rINST<- new save area
movl offStackSaveArea_method(rINST), rINST # rINST<- new method
movl rINST, offThread_method(%ecx) # self->method = new method
movl offMethod_clazz(rINST), %ecx # %ecx = method->clazz
movl offMethod_insns(rINST), rINST # rINST = method->insn
movl offClassObject_pDvmDex(%ecx), %ecx # %ecx = method->clazz->pDvmDex
lea (rINST, %eax, 2), rPC # rPC<- method->insns + catchRelPc
movl rSELF, rINST
movl %ecx, offThread_methodClassDex(rINST) # self->pDvmDex = method->clazz->pDvmDex
/* release the tracked alloc on the exception */
movl %edx, OUT_ARG0(%esp) # Arg 0 = exception
movl rINST, OUT_ARG1(%esp) # Arg 1 = self
SPILL_TMP1(%edx)
call dvmReleaseTrackedAlloc # release the exception
UNSPILL_TMP1(%edx)
/* restore the exception if the handler wants it */
movl rSELF, %ecx
FETCH_INST
movzbl rINSTbl, %eax
cmpl $$OP_MOVE_EXCEPTION, %eax # is it "move-exception"?
jne 1f
movl %edx, offThread_exception(%ecx) # restore exception
1:
movl offThread_curHandlerTable(%ecx), rIBASE # refresh rIBASE
GOTO_NEXT
.LnotCaughtLocally: # %edx = exception
/* fix stack overflow if necessary */
movl rSELF, %ecx
movl offThread_stackOverflowed(%ecx), %eax
cmpl $$0, %eax # did we overflow earlier?
je 1f
movl %ecx, OUT_ARG0(%esp)
movl %edx, OUT_ARG1(%esp)
SPILL_TMP1(%edx)
call dvmCleanupStackOverflow
UNSPILL_TMP1(%edx)
1:
movl rSELF, %ecx
movl %edx, offThread_exception(%ecx) #restore exception
movl %edx, OUT_ARG0(%esp)
movl %ecx, OUT_ARG1(%esp)
call dvmReleaseTrackedAlloc # release the exception
movl rSELF, %ecx
jmp common_gotoBail # bail out
common_abort:
movl $$0xdeadf00d,%eax
call *%eax
/*
* Strings
*/
.section .rodata
.LstrDivideByZero:
.asciz "divide by zero"
.LstrFilledNewArrayNotImplA:
.asciz "filled-new-array only implemented for 'int'"