selfhost: port append + spread — N_SPREAD parser + rt_ensure builtin
This commit is contained in:
@@ -1389,7 +1389,152 @@ fn cgbin(c: *cgen, n: *node) void = {
|
||||
return;
|
||||
};
|
||||
|
||||
// cgappend — Hare-style `append(s, v)` / `append(s, items...)` lowering.
|
||||
// Mirrors cmd/w6c/cgen.c's N_CALL append branch (rt::ensure model).
|
||||
// Each value gets:
|
||||
// ; cgexpr → AX
|
||||
// ; PUSHQ AX
|
||||
// ; ADDQ $1, s.len(BP)
|
||||
// ; LEAQ s(BP), DI ; arg1 = &s
|
||||
// ; MOVQ esz, SI ; arg2 = membsz
|
||||
// ; CALL rt_ensure(SB)
|
||||
// ; MOVQ s.len(BP), CX ; CX = new len
|
||||
// ; SUBQ $1, CX ; CX = slot index
|
||||
// ; [IMULQ esz, CX] ; byte offset (esz>1)
|
||||
// ; MOVQ s.ptr(BP), BX
|
||||
// ; ADDQ CX, BX
|
||||
// ; POPQ AX
|
||||
// ; MOV* AX, (BX) ; store (MOVB / MOVQ)
|
||||
// nkind.N_SPREAD wraps the same body in a counted loop over items.len.
|
||||
fn cgappend(c: *cgen, n: *node) void = {
|
||||
let sn: *node = n.list;
|
||||
if (sn == nil) { return; };
|
||||
if (sn.kind != nkind.N_IDENT) { return; };
|
||||
let snlocal: *local = localfindnode(c, sn.str);
|
||||
if (snlocal == nil) { return; };
|
||||
let sn_off: i32 = snlocal.off;
|
||||
let esz: i32 = elemsizeof(snlocal.tnode);
|
||||
let store_op: str = "MOVQ";
|
||||
if (esz == 1) { store_op = "MOVB"; };
|
||||
|
||||
let vn: *node = sn.next;
|
||||
for (vn != nil) {
|
||||
if (vn.kind == nkind.N_SPREAD) {
|
||||
let it: *node = vn.lhs;
|
||||
if (it == nil) { vn = vn.next; continue; };
|
||||
if (it.kind != nkind.N_IDENT) { vn = vn.next; continue; };
|
||||
let itlocal: *local = localfindnode(c, it.str);
|
||||
if (itlocal == nil) { vn = vn.next; continue; };
|
||||
let it_off: i32 = itlocal.off;
|
||||
let load_op: str = "MOVQ";
|
||||
if (esz == 1) { load_op = "MOVZBQ"; };
|
||||
emitline("\tSUBQ\t$8, SP\n");
|
||||
emitline("\tMOVQ\t$0, (SP)\n");
|
||||
let ll: str = mklabel(c, "spr_l");
|
||||
let le: str = mklabel(c, "spr_e");
|
||||
emitlabel(ll);
|
||||
emitline("\tMOVQ\t(SP), CX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((it_off + 8): i64);
|
||||
emitline("(BP), DX\n");
|
||||
emitline("\tCMPQ\tDX, CX\n");
|
||||
emitline("\tJGE\t"); emitline(le); emitline("\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(it_off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
if (esz > 1) {
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(esz: i64);
|
||||
emitline(", AX\n");
|
||||
emitline("\tIMULQ\tAX, CX\n");
|
||||
};
|
||||
emitline("\tADDQ\tCX, BX\n");
|
||||
emitline("\t"); emitline(load_op); emitline("\t(BX), AX\n");
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
emitline("\tADDQ\t$1, ");
|
||||
emitoff((sn_off + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tLEAQ\t");
|
||||
emitoff(sn_off: i64);
|
||||
emitline("(BP), DI\n");
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(esz: i64);
|
||||
emitline(", SI\n");
|
||||
emitline("\tCALL\trt_ensure(SB)\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((sn_off + 8): i64);
|
||||
emitline("(BP), CX\n");
|
||||
emitline("\tSUBQ\t$1, CX\n");
|
||||
if (esz > 1) {
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(esz: i64);
|
||||
emitline(", AX\n");
|
||||
emitline("\tIMULQ\tAX, CX\n");
|
||||
};
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(sn_off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
emitline("\tADDQ\tCX, BX\n");
|
||||
emitline("\tPOPQ\tAX\n");
|
||||
emitline("\t"); emitline(store_op); emitline("\tAX, (BX)\n");
|
||||
emitline("\tADDQ\t$1, (SP)\n");
|
||||
emitline("\tJMP\t"); emitline(ll); emitline("\n");
|
||||
emitlabel(le);
|
||||
emitline("\tADDQ\t$8, SP\n");
|
||||
vn = vn.next;
|
||||
continue;
|
||||
};
|
||||
cgexpr(c, vn);
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
emitline("\tADDQ\t$1, ");
|
||||
emitoff((sn_off + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tLEAQ\t");
|
||||
emitoff(sn_off: i64);
|
||||
emitline("(BP), DI\n");
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(esz: i64);
|
||||
emitline(", SI\n");
|
||||
emitline("\tCALL\trt_ensure(SB)\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((sn_off + 8): i64);
|
||||
emitline("(BP), CX\n");
|
||||
emitline("\tSUBQ\t$1, CX\n");
|
||||
if (esz > 1) {
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(esz: i64);
|
||||
emitline(", AX\n");
|
||||
emitline("\tIMULQ\tAX, CX\n");
|
||||
};
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(sn_off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
emitline("\tADDQ\tCX, BX\n");
|
||||
emitline("\tPOPQ\tAX\n");
|
||||
emitline("\t"); emitline(store_op); emitline("\tAX, (BX)\n");
|
||||
vn = vn.next;
|
||||
};
|
||||
return;
|
||||
};
|
||||
|
||||
fn cgcall(c: *cgen, n: *node) void = {
|
||||
// Hare-style `append(s, v)` / `append(s, items...)` builtin —
|
||||
// special-cased before pushargsrev so the spread variant can run
|
||||
// a counted loop over the items slice instead of a normal call.
|
||||
let callee: *node = n.lhs;
|
||||
if (callee != nil) {
|
||||
if (callee.kind == nkind.N_IDENT) {
|
||||
if (streq(callee.str, "append")) {
|
||||
if (n.list != nil) {
|
||||
if (n.list.next != nil) {
|
||||
cgappend(c, n);
|
||||
return;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
let nargs: i32 = pushargsrev(c, n.list);
|
||||
// Pop forward. Float args were pushed as 8 bytes from X0 via
|
||||
// SUBQ+MOVSD; pop into the XMM stream (X0..X7). Everything else
|
||||
|
||||
@@ -56,11 +56,12 @@ fn seedprimitives(c: *checker) void = {
|
||||
scopedefine(c.top, "never", skind.SK_TYPE, c.tc.tynever, nil);
|
||||
// `nil`, `true`, `false` are keywords — handled at the lex/parser
|
||||
// level, no symbol needed.
|
||||
// `len`, `alloc`, `free` are pseudo-builtins; scopedefine them so
|
||||
// their use sites resolve. The actual semantics live in cgen.
|
||||
scopedefine(c.top, "len", skind.SK_FN, nil, nil);
|
||||
scopedefine(c.top, "alloc", skind.SK_FN, nil, nil);
|
||||
scopedefine(c.top, "free", skind.SK_FN, nil, nil);
|
||||
// `len`, `alloc`, `free`, `append` are pseudo-builtins; scopedefine
|
||||
// them so their use sites resolve. The actual semantics live in cgen.
|
||||
scopedefine(c.top, "len", skind.SK_FN, nil, nil);
|
||||
scopedefine(c.top, "alloc", skind.SK_FN, nil, nil);
|
||||
scopedefine(c.top, "free", skind.SK_FN, nil, nil);
|
||||
scopedefine(c.top, "append", skind.SK_FN, nil, nil);
|
||||
};
|
||||
|
||||
// installdecl — install the top-level decl's name into the top scope.
|
||||
|
||||
Reference in New Issue
Block a user