There are currently about 130 instructions in Guile’s virtual machine. These instructions represent atomic units of a program’s execution. Ideally, they perform one task without conditional branches, then dispatch to the next instruction in the stream.
Instructions themselves are composed of 1 or more 32-bit units. The low 8 bits of the first word indicate the opcode, and the rest of instruction describe the operands. There are a number of different ways operands can be encoded.
An unsigned n-bit integer. Usually indicates the index of a local variable, but some instructions interpret these operands as immediate values.
An offset from the current
ip, in 32-bit units, as a signed
24-bit value. Indicates a bytecode address, for a relative jump.
An immediate Scheme value (see Immediate objects), encoded directly in 16 or 32 bits.
An immediate Scheme value, encoded as a pair of 32-bit words.
b32 values always go together on the same opcode,
and indicate the high and low bits, respectively. Normally only used on
A statically allocated non-immediate. The address of the non-immediate
is encoded as a signed 32-bit integer, and indicates a relative offset
in 32-bit units. Think of it as
SCM x = ip + offset.
Indirect scheme value, like
n32 but indirected. Think of it as
SCM *x = ip + offset.
An ip-relative address, as a signed 32-bit integer. Could indicate a
bytecode address, as in
make-closure, or a non-immediate address,
lo32 are the same from the perspective of the
virtual machine. The difference is that an assembler might want to
lo32 address to be specified as a label and then some
number of words offset from that label, for example when patching a
field of a statically allocated object.
A boolean value: 1 for true, otherwise 0.
An ignored sequence of n bits.
An instruction is specified by giving its name, then describing its operands. The operands are packed by 32-bit words, with earlier operands occupying the lower bits.
For example, consider the following instruction specification:
Set free variable idx from the closure dst to src.
The first word in the instruction will start with the 8-bit value corresponding to the free-set! opcode in the low bits, followed by dst and src as 12-bit values. The second word starts with 8 dead bits, followed by the index as a 24-bit immediate value.
Sometimes the compiler can figure out that it is compiling a special case that can be run more efficiently. So, for example, while Guile offers a generic test-and-branch instruction, it also offers specific instructions for special cases, so that the following cases all have their own test-and-branch instructions:
(if pred then else) (if (not pred) then else) (if (null? l) then else) (if (not (null? l)) then else)
In addition, some Scheme primitives have their own inline
implementations. For example, in the previous section we saw
Guile’s instruction set is a complete instruction set, in that it provides the instructions that are suited to the problem, and is not concerned with making a minimal, orthogonal set of instructions. More instructions may be added over time.
|• Lexical Environment Instructions:|
|• Top-Level Environment Instructions:|
|• Procedure Call and Return Instructions:|
|• Function Prologue Instructions:|
|• Trampoline Instructions:|
|• Branch Instructions:|
|• Constant Instructions:|
|• Dynamic Environment Instructions:|
|• Miscellaneous Instructions:|
|• Inlined Scheme Instructions:|
|• Inlined Mathematical Instructions:|
|• Inlined Bytevector Instructions:|