[DRAFT] Stack Frames and calling convention - scramjet-js/scramjet GitHub Wiki
Goals
- Minimize function call overhead
- Minimize copying
- Maximize locality
- Maximize function performance
Overview
The interpreter maintains a stack of Frame
objects; the top of the stack
is the one being executed. When a function is called, a new Frame
is
created and pushed onto the stack. When a function returns, the last
frame is popped and execution continues with the previous frame
(if there was one) or stops if there are no more frames.
Calling convention
Invocation
- Push arguments on stack
- Push argument count on stack
- Push function address (or offset) on stack
- Execute appropriate call opcode
Return
- Push return value onto stack
- Execute
e_Exit
opcode
Stack Frame Structure
A given stack frame has as context:
firstCode
-- pointer to first opcode, used to compute offsetspc
-- program counter, next operation to executebottom
-- bottom of the stack for this frame
Interpreter operation
Invocation
- When a function is called, the interpreter pops off the stack an integer containing the number of arguments for the function.
- If the number of argument is less than
Bytecode::s_MinimumLocals
, the interpreter pushesnumLocals - s_MinimumLocals
values containingDatumUdtUtil::s_Undefined
.
Return
- The interpreter saves the top of the stack -- the return value.
- Then it resets the top of the stack back to the index before the
bottom
of the last rame.
Implications for function evaluation
- Arguments are immediately available as locals without copying.
- Access to locals must be computed (albeit fairly simply).
- Functions with more (named) arguments than the minimum
guaranteed number of locals (or those wanting more
locals for whatever reason) may call
reserve
to get more. - Basically, by convention (but not required), a function may treat the bottom of its stack as a set of local variables; its arguments are put there by default.
Notes on performance
- Uniform value size (of
Datum
) allows for random access of the stack. - Other than pushing arguments on the stack, no additional copying is required.
- Function call overhead involves just initializing a few integers and pointers.
- Only dynamic per-frame storage is in the (single) program stack.