You can find the code that is responsible for generating all the
additional variants of the primitives in peeprules.vmg. If you
build Gforth yourself, you can experiment with changing that. If you
break something, it only breaks the gforth-fast engine.
First, you find the static superinstructions there, such as
for having a static superinstruction for < ?branch. You will
find many commented-out lines in this block; most of them are
commented out because they no longer provide a benefit with stack
caching and the IP-update optimization implemented. A few are
commented out because the resulting code does not work.
For these compare-and-branch superinstructions there are branch-to-IP
variants such as cb7a which are surrounded with code that
generates branch-to-ip variants of the same code.
Next, you see variants related to stack caching and IP-uodate optimization. The simplest variant is something like
\E S2 S1 state-prim swap
which results in generating the 2-1 variant for swap.
If the engine does not have stack state 2, no code is
generated. Code like
\E prim-states +
generates all variants that do not change the stack pointer and that
consume and produce all stack items in registers. For + that
means the variants 2-1 and 3-2 (if the highest stack
state is 3). Using prim-states produces good results
for most primitives, but for stack manipulation words such as
swap additional variants are useful and we specify them
explicitly with state-prim.
\E branch-states execute
generates variants that take their inputs from a register and that end in
state 1; for execute it generates 2-1 and
3-1.
Similarly, for the variants with IP offsets we have:
\E S1 S1 state-offset-prim call
This generates variants for call 1-1 for a range of IP
offsets (0–23 is the current setting in the generator).
\E prim-states-offsets lit
generates variants for stack-state transitions 0-1, 1-2
and 2-3 (all with all stack items in registers and without
stack-pointer update) for a range of IP offsets, resulting in 3*24=72
variants in this case.
You see comments with numbers in many of these lines; they just indicate the dynamic usage frequency of the primitive in some benchmark programs.
Towards the end you see
\E gen-ip-updates noop
which generates the IP-update variants of noop, and
\E gen-transitions noop
which generates the stack-state transition variants of noop.
Finally, there is some not very polished code for generating the branch-to-IP variants of conditional branch primitives.
You can build Gforth with a non-default number of maximum stack items in registers by configuring it with, e.g.,:
./configure STACK_CACHE_REGS=4
Note that more registers result in slower compilation in Gforth, but can produce speedups (with diminishing returns); the current limits are a good compromise. Also, the architecture may not have enough registers, and asking for more will result in slowdowns due to putting “registers” in memory.