selfhost: cgen for enum (member fold + as pass-through)
w6c_ww now compiles enum end-to-end and emits byte-identical
asm to the C w6c on the new 994 corpus case (`type mode = enum u8
{ R, W, RW = R | W }; main() { return (mode.RW): i32 }`). Mechanism
mirrors the C side:
- collectenums walks every `type X = enum {...}` at file scope and
pre-resolves each member's u64 value (auto-increment from prior,
sibling-ref folding for `RDWR = READ | WRITE`).
- cgdot recognises `EnumName.MEMBER` before the local lookup and
emits MOVQ $value, AX directly.
- cgtypeassert short-circuits when either side is enum: cgexpr on
the LHS lands the value in AX with the right integer width; no
tag/unwrap.
main.combined.ww (wwdump/ + w6c/) regenerated by ww build.
This commit is contained in:
@@ -251,7 +251,57 @@ fn cgtypetest(c: *cgen, n: *node) void = {
|
||||
return;
|
||||
};
|
||||
|
||||
// isenumexpr — does this expression's static type resolve to an enum?
|
||||
// Recognises enum-member access (`Foo.MEMBER`), enum-typed local
|
||||
// idents, and N_BIN whose either operand is enum (so `R | W` flows
|
||||
// through the cast pass-through too).
|
||||
fn isenumexpr(c: *cgen, e: *node) bool = {
|
||||
if (e == nil) { return false; };
|
||||
let k: i32 = e.kind;
|
||||
if (k == N_DOT) {
|
||||
if (e.lhs != nil) {
|
||||
if (e.lhs.kind == N_IDENT) {
|
||||
if (enumlookup(c, e.lhs.str) != nil) { return true; };
|
||||
};
|
||||
};
|
||||
};
|
||||
if (k == N_IDENT) {
|
||||
let lc: *local = localfindnode(c, e.str);
|
||||
if (lc != nil) {
|
||||
if (lc.tnode != nil) {
|
||||
if (lc.tnode.kind == N_TNAME) {
|
||||
if (enumlookup(c, lc.tnode.str) != nil) { return true; };
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
if (k == N_BIN) {
|
||||
if (isenumexpr(c, e.lhs)) { return true; };
|
||||
if (isenumexpr(c, e.rhs)) { return true; };
|
||||
};
|
||||
if (k == N_UN) {
|
||||
if (isenumexpr(c, e.lhs)) { return true; };
|
||||
};
|
||||
return false;
|
||||
};
|
||||
|
||||
fn isenumtype(c: *cgen, t: *node) bool = {
|
||||
if (t == nil) { return false; };
|
||||
if (t.kind == N_TENUM) { return true; };
|
||||
if (t.kind == N_TNAME) {
|
||||
if (enumlookup(c, t.str) != nil) { return true; };
|
||||
};
|
||||
return false;
|
||||
};
|
||||
|
||||
fn cgtypeassert(c: *cgen, n: *node) void = {
|
||||
// Enum ↔ integer: reinterpret-only. The LHS value already
|
||||
// occupies AX (or AX:BX for str variants, irrelevant here);
|
||||
// no tag/unwrap. Matches cmd/w6c/cgen.c's same short-circuit.
|
||||
if (isenumexpr(c, n.lhs) || isenumtype(c, n.rhs)) {
|
||||
cgexpr(c, n.lhs);
|
||||
return;
|
||||
};
|
||||
// `e as T` — load tag, abort (exit 1) if tag != T's variant
|
||||
// index, otherwise unwrap to T's ABI: scalar/ptr → AX, 16B
|
||||
// str → (AX, BX). Mirrors cgmatch's slot-based value load.
|
||||
@@ -565,6 +615,22 @@ fn cgmatch(c: *cgen, n: *node) void = {
|
||||
fn cgdot(c: *cgen, n: *node) void = {
|
||||
let lhs: *node = n.lhs;
|
||||
let fld: str = n.str;
|
||||
// Enum member access: `EnumName.MEMBER` → inline the constant.
|
||||
// Pre-computed at collectenums time; no value-expr fold here.
|
||||
if (lhs != nil) {
|
||||
if (lhs.kind == N_IDENT) {
|
||||
let en: *enumtype = enumlookup(c, lhs.str);
|
||||
if (en != nil) {
|
||||
let v: u64;
|
||||
if (enummemberval(en, fld, &v)) {
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(v: i64);
|
||||
emitline(", AX\n");
|
||||
return;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
if (lhs != nil) {
|
||||
if (lhs.kind == N_IDENT) {
|
||||
let nm: str = lhs.str;
|
||||
|
||||
Reference in New Issue
Block a user