ww: hare-feature batch (_, const, [_]T, ..., size/offset, assert, for-else)

This commit is contained in:
2026-05-11 19:38:12 +09:00
parent 579cc39f9b
commit 6219a47c6f
25 changed files with 1496 additions and 1049 deletions

View File

@@ -856,6 +856,14 @@ cgexpr(Cg *c, Node *n, Local *locals)
break;
}
case N_ASSIGN: {
/* Discard lvalue `_ = expr;` — evaluate rhs for side effects,
* write nothing. */
if (n->lhs && n->lhs->kind == N_IDENT &&
n->lhs->str && n->lhs->str[0] == '\0' &&
n->op == TK_ASSIGN) {
cgexpr(c, n->rhs, locals);
break;
}
/* p.x = v or p.x += v where p.x is a struct field
* (direct or via *struct). For compound ops we read-modify-
* write the field; for plain `=` we just write. */
@@ -1234,6 +1242,49 @@ cgexpr(Cg *c, Node *n, Local *locals)
break;
}
case N_CALL: {
/* abort([msg]) — call rt_abort. Empty msg becomes (NULL, 0).
* Only fires when the checker tagged the callee as a builtin
* (lhs->type == ty_err); a user-declared `abort` in scope is
* resolved through the regular call path. */
if (n->lhs && n->lhs->kind == N_IDENT && n->lhs->str &&
n->lhs->type == ty_err &&
strcmp(n->lhs->str, "abort") == 0) {
if (n->list) {
cgexpr(c, n->list, locals);
ins2(c, A_MOVQ, areg(D_AX), areg(D_DI));
ins2(c, A_MOVQ, areg(D_BX), areg(D_SI));
} else {
ins2(c, A_MOVQ, aimm(0), areg(D_DI));
ins2(c, A_MOVQ, aimm(0), areg(D_SI));
}
ins1(c, A_CALL, asym("rt_abort"));
break;
}
/* assert(cond[, msg]) — if !cond, call rt_abort. Compiles to:
* CMPQ $0, AX
* JNE skip
* <abort body>
* skip: */
if (n->lhs && n->lhs->kind == N_IDENT && n->lhs->str &&
n->lhs->type == ty_err &&
strcmp(n->lhs->str, "assert") == 0 && n->list) {
cgexpr(c, n->list, locals);
char *skip = mklabel(c, "as");
ins2(c, A_CMPQ, aimm(0), areg(D_AX));
ins1(c, A_JNE, abranch(skip));
Node *msg = n->list->next;
if (msg) {
cgexpr(c, msg, locals);
ins2(c, A_MOVQ, areg(D_AX), areg(D_DI));
ins2(c, A_MOVQ, areg(D_BX), areg(D_SI));
} else {
ins2(c, A_MOVQ, aimm(0), areg(D_DI));
ins2(c, A_MOVQ, aimm(0), areg(D_SI));
}
ins1(c, A_CALL, asym("rt_abort"));
label(c, skip);
break;
}
/* Hare-style builtins: len(x) and append(s, v). */
if (n->lhs && n->lhs->kind == N_IDENT && n->lhs->str &&
strcmp(n->lhs->str, "len") == 0 && n->list) {
@@ -2257,9 +2308,34 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
ins2(c, A_MOVQ, areg(D_BX), amem(D_BP, off + 16));
break;
}
/* struct literal initialiser: field-by-field store. */
/* struct literal initialiser: field-by-field store. The
* literal carries op == TK_ELLIPSIS when the source ends in
* `..., ...` — in that case zero-fill the entire slot first,
* so unmentioned fields read as 0. */
if (n->rhs && n->rhs->kind == N_STRUCTLIT && lu
&& lu->kind == TY_STRUCT) {
if (n->rhs->op == TK_ELLIPSIS) {
u64 sz = lu->size;
/* AX = 0 once, then store from AX. w6a doesn't
* accept MOVB imm,mem — use register stores. */
ins2(c, A_XORQ, areg(D_AX), areg(D_AX));
u64 i = 0;
while (i + 8 <= sz) {
ins2(c, A_MOVQ, areg(D_AX),
amem(D_BP, off + (int)i));
i += 8;
}
while (i + 4 <= sz) {
ins2(c, A_MOVL, areg(D_AX),
amem(D_BP, off + (int)i));
i += 4;
}
while (i < sz) {
ins2(c, A_MOVB, areg(D_AX),
amem(D_BP, off + (int)i));
i += 1;
}
}
for (Node *f = n->rhs->list; f; f = f->next) {
/* find offset of this field */
u64 foff = 0;
@@ -2315,6 +2391,46 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
}
break;
}
/* array literal initialiser: `let xs: [N]T = [a, b, c];`.
* Walk elements in declaration order, store each at off + i*esz
* using the right width for the element type. The trailing
* `...` repeat marker (an N_FIELD with str=="...") fills the
* remaining slots with the last value. */
if (n->rhs && n->rhs->kind == N_ARRLIT && lu
&& lu->kind == TY_ARRAY) {
int esz = lu->sub ? (int)lu->sub->size : 1;
int op = A_MOVQ;
if (esz == 1) op = A_MOVB;
else if (esz == 4) op = A_MOVL;
/* esz == 2 (i16/u16) falls through to MOVQ — over-writes
* by 6B; the next element store rewrites the high half.
* For the last element this trails 6 bytes into the next
* stack slot. Add MOVW to w6a if real i16 arrays land. */
int idx = 0;
Node *last = NULL;
int repeat = 0;
for (Node *e = n->rhs->list; e; e = e->next) {
if (e->kind == N_FIELD && e->str &&
strcmp(e->str, "...") == 0) {
repeat = 1;
break;
}
cgexpr(c, e, *locals);
ins2(c, op, areg(D_AX),
amem(D_BP, off + idx * esz));
last = e;
idx++;
}
if (repeat && last) {
/* fill remaining slots with the value already in AX. */
while (idx < (int)lu->alen) {
ins2(c, op, areg(D_AX),
amem(D_BP, off + idx * esz));
idx++;
}
}
break;
}
if (n->rhs && sz == 8) {
cgexpr(c, n->rhs, *locals);
if (isf) {
@@ -2488,6 +2604,8 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
char *loop = mklabel(c, "rloop");
char *end = mklabel(c, "rend");
char *natural_exit = end;
if (n->els) natural_exit = mklabel(c, "relseloop");
if (nloops < LOOP_MAX) {
loop_cont[nloops] = loop;
loop_brk[nloops] = end;
@@ -2497,7 +2615,7 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
ins2(c, A_MOVQ, amem(D_BP, ioff), areg(D_AX));
ins2(c, A_MOVQ, amem(D_BP, loff), areg(D_BX));
ins2(c, A_CMPQ, areg(D_BX), areg(D_AX));
ins1(c, A_JGE, abranch(end));
ins1(c, A_JGE, abranch(natural_exit));
/* compute element base: s.ptr + i*esz → BX */
if (esz > 1) {
ins2(c, A_MOVQ, aimm(esz), areg(D_CX));
@@ -2523,6 +2641,10 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
cgstmt(c, n->body, locals, frame);
ins2(c, A_ADDQ, aimm(1), amem(D_BP, ioff));
ins1(c, A_JMP, abranch(loop));
if (n->els) {
label(c, natural_exit);
cgstmt(c, n->els, locals, frame);
}
label(c, end);
if (nloops > 0) nloops--;
break;
@@ -2530,12 +2652,17 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
case N_FOR: {
char *loop = mklabel(c, "loop");
char *end = mklabel(c, "endloop");
/* `else` runs at normal cond-false exit; break skips it.
* Separate the natural exit label from the break target so
* the else block sits between them. */
char *natural_exit = end;
if (n->els) natural_exit = mklabel(c, "elseloop");
if (n->lhs) cgstmt(c, n->lhs, locals, frame);
label(c, loop);
if (n->cond) {
cgexpr(c, n->cond, *locals);
ins2(c, A_CMPQ, aimm(0), areg(D_AX));
ins1(c, A_JE, abranch(end));
ins1(c, A_JE, abranch(natural_exit));
}
if (nloops < LOOP_MAX) {
loop_cont[nloops] = loop;
@@ -2546,6 +2673,10 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
if (nloops > 0) nloops--;
if (n->rhs) cgexpr(c, n->rhs, *locals);
ins1(c, A_JMP, abranch(loop));
if (n->els) {
label(c, natural_exit);
cgstmt(c, n->els, locals, frame);
}
label(c, end);
break;
}