7.10.函数运行实现
\(7.10.\)Function Call and Return Implementation
1.The caller's view
In the calling function's view:
- Before calling another function, I must push as many arguments as the function expects to get;
- Next, I invoke the function using
call functionName nArgs
; - After the called function returns, the argument values that I pushed before the call have disappeared from the stack, and a return value (that always exists) appears at the top of the stack;
- After the called function returns, all my memory segments are exactly the same as they were before the call (except that temp is undefined and some values of my static segment may have changed).
2.The callee's view
In the called function's view:
- Before I start executing, my
argument
segment has been initialized with the argument values passed by the caller. - My
local
variables segment has been allocated and initialized to zeros. - My
static
segment has been set to thestatic
segment of the VM file to which I belong (memory segmentsthis
,that
,pointer
, andtemp
are undefined upon entry). - My working stack is empty.
- Before returning, I must push a value onto the stack.
3.Handling call
We can implement the VM code by these steps:
In terms of call
, we do these steps:
Push a label onto the stack, and use the same label when we are going to return after the called function terminates.
1
push returnAddress
Push some label that we generate to save the memory segment of the caller:
1
2
3
4push LCL
push ARG
push THIS
push THATThe
argument
should be repositioned for the called function, and we should reposition it at the beginning of theARG
segment:- We can calculate the address, because we know how many values we
pushed. Say we push 5 values the we do :
1
ARG = SP - 5
- We can calculate the address, because we know how many values we
pushed. Say we push 5 values the we do :
Push the
local
segment to insure that the function can visit its local variables correctly :1
LCL = SP
We execute the called function by simply writing the command
goto nameOfFunction
:1
goto functionName
We insert the
returnAddress
label, so that when the function has been executed, we can get back to the place we invoke it :1
(returnAddress) //Declares a label for the return-address
4.Handling function
The big picture of handling function
is that:
- The first thing translator does is it takes the function name and generates a label, and the label will serve as the entry point to the translated assembly code of the function.
- We simply write some assembly code that handles the setting up of the function's execution.
- We take the function name and generate its label:
- Since we know how many local variables we have to create, we do
push 0
nVars
times.1
2
3(functionName)
//repeat nVars times
push 0
4.Handling return
After setting up, the function can start doing its thing. We need
to generate assembly code that moves the return value to the
caller, reinstates the caller's state, and finally goto
the
return address.
- Create some temporary variable (called
endFrame
) and assign the value ofLCL
to it:1
endFrame = LCL
If you look at the stack diagram, you will see that
endFrame
indeed points at the end of the frame in
the host RAM.
Since we should put the return value at the top of the stack, say we have 5 values, the
endFrame - 5
will be the exact return address.- We need to use
*
to look inside and get the address.1
retaddr = *(endFrame - 5)
- We need to use
Reposition the return value for the caller.
- The value should be copied to
argument 0
, and we already have a pointerARG
located in the position pop
is going to retrieve the return value off the stack. We take this value and put it inARG
.1
*ARG = pop()
- The value should be copied to
The caller expects to see a return value and continue to do its work, so the stack pointer should be just after
ARG
.
1
SP = ARG + 1
Then we can begin to recover the various segments that we saved on the stack before.
1
2
3
4THAT = *(endFrame - 1)
THIS = *(endFrame - 2)
ARG = *(endFrame - 3)
LCL = *(endFrame - 4)Finally we can jump to the return address we copied before:
1
goto retAddr
- Every thing below
SP
is recycled. The next push is going to override the memory location whereSP
shows. - The caller will see the return value at the top of its working stack.
- The stack pointer will point at the next world in the memory.