The page catches up: the backend serves the editor, and emit --x86 is annotated
This commit is contained in:
parent
45c5b43021
commit
a4dce138c0
@ -328,17 +328,30 @@ $ ./_build/default/bin/main.exe run calc-me.flan "1 + 2 * (3 - 0.5) / 2"
|
||||
|
||||
<pre><code class="sh">$ flan
|
||||
usage: flan (read|parse|check|emit|shim) <file.flan>...
|
||||
flan emit <file.flan> [--x86] [--dev] [--debug] [--no-bounds-checks]
|
||||
flan import-c <header.h> [package.flan...] [clang flags...]
|
||||
flan generate-c <package-dir>
|
||||
flan build <file.flan> [-o out] [--no-bounds-checks] [--dev] [--debug] [--sanitize] [--x86] [--target=wasm32-wasi|web]
|
||||
flan run <file.flan> [args...]
|
||||
flan reload <program.flan> <forms.flan> [-o out.so]
|
||||
flan dev <program.flan> [-s socket]</code></pre>
|
||||
flan reload <program.flan> <forms.flan> [-o out.so] [--x86]
|
||||
flan dev <program.flan> [-s socket] [--x86]</code></pre>
|
||||
|
||||
<p><code>read</code>, <code>parse</code>, <code>check</code>, <code>emit</code> and
|
||||
<code>shim</code> each stop the pipeline one stage further along and print what it
|
||||
produced. <code>run</code> builds to a temporary file and execs it.</p>
|
||||
|
||||
<p><code>flan emit --x86</code> prints the hand-written backend's assembly where plain
|
||||
<code>emit</code> prints LLVM IR, and it is <em>annotated</em>: each run of bytes is
|
||||
headed by the Flan form that produced it and its source position, and each function by a
|
||||
map of its frame saying which displacement is which parameter, which is a named local,
|
||||
and below which offset everything is a reusable temporary. Reading a listing of this
|
||||
backend without that map is reading <code>-0x20(%rbp)</code> with no key. The comments
|
||||
cost nothing in the object — the assembler discards them — but they are
|
||||
emitted only by <code>emit</code>, never by a build, so the assembly a build hands to
|
||||
<code>clang</code> is the same text it always was. <code>spike/x86/dump.sh</code> puts all
|
||||
four lowerings of one function side by side, which is what <kbd>C-c C-l</kbd> shows in
|
||||
the editor.</p>
|
||||
|
||||
<p>The smallest program:</p>
|
||||
|
||||
<pre><code>(defn main [] ()
|
||||
@ -1734,6 +1747,7 @@ something surprising.</p>
|
||||
<tr><td><kbd>C-c C-i</kbd></td><td>inspect a value, navigating into its fields</td></tr>
|
||||
<tr><td><kbd>C-c C-m</kbd></td><td>what the macro call at point expands to, one step; <kbd>C-u</kbd> first for all the way</td></tr>
|
||||
<tr><td><kbd>C-c C-a</kbd></td><td>disassemble a function; <kbd>C-u</kbd> first for its LLVM IR</td></tr>
|
||||
<tr><td><kbd>C-c C-l</kbd></td><td>every lowering of a function at once: IR, <code>-O0</code>, <code>-O2</code>, and the hand-written backend</td></tr>
|
||||
<tr><td><kbd>C-c C-g</kbd></td><td>debug under lldb, through dape — bound only once <code>flan-dape.el</code> is loaded, so <code>flan-mode</code> works without dape installed</td></tr>
|
||||
<tr><td><kbd>C-c C-d</kbd></td><td>what the running program currently defines</td></tr>
|
||||
<tr><td><kbd>C-c C-v</kbd></td><td>help on the name at point</td></tr>
|
||||
@ -1902,6 +1916,35 @@ name</strong>, with 38 programs skipped because they do not compile on either si
|
||||
no <code>main</code>, or run forever. <code>dune build @x86</code> runs it as part of the
|
||||
build, so a refusal cannot sit unnoticed.</p>
|
||||
|
||||
<p><strong>It serves the editor now, which is what it was written for.</strong>
|
||||
<code>flan dev --x86</code> and <code>flan reload --x86</code> compile a redefinition
|
||||
through the hand-written backend, and the round trip is about half what LLVM's is —
|
||||
<code>llc</code> was nearly all of it, and an assembler is not:</p>
|
||||
|
||||
<table>
|
||||
<tr><th></th><th>LLVM</th><th><code>--x86</code></th></tr>
|
||||
<tr><td>code generation</td><td><code>llc</code> 42–48ms</td><td><code>as</code> 8.0–8.4ms</td></tr>
|
||||
<tr><td>link</td><td><code>ld</code> 9.7–11.4ms</td><td><code>ld</code> 8.9–9.3ms</td></tr>
|
||||
<tr><td><code>C-c C-c</code>, at the socket</td><td>62–66ms</td><td><strong>27–30ms</strong></td></tr>
|
||||
<tr><td><code>C-x C-e</code></td><td>60–61ms</td><td><strong>24–25ms</strong></td></tr>
|
||||
</table>
|
||||
|
||||
<p>Both columns were measured on one machine, whose <code>llc</code> is slower than the
|
||||
one the 15–17ms above came from; the comparison is the point and not the absolute
|
||||
numbers. What the headline hides is where the time went: <code>ld</code> did not move and
|
||||
is now half the x86 build, and about 9ms of the round trip is the frontend checking the
|
||||
program again — a seventh of the LLVM loop but a third of this one. The next millisecond
|
||||
is in the linker rather than the code generator.</p>
|
||||
|
||||
<p><strong>It requires <code>--two-process</code>, and refuses the merged daemon by
|
||||
name.</strong> A merged host is linked <code>-rdynamic</code>, so it exports every Flan
|
||||
body it contains, and those interpose the prelude of the LLVM-built macro module the
|
||||
compiler loads into <em>itself</em> to expand macros. An LLVM caller lands in an x86 body
|
||||
and the process dies during the first expansion, before the program has started. The
|
||||
marker symbol that refuses a crossed redefinition module does not catch this, and was
|
||||
never meant to: a macro module deliberately carries none. Hidden visibility on a macro
|
||||
module's Flan bodies is the fix and is not built.</p>
|
||||
|
||||
<p><code>--debug</code> is a third flag beside <code>--dev</code> and the optimisation
|
||||
level. <code>--dev</code> asks whether you can redefine the program while it runs;
|
||||
<code>--debug</code> asks whether you can stop it and read it. It emits DWARF, sets
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user