|parrotcode: Parrot Subroutines|
|Contents | Documentation|
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
Sub Closure Continuation Coroutine Eval RetContinuation
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.
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
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 get_hll_global and get_root_global opcodes.
E.g. get a reference to a (possibly) external subroutine:
get_hll_global $P0, "_the_sub" ... .pcc_sub _the_sub:
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.
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" ...
... .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.
invoke # call the subroutine in P0 (P1 was created earlier) invokecc # call sub in P0 and create return continuation in P1
invoke P1 # call return continuation in P1
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. _sub_label: 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 lp: pushtopi # preserve counter vars invoke ret_label: poptopi inc I17 lt I17, I16, lp end _sub_label: # 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 ... updatecc invoke ...
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" invokecc print "back\n" end .pcc_sub _the_sub: print "in sub\n" invoke P1
find_global P0, "_the::sub" invokecc print "back\n" end .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
Leopold Toetsch <firstname.lastname@example.org>