selfhost: port switch — N_SWITCH parser + cgen + scratch slot
This commit is contained in:
@@ -176,6 +176,63 @@ fn parsefor(p: *parser) *node = {
|
||||
return n;
|
||||
};
|
||||
|
||||
fn parseswitch(p: *parser) *node = {
|
||||
let pf: str = p.curfile;
|
||||
let pl: i32 = p.curline;
|
||||
let pc: i32 = p.curcol;
|
||||
advance(p); // past `switch`
|
||||
expecttok(p, tkind.TK_LPAREN, "expected '(' after switch");
|
||||
let n: *node = newnode(p.a, nkind.N_SWITCH, pf, pl, pc);
|
||||
n.lhs = parseexpr(p);
|
||||
expecttok(p, tkind.TK_RPAREN, "expected ')' after switch expression");
|
||||
expecttok(p, tkind.TK_LBRACE, "expected '{' to open switch body");
|
||||
let head: *node = nil;
|
||||
let tail: *node = nil;
|
||||
for (p.curkind == tkind.TK_CASE) {
|
||||
let cpf: str = p.curfile;
|
||||
let cpl: i32 = p.curline;
|
||||
let cpc: i32 = p.curcol;
|
||||
advance(p); // past `case`
|
||||
let cs: *node = newnode(p.a, nkind.N_CASE, cpf, cpl, cpc);
|
||||
let eh: *node = nil;
|
||||
let et: *node = nil;
|
||||
if (p.curkind != tkind.TK_COLON) {
|
||||
p.nocast = 1;
|
||||
for (true) {
|
||||
let e: *node = parseexpr(p);
|
||||
if (eh == nil) { eh = e; }
|
||||
else { et.next = e; };
|
||||
et = e;
|
||||
if (!accepttok(p, tkind.TK_COMMA)) { break; };
|
||||
};
|
||||
p.nocast = 0;
|
||||
};
|
||||
cs.list = eh;
|
||||
expecttok(p, tkind.TK_COLON, "expected ':' after case label");
|
||||
let bh: *node = nil;
|
||||
let bt: *node = nil;
|
||||
for (p.curkind != tkind.TK_CASE) {
|
||||
if (p.curkind == tkind.TK_RBRACE) { break; };
|
||||
if (p.curkind == tkind.TK_EOF) { break; };
|
||||
let s: *node = parsestmt(p);
|
||||
if (s != nil) {
|
||||
if (bh == nil) { bh = s; }
|
||||
else { bt.next = s; };
|
||||
bt = s;
|
||||
};
|
||||
};
|
||||
let blk: *node = newnode(p.a, nkind.N_BLOCK, cpf, cpl, cpc);
|
||||
blk.list = bh;
|
||||
cs.body = blk;
|
||||
if (head == nil) { head = cs; }
|
||||
else { tail.next = cs; };
|
||||
tail = cs;
|
||||
};
|
||||
expecttok(p, tkind.TK_RBRACE, "expected '}' to close switch");
|
||||
n.list = head;
|
||||
return n;
|
||||
};
|
||||
|
||||
fn parsestmt(p: *parser) *node = {
|
||||
let pf: str = p.curfile;
|
||||
let pl: i32 = p.curline;
|
||||
@@ -202,6 +259,11 @@ fn parsestmt(p: *parser) *node = {
|
||||
expecttok(p, tkind.TK_SEMI, "expected ';' after for");
|
||||
return n;
|
||||
};
|
||||
if (p.curkind == tkind.TK_SWITCH) {
|
||||
let n: *node = parseswitch(p);
|
||||
expecttok(p, tkind.TK_SEMI, "expected ';' after switch");
|
||||
return n;
|
||||
};
|
||||
if (p.curkind == tkind.TK_RETURN) {
|
||||
advance(p);
|
||||
let n: *node = newnode(p.a, nkind.N_RETURN, pf, pl, pc);
|
||||
|
||||
@@ -2743,6 +2743,63 @@ fn parsefor(p: *parser) *node = {
|
||||
return n;
|
||||
};
|
||||
|
||||
fn parseswitch(p: *parser) *node = {
|
||||
let pf: str = p.curfile;
|
||||
let pl: i32 = p.curline;
|
||||
let pc: i32 = p.curcol;
|
||||
advance(p); // past `switch`
|
||||
expecttok(p, tkind.TK_LPAREN, "expected '(' after switch");
|
||||
let n: *node = newnode(p.a, nkind.N_SWITCH, pf, pl, pc);
|
||||
n.lhs = parseexpr(p);
|
||||
expecttok(p, tkind.TK_RPAREN, "expected ')' after switch expression");
|
||||
expecttok(p, tkind.TK_LBRACE, "expected '{' to open switch body");
|
||||
let head: *node = nil;
|
||||
let tail: *node = nil;
|
||||
for (p.curkind == tkind.TK_CASE) {
|
||||
let cpf: str = p.curfile;
|
||||
let cpl: i32 = p.curline;
|
||||
let cpc: i32 = p.curcol;
|
||||
advance(p); // past `case`
|
||||
let cs: *node = newnode(p.a, nkind.N_CASE, cpf, cpl, cpc);
|
||||
let eh: *node = nil;
|
||||
let et: *node = nil;
|
||||
if (p.curkind != tkind.TK_COLON) {
|
||||
p.nocast = 1;
|
||||
for (true) {
|
||||
let e: *node = parseexpr(p);
|
||||
if (eh == nil) { eh = e; }
|
||||
else { et.next = e; };
|
||||
et = e;
|
||||
if (!accepttok(p, tkind.TK_COMMA)) { break; };
|
||||
};
|
||||
p.nocast = 0;
|
||||
};
|
||||
cs.list = eh;
|
||||
expecttok(p, tkind.TK_COLON, "expected ':' after case label");
|
||||
let bh: *node = nil;
|
||||
let bt: *node = nil;
|
||||
for (p.curkind != tkind.TK_CASE) {
|
||||
if (p.curkind == tkind.TK_RBRACE) { break; };
|
||||
if (p.curkind == tkind.TK_EOF) { break; };
|
||||
let s: *node = parsestmt(p);
|
||||
if (s != nil) {
|
||||
if (bh == nil) { bh = s; }
|
||||
else { bt.next = s; };
|
||||
bt = s;
|
||||
};
|
||||
};
|
||||
let blk: *node = newnode(p.a, nkind.N_BLOCK, cpf, cpl, cpc);
|
||||
blk.list = bh;
|
||||
cs.body = blk;
|
||||
if (head == nil) { head = cs; }
|
||||
else { tail.next = cs; };
|
||||
tail = cs;
|
||||
};
|
||||
expecttok(p, tkind.TK_RBRACE, "expected '}' to close switch");
|
||||
n.list = head;
|
||||
return n;
|
||||
};
|
||||
|
||||
fn parsestmt(p: *parser) *node = {
|
||||
let pf: str = p.curfile;
|
||||
let pl: i32 = p.curline;
|
||||
@@ -2769,6 +2826,11 @@ fn parsestmt(p: *parser) *node = {
|
||||
expecttok(p, tkind.TK_SEMI, "expected ';' after for");
|
||||
return n;
|
||||
};
|
||||
if (p.curkind == tkind.TK_SWITCH) {
|
||||
let n: *node = parseswitch(p);
|
||||
expecttok(p, tkind.TK_SEMI, "expected ';' after switch");
|
||||
return n;
|
||||
};
|
||||
if (p.curkind == tkind.TK_RETURN) {
|
||||
advance(p);
|
||||
let n: *node = newnode(p.a, nkind.N_RETURN, pf, pl, pc);
|
||||
@@ -8195,6 +8257,8 @@ fn cgstmt(c: *cgen, n: *node) void = {
|
||||
|
||||
if (k == nkind.N_FOR) { cgfor(c, n); return; };
|
||||
|
||||
if (k == nkind.N_SWITCH) { cgswitch(c, n); return; };
|
||||
|
||||
if (k == nkind.N_MASSIGN) { cgmassign(c, n); return; };
|
||||
|
||||
if (k == nkind.N_MLET) { cgmlet(c, n); return; };
|
||||
@@ -8877,6 +8941,65 @@ fn cgmlet(c: *cgen, n: *node) void = {
|
||||
return;
|
||||
};
|
||||
|
||||
// cgswitch — lower `switch (e) { case 1, 2: ...; case: default; }` to
|
||||
// a chain of compares against the scrutinee. Scrutinee lands in a
|
||||
// fresh 8B local slot so case bodies can spill SP without losing it.
|
||||
// Cases are tried top-to-bottom; the `case:` arm with no exprs is the
|
||||
// default and runs after all named arms fail. Mirrors cmd/w6c/cgen.c
|
||||
// N_SWITCH: same labelseq consumption order so labels match byte-for-
|
||||
// byte.
|
||||
fn cgswitch(c: *cgen, n: *node) void = {
|
||||
let swname: str = mkscratchname(c, "sw");
|
||||
let sloff: i32 = localalloc(c, swname, 8, nil);
|
||||
|
||||
if (n.lhs != nil) { cgexpr(c, n.lhs); };
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff(sloff: i64);
|
||||
emitline("(BP)\n");
|
||||
|
||||
let endl: str = mklabel(c, "swend");
|
||||
let defcase: *node = nil;
|
||||
|
||||
let cs: *node = n.list;
|
||||
for (cs != nil) {
|
||||
if (cs.list == nil) {
|
||||
defcase = cs;
|
||||
cs = cs.next;
|
||||
continue;
|
||||
};
|
||||
let body: str = mklabel(c, "swcase");
|
||||
let nxt: str = mklabel(c, "swnext");
|
||||
let e: *node = cs.list;
|
||||
for (e != nil) {
|
||||
cgexpr(c, e);
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(sloff: i64);
|
||||
emitline("(BP), BX\n");
|
||||
emitline("\tCMPQ\tBX, AX\n");
|
||||
emitline("\tJE\t");
|
||||
emitline(body);
|
||||
emitline("\n");
|
||||
e = e.next;
|
||||
};
|
||||
emitline("\tJMP\t");
|
||||
emitline(nxt);
|
||||
emitline("\n");
|
||||
emitlabel(body);
|
||||
if (cs.body != nil) { cgstmt(c, cs.body); };
|
||||
emitline("\tJMP\t");
|
||||
emitline(endl);
|
||||
emitline("\n");
|
||||
emitlabel(nxt);
|
||||
cs = cs.next;
|
||||
};
|
||||
if (defcase != nil) {
|
||||
if (defcase.body != nil) { cgstmt(c, defcase.body); };
|
||||
};
|
||||
emitlabel(endl);
|
||||
c.lastwasreturn = 0;
|
||||
return;
|
||||
};
|
||||
|
||||
fn cgbreak(c: *cgen, n: *node) void = {
|
||||
if (c.looptop > 0) {
|
||||
let lbl: str = c.loopendbuf[c.looptop - 1];
|
||||
@@ -8987,6 +9110,11 @@ fn scanlocals(c: *cgen, n: *node) i32 = {
|
||||
bidx += 1;
|
||||
};
|
||||
};
|
||||
// `switch` allocates an 8B scratch slot for the scrutinee so case
|
||||
// bodies can spill through SP without losing it. The slot is named
|
||||
// ".sw_<labelseq>" at cgen time — unique per switch — so it must
|
||||
// not dedup. Count it here so the frame SUBQ matches.
|
||||
if (n.kind == nkind.N_SWITCH) { total += 8; };
|
||||
// Match-arm binding (`case let v: T => ...`) gets a slot too.
|
||||
// Crucially we do NOT dedup these against c.locals: C cgen
|
||||
// handles a match as an expression with a by-value locals copy,
|
||||
@@ -9750,6 +9878,37 @@ fn emitlabel(s: str) void = {
|
||||
emitline(":\n");
|
||||
};
|
||||
|
||||
// mkscratchname — fresh local-slot name ".<base>_<labelseq>". Used for
|
||||
// compiler-synthesised slots (switch scrutinee, forrange index/len)
|
||||
// that need to be unique per use site but are never referenced by user
|
||||
// code. Increments labelseq so the same source position lines up with
|
||||
// C cgen's labelseq stream.
|
||||
fn mkscratchname(c: *cgen, base: str) str = {
|
||||
let buf: [128]u8;
|
||||
let i: i32 = 0;
|
||||
buf[i] = 46u8; i += 1; // '.'
|
||||
let j: i32 = 0;
|
||||
for (j < base.len) {
|
||||
buf[i] = base[j];
|
||||
i += 1; j += 1;
|
||||
};
|
||||
buf[i] = 95u8; i += 1; // '_'
|
||||
let n: i32 = strconv.i64tos(buf[i:128], c.labelseq: i64);
|
||||
c.labelseq += 1;
|
||||
let total: i32 = i + n;
|
||||
let p: *u8 = amalloc(c.a, (total: u64) + 1u64): *u8;
|
||||
let k: i32 = 0;
|
||||
for (k < total) {
|
||||
p[k] = buf[k];
|
||||
k += 1;
|
||||
};
|
||||
p[total] = 0u8;
|
||||
let r: str;
|
||||
r.ptr = p;
|
||||
r.len = total;
|
||||
return r;
|
||||
};
|
||||
|
||||
// ---- string interning ------------------------------------------------
|
||||
//
|
||||
// streq is provided by sym.ww and reused here.
|
||||
|
||||
@@ -524,6 +524,37 @@ fn emitlabel(s: str) void = {
|
||||
emitline(":\n");
|
||||
};
|
||||
|
||||
// mkscratchname — fresh local-slot name ".<base>_<labelseq>". Used for
|
||||
// compiler-synthesised slots (switch scrutinee, forrange index/len)
|
||||
// that need to be unique per use site but are never referenced by user
|
||||
// code. Increments labelseq so the same source position lines up with
|
||||
// C cgen's labelseq stream.
|
||||
fn mkscratchname(c: *cgen, base: str) str = {
|
||||
let buf: [128]u8;
|
||||
let i: i32 = 0;
|
||||
buf[i] = 46u8; i += 1; // '.'
|
||||
let j: i32 = 0;
|
||||
for (j < base.len) {
|
||||
buf[i] = base[j];
|
||||
i += 1; j += 1;
|
||||
};
|
||||
buf[i] = 95u8; i += 1; // '_'
|
||||
let n: i32 = strconv.i64tos(buf[i:128], c.labelseq: i64);
|
||||
c.labelseq += 1;
|
||||
let total: i32 = i + n;
|
||||
let p: *u8 = amalloc(c.a, (total: u64) + 1u64): *u8;
|
||||
let k: i32 = 0;
|
||||
for (k < total) {
|
||||
p[k] = buf[k];
|
||||
k += 1;
|
||||
};
|
||||
p[total] = 0u8;
|
||||
let r: str;
|
||||
r.ptr = p;
|
||||
r.len = total;
|
||||
return r;
|
||||
};
|
||||
|
||||
// ---- string interning ------------------------------------------------
|
||||
//
|
||||
// streq is provided by sym.ww and reused here.
|
||||
|
||||
@@ -87,6 +87,11 @@ fn scanlocals(c: *cgen, n: *node) i32 = {
|
||||
bidx += 1;
|
||||
};
|
||||
};
|
||||
// `switch` allocates an 8B scratch slot for the scrutinee so case
|
||||
// bodies can spill through SP without losing it. The slot is named
|
||||
// ".sw_<labelseq>" at cgen time — unique per switch — so it must
|
||||
// not dedup. Count it here so the frame SUBQ matches.
|
||||
if (n.kind == nkind.N_SWITCH) { total += 8; };
|
||||
// Match-arm binding (`case let v: T => ...`) gets a slot too.
|
||||
// Crucially we do NOT dedup these against c.locals: C cgen
|
||||
// handles a match as an expression with a by-value locals copy,
|
||||
|
||||
@@ -34,6 +34,8 @@ fn cgstmt(c: *cgen, n: *node) void = {
|
||||
|
||||
if (k == nkind.N_FOR) { cgfor(c, n); return; };
|
||||
|
||||
if (k == nkind.N_SWITCH) { cgswitch(c, n); return; };
|
||||
|
||||
if (k == nkind.N_MASSIGN) { cgmassign(c, n); return; };
|
||||
|
||||
if (k == nkind.N_MLET) { cgmlet(c, n); return; };
|
||||
@@ -716,6 +718,65 @@ fn cgmlet(c: *cgen, n: *node) void = {
|
||||
return;
|
||||
};
|
||||
|
||||
// cgswitch — lower `switch (e) { case 1, 2: ...; case: default; }` to
|
||||
// a chain of compares against the scrutinee. Scrutinee lands in a
|
||||
// fresh 8B local slot so case bodies can spill SP without losing it.
|
||||
// Cases are tried top-to-bottom; the `case:` arm with no exprs is the
|
||||
// default and runs after all named arms fail. Mirrors cmd/w6c/cgen.c
|
||||
// N_SWITCH: same labelseq consumption order so labels match byte-for-
|
||||
// byte.
|
||||
fn cgswitch(c: *cgen, n: *node) void = {
|
||||
let swname: str = mkscratchname(c, "sw");
|
||||
let sloff: i32 = localalloc(c, swname, 8, nil);
|
||||
|
||||
if (n.lhs != nil) { cgexpr(c, n.lhs); };
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff(sloff: i64);
|
||||
emitline("(BP)\n");
|
||||
|
||||
let endl: str = mklabel(c, "swend");
|
||||
let defcase: *node = nil;
|
||||
|
||||
let cs: *node = n.list;
|
||||
for (cs != nil) {
|
||||
if (cs.list == nil) {
|
||||
defcase = cs;
|
||||
cs = cs.next;
|
||||
continue;
|
||||
};
|
||||
let body: str = mklabel(c, "swcase");
|
||||
let nxt: str = mklabel(c, "swnext");
|
||||
let e: *node = cs.list;
|
||||
for (e != nil) {
|
||||
cgexpr(c, e);
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(sloff: i64);
|
||||
emitline("(BP), BX\n");
|
||||
emitline("\tCMPQ\tBX, AX\n");
|
||||
emitline("\tJE\t");
|
||||
emitline(body);
|
||||
emitline("\n");
|
||||
e = e.next;
|
||||
};
|
||||
emitline("\tJMP\t");
|
||||
emitline(nxt);
|
||||
emitline("\n");
|
||||
emitlabel(body);
|
||||
if (cs.body != nil) { cgstmt(c, cs.body); };
|
||||
emitline("\tJMP\t");
|
||||
emitline(endl);
|
||||
emitline("\n");
|
||||
emitlabel(nxt);
|
||||
cs = cs.next;
|
||||
};
|
||||
if (defcase != nil) {
|
||||
if (defcase.body != nil) { cgstmt(c, defcase.body); };
|
||||
};
|
||||
emitlabel(endl);
|
||||
c.lastwasreturn = 0;
|
||||
return;
|
||||
};
|
||||
|
||||
fn cgbreak(c: *cgen, n: *node) void = {
|
||||
if (c.looptop > 0) {
|
||||
let lbl: str = c.loopendbuf[c.looptop - 1];
|
||||
|
||||
@@ -2743,6 +2743,63 @@ fn parsefor(p: *parser) *node = {
|
||||
return n;
|
||||
};
|
||||
|
||||
fn parseswitch(p: *parser) *node = {
|
||||
let pf: str = p.curfile;
|
||||
let pl: i32 = p.curline;
|
||||
let pc: i32 = p.curcol;
|
||||
advance(p); // past `switch`
|
||||
expecttok(p, tkind.TK_LPAREN, "expected '(' after switch");
|
||||
let n: *node = newnode(p.a, nkind.N_SWITCH, pf, pl, pc);
|
||||
n.lhs = parseexpr(p);
|
||||
expecttok(p, tkind.TK_RPAREN, "expected ')' after switch expression");
|
||||
expecttok(p, tkind.TK_LBRACE, "expected '{' to open switch body");
|
||||
let head: *node = nil;
|
||||
let tail: *node = nil;
|
||||
for (p.curkind == tkind.TK_CASE) {
|
||||
let cpf: str = p.curfile;
|
||||
let cpl: i32 = p.curline;
|
||||
let cpc: i32 = p.curcol;
|
||||
advance(p); // past `case`
|
||||
let cs: *node = newnode(p.a, nkind.N_CASE, cpf, cpl, cpc);
|
||||
let eh: *node = nil;
|
||||
let et: *node = nil;
|
||||
if (p.curkind != tkind.TK_COLON) {
|
||||
p.nocast = 1;
|
||||
for (true) {
|
||||
let e: *node = parseexpr(p);
|
||||
if (eh == nil) { eh = e; }
|
||||
else { et.next = e; };
|
||||
et = e;
|
||||
if (!accepttok(p, tkind.TK_COMMA)) { break; };
|
||||
};
|
||||
p.nocast = 0;
|
||||
};
|
||||
cs.list = eh;
|
||||
expecttok(p, tkind.TK_COLON, "expected ':' after case label");
|
||||
let bh: *node = nil;
|
||||
let bt: *node = nil;
|
||||
for (p.curkind != tkind.TK_CASE) {
|
||||
if (p.curkind == tkind.TK_RBRACE) { break; };
|
||||
if (p.curkind == tkind.TK_EOF) { break; };
|
||||
let s: *node = parsestmt(p);
|
||||
if (s != nil) {
|
||||
if (bh == nil) { bh = s; }
|
||||
else { bt.next = s; };
|
||||
bt = s;
|
||||
};
|
||||
};
|
||||
let blk: *node = newnode(p.a, nkind.N_BLOCK, cpf, cpl, cpc);
|
||||
blk.list = bh;
|
||||
cs.body = blk;
|
||||
if (head == nil) { head = cs; }
|
||||
else { tail.next = cs; };
|
||||
tail = cs;
|
||||
};
|
||||
expecttok(p, tkind.TK_RBRACE, "expected '}' to close switch");
|
||||
n.list = head;
|
||||
return n;
|
||||
};
|
||||
|
||||
fn parsestmt(p: *parser) *node = {
|
||||
let pf: str = p.curfile;
|
||||
let pl: i32 = p.curline;
|
||||
@@ -2769,6 +2826,11 @@ fn parsestmt(p: *parser) *node = {
|
||||
expecttok(p, tkind.TK_SEMI, "expected ';' after for");
|
||||
return n;
|
||||
};
|
||||
if (p.curkind == tkind.TK_SWITCH) {
|
||||
let n: *node = parseswitch(p);
|
||||
expecttok(p, tkind.TK_SEMI, "expected ';' after switch");
|
||||
return n;
|
||||
};
|
||||
if (p.curkind == tkind.TK_RETURN) {
|
||||
advance(p);
|
||||
let n: *node = newnode(p.a, nkind.N_RETURN, pf, pl, pc);
|
||||
@@ -8195,6 +8257,8 @@ fn cgstmt(c: *cgen, n: *node) void = {
|
||||
|
||||
if (k == nkind.N_FOR) { cgfor(c, n); return; };
|
||||
|
||||
if (k == nkind.N_SWITCH) { cgswitch(c, n); return; };
|
||||
|
||||
if (k == nkind.N_MASSIGN) { cgmassign(c, n); return; };
|
||||
|
||||
if (k == nkind.N_MLET) { cgmlet(c, n); return; };
|
||||
@@ -8877,6 +8941,65 @@ fn cgmlet(c: *cgen, n: *node) void = {
|
||||
return;
|
||||
};
|
||||
|
||||
// cgswitch — lower `switch (e) { case 1, 2: ...; case: default; }` to
|
||||
// a chain of compares against the scrutinee. Scrutinee lands in a
|
||||
// fresh 8B local slot so case bodies can spill SP without losing it.
|
||||
// Cases are tried top-to-bottom; the `case:` arm with no exprs is the
|
||||
// default and runs after all named arms fail. Mirrors cmd/w6c/cgen.c
|
||||
// N_SWITCH: same labelseq consumption order so labels match byte-for-
|
||||
// byte.
|
||||
fn cgswitch(c: *cgen, n: *node) void = {
|
||||
let swname: str = mkscratchname(c, "sw");
|
||||
let sloff: i32 = localalloc(c, swname, 8, nil);
|
||||
|
||||
if (n.lhs != nil) { cgexpr(c, n.lhs); };
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff(sloff: i64);
|
||||
emitline("(BP)\n");
|
||||
|
||||
let endl: str = mklabel(c, "swend");
|
||||
let defcase: *node = nil;
|
||||
|
||||
let cs: *node = n.list;
|
||||
for (cs != nil) {
|
||||
if (cs.list == nil) {
|
||||
defcase = cs;
|
||||
cs = cs.next;
|
||||
continue;
|
||||
};
|
||||
let body: str = mklabel(c, "swcase");
|
||||
let nxt: str = mklabel(c, "swnext");
|
||||
let e: *node = cs.list;
|
||||
for (e != nil) {
|
||||
cgexpr(c, e);
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(sloff: i64);
|
||||
emitline("(BP), BX\n");
|
||||
emitline("\tCMPQ\tBX, AX\n");
|
||||
emitline("\tJE\t");
|
||||
emitline(body);
|
||||
emitline("\n");
|
||||
e = e.next;
|
||||
};
|
||||
emitline("\tJMP\t");
|
||||
emitline(nxt);
|
||||
emitline("\n");
|
||||
emitlabel(body);
|
||||
if (cs.body != nil) { cgstmt(c, cs.body); };
|
||||
emitline("\tJMP\t");
|
||||
emitline(endl);
|
||||
emitline("\n");
|
||||
emitlabel(nxt);
|
||||
cs = cs.next;
|
||||
};
|
||||
if (defcase != nil) {
|
||||
if (defcase.body != nil) { cgstmt(c, defcase.body); };
|
||||
};
|
||||
emitlabel(endl);
|
||||
c.lastwasreturn = 0;
|
||||
return;
|
||||
};
|
||||
|
||||
fn cgbreak(c: *cgen, n: *node) void = {
|
||||
if (c.looptop > 0) {
|
||||
let lbl: str = c.loopendbuf[c.looptop - 1];
|
||||
@@ -8987,6 +9110,11 @@ fn scanlocals(c: *cgen, n: *node) i32 = {
|
||||
bidx += 1;
|
||||
};
|
||||
};
|
||||
// `switch` allocates an 8B scratch slot for the scrutinee so case
|
||||
// bodies can spill through SP without losing it. The slot is named
|
||||
// ".sw_<labelseq>" at cgen time — unique per switch — so it must
|
||||
// not dedup. Count it here so the frame SUBQ matches.
|
||||
if (n.kind == nkind.N_SWITCH) { total += 8; };
|
||||
// Match-arm binding (`case let v: T => ...`) gets a slot too.
|
||||
// Crucially we do NOT dedup these against c.locals: C cgen
|
||||
// handles a match as an expression with a by-value locals copy,
|
||||
@@ -9750,6 +9878,37 @@ fn emitlabel(s: str) void = {
|
||||
emitline(":\n");
|
||||
};
|
||||
|
||||
// mkscratchname — fresh local-slot name ".<base>_<labelseq>". Used for
|
||||
// compiler-synthesised slots (switch scrutinee, forrange index/len)
|
||||
// that need to be unique per use site but are never referenced by user
|
||||
// code. Increments labelseq so the same source position lines up with
|
||||
// C cgen's labelseq stream.
|
||||
fn mkscratchname(c: *cgen, base: str) str = {
|
||||
let buf: [128]u8;
|
||||
let i: i32 = 0;
|
||||
buf[i] = 46u8; i += 1; // '.'
|
||||
let j: i32 = 0;
|
||||
for (j < base.len) {
|
||||
buf[i] = base[j];
|
||||
i += 1; j += 1;
|
||||
};
|
||||
buf[i] = 95u8; i += 1; // '_'
|
||||
let n: i32 = strconv.i64tos(buf[i:128], c.labelseq: i64);
|
||||
c.labelseq += 1;
|
||||
let total: i32 = i + n;
|
||||
let p: *u8 = amalloc(c.a, (total: u64) + 1u64): *u8;
|
||||
let k: i32 = 0;
|
||||
for (k < total) {
|
||||
p[k] = buf[k];
|
||||
k += 1;
|
||||
};
|
||||
p[total] = 0u8;
|
||||
let r: str;
|
||||
r.ptr = p;
|
||||
r.len = total;
|
||||
return r;
|
||||
};
|
||||
|
||||
// ---- string interning ------------------------------------------------
|
||||
//
|
||||
// streq is provided by sym.ww and reused here.
|
||||
|
||||
@@ -345,6 +345,21 @@ probe_ww_compile(const char *bin)
|
||||
" dec(&c); dec(&c); dec(&c);\n"
|
||||
" return c.x;\n"
|
||||
"};", 2 },
|
||||
/* switch with multi-expr cases + default. Lowers to a chain
|
||||
* of CMPQ + JE; the scrutinee lands in a fresh 8B local slot
|
||||
* (".sw_N") so case bodies can spill SP without losing it.
|
||||
* classify(2)=10, classify(7)=70, classify(99)=0, sum 80. */
|
||||
{ "fn classify(x: i32) i32 = {\n"
|
||||
" switch (x) {\n"
|
||||
" case 1, 2, 3: return 10;\n"
|
||||
" case 7: return 70;\n"
|
||||
" case: return 0;\n"
|
||||
" };\n"
|
||||
" return -1;\n"
|
||||
"};\n"
|
||||
"fn main() i32 = {\n"
|
||||
" return classify(2) + classify(7) + classify(99);\n"
|
||||
"};", 80 },
|
||||
/* Chained N_DOT: o.ptr_to_inner.val (read field through a
|
||||
* pointer-to-struct field). Pre-fix the cgen fell through
|
||||
* silently and returned the inner pointer instead of the
|
||||
|
||||
@@ -149,6 +149,16 @@ main(void)
|
||||
" let m: mode = mode.RW;\n"
|
||||
" return m as i32;\n"
|
||||
"};" },
|
||||
{ "switch",
|
||||
"fn classify(x: i32) i32 = {\n"
|
||||
" switch (x) {\n"
|
||||
" case 1, 2, 3: return 10;\n"
|
||||
" case 7: return 70;\n"
|
||||
" case: return 0;\n"
|
||||
" };\n"
|
||||
" return -1;\n"
|
||||
"};\n"
|
||||
"fn main() i32 = { return classify(2); };" },
|
||||
{ NULL, NULL },
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user