wcc/cgen: extract per-kind helpers from cgexpr/cgstmt (rob pike #5)
This commit is contained in:
@@ -4506,9 +4506,14 @@ fn taggedvariantindex(c: *cgen, tagged: *node, rhs: *node) i32 = {
|
|||||||
// MODULE: wcc
|
// MODULE: wcc
|
||||||
// selfhost/cmd/wcc/cgen_expr.ww — split out of cgen.ww.
|
// selfhost/cmd/wcc/cgen_expr.ww — split out of cgen.ww.
|
||||||
//
|
//
|
||||||
// Houses cgexpr — the per-expression code generator. The remainder of
|
// cgexpr is a thin dispatcher over n.kind; each non-trivial branch
|
||||||
// the cgen lives in cgen.ww (foundation: types, emit primitives, the
|
// lives in a per-kind helper (cgstrlit, cgident, cgindex, cgmatch,
|
||||||
// collect* tables, FFI/module maps) and cgen_stmt.ww (cgstmt).
|
// cgdot, cgun, cgbin, cgcall, cgassign). Trivial literal loads
|
||||||
|
// (N_INTLIT, N_RUNELIT, N_TRUE/FALSE/NIL, N_CAST) stay inline.
|
||||||
|
//
|
||||||
|
// The remainder of cgen lives in cgen.ww (foundation: types, emit
|
||||||
|
// primitives, the collect* tables, FFI/module maps) and cgen_stmt.ww
|
||||||
|
// (cgstmt).
|
||||||
//
|
//
|
||||||
// `use cgen_expr;` is unnecessary at consumer sites — cgen.ww imports
|
// `use cgen_expr;` is unnecessary at consumer sites — cgen.ww imports
|
||||||
// this file, so any caller of cgen transitively gets cgexpr.
|
// this file, so any caller of cgen transitively gets cgexpr.
|
||||||
@@ -4541,19 +4546,7 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
|||||||
emitline(", AX\n");
|
emitline(", AX\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (k == N_STRLIT) {
|
if (k == N_STRLIT) { cgstrlit(c, n); return; };
|
||||||
// Result is the (ptr, len) pair: ptr in AX, len in BX. Call
|
|
||||||
// sites that expect a str arg pick these up directly.
|
|
||||||
let nstr: str = n.str;
|
|
||||||
let lab: str = internstrlit(c, nstr);
|
|
||||||
emitline("\tLEAQ\t");
|
|
||||||
os.write(1, lab.ptr, lab.len: u64);
|
|
||||||
emitline("(SB), AX\n");
|
|
||||||
emitline("\tMOVQ\t$");
|
|
||||||
emitint(nstr.len: i64);
|
|
||||||
emitline(", BX\n");
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
if (k == N_TRUE) {
|
if (k == N_TRUE) {
|
||||||
emitline("\tMOVQ\t$1, AX\n");
|
emitline("\tMOVQ\t$1, AX\n");
|
||||||
return;
|
return;
|
||||||
@@ -4567,7 +4560,48 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (k == N_IDENT) {
|
if (k == N_IDENT) { cgident(c, n); return; };
|
||||||
|
|
||||||
|
if (k == N_INDEX) { cgindex(c, n); return; };
|
||||||
|
|
||||||
|
if (k == N_MATCH) { cgmatch(c, n); return; };
|
||||||
|
|
||||||
|
if (k == N_CAST) {
|
||||||
|
// Type casts are mostly no-ops at the asm level for our
|
||||||
|
// integer-shaped operands. Evaluate the source; AX holds
|
||||||
|
// the bits unchanged. (Sign- or zero-extending narrow loads
|
||||||
|
// to wider types is the loader's job, not cast's, in this
|
||||||
|
// minimal cgen.)
|
||||||
|
cgexpr(c, n.lhs);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (k == N_DOT) { cgdot(c, n); return; };
|
||||||
|
|
||||||
|
if (k == N_UN) { cgun(c, n); return; };
|
||||||
|
|
||||||
|
if (k == N_BIN) { cgbin(c, n); return; };
|
||||||
|
|
||||||
|
if (k == N_CALL) { cgcall(c, n); return; };
|
||||||
|
|
||||||
|
if (k == N_ASSIGN) { cgassign(c, n); return; };
|
||||||
|
};
|
||||||
|
|
||||||
|
fn cgstrlit(c: *cgen, n: *node) void = {
|
||||||
|
// Result is the (ptr, len) pair: ptr in AX, len in BX. Call
|
||||||
|
// sites that expect a str arg pick these up directly.
|
||||||
|
let nstr: str = n.str;
|
||||||
|
let lab: str = internstrlit(c, nstr);
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
os.write(1, lab.ptr, lab.len: u64);
|
||||||
|
emitline("(SB), AX\n");
|
||||||
|
emitline("\tMOVQ\t$");
|
||||||
|
emitint(nstr.len: i64);
|
||||||
|
emitline(", BX\n");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
fn cgident(c: *cgen, n: *node) void = {
|
||||||
let nm: str = n.str;
|
let nm: str = n.str;
|
||||||
let lc: *local = localfindnode(c, nm);
|
let lc: *local = localfindnode(c, nm);
|
||||||
if (lc != nil) {
|
if (lc != nil) {
|
||||||
@@ -4614,7 +4648,7 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (k == N_INDEX) {
|
fn cgindex(c: *cgen, n: *node) void = {
|
||||||
// Element-size-aware load: u8-element bases use MOVZBQ,
|
// Element-size-aware load: u8-element bases use MOVZBQ,
|
||||||
// everything else MOVQ. Fast path when the base is a bare
|
// everything else MOVQ. Fast path when the base is a bare
|
||||||
// ident (mem.ww shape).
|
// ident (mem.ww shape).
|
||||||
@@ -4679,7 +4713,7 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (k == N_MATCH) {
|
fn cgmatch(c: *cgen, n: *node) void = {
|
||||||
// match (e) { case let v: T => stmt; ... }
|
// match (e) { case let v: T => stmt; ... }
|
||||||
//
|
//
|
||||||
// Read the tagged-union slot and dispatch by tag. Slot
|
// Read the tagged-union slot and dispatch by tag. Slot
|
||||||
@@ -4780,17 +4814,7 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (k == N_CAST) {
|
fn cgdot(c: *cgen, n: *node) void = {
|
||||||
// Type casts are mostly no-ops at the asm level for our
|
|
||||||
// integer-shaped operands. Evaluate the source; AX holds
|
|
||||||
// the bits unchanged. (Sign- or zero-extending narrow loads
|
|
||||||
// to wider types is the loader's job, not cast's, in this
|
|
||||||
// minimal cgen.)
|
|
||||||
cgexpr(c, n.lhs);
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (k == N_DOT) {
|
|
||||||
let lhs: *node = n.lhs;
|
let lhs: *node = n.lhs;
|
||||||
let fld: str = n.str;
|
let fld: str = n.str;
|
||||||
if (lhs != nil) {
|
if (lhs != nil) {
|
||||||
@@ -5007,7 +5031,7 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (k == N_UN) {
|
fn cgun(c: *cgen, n: *node) void = {
|
||||||
// Match C cgen ordering: evaluate operand first (load into AX),
|
// Match C cgen ordering: evaluate operand first (load into AX),
|
||||||
// then apply the unary op. AMP / STAR override AX with the
|
// then apply the unary op. AMP / STAR override AX with the
|
||||||
// address / deref. The wasted load before AMP keeps our asm
|
// address / deref. The wasted load before AMP keeps our asm
|
||||||
@@ -5047,7 +5071,7 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (k == N_BIN) {
|
fn cgbin(c: *cgen, n: *node) void = {
|
||||||
let unsignd: bool = nodeisunsigned(c, n.lhs);
|
let unsignd: bool = nodeisunsigned(c, n.lhs);
|
||||||
if (!unsignd) { unsignd = nodeisunsigned(c, n.rhs); };
|
if (!unsignd) { unsignd = nodeisunsigned(c, n.rhs); };
|
||||||
|
|
||||||
@@ -5112,7 +5136,7 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (k == N_CALL) {
|
fn cgcall(c: *cgen, n: *node) void = {
|
||||||
let nargs: i32 = pushargsrev(c, n.list);
|
let nargs: i32 = pushargsrev(c, n.list);
|
||||||
let i: i32 = 0;
|
let i: i32 = 0;
|
||||||
for (i < nargs) {
|
for (i < nargs) {
|
||||||
@@ -5216,7 +5240,7 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (k == N_ASSIGN) {
|
fn cgassign(c: *cgen, n: *node) void = {
|
||||||
let lhs: *node = n.lhs;
|
let lhs: *node = n.lhs;
|
||||||
// `*p = v` — deref-assign. Element width comes from the
|
// `*p = v` — deref-assign. Element width comes from the
|
||||||
// pointer's declared type. Mirrors C cgen: eval rhs (AX,
|
// pointer's declared type. Mirrors C cgen: eval rhs (AX,
|
||||||
@@ -5579,17 +5603,19 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// MODULE: wcc
|
// MODULE: wcc
|
||||||
// selfhost/cmd/wcc/cgen_stmt.ww — split out of cgen.ww.
|
// selfhost/cmd/wcc/cgen_stmt.ww — split out of cgen.ww.
|
||||||
//
|
//
|
||||||
// Houses cgstmt — statements + control flow (N_BLOCK, N_IF, N_FOR,
|
// cgstmt is a thin dispatcher over n.kind; each branch defers to a
|
||||||
// N_BREAK/N_CONTINUE, N_RETURN, N_LET, N_ASSIGN, etc.). The
|
// per-kind helper: cgblock, cgreturn, cgexprstmt, cglet, cgif, cgfor,
|
||||||
// expression generator (cgexpr) lives in cgen_expr.ww; the foundation
|
// cgmassign, cgbreak, cgcontinue.
|
||||||
// (types, emit primitives, collect* tables, FFI/module maps) lives in
|
//
|
||||||
// cgen.ww.
|
// The expression generator (cgexpr) lives in cgen_expr.ww; the
|
||||||
|
// foundation (types, emit primitives, collect* tables, FFI/module
|
||||||
|
// maps) lives in cgen.ww.
|
||||||
|
|
||||||
use os;
|
use os;
|
||||||
use mem;
|
use mem;
|
||||||
@@ -5605,7 +5631,27 @@ fn cgstmt(c: *cgen, n: *node) void = {
|
|||||||
if (n == nil) { return; };
|
if (n == nil) { return; };
|
||||||
let k: i32 = n.kind;
|
let k: i32 = n.kind;
|
||||||
|
|
||||||
if (k == N_BLOCK) {
|
if (k == N_BLOCK) { cgblock(c, n); return; };
|
||||||
|
|
||||||
|
if (k == N_RETURN) { cgreturn(c, n); return; };
|
||||||
|
|
||||||
|
if (k == N_EXPRSTMT) { cgexprstmt(c, n); return; };
|
||||||
|
|
||||||
|
if (k == N_LET) { cglet(c, n); return; };
|
||||||
|
|
||||||
|
if (k == N_IF) { cgif(c, n); return; };
|
||||||
|
|
||||||
|
if (k == N_FOR) { cgfor(c, n); return; };
|
||||||
|
|
||||||
|
if (k == N_MASSIGN) { cgmassign(c, n); return; };
|
||||||
|
|
||||||
|
if (k == N_BREAK) { cgbreak(c, n); return; };
|
||||||
|
if (k == N_CONTINUE) { cgcontinue(c, n); return; };
|
||||||
|
|
||||||
|
c.last_was_return = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
fn cgblock(c: *cgen, n: *node) void = {
|
||||||
let s: *node = n.list;
|
let s: *node = n.list;
|
||||||
for (s != nil) {
|
for (s != nil) {
|
||||||
cgstmt(c, s);
|
cgstmt(c, s);
|
||||||
@@ -5614,7 +5660,7 @@ fn cgstmt(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (k == N_RETURN) {
|
fn cgreturn(c: *cgen, n: *node) void = {
|
||||||
let rhs: *node = n.lhs;
|
let rhs: *node = n.lhs;
|
||||||
if (rhs != nil) {
|
if (rhs != nil) {
|
||||||
// Tuple return `return a, b;` — pack as (AX=v0, DX=v1).
|
// Tuple return `return a, b;` — pack as (AX=v0, DX=v1).
|
||||||
@@ -5681,13 +5727,13 @@ fn cgstmt(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (k == N_EXPRSTMT) {
|
fn cgexprstmt(c: *cgen, n: *node) void = {
|
||||||
if (n.lhs != nil) { cgexpr(c, n.lhs); };
|
if (n.lhs != nil) { cgexpr(c, n.lhs); };
|
||||||
c.last_was_return = 0;
|
c.last_was_return = 0;
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (k == N_LET) {
|
fn cglet(c: *cgen, n: *node) void = {
|
||||||
let nm: str = n.str;
|
let nm: str = n.str;
|
||||||
let sz: i32 = slotsize(c, n.lhs);
|
let sz: i32 = slotsize(c, n.lhs);
|
||||||
let off: i32 = localadd(c, nm, sz, n.lhs);
|
let off: i32 = localadd(c, nm, sz, n.lhs);
|
||||||
@@ -5829,7 +5875,7 @@ fn cgstmt(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (k == N_IF) {
|
fn cgif(c: *cgen, n: *node) void = {
|
||||||
let els: str = mklabel(c, "else");
|
let els: str = mklabel(c, "else");
|
||||||
let endl: str = mklabel(c, "end");
|
let endl: str = mklabel(c, "end");
|
||||||
cgexpr(c, n.cond);
|
cgexpr(c, n.cond);
|
||||||
@@ -5849,7 +5895,7 @@ fn cgstmt(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (k == N_FOR) {
|
fn cgfor(c: *cgen, n: *node) void = {
|
||||||
// Match C cgen's label scheme: <fn>_loop_N for the top,
|
// Match C cgen's label scheme: <fn>_loop_N for the top,
|
||||||
// <fn>_endloop_N for the post-body merge. No separate cont
|
// <fn>_endloop_N for the post-body merge. No separate cont
|
||||||
// label when there's no post-expression.
|
// label when there's no post-expression.
|
||||||
@@ -5885,7 +5931,7 @@ fn cgstmt(c: *cgen, n: *node) void = {
|
|||||||
// the first lvalue, then pop DX into the second. Mirrors
|
// the first lvalue, then pop DX into the second. Mirrors
|
||||||
// cmd/w6c/cgen.c:2424-2440. Lvalues beyond two are dropped (same
|
// cmd/w6c/cgen.c:2424-2440. Lvalues beyond two are dropped (same
|
||||||
// as C — no fixture uses >2 today).
|
// as C — no fixture uses >2 today).
|
||||||
if (k == N_MASSIGN) {
|
fn cgmassign(c: *cgen, n: *node) void = {
|
||||||
if (n.rhs != nil) { cgexpr(c, n.rhs); };
|
if (n.rhs != nil) { cgexpr(c, n.rhs); };
|
||||||
emitline("\tPUSHQ\tDX\n");
|
emitline("\tPUSHQ\tDX\n");
|
||||||
let l0: *node = n.list;
|
let l0: *node = n.list;
|
||||||
@@ -5916,7 +5962,7 @@ fn cgstmt(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (k == N_BREAK) {
|
fn cgbreak(c: *cgen, n: *node) void = {
|
||||||
if (c.loop_top > 0) {
|
if (c.loop_top > 0) {
|
||||||
let lbl: str = c.loop_end_buf[c.loop_top - 1];
|
let lbl: str = c.loop_end_buf[c.loop_top - 1];
|
||||||
emitline("\tJMP\t"); emitline(lbl); emitline("\n");
|
emitline("\tJMP\t"); emitline(lbl); emitline("\n");
|
||||||
@@ -5924,7 +5970,8 @@ fn cgstmt(c: *cgen, n: *node) void = {
|
|||||||
c.last_was_return = 0;
|
c.last_was_return = 0;
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (k == N_CONTINUE) {
|
|
||||||
|
fn cgcontinue(c: *cgen, n: *node) void = {
|
||||||
if (c.loop_top > 0) {
|
if (c.loop_top > 0) {
|
||||||
let lbl: str = c.loop_cont_buf[c.loop_top - 1];
|
let lbl: str = c.loop_cont_buf[c.loop_top - 1];
|
||||||
emitline("\tJMP\t"); emitline(lbl); emitline("\n");
|
emitline("\tJMP\t"); emitline(lbl); emitline("\n");
|
||||||
@@ -5933,8 +5980,6 @@ fn cgstmt(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
c.last_was_return = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// MODULE: wcc
|
// MODULE: wcc
|
||||||
|
|||||||
@@ -1,8 +1,13 @@
|
|||||||
// selfhost/cmd/wcc/cgen_expr.ww — split out of cgen.ww.
|
// selfhost/cmd/wcc/cgen_expr.ww — split out of cgen.ww.
|
||||||
//
|
//
|
||||||
// Houses cgexpr — the per-expression code generator. The remainder of
|
// cgexpr is a thin dispatcher over n.kind; each non-trivial branch
|
||||||
// the cgen lives in cgen.ww (foundation: types, emit primitives, the
|
// lives in a per-kind helper (cgstrlit, cgident, cgindex, cgmatch,
|
||||||
// collect* tables, FFI/module maps) and cgen_stmt.ww (cgstmt).
|
// cgdot, cgun, cgbin, cgcall, cgassign). Trivial literal loads
|
||||||
|
// (N_INTLIT, N_RUNELIT, N_TRUE/FALSE/NIL, N_CAST) stay inline.
|
||||||
|
//
|
||||||
|
// The remainder of cgen lives in cgen.ww (foundation: types, emit
|
||||||
|
// primitives, the collect* tables, FFI/module maps) and cgen_stmt.ww
|
||||||
|
// (cgstmt).
|
||||||
//
|
//
|
||||||
// `use cgen_expr;` is unnecessary at consumer sites — cgen.ww imports
|
// `use cgen_expr;` is unnecessary at consumer sites — cgen.ww imports
|
||||||
// this file, so any caller of cgen transitively gets cgexpr.
|
// this file, so any caller of cgen transitively gets cgexpr.
|
||||||
@@ -35,19 +40,7 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
|||||||
emitline(", AX\n");
|
emitline(", AX\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (k == N_STRLIT) {
|
if (k == N_STRLIT) { cgstrlit(c, n); return; };
|
||||||
// Result is the (ptr, len) pair: ptr in AX, len in BX. Call
|
|
||||||
// sites that expect a str arg pick these up directly.
|
|
||||||
let nstr: str = n.str;
|
|
||||||
let lab: str = internstrlit(c, nstr);
|
|
||||||
emitline("\tLEAQ\t");
|
|
||||||
os.write(1, lab.ptr, lab.len: u64);
|
|
||||||
emitline("(SB), AX\n");
|
|
||||||
emitline("\tMOVQ\t$");
|
|
||||||
emitint(nstr.len: i64);
|
|
||||||
emitline(", BX\n");
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
if (k == N_TRUE) {
|
if (k == N_TRUE) {
|
||||||
emitline("\tMOVQ\t$1, AX\n");
|
emitline("\tMOVQ\t$1, AX\n");
|
||||||
return;
|
return;
|
||||||
@@ -61,7 +54,48 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (k == N_IDENT) {
|
if (k == N_IDENT) { cgident(c, n); return; };
|
||||||
|
|
||||||
|
if (k == N_INDEX) { cgindex(c, n); return; };
|
||||||
|
|
||||||
|
if (k == N_MATCH) { cgmatch(c, n); return; };
|
||||||
|
|
||||||
|
if (k == N_CAST) {
|
||||||
|
// Type casts are mostly no-ops at the asm level for our
|
||||||
|
// integer-shaped operands. Evaluate the source; AX holds
|
||||||
|
// the bits unchanged. (Sign- or zero-extending narrow loads
|
||||||
|
// to wider types is the loader's job, not cast's, in this
|
||||||
|
// minimal cgen.)
|
||||||
|
cgexpr(c, n.lhs);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (k == N_DOT) { cgdot(c, n); return; };
|
||||||
|
|
||||||
|
if (k == N_UN) { cgun(c, n); return; };
|
||||||
|
|
||||||
|
if (k == N_BIN) { cgbin(c, n); return; };
|
||||||
|
|
||||||
|
if (k == N_CALL) { cgcall(c, n); return; };
|
||||||
|
|
||||||
|
if (k == N_ASSIGN) { cgassign(c, n); return; };
|
||||||
|
};
|
||||||
|
|
||||||
|
fn cgstrlit(c: *cgen, n: *node) void = {
|
||||||
|
// Result is the (ptr, len) pair: ptr in AX, len in BX. Call
|
||||||
|
// sites that expect a str arg pick these up directly.
|
||||||
|
let nstr: str = n.str;
|
||||||
|
let lab: str = internstrlit(c, nstr);
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
os.write(1, lab.ptr, lab.len: u64);
|
||||||
|
emitline("(SB), AX\n");
|
||||||
|
emitline("\tMOVQ\t$");
|
||||||
|
emitint(nstr.len: i64);
|
||||||
|
emitline(", BX\n");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
fn cgident(c: *cgen, n: *node) void = {
|
||||||
let nm: str = n.str;
|
let nm: str = n.str;
|
||||||
let lc: *local = localfindnode(c, nm);
|
let lc: *local = localfindnode(c, nm);
|
||||||
if (lc != nil) {
|
if (lc != nil) {
|
||||||
@@ -108,7 +142,7 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (k == N_INDEX) {
|
fn cgindex(c: *cgen, n: *node) void = {
|
||||||
// Element-size-aware load: u8-element bases use MOVZBQ,
|
// Element-size-aware load: u8-element bases use MOVZBQ,
|
||||||
// everything else MOVQ. Fast path when the base is a bare
|
// everything else MOVQ. Fast path when the base is a bare
|
||||||
// ident (mem.ww shape).
|
// ident (mem.ww shape).
|
||||||
@@ -173,7 +207,7 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (k == N_MATCH) {
|
fn cgmatch(c: *cgen, n: *node) void = {
|
||||||
// match (e) { case let v: T => stmt; ... }
|
// match (e) { case let v: T => stmt; ... }
|
||||||
//
|
//
|
||||||
// Read the tagged-union slot and dispatch by tag. Slot
|
// Read the tagged-union slot and dispatch by tag. Slot
|
||||||
@@ -274,17 +308,7 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (k == N_CAST) {
|
fn cgdot(c: *cgen, n: *node) void = {
|
||||||
// Type casts are mostly no-ops at the asm level for our
|
|
||||||
// integer-shaped operands. Evaluate the source; AX holds
|
|
||||||
// the bits unchanged. (Sign- or zero-extending narrow loads
|
|
||||||
// to wider types is the loader's job, not cast's, in this
|
|
||||||
// minimal cgen.)
|
|
||||||
cgexpr(c, n.lhs);
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (k == N_DOT) {
|
|
||||||
let lhs: *node = n.lhs;
|
let lhs: *node = n.lhs;
|
||||||
let fld: str = n.str;
|
let fld: str = n.str;
|
||||||
if (lhs != nil) {
|
if (lhs != nil) {
|
||||||
@@ -501,7 +525,7 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (k == N_UN) {
|
fn cgun(c: *cgen, n: *node) void = {
|
||||||
// Match C cgen ordering: evaluate operand first (load into AX),
|
// Match C cgen ordering: evaluate operand first (load into AX),
|
||||||
// then apply the unary op. AMP / STAR override AX with the
|
// then apply the unary op. AMP / STAR override AX with the
|
||||||
// address / deref. The wasted load before AMP keeps our asm
|
// address / deref. The wasted load before AMP keeps our asm
|
||||||
@@ -541,7 +565,7 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (k == N_BIN) {
|
fn cgbin(c: *cgen, n: *node) void = {
|
||||||
let unsignd: bool = nodeisunsigned(c, n.lhs);
|
let unsignd: bool = nodeisunsigned(c, n.lhs);
|
||||||
if (!unsignd) { unsignd = nodeisunsigned(c, n.rhs); };
|
if (!unsignd) { unsignd = nodeisunsigned(c, n.rhs); };
|
||||||
|
|
||||||
@@ -606,7 +630,7 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (k == N_CALL) {
|
fn cgcall(c: *cgen, n: *node) void = {
|
||||||
let nargs: i32 = pushargsrev(c, n.list);
|
let nargs: i32 = pushargsrev(c, n.list);
|
||||||
let i: i32 = 0;
|
let i: i32 = 0;
|
||||||
for (i < nargs) {
|
for (i < nargs) {
|
||||||
@@ -710,7 +734,7 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (k == N_ASSIGN) {
|
fn cgassign(c: *cgen, n: *node) void = {
|
||||||
let lhs: *node = n.lhs;
|
let lhs: *node = n.lhs;
|
||||||
// `*p = v` — deref-assign. Element width comes from the
|
// `*p = v` — deref-assign. Element width comes from the
|
||||||
// pointer's declared type. Mirrors C cgen: eval rhs (AX,
|
// pointer's declared type. Mirrors C cgen: eval rhs (AX,
|
||||||
@@ -1073,5 +1097,5 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
};
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
// selfhost/cmd/wcc/cgen_stmt.ww — split out of cgen.ww.
|
// selfhost/cmd/wcc/cgen_stmt.ww — split out of cgen.ww.
|
||||||
//
|
//
|
||||||
// Houses cgstmt — statements + control flow (N_BLOCK, N_IF, N_FOR,
|
// cgstmt is a thin dispatcher over n.kind; each branch defers to a
|
||||||
// N_BREAK/N_CONTINUE, N_RETURN, N_LET, N_ASSIGN, etc.). The
|
// per-kind helper: cgblock, cgreturn, cgexprstmt, cglet, cgif, cgfor,
|
||||||
// expression generator (cgexpr) lives in cgen_expr.ww; the foundation
|
// cgmassign, cgbreak, cgcontinue.
|
||||||
// (types, emit primitives, collect* tables, FFI/module maps) lives in
|
//
|
||||||
// cgen.ww.
|
// The expression generator (cgexpr) lives in cgen_expr.ww; the
|
||||||
|
// foundation (types, emit primitives, collect* tables, FFI/module
|
||||||
|
// maps) lives in cgen.ww.
|
||||||
|
|
||||||
use os;
|
use os;
|
||||||
use mem;
|
use mem;
|
||||||
@@ -20,7 +22,27 @@ fn cgstmt(c: *cgen, n: *node) void = {
|
|||||||
if (n == nil) { return; };
|
if (n == nil) { return; };
|
||||||
let k: i32 = n.kind;
|
let k: i32 = n.kind;
|
||||||
|
|
||||||
if (k == N_BLOCK) {
|
if (k == N_BLOCK) { cgblock(c, n); return; };
|
||||||
|
|
||||||
|
if (k == N_RETURN) { cgreturn(c, n); return; };
|
||||||
|
|
||||||
|
if (k == N_EXPRSTMT) { cgexprstmt(c, n); return; };
|
||||||
|
|
||||||
|
if (k == N_LET) { cglet(c, n); return; };
|
||||||
|
|
||||||
|
if (k == N_IF) { cgif(c, n); return; };
|
||||||
|
|
||||||
|
if (k == N_FOR) { cgfor(c, n); return; };
|
||||||
|
|
||||||
|
if (k == N_MASSIGN) { cgmassign(c, n); return; };
|
||||||
|
|
||||||
|
if (k == N_BREAK) { cgbreak(c, n); return; };
|
||||||
|
if (k == N_CONTINUE) { cgcontinue(c, n); return; };
|
||||||
|
|
||||||
|
c.last_was_return = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
fn cgblock(c: *cgen, n: *node) void = {
|
||||||
let s: *node = n.list;
|
let s: *node = n.list;
|
||||||
for (s != nil) {
|
for (s != nil) {
|
||||||
cgstmt(c, s);
|
cgstmt(c, s);
|
||||||
@@ -29,7 +51,7 @@ fn cgstmt(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (k == N_RETURN) {
|
fn cgreturn(c: *cgen, n: *node) void = {
|
||||||
let rhs: *node = n.lhs;
|
let rhs: *node = n.lhs;
|
||||||
if (rhs != nil) {
|
if (rhs != nil) {
|
||||||
// Tuple return `return a, b;` — pack as (AX=v0, DX=v1).
|
// Tuple return `return a, b;` — pack as (AX=v0, DX=v1).
|
||||||
@@ -96,13 +118,13 @@ fn cgstmt(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (k == N_EXPRSTMT) {
|
fn cgexprstmt(c: *cgen, n: *node) void = {
|
||||||
if (n.lhs != nil) { cgexpr(c, n.lhs); };
|
if (n.lhs != nil) { cgexpr(c, n.lhs); };
|
||||||
c.last_was_return = 0;
|
c.last_was_return = 0;
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (k == N_LET) {
|
fn cglet(c: *cgen, n: *node) void = {
|
||||||
let nm: str = n.str;
|
let nm: str = n.str;
|
||||||
let sz: i32 = slotsize(c, n.lhs);
|
let sz: i32 = slotsize(c, n.lhs);
|
||||||
let off: i32 = localadd(c, nm, sz, n.lhs);
|
let off: i32 = localadd(c, nm, sz, n.lhs);
|
||||||
@@ -244,7 +266,7 @@ fn cgstmt(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (k == N_IF) {
|
fn cgif(c: *cgen, n: *node) void = {
|
||||||
let els: str = mklabel(c, "else");
|
let els: str = mklabel(c, "else");
|
||||||
let endl: str = mklabel(c, "end");
|
let endl: str = mklabel(c, "end");
|
||||||
cgexpr(c, n.cond);
|
cgexpr(c, n.cond);
|
||||||
@@ -264,7 +286,7 @@ fn cgstmt(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (k == N_FOR) {
|
fn cgfor(c: *cgen, n: *node) void = {
|
||||||
// Match C cgen's label scheme: <fn>_loop_N for the top,
|
// Match C cgen's label scheme: <fn>_loop_N for the top,
|
||||||
// <fn>_endloop_N for the post-body merge. No separate cont
|
// <fn>_endloop_N for the post-body merge. No separate cont
|
||||||
// label when there's no post-expression.
|
// label when there's no post-expression.
|
||||||
@@ -300,7 +322,7 @@ fn cgstmt(c: *cgen, n: *node) void = {
|
|||||||
// the first lvalue, then pop DX into the second. Mirrors
|
// the first lvalue, then pop DX into the second. Mirrors
|
||||||
// cmd/w6c/cgen.c:2424-2440. Lvalues beyond two are dropped (same
|
// cmd/w6c/cgen.c:2424-2440. Lvalues beyond two are dropped (same
|
||||||
// as C — no fixture uses >2 today).
|
// as C — no fixture uses >2 today).
|
||||||
if (k == N_MASSIGN) {
|
fn cgmassign(c: *cgen, n: *node) void = {
|
||||||
if (n.rhs != nil) { cgexpr(c, n.rhs); };
|
if (n.rhs != nil) { cgexpr(c, n.rhs); };
|
||||||
emitline("\tPUSHQ\tDX\n");
|
emitline("\tPUSHQ\tDX\n");
|
||||||
let l0: *node = n.list;
|
let l0: *node = n.list;
|
||||||
@@ -331,7 +353,7 @@ fn cgstmt(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (k == N_BREAK) {
|
fn cgbreak(c: *cgen, n: *node) void = {
|
||||||
if (c.loop_top > 0) {
|
if (c.loop_top > 0) {
|
||||||
let lbl: str = c.loop_end_buf[c.loop_top - 1];
|
let lbl: str = c.loop_end_buf[c.loop_top - 1];
|
||||||
emitline("\tJMP\t"); emitline(lbl); emitline("\n");
|
emitline("\tJMP\t"); emitline(lbl); emitline("\n");
|
||||||
@@ -339,7 +361,8 @@ fn cgstmt(c: *cgen, n: *node) void = {
|
|||||||
c.last_was_return = 0;
|
c.last_was_return = 0;
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (k == N_CONTINUE) {
|
|
||||||
|
fn cgcontinue(c: *cgen, n: *node) void = {
|
||||||
if (c.loop_top > 0) {
|
if (c.loop_top > 0) {
|
||||||
let lbl: str = c.loop_cont_buf[c.loop_top - 1];
|
let lbl: str = c.loop_cont_buf[c.loop_top - 1];
|
||||||
emitline("\tJMP\t"); emitline(lbl); emitline("\n");
|
emitline("\tJMP\t"); emitline(lbl); emitline("\n");
|
||||||
@@ -348,6 +371,4 @@ fn cgstmt(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
c.last_was_return = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|||||||
@@ -4506,9 +4506,14 @@ fn taggedvariantindex(c: *cgen, tagged: *node, rhs: *node) i32 = {
|
|||||||
// MODULE: wcc
|
// MODULE: wcc
|
||||||
// selfhost/cmd/wcc/cgen_expr.ww — split out of cgen.ww.
|
// selfhost/cmd/wcc/cgen_expr.ww — split out of cgen.ww.
|
||||||
//
|
//
|
||||||
// Houses cgexpr — the per-expression code generator. The remainder of
|
// cgexpr is a thin dispatcher over n.kind; each non-trivial branch
|
||||||
// the cgen lives in cgen.ww (foundation: types, emit primitives, the
|
// lives in a per-kind helper (cgstrlit, cgident, cgindex, cgmatch,
|
||||||
// collect* tables, FFI/module maps) and cgen_stmt.ww (cgstmt).
|
// cgdot, cgun, cgbin, cgcall, cgassign). Trivial literal loads
|
||||||
|
// (N_INTLIT, N_RUNELIT, N_TRUE/FALSE/NIL, N_CAST) stay inline.
|
||||||
|
//
|
||||||
|
// The remainder of cgen lives in cgen.ww (foundation: types, emit
|
||||||
|
// primitives, the collect* tables, FFI/module maps) and cgen_stmt.ww
|
||||||
|
// (cgstmt).
|
||||||
//
|
//
|
||||||
// `use cgen_expr;` is unnecessary at consumer sites — cgen.ww imports
|
// `use cgen_expr;` is unnecessary at consumer sites — cgen.ww imports
|
||||||
// this file, so any caller of cgen transitively gets cgexpr.
|
// this file, so any caller of cgen transitively gets cgexpr.
|
||||||
@@ -4541,19 +4546,7 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
|||||||
emitline(", AX\n");
|
emitline(", AX\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (k == N_STRLIT) {
|
if (k == N_STRLIT) { cgstrlit(c, n); return; };
|
||||||
// Result is the (ptr, len) pair: ptr in AX, len in BX. Call
|
|
||||||
// sites that expect a str arg pick these up directly.
|
|
||||||
let nstr: str = n.str;
|
|
||||||
let lab: str = internstrlit(c, nstr);
|
|
||||||
emitline("\tLEAQ\t");
|
|
||||||
os.write(1, lab.ptr, lab.len: u64);
|
|
||||||
emitline("(SB), AX\n");
|
|
||||||
emitline("\tMOVQ\t$");
|
|
||||||
emitint(nstr.len: i64);
|
|
||||||
emitline(", BX\n");
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
if (k == N_TRUE) {
|
if (k == N_TRUE) {
|
||||||
emitline("\tMOVQ\t$1, AX\n");
|
emitline("\tMOVQ\t$1, AX\n");
|
||||||
return;
|
return;
|
||||||
@@ -4567,7 +4560,48 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (k == N_IDENT) {
|
if (k == N_IDENT) { cgident(c, n); return; };
|
||||||
|
|
||||||
|
if (k == N_INDEX) { cgindex(c, n); return; };
|
||||||
|
|
||||||
|
if (k == N_MATCH) { cgmatch(c, n); return; };
|
||||||
|
|
||||||
|
if (k == N_CAST) {
|
||||||
|
// Type casts are mostly no-ops at the asm level for our
|
||||||
|
// integer-shaped operands. Evaluate the source; AX holds
|
||||||
|
// the bits unchanged. (Sign- or zero-extending narrow loads
|
||||||
|
// to wider types is the loader's job, not cast's, in this
|
||||||
|
// minimal cgen.)
|
||||||
|
cgexpr(c, n.lhs);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (k == N_DOT) { cgdot(c, n); return; };
|
||||||
|
|
||||||
|
if (k == N_UN) { cgun(c, n); return; };
|
||||||
|
|
||||||
|
if (k == N_BIN) { cgbin(c, n); return; };
|
||||||
|
|
||||||
|
if (k == N_CALL) { cgcall(c, n); return; };
|
||||||
|
|
||||||
|
if (k == N_ASSIGN) { cgassign(c, n); return; };
|
||||||
|
};
|
||||||
|
|
||||||
|
fn cgstrlit(c: *cgen, n: *node) void = {
|
||||||
|
// Result is the (ptr, len) pair: ptr in AX, len in BX. Call
|
||||||
|
// sites that expect a str arg pick these up directly.
|
||||||
|
let nstr: str = n.str;
|
||||||
|
let lab: str = internstrlit(c, nstr);
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
os.write(1, lab.ptr, lab.len: u64);
|
||||||
|
emitline("(SB), AX\n");
|
||||||
|
emitline("\tMOVQ\t$");
|
||||||
|
emitint(nstr.len: i64);
|
||||||
|
emitline(", BX\n");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
fn cgident(c: *cgen, n: *node) void = {
|
||||||
let nm: str = n.str;
|
let nm: str = n.str;
|
||||||
let lc: *local = localfindnode(c, nm);
|
let lc: *local = localfindnode(c, nm);
|
||||||
if (lc != nil) {
|
if (lc != nil) {
|
||||||
@@ -4614,7 +4648,7 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (k == N_INDEX) {
|
fn cgindex(c: *cgen, n: *node) void = {
|
||||||
// Element-size-aware load: u8-element bases use MOVZBQ,
|
// Element-size-aware load: u8-element bases use MOVZBQ,
|
||||||
// everything else MOVQ. Fast path when the base is a bare
|
// everything else MOVQ. Fast path when the base is a bare
|
||||||
// ident (mem.ww shape).
|
// ident (mem.ww shape).
|
||||||
@@ -4679,7 +4713,7 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (k == N_MATCH) {
|
fn cgmatch(c: *cgen, n: *node) void = {
|
||||||
// match (e) { case let v: T => stmt; ... }
|
// match (e) { case let v: T => stmt; ... }
|
||||||
//
|
//
|
||||||
// Read the tagged-union slot and dispatch by tag. Slot
|
// Read the tagged-union slot and dispatch by tag. Slot
|
||||||
@@ -4780,17 +4814,7 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (k == N_CAST) {
|
fn cgdot(c: *cgen, n: *node) void = {
|
||||||
// Type casts are mostly no-ops at the asm level for our
|
|
||||||
// integer-shaped operands. Evaluate the source; AX holds
|
|
||||||
// the bits unchanged. (Sign- or zero-extending narrow loads
|
|
||||||
// to wider types is the loader's job, not cast's, in this
|
|
||||||
// minimal cgen.)
|
|
||||||
cgexpr(c, n.lhs);
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (k == N_DOT) {
|
|
||||||
let lhs: *node = n.lhs;
|
let lhs: *node = n.lhs;
|
||||||
let fld: str = n.str;
|
let fld: str = n.str;
|
||||||
if (lhs != nil) {
|
if (lhs != nil) {
|
||||||
@@ -5007,7 +5031,7 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (k == N_UN) {
|
fn cgun(c: *cgen, n: *node) void = {
|
||||||
// Match C cgen ordering: evaluate operand first (load into AX),
|
// Match C cgen ordering: evaluate operand first (load into AX),
|
||||||
// then apply the unary op. AMP / STAR override AX with the
|
// then apply the unary op. AMP / STAR override AX with the
|
||||||
// address / deref. The wasted load before AMP keeps our asm
|
// address / deref. The wasted load before AMP keeps our asm
|
||||||
@@ -5047,7 +5071,7 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (k == N_BIN) {
|
fn cgbin(c: *cgen, n: *node) void = {
|
||||||
let unsignd: bool = nodeisunsigned(c, n.lhs);
|
let unsignd: bool = nodeisunsigned(c, n.lhs);
|
||||||
if (!unsignd) { unsignd = nodeisunsigned(c, n.rhs); };
|
if (!unsignd) { unsignd = nodeisunsigned(c, n.rhs); };
|
||||||
|
|
||||||
@@ -5112,7 +5136,7 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (k == N_CALL) {
|
fn cgcall(c: *cgen, n: *node) void = {
|
||||||
let nargs: i32 = pushargsrev(c, n.list);
|
let nargs: i32 = pushargsrev(c, n.list);
|
||||||
let i: i32 = 0;
|
let i: i32 = 0;
|
||||||
for (i < nargs) {
|
for (i < nargs) {
|
||||||
@@ -5216,7 +5240,7 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (k == N_ASSIGN) {
|
fn cgassign(c: *cgen, n: *node) void = {
|
||||||
let lhs: *node = n.lhs;
|
let lhs: *node = n.lhs;
|
||||||
// `*p = v` — deref-assign. Element width comes from the
|
// `*p = v` — deref-assign. Element width comes from the
|
||||||
// pointer's declared type. Mirrors C cgen: eval rhs (AX,
|
// pointer's declared type. Mirrors C cgen: eval rhs (AX,
|
||||||
@@ -5579,17 +5603,19 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// MODULE: wcc
|
// MODULE: wcc
|
||||||
// selfhost/cmd/wcc/cgen_stmt.ww — split out of cgen.ww.
|
// selfhost/cmd/wcc/cgen_stmt.ww — split out of cgen.ww.
|
||||||
//
|
//
|
||||||
// Houses cgstmt — statements + control flow (N_BLOCK, N_IF, N_FOR,
|
// cgstmt is a thin dispatcher over n.kind; each branch defers to a
|
||||||
// N_BREAK/N_CONTINUE, N_RETURN, N_LET, N_ASSIGN, etc.). The
|
// per-kind helper: cgblock, cgreturn, cgexprstmt, cglet, cgif, cgfor,
|
||||||
// expression generator (cgexpr) lives in cgen_expr.ww; the foundation
|
// cgmassign, cgbreak, cgcontinue.
|
||||||
// (types, emit primitives, collect* tables, FFI/module maps) lives in
|
//
|
||||||
// cgen.ww.
|
// The expression generator (cgexpr) lives in cgen_expr.ww; the
|
||||||
|
// foundation (types, emit primitives, collect* tables, FFI/module
|
||||||
|
// maps) lives in cgen.ww.
|
||||||
|
|
||||||
use os;
|
use os;
|
||||||
use mem;
|
use mem;
|
||||||
@@ -5605,7 +5631,27 @@ fn cgstmt(c: *cgen, n: *node) void = {
|
|||||||
if (n == nil) { return; };
|
if (n == nil) { return; };
|
||||||
let k: i32 = n.kind;
|
let k: i32 = n.kind;
|
||||||
|
|
||||||
if (k == N_BLOCK) {
|
if (k == N_BLOCK) { cgblock(c, n); return; };
|
||||||
|
|
||||||
|
if (k == N_RETURN) { cgreturn(c, n); return; };
|
||||||
|
|
||||||
|
if (k == N_EXPRSTMT) { cgexprstmt(c, n); return; };
|
||||||
|
|
||||||
|
if (k == N_LET) { cglet(c, n); return; };
|
||||||
|
|
||||||
|
if (k == N_IF) { cgif(c, n); return; };
|
||||||
|
|
||||||
|
if (k == N_FOR) { cgfor(c, n); return; };
|
||||||
|
|
||||||
|
if (k == N_MASSIGN) { cgmassign(c, n); return; };
|
||||||
|
|
||||||
|
if (k == N_BREAK) { cgbreak(c, n); return; };
|
||||||
|
if (k == N_CONTINUE) { cgcontinue(c, n); return; };
|
||||||
|
|
||||||
|
c.last_was_return = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
fn cgblock(c: *cgen, n: *node) void = {
|
||||||
let s: *node = n.list;
|
let s: *node = n.list;
|
||||||
for (s != nil) {
|
for (s != nil) {
|
||||||
cgstmt(c, s);
|
cgstmt(c, s);
|
||||||
@@ -5614,7 +5660,7 @@ fn cgstmt(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (k == N_RETURN) {
|
fn cgreturn(c: *cgen, n: *node) void = {
|
||||||
let rhs: *node = n.lhs;
|
let rhs: *node = n.lhs;
|
||||||
if (rhs != nil) {
|
if (rhs != nil) {
|
||||||
// Tuple return `return a, b;` — pack as (AX=v0, DX=v1).
|
// Tuple return `return a, b;` — pack as (AX=v0, DX=v1).
|
||||||
@@ -5681,13 +5727,13 @@ fn cgstmt(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (k == N_EXPRSTMT) {
|
fn cgexprstmt(c: *cgen, n: *node) void = {
|
||||||
if (n.lhs != nil) { cgexpr(c, n.lhs); };
|
if (n.lhs != nil) { cgexpr(c, n.lhs); };
|
||||||
c.last_was_return = 0;
|
c.last_was_return = 0;
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (k == N_LET) {
|
fn cglet(c: *cgen, n: *node) void = {
|
||||||
let nm: str = n.str;
|
let nm: str = n.str;
|
||||||
let sz: i32 = slotsize(c, n.lhs);
|
let sz: i32 = slotsize(c, n.lhs);
|
||||||
let off: i32 = localadd(c, nm, sz, n.lhs);
|
let off: i32 = localadd(c, nm, sz, n.lhs);
|
||||||
@@ -5829,7 +5875,7 @@ fn cgstmt(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (k == N_IF) {
|
fn cgif(c: *cgen, n: *node) void = {
|
||||||
let els: str = mklabel(c, "else");
|
let els: str = mklabel(c, "else");
|
||||||
let endl: str = mklabel(c, "end");
|
let endl: str = mklabel(c, "end");
|
||||||
cgexpr(c, n.cond);
|
cgexpr(c, n.cond);
|
||||||
@@ -5849,7 +5895,7 @@ fn cgstmt(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (k == N_FOR) {
|
fn cgfor(c: *cgen, n: *node) void = {
|
||||||
// Match C cgen's label scheme: <fn>_loop_N for the top,
|
// Match C cgen's label scheme: <fn>_loop_N for the top,
|
||||||
// <fn>_endloop_N for the post-body merge. No separate cont
|
// <fn>_endloop_N for the post-body merge. No separate cont
|
||||||
// label when there's no post-expression.
|
// label when there's no post-expression.
|
||||||
@@ -5885,7 +5931,7 @@ fn cgstmt(c: *cgen, n: *node) void = {
|
|||||||
// the first lvalue, then pop DX into the second. Mirrors
|
// the first lvalue, then pop DX into the second. Mirrors
|
||||||
// cmd/w6c/cgen.c:2424-2440. Lvalues beyond two are dropped (same
|
// cmd/w6c/cgen.c:2424-2440. Lvalues beyond two are dropped (same
|
||||||
// as C — no fixture uses >2 today).
|
// as C — no fixture uses >2 today).
|
||||||
if (k == N_MASSIGN) {
|
fn cgmassign(c: *cgen, n: *node) void = {
|
||||||
if (n.rhs != nil) { cgexpr(c, n.rhs); };
|
if (n.rhs != nil) { cgexpr(c, n.rhs); };
|
||||||
emitline("\tPUSHQ\tDX\n");
|
emitline("\tPUSHQ\tDX\n");
|
||||||
let l0: *node = n.list;
|
let l0: *node = n.list;
|
||||||
@@ -5916,7 +5962,7 @@ fn cgstmt(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (k == N_BREAK) {
|
fn cgbreak(c: *cgen, n: *node) void = {
|
||||||
if (c.loop_top > 0) {
|
if (c.loop_top > 0) {
|
||||||
let lbl: str = c.loop_end_buf[c.loop_top - 1];
|
let lbl: str = c.loop_end_buf[c.loop_top - 1];
|
||||||
emitline("\tJMP\t"); emitline(lbl); emitline("\n");
|
emitline("\tJMP\t"); emitline(lbl); emitline("\n");
|
||||||
@@ -5924,7 +5970,8 @@ fn cgstmt(c: *cgen, n: *node) void = {
|
|||||||
c.last_was_return = 0;
|
c.last_was_return = 0;
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (k == N_CONTINUE) {
|
|
||||||
|
fn cgcontinue(c: *cgen, n: *node) void = {
|
||||||
if (c.loop_top > 0) {
|
if (c.loop_top > 0) {
|
||||||
let lbl: str = c.loop_cont_buf[c.loop_top - 1];
|
let lbl: str = c.loop_cont_buf[c.loop_top - 1];
|
||||||
emitline("\tJMP\t"); emitline(lbl); emitline("\n");
|
emitline("\tJMP\t"); emitline(lbl); emitline("\n");
|
||||||
@@ -5933,8 +5980,6 @@ fn cgstmt(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
c.last_was_return = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// MODULE: wcc
|
// MODULE: wcc
|
||||||
|
|||||||
Reference in New Issue
Block a user