selfhost: mirror defer; e2e tests for LIFO ordering
C cgen has carried defer for a while (defers[] global + reverse walk on every return). Selfhost cgen now mirrors: - cgen struct: deferbuf (**node, LIFO stack) + defertop counter. - cgstmt N_DEFER: push n.lhs. - cgreturn: rundefers() at entry — same as the C cgen pattern. - cgfn fall-through return: rundefers() before zero-AX+RET. DEFER_MAX = 16 matches C cgen. Two new e2e rows: defer with an explicit `return acc;` (321 mod 256 = 65), and defer firing on an implicit void-fn fall-through (87). Both rows verified via the wwstage cgen too. Defer's semantics: queued exprs fire LIFO before the return expr is evaluated, so a return that reads memory mutated by a deferred call sees the post-defer state. Matches C cgen and Hare.
This commit is contained in:
@@ -43,6 +43,14 @@ fn cgstmt(c: *cgen, n: *node) void = {
|
||||
|
||||
if (k == N_YIELD) { cgyield(c, n); return; };
|
||||
|
||||
if (k == N_DEFER) {
|
||||
if (c.defertop < DEFER_MAX) {
|
||||
c.deferbuf[c.defertop] = n.lhs;
|
||||
c.defertop += 1;
|
||||
};
|
||||
return;
|
||||
};
|
||||
|
||||
c.lastwasreturn = 0;
|
||||
};
|
||||
|
||||
@@ -70,7 +78,19 @@ fn cgblock(c: *cgen, n: *node) void = {
|
||||
return;
|
||||
};
|
||||
|
||||
// rundefers — emit cgexpr for every queued defer in LIFO order.
|
||||
// Called from cgreturn and the cgfn implicit-return path.
|
||||
fn rundefers(c: *cgen) void = {
|
||||
let i: i32 = c.defertop - 1;
|
||||
for (i >= 0) {
|
||||
cgexpr(c, c.deferbuf[i]);
|
||||
i -= 1;
|
||||
};
|
||||
return;
|
||||
};
|
||||
|
||||
fn cgreturn(c: *cgen, n: *node) void = {
|
||||
rundefers(c);
|
||||
let rhs: *node = n.lhs;
|
||||
if (rhs != nil) {
|
||||
// Tuple return `return a, b;`:
|
||||
|
||||
Reference in New Issue
Block a user