test: port the asm-pattern observers to ww; byteid carrier partition empty
Four ww tests carry the last eight native byteid carriers' assertions: asmwindow (753 convwrap beta/main/alpha order + window polarity, 754 slice stride + negative scale scan, 755 amp-dot-idx four rows, 758 first-CALL-line extraction + tab-framed disp literals, direct w6c/w6c_ww never ww build), freenoop (930 byte-id strengthened to per-stream compare + negative 'free' grep), structabi (946 param/ret MOVSD windows with polarity tables and SEND/RECV agreement), mangle (989_m1mangle needles + concat byte-id, glob order strengthened to byte-lexicographic). Dead want/stage_mask row fields documented, not invented into runtime legs; the w6c_ww-absent skip gates drop because the Make target declares the tools. BYTEID_WRAPPER_SOURCES, its bins, and test-native-byteid are deleted — the native byte/artifact partition is EMPTY (11 -> 0 this session); docs counts move to 123 carriers.
This commit is contained in:
144
test/byteid/freenoop_test.ww
Normal file
144
test/byteid/freenoop_test.ww
Normal file
@@ -0,0 +1,144 @@
|
||||
package freenoop_test;
|
||||
|
||||
// free()-is-a-no-op byte-identity gate. Port of the retired native
|
||||
// carrier test/wcc/930_free_noop_run.c; every assertion preserved.
|
||||
//
|
||||
// ww has no free by design (rt/alloc.s:30 — drop-amalloc); post-#27
|
||||
// both stages evaluate free's operand for side effects and emit
|
||||
// NOTHING else. Per row the inline source (prefixed exactly as the
|
||||
// carrier wrote it, `package main;` + blank line) compiles through
|
||||
// w6c AND w6c_ww with the asm on stdout; the stages must agree
|
||||
// byte-for-byte and the NEGATIVE gate holds: no substring 'free'
|
||||
// anywhere in the cstage output — no CALL free, no symbol reference
|
||||
// (byte identity extends the proof to wwstage, as in the carrier).
|
||||
//
|
||||
// The carrier captured stdout+stderr MERGED (`> cs.s 2>&1`) and
|
||||
// compared/grepped that one file; here the streams land separately
|
||||
// and EACH is byte-compared and 'free'-grepped — at least as strict
|
||||
// on both axes.
|
||||
//
|
||||
// Path discipline (the carrier's #8 note): no scratch path may carry
|
||||
// the substring 'free' or the whole-output grep false-fails.
|
||||
// testenv.fresh() is /tmp/<16 lowercase hex> — 'free' needs an 'r',
|
||||
// which the hex alphabet cannot produce — and the source basename
|
||||
// stays noopfr.ww, mirroring the carrier's naming.
|
||||
//
|
||||
// Dead data preserved as dead: the carrier's row table carried a
|
||||
// `want` exit-code field its main() never read (no run leg existed);
|
||||
// none is invented here. Runtime behavior lives in the r930_free_*
|
||||
// wwfixtures per the retired carrier's header. Dropped machinery,
|
||||
// not assertions: getpid()-keyed tmpdirs + rm/mkdir accounting
|
||||
// (testenv.fresh/clean own scratch and assert cleanup).
|
||||
|
||||
import os;
|
||||
import os.exec;
|
||||
import strings;
|
||||
import testenv;
|
||||
import time;
|
||||
|
||||
fn fail(label: str, why: str) void = {
|
||||
let m: str = strings.concat("freenoop 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;
|
||||
};
|
||||
|
||||
// `drv noopfr.ww` with the asm on stdout — the carrier's exact shape
|
||||
// (no -o), so the captured stdout IS the .s under comparison.
|
||||
fn runstage(td: str, label: str, stage: str, drv: str,
|
||||
out: *testenv.commandout) void = {
|
||||
let av: []str = [];
|
||||
append(av, drv);
|
||||
append(av, "noopfr.ww");
|
||||
testenv.runcommand(td, td, stage, av, tmo(), out);
|
||||
let ok: bool = out.termination == exec.termination.EXIT
|
||||
&& out.code == 0;
|
||||
if (!ok) { fail(label, strings.concat(stage, " compile failed")); };
|
||||
};
|
||||
|
||||
fn nooprow(label: str, src: str) void = {
|
||||
let td: str = testenv.fresh();
|
||||
testenv.writefile(strings.concat(td, "/noopfr.ww"),
|
||||
strings.concat("package main;\n\n", src));
|
||||
let cs: testenv.commandout;
|
||||
runstage(td, label, "cstage", testenv.driver("w6c"), &cs);
|
||||
let ws: testenv.commandout;
|
||||
runstage(td, label, "wwstage", testenv.driver("w6c_ww"), &ws);
|
||||
if (!testenv.same(cs.stdout, ws.stdout)) {
|
||||
fail(label, "cs != ww .s");
|
||||
};
|
||||
if (!testenv.same(cs.stderr, ws.stderr)) {
|
||||
fail(label, "cs != ww compile stderr");
|
||||
};
|
||||
if (testenv.has(cs.stdout, "free")
|
||||
|| testenv.has(cs.stderr, "free")) {
|
||||
fail(label, "'free' survives in the .s -- lowering is not a no-op");
|
||||
};
|
||||
testenv.clean(td);
|
||||
};
|
||||
|
||||
// The five carrier rows: deref-after-free leak semantics, the N_DOT
|
||||
// operand shape, operand side effects run per call, no stack damage
|
||||
// across 1M no-op frees, and the verbatim Hare-port alloc/free
|
||||
// round-trip (i64 sidesteps the filed narrow-pointee alloc(value)
|
||||
// size divergence).
|
||||
@test fn asmrows() void = {
|
||||
nooprow("free_local_ptr_deref_after", strings.concat(
|
||||
"export fn main() i32 = {\n",
|
||||
" let x: i32 = 5;\n",
|
||||
" let p: *i32 = &x;\n",
|
||||
" free(p);\n",
|
||||
" if (*p != 5) { return 1; };\n",
|
||||
" return 0;\n",
|
||||
"};\n"));
|
||||
nooprow("free_struct_field", strings.concat(
|
||||
"type holder = struct { p: *i32, n: i32 };\n",
|
||||
"export fn main() i32 = {\n",
|
||||
" let x: i32 = 3;\n",
|
||||
" let h: holder = holder { p = &x, n = 4 };\n",
|
||||
" free(h.p);\n",
|
||||
" if (h.n != 4) { return 2; };\n",
|
||||
" return 0;\n",
|
||||
"};\n"));
|
||||
nooprow("free_call_operand_effects", strings.concat(
|
||||
"let g: i32 = 0;\n",
|
||||
"fn bump(p: *i32) *i32 = {\n",
|
||||
" g = g + 2;\n",
|
||||
" return p;\n",
|
||||
"};\n",
|
||||
"export fn main() i32 = {\n",
|
||||
" let x: i32 = 1;\n",
|
||||
" free(bump(&x));\n",
|
||||
" free(bump(&x));\n",
|
||||
" if (g != 4) { return 3; };\n",
|
||||
" return 0;\n",
|
||||
"};\n"));
|
||||
nooprow("free_loop_no_stack_damage", strings.concat(
|
||||
"let g: i32 = 0;\n",
|
||||
"fn bump(p: *i32) *i32 = {\n",
|
||||
" g = g + 1;\n",
|
||||
" return p;\n",
|
||||
"};\n",
|
||||
"export fn main() i32 = {\n",
|
||||
" let x: i32 = 1;\n",
|
||||
" let i: i32 = 0;\n",
|
||||
" for (i < 1000000) {\n",
|
||||
" free(bump(&x));\n",
|
||||
" i += 1;\n",
|
||||
" };\n",
|
||||
" if (g != 1000000) { return 5; };\n",
|
||||
" return 0;\n",
|
||||
"};\n"));
|
||||
nooprow("free_alloc_roundtrip", strings.concat(
|
||||
"import rt;\n",
|
||||
"export fn main() i32 = {\n",
|
||||
" let p: *i64 = alloc(11i64)!;\n",
|
||||
" free(p);\n",
|
||||
" if (*p != 11i64) { return 4; };\n",
|
||||
" return 0;\n",
|
||||
"};\n"));
|
||||
};
|
||||
Reference in New Issue
Block a user