Subroutines and methods are the basic building blocks of larger programs. At the heart of every subroutine call are two fundamental actions: it has to store the current location so it can come back to it, and it has to transfer control to the subroutine. The bsr opcode does both. It pushes the address of the next instruction onto the control stack, and then branches to a label that marks the subroutine: print "in main\n" bsr _sub print "and back\n" end _sub: print "in sub\n" ret At the end of the subroutine, the ret instruction pops a location back off the control stack and goes there, returning control to the caller. The jsr opcode pushes the current location onto the call stack and jumps to a subroutine. Just like the jump opcode, it takes an absolute address in an integer register, so the address has to be calculated first with the set_addr opcode: print "in main\n" set_addr I0, _sub jsr I0 print "and back\n" end _sub: print "in sub\n" ret 9.7.1 Calling Conventions A bsr or jsr is fine for a simple subroutine call, but few subroutines are quite that simple. The biggest issues revolve around register usage. Parrot has 32 registers of each type, and the caller and the subroutine share the same set of registers. How does the subroutine keep from destroying the caller's values? More importantly, who is responsible for saving and restoring registers? Where are arguments for the subroutine stored? Where are the subroutine's return values stored? A number of different answers are possible. You've seen how many ways Parrot has of storing values. The critical point is that the caller and the called subroutine have to agree on all the answers. 9.7.1.1 Reserved registers A very simple system would be to declare that the caller uses registers through 15, and the subroutine uses 16-31. This works in a small program with light register usage. But what about a subroutine call from within another subroutine or a recursive call? The solution doesn't extend to a large scale. 9.7.1.2 Callee saves Another possibility is to make the subroutine responsible for saving the caller's registers: set I0, 42 save I0 # pass args on stack bsr _inc # j = inc(i) restore I1 # restore args from stack print I1 print "\n" end _inc: saveall # preserve all registers restore I0 # get argument inc I0 # do all the work save I0 # push return value restoreall # restore caller's registers ret This example stores arguments to the subroutine and return values from the subroutine on the user stack. The first statement in the _inc subroutine is a saveall to save all the caller's registers onto the backing stacks, and the last statement before the return restores them. One advantage of this approach is that the subroutine can choose to save and restore only the register frames it actually uses, for a small speed gain. The example above could use pushi and popi instead of saveall and restoreall because it uses only integer registers. One disadvantage is that it doesn't allow optimization of tail calls, where the last statement of a recursive subroutine is a call to itself. 9.7.1.3 Parrot-calling conventions Internal subroutines can use whatever calling convention serves them best. Externally visible subroutines and methods need stricter rules. Since these routines may be called as part of an included library or module and even from a different high-level language, it's important to have a consistent interface. Under the Parrot-calling conventions the caller is responsible for preserving its own registers. The first 11 arguments of each register type are passed in Parrot registers, as are several other pieces of information. Register usage for subroutine calls is listed in Table 9-4. Table 9-4. Calling and return conventions Register | Usage | P0 | Subroutine/method object | P1 | Return continuation if applicable | P2 | Object for a method call (invocant) or NULL for a subroutine call | P3 | Array with overflow parameters/return values | S0 | Fully qualified method name , if it's a method call | I0 | True for prototyped parameters | I1 | Number of integer arguments/return results | I2 | Number of string arguments/return results | I3 | Number of PMC arguments/return results | I4 | Number of float arguments/return results | I5 . . . I15 | First 11 integer arguments/return results | N5 . . . N15 | First 11 float arguments/return results | S5 . . . S15 | First 11 string arguments/return results | P5 . . . P15 | First 11 PMC arguments/return results | If there are more than 11 arguments or return values of one type for the subroutine, overflow parameters are passed in an array in P3 . Subroutines without a prototype pass all their arguments or return values in P registers and if needed in the overflow array. [8] [8] Prototyped subroutines have a defined signature. The _inc subroutine from above can be rewritten as a prototyped subroutine: set I16, 42 # use local regs from 16..31 newsub P0, .Sub, _inc # create a new Sub object set I5, I16 # first integer argument set I0, 1 # prototype used set I1, 1 # one integer argument null I2 # no string arguments null I3 # no PMC arguments null I4 # no numeric arguments null P2 # no object (invocant) pushtopi # preserve top I register frame invokecc # call function object in P0 poptopi # restore registers print I5 print "\n" # I16 is still valid here, whatever the subroutine did end .pcc_sub _inc: inc I5 # do all the work set I0, 1 # prototyped return set I1, 1 # one retval in I5 null I2 # nothing else null I3 null I4 invoke P1 # return from the sub Instead of using a simple bsr , this set of conventions uses a subroutine object. There are several kinds of subroutine-like objects, but Sub is a class for PASM subroutines. The .pcc_sub directive defines globally accessible subroutine objects. The _inc function above can be found as: find_global P20, "_inc" Subroutine objects of all kinds can be called with the invoke opcode. With no arguments, it calls the subroutine in P0 , which is the standard for the Parrot-calling conventions. There is also an invoke P x instruction for calling objects held in a different register. The invokecc opcode is like invoke , but it also creates and stores a new return continuation in P1 . When the called subroutine invokes this return continuation, it returns control to the instruction after the function call. This kind of call is known as Continuation Passing Style (CPS). In a simple example like this, it isn't really necessary to set up all the registers to obey to the Parrot-calling conventions. But when you call into library code, the subroutine is likely to check the number and type of arguments passed to it. So it's always a good idea to follow the full conventions. This is equally true for return values. The caller might check how many arguments the subroutine really returned. Setting all these registers for every subroutine call might look wasteful at first glance, and it does increase the size of the bytecode, but you don't need to worry about execution time: the JIT system executes each register setup opcode in one CPU cycle. 9.7.2 Native Call Interface A special version of the Parrot-calling conventions are used by the Native Call Interface (NCI) for calling subroutines with a known prototype in shared libraries. This is not really portable across all libraries, but it's worth a short example. This is a simplified version of the first test in t/pmc/nci.t : loadlib P1, "libnci" # get library object for a shared lib print "loaded\n" dlfunc P0, P1, "nci_dd", "dd" # obtain the function object print "dlfunced\n" set I0, 1 # prototype used - unchecked set N5, 4.0 # first argument invoke # call nci_dd ne N5, 8.0, nok_1 # the test functions returns 2*arg print "ok 1\n" end nok_1: . . . This example shows two new instructions: loadlib and dlfunc . The loadlib opcode obtains a handle for a shared library. It searches for the shared library in the current directory, in runtime/parrot/dynext , and in a few other configured directories. It also tries to load the provided filename unaltered and with appended extensions like .so or .dll . Which extensions it tries depends on the operating system on which Parrot is running. The dlfunc opcode gets a function object from a previously loaded library (second argument) of a specified name (third argument) with a known function signature (fourth argument). The function signature is a string where the first character is the return value and the rest of the parameters are the function parameters. The characters used in NCI function signatures are listed in Table 9-5. Table 9-5. Function signature letters Character | Register set | C type | v | - | void (no return value) | c | I | char | s | I | short | i | I | int | l | I | long | f | N | float | d | N | double | t | S | char * | p | P | void * (or other pointer) | I | - | Parrot_Interp * interpreter | C | - | A callback function pointer | D | - | A callback function pointer | Y | P | The subroutine into which C or D calls | Z | P | The argument for Y | For more information on callback functions, read the documentation in docs/pdds/pdd16_native_call.pod and docs/pmc/struct.pod . 9.7.3 Closures A closure is a subroutine that retains values from the lexical scope where it was defined, even when it's called from an entirely different scope. The closure shown here is equivalent to this Perl 5 code snippet: # sub foo { # my ($n) = @_; # sub {$n += shift} # } # my $closure = foo(10); # print &$closure(3), "\n"; # print &$closure(20), "\n"; # call _foo newsub P16, .Sub, _foo # new subroutine object at address _foo new P17, .PerlInt # value for $n set P17, 10 # we use local vars from P16 . . . set P0, P16 # the subroutine set P5, P17 # first argument pushtopp # save registers invokecc # call foo poptopp # restore registers set P18, P5 # the returned closure # call _closure new P19, .PerlInt # argument to closure set P19, 3 set P0, P18 # the closure set P5, P19 # one argument pushtopp # save registers invokecc # call closure(3) poptopp print P5 # prints 13 print "\n" # call _closure set P19, 20 # and again set P5, P19 set P0, P18 pushtopp invokecc # call closure(20) poptopp print P5 # prints 33 print "\n" end _foo: new_pad 0 # push a new pad store_lex -1, "$n", P5 # store $n newsub P5, .Closure, _closure # P5 has the lexical "$n" in the pad invoke P1 # return _closure: find_lex P16, "$n" # invoking the closure pushes the lexical pad # of the closure on the pad stack add P16, P5 # $n += shift set P5, P16 # set return value invoke P1 # return That's quite a lot of PASM code for such a little bit of Perl 5 code, but anonymous subroutines and closures hide a lot of magic under that simple interface. The core of this example is that when the new subroutine is created in _foo with: newsub P5, .Closure, _closure it inherits and stores the current lexical scratchpad ”the topmost scratchpad on the pad stack at the time. Later, when _closure is invoked from the main body of code, the stored pad is automatically pushed onto the pad stack. So, all the lexical variables that were available when _closure was defined are available when it's called. 9.7.4 Coroutines As we mentioned in Chapter 8, coroutines are subroutines that can suspend themselves and return control to the caller ”and then pick up where they left off the next time they're called, as if they never left. In PASM, coroutines are subroutine-like objects: newsub P0, .Coroutine, _co_entry The Coroutine object has its own user stack, register frame stacks, control stack, and pad stack. The pad stack is inherited from the caller. The coroutine's control stack has the caller's control stack prepended, but is still distinct. When the coroutine invokes itself, it returns to the caller and restores the caller's context (basically swapping all stacks). The next time the coroutine is invoked, it continues to execute from the point at which it previously returned: new_pad 0 # push a new lexical pad on stack new P0, .PerlInt # save one variable in it set P0, 10 store_lex -1, "var", P0 newsub P0, .Coroutine, _cor # make a new coroutine object saveall # preserve environment invoke # invoke the coroutine restoreall print "back\n" saveall invoke # invoke coroutine again restoreall print "done\n" pop_pad end _cor: find_lex P1, "var" # inherited pad from caller print "in cor " print P1 print "\n" inc P1 # var++ saveall invoke # yield( ) restoreall print "again " branch _cor # next invocation of the coroutine This prints out the result: in cor 10 back again in cor 11 done The invoke inside the coroutine is commonly referred to as yield . The coroutine never ends. When it reaches the bottom, it branches back up to _cor and executes until it hits invoke again. The interesting part about this example is that the coroutine yields in the same way that a subroutine is called. This means that the coroutine has to preserve its own register values. This example uses saveall but it could have only stored the registers the coroutine actually used. Saving off the registers like this works because coroutines have their own register frame stacks. 9.7.5 Continuations A continuation is a subroutine that gets a complete copy of the caller's context, including its own copy of the call stack. Invoking a continuation starts or restarts it at the entry point: new P1, .PerlInt set P1, 5 newsub P0, .Continuation, _con _con: print "in cont " print P1 print "\n" dec P1 unless P1, done invoke # P0 done: print "done\n" end This prints: in cont 5 in cont 4 in cont 3 in cont 2 in cont 1 done 9.7.6 Evaluating a Code String This isn't really a subroutine operation, but it does produce a code object that can be invoked. In this case, it's a bytecode segment object. The first step is to get an assembler or compiler for the target language: compreg P1, "PASM" Within the Parrot interpreter there are currently three registered languages: PASM , PIR , and PASM1 . The first two are for Parrot assembly language and Parrot intermediate represention code. The third is for evaluating single statements in PASM. Parrot automatically adds an end opcode at the end of PASM1 strings before they're compiled. This example places a bytecode segment object into the destination register P0 and then invokes it with invoke : compreg P1, "PASM1" # get compiler set S1, "in eval\n" compile P0, P1, "print S1" invoke # eval code P0 print "back again\n" end You can register a compiler or assembler for any language inside the Parrot core and use it to compile and invoke code from that language. These compilers may be written in PASM or reside in shared libraries. compreg "MyLanguage", P10 In this example the compreg opcode registers the subroutine-like object P10 as a compiler for the language "MyLanguage". See examples/compilers and examples/japh/japh16.pasm for an external compiler in a shared library. |