wcc/cgen: #151 push all 3 header words for let-global slice/str by-value arg (wwstage)
A let-global slice or str passed by value as an argument was silently
field-dropped by wwstage: pushargsrev's global branch had only the #150-A
struct arm, and wwstage's nodeisslice/nodeisstr are local-keyed (false for
a global), so a global slice/str ident fell to the scalar single-PUSHQ,
pushing one of the three header words {ptr,len,cap} -> the callee read
garbage for .len/.cap. cstage was already correct.
wwstage-only, caller-side only (slice/str params already received
correctly). A global slice/str arm in pushargsrev, type-keyed on
tichase(arg.type_).kind, with two arms byte-matching cstage's two distinct
sequences -- str via cgslicehdr (CX-base, cgen.c:1866), slice per-word
(BX-base, cgen.c:9124). Gate is isletvar-only (a def has no name(SB)
holder; cstage const-folds it -- def-str/slice-by-value is the residual
task #21). cstage unchanged (w6c md5 unchanged); byte-id 990-997 8/8,
no lib pin flips. test/wcc/829 table-driven, byte-id per type.
This commit is contained in:
7
Makefile
7
Makefile
@@ -257,6 +257,7 @@ TESTS = $(BIN)/test_smoke $(BIN)/test_lex $(BIN)/test_parse $(BIN)/test_check \
|
||||
$(BIN)/test_alias_tuple_coerce \
|
||||
$(BIN)/test_str_eq \
|
||||
$(BIN)/test_overlong_arrlit \
|
||||
$(BIN)/test_slice_str_global_arg \
|
||||
$(BIN)/test_slice_str_global_zero \
|
||||
$(BIN)/test_slice_literal_global \
|
||||
$(BIN)/test_global_arr_elem_field \
|
||||
@@ -707,6 +708,12 @@ $(BIN)/test_overlong_arrlit: test/wcc/828_overlong_arrlit.c $(BIN)/ww \
|
||||
$(LIB)/libwwrt.a | $(BIN)
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
$(BIN)/test_slice_str_global_arg: test/wcc/829_slice_str_global_arg.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_def_arr_infer_len: test/wcc/814_def_arr_infer_len.c $(BIN)/ww \
|
||||
$(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \
|
||||
$(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \
|
||||
|
||||
@@ -17110,6 +17110,50 @@ fn pushargsrev(c: *cgen, arg: *node, param: *node, memphase: bool) i32 = {
|
||||
return rest + gnw;
|
||||
};
|
||||
}; };
|
||||
// #151: a module-global by-value slice/str arg — the
|
||||
// slice/str twin of the #150 struct arm above. wwstage's
|
||||
// nodeisslice/nodeisstr (below) are LOCAL-keyed
|
||||
// (localfindnode→nil for a global) so a global slice/str
|
||||
// ident returned false there and fell to the scalar
|
||||
// single-PUSHQ default, dropping len+cap. cstage is
|
||||
// type-keyed (node_isslice/node_isstr on n->type) so it
|
||||
// pushed all 3 header words. cstage emits a DIFFERENT
|
||||
// per-type sequence — mirror EACH for byte-id: a slice via
|
||||
// its dedicated push arm (BX-base per-word, cgen.c:9124); a
|
||||
// str falls to cgexpr's cgslicehdr (CX-base AX/BX/CX,
|
||||
// cgen.c:1866) then the node_isstr triple push.
|
||||
// LET-only (NOT deflookup, unlike the #150 struct arm):
|
||||
// cstage's slice arm gates let_islet (cgen.c:1869) and a
|
||||
// def-str is const-folded by cgexpr (LEAQ _S_0, MOVQ
|
||||
// $len) — it has no name(SB) holder. A deflookup here
|
||||
// would LEAQ an undefined main.<def>(SB) (w6l fails); a
|
||||
// def must fall through to the const-fold path instead.
|
||||
if (gst != nil && isletvar(c, nm)) {
|
||||
if (gst.kind == tykind.TY_SLICE) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, nm);
|
||||
emitline("(SB), BX\n");
|
||||
emitline("\tMOVQ\t16(BX), AX\n");
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
emitline("\tMOVQ\t8(BX), AX\n");
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
emitline("\tMOVQ\t(BX), AX\n");
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
return rest + 3;
|
||||
};
|
||||
if (gst.kind == tykind.TY_STR) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, nm);
|
||||
emitline("(SB), CX\n");
|
||||
emitline("\tMOVQ\t(CX), AX\n");
|
||||
emitline("\tMOVQ\t8(CX), BX\n");
|
||||
emitline("\tMOVQ\t16(CX), CX\n");
|
||||
emitline("\tPUSHQ\tCX\n");
|
||||
emitline("\tPUSHQ\tBX\n");
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
return rest + 3;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
// #271: aggregate (struct/array) arg from any source the ≤16B
|
||||
|
||||
@@ -755,6 +755,50 @@ fn pushargsrev(c: *cgen, arg: *node, param: *node, memphase: bool) i32 = {
|
||||
return rest + gnw;
|
||||
};
|
||||
}; };
|
||||
// #151: a module-global by-value slice/str arg — the
|
||||
// slice/str twin of the #150 struct arm above. wwstage's
|
||||
// nodeisslice/nodeisstr (below) are LOCAL-keyed
|
||||
// (localfindnode→nil for a global) so a global slice/str
|
||||
// ident returned false there and fell to the scalar
|
||||
// single-PUSHQ default, dropping len+cap. cstage is
|
||||
// type-keyed (node_isslice/node_isstr on n->type) so it
|
||||
// pushed all 3 header words. cstage emits a DIFFERENT
|
||||
// per-type sequence — mirror EACH for byte-id: a slice via
|
||||
// its dedicated push arm (BX-base per-word, cgen.c:9124); a
|
||||
// str falls to cgexpr's cgslicehdr (CX-base AX/BX/CX,
|
||||
// cgen.c:1866) then the node_isstr triple push.
|
||||
// LET-only (NOT deflookup, unlike the #150 struct arm):
|
||||
// cstage's slice arm gates let_islet (cgen.c:1869) and a
|
||||
// def-str is const-folded by cgexpr (LEAQ _S_0, MOVQ
|
||||
// $len) — it has no name(SB) holder. A deflookup here
|
||||
// would LEAQ an undefined main.<def>(SB) (w6l fails); a
|
||||
// def must fall through to the const-fold path instead.
|
||||
if (gst != nil && isletvar(c, nm)) {
|
||||
if (gst.kind == tykind.TY_SLICE) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, nm);
|
||||
emitline("(SB), BX\n");
|
||||
emitline("\tMOVQ\t16(BX), AX\n");
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
emitline("\tMOVQ\t8(BX), AX\n");
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
emitline("\tMOVQ\t(BX), AX\n");
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
return rest + 3;
|
||||
};
|
||||
if (gst.kind == tykind.TY_STR) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, nm);
|
||||
emitline("(SB), CX\n");
|
||||
emitline("\tMOVQ\t(CX), AX\n");
|
||||
emitline("\tMOVQ\t8(CX), BX\n");
|
||||
emitline("\tMOVQ\t16(CX), CX\n");
|
||||
emitline("\tPUSHQ\tCX\n");
|
||||
emitline("\tPUSHQ\tBX\n");
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
return rest + 3;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
// #271: aggregate (struct/array) arg from any source the ≤16B
|
||||
|
||||
@@ -17110,6 +17110,50 @@ fn pushargsrev(c: *cgen, arg: *node, param: *node, memphase: bool) i32 = {
|
||||
return rest + gnw;
|
||||
};
|
||||
}; };
|
||||
// #151: a module-global by-value slice/str arg — the
|
||||
// slice/str twin of the #150 struct arm above. wwstage's
|
||||
// nodeisslice/nodeisstr (below) are LOCAL-keyed
|
||||
// (localfindnode→nil for a global) so a global slice/str
|
||||
// ident returned false there and fell to the scalar
|
||||
// single-PUSHQ default, dropping len+cap. cstage is
|
||||
// type-keyed (node_isslice/node_isstr on n->type) so it
|
||||
// pushed all 3 header words. cstage emits a DIFFERENT
|
||||
// per-type sequence — mirror EACH for byte-id: a slice via
|
||||
// its dedicated push arm (BX-base per-word, cgen.c:9124); a
|
||||
// str falls to cgexpr's cgslicehdr (CX-base AX/BX/CX,
|
||||
// cgen.c:1866) then the node_isstr triple push.
|
||||
// LET-only (NOT deflookup, unlike the #150 struct arm):
|
||||
// cstage's slice arm gates let_islet (cgen.c:1869) and a
|
||||
// def-str is const-folded by cgexpr (LEAQ _S_0, MOVQ
|
||||
// $len) — it has no name(SB) holder. A deflookup here
|
||||
// would LEAQ an undefined main.<def>(SB) (w6l fails); a
|
||||
// def must fall through to the const-fold path instead.
|
||||
if (gst != nil && isletvar(c, nm)) {
|
||||
if (gst.kind == tykind.TY_SLICE) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, nm);
|
||||
emitline("(SB), BX\n");
|
||||
emitline("\tMOVQ\t16(BX), AX\n");
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
emitline("\tMOVQ\t8(BX), AX\n");
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
emitline("\tMOVQ\t(BX), AX\n");
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
return rest + 3;
|
||||
};
|
||||
if (gst.kind == tykind.TY_STR) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, nm);
|
||||
emitline("(SB), CX\n");
|
||||
emitline("\tMOVQ\t(CX), AX\n");
|
||||
emitline("\tMOVQ\t8(CX), BX\n");
|
||||
emitline("\tMOVQ\t16(CX), CX\n");
|
||||
emitline("\tPUSHQ\tCX\n");
|
||||
emitline("\tPUSHQ\tBX\n");
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
return rest + 3;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
// #271: aggregate (struct/array) arg from any source the ≤16B
|
||||
|
||||
274
test/wcc/829_slice_str_global_arg.c
Normal file
274
test/wcc/829_slice_str_global_arg.c
Normal file
@@ -0,0 +1,274 @@
|
||||
/*
|
||||
* 829_slice_str_global_arg — a module-global SLICE or STR passed BY VALUE to
|
||||
* a fn (`let s: str = "..."; take(s)`, `let g: []int = [...]; take(g)`) was
|
||||
* silently miscompiled by wwstage only (task #151, the slice/str twin of the
|
||||
* #150 struct-global arg):
|
||||
*
|
||||
* - cstage (cmd/w6c/cgen.c): node_isslice/node_isstr are TYPE-keyed
|
||||
* (n->type), so a global slice/str ident still pushes all 3 header words
|
||||
* {ptr,len,cap} — slice via its dedicated push arm (BX-base per-word,
|
||||
* cgen.c:9124), str via cgexpr's cgslicehdr (CX-base AX/BX/CX,
|
||||
* cgen.c:1866) then the node_isstr triple push.
|
||||
* - wwstage (selfhost cgenutil.ww pushargsrev): nodeisslice/nodeisstr are
|
||||
* LOCAL-keyed (localfindnode→nil for a global → return false), so a
|
||||
* global slice/str ident fell past every header arm to the scalar
|
||||
* single-PUSHQ default — ONE word (ptr), dropping len+cap. The callee
|
||||
* then read garbage for .len/.cap (silent field-drop).
|
||||
*
|
||||
* The fix (ONE wwstage cgen commit, cstage UNTOUCHED → w6c md5 unchanged):
|
||||
* add a global slice/str arm beside the #150 struct arm in pushargsrev's
|
||||
* `lc == nil` branch, gated `(isletvar || deflookup)`. cstage emits a
|
||||
* DIFFERENT per-type sequence, so each arm is hand-authored to MATCH:
|
||||
* - slice: LEAQ name(SB),BX; MOVQ {16,8,0}(BX),AX; PUSHQ AX (BX-base)
|
||||
* - str: LEAQ name(SB),CX; MOVQ (CX),AX / 8(CX),BX / 16(CX),CX;
|
||||
* PUSHQ CX/BX/AX (cgslicehdr CX-base)
|
||||
* Each `.s` is now byte-IDENTICAL to cstage per type. The LOCAL slice/str
|
||||
* push path (off!=0) is untouched.
|
||||
*
|
||||
* row | shape | want
|
||||
* ---------------+----------------------------------------------+------
|
||||
* str_glob_len | let s:str="abcd"; take reads x.len | 4
|
||||
* str_glob_ptr | let s:str="abcd"; take reads x[0] (uses ptr) | 97
|
||||
* slice_glob_len | let g:[]int=[10..50]; take reads x.len | 5
|
||||
* slice_glob_idx | let g:[]int=[10..50]; take reads x[2] | 30
|
||||
* ctrl_local_str | LOCAL str arg (off!=0, unchanged, byte-id) | 2
|
||||
* ctrl_local_sl | LOCAL []int arg (off!=0, unchanged, byte-id) | 8
|
||||
*
|
||||
* The *_glob_* rows are mutation-sane: pre-fix wwstage drops len/cap and the
|
||||
* callee reads garbage; cstage is correct. ctrl_* guard the unchanged local
|
||||
* path. A byte-id row pins cstage==wwstage `.s` for each source.
|
||||
*
|
||||
* NOTE — slice element type is []int, not []u8: a `let g: []u8 = [intlits]`
|
||||
* module-global is rejected by the wwstage checker ("let: not assignable",
|
||||
* the untyped-int→u8 array-lit coercion) while cstage accepts it — a
|
||||
* SEPARATE pre-existing checker divergence (#10/#125 batch, "bare []u8
|
||||
* cs≠ww"), orthogonal to this caller-side push fix. []int exercises the
|
||||
* identical TY_SLICE push arm. The str rows cover the []u8-header shape
|
||||
* (str IS []u8), including a byte read via x[0].
|
||||
*/
|
||||
#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[] = {
|
||||
/* str_glob_len — THE regression pin (str header): global str by value,
|
||||
* callee reads .len. Pre-fix wwstage drops len → garbage. */
|
||||
{ "str_glob_len",
|
||||
"package main;\n"
|
||||
"let s: str = \"abcd\";\n"
|
||||
"fn take(x: str) int = { return x.len: int; };\n"
|
||||
"export fn main() i32 = { return take(s): i32; };\n",
|
||||
4 },
|
||||
|
||||
/* str_glob_ptr — global str by value, callee reads x[0] (proves the
|
||||
* ptr word survived the push). 'a' == 97. */
|
||||
{ "str_glob_ptr",
|
||||
"package main;\n"
|
||||
"let s: str = \"abcd\";\n"
|
||||
"fn take(x: str) int = { return x[0]: int; };\n"
|
||||
"export fn main() i32 = { return take(s): i32; };\n",
|
||||
97 },
|
||||
|
||||
/* slice_glob_len — global slice by value, callee reads .len. Pre-fix
|
||||
* wwstage drops len/cap → garbage; the TY_SLICE BX-base arm. */
|
||||
{ "slice_glob_len",
|
||||
"package main;\n"
|
||||
"let g: []int = [10, 20, 30, 40, 50];\n"
|
||||
"fn take(x: []int) int = { return x.len: int; };\n"
|
||||
"export fn main() i32 = { return take(g): i32; };\n",
|
||||
5 },
|
||||
|
||||
/* slice_glob_idx — global slice by value, callee reads x[2] (proves
|
||||
* ptr survived the push). */
|
||||
{ "slice_glob_idx",
|
||||
"package main;\n"
|
||||
"let g: []int = [10, 20, 30, 40, 50];\n"
|
||||
"fn take(x: []int) int = { return x[2]; };\n"
|
||||
"export fn main() i32 = { return take(g): i32; };\n",
|
||||
30 },
|
||||
|
||||
/* ctrl_local_str — a LOCAL str arg (off!=0). cgen unchanged; pins the
|
||||
* local-str push path stays byte-id (no regress). */
|
||||
{ "ctrl_local_str",
|
||||
"package main;\n"
|
||||
"fn take(x: str) int = { return x.len: int; };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
"\tlet s: str = \"ab\";\n"
|
||||
"\treturn take(s): i32;\n"
|
||||
"};\n",
|
||||
2 },
|
||||
|
||||
/* ctrl_local_sl — a LOCAL []int arg (off!=0). cgen unchanged; pins the
|
||||
* local-slice push path stays byte-id (no regress). */
|
||||
{ "ctrl_local_sl",
|
||||
"package main;\n"
|
||||
"fn take(x: []int) int = { return x[1]; };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
"\tlet g: []int = [7, 8, 9];\n"
|
||||
"\treturn take(g): i32;\n"
|
||||
"};\n",
|
||||
8 },
|
||||
};
|
||||
|
||||
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/ssg_%d_%d.ww", getpid(), i);
|
||||
snprintf(tmpdir, sizeof tmpdir, "/tmp/ssg_%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 2>/dev/null",
|
||||
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;
|
||||
}
|
||||
|
||||
/* asm_byte_identical — w6c vs w6c_ww .s for the same source must match. */
|
||||
static int
|
||||
asm_byte_identical(const char *bin, const struct row *r, int i)
|
||||
{
|
||||
char src[64], cs[64], ws[64], cmd[1024];
|
||||
snprintf(src, sizeof src, "/tmp/ssg_asm_%d_%d.ww", getpid(), i);
|
||||
snprintf(cs, sizeof cs, "/tmp/ssg_asm_%d_%d_c.s", getpid(), i);
|
||||
snprintf(ws, sizeof ws, "/tmp/ssg_asm_%d_%d_w.s", getpid(), i);
|
||||
|
||||
FILE *f = fopen(src, "wb");
|
||||
if (!f) return -1;
|
||||
fputs(r->src, f);
|
||||
fclose(f);
|
||||
|
||||
snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s 2>/dev/null", bin, cs, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: w6c errored\n", r->label);
|
||||
unlink(src);
|
||||
return -1;
|
||||
}
|
||||
snprintf(cmd, sizeof cmd, "%s/w6c_ww -o %s %s 2>/dev/null",
|
||||
bin, ws, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: w6c_ww errored\n", r->label);
|
||||
unlink(src); unlink(cs);
|
||||
return -1;
|
||||
}
|
||||
|
||||
FILE *fc = fopen(cs, "rb");
|
||||
FILE *fw = fopen(ws, "rb");
|
||||
int rc = 0;
|
||||
if (!fc || !fw) {
|
||||
rc = -1;
|
||||
} else {
|
||||
for (;;) {
|
||||
int a = fgetc(fc);
|
||||
int b = fgetc(fw);
|
||||
if (a != b) { rc = -1; break; }
|
||||
if (a == EOF) break;
|
||||
}
|
||||
}
|
||||
if (fc) fclose(fc);
|
||||
if (fw) fclose(fw);
|
||||
if (rc != 0)
|
||||
fprintf(stderr, "row[%s]: cstage vs wwstage asm differs\n",
|
||||
r->label);
|
||||
unlink(src); unlink(cs); unlink(ws);
|
||||
return rc;
|
||||
}
|
||||
|
||||
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, "slice_str_global_arg: 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,
|
||||
"slice_str_global_arg[%s][%s]: exit=%d want=%d\n",
|
||||
drivers[d].name, rows[i].label,
|
||||
got, rows[i].want);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (access(wdrv, X_OK) == 0) {
|
||||
for (int i = 0; i < n; i++) {
|
||||
total++;
|
||||
if (asm_byte_identical(bin, &rows[i], i) != 0)
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
fprintf(stderr,
|
||||
"slice_str_global_arg: %d/%d fixtures failed\n", fail, total);
|
||||
return 1;
|
||||
}
|
||||
printf("slice_str_global_arg: %d/%d ok\n", total, total);
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user