Skip to content

v0.60.0

Choose a tag to compare

@Arzaroth Arzaroth released this 29 Sep 02:07
· 14 commits to master since this release

Tier 2 reads its own writing

Every tier in this file has a second opinion inside the file. The three
machines are read back instruction by instruction. The module is read back and
executed by a decoder that shares nothing with the encoder. The interpreter
reads everything. Tier 2 had none: the IR was handed to llvmlite, which
verified it and ran it, and agreeing with the tool that consumes you is not a
check. On a host with nothing installed it was not checked at all, which is
four of the six interpreters CI runs.

Layer 16b reads back exactly what layer 16 writes. The list of shapes was
counted rather than guessed at - the catalogue at three orders, thirty generated
programs and the one that divides - and it came to about twenty forms, with one
absence that decides the whole design: there is no phi. Layer 7 already turned
the operand stack into memory, so every value that outlives a block is in an
alloca, no block takes arguments, and a register is written once and read from a
dictionary. A reader for SSA without phi is an afternoon. With it, it is a
dominator tree.

Two details in it are load-bearing and do not look it. sdiv truncates towards
zero and srem takes the sign of its left operand; layer 16 emits the pair and
then corrects the quotient back to the flooring the interpreter does, so a
reader that floored would undo the correction and still agree on every positive
operand. And getelementptr multiplies by the element width, which matters for
every array that is not i8.

Which is how the program that divides came to divide a negative. It existed to
reach the division emitters and reached them with two positive operands - the
one case where truncating and flooring give the same answer. Every tier
corrects for that divergence and nothing had ever asked one of them to. All
nine got it right, which is the answer you want and not the one you can assume.

The check was validated by breaking the reader five ways. Four are caught on
two programs. The fifth is not, and cannot be: masking a zext as though it
were a sext is unobservable when the only zext the backend writes widens an i1
that is already zero or one. That is an equivalent mutant rather than a missing
check, and the difference was settled by listing every conversion layer 16
emits instead of arguing about it.

In the fuzzer it is read-ir, needing no toolchain, so it runs precisely where
the jit tier is skipped: four seconds over two hundred programs on the oldest
interpreter this supports. On the command line it is --run-llvm, taking a path
or - for a pipe, beside --run-wasm.

python3 ouroboros.py --emit-llvm | python3 ouroboros.py --run-llvm -

Full Changelog: v0.59.0...v0.60.0

LOC: 26518