w6c+selfhost: cgen N_ASSIGN TY_SLICE field branch (closes #24)
Parallel to the existing TY_STR branch at cgen.c:1939. Stores AX/BX/CX at field+0/+8/+16 across three sub-shapes (via_ptr, is_global, direct local) — DX as addr scratch where needed so CX (cap) survives. Symmetric in wwstage cgassign. Surfaces tasks #25 (whole-struct rhs) and #26 (whole-tagged rhs) in the same locus class.
This commit is contained in:
@@ -1952,6 +1952,32 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* slice-typed field: rhs cgexpr leaves (AX=ptr,
|
||||
* BX=len, CX=cap); store all three at
|
||||
* field+0/+8/+16. Address scratch must dodge CX
|
||||
* (holds cap), so via_ptr/is_global stage the
|
||||
* struct base in DX. Without this branch the
|
||||
* generic store_op below writes only AX, silently
|
||||
* dropping .len and .cap. */
|
||||
if (n->op == TK_ASSIGN && str_fu && str_fu->kind == TY_SLICE) {
|
||||
cgexpr(c, n->rhs, locals);
|
||||
if (via_ptr) {
|
||||
ins2(c, A_MOVQ, amem(D_BP, boff), areg(D_DX));
|
||||
ins2(c, A_MOVQ, areg(D_AX), amem(D_DX, foff + 0));
|
||||
ins2(c, A_MOVQ, areg(D_BX), amem(D_DX, foff + 8));
|
||||
ins2(c, A_MOVQ, areg(D_CX), amem(D_DX, foff + 16));
|
||||
} else if (is_global) {
|
||||
ins2(c, A_LEAQ, masym(c, base->str), areg(D_DX));
|
||||
ins2(c, A_MOVQ, areg(D_AX), amem(D_DX, foff + 0));
|
||||
ins2(c, A_MOVQ, areg(D_BX), amem(D_DX, foff + 8));
|
||||
ins2(c, A_MOVQ, areg(D_CX), amem(D_DX, foff + 16));
|
||||
} else {
|
||||
ins2(c, A_MOVQ, areg(D_AX), amem(D_BP, boff + foff + 0));
|
||||
ins2(c, A_MOVQ, areg(D_BX), amem(D_BP, boff + foff + 8));
|
||||
ins2(c, A_MOVQ, areg(D_CX), amem(D_BP, boff + foff + 16));
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* compound: load current value into BX */
|
||||
if (n->op != TK_ASSIGN) {
|
||||
if (via_ptr) {
|
||||
|
||||
@@ -11661,6 +11661,26 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
emitline("\n");
|
||||
return;
|
||||
};
|
||||
// slice field via *struct: rhs left
|
||||
// (AX=ptr, BX=len, CX=cap). CX is
|
||||
// taken, so stage the struct addr
|
||||
// in DX. Store all three words at
|
||||
// foff/+8/+16.
|
||||
if (isslicetype(c, fi.tnode)) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
emitline("(BP), DX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitdispreg(fi.foff: i64, "DX");
|
||||
emitline("\n");
|
||||
emitline("\tMOVQ\tBX, ");
|
||||
emitdispreg((fi.foff + 8): i64, "DX");
|
||||
emitline("\n");
|
||||
emitline("\tMOVQ\tCX, ");
|
||||
emitdispreg((fi.foff + 16): i64, "DX");
|
||||
emitline("\n");
|
||||
return;
|
||||
};
|
||||
// f64/f32 plain `=` via *struct: cgexpr left the
|
||||
// value in X0. Reload struct ptr and MOVSD/MOVSS.
|
||||
if (isfloattype(c, fi.tnode)) {
|
||||
@@ -11717,6 +11737,23 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
emitline("(BP)\n");
|
||||
return;
|
||||
};
|
||||
// slice field direct: cgexpr left
|
||||
// (AX=ptr, BX=len, CX=cap); store all
|
||||
// three at +0/+8/+16. The generic
|
||||
// fldstoreop below would only write AX,
|
||||
// dropping .len/.cap.
|
||||
if (isslicetype(c, fi.tnode)) {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((lc.off + fi.foff): i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tBX, ");
|
||||
emitoff((lc.off + fi.foff + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tCX, ");
|
||||
emitoff((lc.off + fi.foff + 16): i64);
|
||||
emitline("(BP)\n");
|
||||
return;
|
||||
};
|
||||
// f64/f32 direct struct local store: route via X0.
|
||||
if (isfloattype(c, fi.tnode)) {
|
||||
let mov: str = "MOVSD";
|
||||
|
||||
@@ -3613,6 +3613,26 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
emitline("\n");
|
||||
return;
|
||||
};
|
||||
// slice field via *struct: rhs left
|
||||
// (AX=ptr, BX=len, CX=cap). CX is
|
||||
// taken, so stage the struct addr
|
||||
// in DX. Store all three words at
|
||||
// foff/+8/+16.
|
||||
if (isslicetype(c, fi.tnode)) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
emitline("(BP), DX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitdispreg(fi.foff: i64, "DX");
|
||||
emitline("\n");
|
||||
emitline("\tMOVQ\tBX, ");
|
||||
emitdispreg((fi.foff + 8): i64, "DX");
|
||||
emitline("\n");
|
||||
emitline("\tMOVQ\tCX, ");
|
||||
emitdispreg((fi.foff + 16): i64, "DX");
|
||||
emitline("\n");
|
||||
return;
|
||||
};
|
||||
// f64/f32 plain `=` via *struct: cgexpr left the
|
||||
// value in X0. Reload struct ptr and MOVSD/MOVSS.
|
||||
if (isfloattype(c, fi.tnode)) {
|
||||
@@ -3669,6 +3689,23 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
emitline("(BP)\n");
|
||||
return;
|
||||
};
|
||||
// slice field direct: cgexpr left
|
||||
// (AX=ptr, BX=len, CX=cap); store all
|
||||
// three at +0/+8/+16. The generic
|
||||
// fldstoreop below would only write AX,
|
||||
// dropping .len/.cap.
|
||||
if (isslicetype(c, fi.tnode)) {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((lc.off + fi.foff): i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tBX, ");
|
||||
emitoff((lc.off + fi.foff + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tCX, ");
|
||||
emitoff((lc.off + fi.foff + 16): i64);
|
||||
emitline("(BP)\n");
|
||||
return;
|
||||
};
|
||||
// f64/f32 direct struct local store: route via X0.
|
||||
if (isfloattype(c, fi.tnode)) {
|
||||
let mov: str = "MOVSD";
|
||||
|
||||
@@ -11661,6 +11661,26 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
emitline("\n");
|
||||
return;
|
||||
};
|
||||
// slice field via *struct: rhs left
|
||||
// (AX=ptr, BX=len, CX=cap). CX is
|
||||
// taken, so stage the struct addr
|
||||
// in DX. Store all three words at
|
||||
// foff/+8/+16.
|
||||
if (isslicetype(c, fi.tnode)) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
emitline("(BP), DX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitdispreg(fi.foff: i64, "DX");
|
||||
emitline("\n");
|
||||
emitline("\tMOVQ\tBX, ");
|
||||
emitdispreg((fi.foff + 8): i64, "DX");
|
||||
emitline("\n");
|
||||
emitline("\tMOVQ\tCX, ");
|
||||
emitdispreg((fi.foff + 16): i64, "DX");
|
||||
emitline("\n");
|
||||
return;
|
||||
};
|
||||
// f64/f32 plain `=` via *struct: cgexpr left the
|
||||
// value in X0. Reload struct ptr and MOVSD/MOVSS.
|
||||
if (isfloattype(c, fi.tnode)) {
|
||||
@@ -11717,6 +11737,23 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
emitline("(BP)\n");
|
||||
return;
|
||||
};
|
||||
// slice field direct: cgexpr left
|
||||
// (AX=ptr, BX=len, CX=cap); store all
|
||||
// three at +0/+8/+16. The generic
|
||||
// fldstoreop below would only write AX,
|
||||
// dropping .len/.cap.
|
||||
if (isslicetype(c, fi.tnode)) {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((lc.off + fi.foff): i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tBX, ");
|
||||
emitoff((lc.off + fi.foff + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tCX, ");
|
||||
emitoff((lc.off + fi.foff + 16): i64);
|
||||
emitline("(BP)\n");
|
||||
return;
|
||||
};
|
||||
// f64/f32 direct struct local store: route via X0.
|
||||
if (isfloattype(c, fi.tnode)) {
|
||||
let mov: str = "MOVSD";
|
||||
|
||||
@@ -192,6 +192,90 @@ static const struct row rows[] = {
|
||||
" return stk[0].x;\n"
|
||||
"};\n",
|
||||
32 },
|
||||
/* Whole-slice rhs to struct field, direct struct local (task #24).
|
||||
* cgen N_ASSIGN had a TY_STR branch that stored ptr+len at +0/+8
|
||||
* but no TY_SLICE branch; the fallthrough generic store wrote only
|
||||
* AX (ptr), silently dropping .len and .cap. Fixed by a parallel
|
||||
* TY_SLICE branch that stores AX/BX/CX at +0/+8/+16. The bug bit
|
||||
* bufio.init's `b.rbuf = rbuf` (first stdlib struct with a []u8
|
||||
* field). Returns 42 when len, cap, and a trailing scalar field
|
||||
* all survived the store. */
|
||||
{ "slice_field_value_write",
|
||||
"type bs = struct { rbuf: []u8, mark: i32 };\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let raw: [8]u8; raw[0] = 0u8;\n"
|
||||
" let rbuf: []u8 = raw[0:5];\n"
|
||||
" let b: bs;\n"
|
||||
" b.mark = 7;\n"
|
||||
" b.rbuf = rbuf;\n"
|
||||
" if (b.rbuf.len != 5) { return 1; };\n"
|
||||
" if (b.rbuf.cap != 5) { return 2; };\n"
|
||||
" if (b.mark != 7) { return 3; };\n"
|
||||
" return 42;\n"
|
||||
"};\n",
|
||||
42 },
|
||||
/* Same bug via pointer to struct — the bufio scenario exactly
|
||||
* (`init(b: *bstream, rbuf: []u8) { b.rbuf = rbuf; }`). The via_ptr
|
||||
* arm of the new TY_SLICE branch stages the struct addr in DX to
|
||||
* avoid clobbering CX (cap). */
|
||||
{ "slice_field_ptr_write",
|
||||
"type bs = struct { rbuf: []u8, mark: i32 };\n"
|
||||
"fn init(b: *bs, rbuf: []u8) void = {\n"
|
||||
" b.rbuf = rbuf;\n"
|
||||
" b.mark = 9;\n"
|
||||
"};\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let raw: [8]u8; raw[0] = 0u8;\n"
|
||||
" let b: bs;\n"
|
||||
" init(&b, raw[0:5]);\n"
|
||||
" if (b.rbuf.len != 5) { return 1; };\n"
|
||||
" if (b.rbuf.cap != 5) { return 2; };\n"
|
||||
" if (b.mark != 9) { return 3; };\n"
|
||||
" return 42;\n"
|
||||
"};\n",
|
||||
42 },
|
||||
/* Distinct byte-pattern check — independent verification that
|
||||
* the AX/BX/CX MOVQs landed at +0/+8/+16 rather than passing by
|
||||
* coincidence with neighbouring zero memory. Distinct byte
|
||||
* patterns at raw[0] (0xAA) and raw[5] (0xFF) are read back
|
||||
* through b.rbuf[i], which only succeeds if .ptr (AX) survived
|
||||
* at +0. Trailing mark uses a distinctive value (0x33) so a
|
||||
* stray CX store one slot too far is caught separately. .len
|
||||
* and .cap are forced equal by ww's slice expression (both
|
||||
* BX = hi-lo), so this row pins .ptr survival while the other
|
||||
* rows pin .len/.cap distinctness from the zero default. */
|
||||
{ "slice_field_distinct_bytes",
|
||||
"type bs = struct { rbuf: []u8, mark: i32 };\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let raw: [16]u8;\n"
|
||||
" raw[0] = 0xAAu8; raw[1] = 0xBBu8; raw[2] = 0xCCu8;\n"
|
||||
" raw[3] = 0xDDu8; raw[4] = 0xEEu8; raw[5] = 0xFFu8;\n"
|
||||
" let b: bs;\n"
|
||||
" b.mark = 0x33;\n"
|
||||
" b.rbuf = raw[0:6];\n"
|
||||
" if (b.rbuf.len != 6) { return 1; };\n"
|
||||
" if (b.rbuf.cap != 6) { return 2; };\n"
|
||||
" if (b.mark != 0x33) { return 3; };\n"
|
||||
" if (b.rbuf[0] != 0xAAu8) { return 4; };\n"
|
||||
" if (b.rbuf[5] != 0xFFu8) { return 5; };\n"
|
||||
" return 42;\n"
|
||||
"};\n",
|
||||
42 },
|
||||
/* Str-field regression for the parallel TY_STR branch right above
|
||||
* the new TY_SLICE branch — pin that store path so a future refactor
|
||||
* doesn't drop it. Direct struct local; `b.s = "hello"` stores
|
||||
* AX/BX at +0/+8, and the trailing i32 mark survives. */
|
||||
{ "str_field_value_write_regression",
|
||||
"type bs = struct { s: str, mark: i32 };\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let b: bs;\n"
|
||||
" b.mark = 11;\n"
|
||||
" b.s = \"hello\";\n"
|
||||
" if (b.s.len != 5) { return 1; };\n"
|
||||
" if (b.mark != 11) { return 2; };\n"
|
||||
" return 42;\n"
|
||||
"};\n",
|
||||
42 },
|
||||
};
|
||||
|
||||
static int
|
||||
|
||||
Reference in New Issue
Block a user