compile,
¶You can also change the implementation of compile,
for a word,
with
set-optimizer
( xt – ) gforth-1.0 “set-optimizer”
Changes the current word such that compile,
ing it
executes xt (with the same stack contents as passed to
compile,
). Note that compile,
must be consistent
with execute
, so you must use set-optimizer
only
to install a more efficient implementation of the same
behaviour.
opt:
( compilation – colon-sys2 ; run-time – nest-sys ) gforth-1.0 “opt:”
Starts a nameless colon definition; when it is complete, this
colon definition will become the compile,
implementation
of the latest word (before the opt:
).
Note that the resulting compile,
must still be equivalent to
postpone literal postpone execute
, so set-optimizer
is
useful for efficiency, not for changing the behaviour. There is
nothing that prevents you from shooting yourself in the foot, however.
You can check whether your uses of set-optimizer
are correct by
comparing the results when you use it with the results you get when
you disable your uses by first defining
: set-optimizer drop ;
As an example of the use of set-optimizer
, we can enhance one
of the definitions of CONSTANT
above as follows.
: CONSTANT ( n "name" -- ; name: -- n ) create , ['] @ set-does> [: >body @ postpone literal ;] set-optimizer ;
The only change is the addition of the set-optimizer
line.
When you define a constant and compile it:
5 constant five : foo five ;
the compiled five
in foo
is now compiled to the literal
5 instead of a generic invocation of five
. The quotation has
the same stack effect as compile,
: ( xt -- )
. The
passed xt belongs to the compile,
d word, i.e., five
in
the example. In the example the xt is first converted to the body
address, then the value 5 at that place is fetched, and that value is
compiled with the postpone literal
(see Literals).
This use of set-optimizer
assumes that the user does not change
the value of a constant with, e.g., 6 ' five >body !
. While
five
has been defined with create
, that is an
implementation detail of CONSTANT
, and if you don’t document
it, the user must not rely on it. And if you use set-optimizer
in a way that assumes that the body does not change (like is done
here), you must not document that create
is used; and
conversely, if you document it, you have to write the compile,
implementation such that it can deal with changing bodies.
Another example is a better-optimized variant of the fvalue
example above:
: compile-fvalue-to ( xt-value-to -- ) drop ]] >body f! [[ ; : fvalue-to ( r xt -- ) >body f! ; ' compile-fvalue-to set-optimizer : fvalue ( r "name" -- ; name: -- r ) create f, ['] f@ set-does> [: >body ]] literal f@ [[ ;] set-optimizer ['] fvalue-to set-to ; 5e fvalue foo : bar foo 1e f+ to foo ; see bar
Compare the code for bar
with the one for the earlier
definition. Here we see the optimization of both the code for reading
the fvalue (coming from the set-optimizer
in fvalue
) and
for writing the fvalue (coming from the set-optimizer
applied
to fvalue-to
. Because an fvalue can change (unlike a
constant), the reading part (inside fvalue
) compiles the
address and a f@
that is performed at run-time.
For fvalue-to
, the compile,
implementation basically
just compiles the code executed by fvalue
inline. The
compilation semantics of to
compiles the address as literal and
then the (to)
implementation (i.e., fvalue-to
). In this
process the >body
is optimized away.
In practice Gforth’s fvalue
includes a few additional twists,
e.g., to support +TO
.
Note that the call to set-optimizer
has to be performed after
the call to set-does>
(or does>
, because
set-does>
overwrites the compile,
implementation itself.
As we can see in the fvalue-to
example, we can also apply
set-optimizer
to individual words rather than inside a defining
word like constant
or fvalue
. In this case, the xt of
the word passed to optimizer is usually unnecessary and is
drop
ped, as in compile-fvalue-to
.
The engine gforth-itc
uses ,
for compile,
and
set-optimizer
has no effect there.