Second attempt and cc'ed to other Perl lists too.

-------- Original Message --------
Subject: [PROPOSAL] call syntax abstraction
Date: Tue, 03 May 2005 13:58:14 +0200

Comments welcome,

=head1 TITLE

Calling convention abstraction


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.


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

=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


  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<> 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


  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]

  .sub "foo"
     params "IN", I16, N16     [4]
     returns "I", 1            [5]

=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]

  .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

=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


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
        case 'i':  i = $N_i;  // get Nth arg as INTVAL constant
        case 'P':  p = $N_P;  // get Nth arg as PMC
     // 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

  IREG(2+n)  aka  REG_INT(cur_opcode[2+n])

similar to the current $1, $2, ...

=head1 AUTHOR

Leopold Toetsch

=head1 SEE ALSO


Reply via email to