selfhost: cgfn pre-scan SysV-class accounting (closes #7)
Mirrors cgen.c §5130-5223 / cgfnparams: reg-spill, tagged-partial-fit, pure-stack. Pure-stack does not bump cursor (cstage semantics). Fixes 16B over-allocation on 7+ arg functions; bootstrap stays byte-identical. Slice/str at reg/stack straddle is deferred to task #11 (cgfnparams doesn't stitch them either); pre-scan stays symmetric until then.
This commit is contained in:
8
Makefile
8
Makefile
@@ -217,7 +217,7 @@ TESTS = $(BIN)/test_smoke $(BIN)/test_lex $(BIN)/test_parse $(BIN)/test_check \
|
||||
$(BIN)/test_e2e $(BIN)/test_ffi $(BIN)/test_dyn $(BIN)/test_stdlib \
|
||||
$(BIN)/test_at_test $(BIN)/test_let_global \
|
||||
$(BIN)/test_int_cast_signed $(BIN)/test_dot_chain \
|
||||
$(BIN)/test_field_signed \
|
||||
$(BIN)/test_field_signed $(BIN)/test_frame_argcount \
|
||||
$(BIN)/test_selfhost $(BIN)/test_w6a_ww $(BIN)/test_w6l_ww \
|
||||
$(BIN)/test_w6c_ww $(BIN)/test_ww_ww $(BIN)/test_self_rebuild \
|
||||
$(BIN)/test_dyn_ww $(BIN)/test_selfcheck $(BIN)/test_at_test_ww
|
||||
@@ -296,6 +296,12 @@ $(BIN)/test_field_signed: test/wcc/660_field_signed.c $(BIN)/ww \
|
||||
$(LIB)/libwwrt.a | $(BIN)
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
$(BIN)/test_frame_argcount: test/wcc/670_frame_argcount.c $(BIN)/ww \
|
||||
$(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \
|
||||
$(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \
|
||||
$(LIB)/libwwrt.a | $(BIN)
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
$(BIN)/test_selfhost: test/wcc/990_selfhost.c $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \
|
||||
$(BIN)/ww $(BIN)/wwdump $(BIN)/wwdump_ww $(LIB)/libwwrt.a | $(BIN)
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
@@ -13680,22 +13680,65 @@ fn cgfn(c: *cgen, fn_: *node) void = {
|
||||
os.write(1, nm.ptr, nm.len: u64);
|
||||
emitline(",$");
|
||||
|
||||
// Pre-scan total frame: 24 bytes per slice param, 16 per str
|
||||
// param, 8 per other param, plus per-let from scanlocals.
|
||||
// Seed c.locals with param-name stubs so scanlocals dedups a
|
||||
// re-declared `let <name>` in the body against the param's
|
||||
// slot (matches C cgen). Stubs get cleared before emission.
|
||||
// Pre-scan total frame: only count params that land in a local
|
||||
// slot. SysV-class accounting; mirrors runtime walk in cstage
|
||||
// cgen.c §5130-5223 and cgfnparams below. A stack-spilled param
|
||||
// is addressed at a positive BP offset by cgfnparams (via
|
||||
// localaddstack) and consumes no frame, so adding its size here
|
||||
// would over-allocate. Seed c.locals with param-name stubs so
|
||||
// scanlocals dedups a re-declared `let <name>` in the body
|
||||
// against the param's slot (matches C cgen). Stubs get cleared
|
||||
// before emission.
|
||||
let scanp: *node = fn_.list;
|
||||
let frame: i32 = 0;
|
||||
let argi: i32 = 0;
|
||||
let fargi: i32 = 0;
|
||||
for (scanp != nil) {
|
||||
if (scanp.kind == nkind.N_PARAM) {
|
||||
// Hare-style variadic `T...`: param is []T inside
|
||||
// the callee, so it occupies a 24B slice slot.
|
||||
if (scanp.op == tkind.TK_ELLIPSIS) { frame += 24; }
|
||||
else { if (istaggedtype(c, scanp.lhs)) { frame += slotsize(c, scanp.lhs); }
|
||||
else { if (isslicetype(c, scanp.lhs)) { frame += 24; }
|
||||
else { if (isstrtype(c, scanp.lhs)) { frame += 16; }
|
||||
else { frame += 8; }; }; }; };
|
||||
let isvar: bool = scanp.op == tkind.TK_ELLIPSIS;
|
||||
let isf: bool = false;
|
||||
let istg: bool = false;
|
||||
let issl: bool = false;
|
||||
let isst: bool = false;
|
||||
if (!isvar) {
|
||||
isf = isfloattype(c, scanp.lhs);
|
||||
istg = istaggedtype(c, scanp.lhs);
|
||||
if (!isf && !istg) {
|
||||
issl = isslicetype(c, scanp.lhs);
|
||||
if (!issl) { isst = isstrtype(c, scanp.lhs); };
|
||||
};
|
||||
};
|
||||
let eb: i32 = 1;
|
||||
let sz: i32 = 8;
|
||||
if (isvar) { eb = 3; sz = 24; }
|
||||
else { if (istg) { sz = slotsize(c, scanp.lhs); eb = sz / 8; }
|
||||
else { if (issl) { eb = 3; sz = 24; }
|
||||
else { if (isst) { eb = 2; sz = 16; }
|
||||
else { if (isf) {
|
||||
eb = 1;
|
||||
sz = 8;
|
||||
if (isf32type(c, scanp.lhs)) { sz = 4; };
|
||||
}; }; }; }; };
|
||||
let regs_left: i32 = 6 - argi;
|
||||
if (isf) { regs_left = 8 - fargi; };
|
||||
if (regs_left >= eb) {
|
||||
frame += sz;
|
||||
if (isf) { fargi += 1; }
|
||||
else { argi += eb; };
|
||||
} else { if (eb > 1 && regs_left > 0 && istg) {
|
||||
// Tagged param straddles the reg/stack boundary;
|
||||
// cgfnparams stitches the tail from positive BP
|
||||
// offsets into a single local slot, so we still
|
||||
// reserve the full size. Slice/str at the same
|
||||
// straddle are *not* stitched by cgfnparams today
|
||||
// (task #11) — once that's fixed the predicate
|
||||
// here must widen symmetrically.
|
||||
frame += sz;
|
||||
argi = 6;
|
||||
} else {
|
||||
// Pure stack: lives at +BP(16+stkcursor*8); no
|
||||
// local slot consumed. The reg cursor stays put.
|
||||
}; };
|
||||
scanseenmark(c, scanp.str);
|
||||
};
|
||||
scanp = scanp.next;
|
||||
|
||||
@@ -562,22 +562,65 @@ fn cgfn(c: *cgen, fn_: *node) void = {
|
||||
os.write(1, nm.ptr, nm.len: u64);
|
||||
emitline(",$");
|
||||
|
||||
// Pre-scan total frame: 24 bytes per slice param, 16 per str
|
||||
// param, 8 per other param, plus per-let from scanlocals.
|
||||
// Seed c.locals with param-name stubs so scanlocals dedups a
|
||||
// re-declared `let <name>` in the body against the param's
|
||||
// slot (matches C cgen). Stubs get cleared before emission.
|
||||
// Pre-scan total frame: only count params that land in a local
|
||||
// slot. SysV-class accounting; mirrors runtime walk in cstage
|
||||
// cgen.c §5130-5223 and cgfnparams below. A stack-spilled param
|
||||
// is addressed at a positive BP offset by cgfnparams (via
|
||||
// localaddstack) and consumes no frame, so adding its size here
|
||||
// would over-allocate. Seed c.locals with param-name stubs so
|
||||
// scanlocals dedups a re-declared `let <name>` in the body
|
||||
// against the param's slot (matches C cgen). Stubs get cleared
|
||||
// before emission.
|
||||
let scanp: *node = fn_.list;
|
||||
let frame: i32 = 0;
|
||||
let argi: i32 = 0;
|
||||
let fargi: i32 = 0;
|
||||
for (scanp != nil) {
|
||||
if (scanp.kind == nkind.N_PARAM) {
|
||||
// Hare-style variadic `T...`: param is []T inside
|
||||
// the callee, so it occupies a 24B slice slot.
|
||||
if (scanp.op == tkind.TK_ELLIPSIS) { frame += 24; }
|
||||
else { if (istaggedtype(c, scanp.lhs)) { frame += slotsize(c, scanp.lhs); }
|
||||
else { if (isslicetype(c, scanp.lhs)) { frame += 24; }
|
||||
else { if (isstrtype(c, scanp.lhs)) { frame += 16; }
|
||||
else { frame += 8; }; }; }; };
|
||||
let isvar: bool = scanp.op == tkind.TK_ELLIPSIS;
|
||||
let isf: bool = false;
|
||||
let istg: bool = false;
|
||||
let issl: bool = false;
|
||||
let isst: bool = false;
|
||||
if (!isvar) {
|
||||
isf = isfloattype(c, scanp.lhs);
|
||||
istg = istaggedtype(c, scanp.lhs);
|
||||
if (!isf && !istg) {
|
||||
issl = isslicetype(c, scanp.lhs);
|
||||
if (!issl) { isst = isstrtype(c, scanp.lhs); };
|
||||
};
|
||||
};
|
||||
let eb: i32 = 1;
|
||||
let sz: i32 = 8;
|
||||
if (isvar) { eb = 3; sz = 24; }
|
||||
else { if (istg) { sz = slotsize(c, scanp.lhs); eb = sz / 8; }
|
||||
else { if (issl) { eb = 3; sz = 24; }
|
||||
else { if (isst) { eb = 2; sz = 16; }
|
||||
else { if (isf) {
|
||||
eb = 1;
|
||||
sz = 8;
|
||||
if (isf32type(c, scanp.lhs)) { sz = 4; };
|
||||
}; }; }; }; };
|
||||
let regs_left: i32 = 6 - argi;
|
||||
if (isf) { regs_left = 8 - fargi; };
|
||||
if (regs_left >= eb) {
|
||||
frame += sz;
|
||||
if (isf) { fargi += 1; }
|
||||
else { argi += eb; };
|
||||
} else { if (eb > 1 && regs_left > 0 && istg) {
|
||||
// Tagged param straddles the reg/stack boundary;
|
||||
// cgfnparams stitches the tail from positive BP
|
||||
// offsets into a single local slot, so we still
|
||||
// reserve the full size. Slice/str at the same
|
||||
// straddle are *not* stitched by cgfnparams today
|
||||
// (task #11) — once that's fixed the predicate
|
||||
// here must widen symmetrically.
|
||||
frame += sz;
|
||||
argi = 6;
|
||||
} else {
|
||||
// Pure stack: lives at +BP(16+stkcursor*8); no
|
||||
// local slot consumed. The reg cursor stays put.
|
||||
}; };
|
||||
scanseenmark(c, scanp.str);
|
||||
};
|
||||
scanp = scanp.next;
|
||||
|
||||
@@ -13680,22 +13680,65 @@ fn cgfn(c: *cgen, fn_: *node) void = {
|
||||
os.write(1, nm.ptr, nm.len: u64);
|
||||
emitline(",$");
|
||||
|
||||
// Pre-scan total frame: 24 bytes per slice param, 16 per str
|
||||
// param, 8 per other param, plus per-let from scanlocals.
|
||||
// Seed c.locals with param-name stubs so scanlocals dedups a
|
||||
// re-declared `let <name>` in the body against the param's
|
||||
// slot (matches C cgen). Stubs get cleared before emission.
|
||||
// Pre-scan total frame: only count params that land in a local
|
||||
// slot. SysV-class accounting; mirrors runtime walk in cstage
|
||||
// cgen.c §5130-5223 and cgfnparams below. A stack-spilled param
|
||||
// is addressed at a positive BP offset by cgfnparams (via
|
||||
// localaddstack) and consumes no frame, so adding its size here
|
||||
// would over-allocate. Seed c.locals with param-name stubs so
|
||||
// scanlocals dedups a re-declared `let <name>` in the body
|
||||
// against the param's slot (matches C cgen). Stubs get cleared
|
||||
// before emission.
|
||||
let scanp: *node = fn_.list;
|
||||
let frame: i32 = 0;
|
||||
let argi: i32 = 0;
|
||||
let fargi: i32 = 0;
|
||||
for (scanp != nil) {
|
||||
if (scanp.kind == nkind.N_PARAM) {
|
||||
// Hare-style variadic `T...`: param is []T inside
|
||||
// the callee, so it occupies a 24B slice slot.
|
||||
if (scanp.op == tkind.TK_ELLIPSIS) { frame += 24; }
|
||||
else { if (istaggedtype(c, scanp.lhs)) { frame += slotsize(c, scanp.lhs); }
|
||||
else { if (isslicetype(c, scanp.lhs)) { frame += 24; }
|
||||
else { if (isstrtype(c, scanp.lhs)) { frame += 16; }
|
||||
else { frame += 8; }; }; }; };
|
||||
let isvar: bool = scanp.op == tkind.TK_ELLIPSIS;
|
||||
let isf: bool = false;
|
||||
let istg: bool = false;
|
||||
let issl: bool = false;
|
||||
let isst: bool = false;
|
||||
if (!isvar) {
|
||||
isf = isfloattype(c, scanp.lhs);
|
||||
istg = istaggedtype(c, scanp.lhs);
|
||||
if (!isf && !istg) {
|
||||
issl = isslicetype(c, scanp.lhs);
|
||||
if (!issl) { isst = isstrtype(c, scanp.lhs); };
|
||||
};
|
||||
};
|
||||
let eb: i32 = 1;
|
||||
let sz: i32 = 8;
|
||||
if (isvar) { eb = 3; sz = 24; }
|
||||
else { if (istg) { sz = slotsize(c, scanp.lhs); eb = sz / 8; }
|
||||
else { if (issl) { eb = 3; sz = 24; }
|
||||
else { if (isst) { eb = 2; sz = 16; }
|
||||
else { if (isf) {
|
||||
eb = 1;
|
||||
sz = 8;
|
||||
if (isf32type(c, scanp.lhs)) { sz = 4; };
|
||||
}; }; }; }; };
|
||||
let regs_left: i32 = 6 - argi;
|
||||
if (isf) { regs_left = 8 - fargi; };
|
||||
if (regs_left >= eb) {
|
||||
frame += sz;
|
||||
if (isf) { fargi += 1; }
|
||||
else { argi += eb; };
|
||||
} else { if (eb > 1 && regs_left > 0 && istg) {
|
||||
// Tagged param straddles the reg/stack boundary;
|
||||
// cgfnparams stitches the tail from positive BP
|
||||
// offsets into a single local slot, so we still
|
||||
// reserve the full size. Slice/str at the same
|
||||
// straddle are *not* stitched by cgfnparams today
|
||||
// (task #11) — once that's fixed the predicate
|
||||
// here must widen symmetrically.
|
||||
frame += sz;
|
||||
argi = 6;
|
||||
} else {
|
||||
// Pure stack: lives at +BP(16+stkcursor*8); no
|
||||
// local slot consumed. The reg cursor stays put.
|
||||
}; };
|
||||
scanseenmark(c, scanp.str);
|
||||
};
|
||||
scanp = scanp.next;
|
||||
|
||||
236
test/wcc/670_frame_argcount.c
Normal file
236
test/wcc/670_frame_argcount.c
Normal file
@@ -0,0 +1,236 @@
|
||||
/*
|
||||
* 670_frame_argcount — function-frame size for functions with more
|
||||
* than six register-class args. wwstage's cgfn pre-scan used to add
|
||||
* a full slot per param regardless of class, then round up — so a
|
||||
* 7-arg i64 fn over-allocated by 16B (cstage $48, wwstage $64) and
|
||||
* broke the bootstrap byte-identity gate (tests 993/994/995). Fix:
|
||||
* pre-scan now mirrors cgfnparams' SysV class accounting (and cstage
|
||||
* cgen.c §5130-5223), so a stack-spilled param adds 0 to the frame
|
||||
* and never bumps the reg cursor.
|
||||
*
|
||||
* Each row asserts value correctness. Byte-identity is asserted at
|
||||
* the bootstrap level by 993/994/995, so this file pins the runtime
|
||||
* contract: a function that mixes reg and stack args must compute
|
||||
* the same answer in both stages.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
static int
|
||||
runwait(const char *cmd)
|
||||
{
|
||||
int rc = system(cmd);
|
||||
if (rc == -1) return -1;
|
||||
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct row { const char *label; const char *src; int want; };
|
||||
|
||||
static const struct row rows[] = {
|
||||
/* 7-arg i64: 6 regs + 1 stack. Pins the frame size that used to
|
||||
* over-allocate by 16B in wwstage. Returns the sum (1..7 = 28). */
|
||||
{ "i64_7args",
|
||||
"fn s7(a: i64, b: i64, c: i64, d: i64, e: i64, f: i64, g: i64) i64 = {\n"
|
||||
" return a + b + c + d + e + f + g;\n"
|
||||
"};\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let r: i64 = s7(1i64, 2i64, 3i64, 4i64, 5i64, 6i64, 7i64);\n"
|
||||
" if (r == 28i64) { return 42; };\n"
|
||||
" return 0;\n"
|
||||
"};\n",
|
||||
42 },
|
||||
/* 8-arg i64: 6 regs + 2 stack. Stack-cursor advances twice. */
|
||||
{ "i64_8args",
|
||||
"fn s8(a: i64, b: i64, c: i64, d: i64, e: i64,\n"
|
||||
" f: i64, g: i64, h: i64) i64 = {\n"
|
||||
" return a + b + c + d + e + f + g + h;\n"
|
||||
"};\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let r: i64 = s8(1i64, 2i64, 3i64, 4i64,\n"
|
||||
" 5i64, 6i64, 7i64, 8i64);\n"
|
||||
" if (r == 36i64) { return 42; };\n"
|
||||
" return 0;\n"
|
||||
"};\n",
|
||||
42 },
|
||||
/* Mixed-signed sum: an i64 thread through 7 args. */
|
||||
{ "i64_7args_signed",
|
||||
"fn s7(a: i64, b: i64, c: i64, d: i64, e: i64, f: i64, g: i64) i64 = {\n"
|
||||
" return a + b + c + d + e + f + g;\n"
|
||||
"};\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let r: i64 = s7(-1i64, -2i64, -3i64, -4i64, -5i64, -6i64, 49i64);\n"
|
||||
" if (r == 28i64) { return 42; };\n"
|
||||
" return 0;\n"
|
||||
"};\n",
|
||||
42 },
|
||||
/* 9-arg f64: 8 XMM regs + 1 stack. Exercises fargi past 8. */
|
||||
{ "f64_9args",
|
||||
"fn s9f(a: f64, b: f64, c: f64, d: f64, e: f64,\n"
|
||||
" f: f64, g: f64, h: f64, i: f64) f64 = {\n"
|
||||
" return a + b + c + d + e + f + g + h + i;\n"
|
||||
"};\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let r: f64 = s9f(1.0, 2.0, 3.0, 4.0, 5.0,\n"
|
||||
" 6.0, 7.0, 8.0, 9.0);\n"
|
||||
" if ((r: i64) == 45i64) { return 42; };\n"
|
||||
" return 0;\n"
|
||||
"};\n",
|
||||
42 },
|
||||
/* Mixed: 5 ints fill ints 1..5, 3 floats fill XMM 1..3, then 4
|
||||
* more ints — int #6 goes to reg, ints #7..9 spill to stack. The
|
||||
* int and float classes have independent cursors, so XMM stays
|
||||
* at 3 even after the trailing 4 ints. */
|
||||
{ "mixed_5i_3f_4i",
|
||||
"fn mx(a: i64, b: i64, c: i64, d: i64, e: i64,\n"
|
||||
" f1: f64, f2: f64, f3: f64,\n"
|
||||
" g: i64, h: i64, i: i64, j: i64) i64 = {\n"
|
||||
" return a + b + c + d + e\n"
|
||||
" + (f1: i64) + (f2: i64) + (f3: i64)\n"
|
||||
" + g + h + i + j;\n"
|
||||
"};\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let r: i64 = mx(1i64, 2i64, 3i64, 4i64, 5i64,\n"
|
||||
" 6.0, 7.0, 8.0,\n"
|
||||
" 9i64, 10i64, 11i64, 12i64);\n"
|
||||
" if (r == 78i64) { return 42; };\n"
|
||||
" return 0;\n"
|
||||
"};\n",
|
||||
42 },
|
||||
/* Slice past the register window: 6 ints saturate argi, the
|
||||
* slice (3 eightbytes) goes wholly to the stack at positive BP
|
||||
* offsets via localaddstack. Verifies every element of the
|
||||
* slice round-trips through a stack-passed slice arg.
|
||||
*
|
||||
* NOTE: a slice at the *straddle* boundary (arg #6 after 5
|
||||
* ints, so 1 reg + 2 stack words) currently mismatches between
|
||||
* pushargsrev (greedy reg fill) and cgfnparams (no stitch for
|
||||
* slice/str — only tagged). That's a separate pre-existing
|
||||
* cgfnparams bug, tracked as task #11 (wwstage cgfnparams:
|
||||
* slice/str at reg/stack straddle don't stitch). Once #11 is
|
||||
* fixed, the cgfn pre-scan must be updated symmetrically and
|
||||
* this fixture extended to cover the straddle case.
|
||||
*/
|
||||
{ "slice_purestack_arg7",
|
||||
"fn ss(a: i64, b: i64, c: i64, d: i64, e: i64, f: i64, xs: []i64) i64 = {\n"
|
||||
" let i: i32 = 0;\n"
|
||||
" let acc: i64 = a + b + c + d + e + f;\n"
|
||||
" for (i < xs.len: i32) { acc = acc + xs[i]; i = i + 1; };\n"
|
||||
" return acc;\n"
|
||||
"};\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let buf: [3]i64;\n"
|
||||
" buf[0] = 100i64; buf[1] = 200i64; buf[2] = 300i64;\n"
|
||||
" let s: []i64 = buf[0:3];\n"
|
||||
" let r: i64 = ss(1i64, 2i64, 3i64, 4i64, 5i64, 6i64, s);\n"
|
||||
" if (r == 621i64) { return 42; };\n"
|
||||
" return 0;\n"
|
||||
"};\n",
|
||||
42 },
|
||||
/* str past the register window: 6 ints saturate argi, the str
|
||||
* (2 eightbytes) goes wholly to the stack via localaddstack.
|
||||
* Both .ptr and .len must round-trip. */
|
||||
{ "str_purestack_arg7",
|
||||
"fn ts(a: i64, b: i64, c: i64, d: i64, e: i64, f: i64, s: str) i64 = {\n"
|
||||
" return a + b + c + d + e + f + s.len: i64;\n"
|
||||
"};\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let r: i64 = ts(1i64, 2i64, 3i64, 4i64, 5i64, 6i64, \"hello\");\n"
|
||||
" if (r == 26i64) { return 42; };\n"
|
||||
" return 0;\n"
|
||||
"};\n",
|
||||
42 },
|
||||
};
|
||||
|
||||
static int
|
||||
run_driver(const char *driver, const struct row *r, int i)
|
||||
{
|
||||
char src[64], tmpdir[64], cmd[1024];
|
||||
snprintf(src, sizeof src, "/tmp/wwfa_%d_%d.ww", getpid(), i);
|
||||
snprintf(tmpdir, sizeof tmpdir, "/tmp/wwfa_%d_d_%d", getpid(), i);
|
||||
|
||||
FILE *f = fopen(src, "wb");
|
||||
if (!f) return -1;
|
||||
fputs(r->src, f);
|
||||
fclose(f);
|
||||
|
||||
mkdir(tmpdir, 0755);
|
||||
snprintf(cmd, sizeof cmd, "cd %s && %s build %s",
|
||||
tmpdir, driver, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: build via %s failed\n",
|
||||
r->label, driver);
|
||||
unlink(src); rmdir(tmpdir);
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char *base = strrchr(src, '/');
|
||||
base = base ? base + 1 : src;
|
||||
char outbin[128];
|
||||
snprintf(outbin, sizeof outbin, "%s/%s", tmpdir, base);
|
||||
char *dot = strrchr(outbin, '.');
|
||||
if (dot && strcmp(dot, ".ww") == 0) *dot = '\0';
|
||||
int got = runwait(outbin);
|
||||
|
||||
unlink(src); unlink(outbin); rmdir(tmpdir);
|
||||
return got;
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
const char *bin = getenv("BIN");
|
||||
if (!bin) bin = "out/bin";
|
||||
char absbin[1024];
|
||||
if (bin[0] != '/') {
|
||||
char cwd[1024];
|
||||
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
|
||||
bin = absbin;
|
||||
}
|
||||
|
||||
char cdrv[1024];
|
||||
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
|
||||
char wdrv[1024];
|
||||
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
|
||||
|
||||
struct { const char *name; const char *path; int gated_on_existence; }
|
||||
drivers[] = {
|
||||
{ "cstage", cdrv, 0 },
|
||||
{ "wwstage", wdrv, 1 },
|
||||
{ NULL, NULL, 0 },
|
||||
};
|
||||
|
||||
int n = (int)(sizeof rows / sizeof rows[0]);
|
||||
int total = 0, fail = 0;
|
||||
for (int d = 0; drivers[d].name; d++) {
|
||||
if (drivers[d].gated_on_existence
|
||||
&& access(drivers[d].path, X_OK) != 0) {
|
||||
fprintf(stderr, "frame_argcount: skip %s (no %s)\n",
|
||||
drivers[d].name, drivers[d].path);
|
||||
continue;
|
||||
}
|
||||
for (int i = 0; i < n; i++) {
|
||||
int got = run_driver(drivers[d].path, &rows[i], i);
|
||||
total++;
|
||||
if (got != rows[i].want) {
|
||||
fprintf(stderr,
|
||||
"frame_argcount[%s][%s]: exit=%d want=%d\n",
|
||||
drivers[d].name, rows[i].label,
|
||||
got, rows[i].want);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fail) {
|
||||
fprintf(stderr,
|
||||
"frame_argcount: %d/%d fixtures failed\n", fail, total);
|
||||
return 1;
|
||||
}
|
||||
printf("frame_argcount: %d/%d ok\n", total, total);
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user