cgen: ww str for-range loop-var narrows to MOVZBQ -- Phase 2 step-3 Fold 1 (align-up)
Ranging a str (for (let b .. = s)) and reading the loop var back emitted MOVZBQ on cstage (correct u8 zero-extend) but MOVQ on wwstage (the missed case, #14). Align wwstage UP. ww cgforrange derived the element-type node only for slice/array; for a str scrutinee it left elemt=nil, so the loop var registered with no type and localloadop short-circuited to MOVQ. Fix: for a str scrutinee, synthesize a u8 element node (type_ = str.sub = u8, from F1) as elemt, so localadd hands the loop var a u8 tnode and the GENERIC narrow-load fires (MOVZBQ) -- consuming str.sub as F1 intended, mirroring how []u8 supplies its element node. NOT an if-str special-case. cstage already correct, untouched (ww-only). str's own type stays nominal. GATE is the ASM SHAPE byte-id (cstage==wwstage at the loop-var read), NOT a runtime probe: the divergence is runtime-benign (MOVQ and MOVZBQ read the same zero-extended byte) so a runtime test passes both ways and cannot distinguish -- it was a byte-id-INVISIBLE divergence (990-997 green despite cstage!=ww, since no bootstrap input exercises a narrow-read str loop var). Verified fail-pre (the cstage-MOVZBQ vs wwstage-MOVQ 1-line diff) / pass-post (.s byte-identical). []u8/slice/array for-range emission unchanged. test/wcc/940 carries the fixture (runtime corpus coverage, both drivers). main.combined.ww regenerated via the canonical make path.
This commit is contained in:
@@ -21356,6 +21356,27 @@ fn cgforrange(c: *cgen, n: *node) void = {
|
||||
let sk: nkind = slctn.kind;
|
||||
if (sk == nkind.N_TSLICE) { elemt = slctn.lhs; };
|
||||
if (sk == nkind.N_TARRAY) { elemt = slctn.lhs; };
|
||||
// str IS []u8 (F1: tystr.sub = tyu8). []u8 hands cgen a real
|
||||
// u8 element node (slctn.lhs); a str scrutinee has none, so the
|
||||
// loop var would register tnode=nil and read back as a wide
|
||||
// MOVQ. Synthesise the u8 element off str.sub so the loop-var
|
||||
// registration carries a u8 tnode and localloadop narrows the
|
||||
// read-back to MOVZBQ on its own — aligning wwstage up to
|
||||
// cstage, whose checker stamps the binding u8. Kind-gated so
|
||||
// str's own type stays nominal.
|
||||
if (sk == nkind.N_TNAME) {
|
||||
if (streq(slctn.str, "str")) {
|
||||
let sti: *tinfo = slctn.type_: *tinfo;
|
||||
if (sti != nil) {
|
||||
if (sti.sub != nil) {
|
||||
let u8n: *node = newnode(nkind.N_TNAME, slctn.file, slctn.line, slctn.col);
|
||||
u8n.str = "u8";
|
||||
u8n.type_ = sti.sub: *void;
|
||||
elemt = u8n;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
// esz: raw elem byte size. For tuple-element slices `[](T0, T1)`,
|
||||
// C cgen reads the resolved tuple's size (sum of raw param sizes,
|
||||
|
||||
Reference in New Issue
Block a user