ww: hare-feature batch (_, const, [_]T, ..., size/offset, assert, for-else)
This commit is contained in:
137
cmd/w6c/cgen.c
137
cmd/w6c/cgen.c
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user