Below is a POD that tries to the describe an extensible calling scheme
that should cover most of our targets HLLs call syntax.
Comments welcome,
leo
=head1 TITLE
Calling convention abstraction
=head1 ABSTRACT
The current Parrot calling conventions as described in
F<docs/pdds/pdd03_calling_conventions.pod> are not covering major
parts of our target languages. The scheme isn't extensible and uses a
lot of resources (opcodes, runloop dispatches, registers) to achieve
it's work.
This proposal describes an abstract, extensible, and more efficient
alternative to pdd03.
=head1 DESCRIPTIOON
All the work related to function calls is done by dedicated opcodes.
No registers are reserved currently, but during the transition phase
and possibly thereafter, registers are/may be used. This will be
specified after the implementation of the compatibly scheme is
completed.
=head2 Opcodes
New B<variable argument list> opcodes:
op args(inconst STR, ...) # call arguments
op results(inconst STR, ...) # get return results
op params(inconst STR, ...) # function parameters
op returns(inconst STR, ...) # function return
op yields(inconst STR, ...) # coroutine yield values
The constant STR argument is a signature string denoting successional
arguments to the opcode.
An opcode to define return context:
op results(inconst INT) # define return context
And:
op argcI(out INT) # amount of I args or returns
op argcP(out INT) # amount of P
op argcS(out INT) # amount of S
op argcN(out INT) # amount of N
=head2 Call opcodes cleanup
While it's not strictly needed it's highly desirable to get rid of the
current implicit register usage in call related opcodes.
See also: I<http://xrl.us/fycc> for an older discussion on that topic.
op invoke(in PMC, in PMC) # $1 = sub/method, $2 = continuation
op invoke(in STR, in PMC) # invocants are covered by args
op invokecc(in PMC) # $1 = sub/meth, create continuation [1]
op invokecc(in STR) # invocants are covered by args
op tailcall(in PMC) # $1 = sub/method
op tailcall(in STR)
[1] if the called thing isa NCI PMC the creation of the continuation
is skipped.
=head2 Signature chars - current calling scheme coverage
I ... INTVAL var
i ... INTVAL constant
N,n,S,s,P,p ... analog
O ... single PMC invocant
@ ... call: flatten array to next args
=head2 Signature chars - possible extensions
@ ... call: flatten
params/results: make array
% ... call: flatten **kw hash
params(/results): make hash
kX ... call: named arguments (key => X)
OO ... call: 2 PMC invocants
: ... call: end of invocants marker
nX ... params: default arguments (name = X)
? ... params: optional part follows
=P ... params/returns: clone P (or maybe cP)
& ... ruby code block
E.g.
args "PP", P10, P20 # function call
args "OIS", P5, I10, S30 # method call
args "P:IS", P5, I10, S30 # same method call
args "P@", P0, P1 # flatten P1, args become P0, *P1
args "%", P2 # P2 is a **kw hash
args "kPkP", "$a", P0, "$b", P1 # named arguments "$a" => P0, ...
params "ni", "$i", 1 # default argument "$i" = 1
params "P?PPP" # await 1 - 4 arguments passed in
=head2 Return context
results 0 # void
results 1 # 1 return result
results n # n return result
results -1 # list context
As the I<results_ic> opcode goes before the call, we can attach a perlish
return context to the return continuation and make it available in the
called function.
=head2 Simple example
.sub main @MAIN
args "IN", I16, N16 [1]
invokecc "foo" [2]
results "I", I16 [3]
.end
.sub "foo"
params "IN", I16, N16 [4]
...
returns "I", 1 [5]
.end
=head2 Comparison with current syntax
.sub main @MAIN
set I0, 1 [1]
set I1, 1
set I2, 0
set I3, 0
set I4, 1
set I5, I16
set N5, N16
set S1, "IN"
invokecc "foo" [2]
set I16, I5 [3]
.end
.sub "foo"
set I16, I5 [4}
set N16, N5
...
set I0, 1 [5]
set I1, 1
set I2, 0
set I3, 0
set I4, 0
set I5, 1
returncc
.end
=head2 opcode and dispatch count comparison
current scheme proposed scheme
opcodes call/result 29 9
dispatches 10 3
opcodes param/ret 25 7
dispatches 9 2
opcodes overall 54 16
dispatches 19 5
=head2 Example main
.sub main @MAIN
params "@", P5 # argv array like now
params "SS", S0, S1 # at least two string arguments
params "S?SS", S0, S1, S2 # 1...3 string arguments
params "IS", I20, S2 # 1st param as int, string
=head1 Implementation notes
=head2 Steps
=over 4
=item * The opcodes "translate" their arguments according to the current
calling conventions.
=item * PIRs call/return syntax shortcuts emit the new opcodes
=item * New Parrot API to access call related stuff replaces current explicit
register access
=item * convert existing PASM code to use new opcodes
=back
Abstraction finished.
=head2 Implementations details:
The new opcodes get some support by the ops-file compiler. The basic
layout is:
op args(inconst STR) {
// internal variable declarations
// user code
VA_SWITCH { // big loop and switch
case 'I': i = $N_I; // get Nth arg as INTVAL
break;
case 'i': i = $N_i; // get Nth arg as INTVAL constant
break;
case 'P': p = $N_P; // get Nth arg as PMC
break;
...
}
// user code
goto NEXT(2+N);
}
The VA_SWITCH is something like:
STRING *sig = $1; // internal variable declaration
size_t n, l;
char *p;
for (n = 0, p = sig->strstart, l = sig->strlen; n < l; ++n, ++p) {
switch(*p) {
// case statements go here
}
}
The $N_I gets according to the I<OpTrans/*.pm> expanded to something
like:
IREG(2+n) aka REG_INT(cur_opcode[2+n])
similar to the current $1, $2, ...
=head1 AUTHOR
Leopold Toetsch
=head1 SEE ALSO
F<docs/pdds/pdd03_calling_conventions.pod>