w6c: keep nested unwrap destinations distinct
This commit is contained in:
@@ -39,11 +39,6 @@ static int *cg_frame;
|
|||||||
* semantics for synthetic scratches). 0 means "not yet allocated";
|
* semantics for synthetic scratches). 0 means "not yet allocated";
|
||||||
* negative offsets returned by local_alloc are the live value. */
|
* negative offsets returned by local_alloc are the live value. */
|
||||||
static int cg_retscr;
|
static int cg_retscr;
|
||||||
/* The aggregate-unwrap receiver must survive its producer call without
|
|
||||||
* perturbing SP: an outstanding PUSHQ at CALL violates SysV alignment.
|
|
||||||
* One BP-relative address spill per function mirrors wwstage's @-prefix
|
|
||||||
* scratch reuse. */
|
|
||||||
static int cg_unwrapdst;
|
|
||||||
/* Per-fn @tupfscr offset (single-slot SSoT). A multi-float tuple return
|
/* Per-fn @tupfscr offset (single-slot SSoT). A multi-float tuple return
|
||||||
* (#164/#107) spills each float out of X0 to this scratch as the L→R
|
* (#164/#107) spills each float out of X0 to this scratch as the L→R
|
||||||
* element walk clobbers X0, then reloads X0/X1 by SSE index after the
|
* element walk clobbers X0, then reloads X0/X1 by SSE index after the
|
||||||
@@ -5367,14 +5362,12 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
fatal("#16: global/chained aggregate "
|
fatal("#16: global/chained aggregate "
|
||||||
"unwrap field dest unresolved "
|
"unwrap field dest unresolved "
|
||||||
"(cgplaceaddr)");
|
"(cgplaceaddr)");
|
||||||
int dstscr;
|
/* Fresh per lowering: cgexpr(rhs) may recursively
|
||||||
if (cg_unwrapdst != 0) {
|
* emit another #16 assignment while this address is
|
||||||
dstscr = cg_unwrapdst;
|
* live. Sharing one per-fn slot would let the inner
|
||||||
} else {
|
* assignment overwrite the outer receiver. */
|
||||||
dstscr = local_alloc(c, &locals, "@unwrapdst",
|
int dstscr = local_alloc(c, &locals, "@unwrapdst",
|
||||||
8, cg_frame);
|
8, cg_frame);
|
||||||
cg_unwrapdst = dstscr;
|
|
||||||
}
|
|
||||||
ins2(c, A_MOVQ, areg(D_BX), amem(D_BP, dstscr));
|
ins2(c, A_MOVQ, areg(D_BX), amem(D_BP, dstscr));
|
||||||
cgexpr(c, n->rhs, locals);
|
cgexpr(c, n->rhs, locals);
|
||||||
ins2(c, A_MOVQ, amem(D_BP, dstscr), areg(D_BX));
|
ins2(c, A_MOVQ, amem(D_BP, dstscr), areg(D_BX));
|
||||||
@@ -15803,7 +15796,6 @@ cgfn(Cg *c, FILE *out, Node *fn)
|
|||||||
nloops = 0;
|
nloops = 0;
|
||||||
cg_ret_type = fn->type ? fn->type->ret : NULL;
|
cg_ret_type = fn->type ? fn->type->ret : NULL;
|
||||||
cg_retscr = 0;
|
cg_retscr = 0;
|
||||||
cg_unwrapdst = 0;
|
|
||||||
cg_tupfscr = 0;
|
cg_tupfscr = 0;
|
||||||
cg_tupargscr = 0;
|
cg_tupargscr = 0;
|
||||||
cg_tupargscr_sz = 0;
|
cg_tupargscr_sz = 0;
|
||||||
|
|||||||
@@ -8780,7 +8780,11 @@ fn cgassign(c: *cgen, n: *syntax.node) void = {
|
|||||||
os.write(2, m16.ptr, m16.len: u64);
|
os.write(2, m16.ptr, m16.len: u64);
|
||||||
os.exit(1);
|
os.exit(1);
|
||||||
};
|
};
|
||||||
let dstscr: i32 = localadd(c, "@unwrapdst", 8, nil);
|
// Fresh per lowering: cgexpr(rhs) may recursively
|
||||||
|
// emit another #16 assignment while this address is
|
||||||
|
// live. Sharing one per-fn slot would let the inner
|
||||||
|
// assignment overwrite the outer receiver.
|
||||||
|
let dstscr: i32 = localalloc(c, "@unwrapdst", 8, nil);
|
||||||
emitline("\tMOVQ\tBX, ");
|
emitline("\tMOVQ\tBX, ");
|
||||||
emitoff(dstscr: i64);
|
emitoff(dstscr: i64);
|
||||||
emitline("(BP)\n");
|
emitline("(BP)\n");
|
||||||
|
|||||||
@@ -299,6 +299,36 @@ fn unwrapaligncheck(label: str, stage: str, s: str) void = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
fn unwrapnestedcheck(label: str, stage: str, s: str) void = {
|
||||||
|
let w: str = probewindow(label, stage, s);
|
||||||
|
if (!testenv.has(w, "TEXT main.probe,$48")) {
|
||||||
|
fail(label, strings.concat(stage,
|
||||||
|
": nested spill frame is not $48"));
|
||||||
|
};
|
||||||
|
if (testenv.has(w, "\tPUSHQ\tBX\n")) {
|
||||||
|
fail(label, strings.concat(stage,
|
||||||
|
": destination PUSHQ remains in nested window"));
|
||||||
|
};
|
||||||
|
let osave: i32 = testenv.pos(w, "\tMOVQ\tBX, -8(BP)\n");
|
||||||
|
let choice: i32 = posafter(w, osave + 1,
|
||||||
|
"\tCALL\tmain.choice(SB)\n");
|
||||||
|
let isave: i32 = posafter(w, choice + 1,
|
||||||
|
"\tMOVQ\tBX, -48(BP)\n");
|
||||||
|
let make: i32 = posafter(w, isave + 1,
|
||||||
|
"\tCALL\tmain.make(SB)\n");
|
||||||
|
let iload: i32 = posafter(w, make + 1,
|
||||||
|
"\tMOVQ\t-48(BP), BX\n");
|
||||||
|
let pass: i32 = posafter(w, iload + 1,
|
||||||
|
"\tCALL\tmain.pass(SB)\n");
|
||||||
|
let oload: i32 = posafter(w, pass + 1,
|
||||||
|
"\tMOVQ\t-8(BP), BX\n");
|
||||||
|
if (osave < 0 || choice < 0 || isave < 0 || make < 0
|
||||||
|
|| iload < 0 || pass < 0 || oload < 0) {
|
||||||
|
fail(label, strings.concat(stage,
|
||||||
|
": nested destination spills are not distinct and scoped"));
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
fn commandexit(dir: str, name: str, av: []str) i32 = {
|
fn commandexit(dir: str, name: str, av: []str) i32 = {
|
||||||
let co: testenv.commandout;
|
let co: testenv.commandout;
|
||||||
testenv.runcommand(dir, dir, name, av, tmo(), &co);
|
testenv.runcommand(dir, dir, name, av, tmo(), &co);
|
||||||
@@ -349,3 +379,65 @@ fn commandexit(dir: str, name: str, av: []str) i32 = {
|
|||||||
};
|
};
|
||||||
testenv.clean(td);
|
testenv.clean(td);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// The outer destination stays live while its call argument lowers a
|
||||||
|
// match arm containing another #16 assignment. A per-fn shared spill
|
||||||
|
// redirected both stores to inner.f and left outer.f unchanged.
|
||||||
|
@test fn unwrapnestedlive() void = {
|
||||||
|
let label: str = "nested_global_unwrap_destination";
|
||||||
|
let td: str = testenv.fresh();
|
||||||
|
let src: str = strings.concat(
|
||||||
|
"package main;\n",
|
||||||
|
"type e = !i32;\n",
|
||||||
|
"type pair = struct { a: i64, b: i64 };\n",
|
||||||
|
"type box = struct { f: pair, guard: i64 };\n",
|
||||||
|
"let outer: box = box {\n",
|
||||||
|
" f = pair { a = 1i64, b = 2i64 }, guard = 7i64\n",
|
||||||
|
"};\n",
|
||||||
|
"let inner: box = box {\n",
|
||||||
|
" f = pair { a = 3i64, b = 4i64 }, guard = 8i64\n",
|
||||||
|
"};\n",
|
||||||
|
"fn make() (pair | e) = {\n",
|
||||||
|
" return pair { a = 111i64, b = 222i64 };\n",
|
||||||
|
"};\n",
|
||||||
|
"fn choice() (i32 | str) = { return 1i32; };\n",
|
||||||
|
"fn pass(x: i64) (pair | e) = {\n",
|
||||||
|
" return pair { a = 111i64 + x, b = 222i64 };\n",
|
||||||
|
"};\n",
|
||||||
|
"fn probe() i32 = {\n",
|
||||||
|
" outer.f = pass(match (choice()) {\n",
|
||||||
|
" case i32 => {\n",
|
||||||
|
" inner.f = make()!;\n",
|
||||||
|
" yield 0i64;\n",
|
||||||
|
" };\n",
|
||||||
|
" case str => yield 0i64;\n",
|
||||||
|
" })!;\n",
|
||||||
|
" if (outer.f.a != 111i64 || outer.f.b != 222i64",
|
||||||
|
" || outer.guard != 7i64) { return 91; };\n",
|
||||||
|
" if (inner.f.a != 111i64 || inner.f.b != 222i64",
|
||||||
|
" || inner.guard != 8i64) { return 92; };\n",
|
||||||
|
" return 37;\n",
|
||||||
|
"};\n",
|
||||||
|
"export fn main() i32 = { return probe(); };\n");
|
||||||
|
testenv.writefile(strings.concat(td, "/src.ww"), src);
|
||||||
|
emitstage(td, label, "cstage", testenv.driver("w6c"), "cs.s");
|
||||||
|
emitstage(td, label, "wwstage", testenv.driver("w6c_ww"), "ws.s");
|
||||||
|
let cs: str = testenv.readfile(strings.concat(td, "/cs.s"));
|
||||||
|
let ws: str = testenv.readfile(strings.concat(td, "/ws.s"));
|
||||||
|
unwrapnestedcheck(label, "cstage", cs);
|
||||||
|
unwrapnestedcheck(label, "wwstage", ws);
|
||||||
|
if (!testenv.same(cs, ws)) {
|
||||||
|
fail(label, "cstage vs wwstage asm differs");
|
||||||
|
};
|
||||||
|
let out: str = strings.concat(td, "/unwrapnested");
|
||||||
|
let bav: []str = [testenv.driver("ww"), "build", "-o", out,
|
||||||
|
"src.ww"];
|
||||||
|
if (commandexit(td, "build", bav) != 0) {
|
||||||
|
fail(label, "nested runtime pin build failed");
|
||||||
|
};
|
||||||
|
let rav: []str = [out];
|
||||||
|
if (commandexit(td, "run", rav) != 37) {
|
||||||
|
fail(label, "nested payload/neighbour runtime exit != 37");
|
||||||
|
};
|
||||||
|
testenv.clean(td);
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user