~james/remy

3ac5bae085495f49c3f24dbfb40242ee81069c4f — james verified 2 months ago
feat(compiler): add verified pixel compiler and templeos runner
A  => .gitignore +2 -0
@@ 1,2 @@
/.build/
__pycache__/

A  => Makefile +23 -0
@@ 1,23 @@
ISABELLE ?= isabelle

.PHONY: all check payload clean

all: .build/ready

.build/ready: ROOT $(wildcard src/*.thy) cli/driver.ML Makefile
	@test "$$('$(ISABELLE)' version)" = Isabelle2026
	"$(ISABELLE)" build -D . -j 1 -o threads=1 -o parallel_proofs=0
	"$(ISABELLE)" export -n -d . -O .build -x 'Remy.Export:code/remy.ML' Remy
	REMY_ROOT="$(CURDIR)" "$$('$(ISABELLE)' getenv -b POLYML_HOME)/$$('$(ISABELLE)' getenv -b ISABELLE_PLATFORM64)/polyc" -o .build/remy cli/driver.ML
	@touch $@

check: all
	python3 tests/native.py

payload: all
	@mkdir -p .build/payload
	@set -e; for name in Rings Weave Cells; do .build/remy examples/$$name.remy .build/payload/$$name.BIN; .build/remy vectors examples/$$name.remy .build/payload/$$name.DAT; cp examples/$$name.remy .build/payload/$$name.TXT; done
	cp guest/Remy.HC .build/payload/Remy.HC

clean:
	rm -rf .build

A  => README.md +36 -0
@@ 1,36 @@
Pixel expressions compile to native x86+64 for TempleOS through a compiler implemented in Isabelle/HOL with proofs of constant folding, lexical slot allocation, bounded stack execution and correspondence with a register and stack model

[![i like men](media/demo.gif)](media/demo.mp4)

## Build

```sh
make check
make payload
```

It uses:
- Isabelle2026 and its bundled Poly/ML toolchain 
- Python

Copy these files onto `C:/` in TempleOS and run:

```c
#include "C:/Remy.HC";
Verify;
Show(0);
```

## Correctness

Each `let` pushes a binding onto the evaluation stack. [Compilation](src/Compiler.thy) adjusts variable offsets as intermediate values accumulate above those bindings and proves that execution leaves the result followed by the original stack whereas accepted programs stay within 512 evaluation words at every instruction boundary

[Native lowering](src/Native.thy) puts the result in `rax` and preserves `r8`, `r9` and `rdx` which carry `x`, `y` and `t`. The temporary registers are `rax`, `rcx` and `rbx` while the [Encoding](src/Encoding.thy) proves decoder inversion and byte bounds for the emitted blocks including their immediate operands and stack displacements

![Source eval](media/correspondence.svg)

Arithmetic wraps modulo `2^64` and shifts mask their count to six bits

Both branches of a nonconstant conditional run before selection which preserves the result because expressions have no side effects. The HolyC runner masks each pixel result to four bits for the TempleOS palette

The execution theorem is relative to the instruction block model. Actual x86 execution + the HolyC calling bridge and the extracted runtime and the OS are outside its scope. Tests compare emitted code against an independent evaluator on Linux and against HOL results inside the recorded TempleOS guest

A  => ROOT +18 -0
@@ 1,18 @@
session Remybase = HOL +
  options [document = false]
  sessions "HOL-Library"
  theories
    "HOL-Library.Word"
    "HOL-Library.Code_Target_Numeral"

session Remy in src = Remybase +
  options [document = false]
  sessions "HOL-Library"
  theories
    Model
    Compiler
    Native
    Encoding
    Frontend
    Examples
    Export

A  => cli/driver.ML +40 -0
@@ 1,40 @@
val root = valOf (OS.Process.getEnv "REMY_ROOT");
val _ = use (root ^ "/.build/Remy.Export/code/remy.ML");

fun chars s = map (Remy.ascii o IntInf.fromInt o Char.ord) (String.explode s);
fun text cs = String.implode (map (Char.chr o IntInf.toInt o Remy.ordinal) cs);

fun read path =
  let
    val stream = TextIO.openIn path
    val input = TextIO.inputN (stream, 65537)
      handle exn => (TextIO.closeIn stream; raise exn)
    val _ = TextIO.closeIn stream
  in
    if size input > 65536 then raise Fail "source exceeds 64 KiB" else chars input
  end;

fun write path bytes =
  let
    val stream = BinIO.openOut path
  in
    (BinIO.output (stream, Byte.stringToBytes (text bytes)); BinIO.closeOut stream)
      handle exn => ((BinIO.closeOut stream handle _ => ()); raise exn)
  end;

fun output (ok, message) =
  (TextIO.output (if ok then TextIO.stdOut else TextIO.stdErr, text message);
   if ok then () else OS.Process.exit OS.Process.failure);

fun main () =
  (case CommandLine.arguments () of
    ["vectors", source, destination] =>
      (case Remy.vectors (read source) of (true, bytes) => write destination bytes | result => output result)
  | ["eval", source, x, y, t] => output (Remy.reference (read source) (chars x) (chars y) (chars t))
  | [source, destination] =>
      let val ((ok, bytes), report) = Remy.artifact (read source)
      in if ok then (write destination bytes; output (true,report)) else output (false,report) end
  | _ => raise Fail "usage: remy source binary | remy vectors source data | remy eval source x y t")
  handle exn =>
    (TextIO.output (TextIO.stdErr, "remy: " ^ General.exnMessage exn ^ "\n");
     OS.Process.exit OS.Process.failure);

A  => examples/Cells.remy +5 -0
@@ 1,5 @@
(let u (+ x (shr t 2))
  (let cell (xor (shr u 4) (shr y 4))
    (if (< (and cell 3) 1)
      (and (+ (xor u y) t) 15)
      (and (shr (+ (* u u) (* y y)) 7) 15))))

A  => examples/Rings.remy +4 -0
@@ 1,4 @@
(let dx (- x 80)
  (let dy (- y 56)
    (let radius (+ (* dx dx) (* dy dy))
      (and (shr (+ radius (* t 32)) 6) 15))))

A  => examples/Weave.remy +3 -0
@@ 1,3 @@
(let drift (+ y (shr t 1))
  (let warp (xor (* x drift) (* (+ x t) y))
    (and (xor (shr warp 5) (shr (+ x drift) 3)) 15)))

A  => guest/Remy.HC +93 -0
@@ 1,93 @@
asm {
_REMY::
    PUSH RBP
    MOV RBP,RSP
    MOV RAX,U64 SF_ARG1[RBP]
    MOV R8,U64 SF_ARG2[RBP]
    MOV R9,U64 SF_ARG3[RBP]
    MOV RDX,U64 SF_ARG4[RBP]
    CALL RAX
    POP RBP
    RET1 32
}

_extern _REMY U64 Pixel(U8 *code,U64 x,U64 y,U64 t);

U8 *names[3]={"Rings","Weave","Cells"};
CDC *image=NULL;
U8 *shader=NULL;
U8 *program=NULL;
I64 selected;

U0 Verify()
{
    I64 i,j,size,total=0;
    U8 *code,path[64];
    U64 *rows;
    for (i=0;i<3;i++) {
        StrPrint(path,"C:/%s.BIN",names[i]);
        code=FileRead(path,&size);
        if (!code) throw('Code');
        "%-6s  %d bytes  ",names[i],size;
        StrPrint(path,"C:/%s.DAT",names[i]);
        rows=FileRead(path,&size);
        if (!rows || size!=16384) throw('Vectors');
        for (j=0;j<size/32;j++) {
            if (Pixel(code,rows[4*j],rows[4*j+1],rows[4*j+2])!=rows[4*j+3]) {
                throw('Mismatch');
            }
            total++;
        }
        "%d comparisons passed\n",size/32;
        Free(rows);
        Free(code);
    }
    "native results match HOL evaluation (%d cases)\n",total;
}

U0 Draw(CTask *,CDC *dc)
{
    dc->color=BLUE;
    GrPrint(dc,80,24,"remy  %s",names[selected]);
    dc->color=BLACK;
    GrBlot(dc,80,64,image);
    GrPrint(dc,80,416,"%s",program);
}

U0 Show(I64 which=0)
{
    I64 x,y,dy,t=0;
    U8 color,path[64];
    if (!(0<=which<3)) return;
    selected=which;
    StrPrint(path,"C:/%s.BIN",names[which]);
    shader=FileRead(path);
    if (!shader) throw('Code');
    StrPrint(path,"C:/%s.TXT",names[which]);
    program=FileRead(path);
    if (!program) throw('Source');
    image=DCNew(480,336);
    SettingsPush;
    AutoComplete(OFF);
    WinMax;
    DocClear;
    Fs->draw_it=&Draw;
    while (!ScanChar) {
        for (y=0;y<112;y++) {
            for (x=0;x<160;x++) {
                color=Pixel(shader,x,y,t)&15;
                for (dy=0;dy<3;dy++)
                    MemSet(image->body+(3*y+dy)*480+3*x,color,3);
            }
        }
        t+=2;
        Refresh;
        Sleep(20);
    }
    Fs->draw_it=NULL;
    SettingsPop;
    DCDel(image);
    Free(shader);
    Free(program);
    DocClear;
}

A  => media/correspondence.svg +17 -0
@@ 1,17 @@
<svg xmlns="http://www.w3.org/2000/svg" width="720" height="208" viewBox="0 0 720 208">
  <defs><marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse"><path d="M 0 0 L 10 5 L 0 10 z" fill="#555"/></marker></defs>
  <rect width="720" height="208" fill="white"/>
  <g fill="none" stroke="#555" stroke-width="1.5" marker-end="url(#arrow)">
    <path d="M 176 45 H 547"/><path d="M 140 64 V 143"/><path d="M 582 64 V 143"/><path d="M 176 164 H 547"/>
  </g>
  <g fill="white" stroke="#555">
    <rect x="62" y="25" width="114" height="40" rx="3"/><rect x="547" y="25" width="113" height="40" rx="3"/>
    <rect x="38" y="144" width="138" height="40" rx="3"/><rect x="547" y="144" width="138" height="40" rx="3"/>
  </g>
  <g font-family="monospace" font-size="14" fill="#222" text-anchor="middle">
    <text x="119" y="50">expression</text><text x="603" y="50">64-bit value</text>
    <text x="107" y="169">x86 blocks</text><text x="616" y="169">RAX + stack</text>
    <text x="360" y="35">source evaluation</text><text x="360" y="153">native block execution</text>
    <text x="360" y="106">optimisation + lexical slots + encoding</text>
  </g>
</svg>

A  => media/demo.gif +0 -0
A  => media/demo.mp4 +0 -0
A  => media/poster.png +0 -0
A  => src/Compiler.thy +237 -0
@@ 1,237 @@
theory Compiler
  imports Model
begin

datatype instruction = Constant pixel | Fetch input | Pick nat | Calculate operator | Choose | Slide

fun step :: "(input \<Rightarrow> pixel) \<Rightarrow> instruction \<Rightarrow> pixel list \<Rightarrow> pixel list option" where
  "step inputs (Constant v) s = Some (v # s)"
| "step inputs (Fetch i) s = Some (inputs i # s)"
| "step inputs (Pick i) s = (if i < length s then Some (s ! i # s) else None)"
| "step inputs (Calculate op) (b # a # s) = Some (operate op a b # s)"
| "step inputs Choose (b # a # c # s) = Some ((if c = 0 then b else a) # s)"
| "step inputs Slide (b # a # s) = Some (b # s)"
| "step inputs _ _ = None"

fun execute :: "(input \<Rightarrow> pixel) \<Rightarrow> instruction list \<Rightarrow> pixel list \<Rightarrow> pixel list option" where
  "execute inputs [] s = Some s"
| "execute inputs (i # code) s = (case step inputs i s of None \<Rightarrow> None | Some t \<Rightarrow> execute inputs code t)"

lemma execute_append:
  "execute inputs (a @ b) s = Option.bind (execute inputs a s) (execute inputs b)"
  by (induction a arbitrary: s) (auto split: option.splits bind_splits)

fun compile :: "expression \<Rightarrow> nat list \<Rightarrow> instruction list" where
  "compile (Literal v) slots = [Constant v]"
| "compile (Input i) slots = [Fetch i]"
| "compile (Variable i) slots = [Pick (slots ! i)]"
| "compile (Binary op a b) slots = compile a slots @ compile b (map Suc slots) @ [Calculate op]"
| "compile (Cond c a b) slots = compile c slots @ compile a (map Suc slots) @
    compile b (map (\<lambda>i. Suc (Suc i)) slots) @ [Choose]"
| "compile (Let a b) slots = compile a slots @ compile b (0 # map Suc slots) @ [Slide]"

definition represents :: "nat list \<Rightarrow> pixel list \<Rightarrow> pixel list \<Rightarrow> bool" where
  "represents slots s env \<longleftrightarrow> length slots = length env \<and>
    (\<forall>i < length env. slots ! i < length s \<and> s ! (slots ! i) = env ! i)"

lemma represents_push:
  "represents slots s env \<Longrightarrow> represents (map Suc slots) (v # s) env"
  by (auto simp: represents_def)

lemma represents_push_two:
  "represents slots s env \<Longrightarrow> represents (map (\<lambda>i. Suc (Suc i)) slots) (a # b # s) env"
  by (auto simp: represents_def)

lemma represents_bind:
  "represents slots s env \<Longrightarrow> represents (0 # map Suc slots) (v # s) (v # env)"
  by (auto simp: represents_def nth_Cons split: nat.splits)

theorem compile_correct:
  assumes "scoped (length env) e" "represents slots s env"
  shows "execute inputs (compile e slots) s = Some (evaluate e env inputs # s)"
  using assms
proof (induction e arbitrary: slots s env)
  case (Literal v)
  then show ?case by simp
next
  case (Input i)
  then show ?case by simp
next
  case (Variable i)
  then show ?case by (simp add: represents_def)
next
  case (Binary op a b)
  have a: "execute inputs (compile a slots) s = Some (evaluate a env inputs # s)"
    using Binary.IH(1) Binary.prems by simp
  have b: "execute inputs (compile b (map Suc slots)) (evaluate a env inputs # s) =
      Some (evaluate b env inputs # evaluate a env inputs # s)"
    using Binary.IH(2) Binary.prems represents_push by simp
  show ?case by (simp add: execute_append a b)
next
  case (Cond c a b)
  have c: "execute inputs (compile c slots) s = Some (evaluate c env inputs # s)"
    using Cond.IH(1) Cond.prems by simp
  have a: "execute inputs (compile a (map Suc slots)) (evaluate c env inputs # s) =
      Some (evaluate a env inputs # evaluate c env inputs # s)"
    using Cond.IH(2) Cond.prems represents_push by simp
  have b: "execute inputs (compile b (map (\<lambda>i. Suc (Suc i)) slots))
      (evaluate a env inputs # evaluate c env inputs # s) =
      Some (evaluate b env inputs # evaluate a env inputs # evaluate c env inputs # s)"
    using Cond.IH(3) Cond.prems represents_push_two by simp
  show ?case by (simp add: execute_append c a b)
next
  case (Let a b)
  have a: "execute inputs (compile a slots) s = Some (evaluate a env inputs # s)"
    using Let.IH(1) Let.prems by simp
  have b: "execute inputs (compile b (0 # map Suc slots)) (evaluate a env inputs # s) =
      Some (evaluate b (evaluate a env inputs # env) inputs # evaluate a env inputs # s)"
    using Let.IH(2) Let.prems represents_bind by simp
  show ?case by (simp add: execute_append a b)
qed

corollary compile_closed:
  "scoped 0 e \<Longrightarrow> execute inputs (compile (optimise e) []) [] = Some [evaluate e [] inputs]"
  using compile_correct[where e="optimise e" and env="[]" and slots="[]" and s="[]" and inputs=inputs]
  by (simp add: represents_def optimise_correct optimise_scoped)

fun height :: "instruction \<Rightarrow> nat \<Rightarrow> nat option" where
  "height (Constant v) n = Some (Suc n)"
| "height (Fetch i) n = Some (Suc n)"
| "height (Pick i) n = (if i < n then Some (Suc n) else None)"
| "height (Calculate op) n = (if 2 \<le> n then Some (n - 1) else None)"
| "height Choose n = (if 3 \<le> n then Some (n - 2) else None)"
| "height Slide n = (if 2 \<le> n then Some (n - 1) else None)"

fun check :: "instruction list \<Rightarrow> nat \<Rightarrow> nat option" where
  "check [] n = Some n"
| "check (i # code) n = Option.bind (height i n) (check code)"

lemma check_append:
  "check (a @ b) n = Option.bind (check a n) (check b)"
  by (induction a arbitrary: n) (auto split: option.splits bind_splits)

lemma step_height:
  "height i (length s) = Some n \<Longrightarrow> \<exists>t. step inputs i s = Some t \<and> length t = n"
  by (cases i; cases s; simp; rename_tac a xs; case_tac xs; simp;
      rename_tac b ys; case_tac ys; auto split: if_splits)

theorem check_sound:
  "check code (length s) = Some n \<Longrightarrow> \<exists>t. execute inputs code s = Some t \<and> length t = n"
proof (induction code arbitrary: s)
  case Nil
  then show ?case by simp
next
  case (Cons i code)
  then obtain m where h: "height i (length s) = Some m" and c: "check code m = Some n"
    by (auto simp: bind_eq_Some_conv)
  obtain t where step: "step inputs i s = Some t" and len: "length t = m"
    using step_height[OF h] by blast
  obtain u where run: "execute inputs code t = Some u" and "length u = n"
    using Cons.IH c len by blast
  then show ?case using step by simp
qed

fun demand :: "expression \<Rightarrow> nat" where
  "demand (Binary op a b) = max (demand a) (Suc (demand b))"
| "demand (Cond c a b) = max (demand c) (max (Suc (demand a)) (2 + demand b))"
| "demand (Let a b) = max (demand a) (Suc (demand b))"
| "demand _ = 1"

lemma demand_positive: "0 < demand e"
  by (induction e) auto

fun bounded :: "nat \<Rightarrow> instruction list \<Rightarrow> nat \<Rightarrow> bool" where
  "bounded limit [] n = (n \<le> limit)"
| "bounded limit (i # code) n = (n \<le> limit \<and> (case height i n of None \<Rightarrow> False | Some m \<Rightarrow> bounded limit code m))"

lemma bounded_start: "bounded limit code n \<Longrightarrow> n \<le> limit"
  by (cases code) auto

lemma bounded_append:
  "bounded limit (a @ b) n \<longleftrightarrow> bounded limit a n \<and> (\<exists>m. check a n = Some m \<and> bounded limit b m)"
  by (induction a arbitrary: n) (auto dest: bounded_start split: option.splits bind_splits)

lemma bounded_check:
  "bounded limit code n \<Longrightarrow> \<exists>m. check code n = Some m \<and> m \<le> limit"
  by (induction code arbitrary: n) (auto split: option.splits bind_splits)

theorem compiled_height:
  "scoped (length slots) e \<Longrightarrow> list_all (\<lambda>i. i < n) slots \<Longrightarrow>
    check (compile e slots) n = Some (Suc n)"
proof (induction e arbitrary: slots n)
  case (Literal v)
  then show ?case by simp
next
  case (Input i)
  then show ?case by simp
next
  case (Variable i)
  then show ?case by (simp add: list_all_length)
next
  case (Binary op a b)
  then show ?case
    by (simp add: check_append list_all_iff)
next
  case (Cond c a b)
  then show ?case by (simp add: check_append list_all_iff)
next
  case (Let a b)
  then show ?case by (simp add: check_append list_all_iff)
qed

fun snapshots :: "(input \<Rightarrow> pixel) \<Rightarrow> instruction list \<Rightarrow> pixel list \<Rightarrow> (pixel list list) option" where
  "snapshots inputs [] s = Some [s]"
| "snapshots inputs (i # code) s = Option.bind (step inputs i s)
    (\<lambda>t. map_option ((#) s) (snapshots inputs code t))"

lemma snapshots_nonempty:
  "snapshots inputs code s = Some frames \<Longrightarrow> frames \<noteq> []"
  by (cases code) (auto split: bind_splits option.splits)

theorem snapshots_execute:
  "snapshots inputs code s = Some frames \<Longrightarrow> execute inputs code s = Some (last frames)"
proof (induction code arbitrary: s frames)
  case Nil
  then show ?case by auto
next
  case (Cons i code)
  then obtain t rest where step: "step inputs i s = Some t"
    and tail: "snapshots inputs code t = Some rest" and frames: "frames = s # rest"
    by (auto split: bind_splits option.splits)
  have "rest \<noteq> []" using snapshots_nonempty[OF tail] .
  then show ?case using Cons.IH[OF tail] step frames by simp
qed

theorem bounded_execution:
  "bounded limit code (length s) \<Longrightarrow>
    \<exists>frames. snapshots inputs code s = Some frames \<and> list_all (\<lambda>t. length t \<le> limit) frames"
proof (induction code arbitrary: s)
  case Nil
  then show ?case by simp
next
  case (Cons i code)
  then obtain m where bound: "length s \<le> limit" and h: "height i (length s) = Some m"
    and tail: "bounded limit code m"
    by (auto split: option.splits)
  obtain t where step: "step inputs i s = Some t" and len: "length t = m"
    using step_height[OF h] by blast
  obtain frames where trace: "snapshots inputs code t = Some frames"
    and safe: "list_all (\<lambda>t. length t \<le> limit) frames"
    using Cons.IH tail len by blast
  show ?case using step trace safe bound by auto
qed

corollary bounded_result:
  "bounded limit code (length s) \<Longrightarrow>
    \<exists>t. execute inputs code s = Some t \<and> length t \<le> limit"
proof -
  assume bound: "bounded limit code (length s)"
  obtain frames where trace: "snapshots inputs code s = Some frames"
    and safe: "list_all (\<lambda>t. length t \<le> limit) frames"
    using bounded_execution[OF bound] by blast
  have "frames \<noteq> []" using snapshots_nonempty[OF trace] .
  then have "length (last frames) \<le> limit" using safe by (simp add: list_all_iff)
  then show ?thesis using snapshots_execute[OF trace] by blast
qed

end

A  => src/Encoding.thy +158 -0
@@ 1,158 @@
theory Encoding
  imports Native
begin

fun octets :: "nat \<Rightarrow> nat \<Rightarrow> nat list" where
  "octets 0 n = []"
| "octets (Suc k) n = n mod 256 # octets k (n div 256)"

fun integer :: "nat list \<Rightarrow> nat" where
  "integer [] = 0"
| "integer (b # bs) = b + 256 * integer bs"

lemma octets_length [simp]: "length (octets k n) = k"
  by (induction k arbitrary: n) auto

lemma octets_bytes: "set (octets k n) \<subseteq> {..<256}"
  by (induction k arbitrary: n) auto

lemma integer_octets: "integer (octets k n) = n mod 256 ^ k"
  by (induction k arbitrary: n) (simp_all add: power_Suc mod_mult2_eq add.commute)

lemma literal_bound: "unat (v :: pixel) < 256 ^ 8"
  using unsigned_less[of v, where ?'a=nat] by simp

lemma integer_literal [simp]: "word_of_nat (integer (octets 8 (unat v))) = (v :: pixel)"
  using literal_bound[of v] by (simp add: integer_octets)

fun pushcode :: "register \<Rightarrow> nat list" where
  "pushcode Rax = [80]"
| "pushcode Rcx = [81]"
| "pushcode Rbx = [83]"
| "pushcode Rdx = [82]"
| "pushcode R8 = [65,80]"
| "pushcode R9 = [65,81]"

fun popcode :: "register \<Rightarrow> nat list" where
  "popcode Rax = [88]"
| "popcode Rcx = [89]"
| "popcode Rbx = [91]"
| "popcode Rdx = [90]"
| "popcode R8 = [65,88]"
| "popcode R9 = [65,89]"

fun arithmetic :: "operator \<Rightarrow> nat list" where
  "arithmetic Add = [72,1,200]"
| "arithmetic Sub = [72,145,72,41,200]"
| "arithmetic Mul = [72,15,175,193]"
| "arithmetic And = [72,33,200]"
| "arithmetic Or = [72,9,200]"
| "arithmetic Xor = [72,49,200]"
| "arithmetic Shl = [72,145,72,211,224]"
| "arithmetic Shr = [72,145,72,211,232]"
| "arithmetic Less = [72,145,72,57,200,15,146,192,72,15,182,192]"

fun encode :: "opcode \<Rightarrow> nat list" where
  "encode (Immediate v) = [72,184] @ octets 8 (unat v)"
| "encode (Push r) = pushcode r"
| "encode (Pop r) = popcode r"
| "encode (Peek i) = [72,139,132,36] @ octets 4 (8 * i)"
| "encode (Arithmetic op) = arithmetic op"
| "encode Conditional = [72,133,219,72,15,69,193]"

fun encodable :: "opcode \<Rightarrow> bool" where
  "encodable (Peek i) = (i < 2 ^ 28)"
| "encodable _ = True"

definition fixed :: "opcode list" where
  "fixed = map Push [Rax,Rcx,Rbx,Rdx,R8,R9] @ map Pop [Rax,Rcx,Rbx,Rdx,R8,R9] @
    map Arithmetic [Add,Sub,Mul,And,Or,Xor,Shl,Shr,Less] @ [Conditional]"

definition decode :: "nat list \<Rightarrow> (opcode \<times> nat list) option" where
  "decode bs = (if take 2 bs = [72,184] then
    if 10 \<le> length bs \<and> list_all (\<lambda>b. b < 256) (take 8 (drop 2 bs)) then
      Some (Immediate (word_of_nat (integer (take 8 (drop 2 bs)))), drop 10 bs) else None
    else if take 4 bs = [72,139,132,36] then
      let n = integer (take 4 (drop 4 bs)) in
      if 8 \<le> length bs \<and> list_all (\<lambda>b. b < 256) (take 4 (drop 4 bs)) \<and>
        n mod 8 = 0 \<and> n div 8 < 2 ^ 28 then Some (Peek (n div 8), drop 8 bs) else None
    else map_option (\<lambda>op. (op, drop (length (encode op)) bs))
      (List.find (\<lambda>op. take (length (encode op)) bs = encode op) fixed))"

lemma octets_all [simp]: "list_all (\<lambda>b. b < 256) (octets k n)"
  using octets_bytes[of k n] by (auto simp: list_all_iff)

lemma peek_bound:
  "(i :: nat) < 2 ^ 28 \<Longrightarrow> 8 * i < 256 ^ 4"
  by (simp; linarith)

theorem decode_encode:
  assumes valid: "encodable op"
  shows "decode (encode op @ tail) = Some (op, tail)"
proof (cases op)
  case (Immediate v)
  then show ?thesis
    by (simp add: decode_def take_add integer_literal)
next
  case (Push r)
  then show ?thesis by (cases r) (simp_all add: decode_def fixed_def)
next
  case (Pop r)
  then show ?thesis by (cases r) (simp_all add: decode_def fixed_def)
next
  case (Peek i)
  have small: "i < 2 ^ 28" using valid Peek by simp
  have bound: "8 * i < 4294967296" using peek_bound[OF small] by simp
  show ?thesis using Peek small bound
    by (simp add: decode_def integer_octets Let_def)
next
  case (Arithmetic op)
  then show ?thesis by (cases op) (simp_all add: decode_def fixed_def)
next
  case Conditional
  then show ?thesis by (simp add: decode_def fixed_def)
qed

theorem encode_bytes: "set (encode op) \<subseteq> {..<256}"
proof (cases op)
  case (Immediate v)
  then show ?thesis using octets_bytes by auto
next
  case (Push r)
  then show ?thesis by (cases r) auto
next
  case (Pop r)
  then show ?thesis by (cases r) auto
next
  case (Peek i)
  then show ?thesis using octets_bytes by auto
next
  case (Arithmetic op)
  then show ?thesis by (cases op) auto
next
  case Conditional
  then show ?thesis by simp
qed

theorem encode_injective:
  "encodable a \<Longrightarrow> encodable b \<Longrightarrow> encode a = encode b \<longleftrightarrow> a = b"
  using decode_encode[of a "[]"] decode_encode[of b "[]"]
  by (metis append_Nil option.inject prod.inject)

fun recover :: "nat \<Rightarrow> nat list \<Rightarrow> (opcode list \<times> nat list) option" where
  "recover 0 bs = Some ([],bs)"
| "recover (Suc n) bs = Option.bind (decode bs) (\<lambda>(op,tail).
    map_option (\<lambda>(ops,rest). (op # ops,rest)) (recover n tail))"

theorem recover_encode:
  "list_all encodable ops \<Longrightarrow>
    recover (length ops) (concat (map encode ops) @ tail) = Some (ops,tail)"
  by (induction ops) (simp_all add: decode_encode)

definition executable :: "expression \<Rightarrow> nat list" where
  "executable e = concat (map encode (native (compile (optimise e) []))) @ [88,195]"

theorem executable_bytes: "set (executable e) \<subseteq> {..<256}"
  unfolding executable_def using encode_bytes by auto

end

A  => src/Examples.thy +71 -0
@@ 1,71 @@
theory Examples
  imports Frontend
begin

lemma overflow: "operate Add (-1) 1 = 0"
  by eval

lemma multiply_overflow: "operate Mul (2 ^ 63) 2 = 0"
  by eval

lemma shift_mask: "operate Shl 7 64 = 7"
  by eval

lemma shift_wrap: "operate Shl 7 65 = 14"
  by eval

lemma logical_shift: "operate Shr (-1) 63 = 1"
  by eval

lemma unsigned_comparison: "operate Less (-1) 1 = 0"
  by eval

lemma unsigned_high_bit: "operate Less 1 (2 ^ 63) = 1"
  by eval

lemma constant_fold:
  "optimise (Binary Add (Literal (-1)) (Literal 1)) = Literal 0"
  by eval

lemma dead_branch:
  "optimise (Cond (Literal 0) (Input X) (Input Y)) = Input Y"
  by eval

lemma literal_code:
  "executable (Literal 42) = [72,184,42,0,0,0,0,0,0,0,80,88,195]"
  by eval

lemma opcode_roundtrip:
  "decode [72,145,72,211,232,195] = Some (Arithmetic Shr,[195])"
  by eval

lemma truncated_literal: "decode [72,184,0,0] = None"
  by eval

lemma unaligned_slot: "decode [72,139,132,36,1,0,0,0] = None"
  by eval

lemma negative_slot: "decode [72,139,132,36,0,0,0,128] = None"
  by eval

lemma lexical_shadow:
  "reference ''(let x 7 (let x (+ x 2) (* x x)))'' ''1'' ''2'' ''3'' = (True,''81'' @ newline)"
  by eval

lemma nested_temporaries:
  "reference ''(+ 10 (let a 3 (+ a (let b 4 (* a b)))))'' ''0'' ''0'' ''0'' = (True,''25'' @ newline)"
  by eval

lemma unbound_rejected: "prepare ''(let a 3 b)'' = None"
  by eval

lemma trailing_rejected: "prepare ''(+ x y) 1'' = None"
  by eval

lemma literal_overflow_rejected: "prepare ''18446744073709551616'' = None"
  by eval

lemma incomplete_rejected: "prepare ''(if x y)'' = None"
  by eval

end

A  => src/Export.thy +11 -0
@@ 1,11 @@
theory Export
  imports Examples
begin

definition ascii :: "integer \<Rightarrow> char" where "ascii = char_of"

definition ordinal :: "char \<Rightarrow> integer" where "ordinal = of_char"

export_code artifact vectors reference ascii ordinal in SML module_name Remy file_prefix remy

end

A  => src/Frontend.thy +137 -0
@@ 1,137 @@
theory Frontend
  imports Encoding
begin

fun finish :: "string \<Rightarrow> string list \<Rightarrow> string list" where
  "finish [] out = out"
| "finish word out = rev word # out"

fun lex :: "string \<Rightarrow> string \<Rightarrow> string list \<Rightarrow> string list" where
  "lex [] word out = rev (finish word out)"
| "lex (c # cs) word out = (if c = CHR '' '' \<or> c = CHR 0x09 \<or> c = CHR 0x0A \<or> c = CHR 0x0D
    then lex cs [] (finish word out)
    else if c = CHR ''('' \<or> c = CHR '')'' then lex cs [] ([c] # finish word out)
    else lex cs (c # word) out)"

definition number :: "string \<Rightarrow> nat option" where
  "number s = (if s = [] \<or> \<not> list_all (\<lambda>c. 48 \<le> nat_of_char c \<and> nat_of_char c < 58) s
    then None else let n = foldl (\<lambda>n c. min (2 ^ 64) (10 * n + nat_of_char c - 48)) 0 s in
    if n < 2 ^ 64 then Some n else None)"

definition valid_name :: "string \<Rightarrow> bool" where
  "valid_name s = (s \<noteq> [] \<and> list_all (\<lambda>c. (97 \<le> nat_of_char c \<and> nat_of_char c \<le> 122)) s
    \<and> s \<noteq> ''let'' \<and> s \<noteq> ''if'')"

fun lookup :: "string \<Rightarrow> string list \<Rightarrow> nat option" where
  "lookup name [] = None"
| "lookup name (x # xs) = (if name = x then Some 0 else map_option Suc (lookup name xs))"

fun operator :: "string \<Rightarrow> operator option" where
  "operator s = (if s = ''+'' then Some Add else if s = ''-'' then Some Sub else if s = ''*'' then Some Mul
    else if s = ''and'' then Some And else if s = ''or'' then Some Or else if s = ''xor'' then Some Xor
    else if s = ''shl'' then Some Shl else if s = ''shr'' then Some Shr else if s = ''<'' then Some Less else None)"

definition atom :: "string list \<Rightarrow> string \<Rightarrow> expression option" where
  "atom names s = (case lookup s names of Some i \<Rightarrow> Some (Variable i) | None \<Rightarrow>
    if s = ''x'' then Some (Input X) else if s = ''y'' then Some (Input Y) else if s = ''t'' then Some (Input T)
    else map_option (Literal \<circ> word_of_nat) (number s))"

fun close :: "expression \<Rightarrow> string list \<Rightarrow> (expression \<times> string list) option" where
  "close e (s # rest) = (if s = '')'' then Some (e,rest) else None)"
| "close e [] = None"

function parse :: "nat \<Rightarrow> string list \<Rightarrow> string list \<Rightarrow> (expression \<times> string list) option" where
  "parse fuel names tokens = (if fuel = 0 then None else case tokens of [] \<Rightarrow> None | s # rest \<Rightarrow>
    if s \<noteq> ''('' then map_option (\<lambda>e. (e,rest)) (atom names s)
    else case rest of [] \<Rightarrow> None | op # args \<Rightarrow>
      if op = ''let'' then (case args of [] \<Rightarrow> None | name # body \<Rightarrow>
        if \<not> valid_name name then None else
        Option.bind (parse (fuel - 1) names body) (\<lambda>(a,tail).
        Option.bind (parse (fuel - 1) (name # names) tail) (\<lambda>(b,tail). close (Let a b) tail)))
      else if op = ''if'' then
        Option.bind (parse (fuel - 1) names args) (\<lambda>(c,tail).
        Option.bind (parse (fuel - 1) names tail) (\<lambda>(a,tail).
        Option.bind (parse (fuel - 1) names tail) (\<lambda>(b,tail). close (Cond c a b) tail)))
      else Option.bind (operator op) (\<lambda>f.
        Option.bind (parse (fuel - 1) names args) (\<lambda>(a,tail).
        Option.bind (parse (fuel - 1) names tail) (\<lambda>(b,tail). close (Binary f a b) tail))))"
  by pat_completeness auto
termination by (relation "measure (\<lambda>(fuel,names,tokens). fuel)") auto

definition prepare :: "string \<Rightarrow> expression option" where
  "prepare s = (if \<not> list_all (\<lambda>c. nat_of_char c < 128) s then None else
    Option.bind (parse 128 [] (lex s [] [])) (\<lambda>(e,rest).
      if rest = [] \<and> scoped 0 e \<and> nodes e \<le> 4096 then
        let code = compile (optimise e) [] in
        if check code 0 = Some 1 \<and> bounded 512 code 0 \<and> list_all encodable (native code)
        then Some e else None else None))"

lemma prepare_scoped: "prepare s = Some e \<Longrightarrow> scoped 0 e"
  by (auto simp: prepare_def Let_def split: if_splits bind_splits prod.splits)

lemma prepare_bounded:
  "prepare s = Some e \<Longrightarrow> bounded 512 (compile (optimise e) []) 0"
  by (auto simp: prepare_def Let_def split: if_splits bind_splits prod.splits)

lemma prepare_encodable:
  "prepare s = Some e \<Longrightarrow> list_all encodable (native (compile (optimise e) []))"
  by (auto simp: prepare_def Let_def split: if_splits bind_splits prod.splits)

theorem prepared_correct:
  "prepare source = Some e \<Longrightarrow>
    execute values (compile (optimise e) []) [] = Some [evaluate e [] values]"
  using compile_closed prepare_scoped by blast

theorem prepared_native:
  "prepare source = Some e \<Longrightarrow>
    map_option observe (run (native (compile (optimise e) [])) s) =
    Some (inputs s, evaluate e [] (inputs s) # stack s)"
  using native_expression prepare_scoped by blast

theorem prepared_bytes:
  "prepare source = Some e \<Longrightarrow>
    recover (length (native (compile (optimise e) []))) (executable e) =
    Some (native (compile (optimise e) []), [88,195])"
  unfolding executable_def using recover_encode prepare_encodable by blast

theorem prepared_stacks:
  "prepare source = Some e \<Longrightarrow>
    \<exists>frames. snapshots values (compile (optimise e) []) [] = Some frames \<and>
      list_all (\<lambda>s. length s \<le> 512) frames"
  using bounded_execution[where s="[]" and limit=512] prepare_bounded by auto

function decimal :: "nat \<Rightarrow> string" where
  "decimal n = (if n < 10 then [char_of_nat (48+n)] else decimal (n div 10) @ [char_of_nat (48+n mod 10)])"
  by pat_completeness auto
termination by (relation "measure id") (auto simp: div_less_dividend)

definition newline :: string where "newline = [CHR 0x0A]"

definition artifact :: "string \<Rightarrow> (bool \<times> string) \<times> string" where
  "artifact s = (case prepare s of None \<Rightarrow> ((False,[]), ''invalid source or resource limit'' @ newline)
    | Some e \<Rightarrow> let opt = optimise e; bytes = executable e in
      ((True, map char_of_nat bytes), decimal (nodes e) @ '' nodes -> '' @ decimal (nodes opt) @
        '' nodes, '' @ decimal (length bytes) @ '' bytes, '' @ decimal (demand opt) @ '' stack words'' @ newline))"

definition boundary :: "nat list" where
  "boundary = [0,1,63,64,65,127,255,256,2 ^ 31,2 ^ 32,2 ^ 63,2 ^ 64 - 1]"

definition sample :: "nat \<Rightarrow> input \<Rightarrow> pixel" where
  "sample i = (\<lambda>v. word_of_nat (if i < 12 then boundary ! ((i + (case v of X \<Rightarrow> 0 | Y \<Rightarrow> 3 | T \<Rightarrow> 7)) mod 12)
    else case v of X \<Rightarrow> (17*i) mod 257 | Y \<Rightarrow> (29*i) mod 131 | T \<Rightarrow> 13*i))"

definition row :: "expression \<Rightarrow> nat \<Rightarrow> nat list" where
  "row e i = concat (map (\<lambda>v. octets 8 (unat v))
    [sample i X, sample i Y, sample i T, evaluate e [] (sample i)])"

definition vectors :: "string \<Rightarrow> bool \<times> string" where
  "vectors s = (case prepare s of None \<Rightarrow> (False, ''invalid source or resource limit'' @ newline)
    | Some e \<Rightarrow> (True, map char_of_nat (concat (map (row e) [0..<512]))))"

definition reference :: "string \<Rightarrow> string \<Rightarrow> string \<Rightarrow> string \<Rightarrow> bool \<times> string" where
  "reference source x y t = (case (prepare source, number x, number y, number t) of
    (Some e,Some a,Some b,Some c) \<Rightarrow>
      (True, decimal (unat (evaluate e [] (\<lambda>v. word_of_nat (case v of X \<Rightarrow> a | Y \<Rightarrow> b | T \<Rightarrow> c)))) @ newline)
    | _ \<Rightarrow> (False,''invalid source or input'' @ newline))"

end

A  => src/Model.thy +97 -0
@@ 1,97 @@
theory Model
  imports "HOL-Library.Word" "HOL-Library.Code_Target_Numeral"
begin

unbundle bit_operations_syntax

type_synonym pixel = "64 word"

datatype input = X | Y | T

datatype operator = Add | Sub | Mul | And | Or | Xor | Shl | Shr | Less

fun operate :: "operator \<Rightarrow> pixel \<Rightarrow> pixel \<Rightarrow> pixel" where
  "operate Add a b = a + b"
| "operate Sub a b = a - b"
| "operate Mul a b = a * b"
| "operate And a b = a AND b"
| "operate Or a b = a OR b"
| "operate Xor a b = a XOR b"
| "operate Shl a b = push_bit (unat b mod 64) a"
| "operate Shr a b = drop_bit (unat b mod 64) a"
| "operate Less a b = (if a < b then 1 else 0)"

datatype expression = Literal pixel | Input input | Variable nat
  | Binary operator expression expression
  | Cond expression expression expression
  | Let expression expression

fun evaluate :: "expression \<Rightarrow> pixel list \<Rightarrow> (input \<Rightarrow> pixel) \<Rightarrow> pixel" where
  "evaluate (Literal v) env inputs = v"
| "evaluate (Input i) env inputs = inputs i"
| "evaluate (Variable i) env inputs = env ! i"
| "evaluate (Binary op a b) env inputs = operate op (evaluate a env inputs) (evaluate b env inputs)"
| "evaluate (Cond c a b) env inputs = (if evaluate c env inputs = 0 then evaluate b env inputs else evaluate a env inputs)"
| "evaluate (Let a b) env inputs = evaluate b (evaluate a env inputs # env) inputs"

fun scoped :: "nat \<Rightarrow> expression \<Rightarrow> bool" where
  "scoped n (Literal v) = True"
| "scoped n (Input i) = True"
| "scoped n (Variable i) = (i < n)"
| "scoped n (Binary op a b) = (scoped n a \<and> scoped n b)"
| "scoped n (Cond c a b) = (scoped n c \<and> scoped n a \<and> scoped n b)"
| "scoped n (Let a b) = (scoped n a \<and> scoped (Suc n) b)"

fun fold_binary :: "operator \<Rightarrow> expression \<Rightarrow> expression \<Rightarrow> expression" where
  "fold_binary op (Literal a) (Literal b) = Literal (operate op a b)"
| "fold_binary op a b = Binary op a b"

fun fold_cond :: "expression \<Rightarrow> expression \<Rightarrow> expression \<Rightarrow> expression" where
  "fold_cond (Literal c) a b = (if c = 0 then b else a)"
| "fold_cond c a b = Cond c a b"

fun optimise :: "expression \<Rightarrow> expression" where
  "optimise (Binary op a b) = fold_binary op (optimise a) (optimise b)"
| "optimise (Cond c a b) = fold_cond (optimise c) (optimise a) (optimise b)"
| "optimise (Let a b) = Let (optimise a) (optimise b)"
| "optimise e = e"

lemma fold_binary_value:
  "evaluate (fold_binary op a b) env inputs = operate op (evaluate a env inputs) (evaluate b env inputs)"
  by (cases a; cases b) auto

lemma fold_cond_value:
  "evaluate (fold_cond c a b) env inputs = (if evaluate c env inputs = 0 then evaluate b env inputs else evaluate a env inputs)"
  by (cases c) auto

theorem optimise_correct:
  "evaluate (optimise e) env inputs = evaluate e env inputs"
  by (induction e arbitrary: env) (simp_all add: fold_binary_value fold_cond_value)

lemma fold_binary_scoped:
  "scoped n a \<Longrightarrow> scoped n b \<Longrightarrow> scoped n (fold_binary op a b)"
  by (cases a; cases b) auto

lemma fold_cond_scoped:
  "scoped n c \<Longrightarrow> scoped n a \<Longrightarrow> scoped n b \<Longrightarrow> scoped n (fold_cond c a b)"
  by (cases c) auto

theorem optimise_scoped: "scoped n e \<Longrightarrow> scoped n (optimise e)"
  by (induction e arbitrary: n) (auto intro: fold_binary_scoped fold_cond_scoped)

fun nodes :: "expression \<Rightarrow> nat" where
  "nodes (Binary op a b) = Suc (nodes a + nodes b)"
| "nodes (Cond c a b) = Suc (nodes c + nodes a + nodes b)"
| "nodes (Let a b) = Suc (nodes a + nodes b)"
| "nodes _ = 1"

lemma fold_binary_nodes: "nodes (fold_binary op a b) \<le> Suc (nodes a + nodes b)"
  by (cases a; cases b) auto

lemma fold_cond_nodes: "nodes (fold_cond c a b) \<le> Suc (nodes c + nodes a + nodes b)"
  by (cases c) auto

theorem optimise_size: "nodes (optimise e) \<le> nodes e"
  by (induction e) (auto intro: order_trans[OF fold_binary_nodes] order_trans[OF fold_cond_nodes])

end

A  => src/Native.thy +163 -0
@@ 1,163 @@
theory Native
  imports Compiler
begin

datatype register = Rax | Rcx | Rbx | Rdx | R8 | R9

datatype opcode = Immediate pixel | Push register | Pop register | Peek nat
  | Arithmetic operator | Conditional

record machine =
  registers :: "register \<Rightarrow> pixel"
  stack :: "pixel list"

fun input_register :: "input \<Rightarrow> register" where
  "input_register X = R8"
| "input_register Y = R9"
| "input_register T = Rdx"

definition inputs :: "machine \<Rightarrow> input \<Rightarrow> pixel" where
  "inputs s i = registers s (input_register i)"

fun exchanges :: "operator \<Rightarrow> bool" where
  "exchanges Sub = True"
| "exchanges Shl = True"
| "exchanges Shr = True"
| "exchanges Less = True"
| "exchanges _ = False"

fun transition :: "opcode \<Rightarrow> machine \<Rightarrow> machine option" where
  "transition (Immediate v) s = Some (s\<lparr>registers := (registers s)(Rax := v)\<rparr>)"
| "transition (Push r) s = Some (s\<lparr>stack := registers s r # stack s\<rparr>)"
| "transition (Pop r) s = (case stack s of [] \<Rightarrow> None | v # vs \<Rightarrow>
    Some (s\<lparr>registers := (registers s)(r := v), stack := vs\<rparr>))"
| "transition (Peek i) s = (if i < length (stack s) then
    Some (s\<lparr>registers := (registers s)(Rax := stack s ! i)\<rparr>) else None)"
| "transition (Arithmetic op) s = Some (s\<lparr>registers :=
    (registers s)(Rcx := if exchanges op then registers s Rax else registers s Rcx,
      Rax := operate op (registers s Rcx) (registers s Rax))\<rparr>)"
| "transition Conditional s = Some (s\<lparr>registers :=
    (registers s)(Rax := if registers s Rbx = 0 then registers s Rax else registers s Rcx)\<rparr>)"

fun run :: "opcode list \<Rightarrow> machine \<Rightarrow> machine option" where
  "run [] s = Some s"
| "run (i # code) s = Option.bind (transition i s) (run code)"

lemma run_append:
  "run (a @ b) s = Option.bind (run a s) (run b)"
  by (induction a arbitrary: s) (auto split: option.splits bind_splits)

fun lower :: "instruction \<Rightarrow> opcode list" where
  "lower (Constant v) = [Immediate v, Push Rax]"
| "lower (Fetch i) = [Push (input_register i)]"
| "lower (Pick i) = [Peek i, Push Rax]"
| "lower (Calculate op) = [Pop Rax, Pop Rcx, Arithmetic op, Push Rax]"
| "lower Choose = [Pop Rax, Pop Rcx, Pop Rbx, Conditional, Push Rax]"
| "lower Slide = [Pop Rax, Pop Rcx, Push Rax]"

definition observe :: "machine \<Rightarrow> (input \<Rightarrow> pixel) \<times> pixel list" where
  "observe s = (inputs s, stack s)"

lemma input_scratch [simp]:
  "input_register i \<noteq> Rax"
  "input_register i \<noteq> Rcx"
  "input_register i \<noteq> Rbx"
  by (cases i; simp)+

lemma inputs_update [simp]:
  "inputs (s\<lparr>registers := (registers s)(Rax := a)\<rparr>) = inputs s"
  "inputs (s\<lparr>registers := (registers s)(Rcx := a)\<rparr>) = inputs s"
  "inputs (s\<lparr>registers := (registers s)(Rbx := a)\<rparr>) = inputs s"
  "inputs (s\<lparr>stack := vs\<rparr>) = inputs s"
  by (auto simp: inputs_def fun_eq_iff split: input.splits)

theorem lower_correct:
  "map_option observe (run (lower i) s) =
    map_option (\<lambda>vs. (inputs s, vs)) (step (inputs s) i (stack s))"
proof (cases i)
  case (Constant v)
  then show ?thesis by (simp add: observe_def)
next
  case (Fetch i)
  then show ?thesis by (simp add: observe_def inputs_def)
next
  case (Pick i)
  then show ?thesis by (simp add: observe_def)
next
  case (Calculate op)
  then show ?thesis
    by (cases "stack s"; simp add: observe_def inputs_def fun_eq_iff split: list.splits input.splits)
next
  case Choose
  then show ?thesis
    by (cases "stack s"; simp add: observe_def inputs_def fun_eq_iff split: list.splits input.splits)
next
  case Slide
  then show ?thesis
    by (cases "stack s"; simp add: observe_def inputs_def fun_eq_iff split: list.splits input.splits)
qed

definition native :: "instruction list \<Rightarrow> opcode list" where
  "native code = concat (map lower code)"

theorem native_correct:
  "map_option observe (run (native code) s) =
    map_option (\<lambda>vs. (inputs s, vs)) (execute (inputs s) code (stack s))"
proof (induction code arbitrary: s)
  case Nil
  then show ?case by (simp add: native_def observe_def)
next
  case (Cons i code)
  show ?case
    using Cons.IH lower_correct[of i s]
    by (auto simp: native_def run_append observe_def split: option.splits bind_splits)
qed

corollary native_expression:
  assumes "scoped 0 e"
  shows "map_option observe (run (native (compile (optimise e) [])) s) =
    Some (inputs s, evaluate e [] (inputs s) # stack s)"
proof -
  have code: "execute (inputs s) (compile (optimise e) []) (stack s) =
      Some (evaluate e [] (inputs s) # stack s)"
    using compile_correct[where e="optimise e" and env="[]" and slots="[]" and s="stack s" and inputs="inputs s"]
      assms optimise_scoped
    by (simp add: represents_def optimise_correct)
  show ?thesis using native_correct[of "compile (optimise e) []" s] code by simp
qed

theorem native_return:
  assumes "scoped 0 e"
  shows "\<exists>q. run (native (compile (optimise e) []) @ [Pop Rax]) s = Some q \<and>
    registers q Rax = evaluate e [] (inputs s) \<and> stack q = stack s \<and> inputs q = inputs s"
proof -
  obtain q where run: "run (native (compile (optimise e) [])) s = Some q"
    and ins: "inputs q = inputs s" and stk: "stack q = evaluate e [] (inputs s) # stack s"
    using native_expression[OF assms, of s]
    by (auto simp: observe_def split: option.splits)
  show ?thesis using run ins stk
    by (auto simp: run_append inputs_def fun_eq_iff)
qed

fun writes :: "opcode \<Rightarrow> register set" where
  "writes (Immediate v) = {Rax}"
| "writes (Pop r) = {r}"
| "writes (Peek i) = {Rax}"
| "writes (Arithmetic op) = {Rax,Rcx}"
| "writes Conditional = {Rax}"
| "writes _ = {}"

lemma transition_preserves:
  "transition i s = Some t \<Longrightarrow> r \<notin> writes i \<Longrightarrow> registers t r = registers s r"
  by (cases i) (auto split: list.splits if_splits)

theorem run_preserves:
  "run code s = Some t \<Longrightarrow> list_all (\<lambda>i. r \<notin> writes i) code \<Longrightarrow>
    registers t r = registers s r"
  by (induction code arbitrary: s) (auto dest: transition_preserves split: option.splits bind_splits)

lemma lower_scratch:
  "list_all (\<lambda>i. writes i \<subseteq> {Rax,Rcx,Rbx}) (lower op)"
  by (cases op) auto

end

A  => tests/native.py +102 -0
@@ 1,102 @@
import ctypes
import mmap
from pathlib import Path
import platform
import random
import struct
import subprocess
import tempfile


ROOT = Path(__file__).resolve().parents[1]
MASK = (1 << 64) - 1
OPERATORS = ("+", "-", "*", "and", "or", "xor", "shl", "shr", "<")


def evaluate(e, env):
    if isinstance(e, int):
        return e
    if isinstance(e, str):
        return env[e]
    op, *args = e
    if op == "let":
        name, a, b = args
        return evaluate(b, {**env, name: evaluate(a, env)})
    if op == "if":
        c, a, b = args
        return evaluate(a if evaluate(c, env) else b, env)
    a, b = (evaluate(x, env) for x in args)
    return {"+": lambda: a + b, "-": lambda: a - b, "*": lambda: a * b,
            "and": lambda: a & b, "or": lambda: a | b, "xor": lambda: a ^ b,
            "shl": lambda: a << (b & 63), "shr": lambda: a >> (b & 63),
            "<": lambda: int(a < b)}[op]() & MASK


def source(e):
    return "(" + " ".join(map(source, e)) + ")" if isinstance(e, tuple) else str(e)


def native(code):
    image = mmap.mmap(-1, len(code), prot=mmap.PROT_READ | mmap.PROT_WRITE | mmap.PROT_EXEC)
    image.write(code)
    address = ctypes.addressof(ctypes.c_char.from_buffer(image))
    bridge = bytes.fromhex("53 49 89 f8 49 89 f1 48 b8") + struct.pack("<Q", address) + bytes.fromhex("ff d0 5b c3")
    thunk = mmap.mmap(-1, len(bridge), prot=mmap.PROT_READ | mmap.PROT_WRITE | mmap.PROT_EXEC)
    thunk.write(bridge)
    function = ctypes.CFUNCTYPE(ctypes.c_uint64, ctypes.c_uint64, ctypes.c_uint64, ctypes.c_uint64)(
        ctypes.addressof(ctypes.c_char.from_buffer(thunk)))
    return function, image, thunk


def generate(rng, depth, names):
    if depth == 0 or rng.randrange(5) == 0:
        return rng.choice([0, 1, 63, 64, 65, MASK, 1 << 63, *names])
    kind = rng.randrange(5)
    if kind == 0:
        name = rng.choice(("a", "b", "c", "x"))
        return ("let", name, generate(rng, depth - 1, names), generate(rng, depth - 1, [*names, name]))
    if kind == 1:
        return ("if", *(generate(rng, depth - 1, names) for _ in range(3)))
    return (rng.choice(OPERATORS), *(generate(rng, depth - 1, names) for _ in range(2)))


def main():
    if platform.system() != "Linux" or platform.machine() != "x86_64":
        raise RuntimeError("native differential tests require an x86-64 Linux host")
    rng = random.Random(0x52454D59)
    cases = [(op, "x", "y") for op in OPERATORS]
    cases += [("let", "a", ("+", "x", 3), ("+", "y", ("let", "b", "t", ("*", "a", "b")))),
              ("let", "x", "y", ("if", "t", ("+", "x", 1), ("-", "x", 1)))]
    cases += [generate(rng, 5, ["x", "y", "t"]) for _ in range(117)]
    comparisons = 0
    with tempfile.TemporaryDirectory() as directory:
        path, binary = Path(directory) / "source", Path(directory) / "binary"
        for case in cases:
            path.write_text(source(case))
            result = subprocess.run([ROOT / ".build/remy", path, binary], capture_output=True, text=True, timeout=30)
            assert result.returncode == 0, (case, result.stderr)
            function, image, thunk = native(binary.read_bytes())
            vectors = [(a, b, a ^ b) for a in (0, 1, MASK, 1 << 63) for b in (0, 1, 63, 64, 65, MASK)]
            vectors += [tuple(rng.getrandbits(64) for _ in range(3)) for _ in range(24)]
            for x, y, t in vectors:
                expected = evaluate(case, {"x": x, "y": y, "t": t})
                actual = function(x, y, t)
                assert actual == expected, (source(case), x, y, t, expected, actual)
                comparisons += 1
            result = subprocess.run([ROOT / ".build/remy", "eval", path, str(x), str(y), str(t)],
                                    capture_output=True, text=True, timeout=30)
            assert result.returncode == 0 and int(result.stdout) == expected, (case, result.stdout, result.stderr)
            del function
            image.close()
            thunk.close()
        for bad in ("", "(+ x)", "(+ x y) 1", "(let a 3 b)", "(if 1 2)", "(let if 3 4)",
                    "18446744073709551616", "(+ x z)", "(div x y)", "é", " " * 65537):
            path.write_text(bad)
            binary.write_bytes(b"preserve")
            result = subprocess.run([ROOT / ".build/remy", path, binary], capture_output=True, timeout=30)
            assert result.returncode != 0 and binary.read_bytes() == b"preserve", bad
    print(f"{len(cases)} programs, {comparisons} native comparisons, HOL reference checks and 11 rejection cases passed")


if __name__ == "__main__":
    main()