Subs - Parrot Subroutines


Parrot comes with different subroutine and alike classes which implement CPS (Continuation Passing Style) and PCC (Parrot Calling Conventions) docs/pdds/pdd03_calling_conventions.pod.

Please note, that this document refers to PASM assembler only. The PIR assembler has a more HLL-like syntax for Parrot Calling Conventions. S. imcc/docs/calling_conventions.pod

Class Tree ^


Items in the Subs Context ^

  Subtype       Controlstack  PadStack UserStack RegStacks Warnings
  Sub                -           -         -         -        C
  Closure            -           C         -         -        C
  Continuation       C           C         C         C        C
  Coroutine          C           C         C         C        C
  RetContinuation    X           X         X         X        X

  "C" ... COWed copy is in context
  "X" ... is in context
  "-" ... isn't.


Creation ^

Create a subroutine of class Sub and assign the subroutine address to it:

  new P0, 'Sub'
  set_addr P0, _sub_label

This can be done with one opcode:

  newsub P0, .Sub, _sub_label

Create a subroutine (in P0) and a return continuation (in P1):

  newsub .Sub, .RetContinuation, _sub_label, ret_label

Refering to existing Subs ^

Subroutines denoted with .pcc_sub (and all PIR .sub subroutines that use Parrot Calling Conventions) are stored in the constant table and can be fetched with the find_global opcode.

E.g. get a reference to a (possibly) external subroutine:

  find_global P0, "_the_sub"
  .pcc_sub _the_sub:

Program entry point ^

Exactly one subroutine in the first executed source or byte code file may be flagged as the "main" subroutine, where executions starts.

  .pcc_sub :main _main:

In the absence of a :main entry Parrot starts execution at the first statement.

Automatically loaded initializer code ^

If a subroutine is marked as :load this subroutine is run, before the load_bytecode opcode returns.


  .pcc_sub :main _main:
     print "in main\n"
     load_bytecode "library_code.pasm"

# library_code.pasm

  .pcc_sub :load _my_lib_init:
     invoke P1

:load is ignored, if another subroutine in that file is marked with :main.

If a subroutine is marked as :init this subroutine is run, before the :main or the first subroutine in the source file runs.

Invocation i.e. calling the sub ^

  invoke        # call the subroutine in P0 (P1 was created earlier)

  invokecc      # call sub in P0 and create return continuation in P1

Returning from a sub ^

  invoke P1     # call return continuation in P1

All together now ^

The following scheme can be used if a subroutine is called once or if performance doesn't matter:

    newsub P0, .Sub, _sub_label # create subroutine
    set I5, 42                  # pass an argument
    invokecc                    # create ret continuation and call sub
    end                         # fin.
    print I5                    # do something with parameters
    invoke P1                   # return

If a subroutine is called several times, for instance inside a loop, the creation of the return continuation can be done outside the loop if performance is an issue:

    newsub .Sub, .RetContinuation, _sub_label, ret_label
    set I16, 1000000
    set I17, 0
    pushtopi            # preserve counter vars
    inc I17
    lt I17, I16, lp
    # do_something
    invoke P1

If items in the interpreter context are changed between creation of the subroutine/return continuation and its invocation, the updatecc opcode should be used, so that the state of the return continuation matches that of the interpreter:

    newsub .Sub, .RetContinuation, _sub_label, ret_label
    warningson 1

Generating a Subroutine Symbol Table Entry ^

When a subroutine label is prefixed by .pcc_sub, the name of the subroutine (i.e. the label) gets stored in the global stash.

    find_global P0, "_the_sub"
    print "back\n"

  .pcc_sub _the_sub:
    print "in sub\n"
    invoke P1

Optimized Tail Calls ^

    find_global P0, "_the::sub"
    print "back\n"

  .pcc_sub _the::sub:
    print "in sub\n"                    # must preserve P1
    find_global P0, "_next::sub"
    get_addr I0, P0                     # get the absolute address
    jump I0                             # jump to absolute address

  .pcc_sub _next::sub:                  # must preserve P1
    print "in next sub\n"
    invoke P1                           # return to main


src/pmc/sub.pmc, src/pmc/closure.pmc, src/pmc/continuation.pmc, src/pmc/coroutine.pmc, sub.c, t/pmc/sub.t


docs/pdds/pdd03_calling_conventions.pod imcc/docs/calling_conventions.pod


Leopold Toetsch <>