This section offers some insight into bytecode design in the form of questions and answers.
Question:
Does it make sense to define a new bytecode instruction for
RESTART-CASE
? Why? Why not?
RESTART-CASE
is a glorified LET
binding
for SYSTEM::*ACTIVE-RESTARTS*
and could well profit
from a separate bytecode: it would make it non-consing[3].
(Remember that RESTART
s have dynamic extent and therefore do not
really need to be heap allocated.)
The reason HANDLER-BIND
has its own bytecodes and
RESTART-CASE
does not is that HANDLER-BIND
can occur in inner
computation loops, whereas RESTART-CASE
occurs only as part of
user-interface programming and therefore not in inner loops where its
consing could hurt much.
Question:
Consider this function and its disassembly:
(defun foo (x y) (if (or (= x 0) (= y 0)) (+ x y) (foo y (1- x))))
(DISASSEMBLE
'foo)
8 (LOAD&PUSH 1)
9 (LOAD&DEC&PUSH 3)
11 (JMPTAIL 2 5 L0)
Why are the arguments pushed onto the STACK
, just to be popped off of
it during the JMPTAIL
?
Why not a sequence of LOAD
,
STORE
and
SKIP
instructions
followed by a JMP
?
Using JMPTAIL
requires 3
instructions, JMP
requires more.
When JMPTAIL
needs to be called, we
usually have some stuff close to the top of the STACK
which will
become the new arguments, and some junk between these new arguments
and the closure object. JMPTAIL
removes the junk. JMPTAIL
is a
convenient shortcut which shortens the bytecode - because typically
one would really have to clean-up the STACK
by hand or make the
calculations in src/compiler.lisp
more complicated.
These notes document CLISP version 2.49.93+ | Last modified: 2018-02-19 |