cstage+selfhost+test: fold unary-over-literal in def DATA emit (#24)
Top-level `def NEG: i32 = -100;` skipped DATA emission in both stages — cstage's emit_defs and wwstage's emitdefconstants each carried a literal-leaf whitelist that excluded N_UN nodes. Same gap in check.c's eval_enum_value cstage-side. Surfaced during #10 (lib/os forced an `at` enum for AT_FDCWD=-100 etc. as workaround). Factor a single fold_int_literal helper (cstage check.c; wwstage cgen.ww). Handles N_INTLIT / N_RUNELIT / N_TRUE / N_FALSE / N_NIL plus N_UN with TK_MINUS / TK_TILDE / TK_PLUS recursively. Consume from eval_enum_value, emit_defs, emitdefconstants, enumevalmember — single source of truth for "is this a literal-leaf foldable". Side effect: cstage's def-emit set widens from {INTLIT, RUNELIT, TRUE} to match wwstage's pre-existing 5-shape set plus the new unary peel. Bootstrap byte-id holds (995_self_rebuild green). Test 631 (def_neg_global): 6 rows × cstage/wwstage run + asm byte-identity diff. Covers all three unary arms (-, ~, +), positive regression-pin, i32 + i64 + u32 slots. Follows up #26: revert lib/os.ww `at` enum to three top-level defs.
This commit is contained in:
@@ -16933,13 +16933,39 @@ fn aliaslookup(c: *cgen, name: str) *node = {
|
||||
// member's u64 value (supporting auto-increment and sibling refs),
|
||||
// and stash them so cgdot can fold `Foo.MEMBER` → MOVQ $value, AX.
|
||||
|
||||
fn enumevalmember(prev: *enummember, e: *node, out: *u64) bool = {
|
||||
// foldintliteral — fold the literal subset usable for top-level
|
||||
// constant slots: int/rune literal, true/false/nil, and a unary
|
||||
// +/-/~ over the same (any depth). No sibling-ident, no binary op.
|
||||
// Shared between enumevalmember (literal leaves) and
|
||||
// emitdefconstants (top-level def rhs).
|
||||
//
|
||||
// Whitelist kept tight on purpose: anything richer (sibling refs,
|
||||
// arithmetic) belongs in enumevalmember, which calls this for its
|
||||
// literal leaves and handles the rest itself.
|
||||
fn foldintliteral(e: *node, out: *u64) bool = {
|
||||
if (e == nil) { return false; };
|
||||
let k: nkind = e.kind;
|
||||
if (k == nkind.N_INTLIT) { *out = e.uval; return true; };
|
||||
if (k == nkind.N_RUNELIT) { *out = e.uval; return true; };
|
||||
if (k == nkind.N_TRUE) { *out = 1u64; return true; };
|
||||
if (k == nkind.N_FALSE) { *out = 0u64; return true; };
|
||||
if (k == nkind.N_NIL) { *out = 0u64; return true; };
|
||||
if (k == nkind.N_UN) {
|
||||
let v: u64;
|
||||
if (!foldintliteral(e.lhs, &v)) { return false; };
|
||||
let op: tkind = e.op;
|
||||
if (op == tkind.TK_MINUS) { *out = (-(v: i64)): u64; return true; };
|
||||
if (op == tkind.TK_TILDE) { *out = ~v; return true; };
|
||||
if (op == tkind.TK_PLUS) { *out = v; return true; };
|
||||
return false;
|
||||
};
|
||||
return false;
|
||||
};
|
||||
|
||||
fn enumevalmember(prev: *enummember, e: *node, out: *u64) bool = {
|
||||
if (e == nil) { return false; };
|
||||
if (foldintliteral(e, out)) { return true; };
|
||||
let k: nkind = e.kind;
|
||||
if (k == nkind.N_IDENT) {
|
||||
let m: *enummember = prev;
|
||||
for (m != nil) {
|
||||
@@ -18065,8 +18091,12 @@ fn emitletdataw(c: *cgen, file: *node) void = {
|
||||
};
|
||||
};
|
||||
|
||||
// emitdefconstants — DATA directive per top-level int-literal `def`.
|
||||
// 8 bytes little-endian to match what the C cgen emits.
|
||||
// emitdefconstants — DATA directive per top-level fold-to-literal
|
||||
// `def`. 8 bytes little-endian to match what the C cgen emits.
|
||||
// foldintliteral gates: int/rune literal, true/false/nil, and a
|
||||
// unary +/-/~ over the same. `def NEG: i32 = -100;` arrives as
|
||||
// N_UN(TK_MINUS, N_INTLIT) — the unary peel is exactly what the
|
||||
// gate is for.
|
||||
fn emitdefconstants(c: *cgen, file: *node) void = {
|
||||
let d: *node = file.list;
|
||||
for (d != nil) {
|
||||
@@ -18075,11 +18105,7 @@ fn emitdefconstants(c: *cgen, file: *node) void = {
|
||||
let v: u64 = 0u64;
|
||||
let ok: bool = false;
|
||||
if (r != nil) {
|
||||
if (r.kind == nkind.N_INTLIT) { v = r.uval; ok = true; };
|
||||
if (r.kind == nkind.N_RUNELIT) { v = r.uval; ok = true; };
|
||||
if (r.kind == nkind.N_TRUE) { v = 1u64; ok = true; };
|
||||
if (r.kind == nkind.N_FALSE) { v = 0u64; ok = true; };
|
||||
if (r.kind == nkind.N_NIL) { v = 0u64; ok = true; };
|
||||
ok = foldintliteral(r, &v);
|
||||
};
|
||||
if (ok) {
|
||||
emitline("DATA ");
|
||||
|
||||
@@ -117,13 +117,39 @@ fn aliaslookup(c: *cgen, name: str) *node = {
|
||||
// member's u64 value (supporting auto-increment and sibling refs),
|
||||
// and stash them so cgdot can fold `Foo.MEMBER` → MOVQ $value, AX.
|
||||
|
||||
fn enumevalmember(prev: *enummember, e: *node, out: *u64) bool = {
|
||||
// foldintliteral — fold the literal subset usable for top-level
|
||||
// constant slots: int/rune literal, true/false/nil, and a unary
|
||||
// +/-/~ over the same (any depth). No sibling-ident, no binary op.
|
||||
// Shared between enumevalmember (literal leaves) and
|
||||
// emitdefconstants (top-level def rhs).
|
||||
//
|
||||
// Whitelist kept tight on purpose: anything richer (sibling refs,
|
||||
// arithmetic) belongs in enumevalmember, which calls this for its
|
||||
// literal leaves and handles the rest itself.
|
||||
fn foldintliteral(e: *node, out: *u64) bool = {
|
||||
if (e == nil) { return false; };
|
||||
let k: nkind = e.kind;
|
||||
if (k == nkind.N_INTLIT) { *out = e.uval; return true; };
|
||||
if (k == nkind.N_RUNELIT) { *out = e.uval; return true; };
|
||||
if (k == nkind.N_TRUE) { *out = 1u64; return true; };
|
||||
if (k == nkind.N_FALSE) { *out = 0u64; return true; };
|
||||
if (k == nkind.N_NIL) { *out = 0u64; return true; };
|
||||
if (k == nkind.N_UN) {
|
||||
let v: u64;
|
||||
if (!foldintliteral(e.lhs, &v)) { return false; };
|
||||
let op: tkind = e.op;
|
||||
if (op == tkind.TK_MINUS) { *out = (-(v: i64)): u64; return true; };
|
||||
if (op == tkind.TK_TILDE) { *out = ~v; return true; };
|
||||
if (op == tkind.TK_PLUS) { *out = v; return true; };
|
||||
return false;
|
||||
};
|
||||
return false;
|
||||
};
|
||||
|
||||
fn enumevalmember(prev: *enummember, e: *node, out: *u64) bool = {
|
||||
if (e == nil) { return false; };
|
||||
if (foldintliteral(e, out)) { return true; };
|
||||
let k: nkind = e.kind;
|
||||
if (k == nkind.N_IDENT) {
|
||||
let m: *enummember = prev;
|
||||
for (m != nil) {
|
||||
@@ -1249,8 +1275,12 @@ fn emitletdataw(c: *cgen, file: *node) void = {
|
||||
};
|
||||
};
|
||||
|
||||
// emitdefconstants — DATA directive per top-level int-literal `def`.
|
||||
// 8 bytes little-endian to match what the C cgen emits.
|
||||
// emitdefconstants — DATA directive per top-level fold-to-literal
|
||||
// `def`. 8 bytes little-endian to match what the C cgen emits.
|
||||
// foldintliteral gates: int/rune literal, true/false/nil, and a
|
||||
// unary +/-/~ over the same. `def NEG: i32 = -100;` arrives as
|
||||
// N_UN(TK_MINUS, N_INTLIT) — the unary peel is exactly what the
|
||||
// gate is for.
|
||||
fn emitdefconstants(c: *cgen, file: *node) void = {
|
||||
let d: *node = file.list;
|
||||
for (d != nil) {
|
||||
@@ -1259,11 +1289,7 @@ fn emitdefconstants(c: *cgen, file: *node) void = {
|
||||
let v: u64 = 0u64;
|
||||
let ok: bool = false;
|
||||
if (r != nil) {
|
||||
if (r.kind == nkind.N_INTLIT) { v = r.uval; ok = true; };
|
||||
if (r.kind == nkind.N_RUNELIT) { v = r.uval; ok = true; };
|
||||
if (r.kind == nkind.N_TRUE) { v = 1u64; ok = true; };
|
||||
if (r.kind == nkind.N_FALSE) { v = 0u64; ok = true; };
|
||||
if (r.kind == nkind.N_NIL) { v = 0u64; ok = true; };
|
||||
ok = foldintliteral(r, &v);
|
||||
};
|
||||
if (ok) {
|
||||
emitline("DATA ");
|
||||
|
||||
@@ -16933,13 +16933,39 @@ fn aliaslookup(c: *cgen, name: str) *node = {
|
||||
// member's u64 value (supporting auto-increment and sibling refs),
|
||||
// and stash them so cgdot can fold `Foo.MEMBER` → MOVQ $value, AX.
|
||||
|
||||
fn enumevalmember(prev: *enummember, e: *node, out: *u64) bool = {
|
||||
// foldintliteral — fold the literal subset usable for top-level
|
||||
// constant slots: int/rune literal, true/false/nil, and a unary
|
||||
// +/-/~ over the same (any depth). No sibling-ident, no binary op.
|
||||
// Shared between enumevalmember (literal leaves) and
|
||||
// emitdefconstants (top-level def rhs).
|
||||
//
|
||||
// Whitelist kept tight on purpose: anything richer (sibling refs,
|
||||
// arithmetic) belongs in enumevalmember, which calls this for its
|
||||
// literal leaves and handles the rest itself.
|
||||
fn foldintliteral(e: *node, out: *u64) bool = {
|
||||
if (e == nil) { return false; };
|
||||
let k: nkind = e.kind;
|
||||
if (k == nkind.N_INTLIT) { *out = e.uval; return true; };
|
||||
if (k == nkind.N_RUNELIT) { *out = e.uval; return true; };
|
||||
if (k == nkind.N_TRUE) { *out = 1u64; return true; };
|
||||
if (k == nkind.N_FALSE) { *out = 0u64; return true; };
|
||||
if (k == nkind.N_NIL) { *out = 0u64; return true; };
|
||||
if (k == nkind.N_UN) {
|
||||
let v: u64;
|
||||
if (!foldintliteral(e.lhs, &v)) { return false; };
|
||||
let op: tkind = e.op;
|
||||
if (op == tkind.TK_MINUS) { *out = (-(v: i64)): u64; return true; };
|
||||
if (op == tkind.TK_TILDE) { *out = ~v; return true; };
|
||||
if (op == tkind.TK_PLUS) { *out = v; return true; };
|
||||
return false;
|
||||
};
|
||||
return false;
|
||||
};
|
||||
|
||||
fn enumevalmember(prev: *enummember, e: *node, out: *u64) bool = {
|
||||
if (e == nil) { return false; };
|
||||
if (foldintliteral(e, out)) { return true; };
|
||||
let k: nkind = e.kind;
|
||||
if (k == nkind.N_IDENT) {
|
||||
let m: *enummember = prev;
|
||||
for (m != nil) {
|
||||
@@ -18065,8 +18091,12 @@ fn emitletdataw(c: *cgen, file: *node) void = {
|
||||
};
|
||||
};
|
||||
|
||||
// emitdefconstants — DATA directive per top-level int-literal `def`.
|
||||
// 8 bytes little-endian to match what the C cgen emits.
|
||||
// emitdefconstants — DATA directive per top-level fold-to-literal
|
||||
// `def`. 8 bytes little-endian to match what the C cgen emits.
|
||||
// foldintliteral gates: int/rune literal, true/false/nil, and a
|
||||
// unary +/-/~ over the same. `def NEG: i32 = -100;` arrives as
|
||||
// N_UN(TK_MINUS, N_INTLIT) — the unary peel is exactly what the
|
||||
// gate is for.
|
||||
fn emitdefconstants(c: *cgen, file: *node) void = {
|
||||
let d: *node = file.list;
|
||||
for (d != nil) {
|
||||
@@ -18075,11 +18105,7 @@ fn emitdefconstants(c: *cgen, file: *node) void = {
|
||||
let v: u64 = 0u64;
|
||||
let ok: bool = false;
|
||||
if (r != nil) {
|
||||
if (r.kind == nkind.N_INTLIT) { v = r.uval; ok = true; };
|
||||
if (r.kind == nkind.N_RUNELIT) { v = r.uval; ok = true; };
|
||||
if (r.kind == nkind.N_TRUE) { v = 1u64; ok = true; };
|
||||
if (r.kind == nkind.N_FALSE) { v = 0u64; ok = true; };
|
||||
if (r.kind == nkind.N_NIL) { v = 0u64; ok = true; };
|
||||
ok = foldintliteral(r, &v);
|
||||
};
|
||||
if (ok) {
|
||||
emitline("DATA ");
|
||||
|
||||
Reference in New Issue
Block a user