1
0
Fork 0
cl-sites/guile.html_node/VM-Programs.html

226 lines
12 KiB
HTML
Raw Normal View History

2024-12-17 12:49:28 +01:00
<!DOCTYPE html>
<html>
<!-- Created by GNU Texinfo 7.1, https://www.gnu.org/software/texinfo/ -->
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<!-- This manual documents Guile version 3.0.10.
Copyright (C) 1996-1997, 2000-2005, 2009-2023 Free Software Foundation,
Inc.
Copyright (C) 2021 Maxime Devos
Copyright (C) 2024 Tomas Volf
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3 or
any later version published by the Free Software Foundation; with no
Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A
copy of the license is included in the section entitled "GNU Free
Documentation License." -->
<title>VM Programs (Guile Reference Manual)</title>
<meta name="description" content="VM Programs (Guile Reference Manual)">
<meta name="keywords" content="VM Programs (Guile Reference Manual)">
<meta name="resource-type" content="document">
<meta name="distribution" content="global">
<meta name="Generator" content=".texi2any-real">
<meta name="viewport" content="width=device-width,initial-scale=1">
<link href="index.html" rel="start" title="Top">
<link href="Concept-Index.html" rel="index" title="Concept Index">
<link href="index.html#SEC_Contents" rel="contents" title="Table of Contents">
<link href="A-Virtual-Machine-for-Guile.html" rel="up" title="A Virtual Machine for Guile">
<link href="Object-File-Format.html" rel="next" title="Object File Format">
<link href="Variables-and-the-VM.html" rel="prev" title="Variables and the VM">
<style type="text/css">
<!--
a.copiable-link {visibility: hidden; text-decoration: none; line-height: 0em}
div.example {margin-left: 3.2em}
span:hover a.copiable-link {visibility: visible}
-->
</style>
<link rel="stylesheet" type="text/css" href="https://www.gnu.org/software/gnulib/manual.css">
</head>
<body lang="en">
<div class="subsection-level-extent" id="VM-Programs">
<div class="nav-panel">
<p>
Next: <a href="Object-File-Format.html" accesskey="n" rel="next">Object File Format</a>, Previous: <a href="Variables-and-the-VM.html" accesskey="p" rel="prev">Variables and the VM</a>, Up: <a href="A-Virtual-Machine-for-Guile.html" accesskey="u" rel="up">A Virtual Machine for Guile</a> &nbsp; [<a href="index.html#SEC_Contents" title="Table of contents" rel="contents">Contents</a>][<a href="Concept-Index.html" title="Index" rel="index">Index</a>]</p>
</div>
<hr>
<h4 class="subsection" id="Compiled-Procedures-are-VM-Programs"><span>9.3.5 Compiled Procedures are VM Programs<a class="copiable-link" href="#Compiled-Procedures-are-VM-Programs"> &para;</a></span></h4>
<p>By default, when you enter in expressions at Guile&rsquo;s REPL, they are
first compiled to bytecode. Then that bytecode is executed to produce a
value. If the expression evaluates to a procedure, the result of this
process is a compiled procedure.
</p>
<p>A compiled procedure is a compound object consisting of its bytecode and
a reference to any captured lexical variables. In addition, when a
procedure is compiled, it has associated metadata written to side
tables, for instance a line number mapping, or its docstring. You can
pick apart these pieces with the accessors in <code class="code">(system vm
program)</code>. See <a class="xref" href="Compiled-Procedures.html">Compiled Procedures</a>, for a full API reference.
</p>
<p>A procedure may reference data that was statically allocated when the
procedure was compiled. For example, a pair of immediate objects
(see <a class="pxref" href="Immediate-Objects.html">Immediate Objects</a>) can be allocated directly in the memory
segment that contains the compiled bytecode, and accessed directly by
the bytecode.
</p>
<p>Another use for statically allocated data is to serve as a cache for a
bytecode. Top-level variable lookups are handled in this way; the first
time a top-level binding is referenced, the resolved variable will be
stored in a cache. Thereafter all access to the variable goes through
the cache cell. The variable&rsquo;s value may change in the future, but the
variable itself will not.
</p>
<p>We can see how these concepts tie together by disassembling the
<code class="code">foo</code> function we defined earlier to see what is going on:
</p>
<div class="example smallexample">
<pre class="example-preformatted">scheme@(guile-user)&gt; (define (foo a) (lambda (b) (vector foo a b)))
scheme@(guile-user)&gt; ,x foo
Disassembly of #&lt;procedure foo (a)&gt; at #xf1da30:
0 (instrument-entry 164) at (unknown file):5:0
2 (assert-nargs-ee/locals 2 1) ;; 3 slots (1 arg)
3 (allocate-words/immediate 2 3) at (unknown file):5:16
4 (load-u64 0 0 65605)
7 (word-set!/immediate 2 0 0)
8 (load-label 0 7) ;; anonymous procedure at #xf1da6c
10 (word-set!/immediate 2 1 0)
11 (scm-set!/immediate 2 2 1)
12 (reset-frame 1) ;; 1 slot
13 (handle-interrupts)
14 (return-values)
----------------------------------------
Disassembly of anonymous procedure at #xf1da6c:
0 (instrument-entry 183) at (unknown file):5:16
2 (assert-nargs-ee/locals 2 3) ;; 5 slots (1 arg)
3 (static-ref 2 152) ;; #&lt;variable 112e530 value: #&lt;procedure foo (a)&gt;&gt;
5 (immediate-tag=? 2 7 0) ;; heap-object?
7 (je 19) ;; -&gt; L2
8 (static-ref 2 119) ;; #&lt;directory (guile-user) ca9750&gt;
10 (static-ref 1 127) ;; foo
12 (call-scm&lt;-scm-scm 2 2 1 40)
14 (immediate-tag=? 2 7 0) ;; heap-object?
16 (jne 8) ;; -&gt; L1
17 (scm-ref/immediate 0 2 1)
18 (immediate-tag=? 0 4095 2308) ;; undefined?
20 (je 4) ;; -&gt; L1
21 (static-set! 2 134) ;; #&lt;variable 112e530 value: #&lt;procedure foo (a)&gt;&gt;
23 (j 3) ;; -&gt; L2
L1:
24 (throw/value 1 151) ;; #(unbound-variable #f &quot;Unbound variable: ~S&quot;)
L2:
26 (scm-ref/immediate 2 2 1)
27 (allocate-words/immediate 1 4) at (unknown file):5:28
28 (load-u64 0 0 781)
31 (word-set!/immediate 1 0 0)
32 (scm-set!/immediate 1 1 2)
33 (scm-ref/immediate 4 4 2)
34 (scm-set!/immediate 1 2 4)
35 (scm-set!/immediate 1 3 3)
36 (mov 4 1)
37 (reset-frame 1) ;; 1 slot
38 (handle-interrupts)
39 (return-values)
</pre></div>
<p>The first thing to notice is that the bytecode is at a fairly low level.
When a program is compiled from Scheme to bytecode, it is expressed in
terms of more primitive operations. As such, there can be more
instructions than you might expect.
</p>
<p>The first chunk of instructions is the outer <code class="code">foo</code> procedure. It
is followed by the code for the contained closure. The code can look
daunting at first glance, but with practice it quickly becomes
comprehensible, and indeed being able to read bytecode is an important
step to understanding the low-level performance of Guile programs.
</p>
<p>The <code class="code">foo</code> function begins with a prelude. The
<code class="code">instrument-entry</code> bytecode increments a counter associated with
the function. If the counter reaches a certain threshold, Guile will
emit machine code (&ldquo;JIT-compile&rdquo;) for <code class="code">foo</code>. Emitting machine
code is fairly cheap but it does take time, so it&rsquo;s not something you
want to do for every function. Using a per-function counter and a
global threshold allows Guile to spend time JIT-compiling only the
&ldquo;hot&rdquo; functions.
</p>
<p>Next in the prelude is an argument-checking instruction, which checks
that it was called with only 1 argument (plus the callee function itself
makes 2) and then reserves stack space for an additional 1 local.
</p>
<p>Then from <code class="code">ip</code> 3 to 11, we allocate a new closure by allocating a
three-word object, initializing its first word to store a type tag,
setting its second word to its code pointer, and finally at <code class="code">ip</code>
11, storing local value 1 (the <code class="code">a</code> argument) into the third word
(the first free variable).
</p>
<p>Before returning, <code class="code">foo</code> &ldquo;resets the frame&rdquo; to hold only one local
(the return value), runs any pending interrupts (see <a class="pxref" href="Asyncs.html">Asynchronous Interrupts</a>) and
then returns.
</p>
<p>Note that local variables in Guile&rsquo;s virtual machine are usually
addressed relative to the stack pointer, which leads to a pleasantly
efficient <code class="code">sp[<var class="var">n</var>]</code> access. However it can make the
disassembly hard to read, because the <code class="code">sp</code> can change during the
function, and because incoming arguments are relative to the <code class="code">fp</code>,
not the <code class="code">sp</code>.
</p>
<p>To know what <code class="code">fp</code>-relative slot corresponds to an
<code class="code">sp</code>-relative reference, scan up in the disassembly until you get
to a &ldquo;<var class="var">n</var> slots&rdquo; annotation; in our case, 3, indicating that the
frame has space for 3 slots. Thus a zero-indexed <code class="code">sp</code>-relative
slot of 2 corresponds to the <code class="code">fp</code>-relative slot of 0, which
initially held the value of the closure being called. This means that
Guile doesn&rsquo;t need the value of the closure to compute its result, and
so slot 0 was free for re-use, in this case for the result of making a
new closure.
</p>
<p>A closure is code with data. As you can see, making the closure
involved making an object (<code class="code">ip</code> 3), putting a code pointer in it
(<code class="code">ip</code> 8 and 10), and putting in the closure&rsquo;s free variable
(<code class="code">ip</code> 11).
</p>
<p>The second stanza disassembles the code for the closure. After the
prelude, all of the code between <code class="code">ip</code> 5 and 24 is related to
loading the toplevel variable <code class="code">foo</code> into slot 1. This lookup
happens only once, and is associated with a cache; after the first run,
the value in the cache will be a bound variable, and the code will jump
from <code class="code">ip</code> 7 to 26. On the first run, Guile gets the module
associated with the function, calls out to a run-time routine to look up
the variable, and checks that the variable is bound before initializing
the cache. Either way, <code class="code">ip</code> 26 dereferences the variable into
local 2.
</p>
<p>What follows is the allocation and initialization of the vector return
value. <code class="code">Ip</code> 27 does the allocation, and the following two
instructions initialize the type-and-length tag for the object&rsquo;s first
word. <code class="code">Ip</code> 32 sets word 1 of the object (the first vector slot) to
the value of <code class="code">foo</code>; <code class="code">ip</code> 33 fetches the closure variable for
<code class="code">a</code>, then in <code class="code">ip</code> 34 stores it in the second vector slot; and
finally, in <code class="code">ip</code> 35, local <code class="code">b</code> is stored to the third vector
slot. This is followed by the return sequence.
</p>
</div>
<hr>
<div class="nav-panel">
<p>
Next: <a href="Object-File-Format.html">Object File Format</a>, Previous: <a href="Variables-and-the-VM.html">Variables and the VM</a>, Up: <a href="A-Virtual-Machine-for-Guile.html">A Virtual Machine for Guile</a> &nbsp; [<a href="index.html#SEC_Contents" title="Table of contents" rel="contents">Contents</a>][<a href="Concept-Index.html" title="Index" rel="index">Index</a>]</p>
</div>
</body>
</html>