14.4 Source code of primitive variants

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

cb7 = < ?branch

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.