1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-06-09 13:30:26 +02:00

Document new VM instructions

* doc/ref/vm.texi (Stack Layout): Add a note about unboxed values.
  (Instruction Set): Update for new instructions.
This commit is contained in:
Andy Wingo 2016-02-01 21:21:38 +01:00
parent 5fceaed5e1
commit a653271f98

View file

@ -189,6 +189,12 @@ new call frame.
In this way, the dynamic link links the current frame to the previous In this way, the dynamic link links the current frame to the previous
frame. Computing a stack trace involves traversing these frames. frame. Computing a stack trace involves traversing these frames.
Each stack local in Guile is 64 bits wide, even on 32-bit architectures.
This allows Guile to preserve its uniform treatment of stack locals
while allowing for unboxed arithmetic on 64-bit integers and
floating-point numbers. @xref{Instruction Set}, for more on unboxed
arithmetic.
As an implementation detail, we actually store the dynamic link as an As an implementation detail, we actually store the dynamic link as an
offset and not an absolute value because the stack can move at runtime offset and not an absolute value because the stack can move at runtime
as it expands or during partial continuation calls. If it were an as it expands or during partial continuation calls. If it were an
@ -461,7 +467,7 @@ compiled @code{.go} files. It's good times!
@node Instruction Set @node Instruction Set
@subsection Instruction Set @subsection Instruction Set
There are currently about 130 instructions in Guile's virtual machine. There are currently about 175 instructions in Guile's virtual machine.
These instructions represent atomic units of a program's execution. These instructions represent atomic units of a program's execution.
Ideally, they perform one task without conditional branches, then Ideally, they perform one task without conditional branches, then
dispatch to the next instruction in the stream. dispatch to the next instruction in the stream.
@ -551,6 +557,16 @@ In addition, some Scheme primitives have their own inline
implementations. For example, in the previous section we saw implementations. For example, in the previous section we saw
@code{cons}. @code{cons}.
Finally, for instructions with operands that encode references to the
stack, the interpretation of those stack values is up to the instruction
itself. Most instructions expect their operands to be tagged SCM values
(@code{scm} representation), but some instructions expect unboxed
integers (@code{u64} and @code{s64} representations) or floating-point
numbers (@var{f64} representation). Instructions have static types:
they must receive their operands in the format they expect. It's up to
the compiler to ensure this is the case. Unless otherwise mentioned,
all operands and results are boxed as SCM values.
@menu @menu
* Lexical Environment Instructions:: * Lexical Environment Instructions::
* Top-Level Environment Instructions:: * Top-Level Environment Instructions::
@ -564,6 +580,8 @@ implementations. For example, in the previous section we saw
* Inlined Scheme Instructions:: * Inlined Scheme Instructions::
* Inlined Mathematical Instructions:: * Inlined Mathematical Instructions::
* Inlined Bytevector Instructions:: * Inlined Bytevector Instructions::
* Unboxed Integer Arithmetic::
* Unboxed Floating-Point Arithmetic::
@end menu @end menu
@ -707,10 +725,8 @@ in which to save the @code{ip} and @code{fp}.
Returning values is similar. Multiple-value returns should have values Returning values is similar. Multiple-value returns should have values
already shuffled down to start from @code{fp}-relative slot 1 before already shuffled down to start from @code{fp}-relative slot 1 before
emitting @code{return-values}. There is a short-cut in the single-value emitting @code{return-values}. We start from slot 1 instead of slot 0
case, in that @code{return} handles the trivial shuffling itself. We to make tail calls to @code{values} trivial.
start from slot 1 instead of slot 0 to make tail calls to @code{values}
trivial.
In both calls and returns, the @code{sp} is used to indicate to the In both calls and returns, the @code{sp} is used to indicate to the
callee or caller the number of arguments or return values, respectively. callee or caller the number of arguments or return values, respectively.
@ -772,10 +788,6 @@ return values equals @var{nvalues} exactly. After @code{receive-values}
has run, the values can be copied down via @code{mov}, or used in place. has run, the values can be copied down via @code{mov}, or used in place.
@end deftypefn @end deftypefn
@deftypefn Instruction {} return s24:@var{src}
Return a value.
@end deftypefn
@deftypefn Instruction {} return-values c24:@var{nlocals} @deftypefn Instruction {} return-values c24:@var{nlocals}
Return a number of values from a call frame. This opcode corresponds to Return a number of values from a call frame. This opcode corresponds to
an application of @code{values} in tail position. As with tail calls, an application of @code{values} in tail position. As with tail calls,
@ -935,6 +947,13 @@ not generated by the compiler.
Load a builtin stub by index into @var{dst}. Load a builtin stub by index into @var{dst}.
@end deftypefn @end deftypefn
@deftypefn Instruction {} apply-non-program x24:@var{_}
An instruction used only by a special trampoline that the VM uses to
apply non-programs. Using that trampoline allows profilers and
backtrace utilities to avoid seeing the instruction pointer from the
calling frame.
@end deftypefn
@node Branch Instructions @node Branch Instructions
@subsubsection Branch Instructions @subsubsection Branch Instructions
@ -990,10 +1009,9 @@ See @code{libguile/tags.h} for all the details.
@deftypefn Instruction {} br-if-eq s24:@var{a} x8:@var{_} s24:@var{b} b1:@var{invert} x7:@var{_} l24:@var{offset} @deftypefn Instruction {} br-if-eq s24:@var{a} x8:@var{_} s24:@var{b} b1:@var{invert} x7:@var{_} l24:@var{offset}
@deftypefnx Instruction {} br-if-eqv s24:@var{a} x8:@var{_} s24:@var{b} b1:@var{invert} x7:@var{_} l24:@var{offset} @deftypefnx Instruction {} br-if-eqv s24:@var{a} x8:@var{_} s24:@var{b} b1:@var{invert} x7:@var{_} l24:@var{offset}
@deftypefnx Instruction {} br-if-equal s24:@var{a} x8:@var{_} s24:@var{b} b1:@var{invert} x7:@var{_} l24:@var{offset} If the value in @var{a} is @code{eq?} or @code{eqv?} to the value in
If the value in @var{a} is @code{eq?}, @code{eqv?}, or @code{equal?} to @var{b}, respectively, add @var{offset} to the current instruction
the value in @var{b}, respectively, add @var{offset} to the current pointer.
instruction pointer.
@end deftypefn @end deftypefn
@deftypefn Instruction {} br-if-= s24:@var{a} x8:@var{_} s24:@var{b} b1:@var{invert} x7:@var{_} l24:@var{offset} @deftypefn Instruction {} br-if-= s24:@var{a} x8:@var{_} s24:@var{b} b1:@var{invert} x7:@var{_} l24:@var{offset}
@ -1179,6 +1197,10 @@ Reference the fluid in @var{src}, and place the value in @var{dst}.
Set the value of the fluid in @var{dst} to the value in @var{src}. Set the value of the fluid in @var{dst} to the value in @var{src}.
@end deftypefn @end deftypefn
@deftypefn Instruction {} current-thread s24:@var{dst}
Write the value of the current thread to @var{dst}.
@end deftypefn
@node Miscellaneous Instructions @node Miscellaneous Instructions
@subsubsection Miscellaneous Instructions @subsubsection Miscellaneous Instructions
@ -1234,12 +1256,14 @@ be filled with the value in slot @var{init}.
@end deftypefn @end deftypefn
@deftypefn Instruction {} vector-length s12:@var{dst} s12:@var{src} @deftypefn Instruction {} vector-length s12:@var{dst} s12:@var{src}
Store the length of the vector in @var{src} in @var{dst}. Store the length of the vector in @var{src} in @var{dst}, as an unboxed
unsigned 64-bit integer.
@end deftypefn @end deftypefn
@deftypefn Instruction {} vector-ref s8:@var{dst} s8:@var{src} s8:@var{idx} @deftypefn Instruction {} vector-ref s8:@var{dst} s8:@var{src} s8:@var{idx}
Fetch the item at position @var{idx} in the vector in @var{src}, and Fetch the item at position @var{idx} in the vector in @var{src}, and
store it in @var{dst}. store it in @var{dst}. The @var{idx} value should be an unboxed
unsigned 64-bit integer.
@end deftypefn @end deftypefn
@deftypefn Instruction {} vector-ref/immediate s8:@var{dst} s8:@var{src} c8:@var{idx} @deftypefn Instruction {} vector-ref/immediate s8:@var{dst} s8:@var{src} c8:@var{idx}
@ -1248,7 +1272,8 @@ Fill @var{dst} with the item @var{idx} elements into the vector at
@end deftypefn @end deftypefn
@deftypefn Instruction {} vector-set! s8:@var{dst} s8:@var{idx} s8:@var{src} @deftypefn Instruction {} vector-set! s8:@var{dst} s8:@var{idx} s8:@var{src}
Store @var{src} into the vector @var{dst} at index @var{idx}. Store @var{src} into the vector @var{dst} at index @var{idx}. The
@var{idx} value should be an unboxed unsigned 64-bit integer.
@end deftypefn @end deftypefn
@deftypefn Instruction {} vector-set!/immediate s8:@var{dst} c8:@var{idx} s8:@var{src} @deftypefn Instruction {} vector-set!/immediate s8:@var{dst} c8:@var{idx} s8:@var{src}
@ -1263,16 +1288,19 @@ Store the vtable of @var{src} into @var{dst}.
@deftypefn Instruction {} allocate-struct s8:@var{dst} s8:@var{vtable} s8:@var{nfields} @deftypefn Instruction {} allocate-struct s8:@var{dst} s8:@var{vtable} s8:@var{nfields}
Allocate a new struct with @var{vtable}, and place it in @var{dst}. The Allocate a new struct with @var{vtable}, and place it in @var{dst}. The
struct will be constructed with space for @var{nfields} fields, which struct will be constructed with space for @var{nfields} fields, which
should correspond to the field count of the @var{vtable}. should correspond to the field count of the @var{vtable}. The @var{idx}
value should be an unboxed unsigned 64-bit integer.
@end deftypefn @end deftypefn
@deftypefn Instruction {} struct-ref s8:@var{dst} s8:@var{src} s8:@var{idx} @deftypefn Instruction {} struct-ref s8:@var{dst} s8:@var{src} s8:@var{idx}
Fetch the item at slot @var{idx} in the struct in @var{src}, and store Fetch the item at slot @var{idx} in the struct in @var{src}, and store
it in @var{dst}. it in @var{dst}. The @var{idx} value should be an unboxed unsigned
64-bit integer.
@end deftypefn @end deftypefn
@deftypefn Instruction {} struct-set! s8:@var{dst} s8:@var{idx} s8:@var{src} @deftypefn Instruction {} struct-set! s8:@var{dst} s8:@var{idx} s8:@var{src}
Store @var{src} into the struct @var{dst} at slot @var{idx}. Store @var{src} into the struct @var{dst} at slot @var{idx}. The
@var{idx} value should be an unboxed unsigned 64-bit integer.
@end deftypefn @end deftypefn
@deftypefn Instruction {} allocate-struct/immediate s8:@var{dst} s8:@var{vtable} c8:@var{nfields} @deftypefn Instruction {} allocate-struct/immediate s8:@var{dst} s8:@var{vtable} c8:@var{nfields}
@ -1291,12 +1319,14 @@ Make a new array with @var{type}, @var{fill}, and @var{bounds}, storing it in @v
@end deftypefn @end deftypefn
@deftypefn Instruction {} string-length s12:@var{dst} s12:@var{src} @deftypefn Instruction {} string-length s12:@var{dst} s12:@var{src}
Store the length of the string in @var{src} in @var{dst}. Store the length of the string in @var{src} in @var{dst}, as an unboxed
unsigned 64-bit integer.
@end deftypefn @end deftypefn
@deftypefn Instruction {} string-ref s8:@var{dst} s8:@var{src} s8:@var{idx} @deftypefn Instruction {} string-ref s8:@var{dst} s8:@var{src} s8:@var{idx}
Fetch the character at position @var{idx} in the string in @var{src}, and store Fetch the character at position @var{idx} in the string in @var{src},
it in @var{dst}. and store it in @var{dst}. The @var{idx} value should be an unboxed
unsigned 64-bit integer.
@end deftypefn @end deftypefn
@deftypefn Instruction {} cons s8:@var{dst} s8:@var{car} s8:@var{cdr} @deftypefn Instruction {} cons s8:@var{dst} s8:@var{car} s8:@var{cdr}
@ -1340,16 +1370,16 @@ All of these operations place their result in their first operand,
Add @var{a} to @var{b}. Add @var{a} to @var{b}.
@end deftypefn @end deftypefn
@deftypefn Instruction {} add1 s12:@var{dst} s12:@var{src} @deftypefn Instruction {} add/immediate s8:@var{dst} s8:@var{src} c8:@var{imm}
Add 1 to the value in @var{src}. Add the unsigned integer @var{imm} to the value in @var{src}.
@end deftypefn @end deftypefn
@deftypefn Instruction {} sub s8:@var{dst} s8:@var{a} s8:@var{b} @deftypefn Instruction {} sub s8:@var{dst} s8:@var{a} s8:@var{b}
Subtract @var{b} from @var{a}. Subtract @var{b} from @var{a}.
@end deftypefn @end deftypefn
@deftypefn Instruction {} sub1 s12:@var{dst} s12:@var{src} @deftypefn Instruction {} sub/immediate s8:@var{dst} s8:@var{src} s8:@var{imm}
Subtract 1 from @var{src}. Subtract the unsigned integer @var{imm} from the value in @var{src}.
@end deftypefn @end deftypefn
@deftypefn Instruction {} mul s8:@var{dst} s8:@var{a} s8:@var{b} @deftypefn Instruction {} mul s8:@var{dst} s8:@var{a} s8:@var{b}
@ -1388,6 +1418,10 @@ Compute the bitwise inclusive @code{or} of @var{a} with @var{b}.
Compute the bitwise exclusive @code{or} of @var{a} with @var{b}. Compute the bitwise exclusive @code{or} of @var{a} with @var{b}.
@end deftypefn @end deftypefn
@deftypefn Instruction {} logsub s8:@var{dst} s8:@var{a} s8:@var{b}
Place the bitwise @code{and} of @var{a} and the bitwise @code{not} of
@var{b} into @var{dst}.
@end deftypefn
@node Inlined Bytevector Instructions @node Inlined Bytevector Instructions
@subsubsection Inlined Bytevector Instructions @subsubsection Inlined Bytevector Instructions
@ -1398,6 +1432,11 @@ a clear path for eventual native compilation. Without this, Scheme
programs would need other primitives for accessing raw bytes -- but programs would need other primitives for accessing raw bytes -- but
these primitives are as good as any. these primitives are as good as any.
@deftypefn Instruction {} bv-length s12:@var{dst} s12:@var{src}
Store the length of the bytevector in @var{src} in @var{dst}, as an
unboxed unsigned 64-bit integer.
@end deftypefn
@deftypefn Instruction {} bv-u8-ref s8:@var{dst} s8:@var{src} s8:@var{idx} @deftypefn Instruction {} bv-u8-ref s8:@var{dst} s8:@var{src} s8:@var{idx}
@deftypefnx Instruction {} bv-s8-ref s8:@var{dst} s8:@var{src} s8:@var{idx} @deftypefnx Instruction {} bv-s8-ref s8:@var{dst} s8:@var{src} s8:@var{idx}
@deftypefnx Instruction {} bv-u16-ref s8:@var{dst} s8:@var{src} s8:@var{idx} @deftypefnx Instruction {} bv-u16-ref s8:@var{dst} s8:@var{src} s8:@var{idx}
@ -1411,6 +1450,12 @@ these primitives are as good as any.
Fetch the item at byte offset @var{idx} in the bytevector @var{src}, and Fetch the item at byte offset @var{idx} in the bytevector @var{src}, and
store it in @var{dst}. All accesses use native endianness. store it in @var{dst}. All accesses use native endianness.
The @var{idx} value should be an unboxed unsigned 64-bit integer.
The results are all written to the stack as unboxed values, either as
signed 64-bit integers, unsigned 64-bit integers, or IEEE double
floating point numbers.
@end deftypefn @end deftypefn
@deftypefn Instruction {} bv-u8-set! s8:@var{dst} s8:@var{idx} s8:@var{src} @deftypefn Instruction {} bv-u8-set! s8:@var{dst} s8:@var{idx} s8:@var{src}
@ -1426,4 +1471,138 @@ store it in @var{dst}. All accesses use native endianness.
Store @var{src} into the bytevector @var{dst} at byte offset @var{idx}. Store @var{src} into the bytevector @var{dst} at byte offset @var{idx}.
Multibyte values are written using native endianness. Multibyte values are written using native endianness.
The @var{idx} value should be an unboxed unsigned 64-bit integer.
The @var{src} values are all unboxed, either as signed 64-bit integers,
unsigned 64-bit integers, or IEEE double floating point numbers.
@end deftypefn @end deftypefn
@node Unboxed Integer Arithmetic
@subsubsection Unboxed Integer Arithmetic
Guile supports two kinds of unboxed integers: unsigned 64-bit integers,
and signed 64-bit integers. Guile prefers unsigned integers, in the
sense that Guile's compiler supports them better and the virtual machine
has more operations that work on them. Still, signed integers are
supported at least to allow @code{bv-s64-ref} and related instructions
to avoid boxing their values.
@deftypefn Instruction {} scm->u64 s12:@var{dst} s12:@var{src}
Unbox the SCM value at @var{src} to a unsigned 64-bit integer, placing
the result in @var{dst}. If the @var{src} value is not an exact integer
in the unsigned 64-bit range, signal an error.
@end deftypefn
@deftypefn Instruction {} u64->scm s12:@var{dst} s12:@var{src}
Box the unsigned 64-bit integer at @var{src} to a SCM value and place
the result in @var{dst}. The result will be a fixnum or a bignum.
@end deftypefn
@deftypefn Instruction {} load-u64 s24:@var{dst} au32:@var{high-bits} au32:@var{low-bits}
Load a 64-bit value formed by joining @var{high-bits} and
@var{low-bits}, and write it to @var{dst}.
@end deftypefn
@deftypefn Instruction {} scm->s64 s12:@var{dst} s12:@var{src}
@deftypefnx Instruction {} s64->scm s12:@var{dst} s12:@var{src}
@deftypefnx Instruction {} load-s64 s24:@var{dst} as32:@var{high-bits} as32:@var{low-bits}
Like @code{scm->u64}, @code{u64->scm}, and @code{load-u64}, but for
signed 64-bit integers.
@end deftypefn
Sometimes the compiler can know that we will only need a subset of the
bits in an integer. In that case we can sometimes unbox an integer even
if it might be out of range.
@deftypefn Instruction {} scm->u64/truncate s12:@var{dst} s12:@var{src}
Take the SCM value in @var{dst} and @code{logand} it with @code{(1- (ash
1 64))}. Place the unboxed result in @var{dst}.
@end deftypefn
@deftypefn Instruction {} br-if-u64-= s24:@var{a} x8:@var{_} s24:@var{b} b1:@var{invert} x7:@var{_} l24:@var{offset}
@deftypefnx Instruction {} br-if-u64-< s24:@var{a} x8:@var{_} s24:@var{b} b1:@var{invert} x7:@var{_} l24:@var{offset}
@deftypefnx Instruction {} br-if-u64-<= s24:@var{a} x8:@var{_} s24:@var{b} b1:@var{invert} x7:@var{_} l24:@var{offset}
If the unboxed unsigned 64-bit integer value in @var{a} is @code{=},
@code{<}, or @code{<=} to the unboxed unsigned 64-bit integer value in
@var{b}, respectively, add @var{offset} to the current instruction
pointer.
@end deftypefn
@deftypefn Instruction {} br-if-u64-=-scm s24:@var{a} x8:@var{_} s24:@var{b} b1:@var{invert} x7:@var{_} l24:@var{offset}
@deftypefnx Instruction {} br-if-u64-<-scm s24:@var{a} x8:@var{_} s24:@var{b} b1:@var{invert} x7:@var{_} l24:@var{offset}
@deftypefnx Instruction {} br-if-u64-<=-scm s24:@var{a} x8:@var{_} s24:@var{b} b1:@var{invert} x7:@var{_} l24:@var{offset}
If the unboxed unsigned 64-bit integer value in @var{a} is @code{=},
@code{<}, or @code{<=} to the SCM value in @var{b}, respectively, add
@var{offset} to the current instruction pointer.
@end deftypefn
@deftypefn Instruction {} uadd s8:@var{dst} s8:@var{a} s8:@var{b}
@deftypefnx Instruction {} usub s8:@var{dst} s8:@var{a} s8:@var{b}
@deftypefnx Instruction {} umul s8:@var{dst} s8:@var{a} s8:@var{b}
Like @code{add}, @code{sub}, and @code{mul}, except taking
the operands as unboxed unsigned 64-bit integers, and producing the
same. The result will be silently truncated to 64 bits.
@end deftypefn
@deftypefn Instruction {} uadd/immediate s8:@var{dst} s8:@var{a} c8:@var{b}
@deftypefnx Instruction {} usub/immediate s8:@var{dst} s8:@var{a} c8:@var{b}
@deftypefnx Instruction {} umul/immediate s8:@var{dst} s8:@var{a} c8:@var{b}
Like @code{uadd}, @code{usub}, and @code{umul}, except the second
operand is an immediate unsigned 8-bit integer.
@end deftypefn
@deftypefn Instruction {} ulogand s8:@var{dst} s8:@var{a} s8:@var{b}
@deftypefnx Instruction {} ulogior s8:@var{dst} s8:@var{a} s8:@var{b}
@deftypefnx Instruction {} ulogsub s8:@var{dst} s8:@var{a} s8:@var{b}
Like @code{logand}, @code{logior}, and @code{logsub}, but operating on
unboxed unsigned 64-bit integers.
@end deftypefn
@deftypefn Instruction {} ulsh s8:@var{dst} s8:@var{a} s8:@var{b}
Shift the unboxed unsigned 64-bit integer in @var{a} left by @var{b}
bits, also an unboxed unsigned 64-bit integer. Truncate to 64 bits and
write to @var{dst} as an unboxed value. Only the lower 6 bits of
@var{b} are used.
@end deftypefn
@deftypefn Instruction {} ursh s8:@var{dst} s8:@var{a} s8:@var{b}
Like @code{ulsh}, but shifting right.
@end deftypefn
@deftypefn Instruction {} ulsh/immediate s8:@var{dst} s8:@var{a} c8:@var{b}
@deftypefnx Instruction {} ursh/immediate s8:@var{dst} s8:@var{a} c8:@var{b}
Like @code{ulsh} and @code{ursh}, but encoding @code{b} as an immediate
8-bit unsigned integer.
@end deftypefn
@node Unboxed Floating-Point Arithmetic
@subsubsection Unboxed Floating-Point Arithmetic
@deftypefn Instruction {} scm->f64 s12:@var{dst} s12:@var{src}
Unbox the SCM value at @var{src} to an IEEE double, placing the result
in @var{dst}. If the @var{src} value is not a real number, signal an
error.
@end deftypefn
@deftypefn Instruction {} f64->scm s12:@var{dst} s12:@var{src}
Box the IEEE double at @var{src} to a SCM value and place the result in
@var{dst}.
@end deftypefn
@deftypefn Instruction {} load-f64 s24:@var{dst} au32:@var{high-bits} au32:@var{low-bits}
Load a 64-bit value formed by joining @var{high-bits} and
@var{low-bits}, and write it to @var{dst}.
@end deftypefn
@deftypefn Instruction {} fadd s8:@var{dst} s8:@var{a} s8:@var{b}
@deftypefnx Instruction {} fsub s8:@var{dst} s8:@var{a} s8:@var{b}
@deftypefnx Instruction {} fmul s8:@var{dst} s8:@var{a} s8:@var{b}
@deftypefnx Instruction {} fdiv s8:@var{dst} s8:@var{a} s8:@var{b}
Like @code{add}, @code{sub}, @code{div}, and @code{mul}, except taking
the operands as unboxed IEEE double floating-point numbers, and producing
the same.
@end deftypefn