The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Subs - Parrot Subroutines

DESCRIPTION

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

  Sub
    Closure
      Continuation
	Coroutine
      Eval
      RetContinuation

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.

SYNOPSIS

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.

e.g.

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

# library_code.pasm

   ...
.pcc_sub @LOAD _my_lib_init:
   ...
   invoke P1

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.
_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
...

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"
  invokecc
  print "back\n"
  end

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

Optimized Tail Calls

  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

FILES

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

SEE ALSO

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

AUTHOR

Leopold Toetsch <lt@toetsch.at>