wcc/cgen: zero-init sub-8-byte bare lets, both stages; bytes test honest (#16-team)
A bare 'let x: T;' with 1 <= size(T) <= 7 matched no zero-fill arm in either stage (8B and >8B were already zeroed) - 'let c: [3]u8;' read stack garbage. User-ruled zero-value semantics: cstage gate sz>8 -> sz>0; wwstage zsz==8 arm hoisted above the fill-run arm (required - 8B would otherwise route into the run and diverge) and run gate zsz>0. New 840 pin: dirty-frame probe rows, dual-dim (run + cs/ww byte-id); discriminators fail exit-154 on pre-fix binaries. Fused with the lib/bytes test conversion (rule 11): either half alone turns 967 red. The old exit(signalled+10) wrapped a real 1782-count ltrim failure to exit 0 - green depended on the garbage. Converted to assert form (completes the 35/35 @test conversion); ltrim rows keep the bare 'let c: [3]u8;' as the consumer proof of the fix.
This commit is contained in:
@@ -12896,11 +12896,11 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
|
||||
}
|
||||
} else if (sz == 8) {
|
||||
ins2(c, A_MOVQ, aimm(0), amem(D_BP, off));
|
||||
} else if (!n->rhs && sz > 8) {
|
||||
/* `let x: T;` with no rhs for a multi-word composite
|
||||
* (str/slice/tuple/struct/tagged/ARRAY). Zero the slot so
|
||||
* reads after the bare let see {0...} rather than
|
||||
* whatever the stack already held.
|
||||
} else if (!n->rhs && sz > 0) {
|
||||
/* `let x: T;` with no rhs for a composite (str/slice/
|
||||
* tuple/struct/tagged/ARRAY). Zero the slot so reads after
|
||||
* the bare let see {0...} rather than whatever the stack
|
||||
* already held.
|
||||
*
|
||||
* #84: arrays were excluded here (`!TY_ARRAY`), so a
|
||||
* dirtied-stack `let a: [3]int;` read garbage — BOTH
|
||||
@@ -12909,7 +12909,17 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
|
||||
* composite. Extent is lu->size (chased ABI size, rule-13
|
||||
* — never a hardcoded count×elemsize). The unrolled
|
||||
* word/dword/byte run mirrors the composite path; the
|
||||
* largest real local array ([256]u8) is 32 MOVQs. */
|
||||
* largest real local array ([256]u8) is 32 MOVQs.
|
||||
*
|
||||
* #16: the gate was `sz > 8`, so a SUB-8 aggregate
|
||||
* (`let c: [3]u8;` = 3, a 3-byte struct, etc.) matched
|
||||
* neither this arm nor the `sz == 8` MOVQ-$0 arm above and
|
||||
* fell through to NOTHING — the exact stack-garbage read
|
||||
* ken's bytes verdict pinpointed (ltrim_cases' `let c:
|
||||
* [3]u8;`). Widening to `sz > 0` routes 1..7-byte slots
|
||||
* through the same MOVL/MOVB tail; the run already sizes
|
||||
* itself to any extent. (sz == 8 stays on the immediate
|
||||
* MOVQ $0 above; sz == 0 — `[0]T` — needs no stores.) */
|
||||
ins2(c, A_XORQ, areg(D_AX), areg(D_AX));
|
||||
int zi = 0;
|
||||
while (zi + 8 <= sz) {
|
||||
@@ -12928,7 +12938,6 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
|
||||
zi += 1;
|
||||
}
|
||||
}
|
||||
/* arrays left uninitialised — caller writes via index */
|
||||
letlink:
|
||||
/* #152: link the binding into the lookup chain AFTER its
|
||||
* initializer emits, so a self-shadowing init (`let x =
|
||||
|
||||
Reference in New Issue
Block a user