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>

Reply via email to