wcc/cgenexpr: cgexpr node-kind if-ladder -> switch (Wave-2 structural)

23-arm top-level if (k == nkind.N_X) dispatch ladder becomes one
switch (k) with an empty-label default case for the AX=0 fallback.
N_RUNELIT stays a separate arm (no float check, unlike cstage's
INTLIT grouping). Not byte-id-neutral (if-chain -> switch); 990-997
cstage==wwstage byte-id is the functional-equivalence gate. w6c +
wwdump combined.ww regenerated.
This commit is contained in:
2026-06-03 00:42:01 +09:00
parent 711762b6d8
commit 2303341cb0
3 changed files with 93 additions and 141 deletions

View File

@@ -19774,7 +19774,8 @@ fn cgexpr(c: *cgen, n: *node) void = {
if (n == nil) { return; };
let k: nkind = n.kind;
if (k == nkind.N_INTLIT) {
switch (k) {
case nkind.N_INTLIT:
// A no-decimal `0f64`/`8f64` is an N_INTLIT carrying float
// TYPE; it must reach X0 like a true float literal, not the
// integer-immediate path (which strands it in AX and an SSE
@@ -19802,8 +19803,7 @@ fn cgexpr(c: *cgen, n: *node) void = {
emitint(n.uval: i64);
emitline(", AX\n");
return;
};
if (k == nkind.N_FLOATLIT) {
case nkind.N_FLOATLIT:
// The bits come from n.uval — the parser populates it from
// the lexer's bitcast of t.fval.
cgfloatbits(c, n.uval);
@@ -19812,71 +19812,55 @@ fn cgexpr(c: *cgen, n: *node) void = {
emitline("\tCVTSD2SS\tX0, X0\n");
};
return;
};
if (k == nkind.N_RUNELIT) {
case nkind.N_RUNELIT:
emitline("\tMOVQ\t$");
emitint(n.uval: i64);
emitline(", AX\n");
return;
};
if (k == nkind.N_STRLIT) { cgstrlit(c, n); return; };
if (k == nkind.N_TRUE) {
case nkind.N_STRLIT: cgstrlit(c, n); return;
case nkind.N_TRUE:
emitline("\tMOVQ\t$1, AX\n");
return;
};
if (k == nkind.N_FALSE) {
case nkind.N_FALSE:
emitline("\tMOVQ\t$0, AX\n");
return;
};
if (k == nkind.N_NIL) {
case nkind.N_NIL:
emitline("\tMOVQ\t$0, AX\n");
return;
};
if (k == nkind.N_VOIDLIT) {
case nkind.N_VOIDLIT:
// void value: zero-size, but the consumer's ABI expects a
// deterministic AX. Emit 0 like nil/false do.
emitline("\tMOVQ\t$0, AX\n");
return;
};
if (k == nkind.N_IDENT) { cgident(c, n); return; };
if (k == nkind.N_INDEX) { cgindex(c, n); return; };
if (k == nkind.N_SLICE) { cgslice(c, n); return; };
if (k == nkind.N_MATCH) { cgmatch(c, n); return; };
if (k == nkind.N_CAST) { cgcast(c, n); return; };
if (k == nkind.N_DOT) { cgdot(c, n); return; };
if (k == nkind.N_UN) { cgun(c, n); return; };
if (k == nkind.N_BIN) { cgbin(c, n); return; };
if (k == nkind.N_CALL) { cgcall(c, n); return; };
if (k == nkind.N_ASSIGN) { cgassign(c, n); return; };
if (k == nkind.N_TRYPROP) { cgtryprop(c, n); return; };
if (k == nkind.N_TRYUNW) { cgtryunw(c, n); return; };
if (k == nkind.N_TYPETEST) { cgtypetest(c, n); return; };
if (k == nkind.N_TYPEASSERT) { cgtypeassert(c, n); return; };
if (k == nkind.N_TUPLE) {
case nkind.N_IDENT: cgident(c, n); return;
case nkind.N_INDEX: cgindex(c, n); return;
case nkind.N_SLICE: cgslice(c, n); return;
case nkind.N_MATCH: cgmatch(c, n); return;
case nkind.N_CAST: cgcast(c, n); return;
case nkind.N_DOT: cgdot(c, n); return;
case nkind.N_UN: cgun(c, n); return;
case nkind.N_BIN: cgbin(c, n); return;
case nkind.N_CALL: cgcall(c, n); return;
case nkind.N_ASSIGN: cgassign(c, n); return;
case nkind.N_TRYPROP: cgtryprop(c, n); return;
case nkind.N_TRYUNW: cgtryunw(c, n); return;
case nkind.N_TYPETEST: cgtypetest(c, n); return;
case nkind.N_TYPEASSERT: cgtypeassert(c, n); return;
case nkind.N_TUPLE:
// #241: a literal tuple rvalue `(a, b)` is a value — pack its
// elements into the register cursor (mirror cgreturn's N_TUPLE
// arm) so a let-bind / destructure consumer reads every element,
// not just AX = 0 from the default arm below.
cgtuplelittocursor(c, n);
return;
case:
// Default fallback: produce a deterministic AX = 0. Mirrors
// the C cgen's `default: cgexpr_int(c, 0)` branch, which is
// what `return eof{};` (N_STRUCTLIT with an empty !void
// variant) silently relies on — without this AX carries a
// stale value into the tagged-union return shuffle.
emitline("\tMOVQ\t$0, AX\n");
};
// Default fallback: produce a deterministic AX = 0. Mirrors
// the C cgen's `default: cgexpr_int(c, 0)` branch, which is
// what `return eof{};` (N_STRUCTLIT with an empty !void
// variant) silently relies on — without this AX carries a
// stale value into the tagged-union return shuffle.
emitline("\tMOVQ\t$0, AX\n");
};
// cgtagvariantidx — find the 0-based variant index of `vt` inside the

View File

@@ -38,7 +38,8 @@ fn cgexpr(c: *cgen, n: *node) void = {
if (n == nil) { return; };
let k: nkind = n.kind;
if (k == nkind.N_INTLIT) {
switch (k) {
case nkind.N_INTLIT:
// A no-decimal `0f64`/`8f64` is an N_INTLIT carrying float
// TYPE; it must reach X0 like a true float literal, not the
// integer-immediate path (which strands it in AX and an SSE
@@ -66,8 +67,7 @@ fn cgexpr(c: *cgen, n: *node) void = {
emitint(n.uval: i64);
emitline(", AX\n");
return;
};
if (k == nkind.N_FLOATLIT) {
case nkind.N_FLOATLIT:
// The bits come from n.uval — the parser populates it from
// the lexer's bitcast of t.fval.
cgfloatbits(c, n.uval);
@@ -76,71 +76,55 @@ fn cgexpr(c: *cgen, n: *node) void = {
emitline("\tCVTSD2SS\tX0, X0\n");
};
return;
};
if (k == nkind.N_RUNELIT) {
case nkind.N_RUNELIT:
emitline("\tMOVQ\t$");
emitint(n.uval: i64);
emitline(", AX\n");
return;
};
if (k == nkind.N_STRLIT) { cgstrlit(c, n); return; };
if (k == nkind.N_TRUE) {
case nkind.N_STRLIT: cgstrlit(c, n); return;
case nkind.N_TRUE:
emitline("\tMOVQ\t$1, AX\n");
return;
};
if (k == nkind.N_FALSE) {
case nkind.N_FALSE:
emitline("\tMOVQ\t$0, AX\n");
return;
};
if (k == nkind.N_NIL) {
case nkind.N_NIL:
emitline("\tMOVQ\t$0, AX\n");
return;
};
if (k == nkind.N_VOIDLIT) {
case nkind.N_VOIDLIT:
// void value: zero-size, but the consumer's ABI expects a
// deterministic AX. Emit 0 like nil/false do.
emitline("\tMOVQ\t$0, AX\n");
return;
};
if (k == nkind.N_IDENT) { cgident(c, n); return; };
if (k == nkind.N_INDEX) { cgindex(c, n); return; };
if (k == nkind.N_SLICE) { cgslice(c, n); return; };
if (k == nkind.N_MATCH) { cgmatch(c, n); return; };
if (k == nkind.N_CAST) { cgcast(c, n); return; };
if (k == nkind.N_DOT) { cgdot(c, n); return; };
if (k == nkind.N_UN) { cgun(c, n); return; };
if (k == nkind.N_BIN) { cgbin(c, n); return; };
if (k == nkind.N_CALL) { cgcall(c, n); return; };
if (k == nkind.N_ASSIGN) { cgassign(c, n); return; };
if (k == nkind.N_TRYPROP) { cgtryprop(c, n); return; };
if (k == nkind.N_TRYUNW) { cgtryunw(c, n); return; };
if (k == nkind.N_TYPETEST) { cgtypetest(c, n); return; };
if (k == nkind.N_TYPEASSERT) { cgtypeassert(c, n); return; };
if (k == nkind.N_TUPLE) {
case nkind.N_IDENT: cgident(c, n); return;
case nkind.N_INDEX: cgindex(c, n); return;
case nkind.N_SLICE: cgslice(c, n); return;
case nkind.N_MATCH: cgmatch(c, n); return;
case nkind.N_CAST: cgcast(c, n); return;
case nkind.N_DOT: cgdot(c, n); return;
case nkind.N_UN: cgun(c, n); return;
case nkind.N_BIN: cgbin(c, n); return;
case nkind.N_CALL: cgcall(c, n); return;
case nkind.N_ASSIGN: cgassign(c, n); return;
case nkind.N_TRYPROP: cgtryprop(c, n); return;
case nkind.N_TRYUNW: cgtryunw(c, n); return;
case nkind.N_TYPETEST: cgtypetest(c, n); return;
case nkind.N_TYPEASSERT: cgtypeassert(c, n); return;
case nkind.N_TUPLE:
// #241: a literal tuple rvalue `(a, b)` is a value — pack its
// elements into the register cursor (mirror cgreturn's N_TUPLE
// arm) so a let-bind / destructure consumer reads every element,
// not just AX = 0 from the default arm below.
cgtuplelittocursor(c, n);
return;
case:
// Default fallback: produce a deterministic AX = 0. Mirrors
// the C cgen's `default: cgexpr_int(c, 0)` branch, which is
// what `return eof{};` (N_STRUCTLIT with an empty !void
// variant) silently relies on — without this AX carries a
// stale value into the tagged-union return shuffle.
emitline("\tMOVQ\t$0, AX\n");
};
// Default fallback: produce a deterministic AX = 0. Mirrors
// the C cgen's `default: cgexpr_int(c, 0)` branch, which is
// what `return eof{};` (N_STRUCTLIT with an empty !void
// variant) silently relies on — without this AX carries a
// stale value into the tagged-union return shuffle.
emitline("\tMOVQ\t$0, AX\n");
};
// cgtagvariantidx — find the 0-based variant index of `vt` inside the

View File

@@ -19774,7 +19774,8 @@ fn cgexpr(c: *cgen, n: *node) void = {
if (n == nil) { return; };
let k: nkind = n.kind;
if (k == nkind.N_INTLIT) {
switch (k) {
case nkind.N_INTLIT:
// A no-decimal `0f64`/`8f64` is an N_INTLIT carrying float
// TYPE; it must reach X0 like a true float literal, not the
// integer-immediate path (which strands it in AX and an SSE
@@ -19802,8 +19803,7 @@ fn cgexpr(c: *cgen, n: *node) void = {
emitint(n.uval: i64);
emitline(", AX\n");
return;
};
if (k == nkind.N_FLOATLIT) {
case nkind.N_FLOATLIT:
// The bits come from n.uval — the parser populates it from
// the lexer's bitcast of t.fval.
cgfloatbits(c, n.uval);
@@ -19812,71 +19812,55 @@ fn cgexpr(c: *cgen, n: *node) void = {
emitline("\tCVTSD2SS\tX0, X0\n");
};
return;
};
if (k == nkind.N_RUNELIT) {
case nkind.N_RUNELIT:
emitline("\tMOVQ\t$");
emitint(n.uval: i64);
emitline(", AX\n");
return;
};
if (k == nkind.N_STRLIT) { cgstrlit(c, n); return; };
if (k == nkind.N_TRUE) {
case nkind.N_STRLIT: cgstrlit(c, n); return;
case nkind.N_TRUE:
emitline("\tMOVQ\t$1, AX\n");
return;
};
if (k == nkind.N_FALSE) {
case nkind.N_FALSE:
emitline("\tMOVQ\t$0, AX\n");
return;
};
if (k == nkind.N_NIL) {
case nkind.N_NIL:
emitline("\tMOVQ\t$0, AX\n");
return;
};
if (k == nkind.N_VOIDLIT) {
case nkind.N_VOIDLIT:
// void value: zero-size, but the consumer's ABI expects a
// deterministic AX. Emit 0 like nil/false do.
emitline("\tMOVQ\t$0, AX\n");
return;
};
if (k == nkind.N_IDENT) { cgident(c, n); return; };
if (k == nkind.N_INDEX) { cgindex(c, n); return; };
if (k == nkind.N_SLICE) { cgslice(c, n); return; };
if (k == nkind.N_MATCH) { cgmatch(c, n); return; };
if (k == nkind.N_CAST) { cgcast(c, n); return; };
if (k == nkind.N_DOT) { cgdot(c, n); return; };
if (k == nkind.N_UN) { cgun(c, n); return; };
if (k == nkind.N_BIN) { cgbin(c, n); return; };
if (k == nkind.N_CALL) { cgcall(c, n); return; };
if (k == nkind.N_ASSIGN) { cgassign(c, n); return; };
if (k == nkind.N_TRYPROP) { cgtryprop(c, n); return; };
if (k == nkind.N_TRYUNW) { cgtryunw(c, n); return; };
if (k == nkind.N_TYPETEST) { cgtypetest(c, n); return; };
if (k == nkind.N_TYPEASSERT) { cgtypeassert(c, n); return; };
if (k == nkind.N_TUPLE) {
case nkind.N_IDENT: cgident(c, n); return;
case nkind.N_INDEX: cgindex(c, n); return;
case nkind.N_SLICE: cgslice(c, n); return;
case nkind.N_MATCH: cgmatch(c, n); return;
case nkind.N_CAST: cgcast(c, n); return;
case nkind.N_DOT: cgdot(c, n); return;
case nkind.N_UN: cgun(c, n); return;
case nkind.N_BIN: cgbin(c, n); return;
case nkind.N_CALL: cgcall(c, n); return;
case nkind.N_ASSIGN: cgassign(c, n); return;
case nkind.N_TRYPROP: cgtryprop(c, n); return;
case nkind.N_TRYUNW: cgtryunw(c, n); return;
case nkind.N_TYPETEST: cgtypetest(c, n); return;
case nkind.N_TYPEASSERT: cgtypeassert(c, n); return;
case nkind.N_TUPLE:
// #241: a literal tuple rvalue `(a, b)` is a value — pack its
// elements into the register cursor (mirror cgreturn's N_TUPLE
// arm) so a let-bind / destructure consumer reads every element,
// not just AX = 0 from the default arm below.
cgtuplelittocursor(c, n);
return;
case:
// Default fallback: produce a deterministic AX = 0. Mirrors
// the C cgen's `default: cgexpr_int(c, 0)` branch, which is
// what `return eof{};` (N_STRUCTLIT with an empty !void
// variant) silently relies on — without this AX carries a
// stale value into the tagged-union return shuffle.
emitline("\tMOVQ\t$0, AX\n");
};
// Default fallback: produce a deterministic AX = 0. Mirrors
// the C cgen's `default: cgexpr_int(c, 0)` branch, which is
// what `return eof{};` (N_STRUCTLIT with an empty !void
// variant) silently relies on — without this AX carries a
// stale value into the tagged-union return shuffle.
emitline("\tMOVQ\t$0, AX\n");
};
// cgtagvariantidx — find the 0-based variant index of `vt` inside the