cmd+rt+selfhost+test: graduate alloc to (*T | nomem) / ([]T | nomem)
Per Hare convention, alloc is a typed builtin that returns a tagged
union carrying nomem as the OOM variant. Callers spell their policy:
`alloc(T)!` aborts on OOM (the old behavior), `alloc(T)?` propagates
when the enclosing fn already returns nomem.
cstage: check builds TY_TAGGED{*T | nomem} (or {[]T | nomem}); cgen
emits AX=tag, DX=ptr per the general tagged-return ABI (the (*T|!void)
nullable-ptr fold gated in ea76ee4 keeps this clean). wwstage cgalloc
mirrors. rt/alloc.s zeroes AX on syscall error so the builtin's null
check sees a clean 0 instead of mmap's -errno leaking through as a
poisoned pointer.
Migration: 3 `!` sites in test/wcc/700_e2e.c, 1 `!` site in
rt/ensure.ww (preserves the pre-existing sizeof bug tracked by #27),
1 `?` site in selfhost/test/tagged_ptr_ret.ww (allocbox exercises
real `?` propagation against a (*T | nomem) return).
130/130 tests green, 994_w6c_ww + 995_self_rebuild stage byte-identity
preserved. Follow-ups #31 (wwstage checkletassign leniency), #32
(wwstage slice-form gap), #33 (tagged_ptr_ret.ww make-test wiring).
This commit is contained in:
@@ -14581,7 +14581,14 @@ fn cgbin(c: *cgen, n: *node) void = {
|
||||
// region. For an N_STRUCTLIT arg, allocate the struct's totsize and
|
||||
// emit per-field stores at each field's offset. For a scalar/ptr,
|
||||
// allocate 8 bytes and store one word. Mirrors cmd/w6c/cgen.c's
|
||||
// alloc-special branch in N_CALL. Returns the heap ptr in AX.
|
||||
// alloc-special branch in N_CALL.
|
||||
//
|
||||
// Task #30: result is the graduated `(*T | nomem)` tagged-pointer
|
||||
// pair (AX=tag, DX=ptr). rt_alloc now returns 0 on OOM
|
||||
// (rt/alloc.s); branch on AX to emit the nomem variant (tag=1,
|
||||
// DX=0) or the success variant (tag=0, DX=ptr) after the
|
||||
// value-init stores complete. Callers wrap with `!` / `?` to
|
||||
// consume the union.
|
||||
fn cgalloc(c: *cgen, n: *node) void = {
|
||||
let v: *node = n.list;
|
||||
let sz: i32 = 8;
|
||||
@@ -14597,10 +14604,18 @@ fn cgalloc(c: *cgen, n: *node) void = {
|
||||
si = structlookup(c, sname);
|
||||
if (si != nil) { sz = si.totsize; };
|
||||
};
|
||||
let okl: str = mklabel(c, "alloc_ok");
|
||||
let donel: str = mklabel(c, "alloc_done");
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(sz: i64);
|
||||
emitline(", DI\n");
|
||||
emitline("\tCALL\trt_alloc(SB)\n");
|
||||
emitline("\tCMPQ\t$0, AX\n");
|
||||
emitline("\tJNE\t"); emitline(okl); emitline("\n");
|
||||
emitline("\tMOVQ\t$1, AX\n");
|
||||
emitline("\tMOVQ\t$0, DX\n");
|
||||
emitline("\tJMP\t"); emitline(donel); emitline("\n");
|
||||
emitlabel(okl);
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
if (v.kind == nkind.N_STRUCTLIT) {
|
||||
if (si != nil) {
|
||||
@@ -14653,7 +14668,9 @@ fn cgalloc(c: *cgen, n: *node) void = {
|
||||
emitline(sop);
|
||||
emitline("\tAX, (BX)\n");
|
||||
};
|
||||
emitline("\tPOPQ\tAX\n");
|
||||
emitline("\tPOPQ\tDX\n");
|
||||
emitline("\tMOVQ\t$0, AX\n");
|
||||
emitlabel(donel);
|
||||
};
|
||||
|
||||
// cgappend — Hare-style `append(s, v)` / `append(s, items...)` lowering.
|
||||
|
||||
@@ -2711,7 +2711,14 @@ fn cgbin(c: *cgen, n: *node) void = {
|
||||
// region. For an N_STRUCTLIT arg, allocate the struct's totsize and
|
||||
// emit per-field stores at each field's offset. For a scalar/ptr,
|
||||
// allocate 8 bytes and store one word. Mirrors cmd/w6c/cgen.c's
|
||||
// alloc-special branch in N_CALL. Returns the heap ptr in AX.
|
||||
// alloc-special branch in N_CALL.
|
||||
//
|
||||
// Task #30: result is the graduated `(*T | nomem)` tagged-pointer
|
||||
// pair (AX=tag, DX=ptr). rt_alloc now returns 0 on OOM
|
||||
// (rt/alloc.s); branch on AX to emit the nomem variant (tag=1,
|
||||
// DX=0) or the success variant (tag=0, DX=ptr) after the
|
||||
// value-init stores complete. Callers wrap with `!` / `?` to
|
||||
// consume the union.
|
||||
fn cgalloc(c: *cgen, n: *node) void = {
|
||||
let v: *node = n.list;
|
||||
let sz: i32 = 8;
|
||||
@@ -2727,10 +2734,18 @@ fn cgalloc(c: *cgen, n: *node) void = {
|
||||
si = structlookup(c, sname);
|
||||
if (si != nil) { sz = si.totsize; };
|
||||
};
|
||||
let okl: str = mklabel(c, "alloc_ok");
|
||||
let donel: str = mklabel(c, "alloc_done");
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(sz: i64);
|
||||
emitline(", DI\n");
|
||||
emitline("\tCALL\trt_alloc(SB)\n");
|
||||
emitline("\tCMPQ\t$0, AX\n");
|
||||
emitline("\tJNE\t"); emitline(okl); emitline("\n");
|
||||
emitline("\tMOVQ\t$1, AX\n");
|
||||
emitline("\tMOVQ\t$0, DX\n");
|
||||
emitline("\tJMP\t"); emitline(donel); emitline("\n");
|
||||
emitlabel(okl);
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
if (v.kind == nkind.N_STRUCTLIT) {
|
||||
if (si != nil) {
|
||||
@@ -2783,7 +2798,9 @@ fn cgalloc(c: *cgen, n: *node) void = {
|
||||
emitline(sop);
|
||||
emitline("\tAX, (BX)\n");
|
||||
};
|
||||
emitline("\tPOPQ\tAX\n");
|
||||
emitline("\tPOPQ\tDX\n");
|
||||
emitline("\tMOVQ\t$0, AX\n");
|
||||
emitlabel(donel);
|
||||
};
|
||||
|
||||
// cgappend — Hare-style `append(s, v)` / `append(s, items...)` lowering.
|
||||
|
||||
@@ -14581,7 +14581,14 @@ fn cgbin(c: *cgen, n: *node) void = {
|
||||
// region. For an N_STRUCTLIT arg, allocate the struct's totsize and
|
||||
// emit per-field stores at each field's offset. For a scalar/ptr,
|
||||
// allocate 8 bytes and store one word. Mirrors cmd/w6c/cgen.c's
|
||||
// alloc-special branch in N_CALL. Returns the heap ptr in AX.
|
||||
// alloc-special branch in N_CALL.
|
||||
//
|
||||
// Task #30: result is the graduated `(*T | nomem)` tagged-pointer
|
||||
// pair (AX=tag, DX=ptr). rt_alloc now returns 0 on OOM
|
||||
// (rt/alloc.s); branch on AX to emit the nomem variant (tag=1,
|
||||
// DX=0) or the success variant (tag=0, DX=ptr) after the
|
||||
// value-init stores complete. Callers wrap with `!` / `?` to
|
||||
// consume the union.
|
||||
fn cgalloc(c: *cgen, n: *node) void = {
|
||||
let v: *node = n.list;
|
||||
let sz: i32 = 8;
|
||||
@@ -14597,10 +14604,18 @@ fn cgalloc(c: *cgen, n: *node) void = {
|
||||
si = structlookup(c, sname);
|
||||
if (si != nil) { sz = si.totsize; };
|
||||
};
|
||||
let okl: str = mklabel(c, "alloc_ok");
|
||||
let donel: str = mklabel(c, "alloc_done");
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(sz: i64);
|
||||
emitline(", DI\n");
|
||||
emitline("\tCALL\trt_alloc(SB)\n");
|
||||
emitline("\tCMPQ\t$0, AX\n");
|
||||
emitline("\tJNE\t"); emitline(okl); emitline("\n");
|
||||
emitline("\tMOVQ\t$1, AX\n");
|
||||
emitline("\tMOVQ\t$0, DX\n");
|
||||
emitline("\tJMP\t"); emitline(donel); emitline("\n");
|
||||
emitlabel(okl);
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
if (v.kind == nkind.N_STRUCTLIT) {
|
||||
if (si != nil) {
|
||||
@@ -14653,7 +14668,9 @@ fn cgalloc(c: *cgen, n: *node) void = {
|
||||
emitline(sop);
|
||||
emitline("\tAX, (BX)\n");
|
||||
};
|
||||
emitline("\tPOPQ\tAX\n");
|
||||
emitline("\tPOPQ\tDX\n");
|
||||
emitline("\tMOVQ\t$0, AX\n");
|
||||
emitlabel(donel);
|
||||
};
|
||||
|
||||
// cgappend — Hare-style `append(s, v)` / `append(s, items...)` lowering.
|
||||
|
||||
@@ -13,10 +13,19 @@
|
||||
// Two match arms cover both runtime outcomes — success unwrap (tag=0,
|
||||
// ptr payload in DX) and error propagation (tag=1) — exercising the
|
||||
// same AX/DX ABI both stages must agree on.
|
||||
//
|
||||
// Task #30: the `alloc` builtin itself now returns `(*T | nomem)`. The
|
||||
// allocbox arm below propagates the builtin's tagged return through
|
||||
// the enclosing fn via `?` against a same-shape `(*point | nomem)`
|
||||
// — matching the team-lead spec's "exercise `alloc(T)?` against a
|
||||
// real function returning `(T | nomem)`".
|
||||
|
||||
package test;
|
||||
package main;
|
||||
|
||||
import fmt;
|
||||
import os;
|
||||
|
||||
type point = struct { x: i32, y: i32 };
|
||||
|
||||
fn alloc1(fail: i64) (*u8 | nomem) = {
|
||||
if (fail != 0i64) { let e: nomem; return e; };
|
||||
@@ -29,6 +38,11 @@ fn caller(fail: i64) (*u8 | nomem) = {
|
||||
return p;
|
||||
};
|
||||
|
||||
fn allocbox() (*point | nomem) = {
|
||||
let p: *point = alloc(point { x = 3, y = 4 })?;
|
||||
return p;
|
||||
};
|
||||
|
||||
export fn main() i32 = {
|
||||
let rc: i32 = 0;
|
||||
match (caller(0i64)) {
|
||||
@@ -50,5 +64,15 @@ export fn main() i32 = {
|
||||
fmt.println("nomem as expected");
|
||||
};
|
||||
};
|
||||
match (allocbox()) {
|
||||
case let p: *point => {
|
||||
fmt.println("allocbox ok");
|
||||
if (p.x * p.x + p.y * p.y != 25) { rc = 4; };
|
||||
};
|
||||
case nomem => {
|
||||
fmt.println("allocbox unexpected nomem");
|
||||
rc = 5;
|
||||
};
|
||||
};
|
||||
return rc;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user