9.5 Stacks and Register Frames

     

9.5 Stacks and Register Frames

Parrot provides 32 registers of each type: integer, floating-point number, string, and PMC. This is a generous number of registers, but it's still too restrictive for the average use. You can hardly limit your code to 32 integers at a time. This is especially true when you start working with subroutines and need a way to store the caller's values and the subroutine's values. So, Parrot also provides stacks for storing values outside the 32 registers. Parrot has seven basic stacks, each used for a different purpose: the user stack, the control stack, the pad stack, and the four register-backing stacks.

9.5.1 User Stack

The user stack, also known as the general-purpose stack, stores individual values. The two main opcodes for working with the user stack are save , to push a value onto the stack, and restore , to pop one off the stack:

 save 42         # push onto user stack restore I1      # pop off user stack 

The one argument to save can be either a constant or a register. The user stack is a typed stack, so restore will only pop a value into a register of the same type as the original value:

 save 1 set I0, 4 restore I0 print I0        # prints 1 end 

If that restore were restore N0 instead of an integer register, you'd get an exception, "Wrong type on top of stack!"

A handful of other instructions are useful for manipulating the user stack. rotate_up rotates a given number of elements on the user stack to put a different element on the top of the stack. The depth opcode returns the number of entries currently on the stack. The entrytype opcode returns the type of the stack entry at a given depth, and lookback returns the value of the element at the given depth without popping the element off the stack:

 save 1 save 2.3 set S0, "hi\n" save S0 save P0 entrytype I0, 0 print I0         # prints 4 (PMC) entrytype I0, 1 print I0         # prints 3 (STRING) entrytype I0, 2 print I0         # prints 2 (FLOATVAL) entrytype I0, 3 print I0         # prints 1 (INTVAL) print "\n" depth I2         # get entries print I2         # prints 4 print "\n" lookback S1, 1   # get entry at depth 1 print S1         # prints "hi\n" depth I2         # unchanged print I2         # prints 4 print "\n" end 

This example pushes four elements onto the user stack: an integer, a floating-point number, a string, and a PMC. It checks the entrytype of all four elements and prints them out. It then checks the depth of the stack, gets the value of the second element with a lookback , and checks that the number of elements hasn't changed.

9.5.2 Control Stack

The control stack, also known as the call stack, stores return addresses for subroutines called by bsr and exception handlers. There are no instructions for directly manipulating the control stack.

9.5.3 Register Frames

The final set of stacks are the register backing stacks. Parrot has four backing stacks, one for each type of register. Instead of saving and restoring individual values, the backing stacks work with register frames. Each register frame is the full set of 32 registers for one type. Each frame is separated into two halves : the bottom half (registers 0-15) and the top half (registers 16-32). Some opcodes work with full frames while others work with half-frames. The backing stacks are commonly used for saving the contents of all the registers (or just the top half of each frame) before a subroutine call, so they can be restored when control returns to the caller.

PASM has five opcodes for storing full register frames, one for each register type and one that saves all four at once:

 pushi               # copy I-register frame pushn               # copy N-register frame pushs               # copy S-register frame pushp               # copy P-register frame saveall             # copy all register frames 

Each pushi , pushn , pushs , or pushp pushes a register frame containing all the current values of one register type onto the backing stack of that type. saveall simply calls pushi , pushn , pushs , and pushp .

PASM also has five opcodes to restore full register frames. Again, it has one for each register type and one that restores all four at once:

 popi                # restore I-register frame popn                # restore N-register frame pops                # restore S-register frame popp                # restore P-register frame restoreall          # restore all register frames 

The popi , popn , pops , and popp opcodes pop a single register frame off a particular stack and replace the values in all 32 registers of that type with the values in the restored register frame. restoreall calls popi , popn , pops , and popp , restoring every register of every type to values saved earlier.

Saving a register frame to the backing stack doesn't alter the values stored in the registers; it simply copies the values:

 set I0, 1 print I0            # prints 1 pushi               # copy away I0..I31 print I0            # unchanged, still 1 inc I0 print I0            # now 2 popi                # restore registers to state of previous pushi print I0            # old value restored, now 1 print "\n" end 

This example sets the value of I0 to 1 and stores the complete set of integer registers. Before I0 is incremented, it has the same value as before the pushi .

In Section 9.2.2 earlier in this chapter, we mentioned that string and PMC registers hold pointers to the actual objects. When string or PMC register frames are saved, only the pointers are copied , not the actual contents of the strings or PMCs. The same is true when string or PMC register frames are restored:

 set S0, "hello"          # set S0 to "hello" pushs substr S0, 0, 5, "world" # alter the string in S0 set S0, "test"           # set S0 to a new string pops                     # restores the first string pointer print S0                 # prints "world" end 

In this example, we first use the pushs opcode to copy the string pointer to the string register frame stack. This gives us two pointers to the same underlying string, with one currently stored in S0 , and the other saved in the string register frame stack. If we then use substr to alter the contents of the string, both pointers will now point to the altered string, and so restoring our original pointer using pops does not restore the original string value.

Each of the above pushX and popX opcodes has a variant that will save or restore only the top or bottom half of one register set or all the register sets:

 pushtopi            # save I16..I31 popbottoms          # restore S0..S15 savetop             # save regs 16-31 in each frame restoretop          # restore regs 16-31 in each frame 

PASM also has opcodes to clear individual register frames: cleari , clearn , clears , and clearp . These reset the numeric registers to 0 values and the string and PMC registers to null pointers, which is the same state that they have when the interpreter first starts.

The user stack can be useful for holding onto some values that would otherwise be obliterated by a restoreall :

 #  . . .  coming from a subroutine save I5     # Push some registers save I6     # holding the return values save N5     # of the sub. restoreall  # restore registers to state before calling subroutine restore N0  # pop off last pushed restore I0  # pop 2nd restore I1  # and so on 



Perl 6 and Parrot Essentials
Perl 6 and Parrot Essentials, Second Edition
ISBN: 059600737X
EAN: 2147483647
Year: 2003
Pages: 116

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net