The 18 active rows keep compile + cs==ws byte-id and the tab-anchored ADDQ $N,SP cleanup markers with both polarities (mem-class rows must contain their slot's cleanup; the exactly-48B boundary row must not). The per-row driver build+run legs (incl. the uninit-global zero-box exit 91) move to the r929 run fixture corpus per the residual audit port split; the four buildfail rows were already dead here, owned by the r929_memarg_reject_* fixtures.
456 lines
16 KiB
Plaintext
456 lines
16 KiB
Plaintext
package memarg_test;
|
|
|
|
// Direct-w6c asm-window gate over >48B tagged by-value ARGS (#38b).
|
|
// Port of the retired native carrier test/wcc/929_tagged_memarg_run.c.
|
|
// A tagged arg whose slot exceeds the 6-reg convention (48B) is
|
|
// MEMORY-class: the caller stages the slot on the outgoing stack and
|
|
// the caller-cleanup ADDQ reclaims it after CALL (ref/qbe/amd64/
|
|
// sysv.c:80-85 inmem, :411-426 stack blit). Pre-fix the widened
|
|
// concrete source slipped the guard SILENTLY on both stages AND
|
|
// cs != ww — the exact tab-anchored cleanup markers are the
|
|
// discriminator no fixture can own.
|
|
//
|
|
// Per row: w6c and w6c_ww both compile (rc 0, .s on stdout as the
|
|
// carrier drove them), byte-identical .s (rule 10), then the marker:
|
|
// `needs` rows must contain their `\tADDQ\t$N, SP\n` cleanup line
|
|
// (N = slot bytes; $112/$120 for the two-mem-arg layouts), and the
|
|
// boundary48_register row (40B payload = EXACTLY 48B slot) must NOT
|
|
// contain `\tADDQ\t$48, SP\n` — an off-by-one in tagged_memarg_size
|
|
// flips every 48B-slot call in the tree.
|
|
//
|
|
// The carrier's per-row driver build+run legs move to the r929 run
|
|
// fixture corpus per the residual audit port split (the uninit-global
|
|
// row's zero-box exit 91 rides there); the four buildfail rows were
|
|
// already dead here — their #38b loud-stop diagnostics are owned by
|
|
// the r929_memarg_reject_* fixtures.
|
|
|
|
import os;
|
|
import os.exec;
|
|
import strings;
|
|
import testenv;
|
|
import time;
|
|
|
|
fn fail(label: str, why: str) void = {
|
|
let m: str = strings.concat("memarg FAIL: ", label, " -- ", why,
|
|
"\n");
|
|
os.write(2, m.ptr, m.len: u64);
|
|
assert(false);
|
|
};
|
|
|
|
fn tmo() time.duration = {
|
|
return (180i64 * (time.second: i64)): time.duration;
|
|
};
|
|
|
|
fn emitstdout(td: str, label: str, stage: str, drv: str) str = {
|
|
let av: []str = [drv, strings.concat(td, "/case.ww")];
|
|
let co: testenv.commandout;
|
|
testenv.runcommand(td, td, stage, av, tmo(), &co);
|
|
if (co.termination != exec.termination.EXIT || co.code != 0) {
|
|
fail(label, strings.concat(stage, " compile failed"));
|
|
};
|
|
return co.stdout;
|
|
};
|
|
|
|
// needs/rejects: "" skips that polarity, like the carrier's NULLs.
|
|
fn memargrow(label: str, src: str, needs: str, rejects: str) void = {
|
|
let td: str = testenv.fresh();
|
|
testenv.writefile(strings.concat(td, "/case.ww"),
|
|
strings.concat("package main;\n\n", src));
|
|
let cs: str = emitstdout(td, label, "cstage", testenv.driver("w6c"));
|
|
let ws: str = emitstdout(td, label, "wwstage",
|
|
testenv.driver("w6c_ww"));
|
|
if (!testenv.same(cs, ws)) {
|
|
fail(label, "cstage vs wwstage asm differs");
|
|
};
|
|
if (needs.len != 0 && !testenv.has(cs, needs)) {
|
|
fail(label, "expected mem-cleanup marker missing from .s");
|
|
};
|
|
if (rejects.len != 0 && testenv.has(cs, rejects)) {
|
|
fail(label, strings.concat("boundary row emitted the ",
|
|
"mem-cleanup marker (classifier off-by-one)"));
|
|
};
|
|
testenv.clean(td);
|
|
};
|
|
|
|
// 56B slot: 48B all-scalar payload + tag; m3 is the late word at slot
|
|
// offset +48, dead under the old 6-reg cap.
|
|
fn mem56types() str = {
|
|
return strings.concat(
|
|
"type t_lit = rune;\n",
|
|
"type t_any = void;\n",
|
|
"type t_rep = struct { id: size, origin: size, m0: size,\n",
|
|
" m1: size, m2: size, m3: size };\n",
|
|
"type t_u = (t_lit | t_any | t_rep);\n");
|
|
};
|
|
|
|
// Branched callee reads the tag AND the late payload words per arm,
|
|
// late word FIRST — a single-return callee would mask wrong word
|
|
// routing by coincidence.
|
|
fn mem56probe() str = {
|
|
return strings.concat(
|
|
"fn probe(a: t_u) i32 = {\n",
|
|
" match (a) {\n",
|
|
" case let l: t_lit => {\n",
|
|
" if (l == 'x') { return 1; };\n",
|
|
" return 91;\n",
|
|
" };\n",
|
|
" case t_any => { return 2; };\n",
|
|
" case let r: t_rep => {\n",
|
|
" if (r.m3 != 1234) { return 92; };\n",
|
|
" if (r.m2 != 12) { return 93; };\n",
|
|
" if (r.id != 7) { return 94; };\n",
|
|
" return 3;\n",
|
|
" };\n",
|
|
" };\n",
|
|
" return 99;\n",
|
|
"};\n");
|
|
};
|
|
|
|
fn replit() str = {
|
|
return strings.concat(
|
|
"t_rep { id = 7, origin = 9, m0 = 10, m1 = 11, m2 = 12,\n",
|
|
" m3 = 1234 }");
|
|
};
|
|
|
|
def NEED56: str = "\tADDQ\t$56, SP\n";
|
|
|
|
@test fn mem56rows() void = {
|
|
memargrow("mem56_ident_allvariants", strings.concat(mem56types(),
|
|
mem56probe(),
|
|
"export fn main() i32 = {\n",
|
|
" let a: t_u = ('x': t_lit);\n",
|
|
" if (probe(a) != 1) { return 1; };\n",
|
|
" let av: t_any;\n",
|
|
" let b: t_u = av;\n",
|
|
" if (probe(b) != 2) { return 2; };\n",
|
|
" let r: t_rep = ", replit(), ";\n",
|
|
" let cc: t_u = r;\n",
|
|
" if (probe(cc) != 3) { return 3; };\n",
|
|
" return 0;\n",
|
|
"};\n"), NEED56, "");
|
|
memargrow("mem64_ident_lateword", strings.concat(
|
|
"type t_lit = rune;\n",
|
|
"type t_rep = struct { id: size, origin: size, m0: size,\n",
|
|
" m1: size, m2: size, m3: size, m4: size };\n",
|
|
"type t_u = (t_lit | t_rep);\n",
|
|
"fn probe(a: t_u) i32 = {\n",
|
|
" match (a) {\n",
|
|
" case let l: t_lit => {\n",
|
|
" if (l == 'y') { return 1; };\n",
|
|
" return 91;\n",
|
|
" };\n",
|
|
" case let r: t_rep => {\n",
|
|
" if (r.m4 != 7777) { return 92; };\n",
|
|
" if (r.m3 != 5) { return 93; };\n",
|
|
" return 2;\n",
|
|
" };\n",
|
|
" };\n",
|
|
" return 99;\n",
|
|
"};\n",
|
|
"export fn main() i32 = {\n",
|
|
" let a: t_u = ('y': t_lit);\n",
|
|
" if (probe(a) != 1) { return 1; };\n",
|
|
" let r: t_rep = t_rep { id = 1, origin = 2, m0 = 3,\n",
|
|
" m1 = 4, m2 = 5, m3 = 5, m4 = 7777 };\n",
|
|
" let b: t_u = r;\n",
|
|
" if (probe(b) != 2) { return 2; };\n",
|
|
" return 0;\n",
|
|
"};\n"), "\tADDQ\t$64, SP\n", "");
|
|
memargrow("mem56_widen_concrete", strings.concat(mem56types(),
|
|
mem56probe(),
|
|
"export fn main() i32 = {\n",
|
|
" if (probe('x': t_lit) != 1) { return 1; };\n",
|
|
" let av: t_any;\n",
|
|
" if (probe(av) != 2) { return 2; };\n",
|
|
" let r: t_rep = ", replit(), ";\n",
|
|
" if (probe(r) != 3) { return 3; };\n",
|
|
" return 0;\n",
|
|
"};\n"), NEED56, "");
|
|
memargrow("mem56_subset_remap", strings.concat(mem56types(),
|
|
"type t_sub = (t_lit | t_any);\n",
|
|
mem56probe(),
|
|
"export fn main() i32 = {\n",
|
|
" let s: t_sub = ('x': t_lit);\n",
|
|
" if (probe(s) != 1) { return 1; };\n",
|
|
" let s2: t_sub = (void: t_any);\n",
|
|
" if (probe(s2) != 2) { return 2; };\n",
|
|
" return 0;\n",
|
|
"};\n"), NEED56, "");
|
|
memargrow("mem56_srcshapes", strings.concat(mem56types(),
|
|
"type holder = struct { k: i64, u: t_u };\n",
|
|
mem56probe(),
|
|
"export fn main() i32 = {\n",
|
|
" let arr: [2]t_u = [\n",
|
|
" ('x': t_lit): t_u,\n",
|
|
" ('x': t_lit): t_u,\n",
|
|
" ];\n",
|
|
" if (probe(arr[1]) != 1) { return 1; };\n",
|
|
" let r: t_rep = ", replit(), ";\n",
|
|
" let h: holder = holder { k = 9, u = r };\n",
|
|
" if (probe(h.u) != 3) { return 2; };\n",
|
|
" let x: t_u = ('x': t_lit);\n",
|
|
" let p: *t_u = &x;\n",
|
|
" if (probe(*p) != 1) { return 3; };\n",
|
|
" return 0;\n",
|
|
"};\n"), NEED56, "");
|
|
memargrow("mem56_mixed_orders", strings.concat(mem56types(),
|
|
"fn before(k: i64, s: str, a: t_u) i64 = {\n",
|
|
" if (k != 5) { return 91; };\n",
|
|
" if (s.len != 3) { return 92; };\n",
|
|
" match (a) {\n",
|
|
" case let r: t_rep => { return k + (r.m3: i64); };\n",
|
|
" case => { return 93; };\n",
|
|
" };\n",
|
|
" return 99;\n",
|
|
"};\n",
|
|
"fn after(a: t_u, k: i64, s: str) i64 = {\n",
|
|
" if (k != 6) { return 91; };\n",
|
|
" if (s.len != 3) { return 92; };\n",
|
|
" match (a) {\n",
|
|
" case let r: t_rep => { return k + (r.m3: i64); };\n",
|
|
" case => { return 93; };\n",
|
|
" };\n",
|
|
" return 99;\n",
|
|
"};\n",
|
|
"export fn main() i32 = {\n",
|
|
" let r: t_rep = ", replit(), ";\n",
|
|
" let a: t_u = r;\n",
|
|
" if (before(5, \"abc\", a) != 1239) { return 1; };\n",
|
|
" if (after(a, 6, \"abc\") != 1240) { return 2; };\n",
|
|
" return 0;\n",
|
|
"};\n"), NEED56, "");
|
|
memargrow("mem56_call_in_loop", strings.concat(mem56types(),
|
|
"fn grab(a: t_u) i64 = {\n",
|
|
" match (a) {\n",
|
|
" case let r: t_rep => { return r.m3: i64; };\n",
|
|
" case => { return -1; };\n",
|
|
" };\n",
|
|
" return -2;\n",
|
|
"};\n",
|
|
"export fn main() i32 = {\n",
|
|
" let r: t_rep = ", replit(), ";\n",
|
|
" let a: t_u = r;\n",
|
|
" let canary: i64 = 4242;\n",
|
|
" let sum: i64 = 0;\n",
|
|
" let i: i64 = 0;\n",
|
|
" for (i < 200000) {\n",
|
|
" sum += grab(a) - 1234;\n",
|
|
" i += 1;\n",
|
|
" };\n",
|
|
" if (canary != 4242) { return 2; };\n",
|
|
" if (sum != 0) { return 1; };\n",
|
|
" return 0;\n",
|
|
"};\n"), NEED56, "");
|
|
// TWO mem args (56B + 64B): $120 pins the left-to-right outgoing
|
|
// layout (leftmost mem arg at 16(BP)).
|
|
memargrow("mem56_twomem", strings.concat(mem56types(),
|
|
"type w_rep = struct { id: size, origin: size, m0: size,\n",
|
|
" m1: size, m2: size, m3: size, m4: size };\n",
|
|
"type w_u = (t_lit | w_rep);\n",
|
|
"fn both(a: t_u, b: w_u) i64 = {\n",
|
|
" let x: i64 = 0;\n",
|
|
" match (a) {\n",
|
|
" case let r: t_rep => { x = r.m3: i64; };\n",
|
|
" case => { return 91; };\n",
|
|
" };\n",
|
|
" match (b) {\n",
|
|
" case let r: w_rep => { return x + (r.m4: i64); };\n",
|
|
" case => { return 92; };\n",
|
|
" };\n",
|
|
" return 99;\n",
|
|
"};\n",
|
|
"export fn main() i32 = {\n",
|
|
" let r: t_rep = ", replit(), ";\n",
|
|
" let a: t_u = r;\n",
|
|
" let w: w_rep = w_rep { id = 1, origin = 2, m0 = 3,\n",
|
|
" m1 = 4, m2 = 5, m3 = 6, m4 = 2 };\n",
|
|
" let b: w_u = w;\n",
|
|
" if (both(a, b) != 1236) { return 1; };\n",
|
|
" return 0;\n",
|
|
"};\n"), "\tADDQ\t$120, SP\n", "");
|
|
// The real lib/regex inst layout; outer-tag dispatch only (inner-
|
|
// field matches ride the pre-existing match-on-tagged-struct-field
|
|
// divergence class, out of scope here).
|
|
memargrow("mem56_inst_shape", strings.concat(
|
|
"type inst_lit = rune;\n",
|
|
"type inst_any = void;\n",
|
|
"type inst_repeat = struct { id: size, origin: size,\n",
|
|
" min: (void | size), max: (void | size) };\n",
|
|
"type inst = (inst_lit | inst_any | inst_repeat);\n",
|
|
"fn is_consuming(a: inst) bool = {\n",
|
|
" return a is inst_lit || a is inst_any;\n",
|
|
"};\n",
|
|
"fn rep_id(a: inst) i64 = {\n",
|
|
" match (a) {\n",
|
|
" case let r: inst_repeat => { return r.origin: i64; };\n",
|
|
" case => { return -1; };\n",
|
|
" };\n",
|
|
" return -2;\n",
|
|
"};\n",
|
|
"export fn main() i32 = {\n",
|
|
" let a: inst = ('x': inst_lit);\n",
|
|
" if (!is_consuming(a)) { return 1; };\n",
|
|
" let r: inst_repeat = inst_repeat { id = 7, origin = 9,\n",
|
|
" min = (11: size), max = (1234: size) };\n",
|
|
" let b: inst = r;\n",
|
|
" if (is_consuming(b)) { return 2; };\n",
|
|
" if (rep_id(b) != 9) { return 3; };\n",
|
|
" return 0;\n",
|
|
"};\n"), NEED56, "");
|
|
};
|
|
|
|
// #40/FB3 place-resolved element sources (cgplaceaddr fallback).
|
|
@test fn elemrows() void = {
|
|
memargrow("mem56_elem_source", strings.concat(mem56types(),
|
|
mem56probe(),
|
|
"export fn main() i32 = {\n",
|
|
" let xs: []t_u = [];\n",
|
|
" let a: t_u = ('x': t_lit);\n",
|
|
" append(xs, a);\n",
|
|
" let r: t_rep = ", replit(), ";\n",
|
|
" let b: t_u = r;\n",
|
|
" append(xs, b);\n",
|
|
" if (probe(xs[0]) != 1) { return 1; };\n",
|
|
" if (probe(xs[1]) != 3) { return 2; };\n",
|
|
" let i: size = 1;\n",
|
|
" if (probe(xs[i]) != 3) { return 3; };\n",
|
|
" return 0;\n",
|
|
"};\n"), NEED56, "");
|
|
memargrow("mem56_elem_deref_spine", strings.concat(mem56types(),
|
|
mem56probe(),
|
|
"export fn main() i32 = {\n",
|
|
" let xs: []t_u = [];\n",
|
|
" let r: t_rep = ", replit(), ";\n",
|
|
" let b: t_u = r;\n",
|
|
" append(xs, b);\n",
|
|
" let p: *[]t_u = &xs;\n",
|
|
" if (probe((*p)[0]) != 3) { return 1; };\n",
|
|
" return 0;\n",
|
|
"};\n"), NEED56, "");
|
|
memargrow("mem56_elem_nested_spine", strings.concat(mem56types(),
|
|
"type re_t = struct { flag: i64, insts: []t_u };\n",
|
|
"type thr = struct { pc: size, gen: i64 };\n",
|
|
mem56probe(),
|
|
"export fn main() i32 = {\n",
|
|
" let xs: []t_u = [];\n",
|
|
" let a: t_u = ('x': t_lit);\n",
|
|
" append(xs, a);\n",
|
|
" let r: t_rep = ", replit(), ";\n",
|
|
" let b: t_u = r;\n",
|
|
" append(xs, b);\n",
|
|
" let re: re_t = re_t { flag = 4, insts = xs };\n",
|
|
" let threads: []thr = [];\n",
|
|
" append(threads, thr { pc = 1, gen = 5 });\n",
|
|
" if (probe(re.insts[threads[0].pc]) != 3) { return 1; };\n",
|
|
" return 0;\n",
|
|
"};\n"), NEED56, "");
|
|
memargrow("mem56_elem_mixed_regs", strings.concat(mem56types(),
|
|
"fn before(k: i64, s: str, a: t_u) i64 = {\n",
|
|
" if (k != 5) { return 91; };\n",
|
|
" if (s.len != 3) { return 92; };\n",
|
|
" match (a) {\n",
|
|
" case let r: t_rep => { return k + (r.m3: i64); };\n",
|
|
" case => { return 93; };\n",
|
|
" };\n",
|
|
" return 99;\n",
|
|
"};\n",
|
|
"fn after(a: t_u, k: i64, s: str) i64 = {\n",
|
|
" if (k != 6) { return 91; };\n",
|
|
" if (s.len != 3) { return 92; };\n",
|
|
" match (a) {\n",
|
|
" case let r: t_rep => { return k + (r.m3: i64); };\n",
|
|
" case => { return 93; };\n",
|
|
" };\n",
|
|
" return 99;\n",
|
|
"};\n",
|
|
"export fn main() i32 = {\n",
|
|
" let xs: []t_u = [];\n",
|
|
" let r: t_rep = ", replit(), ";\n",
|
|
" let b: t_u = r;\n",
|
|
" append(xs, b);\n",
|
|
" if (before(5, \"abc\", xs[0]) != 1239) { return 1; };\n",
|
|
" if (after(xs[0], 6, \"abc\") != 1240) { return 2; };\n",
|
|
" return 0;\n",
|
|
"};\n"), NEED56, "");
|
|
// Inner CALL runs inside the mem pre-pass with the other arg's 7
|
|
// words already staged — the resolver's push-balance claim; $112
|
|
// = both 56B slots.
|
|
memargrow("mem56_elem_callidx_twomem", strings.concat(mem56types(),
|
|
mem56probe(),
|
|
"fn pick() size = {\n",
|
|
" return 1;\n",
|
|
"};\n",
|
|
"fn two(a: t_u, b: t_u) i32 = {\n",
|
|
" return probe(a) * 10 + probe(b);\n",
|
|
"};\n",
|
|
"export fn main() i32 = {\n",
|
|
" let xs: []t_u = [];\n",
|
|
" let a: t_u = ('x': t_lit);\n",
|
|
" append(xs, a);\n",
|
|
" let r: t_rep = ", replit(), ";\n",
|
|
" let b: t_u = r;\n",
|
|
" append(xs, b);\n",
|
|
" if (two(xs[0], xs[pick()]) != 13) { return 1; };\n",
|
|
" if (two(xs[pick()], xs[0]) != 31) { return 2; };\n",
|
|
" return 0;\n",
|
|
"};\n"), "\tADDQ\t$112, SP\n", "");
|
|
// Slice globals have DATA storage, so the element resolves via the
|
|
// let_islet arm; the storage-LESS global-VALUE twin stays loud
|
|
// (task #25) and is pinned by the r929 reject fixtures.
|
|
memargrow("mem56_elem_global_slice", strings.concat(mem56types(),
|
|
"let gxs: []t_u;\n",
|
|
mem56probe(),
|
|
"export fn main() i32 = {\n",
|
|
" let r: t_rep = ", replit(), ";\n",
|
|
" let b: t_u = r;\n",
|
|
" append(gxs, b);\n",
|
|
" if (probe(gxs[0]) != 3) { return 1; };\n",
|
|
" return 0;\n",
|
|
"};\n"), NEED56, "");
|
|
};
|
|
|
|
@test fn boundaryrows() void = {
|
|
memargrow("boundary48_register", strings.concat(
|
|
"type t_lit = rune;\n",
|
|
"type t_rep = struct { id: size, origin: size, m0: size,\n",
|
|
" m1: size, m2: size };\n",
|
|
"type t_u = (t_lit | t_rep);\n",
|
|
"fn probe(a: t_u) i32 = {\n",
|
|
" match (a) {\n",
|
|
" case let l: t_lit => { return 1; };\n",
|
|
" case let r: t_rep => {\n",
|
|
" if (r.m2 != 12) { return 92; };\n",
|
|
" return 2;\n",
|
|
" };\n",
|
|
" };\n",
|
|
" return 99;\n",
|
|
"};\n",
|
|
"export fn main() i32 = {\n",
|
|
" let a: t_u = ('x': t_lit);\n",
|
|
" if (probe(a) != 1) { return 1; };\n",
|
|
" let r: t_rep = t_rep { id = 7, origin = 9, m0 = 10,\n",
|
|
" m1 = 11, m2 = 12 };\n",
|
|
" let b: t_u = r;\n",
|
|
" if (probe(b) != 2) { return 2; };\n",
|
|
" return 0;\n",
|
|
"};\n"), "", "\tADDQ\t$48, SP\n");
|
|
// GRADUATED by #87 (tagged globals emit static DATA): the uninit
|
|
// >48B tagged GLOBAL arg compiles through the cgplaceaddr arm;
|
|
// its zero-box run-exit 91 rides the r929 run fixture corpus.
|
|
memargrow("global_uninit_memarg_src", strings.concat(mem56types(),
|
|
"let g: t_u;\n",
|
|
mem56probe(),
|
|
"export fn main() i32 = {\n",
|
|
" return probe(g);\n",
|
|
"};\n"), "", "");
|
|
// #35 Family C flip: the exact-type same-type cast is peeled
|
|
// before memarg place resolution, so the IDENT place is wired.
|
|
memargrow("memarg_idcast_peeled", strings.concat(mem56types(),
|
|
mem56probe(),
|
|
"export fn main() i32 = {\n",
|
|
" let a: t_u = ('x': t_lit);\n",
|
|
" if (probe((a: t_u)) != 1) { return 1; };\n",
|
|
" return 0;\n",
|
|
"};\n"), "", "");
|
|
};
|