selfhost/cmd/wcc/cgenstmt+test: emit slice-form alloc let-init shortcut

Cstage's cmd/w6c/cgen.c:6363-6411 special-cases `let s: []T =
alloc([], n)!;` to inline rt_alloc + null-check + exit(1) + slice
header build, avoiding a generic call-then-store path. Wwstage's
cglet had no mirror — pre-#31 the path was rejected at check, but
once #31 made the check side accept it, the cgen side would have
silently miscompiled. Mirror added at cgenstmt.ww cglet rhs head,
emitting byte-identical asm.

Element size goes through elemsizeofc so str (16), structs, and
tagged aliases all match cstage's lu->sub->size uniformly — the
defensive path matters because check today only allows []u8, but
relaxing that is its own task.

Test exercises the path: writes to s[0] and s[15], reads back. Would
SIGSEGV on a junk header. 994 + 995 byte-identity green.
This commit is contained in:
2026-05-20 00:28:27 +09:00
parent 6f10c832a4
commit 30a0856fe5
4 changed files with 276 additions and 0 deletions

View File

@@ -43,6 +43,25 @@ fn allocbox() (*point | nomem) = {
return p;
};
// Task #32: slice-form `let s: []T = alloc([], n)!;` shortcut. Both
// stages must lower to `n*esz` bytes via rt_alloc, abort on null, and
// build a {ptr, 0, n} header in the let slot. Pre-#32 wwstage fell
// through to cgalloc, allocating 8B and dropping the slice header
// entirely — silent miscompile. Cap-only would pass on a junk header
// pointing to dead memory; write-then-read on s[0]/s[cap-1] proves
// the ptr field is a real rt_alloc'd region (would SIGSEGV otherwise).
// IMULQ esz path is currently unreachable from user code — check.c
// pins the alloc shape to []u8 (cstage check.c:1052-1082) — so this
// row only exercises esz=1; the cgen elemsizeofc resolution stays
// defensive against a future check.c relaxation.
fn sliceshort() i32 = {
let s: []u8 = alloc([], 16)!;
if (s.cap != 16) { return -1i32; };
s[0] = 42u8;
s[15] = 99u8;
return (s[0]: i32) + (s[15]: i32);
};
export fn main() i32 = {
let rc: i32 = 0;
match (caller(0i64)) {
@@ -74,5 +93,7 @@ export fn main() i32 = {
rc = 5;
};
};
if (sliceshort() != 141i32) { rc = 6; }
else { fmt.println("sliceshort ok"); };
return rc;
};