wcc: str -> 24B {ptr,len,cap}, 3-reg ABI -- parity with []u8 (both stages)
A ww `str` becomes a 24-byte {ptr,len,cap} value, identical in layout to
[]u8 -- the enabling prerequisite for the Phase 2 `str == []u8` collapse.
Both stages, atomically:
- ty_str 16->24B; str value flows 3-reg AX/BX/CX (was 2-reg); str literals
emit cap (=len).
- str in a tagged union grows to a 32B slot, using the AX/DX/CX/R8 4th-word
path already used by 32B slice-variant unions -- str-variant is now
structurally identical.
- tuple (scalar,str) return: 4-reg AX/DX/CX/R8 + 32B receive, extending the
existing type-keyed return (no sret).
- str == []u8 for index and .ptr/.len/.cap, kind-gated where size-based
dispatch collided at 24B; cstage and wwstage mirror exactly.
- table-driven runtime coverage: test/wcc/928_str_abi_run.c.
Cannot be split (rule 10/11): a 24B str and a 16B str cannot coexist across
the two compiler stages without breaking byte-identity, so the size change
and every dependent ABI/codegen site land in one atomic commit, both stages.
Known follow-ups (zero corpus impact, tracked): str-literal global .cap
static-init; >16B struct by-value (pre-existing); tagged-union
match-scrutinee stage divergence (pre-existing).
This commit is contained in:
@@ -205,7 +205,20 @@ export fn typesinit(c: *tctx) void = {
|
||||
c.tyuintptr= prim(tykind.TY_UINTPTR, "uintptr", 8u64, 8u64);
|
||||
c.tyf32 = prim(tykind.TY_F32, "f32", 4u64, 4u64);
|
||||
c.tyf64 = prim(tykind.TY_F64, "f64", 8u64, 8u64);
|
||||
c.tystr = prim(tykind.TY_STR, "str", 16u64, 8u64); // sizelint-ok: SSoT for tystr (#64)
|
||||
// str IS []u8: { *u8, len, cap } — 24B, 3-reg ABI (#1/Phase 3).
|
||||
// Size sourced from a u8-slice's size (typeslice SSoT) so str and
|
||||
// []u8 can never drift; no second hardcoded 24. Mirrors cstage
|
||||
// type.c `type_slice(a, ty_u8)->size`.
|
||||
//
|
||||
// The slice tinfo MUST land in a local first: the inline form
|
||||
// `typeslice(c.tyu8).size` triggers a cgen bug — `call().field`
|
||||
// where the call returns a *pointer* emits no deref (it uses the
|
||||
// returned pointer AS the field value), so tystr.size would become
|
||||
// a heap address → runaway slot-size loops. Filed as task #6
|
||||
// (cstage cgen.c N_DOT base=N_CALL-returning-pointer + wwstage
|
||||
// cgdot mirror); retained here as a local until that lands.
|
||||
let u8slice: *tinfo = typeslice(c.tyu8);
|
||||
c.tystr = prim(tykind.TY_STR, "str", u8slice.size, 8u64);
|
||||
c.tyerr = prim(tykind.TY_ERR, "<err>", 0u64, 1u64);
|
||||
c.tynever = prim(tykind.TY_NEVER, "never", 0u64, 1u64);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user