test: group examples into per-category folders

Move examples/*.sx and their expected/ snapshots into per-category
subfolders (examples/<category>/...). Folder = leading filename token,
with ffi-objc/ffi-jni kept whole; filenames are unchanged. The corpus
runner and LSP sweep now discover each category's expected/ dir, while
issues/ stays flat. Example 1058's repo-root-relative companion import
is made file-relative. Path strings embedded in 164 snapshots were
regenerated (path-only changes). Test-layout docs in CLAUDE.md updated.
This commit is contained in:
agra
2026-06-21 14:41:34 +03:00
parent 6d1409bc1f
commit 66bdc70bf1
3357 changed files with 456 additions and 363 deletions

View File

@@ -0,0 +1,25 @@
// Stream B1 (fibers) — `abi(.naked)` emits a naked function end-to-end.
//
// An `abi(.naked)` function has no calling-convention prologue/epilogue/frame
// and no implicit `__sx_ctx`: its body is a single asm block that sets the
// return register and emits its own `ret`. This is the substrate the per-arch
// fiber context-switch is built on (design §4.6) — a `.c` epilogue would
// restore SP from the wrong stack across a switch (SP-in ≠ SP-out by design).
//
// Lowered via LLVM's `naked` function attribute: the body is emitted verbatim
// (the inline asm + its own `ret`) with NO frame setup; the IR shows
// `attributes #N = { naked noinline nounwind }` and the bare asm. aarch64-pinned
// (the asm body is per-arch); runs end-to-end here (exit 42), ir-only on a
// mismatch. See the x86_64 sibling 1802. NOTE: the `.ir` proves the `naked`
// keyword + asm emitted, NOT register-save correctness (that's the B1.3
// switch-stress harness's job).
answer :: () -> i64 abi(.naked) {
asm volatile {
#string ASM
mov x0, #42
ret
ASM
};
}
main :: () -> i64 { return answer(); }

View File

@@ -0,0 +1,22 @@
// Stream B1 (fibers) — `abi(.naked)` on a GENERIC function emits a correct naked
// body (regression for an adversarial-review finding).
//
// A generic function is monomorphized through a different Function-creation path
// (lower/generic.zig) than a plain decl. That path originally left `is_naked`
// unset, so a generic `abi(.naked)` instance silently shipped a FRAMED body — it
// returned 42 but leaked the prologue's stack adjustment (the exact SP-in ≠
// SP-out corruption the `.naked` ABI exists to avoid). generic.zig (and the
// sibling pack-expansion path in pack.zig) now set `is_naked` and emit the
// asm-only naked body. This pins that: the monomorphized `answer__i64` is a
// proper naked function (no frame), returning 42. aarch64-pinned (the asm body
// is per-arch); runs end-to-end on a matching host, ir-only elsewhere.
answer :: ($T: Type) -> i64 abi(.naked) {
asm volatile {
#string A
mov x0, #42
ret
A
};
}
main :: () -> i64 { return answer(i64); }

View File

@@ -0,0 +1,19 @@
// Stream B1 (fibers) — x86_64 sibling of 1800: `abi(.naked)` emits a naked
// function whose body is raw x86_64 asm (returns 42 in eax, then its own `ret`).
//
// Lowered via LLVM's `naked` attribute (no prologue/epilogue/frame). x86_64-
// pinned via `.build`: ir-only on a non-x86 host — the `.ir` snapshot locks the
// `naked` attribute + the bare asm body (`movl $42, %eax` / `ret`) and the
// frame-pointer-free attribute set — and runs end-to-end (exit 42) on
// x86_64-linux. The IR text (the `naked` attribute, the `call void asm`) is
// target-independent; only the asm string differs from the aarch64 1800.
answer :: () -> i64 abi(.naked) {
asm volatile {
#string A
movl $42, %eax
ret
A
};
}
main :: () -> i64 { return answer(); }

View File

@@ -0,0 +1,25 @@
// Stream B1 (fibers) — an `abi(.naked)` function with PARAMETERS reads its args
// from ABI registers (the shape the fiber context-switch needs: `swap_context`
// reads `from`/`to` from x0/x1).
//
// A naked function has no frame, so params are NOT spilled to stack slots — they
// stay in their ABI registers and the asm body reads them directly. Here `a` is
// in x0, `b` in x1 (aarch64 AAPCS), and the result returns in x0: `add x0, x0,
// x1`. The lowering skips the param-alloca loop for `.naked` (decl.zig /
// generic.zig); the LLVM args are declared-but-unused, which the verifier allows
// (spilling them would emit `store i64 %0, …` → "cannot use argument of naked
// function"). aarch64-pinned; runs end-to-end (exit 42), ir-only on a mismatch.
//
// Regression for an adversarial-review finding: before the param-alloca guard, a
// param-bearing `.naked` fn emitted invalid LLVM (loud verifier error) instead of
// a working naked function.
add :: (a: i64, b: i64) -> i64 abi(.naked) {
asm volatile {
#string A
add x0, x0, x1
ret
A
};
}
main :: () -> i64 { return add(40, 2); }

View File

@@ -0,0 +1,48 @@
// Stream B1 (fibers) step B1.1 — per-fiber `context` root, via plain
// snapshot + `push` (NO compiler change — the substrate the fiber spawn relies on).
//
// `context` is an implicit `*Context` parameter (slot 0) threaded through every
// sx call, and `push Context` allocates the new context on the caller's stack
// frame — so a function's context is CALL-CARRIED and rides its own stack, never
// read from a global. That is exactly what a fiber needs: each fiber, on its own
// stack, reads its own root context.
//
// This locks the spawn convention end-to-end with ordinary language features:
// 1. capture the spawner's context as a value (`snap := context`)
// 2. store the snapshot in a struct (the stand-in `Fiber`)
// 3. a trampoline, running under a DIFFERENT ambient context, installs the
// fiber's stored root before entering the body (`push f.root { … }`)
// The body sees the snapshot (42), not the trampoline's ambient context (99),
// and the `push` scope restores the ambient context on exit. No fiber runtime
// exists yet (that is B1.3) — this proves the context plumbing it will build on.
#import "modules/std.sx";
// Stand-in for a Fiber: stores the spawner's snapshotted root Context.
Fiber :: struct { root: Context; }
// Reads this call's context sentinel (carried in `context.data` here for the
// test; a real fiber carries its allocator / io the same way).
sentinel :: () -> i64 { return xx context.data; }
// Trampoline: runs under its own ambient context, but installs the fiber's
// stored root before entering the fiber body.
run_fiber :: (f: *Fiber) -> i64 {
push f.root {
return sentinel();
}
}
main :: () {
snap := context;
snap.data = xx 42; // the spawner's sentinel
f := Fiber.{ root = snap }; // snapshot stored in the fiber
other := context;
other.data = xx 99; // a DIFFERENT ambient context
push other {
// Ambient is 99 here; the trampoline must install f.root (42).
print("fiber root: {}\n", run_fiber(@f));
// After run_fiber returns, this scope's ambient context is intact.
print("ambient after: {}\n", sentinel());
}
}

View File

@@ -0,0 +1,28 @@
// B1.2 — the async ergonomic layer over the `Io` capability, blocking
// default. `context.io.async(worker, ..args)` runs the worker to completion
// inline and returns a `.ready` Future($R); `f.await()` yields the result
// (a value-failable `($R, !IoErr)`, handled with `or`). `context.io.now_ms()`
// reads the monotonic clock through the same capability.
//
// Worker form: a lambda whose params are annotated at the call site
// (`(a: i64, b: i64) -> i64 => …`); `..args` forwards the call-site
// arguments to it.
#import "modules/std.sx";
main :: () {
// Homogeneous args.
s := context.io.async((a: i64, b: i64) -> i64 => a + b, 40, 2);
print("sum: {}\n", s.await() or { -1 });
// Single arg.
d := context.io.async((x: i64) -> i64 => x * 2, 21);
print("double: {}\n", d.await() or { -1 });
// Nullary worker — the variadic `async` binds an empty pack, so no separate
// `async_void` entry is needed.
n := context.io.async(() -> i64 => 42);
print("nullary: {}\n", n.await() or { -1 });
// The Io capability also carries a monotonic clock.
if context.io.now_ms() >= 0 { print("clock ok\n"); }
}

View File

@@ -0,0 +1,17 @@
// B1.2 — cancellation rides the `!` error channel (model (a)). `f.cancel()`
// sets the per-future cancel flag + marks `state = .canceled`, so a
// subsequent `f.await()` raises `error.Canceled` out of its value-failable
// `($R, !IoErr)` — caught here with `or`. A future that is NOT canceled
// awaits its value normally.
#import "modules/std.sx";
main :: () {
// Not canceled → await yields the value.
ok := context.io.async((n: i64) -> i64 => n, 7);
print("ok: {}\n", ok.await() or { -1 });
// Canceled → await raises .Canceled → the `or` default is taken.
c := context.io.async((n: i64) -> i64 => n, 7);
c.cancel();
print("canceled: {}\n", c.await() or { -99 });
}

View File

@@ -0,0 +1,150 @@
// Stream B1 (fibers) B1.3a — the stackful context switch, in pure sx over the
// `abi(.naked)` primitive. `swap_context(from, to)` saves the callee-saved
// registers + SP/LR into `*from` and loads them from `*to`, then `ret`s onto
// `to`'s stack (SP-in ≠ SP-out by design — why it must be `.naked`, not `.c`).
// A fiber is bootstrapped by hand: its saved context starts with SP = the top
// of a fresh `alloc_bytes` stack, LR = a global-asm trampoline, and x19 = the
// `*Fiber` (the trampoline moves it to x0 and `bl`s the exported entry).
//
// This is the foundational switch + an indirect survival harness:
// - a 2-fiber ping-pong (A ⇄ B, 3 rounds each) — resume-mid-stack across
// switches; a per-fiber stack canary held live across every suspend must
// survive (the compiler allocates it to a callee-saved reg / stack slot,
// so a clobbered switch would corrupt it);
// - a deep recursive chain (64 frames) suspended at the bottom and resumed —
// every frame's stack-local is verified on the unwind.
//
// What it does NOT yet do (B1.3a-2): EXPLICITLY scribble every callee-saved GP
// (x19-x28) + FP (d8-d15) register with sentinels and check them in asm — the
// full §10.7 gate. This harness exercises preservation indirectly (via
// compiler-allocated live values), which catches a broken SP/LR or a dropped
// callee-saved, but not a single specific register the allocator didn't use.
//
// aarch64-pinned (the asm + the 13-slot save area are per-arch); runs
// end-to-end here, ir-only on a mismatch. The x86_64 sibling + `mmap` guard-
// page stacks are B1.3b.
#import "modules/std.sx";
// Saved context: x19..x28 (10), x29/fp, x30/lr, sp — 13 u64 slots.
FiberCtx :: struct { regs: [13]u64; }
Fiber :: struct {
ctx: FiberCtx;
peer: *FiberCtx; // ping-pong hand-off target
finish: *FiberCtx; // where to switch when the body ends (the spawner)
count: *i64; // shared round counter
verified: *i64; // shared count of verified recursion frames
rounds: i64;
id: i64;
mode: i64; // 0 = ping-pong, 1 = deep recursion
canary_fail: i64;
depth_fail: i64;
}
// The switch: x0 = from, x1 = to (read straight from the ABI registers — a
// naked fn has no frame, so its params are never spilled).
swap_context :: (from: *FiberCtx, to: *FiberCtx) abi(.naked) {
asm volatile {
#string ASM
stp x19, x20, [x0, #0]
stp x21, x22, [x0, #16]
stp x23, x24, [x0, #32]
stp x25, x26, [x0, #48]
stp x27, x28, [x0, #64]
stp x29, x30, [x0, #80]
mov x9, sp
str x9, [x0, #96]
ldp x19, x20, [x1, #0]
ldp x21, x22, [x1, #16]
ldp x23, x24, [x1, #32]
ldp x25, x26, [x1, #48]
ldp x27, x28, [x1, #64]
ldp x29, x30, [x1, #80]
ldr x9, [x1, #96]
mov sp, x9
ret
ASM
};
}
// First-entry trampoline: a fiber's bootstrapped LR points here. x19 holds the
// `*Fiber` (preset in the saved context); move it to x0 and call the body.
asm {
#string T
.global _fib_tramp
_fib_tramp:
mov x0, x19
bl _fib_body
brk #0
T,
};
fib_tramp :: () extern;
// Descend `depth` frames, yield to the spawner at the bottom, then on resume
// verify every frame's stack-local survived the switch.
descend :: (self: *Fiber, depth: i64) -> i64 {
if depth == 0 {
swap_context(@self.ctx, self.finish);
return 0;
}
marker : i64 = depth * 7 + 3;
bad := descend(self, depth - 1);
if marker == depth * 7 + 3 { self.verified.* = self.verified.* + 1; } else { bad = bad + 1; }
return bad;
}
fib_body :: (self: *Fiber) export "fib_body" {
if self.mode == 1 {
self.depth_fail = descend(self, 64);
swap_context(@self.ctx, self.finish);
return;
}
canary : u64 = 0xCA11AB1E0000 + (xx self.id);
i := 0;
while i < self.rounds {
self.count.* = self.count.* + 1;
swap_context(@self.ctx, self.peer);
if canary != 0xCA11AB1E0000 + (xx self.id) { self.canary_fail = self.canary_fail + 1; }
i = i + 1;
}
swap_context(@self.ctx, self.finish);
}
STACK :: 131072;
boot :: (f: *Fiber) {
base : *void = context.allocator.alloc_bytes(STACK);
top : u64 = (xx base) + STACK;
top = top - (top % 16); // 16-byte aligned stack top (AAPCS)
f.ctx.regs[0] = xx f; // x19 = self
f.ctx.regs[10] = 0; // fp
f.ctx.regs[11] = xx fib_tramp; // lr → trampoline
f.ctx.regs[12] = top; // sp
f.canary_fail = 0;
f.depth_fail = 0;
}
main :: () -> i64 {
main_ctx : FiberCtx = ---;
count : i64 = 0;
verified : i64 = 0;
// Scenario 1: 2-fiber ping-pong with a per-fiber stack canary.
a : Fiber = ---; a.id = 1; a.mode = 0; a.rounds = 3; a.count = @count; a.verified = @verified; a.finish = @main_ctx;
b : Fiber = ---; b.id = 2; b.mode = 0; b.rounds = 3; b.count = @count; b.verified = @verified; b.finish = @main_ctx;
a.peer = @b.ctx; b.peer = @a.ctx;
boot(@a); boot(@b);
swap_context(@main_ctx, @a.ctx);
print("rounds: {}\n", count);
print("canary fails: {}\n", a.canary_fail + b.canary_fail);
// Scenario 2: a deep recursive chain suspended at the bottom, then resumed.
c : Fiber = ---; c.id = 3; c.mode = 1; c.count = @count; c.verified = @verified; c.peer = @main_ctx; c.finish = @main_ctx;
boot(@c);
swap_context(@main_ctx, @c.ctx); // descend to the bottom, yields back
swap_context(@main_ctx, @c.ctx); // resume → unwind + verify, then finish
print("frames verified: {}\n", verified);
print("depth fails: {}\n", c.depth_fail);
return 0;
}

View File

@@ -0,0 +1,256 @@
// Stream B1 (fibers) B1.3a-2 — the context-switch STRESS GATE (design §10.7):
// explicitly scribble EVERY callee-saved register with a sentinel, switch, and
// verify every one survived. This is the correctness gate the run/snapshot
// tests can't be — a switch that drops a register "happens to print right".
//
// `swap_context` here saves the COMPLETE AAPCS64 callee-saved set: the integer
// regs x19-x28 + x29(fp) + x30(lr) + sp, AND the FP regs d8-d15. Per AAPCS64
// §6.1.2 only the LOW 64 bits of v8-v15 are callee-saved, so `d8-d15` is exactly
// sufficient (q8-q15 is not required). x18 is the Apple platform register —
// reserved, never touched. (1807 is the foundational GP-only switch; this is the
// complete one + the explicit gate.)
//
// The gate is a 2-fiber MUTUAL scribble: A loads sentinels base_A+1.. into every
// callee-saved reg and yields; while A is suspended, B loads its OWN distinct
// sentinels into the same physical registers; when A resumes it checks every reg
// still holds base_A — which is only possible if `swap_context` saved+restored
// it. (A single fiber yielding to an idle peer would NOT exercise preservation —
// the peer must clobber the registers. Validated adversarially: dropping the
// d8-d15 save/restore makes this report 8 mismatches; dropping x27/x28 reports 2.)
//
// Honest scope (what a register-sentinel gate does NOT cover, all spec-correct
// for a call-boundary swap but worth stating): NZCV flags, FPSR, FPCR (rounding
// mode — thread-global, bleeds across fibers if changed), and TPIDR_EL0/TLS
// (errno, allocator thread-caches — shared by same-thread fibers) are not
// swapped. fp=0 bootstrap means unwinders/signal handlers can't walk past a
// fiber's trampoline (no CFI for the swap). These matter at the N×M:1 / signals
// stages, not for the single-thread switch this gate proves.
//
// aarch64-pinned (per-arch asm + 21-slot save area); runs end-to-end here,
// ir-only on a mismatch. x86_64 sibling + mmap guard-page stacks are B1.3b.
#import "modules/std.sx";
// 21 slots: [0..9]=x19..x28 [10]=fp [11]=lr [12]=sp [13..20]=d8..d15.
FiberCtx :: struct { regs: [21]u64; }
Fiber :: struct {
ctx: FiberCtx;
peer: *FiberCtx; // scribble_verify yields here — the clobberer
next: *FiberCtx; // where to switch after verifying
base: u64;
mismatches: i64;
}
// The complete switch: save callee-saved (x19-x28, fp, lr, sp, d8-d15) into
// *from, load from *to, ret onto to's stack. x0=from, x1=to (read straight from
// the ABI registers — a naked fn has no frame). `export`ed so scribble_verify
// can reach it by symbol with `bl`.
swap_context :: (from: *FiberCtx, to: *FiberCtx) abi(.naked) export "swap_context" {
asm volatile {
#string ASM
stp x19, x20, [x0, #0]
stp x21, x22, [x0, #16]
stp x23, x24, [x0, #32]
stp x25, x26, [x0, #48]
stp x27, x28, [x0, #64]
stp x29, x30, [x0, #80]
mov x9, sp
str x9, [x0, #96]
stp d8, d9, [x0, #104]
stp d10, d11, [x0, #120]
stp d12, d13, [x0, #136]
stp d14, d15, [x0, #152]
ldp x19, x20, [x1, #0]
ldp x21, x22, [x1, #16]
ldp x23, x24, [x1, #32]
ldp x25, x26, [x1, #48]
ldp x27, x28, [x1, #64]
ldp x29, x30, [x1, #80]
ldr x9, [x1, #96]
mov sp, x9
ldp d8, d9, [x1, #104]
ldp d10, d11, [x1, #120]
ldp d12, d13, [x1, #136]
ldp d14, d15, [x1, #152]
ret
ASM
};
}
// Load sentinel base+1..+10 into x19-x28 and base+11..+18 into d8-d15, yield to
// `peer`, and on resume count the registers that did NOT survive. Naked, so it
// honors its caller's ABI by hand: a 176-byte frame saves the CALLER's
// callee-saved (which it clobbers) + base (x2 is not preserved across the swap);
// after the swap it reloads base, compares every reg, restores the caller's
// regs, and returns the mismatch count in x0. The original return address is
// saved (frame+88) before the `bl` and reloaded after — the swap round-trips
// sp+lr, so execution resumes right after the `bl` on the same frame.
scribble_verify :: (self_ctx: *FiberCtx, peer: *FiberCtx, base: u64) -> i64 abi(.naked) export "scribble_verify" {
asm volatile {
#string SV
sub sp, sp, #176
stp x19, x20, [sp, #0]
stp x21, x22, [sp, #16]
stp x23, x24, [sp, #32]
stp x25, x26, [sp, #48]
stp x27, x28, [sp, #64]
stp x29, x30, [sp, #80]
stp d8, d9, [sp, #96]
stp d10, d11, [sp, #112]
stp d12, d13, [sp, #128]
stp d14, d15, [sp, #144]
str x2, [sp, #160]
add x19, x2, #1
add x20, x2, #2
add x21, x2, #3
add x22, x2, #4
add x23, x2, #5
add x24, x2, #6
add x25, x2, #7
add x26, x2, #8
add x27, x2, #9
add x28, x2, #10
add x9, x2, #11
fmov d8, x9
add x9, x2, #12
fmov d9, x9
add x9, x2, #13
fmov d10, x9
add x9, x2, #14
fmov d11, x9
add x9, x2, #15
fmov d12, x9
add x9, x2, #16
fmov d13, x9
add x9, x2, #17
fmov d14, x9
add x9, x2, #18
fmov d15, x9
bl _swap_context
ldr x2, [sp, #160]
mov x10, #0
add x9, x2, #1
cmp x19, x9
cinc x10, x10, ne
add x9, x2, #2
cmp x20, x9
cinc x10, x10, ne
add x9, x2, #3
cmp x21, x9
cinc x10, x10, ne
add x9, x2, #4
cmp x22, x9
cinc x10, x10, ne
add x9, x2, #5
cmp x23, x9
cinc x10, x10, ne
add x9, x2, #6
cmp x24, x9
cinc x10, x10, ne
add x9, x2, #7
cmp x25, x9
cinc x10, x10, ne
add x9, x2, #8
cmp x26, x9
cinc x10, x10, ne
add x9, x2, #9
cmp x27, x9
cinc x10, x10, ne
add x9, x2, #10
cmp x28, x9
cinc x10, x10, ne
add x9, x2, #11
fmov x11, d8
cmp x11, x9
cinc x10, x10, ne
add x9, x2, #12
fmov x11, d9
cmp x11, x9
cinc x10, x10, ne
add x9, x2, #13
fmov x11, d10
cmp x11, x9
cinc x10, x10, ne
add x9, x2, #14
fmov x11, d11
cmp x11, x9
cinc x10, x10, ne
add x9, x2, #15
fmov x11, d12
cmp x11, x9
cinc x10, x10, ne
add x9, x2, #16
fmov x11, d13
cmp x11, x9
cinc x10, x10, ne
add x9, x2, #17
fmov x11, d14
cmp x11, x9
cinc x10, x10, ne
add x9, x2, #18
fmov x11, d15
cmp x11, x9
cinc x10, x10, ne
ldp x19, x20, [sp, #0]
ldp x21, x22, [sp, #16]
ldp x23, x24, [sp, #32]
ldp x25, x26, [sp, #48]
ldp x27, x28, [sp, #64]
ldp x29, x30, [sp, #80]
ldp d8, d9, [sp, #96]
ldp d10, d11, [sp, #112]
ldp d12, d13, [sp, #128]
ldp d14, d15, [sp, #144]
mov x0, x10
add sp, sp, #176
ret
SV
};
}
asm {
#string T
.global _fib_tramp
_fib_tramp:
mov x0, x19
bl _fib_body
brk #0
T,
};
fib_tramp :: () extern;
fib_body :: (self: *Fiber) export "fib_body" {
self.mismatches = scribble_verify(@self.ctx, self.peer, self.base);
swap_context(@self.ctx, self.next);
}
STACK :: 131072;
boot :: (f: *Fiber) {
base : *void = context.allocator.alloc_bytes(STACK);
top : u64 = (xx base) + STACK;
top = top - (top % 16);
f.ctx.regs[0] = xx f; // x19 = self
f.ctx.regs[10] = 0; // fp
f.ctx.regs[11] = xx fib_tramp; // lr → trampoline
f.ctx.regs[12] = top; // sp
// Zero the FP save slots so the first switch-to loads 0 (not garbage) into
// d8-d15 — removes the first-entry foot-gun (adversarial-review note).
i := 13;
while i < 21 { f.ctx.regs[i] = 0; i = i + 1; }
f.mismatches = -1;
}
main :: () -> i64 {
main_ctx : FiberCtx = ---;
a : Fiber = ---; a.base = 0x5000;
b : Fiber = ---; b.base = 0x6000;
a.peer = @b.ctx; a.next = @b.ctx; // A yields to B, then hands B the baton
b.peer = @a.ctx; b.next = @main_ctx; // B yields to A, then returns to main
boot(@a); boot(@b);
swap_context(@main_ctx, @a.ctx);
print("A mismatches: {}\n", a.mismatches); // 0 — every callee-saved survived
print("B mismatches: {}\n", b.mismatches); // 0
return 0;
}

View File

@@ -0,0 +1,104 @@
// Stream B1 (fibers) B1.3b — fiber stacks are `mmap`'d with a PROT_NONE GUARD
// PAGE at their low end (§8.1.1: a fixed stack without a guard silently
// corrupts neighbors on overflow — the guard turns overflow into an immediate,
// loud fault instead). The stack grows DOWN, so the guard sits at the lowest
// address; when a fiber's SP descends past the usable region it hits the
// unwritable guard and faults at the boundary.
//
// This is the positive integration: a fiber bootstrapped on a guarded `mmap`
// stack runs real recursion + yields correctly, and the guard syscall is
// asserted to have succeeded (`mprotect` → 0). The guard FIRING on overflow is
// validated separately (a fiber recursing past its 128KB stack faults with
// "Bus error" at the guard page, address = region+GUARD) — a deterministic
// corpus assertion for it is omitted because a deliberate stack-overflow crash
// is host/runtime-fragile (the sx crash handler turns the fault into SIGABRT,
// and the fault address varies run-to-run).
//
// aarch64-macos-pinned: the `mmap` flag constants (MAP_ANON = 0x1000) and the
// 16 KB page size are Apple-specific; the asm switch is per-arch. Runs
// end-to-end here, ir-only on a mismatch.
#import "modules/std.sx";
mmap :: (addr: *void, len: i64, prot: i32, flags: i32, fd: i32, off: i64) -> *void extern libc "mmap";
mprotect :: (addr: *void, len: i64, prot: i32) -> i32 extern libc "mprotect";
PROT_NONE :: 0;
PROT_RW :: 3; // PROT_READ | PROT_WRITE
MAP_AP :: 0x1002; // macOS MAP_PRIVATE (0x2) | MAP_ANON (0x1000)
GUARD :: 16384; // one 16 KB page (aarch64-macOS)
STACK :: 131072; // 128 KB usable
FiberCtx :: struct { regs: [13]u64; }
Fiber :: struct { ctx: FiberCtx; finish: *FiberCtx; out: i64; guard_ok: i64; }
swap_context :: (from: *FiberCtx, to: *FiberCtx) abi(.naked) {
asm volatile {
#string ASM
stp x19, x20, [x0, #0]
stp x21, x22, [x0, #16]
stp x23, x24, [x0, #32]
stp x25, x26, [x0, #48]
stp x27, x28, [x0, #64]
stp x29, x30, [x0, #80]
mov x9, sp
str x9, [x0, #96]
ldp x19, x20, [x1, #0]
ldp x21, x22, [x1, #16]
ldp x23, x24, [x1, #32]
ldp x25, x26, [x1, #48]
ldp x27, x28, [x1, #64]
ldp x29, x30, [x1, #80]
ldr x9, [x1, #96]
mov sp, x9
ret
ASM
};
}
asm {
#string T
.global _fib_tramp
_fib_tramp:
mov x0, x19
bl _fib_body
brk #0
T,
};
fib_tramp :: () extern;
// Real recursion → genuine stack usage on the guarded stack (well within 128KB).
sum_to :: (n: i64) -> i64 {
if n == 0 { return 0; }
return n + sum_to(n - 1);
}
fib_body :: (self: *Fiber) export "fib_body" {
self.out = sum_to(200); // 200*201/2 = 20100
swap_context(@self.ctx, self.finish);
}
// mmap a [guard | usable-stack] region and mprotect the low guard page
// PROT_NONE. Returns the 16-aligned stack top; reports guard-syscall success.
guarded_stack :: (f: *Fiber, size: i64) -> u64 {
region : *void = mmap(null, GUARD + size, PROT_RW, MAP_AP, -1, 0);
if (xx region) == (xx (0 - 1)) { f.guard_ok = 0; return 0; }
f.guard_ok = 0;
if mprotect(region, GUARD, PROT_NONE) == 0 { f.guard_ok = 1; }
usable : u64 = (xx region) + GUARD;
top : u64 = usable + size;
return top - (top % 16);
}
main :: () -> i64 {
main_ctx : FiberCtx = ---;
f : Fiber = ---; f.finish = @main_ctx; f.out = -1; f.guard_ok = 0;
top := guarded_stack(@f, STACK);
f.ctx.regs[0] = xx @f;
f.ctx.regs[10] = 0;
f.ctx.regs[11] = xx fib_tramp;
f.ctx.regs[12] = top;
swap_context(@main_ctx, @f.ctx);
print("guard armed: {}\n", f.guard_ok); // 1 — mprotect(PROT_NONE) succeeded
print("sum: {}\n", f.out); // 20100 — fiber ran on the guarded stack
return 0;
}

View File

@@ -0,0 +1,341 @@
// Win64 fiber context-switch STRESS GATE (x86_64-windows-gnu).
// Mirrors the aarch64 §10.7 gate (examples/1808) for the Win64 ABI:
// - callee-saved: rbx,rbp,rdi,rsi,r12-r15 (8 GP) + rsp + xmm6-xmm15 (10 XMM,
// 128-bit — Win64 HAS callee-saved XMM, unlike SysV/aarch64).
// - args rcx,rdx,r8,r9; result rax; 32-byte shadow + 16-align at each call;
// COFF symbols (no leading underscore); return address rides the stack.
// 2-fiber MUTUAL scribble: A (base 0x5000) and B (base 0x6000) each load
// distinct sentinels into EVERY callee-saved reg and yield to each other, so a
// sentinel survives only if swap_good saved+restored it (each fiber physically
// clobbers the other's registers while it is suspended). Self-validating by
// construction; the corruption-DETECTION of this exact scribble/verify logic
// was negative-controlled on aarch64 (examples/1808). Prints three fields:
// "<gp_mismatch> <xmm_mismatch> <P|F>" — expected "0 0 P".
//
// VALIDATED: built with `--target x86_64-windows-gnu --self-contained` and run
// on a Windows 7 x64 VM (UTM) -> printed "0 0 P" (every GP + XMM callee-saved
// survived). Pinned x86_64-windows-gnu: ir-only on a non-Windows host (the .ir
// locks the Win64-ABI lowering); runs end-to-end on a Windows x86_64 runner.
// Adversarially reviewed (no critical/minor bugs) before the VM run.
kernel32 :: #library "kernel32";
GetStdHandle :: (n: u32) -> *void extern;
WriteFile :: (file: *void, buf: *u8, n: u32, written: *u32, overlapped: *void) -> i32 extern;
ExitProcess :: (code: u32) -> void extern;
// 30 u64: GP rbx@0 rbp@8 rdi@16 rsi@24 r12@32 r13@40 r14@48 r15@56, rsp@64,
// pad@72, xmm6@80 xmm7@96 ... xmm15@224 (movups, 16 bytes each).
FiberCtx :: struct { regs: [30]u64; }
Fiber :: struct {
ctx: FiberCtx;
peer: *FiberCtx;
next: *FiberCtx;
base: u64;
gp: u64;
xmm: u64;
}
swap_good :: (from: *FiberCtx, to: *FiberCtx) abi(.naked) export "swap_good" {
asm volatile {
#string A
movq %rbx, 0(%rcx)
movq %rbp, 8(%rcx)
movq %rdi, 16(%rcx)
movq %rsi, 24(%rcx)
movq %r12, 32(%rcx)
movq %r13, 40(%rcx)
movq %r14, 48(%rcx)
movq %r15, 56(%rcx)
movq %rsp, 64(%rcx)
movups %xmm6, 80(%rcx)
movups %xmm7, 96(%rcx)
movups %xmm8, 112(%rcx)
movups %xmm9, 128(%rcx)
movups %xmm10, 144(%rcx)
movups %xmm11, 160(%rcx)
movups %xmm12, 176(%rcx)
movups %xmm13, 192(%rcx)
movups %xmm14, 208(%rcx)
movups %xmm15, 224(%rcx)
movq 0(%rdx), %rbx
movq 8(%rdx), %rbp
movq 16(%rdx), %rdi
movq 24(%rdx), %rsi
movq 32(%rdx), %r12
movq 40(%rdx), %r13
movq 48(%rdx), %r14
movq 56(%rdx), %r15
movups 80(%rdx), %xmm6
movups 96(%rdx), %xmm7
movups 112(%rdx), %xmm8
movups 128(%rdx), %xmm9
movups 144(%rdx), %xmm10
movups 160(%rdx), %xmm11
movups 176(%rdx), %xmm12
movups 192(%rdx), %xmm13
movups 208(%rdx), %xmm14
movups 224(%rdx), %xmm15
movq 64(%rdx), %rsp
ret
A
};
}
// scribble_verify(self_ctx=rcx, peer=rdx, base=r8, swapfn=r9) -> rax packed
// (xmm_mismatch << 16) | gp_mismatch. Naked: 264-byte frame saves the caller's
// callee-saved (it scribbles them) + base; yields via `call *%r9`; verifies on
// resume; restores the caller regs; returns.
scribble_verify :: (self_ctx: *FiberCtx, peer: *FiberCtx, base: u64) -> u64 abi(.naked) export "scribble_verify" {
asm volatile {
#string SV
subq $264, %rsp
movq %r8, 32(%rsp)
movq %rbx, 40(%rsp)
movq %rbp, 48(%rsp)
movq %rdi, 56(%rsp)
movq %rsi, 64(%rsp)
movq %r12, 72(%rsp)
movq %r13, 80(%rsp)
movq %r14, 88(%rsp)
movq %r15, 96(%rsp)
movups %xmm6, 104(%rsp)
movups %xmm7, 120(%rsp)
movups %xmm8, 136(%rsp)
movups %xmm9, 152(%rsp)
movups %xmm10, 168(%rsp)
movups %xmm11, 184(%rsp)
movups %xmm12, 200(%rsp)
movups %xmm13, 216(%rsp)
movups %xmm14, 232(%rsp)
movups %xmm15, 248(%rsp)
leaq 1(%r8), %rbx
leaq 2(%r8), %rbp
leaq 3(%r8), %rdi
leaq 4(%r8), %rsi
leaq 5(%r8), %r12
leaq 6(%r8), %r13
leaq 7(%r8), %r14
leaq 8(%r8), %r15
leaq 9(%r8), %rax
movq %rax, %xmm6
leaq 10(%r8), %rax
movq %rax, %xmm7
leaq 11(%r8), %rax
movq %rax, %xmm8
leaq 12(%r8), %rax
movq %rax, %xmm9
leaq 13(%r8), %rax
movq %rax, %xmm10
leaq 14(%r8), %rax
movq %rax, %xmm11
leaq 15(%r8), %rax
movq %rax, %xmm12
leaq 16(%r8), %rax
movq %rax, %xmm13
leaq 17(%r8), %rax
movq %rax, %xmm14
leaq 18(%r8), %rax
movq %rax, %xmm15
call swap_good
movq 32(%rsp), %r8
xorq %r10, %r10
xorq %r11, %r11
leaq 1(%r8), %rax
cmpq %rax, %rbx
setne %dl
movzbq %dl, %rdx
addq %rdx, %r10
leaq 2(%r8), %rax
cmpq %rax, %rbp
setne %dl
movzbq %dl, %rdx
addq %rdx, %r10
leaq 3(%r8), %rax
cmpq %rax, %rdi
setne %dl
movzbq %dl, %rdx
addq %rdx, %r10
leaq 4(%r8), %rax
cmpq %rax, %rsi
setne %dl
movzbq %dl, %rdx
addq %rdx, %r10
leaq 5(%r8), %rax
cmpq %rax, %r12
setne %dl
movzbq %dl, %rdx
addq %rdx, %r10
leaq 6(%r8), %rax
cmpq %rax, %r13
setne %dl
movzbq %dl, %rdx
addq %rdx, %r10
leaq 7(%r8), %rax
cmpq %rax, %r14
setne %dl
movzbq %dl, %rdx
addq %rdx, %r10
leaq 8(%r8), %rax
cmpq %rax, %r15
setne %dl
movzbq %dl, %rdx
addq %rdx, %r10
leaq 9(%r8), %rax
movq %xmm6, %rcx
cmpq %rax, %rcx
setne %dl
movzbq %dl, %rdx
addq %rdx, %r11
leaq 10(%r8), %rax
movq %xmm7, %rcx
cmpq %rax, %rcx
setne %dl
movzbq %dl, %rdx
addq %rdx, %r11
leaq 11(%r8), %rax
movq %xmm8, %rcx
cmpq %rax, %rcx
setne %dl
movzbq %dl, %rdx
addq %rdx, %r11
leaq 12(%r8), %rax
movq %xmm9, %rcx
cmpq %rax, %rcx
setne %dl
movzbq %dl, %rdx
addq %rdx, %r11
leaq 13(%r8), %rax
movq %xmm10, %rcx
cmpq %rax, %rcx
setne %dl
movzbq %dl, %rdx
addq %rdx, %r11
leaq 14(%r8), %rax
movq %xmm11, %rcx
cmpq %rax, %rcx
setne %dl
movzbq %dl, %rdx
addq %rdx, %r11
leaq 15(%r8), %rax
movq %xmm12, %rcx
cmpq %rax, %rcx
setne %dl
movzbq %dl, %rdx
addq %rdx, %r11
leaq 16(%r8), %rax
movq %xmm13, %rcx
cmpq %rax, %rcx
setne %dl
movzbq %dl, %rdx
addq %rdx, %r11
leaq 17(%r8), %rax
movq %xmm14, %rcx
cmpq %rax, %rcx
setne %dl
movzbq %dl, %rdx
addq %rdx, %r11
leaq 18(%r8), %rax
movq %xmm15, %rcx
cmpq %rax, %rcx
setne %dl
movzbq %dl, %rdx
addq %rdx, %r11
movq %r11, %rax
shlq $16, %rax
orq %r10, %rax
movq 40(%rsp), %rbx
movq 48(%rsp), %rbp
movq 56(%rsp), %rdi
movq 64(%rsp), %rsi
movq 72(%rsp), %r12
movq 80(%rsp), %r13
movq 88(%rsp), %r14
movq 96(%rsp), %r15
movups 104(%rsp), %xmm6
movups 120(%rsp), %xmm7
movups 136(%rsp), %xmm8
movups 152(%rsp), %xmm9
movups 168(%rsp), %xmm10
movups 184(%rsp), %xmm11
movups 200(%rsp), %xmm12
movups 216(%rsp), %xmm13
movups 232(%rsp), %xmm14
movups 248(%rsp), %xmm15
addq $264, %rsp
ret
SV
};
}
asm {
#string T
.global fib_tramp
fib_tramp:
movq %rbx, %rcx
subq $32, %rsp
call fib_body
ud2
T,
};
fib_tramp :: () extern;
fib_body :: (self: *Fiber) export "fib_body" {
packed := scribble_verify(@self.ctx, self.peer, self.base);
self.gp = packed & 0xffff;
self.xmm = (packed >> 16) & 0xffff;
swap_good(@self.ctx, self.next);
}
STACK :: 131072;
g_out : *void = null;
boot :: (f: *Fiber) {
raw : *void = VirtualAlloc(null, STACK, 0x3000, 0x4); // MEM_COMMIT|RESERVE, PAGE_READWRITE
top : u64 = (xx raw) + STACK;
top = top - (top % 16);
slot : *u64 = xx (top - 8);
slot.* = xx fib_tramp;
i := 0;
while i < 30 { f.ctx.regs[i] = 0; i = i + 1; }
f.ctx.regs[0] = xx f; // rbx = self
f.ctx.regs[8] = top - 8; // rsp
f.gp = 0; f.xmm = 0;
}
VirtualAlloc :: (addr: *void, size: i64, typ: u32, protect: u32) -> *void extern;
run :: () -> u64 {
main_ctx : FiberCtx = ---;
a : Fiber = ---; a.base = 0x5000;
b : Fiber = ---; b.base = 0x6000;
a.peer = @b.ctx; a.next = @b.ctx;
b.peer = @a.ctx; b.next = @main_ctx;
boot(@a); boot(@b);
swap_good(@main_ctx, @a.ctx);
// pack both fibers' counts: ((a.xmm+b.xmm) << 16) | (a.gp+b.gp)
return (((a.xmm + b.xmm) << 16) | (a.gp + b.gp));
}
emit_num :: (v: u64) {
if v == 0 { z : [1]u8 = .[48]; w : u32 = 0; WriteFile(g_out, @z[0], 1, @w, null); return; }
buf : [20]u8 = ---;
i : i64 = 20;
vv := v;
while vv > 0 { i = i - 1; buf[i] = xx (48 + (vv % 10)); vv = vv / 10; }
w : u32 = 0;
WriteFile(g_out, @buf[i], xx (20 - i), @w, null);
}
emit_b :: (c: u8) { b : [1]u8 = ---; b[0] = c; w : u32 = 0; WriteFile(g_out, @b[0], 1, @w, null); }
main :: () {
g_out = GetStdHandle(0xFFFFFFF5);
good := run();
gg := good & 0xffff; gx := (good >> 16) & 0xffff;
emit_num(gg); emit_b(32); emit_num(gx); emit_b(32);
pass := 0;
if gg == 0 { if gx == 0 { pass = 1; } }
if pass == 1 { emit_b(80); } else { emit_b(70); } // 'P' / 'F'
emit_b(10);
ExitProcess(0);
}

View File

@@ -0,0 +1 @@
{ "target": "macos" }

View File

@@ -0,0 +1 @@
42

View File

@@ -0,0 +1,15 @@
; Function Attrs: naked noinline nounwind
define internal i64 @answer() #0 {
entry:
call void asm sideeffect " mov x0, #42\0A ret\0A", ""()
unreachable
}
; Function Attrs: nounwind
define i32 @main() #1 {
entry:
%call = call i64 @answer()
%ca.tr = trunc i64 %call to i32
ret i32 %ca.tr
}

View File

@@ -0,0 +1 @@
{ "target": "macos" }

View File

@@ -0,0 +1 @@
42

View File

@@ -0,0 +1,15 @@
; Function Attrs: nounwind
define i32 @main() #0 {
entry:
%call = call i64 @answer__i64()
%ca.tr = trunc i64 %call to i32
ret i32 %ca.tr
}
; Function Attrs: naked noinline nounwind
define internal i64 @answer__i64() #1 {
entry:
call void asm sideeffect " mov x0, #42\0A ret\0A", ""()
unreachable
}

View File

@@ -0,0 +1 @@
{ "target": "x86_64-linux" }

View File

@@ -0,0 +1,15 @@
; Function Attrs: naked noinline nounwind
define internal i64 @answer() #0 {
entry:
call void asm sideeffect " movl $$42, %eax\0A ret\0A", ""()
unreachable
}
; Function Attrs: nounwind
define i32 @main() #1 {
entry:
%call = call i64 @answer()
%ca.tr = trunc i64 %call to i32
ret i32 %ca.tr
}

View File

@@ -0,0 +1 @@
{ "target": "macos" }

View File

@@ -0,0 +1,15 @@
; Function Attrs: naked noinline nounwind
define internal i64 @add(i64 %0, i64 %1) #0 {
entry:
call void asm sideeffect " add x0, x0, x1\0A ret\0A", ""()
unreachable
}
; Function Attrs: nounwind
define i32 @main() #1 {
entry:
%call = call i64 @add(i64 40, i64 2)
%ca.tr = trunc i64 %call to i32
ret i32 %ca.tr
}

View File

@@ -0,0 +1,2 @@
fiber root: 42
ambient after: 99

View File

@@ -0,0 +1,4 @@
sum: 42
double: 42
nullary: 42
clock ok

View File

@@ -0,0 +1 @@
0

View File

@@ -0,0 +1,2 @@
ok: 7
canceled: -99

View File

@@ -0,0 +1 @@
{ "target": "macos" }

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,4 @@
rounds: 6
canary fails: 0
frames verified: 64
depth fails: 0

View File

@@ -0,0 +1 @@
{ "target": "macos" }

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,2 @@
A mismatches: 0
B mismatches: 0

View File

@@ -0,0 +1 @@
{ "target": "macos" }

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,2 @@
guard armed: 1
sum: 20100

View File

@@ -0,0 +1 @@
{ "target": "x86_64-windows-gnu" }

View File

@@ -0,0 +1,326 @@
module asm ".global fib_tramp"
module asm "fib_tramp:"
module asm " movq %rbx, %rcx"
module asm " subq $32, %rsp"
module asm " call fib_body"
module asm " ud2"
@g_out = internal global ptr null
; Function Attrs: nounwind
declare ptr @GetStdHandle(i32) #0
; Function Attrs: nounwind
declare i32 @WriteFile(ptr, ptr, i32, ptr, ptr) #0
; Function Attrs: nounwind
declare void @ExitProcess(i32) #0
; Function Attrs: naked noinline nounwind
define void @swap_good(ptr %0, ptr %1) #1 {
entry:
call void asm sideeffect " movq %rbx, 0(%rcx)\0A movq %rbp, 8(%rcx)\0A movq %rdi, 16(%rcx)\0A movq %rsi, 24(%rcx)\0A movq %rN, 32(%rcx)\0A movq %rN, 40(%rcx)\0A movq %rN, 48(%rcx)\0A movq %rN, 56(%rcx)\0A movq %rsp, 64(%rcx)\0A movups %xmmN, 80(%rcx)\0A movups %xmmN, 96(%rcx)\0A movups %xmmN, 112(%rcx)\0A movups %xmmN, 128(%rcx)\0A movups %xmmN, 144(%rcx)\0A movups %xmmN, 160(%rcx)\0A movups %xmmN, 176(%rcx)\0A movups %xmmN, 192(%rcx)\0A movups %xmmN, 208(%rcx)\0A movups %xmmN, 224(%rcx)\0A movq 0(%rdx), %rbx\0A movq 8(%rdx), %rbp\0A movq 16(%rdx), %rdi\0A movq 24(%rdx), %rsi\0A movq 32(%rdx), %rN\0A movq 40(%rdx), %rN\0A movq 48(%rdx), %rN\0A movq 56(%rdx), %rN\0A movups 80(%rdx), %xmmN\0A movups 96(%rdx), %xmmN\0A movups 112(%rdx), %xmmN\0A movups 128(%rdx), %xmmN\0A movups 144(%rdx), %xmmN\0A movups 160(%rdx), %xmmN\0A movups 176(%rdx), %xmmN\0A movups 192(%rdx), %xmmN\0A movups 208(%rdx), %xmmN\0A movups 224(%rdx), %xmmN\0A movq 64(%rdx), %rsp\0A ret\0A", ""()
unreachable
}
; Function Attrs: naked noinline nounwind
define i64 @scribble_verify(ptr %0, ptr %1, i64 %2) #1 {
entry:
call void asm sideeffect " subq $$264, %rsp\0A movq %rN, 32(%rsp)\0A movq %rbx, 40(%rsp)\0A movq %rbp, 48(%rsp)\0A movq %rdi, 56(%rsp)\0A movq %rsi, 64(%rsp)\0A movq %rN, 72(%rsp)\0A movq %rN, 80(%rsp)\0A movq %rN, 88(%rsp)\0A movq %rN, 96(%rsp)\0A movups %xmmN, 104(%rsp)\0A movups %xmmN, 120(%rsp)\0A movups %xmmN, 136(%rsp)\0A movups %xmmN, 152(%rsp)\0A movups %xmmN, 168(%rsp)\0A movups %xmmN, 184(%rsp)\0A movups %xmmN, 200(%rsp)\0A movups %xmmN, 216(%rsp)\0A movups %xmmN, 232(%rsp)\0A movups %xmmN, 248(%rsp)\0A leaq 1(%rN), %rbx\0A leaq 2(%rN), %rbp\0A leaq 3(%rN), %rdi\0A leaq 4(%rN), %rsi\0A leaq 5(%rN), %rN\0A leaq 6(%rN), %rN\0A leaq 7(%rN), %rN\0A leaq 8(%rN), %rN\0A leaq 9(%rN), %rax\0A movq %rax, %xmmN\0A leaq 10(%rN), %rax\0A movq %rax, %xmmN\0A leaq 11(%rN), %rax\0A movq %rax, %xmmN\0A leaq 12(%rN), %rax\0A movq %rax, %xmmN\0A leaq 13(%rN), %rax\0A movq %rax, %xmmN\0A leaq 14(%rN), %rax\0A movq %rax, %xmmN\0A leaq 15(%rN), %rax\0A movq %rax, %xmmN\0A leaq 16(%rN), %rax\0A movq %rax, %xmmN\0A leaq 17(%rN), %rax\0A movq %rax, %xmmN\0A leaq 18(%rN), %rax\0A movq %rax, %xmmN\0A call swap_good\0A movq 32(%rsp), %rN\0A xorq %rN, %rN\0A xorq %rN, %rN\0A leaq 1(%rN), %rax\0A cmpq %rax, %rbx\0A setne %dl\0A movzbq %dl, %rdx\0A addq %rdx, %rN\0A leaq 2(%rN), %rax\0A cmpq %rax, %rbp\0A setne %dl\0A movzbq %dl, %rdx\0A addq %rdx, %rN\0A leaq 3(%rN), %rax\0A cmpq %rax, %rdi\0A setne %dl\0A movzbq %dl, %rdx\0A addq %rdx, %rN\0A leaq 4(%rN), %rax\0A cmpq %rax, %rsi\0A setne %dl\0A movzbq %dl, %rdx\0A addq %rdx, %rN\0A leaq 5(%rN), %rax\0A cmpq %rax, %rN\0A setne %dl\0A movzbq %dl, %rdx\0A addq %rdx, %rN\0A leaq 6(%rN), %rax\0A cmpq %rax, %rN\0A setne %dl\0A movzbq %dl, %rdx\0A addq %rdx, %rN\0A leaq 7(%rN), %rax\0A cmpq %rax, %rN\0A setne %dl\0A movzbq %dl, %rdx\0A addq %rdx, %rN\0A leaq 8(%rN), %rax\0A cmpq %rax, %rN\0A setne %dl\0A movzbq %dl, %rdx\0A addq %rdx, %rN\0A leaq 9(%rN), %rax\0A movq %xmmN, %rcx\0A cmpq %rax, %rcx\0A setne %dl\0A movzbq %dl, %rdx\0A addq %rdx, %rN\0A leaq 10(%rN), %rax\0A movq %xmmN, %rcx\0A cmpq %rax, %rcx\0A setne %dl\0A movzbq %dl, %rdx\0A addq %rdx, %rN\0A leaq 11(%rN), %rax\0A movq %xmmN, %rcx\0A cmpq %rax, %rcx\0A setne %dl\0A movzbq %dl, %rdx\0A addq %rdx, %rN\0A leaq 12(%rN), %rax\0A movq %xmmN, %rcx\0A cmpq %rax, %rcx\0A setne %dl\0A movzbq %dl, %rdx\0A addq %rdx, %rN\0A leaq 13(%rN), %rax\0A movq %xmmN, %rcx\0A cmpq %rax, %rcx\0A setne %dl\0A movzbq %dl, %rdx\0A addq %rdx, %rN\0A leaq 14(%rN), %rax\0A movq %xmmN, %rcx\0A cmpq %rax, %rcx\0A setne %dl\0A movzbq %dl, %rdx\0A addq %rdx, %rN\0A leaq 15(%rN), %rax\0A movq %xmmN, %rcx\0A cmpq %rax, %rcx\0A setne %dl\0A movzbq %dl, %rdx\0A addq %rdx, %rN\0A leaq 16(%rN), %rax\0A movq %xmmN, %rcx\0A cmpq %rax, %rcx\0A setne %dl\0A movzbq %dl, %rdx\0A addq %rdx, %rN\0A leaq 17(%rN), %rax\0A movq %xmmN, %rcx\0A cmpq %rax, %rcx\0A setne %dl\0A movzbq %dl, %rdx\0A addq %rdx, %rN\0A leaq 18(%rN), %rax\0A movq %xmmN, %rcx\0A cmpq %rax, %rcx\0A setne %dl\0A movzbq %dl, %rdx\0A addq %rdx, %rN\0A movq %rN, %rax\0A shlq $$16, %rax\0A orq %rN, %rax\0A movq 40(%rsp), %rbx\0A movq 48(%rsp), %rbp\0A movq 56(%rsp), %rdi\0A movq 64(%rsp), %rsi\0A movq 72(%rsp), %rN\0A movq 80(%rsp), %rN\0A movq 88(%rsp), %rN\0A movq 96(%rsp), %rN\0A movups 104(%rsp), %xmmN\0A movups 120(%rsp), %xmmN\0A movups 136(%rsp), %xmmN\0A movups 152(%rsp), %xmmN\0A movups 168(%rsp), %xmmN\0A movups 184(%rsp), %xmmN\0A movups 200(%rsp), %xmmN\0A movups 216(%rsp), %xmmN\0A movups 232(%rsp), %xmmN\0A movups 248(%rsp), %xmmN\0A addq $$264, %rsp\0A ret\0A", ""()
unreachable
}
; Function Attrs: nounwind
declare void @fib_tramp() #0
; Function Attrs: nounwind
define void @fib_body(ptr %0) #0 {
entry:
%alloca = alloca ptr, align 8
store ptr %0, ptr %alloca, align 8
%load = load ptr, ptr %alloca, align 8
%gep = getelementptr inbounds nuw { { [30 x i64] }, ptr, ptr, i64, i64, i64 }, ptr %load, i32 0, i32 0
%loadN = load ptr, ptr %alloca, align 8
%loadN = load { { [30 x i64] }, ptr, ptr, i64, i64, i64 }, ptr %loadN, align 8
%sg = extractvalue { { [30 x i64] }, ptr, ptr, i64, i64, i64 } %loadN, 1
%loadN = load ptr, ptr %alloca, align 8
%loadN = load { { [30 x i64] }, ptr, ptr, i64, i64, i64 }, ptr %loadN, align 8
%sgN = extractvalue { { [30 x i64] }, ptr, ptr, i64, i64, i64 } %loadN, 3
%call = call i64 @scribble_verify(ptr %gep, ptr %sg, i64 %sgN)
%allocaN = alloca i64, align 8
store i64 %call, ptr %allocaN, align 8
%loadN = load i64, ptr %allocaN, align 8
%and = and i64 %loadN, 65535
%loadN = load ptr, ptr %alloca, align 8
%gepN = getelementptr inbounds nuw { { [30 x i64] }, ptr, ptr, i64, i64, i64 }, ptr %loadN, i32 0, i32 4
store i64 %and, ptr %gepN, align 8
%loadN = load i64, ptr %allocaN, align 8
%lshr = lshr i64 %loadN, 16
%andN = and i64 %lshr, 65535
%loadN = load ptr, ptr %alloca, align 8
%gepN = getelementptr inbounds nuw { { [30 x i64] }, ptr, ptr, i64, i64, i64 }, ptr %loadN, i32 0, i32 5
store i64 %andN, ptr %gepN, align 8
%loadN = load ptr, ptr %alloca, align 8
%gepN = getelementptr inbounds nuw { { [30 x i64] }, ptr, ptr, i64, i64, i64 }, ptr %loadN, i32 0, i32 0
%loadN = load ptr, ptr %alloca, align 8
%loadN = load { { [30 x i64] }, ptr, ptr, i64, i64, i64 }, ptr %loadN, align 8
%sgN = extractvalue { { [30 x i64] }, ptr, ptr, i64, i64, i64 } %loadN, 2
call void @swap_good(ptr %gepN, ptr %sgN)
ret void
}
; Function Attrs: nounwind
define internal void @boot(ptr %0) #0 {
entry:
%alloca = alloca ptr, align 8
store ptr %0, ptr %alloca, align 8
%allocaN = alloca ptr, align 8
%call = call ptr @VirtualAlloc(ptr null, i64 131072, i32 12288, i32 4)
store ptr %call, ptr %allocaN, align 8
%allocaN = alloca i64, align 8
%load = load ptr, ptr %allocaN, align 8
%pti = ptrtoint ptr %load to i64
%add = add i64 %pti, 131072
store i64 %add, ptr %allocaN, align 8
%loadN = load i64, ptr %allocaN, align 8
%loadN = load i64, ptr %allocaN, align 8
%urem = urem i64 %loadN, 16
%sub = sub i64 %loadN, %urem
store i64 %sub, ptr %allocaN, align 8
%allocaN = alloca ptr, align 8
%loadN = load i64, ptr %allocaN, align 8
%subN = sub i64 %loadN, 8
%itp = inttoptr i64 %subN to ptr
store ptr %itp, ptr %allocaN, align 8
%loadN = load ptr, ptr %allocaN, align 8
store ptr @fib_tramp, ptr %loadN, align 8
%allocaN = alloca i64, align 8
store i64 0, ptr %allocaN, align 8
br label %while.hdr.0
while.hdr.0: ; preds = %while.body.1, %entry
%loadN = load i64, ptr %allocaN, align 8
%icmp = icmp slt i64 %loadN, 30
br i1 %icmp, label %while.body.1, label %while.exit.2
while.body.1: ; preds = %while.hdr.0
%loadN = load i64, ptr %allocaN, align 8
%loadN = load ptr, ptr %alloca, align 8
%gep = getelementptr inbounds nuw { { [30 x i64] }, ptr, ptr, i64, i64, i64 }, ptr %loadN, i32 0, i32 0
%gepN = getelementptr inbounds nuw { [30 x i64] }, ptr %gep, i32 0, i32 0
%igp.ptr = getelementptr i64, ptr %gepN, i64 %loadN
store i64 0, ptr %igp.ptr, align 8
%loadN = load i64, ptr %allocaN, align 8
%addN = add i64 %loadN, 1
store i64 %addN, ptr %allocaN, align 8
br label %while.hdr.0
while.exit.2: ; preds = %while.hdr.0
%loadN = load ptr, ptr %alloca, align 8
%ptiN = ptrtoint ptr %loadN to i64
%loadN = load ptr, ptr %alloca, align 8
%gepN = getelementptr inbounds nuw { { [30 x i64] }, ptr, ptr, i64, i64, i64 }, ptr %loadN, i32 0, i32 0
%gepN = getelementptr inbounds nuw { [30 x i64] }, ptr %gepN, i32 0, i32 0
%igp.ptr21 = getelementptr i64, ptr %gepN, i64 0
store i64 %ptiN, ptr %igp.ptr21, align 8
%loadN = load i64, ptr %allocaN, align 8
%subN = sub i64 %loadN, 8
%loadN = load ptr, ptr %alloca, align 8
%gepN = getelementptr inbounds nuw { { [30 x i64] }, ptr, ptr, i64, i64, i64 }, ptr %loadN, i32 0, i32 0
%gepN = getelementptr inbounds nuw { [30 x i64] }, ptr %gepN, i32 0, i32 0
%igp.ptr27 = getelementptr i64, ptr %gepN, i64 8
store i64 %subN, ptr %igp.ptr27, align 8
%loadN = load ptr, ptr %alloca, align 8
%gepN = getelementptr inbounds nuw { { [30 x i64] }, ptr, ptr, i64, i64, i64 }, ptr %loadN, i32 0, i32 4
store i64 0, ptr %gepN, align 8
%loadN = load ptr, ptr %alloca, align 8
%gepN = getelementptr inbounds nuw { { [30 x i64] }, ptr, ptr, i64, i64, i64 }, ptr %loadN, i32 0, i32 5
store i64 0, ptr %gepN, align 8
ret void
}
; Function Attrs: nounwind
declare ptr @VirtualAlloc(ptr, i64, i32, i32) #0
; Function Attrs: nounwind
define internal i64 @run() #0 {
entry:
%alloca = alloca { [30 x i64] }, align 8
store { [30 x i64] } undef, ptr %alloca, align 8
%allocaN = alloca { { [30 x i64] }, ptr, ptr, i64, i64, i64 }, align 8
store { { [30 x i64] }, ptr, ptr, i64, i64, i64 } undef, ptr %allocaN, align 8
%gep = getelementptr inbounds nuw { { [30 x i64] }, ptr, ptr, i64, i64, i64 }, ptr %allocaN, i32 0, i32 3
store i64 20480, ptr %gep, align 8
%allocaN = alloca { { [30 x i64] }, ptr, ptr, i64, i64, i64 }, align 8
store { { [30 x i64] }, ptr, ptr, i64, i64, i64 } undef, ptr %allocaN, align 8
%gepN = getelementptr inbounds nuw { { [30 x i64] }, ptr, ptr, i64, i64, i64 }, ptr %allocaN, i32 0, i32 3
store i64 24576, ptr %gepN, align 8
%gepN = getelementptr inbounds nuw { { [30 x i64] }, ptr, ptr, i64, i64, i64 }, ptr %allocaN, i32 0, i32 0
%gepN = getelementptr inbounds nuw { { [30 x i64] }, ptr, ptr, i64, i64, i64 }, ptr %allocaN, i32 0, i32 1
store ptr %gepN, ptr %gepN, align 8
%gepN = getelementptr inbounds nuw { { [30 x i64] }, ptr, ptr, i64, i64, i64 }, ptr %allocaN, i32 0, i32 0
%gepN = getelementptr inbounds nuw { { [30 x i64] }, ptr, ptr, i64, i64, i64 }, ptr %allocaN, i32 0, i32 2
store ptr %gepN, ptr %gepN, align 8
%gepN = getelementptr inbounds nuw { { [30 x i64] }, ptr, ptr, i64, i64, i64 }, ptr %allocaN, i32 0, i32 0
%gepN = getelementptr inbounds nuw { { [30 x i64] }, ptr, ptr, i64, i64, i64 }, ptr %allocaN, i32 0, i32 1
store ptr %gepN, ptr %gepN, align 8
%gepN = getelementptr inbounds nuw { { [30 x i64] }, ptr, ptr, i64, i64, i64 }, ptr %allocaN, i32 0, i32 2
store ptr %alloca, ptr %gepN, align 8
call void @boot(ptr %allocaN)
call void @boot(ptr %allocaN)
%gepN = getelementptr inbounds nuw { { [30 x i64] }, ptr, ptr, i64, i64, i64 }, ptr %allocaN, i32 0, i32 0
call void @swap_good(ptr %alloca, ptr %gepN)
%load = load { { [30 x i64] }, ptr, ptr, i64, i64, i64 }, ptr %allocaN, align 8
%sg = extractvalue { { [30 x i64] }, ptr, ptr, i64, i64, i64 } %load, 5
%loadN = load { { [30 x i64] }, ptr, ptr, i64, i64, i64 }, ptr %allocaN, align 8
%sgN = extractvalue { { [30 x i64] }, ptr, ptr, i64, i64, i64 } %loadN, 5
%add = add i64 %sg, %sgN
%shl = shl i64 %add, 16
%loadN = load { { [30 x i64] }, ptr, ptr, i64, i64, i64 }, ptr %allocaN, align 8
%sgN = extractvalue { { [30 x i64] }, ptr, ptr, i64, i64, i64 } %loadN, 4
%loadN = load { { [30 x i64] }, ptr, ptr, i64, i64, i64 }, ptr %allocaN, align 8
%sgN = extractvalue { { [30 x i64] }, ptr, ptr, i64, i64, i64 } %loadN, 4
%addN = add i64 %sgN, %sgN
%or = or i64 %shl, %addN
ret i64 %or
}
; Function Attrs: nounwind
define internal void @emit_num(i64 %0) #0 {
entry:
%alloca = alloca i64, align 8
%allocaN = alloca [1 x i8], align 1
%allocaN = alloca i32, align 4
%allocaN = alloca [20 x i8], align 1
%allocaN = alloca i64, align 8
%allocaN = alloca i64, align 8
%allocaN = alloca i32, align 4
store i64 %0, ptr %alloca, align 8
%load = load i64, ptr %alloca, align 8
%icmp = icmp eq i64 %load, 0
br i1 %icmp, label %if.then.3, label %if.merge.4
if.then.3: ; preds = %entry
store [1 x i8] c"0", ptr %allocaN, align 1
store i32 0, ptr %allocaN, align 4
%gload = load ptr, ptr @g_out, align 8
%igp.ptr = getelementptr i8, ptr %allocaN, i64 0
%call = call i32 @WriteFile(ptr %gload, ptr %igp.ptr, i32 1, ptr %allocaN, ptr null)
ret void
if.merge.4: ; preds = %entry
store i64 20, ptr %allocaN, align 8
%loadN = load i64, ptr %alloca, align 8
store i64 %loadN, ptr %allocaN, align 8
br label %while.hdr.5
while.hdr.5: ; preds = %while.body.6, %if.merge.4
%loadN = load i64, ptr %allocaN, align 8
%icmpN = icmp ugt i64 %loadN, 0
br i1 %icmpN, label %while.body.6, label %while.exit.7
while.body.6: ; preds = %while.hdr.5
%loadN = load i64, ptr %allocaN, align 8
%sub = sub i64 %loadN, 1
store i64 %sub, ptr %allocaN, align 8
%loadN = load i64, ptr %allocaN, align 8
%urem = urem i64 %loadN, 10
%add = add i64 48, %urem
%trunc = trunc i64 %add to i8
%loadN = load i64, ptr %allocaN, align 8
%igp.ptr12 = getelementptr i8, ptr %allocaN, i64 %loadN
store i8 %trunc, ptr %igp.ptr12, align 1
%loadN = load i64, ptr %allocaN, align 8
%udiv = udiv i64 %loadN, 10
store i64 %udiv, ptr %allocaN, align 8
br label %while.hdr.5
while.exit.7: ; preds = %while.hdr.5
store i32 0, ptr %allocaN, align 4
%gloadN = load ptr, ptr @g_out, align 8
%loadN = load i64, ptr %allocaN, align 8
%igp.ptr17 = getelementptr i8, ptr %allocaN, i64 %loadN
%loadN = load i64, ptr %allocaN, align 8
%subN = sub i64 20, %loadN
%truncN = trunc i64 %subN to i32
%callN = call i32 @WriteFile(ptr %gloadN, ptr %igp.ptr17, i32 %truncN, ptr %allocaN, ptr null)
ret void
}
; Function Attrs: nounwind
define internal void @emit_b(i8 %0) #0 {
entry:
%alloca = alloca i8, align 1
store i8 %0, ptr %alloca, align 1
%allocaN = alloca [1 x i8], align 1
%load = load i8, ptr %alloca, align 1
%igp.ptr = getelementptr i8, ptr %allocaN, i64 0
store i8 %load, ptr %igp.ptr, align 1
%allocaN = alloca i32, align 4
store i32 0, ptr %allocaN, align 4
%gload = load ptr, ptr @g_out, align 8
%igp.ptr3 = getelementptr i8, ptr %allocaN, i64 0
%call = call i32 @WriteFile(ptr %gload, ptr %igp.ptr3, i32 1, ptr %allocaN, ptr null)
ret void
}
; Function Attrs: nounwind
define i32 @main() #0 {
entry:
%call = call ptr @GetStdHandle(i32 -11)
store ptr %call, ptr @g_out, align 8
%callN = call i64 @run()
%alloca = alloca i64, align 8
store i64 %callN, ptr %alloca, align 8
%load = load i64, ptr %alloca, align 8
%and = and i64 %load, 65535
%allocaN = alloca i64, align 8
store i64 %and, ptr %allocaN, align 8
%loadN = load i64, ptr %alloca, align 8
%lshr = lshr i64 %loadN, 16
%andN = and i64 %lshr, 65535
%allocaN = alloca i64, align 8
store i64 %andN, ptr %allocaN, align 8
%loadN = load i64, ptr %allocaN, align 8
call void @emit_num(i64 %loadN)
call void @emit_b(i8 32)
%loadN = load i64, ptr %allocaN, align 8
call void @emit_num(i64 %loadN)
call void @emit_b(i8 32)
%allocaN = alloca i64, align 8
store i64 0, ptr %allocaN, align 8
%loadN = load i64, ptr %allocaN, align 8
%icmp = icmp eq i64 %loadN, 0
br i1 %icmp, label %if.then.8, label %if.merge.9
if.then.8: ; preds = %entry
%loadN = load i64, ptr %allocaN, align 8
%icmpN = icmp eq i64 %loadN, 0
br i1 %icmpN, label %if.then.10, label %if.merge.11
if.merge.9: ; preds = %if.merge.11, %entry
%loadN = load i64, ptr %allocaN, align 8
%icmpN = icmp eq i64 %loadN, 1
br i1 %icmpN, label %if.then.12, label %if.else.13
if.then.10: ; preds = %if.then.8
store i64 1, ptr %allocaN, align 8
br label %if.merge.11
if.merge.11: ; preds = %if.then.10, %if.then.8
br label %if.merge.9
if.then.12: ; preds = %if.merge.9
call void @emit_b(i8 80)
br label %if.merge.14
if.else.13: ; preds = %if.merge.9
call void @emit_b(i8 70)
br label %if.merge.14
if.merge.14: ; preds = %if.else.13, %if.then.12
call void @emit_b(i8 10)
call void @ExitProcess(i32 0)
ret i32 0
}