parrotcode: Parrot Calling Conventions | |
Contents | Documentation |
docs/pdds/pdd03_calling_conventions.pod - Parrot Calling Conventions
Parrot's inter-routine calling conventions.
$Revision$
This document describes how to pass arguments from registers to subroutines, and how subroutines can extract their parameters into registers for use.
Since Parrot's calling conventions are continuation-based, there is arguably very little difference between a call and a return. Because of this, the conversion rules are the same regardless of whether code is invoking a subroutine or a return continuation.
There are four opcodes involved in parameter and return value propagation:
set_args
,
for passing arguments;set_returns
,
for returning values;get_params
,
for accepting parameters; andget_results
,
for accepting return values.FAQ: Given Parrot's internal use of continuation-passing style ["CPS"], it would be possible to use one pair of opcodes for both call and return, since under CPS returns are calls. And perhaps someday we will have only two opcodes. But for now, certain efficiency hacks are easier with four opcodes.)
The common syntax of these opcodes is:
<set_opcode> "flags0, flags1, ..., flagsN", VAL0, VAL1, ... VALN
<get_opcode> "flags0, flags1, ..., flagsN", REG0, REG1, ... REGN
<get_opcode> "..., 0x200, flags0, ...", ..., "name", REG0, ...
The flags string is a literal quoted string denoting a list of zero or more comma-separated integers. Integers may be specified either in decimal, or if prefixed with "0b"/"0x", in binary/hexadecimal. There must be exactly one integer for each value or register given.
For documentation purposes we'll number the bits 0 (low) through 30 (high). Bit 31 (and higher, where available) will not be used.
Some values and registers do not correspond directly to values passed or received. (See the descriptions of the OPT_FLAG and NAMED bits, below.)
Each integer in the flag string controls the processing of the corresponding value or register.
These bits of each flag word have common meanings for all argument/ return-value opcodes:
0-3 TYPE
0b0000 = I
0b0001 = S
0b0010 = P
0b0011 = N
Don't set these bits yourself; the assembler will do it.
Just before calling a subroutine with invokecc
or calling a method with call_methodcc
, use the set_args
opcode to tell Parrot where the subroutine's or method's arguments will come from and how they should be expanded by the target.
Similarly, just before returning from such a subroutine or method, use the set_returns
opcode to tell Parrot where the return values will come from and how to expand them for the caller's use.
These bits of each flag word have these meanings specific to set_args
and set_returns
:
CONSTANT
FLAT
(P only)NAMED
bit is also set, the aggregate will be used as a hash; its contents, as key/value pairs, will be passed as named parameters. The PMC must implement the full hash interface. {{ RT#45367: Limit the required interface. }}NAMED
bit is not set, the aggregate will be used as an array; its contents will be passed as positional parameters.NAMED
(FLAT
or string constant only)As the first opcode in a subroutine that will be called with invokecc
or a method that will be called with call_methodcc
, use the get_params
opcode to tell Parrot where the subroutine's or method's arguments should be stored and how they should be expanded.
Similarly, just before (yes, before) calling such a subroutine or method, use the get_results
opcode to tell Parrot where the return values should be stored and how to expand them for your use.
NOTE: It should be obvious, but in case it's not: You must name only registers as targets of these opcodes, not constants. (You can't store anything into a constant. That would make it a variable.)
These bits of each flag word have these meanings specific to get_params
and get_results
:
SLURPY
(P only)NAMED
bit is also set, the aggregate will be the HLL-specific hash type and the contents will be all unassigned _named_ parameters.NAMED
bit is not set, the aggregate will be the HLL-specific array type and the contents will be all unassigned positional parameters.OPTIONAL
OPT_FLAG
(I only)READONLY
(P only)NAMED
(SLURPY
or string constant only)If too many or too few values are provided for the given target registers, Parrot by default will throw an exception for get_params
, but not for get_results
. This error behavior can be controlled via the errorson
and errorsoff
opcodes using PARROT_ERRORS_PARAM_COUNT_FLAG
for get_params
and PARROT_ERRORS_RESULT_COUNT_FLAG
for get_results
. (It is not possible to control underflow behavior separately from overflow.)
Note that if the final target is a P register with FLAT set, then overflow can never occur. Similarly, if all target registers are marked OPTIONAL, then underflow is impossible.
Note also that when these errors are off, any excess destination registers in the case of underflow are left untouched, rather than being reset to zero or null. Excess registers explicitly marked OPTIONAL are always reset, regardless of the error flag settings.
RT#45357 - which exception? We really could use an exception subsystem. Oh, wait, that's my job. Never mind. --Chip
Named values (arguments, or values to return) must be listed textually after all the positional values. FLAT
and non-FLAT
values may be mixed in any order.
Named targets (parameters, or returned values) must appear after all the positional targets. A SLURPY
positional target, if present, must be the last positional target; a SLURPY
named target, if present, must be the last named target.
So the acceptable ordering of targets is:
Positional targets can only be filled with positional values.
Named targets can be filled with either positional or named values. However, if a named target was already filled by a positional value, and then a named value is also given, this is an overflow error.
Unlike the set_*
opcodes, the get_*
opcodes must perform conversion from one register type to another. Here are the conversion rules:
assign
(standard conversion).set
(pass by reference).assign
ed the given integer.assign
ed the given number.assign
ed the given string.[1] or some other type specified by the current HLL type map, which may substitute an alternative type for each default low-level Parrot type (array, hash, string, number, etc.).
Required features are missing:
foo(1, i) # 2 positional arguments
foo(x, ar :flat, y) # flattening array
foo(p, 'key' => value) # named argument
foo(p, value :named('key')) # the same
foo(kw :named :flat) # a flattening hash
# all together now: three positional (one flat) with two named (one flat)
foo(a, b, c :flat, 'x' => 3, 'y' => 4, z :flat :named('z'))
.param int i # positional parameter
.param pmc argv :slurpy # slurpy array
.param pmc value :named('key') # named parameter
.param int x :optional # optional parameter
.param int has_x :opt_flag # flag 0/1 x was passed
.param pmc kw :slurpy :named # slurpy hash
.return (i, ar: flat, value :named('key') )
x = foo() # single result
(i, j :optional, ar :slurpy, value :named('key') ) = foo()
None.
|