cgen: store a 2-byte struct-literal field with MOVW, not an over-wide MOVQ (#15)
cg_structlit_fill/cgstructlitfill dispatched scalar field stores as {1->MOVB, 4->MOVL, else->MOVQ} with no fsz==2 case, so a 2-byte field was stored with an 8-byte MOVQ. Interior over-stores were harmlessly overwritten by the next field, but the LAST field at the frame edge corrupted the saved base pointer: an (S|e) union success variant places the struct payload after the 8B tag, landing the last field at -4(BP), so MOVQ AX,-4(BP) wrote into saved [BP] and POPQ BP restored garbage — a silent both-stage caller-frame clobber. Route the scalar store through the existing fldstoreop/fieldstoreop helper ({1->MOVB,2->MOVW,4->MOVL,else->MOVQ}), both stages; the #13 graduation comments already pre-documented this resolution. Pure width fix, no loud-stop (scalar widths are always {1,2,4,8} and narrowing is always correct). Value-asserting pin: an i64 sentinel live across the union-maker call (detects the clobber directly) + all members, with a non-union control.
This commit is contained in:
@@ -5189,11 +5189,10 @@ export fn dotchainresolve(c: *cgen, n: *syntax.node,
|
||||
// preserved because the existing inline code's reload-before-each-
|
||||
// store pattern matches the helper's per-store reload exactly.
|
||||
//
|
||||
// Graduation note (task #13): the scalar store currently uses the
|
||||
// explicit {1→MOVB, 4→MOVL, else MOVQ} dispatch to match cstage
|
||||
// byte-identically — cstage hasn't yet learned MOVW for fsz==2. Once
|
||||
// #13 aligns both stages, the dispatch can switch to fieldstoreop
|
||||
// which already returns MOVW where appropriate.
|
||||
// The scalar store routes through fieldstoreop (full field-width
|
||||
// {1→MOVB, 2→MOVW, 4→MOVL, else MOVQ}); the missing MOVW for fsz==2
|
||||
// over-stored a 2-byte tail field past its slot into saved BP on a
|
||||
// union-return success variant (#15). Symmetric with cstage fldstoreop.
|
||||
fn cgstructlitfill(c: *cgen, si: *structinfo, lit: *syntax.node,
|
||||
mode: i32, srcoff: i32, srcname: str,
|
||||
disp: i32) void = {
|
||||
@@ -5704,16 +5703,13 @@ fn cgstructlitfill(c: *cgen, si: *structinfo, lit: *syntax.node,
|
||||
};
|
||||
fi = nil;
|
||||
} else {
|
||||
// Explicit {1→MOVB, 4→MOVL, else MOVQ}
|
||||
// dispatch (not fieldstoreop) to match
|
||||
// cstage byte-identically. wwstage's
|
||||
// fieldstoreop would return MOVW for
|
||||
// fsz==2 which cstage doesn't emit —
|
||||
// tracked as task #13.
|
||||
let fsz: i32 = fi.fsz;
|
||||
let op: str = "MOVQ";
|
||||
if (fsz == 1) { op = "MOVB"; };
|
||||
if (fsz == 4) { op = "MOVL"; };
|
||||
// Full field-width store via fieldstoreop
|
||||
// {1→MOVB, 2→MOVW, 4→MOVL, else MOVQ}: the
|
||||
// missing MOVW for fsz==2 over-stored a
|
||||
// 2-byte tail field past its slot into
|
||||
// saved BP on a union-return success
|
||||
// variant (#15). Symmetric with cstage.
|
||||
let op: str = fieldstoreop(c, fi);
|
||||
emitline("\t");
|
||||
emitline(op);
|
||||
emitline("\tAX, ");
|
||||
|
||||
Reference in New Issue
Block a user