ww: cgen trap batch (def-str field, chained-ptr write, scalar+str tuple ABI)
This commit is contained in:
@@ -36,6 +36,8 @@ fn cgstmt(c: *cgen, n: *node) void = {
|
||||
|
||||
if (k == N_MASSIGN) { cgmassign(c, n); return; };
|
||||
|
||||
if (k == N_MLET) { cgmlet(c, n); return; };
|
||||
|
||||
if (k == N_BREAK) { cgbreak(c, n); return; };
|
||||
if (k == N_CONTINUE) { cgcontinue(c, n); return; };
|
||||
|
||||
@@ -54,18 +56,35 @@ fn cgblock(c: *cgen, n: *node) void = {
|
||||
fn cgreturn(c: *cgen, n: *node) void = {
|
||||
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.
|
||||
// Tuple return `return a, b;`:
|
||||
// (scalar, scalar) — AX = v0, DX = v1.
|
||||
// (scalar, str) / (str, scalar) — AX = scalar elem,
|
||||
// DX = str.ptr, CX = str.len.
|
||||
// 24B convention mirrors the tagged-union return below; receive
|
||||
// sites destructure off the same regs regardless of position.
|
||||
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");
|
||||
let v0_is_str: bool = nodeisstr(c, v);
|
||||
let v1_is_str: bool = nodeisstr(c, v2);
|
||||
if ((v0_is_str || v1_is_str) && !(v0_is_str && v1_is_str)) {
|
||||
let strn: *node = v;
|
||||
let scaln: *node = v2;
|
||||
if (v1_is_str) { strn = v2; scaln = v; };
|
||||
cgexpr(c, scaln);
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
cgexpr(c, strn);
|
||||
emitline("\tMOVQ\tBX, CX\n");
|
||||
emitline("\tMOVQ\tAX, DX\n");
|
||||
emitline("\tPOPQ\tAX\n");
|
||||
} else {
|
||||
cgexpr(c, v2);
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
cgexpr(c, v);
|
||||
emitline("\tPOPQ\tDX\n");
|
||||
};
|
||||
} else {
|
||||
cgexpr(c, v);
|
||||
};
|
||||
@@ -187,6 +206,50 @@ fn cglet(c: *cgen, n: *node) void = {
|
||||
c.lastwasreturn = 0;
|
||||
return;
|
||||
};
|
||||
// 24B tuple init for `let t: (scalar, str) = call()` /
|
||||
// `let t: (str, scalar) = call()`. Per the AX:DX:CX return
|
||||
// convention: AX = scalar elem, DX = str.ptr, CX = str.len.
|
||||
// Layout is positional, so we route each register to the
|
||||
// slot dictated by element type, not by AX/DX position.
|
||||
if (n.lhs != nil) {
|
||||
if (n.lhs.kind == N_TTUPLE) {
|
||||
let p0: *node = n.lhs.list;
|
||||
let p1: *node = nil;
|
||||
if (p0 != nil) { p1 = p0.next; };
|
||||
let s0_is_str: bool = isstrtyperaw(p0);
|
||||
let s1_is_str: bool = isstrtyperaw(p1);
|
||||
if (p0 != nil) {
|
||||
if (p1 != nil) {
|
||||
if (s0_is_str != s1_is_str) {
|
||||
cgexpr(c, rhs);
|
||||
if (s0_is_str) {
|
||||
emitline("\tMOVQ\tDX, ");
|
||||
emitoff(off: i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tCX, ");
|
||||
emitoff((off + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((off + 16): i64);
|
||||
emitline("(BP)\n");
|
||||
} else {
|
||||
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.lastwasreturn = 0;
|
||||
return;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
// Array literal init: `let xs: [N]T = [a, b, c];` (or [_]T).
|
||||
// Walk elements in declaration order, store each at off + i*esz
|
||||
// using the right width for the element type. Trailing `...`
|
||||
@@ -459,6 +522,109 @@ fn cgmassign(c: *cgen, n: *node) void = {
|
||||
return;
|
||||
};
|
||||
|
||||
// Multi-let from a tuple-returning call: `let n, s = call();` or
|
||||
// `let (n, s) = call();`. wwstage has no checker, so each binding's
|
||||
// type is taken from its explicit annotation (l.lhs) when present
|
||||
// or inferred from the called fn's return-type tuple element.
|
||||
//
|
||||
// Per the AX:DX:CX return convention (mirrors C cgen N_MLET):
|
||||
// (scalar, scalar) — AX → l0, DX → l1.
|
||||
// (scalar, str) — AX → scalar slot, (DX, CX) → str slot
|
||||
// as (.ptr, .len). Position-agnostic — the
|
||||
// regs are routed by element type, not by AX/DX.
|
||||
fn cgmlet(c: *cgen, n: *node) void = {
|
||||
let rhs: *node = n.rhs;
|
||||
if (rhs == nil) { return; };
|
||||
|
||||
let p0t: *node = nil;
|
||||
let p1t: *node = nil;
|
||||
if (rhs.kind == N_CALL) {
|
||||
let callee: *node = rhs.lhs;
|
||||
if (callee != nil) {
|
||||
let cnm: str;
|
||||
cnm.ptr = nil; cnm.len = 0;
|
||||
if (callee.kind == N_IDENT) { cnm = callee.str; };
|
||||
if (callee.kind == N_DOT) { cnm = callee.str; };
|
||||
if (cnm.len > 0) {
|
||||
let rt: *node = fnretlookup(c, cnm);
|
||||
if (rt != nil) {
|
||||
if (rt.kind == N_TTUPLE) {
|
||||
p0t = rt.list;
|
||||
if (p0t != nil) { p1t = p0t.next; };
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
let l0: *node = n.list;
|
||||
let l1: *node = nil;
|
||||
if (l0 != nil) { l1 = l0.next; };
|
||||
|
||||
let t0: *node = nil;
|
||||
let t1: *node = nil;
|
||||
if (l0 != nil) { t0 = l0.lhs; };
|
||||
if (l1 != nil) { t1 = l1.lhs; };
|
||||
if (t0 == nil) { t0 = p0t; };
|
||||
if (t1 == nil) { t1 = p1t; };
|
||||
|
||||
let s0_is_str: bool = isstrtyperaw(t0);
|
||||
let s1_is_str: bool = isstrtyperaw(t1);
|
||||
|
||||
cgexpr(c, rhs);
|
||||
|
||||
if (l0 != nil) {
|
||||
if (l1 != nil) {
|
||||
if (s0_is_str != s1_is_str) {
|
||||
let sz0: i32 = 8;
|
||||
let sz1: i32 = 8;
|
||||
if (s0_is_str) { sz0 = 16; };
|
||||
if (s1_is_str) { sz1 = 16; };
|
||||
let off0: i32 = localadd(c, l0.str, sz0, t0);
|
||||
let off1: i32 = localadd(c, l1.str, sz1, t1);
|
||||
if (s0_is_str) {
|
||||
emitline("\tMOVQ\tDX, ");
|
||||
emitoff(off0: i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tCX, ");
|
||||
emitoff((off0 + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff(off1: i64);
|
||||
emitline("(BP)\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff(off0: i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tDX, ");
|
||||
emitoff(off1: i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tCX, ");
|
||||
emitoff((off1 + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
c.lastwasreturn = 0;
|
||||
return;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
if (l0 != nil) {
|
||||
let off: i32 = localadd(c, l0.str, 8, t0);
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff(off: i64);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
if (l1 != nil) {
|
||||
let off: i32 = localadd(c, l1.str, 8, t1);
|
||||
emitline("\tMOVQ\tDX, ");
|
||||
emitoff(off: i64);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
c.lastwasreturn = 0;
|
||||
return;
|
||||
};
|
||||
|
||||
fn cgbreak(c: *cgen, n: *node) void = {
|
||||
if (c.looptop > 0) {
|
||||
let lbl: str = c.loopendbuf[c.looptop - 1];
|
||||
|
||||
Reference in New Issue
Block a user