# Playing with the Return Stack

## Local variables

It's a common pattern, to avoid PS juggling, to place an element on RS (Return
Stack) and recall that element with r@. It works well, but unfortunately, this
only works with a single element. Another drawback is that this element can be
"buried" by a pattern (a "next" loop for example) that needs to place something
on RS.

A workaround to this is to declare a "private" value near the word and store
temporary values in there. It works too, but because those values are statically
allocated, this can't be done in words that require recursion. Also, it's a bit
verbose.

Local variables are there to the rescue! There are 4 of them: V1 V2 V3 V4. These
words do a simple thing: they reference an element on RS with "to" semantics
[doc/usage/bind]. They are also impervious to being "buried" because they
remember their "slot" at compile time and adjust their compilation accordingly.

In other words, if RSTART is the address in memory of RSP when the word starts
executing and considering the fact that RSP grows "downwards" in memory,
V1=RSTART-4 and V4=RSTART-16.

When using local variables, you are responsible for pushing and popping to/from
RS. All those variables give "to" semantics to an "RS slot". Example:

  : foo ( a b c -- ) >r >r >r V1 . spc> V2 . spc> V3 . 2rdrop rdrop ;

  1 2 3 foo \ prints "3 2 1"

  : inc ( a -- a+1 ) >r 1 to+ V1 V1 rdrop ;
  42 inc . \ prints 43

  \ this works too
  : inc5 ( a -- a+5 ) >r 5 >r begin 1 to+ V1 next V1 rdrop ;
  42 inc5 . \ prints 47

## Fancy RS allocation

Sometimes, you want to store data that is more than 4 bytes in size into RS. For
this, there is [rallot] and [rfree], both immediates. [rallot] parses the
following word and expects a size to be placed on PS. It compiles a negated rs+,
of that size and then compiles code that pushes current RSP to PS, that is, the
address of the newly allocated space on RS. In the "immediate" space, it pushed
the allocated size to PS.

When you're finished using that RS space, you use [rfree] which takes the size
that [rallot] pushed to PS and compiles at rs+, of that size.

RSP must at all time stay 32-bit aligned, so [rallot] takes care of aligning
allocated size to 32-bit. [rfree] trusts [rallot] and doesn't do this.
Therefore, if the input for [rfree] doesn't come from [rallot], it should be
32-bit aligned.

  : foo ( mystruct -- )
    [rallot] MyStruct SZ tuck MyStruct SZ move ( local-struct-copy )
    \ Play with the local struct copy
    [rfree] ;

## Manual [rcnt] adjustments

The "R counter" that determines local variable slots at compile time is
oblivious to conditional code or loops. It's not common to have code that
conditionally maintain separate RS levels (they always need to stay balanced, of
course), but it can happen. For example, in early "exit" paths, we often have to
include a few "rdrop" before the "exit" call. If you have a fancy word that
returns a variable number of arguments, you might end up with >r and r> in
loops. All of this messes up the "R counter". You can manually adjust it through
the [rcnt] variable. For example, if you want your next ">r" to push to V1, you
would precede it with:

  [ 0 [rcnt] ! ]

Another use for manual [rcnt] adjustments is to have local variables to "below"
the word line. For example, if for some reason you'd want "V1" to be bound to
the word's return address, you can assign -4 to [rcnt]:

  : foo [ -4 [rcnt] ! ] $1234 to V1 ; \ returns to address $1234
