NAME ^

PIR - calling conventions

VERSION ^

0.5

NOTE ABOUT CHANGES IN PROGRESS ^

The syntax here is going to stay, but the calling convention is in the middle of a transition. Instead of fixed registers, the new convention allows the user to use any registers in any order, and converts types as required. Additional syntax features, like the :flat adverb for arguments and :slurpy for parameters, will make sense to explain here once the document as a whole is updated.

One thing that's handy: If you have a list in a PMC and you want it to be flattened out into a list, append :flat to the argument name in the .arg directive or in the shortcut parameter list.

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 ^

Explicitly Calling PASM Subroutines ^

  .const .Sub $P0 = "_sub_label" 
  $P1 = new 'Continuation'
  set_addr $P1, ret_addr
  ...
  .local int x
  .local num y
  .local str z
  .pcc_begin
  .arg x
  .arg y
  .arg z
  .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

  .return func_call()   # tail call function

  .return o."meth"()    # tail method call

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 list of zero or more items with the following meanings:

:main

Define "main" entry point to start execution. If multiple subroutines are marked as :main, the last marked subroutine is entered.

:load

Run this subroutine during the load_library opcode. :load is ignored, if another subroutine in that file is marked with :main. If multiple subs have the :load pragma, the subs are run in source code order.

:init

Run the subroutine when the program is run directly (that is, not loaded as a module). This is different from :load, which runs a subroutine when a library is being loaded. To get both behaviours, use :init :load.

:anon

Do not install this subroutine in the namespace. Allows the subroutine name to be reused.

:multi(Type1, Type2...)

Engage in multiple dispatch with the listed types.

:immediate or :postcomp

This subroutine is executed immediately after being compiled. (Analagous to BEGIN in perl5.)

:method

The marked .sub is a method. In the method body, the object PMC can be referred to with self.

:vtable

The marked .sub overrides a v-table method. By default, a sub with the same name as a v-table method does not override the v-table method. To specify that there should be no namespace entry (that is, it just overrides the v-table method but is callable as a normal method), use :vtable :anon. To give the v-table method a different name, use :vtable("..."). For example, to have the method ToString also be the v-table method get_string), use :vtable("get_string").

:outer(subname)

The marked .sub is lexically nested within the sub known by subname.

Notes:

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

AUTHOR ^

Leopold Toetsch <lt@toetsch.at>

CHANGES ^

0.5 eliminate {non_,}prototyped distinction

0.4 methods and shortcuts documented

0.3 updated parrot calling conventions and invoke

0.2 initial, checked in

0.1 initial proposal


parrot