wcc/cgen: split cgexpr→cgen_expr.ww, cgstmt→cgen_stmt.ww (rob pike #5)
This commit is contained in:
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
1077
selfhost/cmd/wcc/cgen_expr.ww
Normal file
1077
selfhost/cmd/wcc/cgen_expr.ww
Normal file
File diff suppressed because it is too large
Load Diff
353
selfhost/cmd/wcc/cgen_stmt.ww
Normal file
353
selfhost/cmd/wcc/cgen_stmt.ww
Normal file
@@ -0,0 +1,353 @@
|
||||
// selfhost/cmd/wcc/cgen_stmt.ww — split out of cgen.ww.
|
||||
//
|
||||
// Houses cgstmt — statements + control flow (N_BLOCK, N_IF, N_FOR,
|
||||
// N_BREAK/N_CONTINUE, N_RETURN, N_LET, N_ASSIGN, etc.). 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 mem;
|
||||
use ast;
|
||||
use tok;
|
||||
use typ;
|
||||
use sym;
|
||||
use strconv;
|
||||
|
||||
// ---- statement cgen --------------------------------------------------
|
||||
|
||||
fn cgstmt(c: *cgen, n: *node) void = {
|
||||
if (n == nil) { return; };
|
||||
let k: i32 = n.kind;
|
||||
|
||||
if (k == N_BLOCK) {
|
||||
let s: *node = n.list;
|
||||
for (s != nil) {
|
||||
cgstmt(c, s);
|
||||
s = s.next;
|
||||
};
|
||||
return;
|
||||
};
|
||||
|
||||
if (k == N_RETURN) {
|
||||
let rhs: *node = n.lhs;
|
||||
if (rhs != nil) {
|
||||
// Tuple return `return a, b;` — pack as (AX=v0, DX=v1).
|
||||
// Matches C cgen: evaluate v1 first (PUSHQ), then v0
|
||||
// into AX, then POPQ DX. End state: AX = v0, DX = v1.
|
||||
if (rhs.kind == N_TUPLE) {
|
||||
let v: *node = rhs.list;
|
||||
if (v != nil) {
|
||||
let v2: *node = v.next;
|
||||
if (v2 != nil) {
|
||||
cgexpr(c, v2);
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
cgexpr(c, v);
|
||||
emitline("\tPOPQ\tDX\n");
|
||||
} else {
|
||||
cgexpr(c, v);
|
||||
};
|
||||
};
|
||||
emitline("\tMOVQ\tBP, SP\n");
|
||||
emitline("\tPOPQ\tBP\n");
|
||||
emitline("\tRET\n");
|
||||
c.last_was_return = 1;
|
||||
return;
|
||||
};
|
||||
// Tagged-union return: pack as (AX=tag, DX=value0, CX=value1).
|
||||
// For str variant, cgexpr leaves (AX=ptr, BX=len), so we
|
||||
// shuffle DX←AX (ptr) and CX←BX (len), then load tag.
|
||||
// For other variants, cgexpr leaves AX, shuffle DX←AX.
|
||||
if (istaggedtype(c.fn_ret)) {
|
||||
cgexpr(c, rhs);
|
||||
let idx: i32 = taggedvariantindex(c, c.fn_ret, rhs);
|
||||
if (nodeisstr(c, rhs)) {
|
||||
emitline("\tMOVQ\tBX, CX\n");
|
||||
emitline("\tMOVQ\tAX, DX\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\tAX, DX\n");
|
||||
};
|
||||
emitline("\tMOVQ\t$");
|
||||
if (idx < 0) { idx = 0; };
|
||||
emitint(idx: i64);
|
||||
emitline(", AX\n");
|
||||
emitline("\tMOVQ\tBP, SP\n");
|
||||
emitline("\tPOPQ\tBP\n");
|
||||
emitline("\tRET\n");
|
||||
c.last_was_return = 1;
|
||||
return;
|
||||
};
|
||||
cgexpr(c, rhs);
|
||||
} else {
|
||||
// Bare `return;` in a void fn — zero AX so the caller
|
||||
// sees a deterministic value (matches C cgen, which
|
||||
// always falls through to `cgexpr_int(c, 0)`).
|
||||
emitline("\tMOVQ\t$0, AX\n");
|
||||
};
|
||||
// SysV: 16-byte aggregates (str, 2-tuple) return in (AX, DX).
|
||||
// cgexpr leaves str in (AX, BX); shuffle BX→DX.
|
||||
if (isstrtype(c, c.fn_ret)) {
|
||||
emitline("\tMOVQ\tBX, DX\n");
|
||||
};
|
||||
emitline("\tMOVQ\tBP, SP\n");
|
||||
emitline("\tPOPQ\tBP\n");
|
||||
emitline("\tRET\n");
|
||||
c.last_was_return = 1;
|
||||
return;
|
||||
};
|
||||
|
||||
if (k == N_EXPRSTMT) {
|
||||
if (n.lhs != nil) { cgexpr(c, n.lhs); };
|
||||
c.last_was_return = 0;
|
||||
return;
|
||||
};
|
||||
|
||||
if (k == N_LET) {
|
||||
let nm: str = n.str;
|
||||
let sz: i32 = slotsize(c, n.lhs);
|
||||
let off: i32 = localadd(c, nm, sz, n.lhs);
|
||||
if (n.rhs != nil) {
|
||||
let rhs: *node = n.rhs;
|
||||
// Tagged-union init: `let r: (T | E) = expr;`.
|
||||
// - If rhs is a CALL to a fn returning tagged-union,
|
||||
// the result is already in (AX=tag, DX=v0, CX=v1);
|
||||
// just spill all three.
|
||||
// - Otherwise rhs is a bare variant value: pack tag +
|
||||
// value(s).
|
||||
if (istaggedtype(n.lhs)) {
|
||||
let rhsreturnstagged: bool = false;
|
||||
if (rhs.kind == N_CALL) {
|
||||
let callee: *node = rhs.lhs;
|
||||
if (callee != nil) {
|
||||
let calleename: str;
|
||||
calleename.ptr = nil; calleename.len = 0;
|
||||
if (callee.kind == N_IDENT) { calleename = callee.str; };
|
||||
if (callee.kind == N_DOT) { calleename = callee.str; };
|
||||
if (calleename.len > 0) {
|
||||
let rt: *node = fnretlookup(c, calleename);
|
||||
if (istaggedtype(rt)) { rhsreturnstagged = true; };
|
||||
};
|
||||
};
|
||||
};
|
||||
cgexpr(c, rhs);
|
||||
if (rhsreturnstagged) {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff(off: i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tDX, ");
|
||||
emitoff((off + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tCX, ");
|
||||
emitoff((off + 16): i64);
|
||||
emitline("(BP)\n");
|
||||
c.last_was_return = 0;
|
||||
return;
|
||||
};
|
||||
let tagidx: i32 = taggedvariantindex(c, n.lhs, rhs);
|
||||
if (tagidx < 0) { tagidx = 0; };
|
||||
if (nodeisstr(c, rhs)) {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((off + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tBX, ");
|
||||
emitoff((off + 16): i64);
|
||||
emitline("(BP)\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((off + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(tagidx: i64);
|
||||
emitline(", ");
|
||||
emitoff(off: i64);
|
||||
emitline("(BP)\n");
|
||||
c.last_was_return = 0;
|
||||
return;
|
||||
};
|
||||
// Struct literal init: `let p: point = point{x=..., y=...};`.
|
||||
// For each field in the lit, evaluate its value and store at
|
||||
// the field's offset within the slot. Field-name → offset
|
||||
// from the struct registry.
|
||||
if (rhs.kind == N_STRUCTLIT) {
|
||||
let trefn: *node = rhs.lhs;
|
||||
let sname: str;
|
||||
sname.ptr = nil; sname.len = 0;
|
||||
if (trefn != nil) {
|
||||
if (trefn.kind == N_IDENT) { sname = trefn.str; }
|
||||
else { if (trefn.kind == N_TNAME) { sname = trefn.str; }; };
|
||||
};
|
||||
let si: *structinfo = structlookup(c, sname);
|
||||
if (si != nil) {
|
||||
let fieldnode: *node = rhs.list;
|
||||
for (fieldnode != nil) {
|
||||
if (fieldnode.kind == N_FIELD) {
|
||||
let fname: str = fieldnode.str;
|
||||
let fi: *fieldinfo = si.fields;
|
||||
for (fi != nil) {
|
||||
let fn_: str = fi.fname;
|
||||
if (streq(fn_, fname)) {
|
||||
cgexpr(c, fieldnode.lhs);
|
||||
let sop: str = fieldstoreop(fi);
|
||||
emitline("\t");
|
||||
emitline(sop);
|
||||
emitline("\tAX, ");
|
||||
emitoff((off + fi.foff): i64);
|
||||
emitline("(BP)\n");
|
||||
fi = nil;
|
||||
} else {
|
||||
fi = fi.finext;
|
||||
};
|
||||
};
|
||||
};
|
||||
fieldnode = fieldnode.next;
|
||||
};
|
||||
c.last_was_return = 0;
|
||||
return;
|
||||
};
|
||||
};
|
||||
cgexpr(c, rhs);
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff(off: i64);
|
||||
emitline("(BP)\n");
|
||||
// str init: cgexpr also leaves len in BX; store both.
|
||||
if (sz == 16) {
|
||||
emitline("\tMOVQ\tBX, ");
|
||||
emitoff((off + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
// slice init: ptr/len/cap in AX/BX/CX.
|
||||
if (sz == 24) {
|
||||
emitline("\tMOVQ\tBX, ");
|
||||
emitoff((off + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tCX, ");
|
||||
emitoff((off + 16): i64);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
} else {
|
||||
// Bare `let x: T;` with no initializer. C cgen
|
||||
// (cmd/w6c/cgen.c:2181-2183) zero-inits only when
|
||||
// the underlying type's natural size is 8 — pointers,
|
||||
// i64/u64, function pointers, ints. Structs/arrays/
|
||||
// slices/strings/tagged/tuples are left for per-field
|
||||
// writes. ww's slotsize pads struct slots up to 8,
|
||||
// so we can't just check sz == 8: walk the type AST
|
||||
// directly to make the same call.
|
||||
if (typeis8byteprimitive(c, n.lhs)) {
|
||||
emitline("\tMOVQ\t$0, ");
|
||||
emitoff(off: i64);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
};
|
||||
c.last_was_return = 0;
|
||||
return;
|
||||
};
|
||||
|
||||
if (k == N_IF) {
|
||||
let els: str = mklabel(c, "else");
|
||||
let endl: str = mklabel(c, "end");
|
||||
cgexpr(c, n.cond);
|
||||
emitline("\tCMPQ\t$0, AX\n");
|
||||
emitline("\tJE\t");
|
||||
if (n.els != nil) { emitline(els); }
|
||||
else { emitline(endl); };
|
||||
emitline("\n");
|
||||
if (n.body != nil) { cgstmt(c, n.body); };
|
||||
if (n.els != nil) {
|
||||
emitline("\tJMP\t"); emitline(endl); emitline("\n");
|
||||
emitlabel(els);
|
||||
cgstmt(c, n.els);
|
||||
};
|
||||
emitlabel(endl);
|
||||
c.last_was_return = 0;
|
||||
return;
|
||||
};
|
||||
|
||||
if (k == N_FOR) {
|
||||
// Match C cgen's label scheme: <fn>_loop_N for the top,
|
||||
// <fn>_endloop_N for the post-body merge. No separate cont
|
||||
// label when there's no post-expression.
|
||||
let topl: str = mklabel(c, "loop");
|
||||
let endl: str = mklabel(c, "endloop");
|
||||
|
||||
if (n.lhs != nil) { cgstmt(c, n.lhs); };
|
||||
|
||||
emitlabel(topl);
|
||||
if (n.cond != nil) {
|
||||
cgexpr(c, n.cond);
|
||||
emitline("\tCMPQ\t$0, AX\n");
|
||||
emitline("\tJE\t"); emitline(endl); emitline("\n");
|
||||
};
|
||||
|
||||
c.loop_end_buf[c.loop_top] = endl;
|
||||
c.loop_cont_buf[c.loop_top] = topl;
|
||||
c.loop_top += 1;
|
||||
|
||||
if (n.body != nil) { cgstmt(c, n.body); };
|
||||
|
||||
c.loop_top -= 1;
|
||||
|
||||
if (n.rhs != nil) { cgexpr(c, n.rhs); };
|
||||
emitline("\tJMP\t"); emitline(topl); emitline("\n");
|
||||
emitlabel(endl);
|
||||
c.last_was_return = 0;
|
||||
return;
|
||||
};
|
||||
|
||||
// Tuple-destructure assign: `a, b = call();`. The call's tuple
|
||||
// return lands in (AX, DX); push DX to free it, store AX into
|
||||
// the first lvalue, then pop DX into the second. Mirrors
|
||||
// cmd/w6c/cgen.c:2424-2440. Lvalues beyond two are dropped (same
|
||||
// as C — no fixture uses >2 today).
|
||||
if (k == N_MASSIGN) {
|
||||
if (n.rhs != nil) { cgexpr(c, n.rhs); };
|
||||
emitline("\tPUSHQ\tDX\n");
|
||||
let l0: *node = n.list;
|
||||
let l1: *node = nil;
|
||||
if (l0 != nil) { l1 = l0.next; };
|
||||
if (l0 != nil) {
|
||||
if (l0.kind == N_IDENT) {
|
||||
let off: i32 = localfind(c, l0.str);
|
||||
if (off != 0) {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff(off: i64);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
};
|
||||
};
|
||||
emitline("\tPOPQ\tDX\n");
|
||||
if (l1 != nil) {
|
||||
if (l1.kind == N_IDENT) {
|
||||
let off: i32 = localfind(c, l1.str);
|
||||
if (off != 0) {
|
||||
emitline("\tMOVQ\tDX, ");
|
||||
emitoff(off: i64);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
};
|
||||
};
|
||||
c.last_was_return = 0;
|
||||
return;
|
||||
};
|
||||
|
||||
if (k == N_BREAK) {
|
||||
if (c.loop_top > 0) {
|
||||
let lbl: str = c.loop_end_buf[c.loop_top - 1];
|
||||
emitline("\tJMP\t"); emitline(lbl); emitline("\n");
|
||||
};
|
||||
c.last_was_return = 0;
|
||||
return;
|
||||
};
|
||||
if (k == N_CONTINUE) {
|
||||
if (c.loop_top > 0) {
|
||||
let lbl: str = c.loop_cont_buf[c.loop_top - 1];
|
||||
emitline("\tJMP\t"); emitline(lbl); emitline("\n");
|
||||
};
|
||||
c.last_was_return = 0;
|
||||
return;
|
||||
};
|
||||
|
||||
c.last_was_return = 0;
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user