NAME
IMCC - calling conventions
VERSION
OVERVIEW
This document describes subroutine calling conventions.
DESCRIPTION
As imcc does register allocation, it has to track the life span of variables. This includes the (possible) data flow in and out of subroutines.
Parrot calling conventions - CPS
PASM Subroutines
newsub $P0, .Sub, _sub_label
newsub $P1, .Continuation, ret_addr
...
.pcc_begin prototyped|non_prototyped
.arg x # I5
.arg y # I6
.arg z # I7
.pcc_call $P0, $P1 # r = _sub_label(x, y, z)
ret_addr:
.local int r # optional - new result var
.result r
.pcc_end
.pcc_sub _sub_label [prototyped|non_prototyped]
.param int a # I5
.param int b # I6
.param int c # I7
...
.pcc_begin_return
.return xy # e.g. I5
.pcc_end_return
...
.end
Notes:
prototyped, non_rototyped
If a subroutine doesn't have any prototyped specifier, code gets emitted to receive parameters by both flavors.
newsub
Currently needs the .Class syntax, i.e. a dot in front of the class name.
pcc_call
Takes either 2 arguments: the sub and the return continuation, or the sub only. For the latter case an invokecc gets emitted.
Saved Regs:
Only the top half of registers are preserved currently.
.args, .param, .result, and .return are optional.
.param
The .param declarations must be the first statements in the sub if any. No other statements are allowed between .param.
NCI
Proposed syntax:
$P0 = load_lib "libname"
$P1 = dlfunc $P0, "funcname", "signature"
...
.nci_begin
.arg x # I5
.arg y # I6
.arg z # I7
invoke $P1 # r = funcname(x, y, z)
.local int r # optional - new result var
.result r
.nci_end
This prepares parameters as described in pdd03_calling_conventions.pod, saves the registers and invokes the function. The .arg pseudo ops put the given argument into increasing registers of the appropriate type.
Status
Partially implemented, s. t/*/pcc.t.
Exception handlers
TBD.
Stack calling conventions
Arguments are saved in reverse order onto the user stack:
.arg y # save args in reversed order
.arg x
call _foo #(r, s) = _foo(x,y)
.local int r
.local int s
.result r # restore results in order
.result s #
and return values are restored in argument order from there.
The subroutine is responsible for preserving registers.
.sub _foo # sub foo(int a, int b)
saveall
.param int a # receive arguments from left to right
.param int b
...
.return mi # return (pl, mi), push results
.return pl # in reverse order
restoreall
ret
.end
Rational
Pushing arguments in reversed order on the user stack makes the left most argument the top of stack entry. This allows for a variable number of function arguments (and return values), where the left most argument before a variable number of following arguments is the argument count.
Status
Implemented. When the subroutine is in the same compilation unit, the callee can saveall registers; when the subroutine is in a different compilation unit, the callee must preserve all used registers.
Invoking subroutines
IMCC tries to keep track of the address where the invoke will branch to, but can only succeed to do so when the set_addr and the invoke opcodes are located together.
$P10 = new Sub
$I1 = addr _the_sub
$P10 = $I1
invoke $P10 # ok
But not:
bsr get_addr
invoke $P10 # error
...
get_addr:
$P10 = new Sub
$I1 = addr _the_sub
$P10 = $I1
ret
The latter example will very likely lead to an incorrect CFG and thus to incorrect register allocation.
Status
Implemented. When the subroutine does saveall/restoreall, the branch from the ret statement back is ignored in the CFG.
Namespaces and lexicals
- Should imcc keep track of pad opcodes?
- Should imcc even emit such opcodes from e.g. .local directives?
FILES
imcc.y, t/syn/bsr.t
AUTHOR
Leopold Toetsch <lt@toetsch.at>