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:
@@ -4130,16 +4130,30 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
* bytes. Size comes from the value's static type.
|
||||
* `n->lhs->type == ty_err` gate (mirrors assert above)
|
||||
* — check.c only stamps ty_err when no user-scoped
|
||||
* `alloc` shadows the builtin (task #23). */
|
||||
* `alloc` shadows the builtin (task #23).
|
||||
*
|
||||
* Result is the graduated `(*T | nomem)` tagged-pointer
|
||||
* ABI (AX=tag, DX=ptr) — task #30. rt_alloc returns 0
|
||||
* on OOM (rt/alloc.s); we branch on AX, building tag=1
|
||||
* (nomem, DX=0) on null and tag=0 (success, DX=ptr)
|
||||
* after the value-init stores complete. Callers wrap
|
||||
* with `!` / `?` to consume the union. */
|
||||
Node *v = n->list;
|
||||
Type *t = v->type;
|
||||
Type *u = (t && t->kind == TY_NAMED) ? t->under : t;
|
||||
Type *def = type_default(t);
|
||||
int sz = def ? (int)def->size : 8;
|
||||
if (sz == 0) sz = 8;
|
||||
/* call os.alloc(sz) */
|
||||
char *alloc_ok = mklabel(c, "alloc_ok");
|
||||
char *alloc_done = mklabel(c, "alloc_done");
|
||||
ins2(c, A_MOVQ, aimm(sz), areg(D_DI));
|
||||
ins1(c, A_CALL, asym(ffi_resolve("alloc")));
|
||||
ins2(c, A_CMPQ, aimm(0), areg(D_AX));
|
||||
ins1(c, A_JNE, abranch(alloc_ok));
|
||||
ins2(c, A_MOVQ, aimm(1), areg(D_AX));
|
||||
ins2(c, A_MOVQ, aimm(0), areg(D_DX));
|
||||
ins1(c, A_JMP, abranch(alloc_done));
|
||||
label(c, alloc_ok);
|
||||
ins1(c, A_PUSHQ, areg(D_AX)); /* save ptr */
|
||||
if (v->kind == N_STRUCTLIT && u && u->kind == TY_STRUCT) {
|
||||
for (Node *f = v->list; f; f = f->next) {
|
||||
@@ -4190,7 +4204,9 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
else if (sz == 4) op = A_MOVL;
|
||||
ins2(c, op, areg(D_AX), amem(D_BX, 0));
|
||||
}
|
||||
ins1(c, A_POPQ, areg(D_AX)); /* return the ptr */
|
||||
ins1(c, A_POPQ, areg(D_DX)); /* DX = success ptr */
|
||||
ins2(c, A_MOVQ, aimm(0), areg(D_AX));
|
||||
label(c, alloc_done);
|
||||
break;
|
||||
}
|
||||
if (n->lhs && n->lhs->kind == N_IDENT && n->lhs->str &&
|
||||
@@ -6346,29 +6362,52 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
|
||||
int isf32 = type_isf32(lt);
|
||||
/* alloc([], n) initialiser for a slice local: allocate
|
||||
* n*esize bytes, build the {ptr, 0, n} header in the slot.
|
||||
* Element size comes from the declared slice type. */
|
||||
if (n->rhs && lu && lu->kind == TY_SLICE && sz == 24
|
||||
&& n->rhs->kind == N_CALL && n->rhs->lhs
|
||||
&& n->rhs->lhs->kind == N_IDENT
|
||||
&& strcmp(n->rhs->lhs->str, "alloc") == 0
|
||||
&& n->rhs->list && n->rhs->list->kind == N_ARRLIT
|
||||
&& n->rhs->list->list == NULL
|
||||
&& n->rhs->list->next && n->rhs->list->next->next == NULL) {
|
||||
Node *count = n->rhs->list->next;
|
||||
int esz = (lu->sub) ? (int)lu->sub->size : 1;
|
||||
cgexpr(c, count, *locals); /* AX = n */
|
||||
ins1(c, A_PUSHQ, areg(D_AX)); /* save count */
|
||||
if (esz > 1) {
|
||||
ins2(c, A_MOVQ, aimm(esz), areg(D_BX));
|
||||
ins2(c, A_IMULQ, areg(D_BX), areg(D_AX));
|
||||
* Element size comes from the declared slice type.
|
||||
*
|
||||
* Task #30 graduated the builtin to `([]T | nomem)`. The let
|
||||
* declares a bare `[]T`, so the canonical idiom wraps in `!`
|
||||
* to abort on OOM; walk into the N_TRYUNW to keep the
|
||||
* direct-store fast path. */
|
||||
{
|
||||
Node *call = NULL;
|
||||
int via_tryunw = 0;
|
||||
if (n->rhs && n->rhs->kind == N_TRYUNW && n->rhs->lhs
|
||||
&& n->rhs->lhs->kind == N_CALL) {
|
||||
call = n->rhs->lhs;
|
||||
via_tryunw = 1;
|
||||
}
|
||||
if (call && lu && lu->kind == TY_SLICE && sz == 24
|
||||
&& call->lhs && call->lhs->kind == N_IDENT
|
||||
&& strcmp(call->lhs->str, "alloc") == 0
|
||||
&& call->list && call->list->kind == N_ARRLIT
|
||||
&& call->list->list == NULL
|
||||
&& call->list->next
|
||||
&& call->list->next->next == NULL) {
|
||||
Node *count = call->list->next;
|
||||
int esz = (lu->sub) ? (int)lu->sub->size : 1;
|
||||
cgexpr(c, count, *locals); /* AX = n */
|
||||
ins1(c, A_PUSHQ, areg(D_AX)); /* save count */
|
||||
if (esz > 1) {
|
||||
ins2(c, A_MOVQ, aimm(esz), areg(D_BX));
|
||||
ins2(c, A_IMULQ, areg(D_BX), areg(D_AX));
|
||||
}
|
||||
ins2(c, A_MOVQ, areg(D_AX), areg(D_DI));
|
||||
ins1(c, A_CALL, asym(ffi_resolve("alloc")));
|
||||
if (via_tryunw) {
|
||||
char *ok = mklabel(c, "tryunw_ok");
|
||||
ins2(c, A_CMPQ, aimm(0), areg(D_AX));
|
||||
ins1(c, A_JNE, abranch(ok));
|
||||
ins2(c, A_MOVQ, aimm(1), areg(D_DI));
|
||||
ins2(c, A_MOVQ, aimm(60), areg(D_AX));
|
||||
ins0(c, A_SYSCALL);
|
||||
label(c, ok);
|
||||
}
|
||||
ins1(c, A_POPQ, areg(D_BX)); /* count */
|
||||
ins2(c, A_MOVQ, areg(D_AX), amem(D_BP, off + 0));
|
||||
ins2(c, A_MOVQ, aimm(0), amem(D_BP, off + 8));
|
||||
ins2(c, A_MOVQ, areg(D_BX), amem(D_BP, off + 16));
|
||||
break;
|
||||
}
|
||||
ins2(c, A_MOVQ, areg(D_AX), areg(D_DI));
|
||||
ins1(c, A_CALL, asym(ffi_resolve("alloc")));
|
||||
ins1(c, A_POPQ, areg(D_BX)); /* count */
|
||||
ins2(c, A_MOVQ, areg(D_AX), amem(D_BP, off + 0));
|
||||
ins2(c, A_MOVQ, aimm(0), amem(D_BP, off + 8));
|
||||
ins2(c, A_MOVQ, areg(D_BX), amem(D_BP, off + 16));
|
||||
break;
|
||||
}
|
||||
/* str initialiser: cgexpr produces (AX=ptr, BX=len). */
|
||||
if (n->rhs && type_isstr(lt) && sz == 16) {
|
||||
|
||||
Reference in New Issue
Block a user