NAME ^

PIR - calling conventions

VERSION ^

0.1 initial proposal

0.2 initial, checked in

0.3 updated parrot calling conventions and invoke

0.4 methods and shortcuts documented

OVERVIEW ^

This document describes subroutine and method 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 ^

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

The Short Way ^

  ...  # variable decls
  r = _sub_label(x, y, z)
  (r1[, r2 ...]) = _sub_label(x, y, z)
  _sub_label(x, y, z)

Instead of the label a Subroutine object can be used too:

   find_global $P0, "_sub_label"
   $P0(args)

Subroutines ^

  .sub _sub_label [Subpragma, ...]
   .param int a # I5
   .param int b # I6
   .param int c # I7
  ...
  .pcc_begin_return
   .return xy   # e.g. I5
  .pcc_end_return
  ...
  .end

An alternative syntaxs allow to express a return in one line. The surrounded parentheses are mandatory . Besides making sequence break more conspiscuous, this is necessary to distinguish this syntax from other uses of the .return directive that will be probably deprecated.

  .return ( a, b )      # return the values of a and b

  .return ()            # return no value

Similarly, one can yield using the .yield directive

  .yield ( a, b )      # yield with the values of a and b

  .yield ()            # yield with no value

Subpragma ^

This is a comma separated list of zero or more items with the following meaning:

Notes:

Getting the Parameter Count ^

The reserved words argcI, argcS, argcP, and argcN hold the count of passed parameters (or return values) according to docs/pdds/pdd03_calling_conventions.pod. The variable is_prototyped is an alias for I0.

Calling Methods ^

The syntax is very similar to subroutine calls. The call is done with meth_call which must immediately be preceded by the .invocant:

   .local pmc class
   .local pmc obj
   newclass class, "Foo"
   find_type $I0, "Foo"
   new obj, $I0
  .pcc_begin prototyped|non_prototyped
  .arg x        # I5
  .arg y        # I6
  .arg z        # I7
  .invocant obj
  .meth_call "_method" [, $P1 ] # r = obj."_method"(x, y, z)
  .local int r  # optional - new result var
  .result r
  .pcc_end

The return continuation is optional. The method can be a string constant or a string variable.

Shortcuts ^

  r = obj."_method"(args)
  (r1, r2) = obj."_method"(args)
  obj."_method"(args)

Methods ^

  .namespace [ "Foo" ]

  .sub _sub_label method [,Subpragma, ...]
   .param int a # I5
   .param int b # I6
   .param int c # I7
   ...
   self."_other_meth"()
  ...
  .pcc_begin_return
   .return xy   # e.g. I5
  .pcc_end_return
  ...
  .end

The variable "self" automatically refers to the invocating object, if the subroutine declaration contains "method".

Restore namespace to the global namespace:

  .namespace [ "" ]

NCI ^

Proposed syntax:

  load_lib $P0, "libname"
  dlfunc $P1, $P0, "funcname", "signature"
  ...
  .pcc_begin prototyped
  .arg x        # I5
  .arg y        # I6
  .arg z        # I7
  .nci_call $P1 # r = funcname(x, y, z)
  .local int r  # optional - new result var
  .result r
  .pcc_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.

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/imcc.y, imcc/t/syn/bsr.t, imcc/t/syn/pcc.t, imcc/t/syn/objects.t, docs/pdds/pdd03_calling_conventions.pod

AUTHOR ^

Leopold Toetsch <lt@toetsch.at>


parrot