selfhost: cgfn pre-scan SysV-class accounting (closes #7)

Mirrors cgen.c §5130-5223 / cgfnparams: reg-spill, tagged-partial-fit,
pure-stack. Pure-stack does not bump cursor (cstage semantics). Fixes
16B over-allocation on 7+ arg functions; bootstrap stays byte-identical.

Slice/str at reg/stack straddle is deferred to task #11 (cgfnparams
doesn't stitch them either); pre-scan stays symmetric until then.
This commit is contained in:
2026-05-13 22:40:43 +09:00
parent c4b3aca5e4
commit 5c8724845a
5 changed files with 408 additions and 37 deletions

View File

@@ -13680,22 +13680,65 @@ fn cgfn(c: *cgen, fn_: *node) void = {
os.write(1, nm.ptr, nm.len: u64);
emitline(",$");
// Pre-scan total frame: 24 bytes per slice param, 16 per str
// param, 8 per other param, plus per-let from scanlocals.
// Seed c.locals with param-name stubs so scanlocals dedups a
// re-declared `let <name>` in the body against the param's
// slot (matches C cgen). Stubs get cleared before emission.
// Pre-scan total frame: only count params that land in a local
// slot. SysV-class accounting; mirrors runtime walk in cstage
// cgen.c §5130-5223 and cgfnparams below. A stack-spilled param
// is addressed at a positive BP offset by cgfnparams (via
// localaddstack) and consumes no frame, so adding its size here
// would over-allocate. Seed c.locals with param-name stubs so
// scanlocals dedups a re-declared `let <name>` in the body
// against the param's slot (matches C cgen). Stubs get cleared
// before emission.
let scanp: *node = fn_.list;
let frame: i32 = 0;
let argi: i32 = 0;
let fargi: i32 = 0;
for (scanp != nil) {
if (scanp.kind == nkind.N_PARAM) {
// Hare-style variadic `T...`: param is []T inside
// the callee, so it occupies a 24B slice slot.
if (scanp.op == tkind.TK_ELLIPSIS) { frame += 24; }
else { if (istaggedtype(c, scanp.lhs)) { frame += slotsize(c, scanp.lhs); }
else { if (isslicetype(c, scanp.lhs)) { frame += 24; }
else { if (isstrtype(c, scanp.lhs)) { frame += 16; }
else { frame += 8; }; }; }; };
let isvar: bool = scanp.op == tkind.TK_ELLIPSIS;
let isf: bool = false;
let istg: bool = false;
let issl: bool = false;
let isst: bool = false;
if (!isvar) {
isf = isfloattype(c, scanp.lhs);
istg = istaggedtype(c, scanp.lhs);
if (!isf && !istg) {
issl = isslicetype(c, scanp.lhs);
if (!issl) { isst = isstrtype(c, scanp.lhs); };
};
};
let eb: i32 = 1;
let sz: i32 = 8;
if (isvar) { eb = 3; sz = 24; }
else { if (istg) { sz = slotsize(c, scanp.lhs); eb = sz / 8; }
else { if (issl) { eb = 3; sz = 24; }
else { if (isst) { eb = 2; sz = 16; }
else { if (isf) {
eb = 1;
sz = 8;
if (isf32type(c, scanp.lhs)) { sz = 4; };
}; }; }; }; };
let regs_left: i32 = 6 - argi;
if (isf) { regs_left = 8 - fargi; };
if (regs_left >= eb) {
frame += sz;
if (isf) { fargi += 1; }
else { argi += eb; };
} else { if (eb > 1 && regs_left > 0 && istg) {
// Tagged param straddles the reg/stack boundary;
// cgfnparams stitches the tail from positive BP
// offsets into a single local slot, so we still
// reserve the full size. Slice/str at the same
// straddle are *not* stitched by cgfnparams today
// (task #11) — once that's fixed the predicate
// here must widen symmetrically.
frame += sz;
argi = 6;
} else {
// Pure stack: lives at +BP(16+stkcursor*8); no
// local slot consumed. The reg cursor stays put.
}; };
scanseenmark(c, scanp.str);
};
scanp = scanp.next;