selfhost: port slice reassignment — N_SLICE cgexpr + N_ASSIGN triple store
Mirror of 548547a in the wwstage cgen.
cgexpr learns N_SLICE: `base[lo:hi]` leaves (AX=ptr, BX=len, CX=cap)
so callers (return, arg push, reassignment, let init for fn-returning-
slice / slice-ident) share one triple ABI. Without this, the existing
let-init that just forwards (AX,BX,CX) to the slot was silently storing
junk on a base[lo:hi] rhs.
cgassign gains a slice branch parallel to str: for a local slice ident
lhs, store all three halves to off+0/+8/+16; for a slice global, stash
CX into DI before LEAQ-ing the symbol address into CX (CX is both the
incoming cap and the LEAQ scratch), then store AX/BX/DI at +0/+8/+16.
N_IDENT slice-local triple load was already in place from the earlier
selfhost port; only the cgexpr and cgassign halves needed adding.
Byte-identical to C w6c on the corpus — make test 26/26, 994_w6c_ww
passes on 10 inputs, 990_selfhost + 995_self_rebuild reach fixed point.
This commit is contained in:
@@ -6042,6 +6042,8 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
||||
|
||||
if (k == nkind.N_INDEX) { cgindex(c, n); return; };
|
||||
|
||||
if (k == nkind.N_SLICE) { cgslice(c, n); return; };
|
||||
|
||||
if (k == nkind.N_MATCH) { cgmatch(c, n); return; };
|
||||
|
||||
if (k == nkind.N_CAST) { cgcast(c, n); return; };
|
||||
@@ -6540,6 +6542,88 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
return;
|
||||
};
|
||||
|
||||
// cgslice — `base[lo:hi]` as a slice value. Leaves (AX=base+lo,
|
||||
// BX=hi-lo, CX=hi-lo) so callers can route to a slice slot,
|
||||
// return, or arg with the same triple ABI. Cap defaults to the
|
||||
// new length; no syntax for a wider cap yet. Element scaling
|
||||
// on the ptr isn't wired — non-u8 slices need a follow-up audit.
|
||||
fn cgslice(c: *cgen, n: *node) void = {
|
||||
let base: *node = n.lhs;
|
||||
let lo: *node = n.rhs;
|
||||
let hi: *node = n.cond;
|
||||
let baselocal: *local = nil;
|
||||
if (base != nil) {
|
||||
if (base.kind == nkind.N_IDENT) {
|
||||
baselocal = localfindnode(c, base.str);
|
||||
};
|
||||
};
|
||||
// base address
|
||||
if (baselocal != nil) {
|
||||
let tn: *node = baselocal.tnode;
|
||||
let isarray: bool = false;
|
||||
if (tn != nil) {
|
||||
if (tn.kind == nkind.N_TARRAY) { isarray = true; };
|
||||
};
|
||||
if (isarray) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitoff(baselocal.off: i64);
|
||||
emitline("(BP), AX\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(baselocal.off: i64);
|
||||
emitline("(BP), AX\n");
|
||||
};
|
||||
} else { if (base != nil) {
|
||||
cgexpr(c, base);
|
||||
};};
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
// lo (default 0)
|
||||
if (lo != nil) { cgexpr(c, lo); }
|
||||
else { emitline("\tMOVQ\t$0, AX\n"); };
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
// hi (default base length)
|
||||
if (hi != nil) {
|
||||
cgexpr(c, hi);
|
||||
} else { if (baselocal != nil) {
|
||||
let tn: *node = baselocal.tnode;
|
||||
let handled: bool = false;
|
||||
if (tn != nil) {
|
||||
if (tn.kind == nkind.N_TARRAY) {
|
||||
let lenn: *node = tn.rhs;
|
||||
if (lenn != nil) {
|
||||
if (lenn.kind == nkind.N_INTLIT) {
|
||||
emitline("\tMOVQ\t$");
|
||||
emituint(lenn.uval);
|
||||
emitline(", AX\n");
|
||||
handled = true;
|
||||
};
|
||||
};
|
||||
} else { if (tn.kind == nkind.N_TSLICE) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((baselocal.off + 8): i64);
|
||||
emitline("(BP), AX\n");
|
||||
handled = true;
|
||||
} else { if (tn.kind == nkind.N_TNAME) {
|
||||
if (streq(tn.str, "str")) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((baselocal.off + 8): i64);
|
||||
emitline("(BP), AX\n");
|
||||
handled = true;
|
||||
};
|
||||
};};};
|
||||
};
|
||||
if (!handled) { emitline("\tMOVQ\t$0, AX\n"); };
|
||||
} else {
|
||||
emitline("\tMOVQ\t$0, AX\n");
|
||||
};};
|
||||
emitline("\tMOVQ\tAX, BX\n");
|
||||
emitline("\tPOPQ\tCX\n");
|
||||
emitline("\tPOPQ\tAX\n");
|
||||
emitline("\tADDQ\tCX, AX\n");
|
||||
emitline("\tSUBQ\tCX, BX\n");
|
||||
emitline("\tMOVQ\tBX, CX\n");
|
||||
};
|
||||
|
||||
fn cgmatch(c: *cgen, n: *node) void = {
|
||||
// match (e) { case let v: T => stmt; ... }
|
||||
//
|
||||
@@ -7896,9 +7980,11 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
if (off == 0) {
|
||||
// Top-level let target: RIP-relative store
|
||||
// for `=`, or load→combine→store for the
|
||||
// compound forms. For a str global, take its
|
||||
// address into CX and store both halves; the
|
||||
// asm has no `name+8(SB)` operand form.
|
||||
// compound forms. For a str/slice global,
|
||||
// take its address into CX and store both
|
||||
// halves (plus cap for slice — stashed via
|
||||
// DI since LEAQ overwrites CX); the asm has
|
||||
// no `name+8(SB)` operand form.
|
||||
if (!isletvar(c, nm)) { return; };
|
||||
// Float global: rhs lands in X0; store via
|
||||
// LEAQ+indirect since MOVSS/MOVSD have no
|
||||
@@ -7937,6 +8023,16 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\tBX, 8(CX)\n");
|
||||
return;
|
||||
};
|
||||
if (letvarisslice(c, nm)) {
|
||||
emitline("\tMOVQ\tCX, DI\n");
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, nm);
|
||||
emitline("(SB), CX\n");
|
||||
emitline("\tMOVQ\tAX, (CX)\n");
|
||||
emitline("\tMOVQ\tBX, 8(CX)\n");
|
||||
emitline("\tMOVQ\tDI, 16(CX)\n");
|
||||
return;
|
||||
};
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitsymname(c, nm);
|
||||
emitline("(SB)\n");
|
||||
@@ -7976,11 +8072,16 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
};
|
||||
return;
|
||||
};
|
||||
// Detect str-typed local — assignment must store both
|
||||
// halves (AX=ptr at +0, BX=len at +8).
|
||||
// Detect str/slice-typed local — assignment must store
|
||||
// both halves (AX=ptr at +0, BX=len at +8) for str,
|
||||
// plus the cap (CX at +16) for slice.
|
||||
let lcstr: bool = false;
|
||||
let lcsl: bool = false;
|
||||
let lcn: *local = localfindnode(c, nm);
|
||||
if (lcn != nil) { lcstr = isstrtype(c, lcn.tnode); };
|
||||
if (lcn != nil) {
|
||||
lcstr = isstrtype(c, lcn.tnode);
|
||||
lcsl = isslicetype(c, lcn.tnode);
|
||||
};
|
||||
let lcf: bool = false;
|
||||
let lcf32: bool = false;
|
||||
if (lcn != nil) {
|
||||
@@ -8006,11 +8107,16 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff(off: i64);
|
||||
emitline("(BP)\n");
|
||||
if (lcstr) {
|
||||
if (lcstr || lcsl) {
|
||||
emitline("\tMOVQ\tBX, ");
|
||||
emitoff((off + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
if (lcsl) {
|
||||
emitline("\tMOVQ\tCX, ");
|
||||
emitoff((off + 16): i64);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
return;
|
||||
};
|
||||
if (n.op == tkind.TK_PLUSEQ) {
|
||||
|
||||
@@ -80,6 +80,8 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
||||
|
||||
if (k == nkind.N_INDEX) { cgindex(c, n); return; };
|
||||
|
||||
if (k == nkind.N_SLICE) { cgslice(c, n); return; };
|
||||
|
||||
if (k == nkind.N_MATCH) { cgmatch(c, n); return; };
|
||||
|
||||
if (k == nkind.N_CAST) { cgcast(c, n); return; };
|
||||
@@ -578,6 +580,88 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
return;
|
||||
};
|
||||
|
||||
// cgslice — `base[lo:hi]` as a slice value. Leaves (AX=base+lo,
|
||||
// BX=hi-lo, CX=hi-lo) so callers can route to a slice slot,
|
||||
// return, or arg with the same triple ABI. Cap defaults to the
|
||||
// new length; no syntax for a wider cap yet. Element scaling
|
||||
// on the ptr isn't wired — non-u8 slices need a follow-up audit.
|
||||
fn cgslice(c: *cgen, n: *node) void = {
|
||||
let base: *node = n.lhs;
|
||||
let lo: *node = n.rhs;
|
||||
let hi: *node = n.cond;
|
||||
let baselocal: *local = nil;
|
||||
if (base != nil) {
|
||||
if (base.kind == nkind.N_IDENT) {
|
||||
baselocal = localfindnode(c, base.str);
|
||||
};
|
||||
};
|
||||
// base address
|
||||
if (baselocal != nil) {
|
||||
let tn: *node = baselocal.tnode;
|
||||
let isarray: bool = false;
|
||||
if (tn != nil) {
|
||||
if (tn.kind == nkind.N_TARRAY) { isarray = true; };
|
||||
};
|
||||
if (isarray) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitoff(baselocal.off: i64);
|
||||
emitline("(BP), AX\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(baselocal.off: i64);
|
||||
emitline("(BP), AX\n");
|
||||
};
|
||||
} else { if (base != nil) {
|
||||
cgexpr(c, base);
|
||||
};};
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
// lo (default 0)
|
||||
if (lo != nil) { cgexpr(c, lo); }
|
||||
else { emitline("\tMOVQ\t$0, AX\n"); };
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
// hi (default base length)
|
||||
if (hi != nil) {
|
||||
cgexpr(c, hi);
|
||||
} else { if (baselocal != nil) {
|
||||
let tn: *node = baselocal.tnode;
|
||||
let handled: bool = false;
|
||||
if (tn != nil) {
|
||||
if (tn.kind == nkind.N_TARRAY) {
|
||||
let lenn: *node = tn.rhs;
|
||||
if (lenn != nil) {
|
||||
if (lenn.kind == nkind.N_INTLIT) {
|
||||
emitline("\tMOVQ\t$");
|
||||
emituint(lenn.uval);
|
||||
emitline(", AX\n");
|
||||
handled = true;
|
||||
};
|
||||
};
|
||||
} else { if (tn.kind == nkind.N_TSLICE) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((baselocal.off + 8): i64);
|
||||
emitline("(BP), AX\n");
|
||||
handled = true;
|
||||
} else { if (tn.kind == nkind.N_TNAME) {
|
||||
if (streq(tn.str, "str")) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((baselocal.off + 8): i64);
|
||||
emitline("(BP), AX\n");
|
||||
handled = true;
|
||||
};
|
||||
};};};
|
||||
};
|
||||
if (!handled) { emitline("\tMOVQ\t$0, AX\n"); };
|
||||
} else {
|
||||
emitline("\tMOVQ\t$0, AX\n");
|
||||
};};
|
||||
emitline("\tMOVQ\tAX, BX\n");
|
||||
emitline("\tPOPQ\tCX\n");
|
||||
emitline("\tPOPQ\tAX\n");
|
||||
emitline("\tADDQ\tCX, AX\n");
|
||||
emitline("\tSUBQ\tCX, BX\n");
|
||||
emitline("\tMOVQ\tBX, CX\n");
|
||||
};
|
||||
|
||||
fn cgmatch(c: *cgen, n: *node) void = {
|
||||
// match (e) { case let v: T => stmt; ... }
|
||||
//
|
||||
@@ -1934,9 +2018,11 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
if (off == 0) {
|
||||
// Top-level let target: RIP-relative store
|
||||
// for `=`, or load→combine→store for the
|
||||
// compound forms. For a str global, take its
|
||||
// address into CX and store both halves; the
|
||||
// asm has no `name+8(SB)` operand form.
|
||||
// compound forms. For a str/slice global,
|
||||
// take its address into CX and store both
|
||||
// halves (plus cap for slice — stashed via
|
||||
// DI since LEAQ overwrites CX); the asm has
|
||||
// no `name+8(SB)` operand form.
|
||||
if (!isletvar(c, nm)) { return; };
|
||||
// Float global: rhs lands in X0; store via
|
||||
// LEAQ+indirect since MOVSS/MOVSD have no
|
||||
@@ -1975,6 +2061,16 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\tBX, 8(CX)\n");
|
||||
return;
|
||||
};
|
||||
if (letvarisslice(c, nm)) {
|
||||
emitline("\tMOVQ\tCX, DI\n");
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, nm);
|
||||
emitline("(SB), CX\n");
|
||||
emitline("\tMOVQ\tAX, (CX)\n");
|
||||
emitline("\tMOVQ\tBX, 8(CX)\n");
|
||||
emitline("\tMOVQ\tDI, 16(CX)\n");
|
||||
return;
|
||||
};
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitsymname(c, nm);
|
||||
emitline("(SB)\n");
|
||||
@@ -2014,11 +2110,16 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
};
|
||||
return;
|
||||
};
|
||||
// Detect str-typed local — assignment must store both
|
||||
// halves (AX=ptr at +0, BX=len at +8).
|
||||
// Detect str/slice-typed local — assignment must store
|
||||
// both halves (AX=ptr at +0, BX=len at +8) for str,
|
||||
// plus the cap (CX at +16) for slice.
|
||||
let lcstr: bool = false;
|
||||
let lcsl: bool = false;
|
||||
let lcn: *local = localfindnode(c, nm);
|
||||
if (lcn != nil) { lcstr = isstrtype(c, lcn.tnode); };
|
||||
if (lcn != nil) {
|
||||
lcstr = isstrtype(c, lcn.tnode);
|
||||
lcsl = isslicetype(c, lcn.tnode);
|
||||
};
|
||||
let lcf: bool = false;
|
||||
let lcf32: bool = false;
|
||||
if (lcn != nil) {
|
||||
@@ -2044,11 +2145,16 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff(off: i64);
|
||||
emitline("(BP)\n");
|
||||
if (lcstr) {
|
||||
if (lcstr || lcsl) {
|
||||
emitline("\tMOVQ\tBX, ");
|
||||
emitoff((off + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
if (lcsl) {
|
||||
emitline("\tMOVQ\tCX, ");
|
||||
emitoff((off + 16): i64);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
return;
|
||||
};
|
||||
if (n.op == tkind.TK_PLUSEQ) {
|
||||
|
||||
@@ -6042,6 +6042,8 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
||||
|
||||
if (k == nkind.N_INDEX) { cgindex(c, n); return; };
|
||||
|
||||
if (k == nkind.N_SLICE) { cgslice(c, n); return; };
|
||||
|
||||
if (k == nkind.N_MATCH) { cgmatch(c, n); return; };
|
||||
|
||||
if (k == nkind.N_CAST) { cgcast(c, n); return; };
|
||||
@@ -6540,6 +6542,88 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
return;
|
||||
};
|
||||
|
||||
// cgslice — `base[lo:hi]` as a slice value. Leaves (AX=base+lo,
|
||||
// BX=hi-lo, CX=hi-lo) so callers can route to a slice slot,
|
||||
// return, or arg with the same triple ABI. Cap defaults to the
|
||||
// new length; no syntax for a wider cap yet. Element scaling
|
||||
// on the ptr isn't wired — non-u8 slices need a follow-up audit.
|
||||
fn cgslice(c: *cgen, n: *node) void = {
|
||||
let base: *node = n.lhs;
|
||||
let lo: *node = n.rhs;
|
||||
let hi: *node = n.cond;
|
||||
let baselocal: *local = nil;
|
||||
if (base != nil) {
|
||||
if (base.kind == nkind.N_IDENT) {
|
||||
baselocal = localfindnode(c, base.str);
|
||||
};
|
||||
};
|
||||
// base address
|
||||
if (baselocal != nil) {
|
||||
let tn: *node = baselocal.tnode;
|
||||
let isarray: bool = false;
|
||||
if (tn != nil) {
|
||||
if (tn.kind == nkind.N_TARRAY) { isarray = true; };
|
||||
};
|
||||
if (isarray) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitoff(baselocal.off: i64);
|
||||
emitline("(BP), AX\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(baselocal.off: i64);
|
||||
emitline("(BP), AX\n");
|
||||
};
|
||||
} else { if (base != nil) {
|
||||
cgexpr(c, base);
|
||||
};};
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
// lo (default 0)
|
||||
if (lo != nil) { cgexpr(c, lo); }
|
||||
else { emitline("\tMOVQ\t$0, AX\n"); };
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
// hi (default base length)
|
||||
if (hi != nil) {
|
||||
cgexpr(c, hi);
|
||||
} else { if (baselocal != nil) {
|
||||
let tn: *node = baselocal.tnode;
|
||||
let handled: bool = false;
|
||||
if (tn != nil) {
|
||||
if (tn.kind == nkind.N_TARRAY) {
|
||||
let lenn: *node = tn.rhs;
|
||||
if (lenn != nil) {
|
||||
if (lenn.kind == nkind.N_INTLIT) {
|
||||
emitline("\tMOVQ\t$");
|
||||
emituint(lenn.uval);
|
||||
emitline(", AX\n");
|
||||
handled = true;
|
||||
};
|
||||
};
|
||||
} else { if (tn.kind == nkind.N_TSLICE) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((baselocal.off + 8): i64);
|
||||
emitline("(BP), AX\n");
|
||||
handled = true;
|
||||
} else { if (tn.kind == nkind.N_TNAME) {
|
||||
if (streq(tn.str, "str")) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((baselocal.off + 8): i64);
|
||||
emitline("(BP), AX\n");
|
||||
handled = true;
|
||||
};
|
||||
};};};
|
||||
};
|
||||
if (!handled) { emitline("\tMOVQ\t$0, AX\n"); };
|
||||
} else {
|
||||
emitline("\tMOVQ\t$0, AX\n");
|
||||
};};
|
||||
emitline("\tMOVQ\tAX, BX\n");
|
||||
emitline("\tPOPQ\tCX\n");
|
||||
emitline("\tPOPQ\tAX\n");
|
||||
emitline("\tADDQ\tCX, AX\n");
|
||||
emitline("\tSUBQ\tCX, BX\n");
|
||||
emitline("\tMOVQ\tBX, CX\n");
|
||||
};
|
||||
|
||||
fn cgmatch(c: *cgen, n: *node) void = {
|
||||
// match (e) { case let v: T => stmt; ... }
|
||||
//
|
||||
@@ -7896,9 +7980,11 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
if (off == 0) {
|
||||
// Top-level let target: RIP-relative store
|
||||
// for `=`, or load→combine→store for the
|
||||
// compound forms. For a str global, take its
|
||||
// address into CX and store both halves; the
|
||||
// asm has no `name+8(SB)` operand form.
|
||||
// compound forms. For a str/slice global,
|
||||
// take its address into CX and store both
|
||||
// halves (plus cap for slice — stashed via
|
||||
// DI since LEAQ overwrites CX); the asm has
|
||||
// no `name+8(SB)` operand form.
|
||||
if (!isletvar(c, nm)) { return; };
|
||||
// Float global: rhs lands in X0; store via
|
||||
// LEAQ+indirect since MOVSS/MOVSD have no
|
||||
@@ -7937,6 +8023,16 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\tBX, 8(CX)\n");
|
||||
return;
|
||||
};
|
||||
if (letvarisslice(c, nm)) {
|
||||
emitline("\tMOVQ\tCX, DI\n");
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, nm);
|
||||
emitline("(SB), CX\n");
|
||||
emitline("\tMOVQ\tAX, (CX)\n");
|
||||
emitline("\tMOVQ\tBX, 8(CX)\n");
|
||||
emitline("\tMOVQ\tDI, 16(CX)\n");
|
||||
return;
|
||||
};
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitsymname(c, nm);
|
||||
emitline("(SB)\n");
|
||||
@@ -7976,11 +8072,16 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
};
|
||||
return;
|
||||
};
|
||||
// Detect str-typed local — assignment must store both
|
||||
// halves (AX=ptr at +0, BX=len at +8).
|
||||
// Detect str/slice-typed local — assignment must store
|
||||
// both halves (AX=ptr at +0, BX=len at +8) for str,
|
||||
// plus the cap (CX at +16) for slice.
|
||||
let lcstr: bool = false;
|
||||
let lcsl: bool = false;
|
||||
let lcn: *local = localfindnode(c, nm);
|
||||
if (lcn != nil) { lcstr = isstrtype(c, lcn.tnode); };
|
||||
if (lcn != nil) {
|
||||
lcstr = isstrtype(c, lcn.tnode);
|
||||
lcsl = isslicetype(c, lcn.tnode);
|
||||
};
|
||||
let lcf: bool = false;
|
||||
let lcf32: bool = false;
|
||||
if (lcn != nil) {
|
||||
@@ -8006,11 +8107,16 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff(off: i64);
|
||||
emitline("(BP)\n");
|
||||
if (lcstr) {
|
||||
if (lcstr || lcsl) {
|
||||
emitline("\tMOVQ\tBX, ");
|
||||
emitoff((off + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
if (lcsl) {
|
||||
emitline("\tMOVQ\tCX, ");
|
||||
emitoff((off + 16): i64);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
return;
|
||||
};
|
||||
if (n.op == tkind.TK_PLUSEQ) {
|
||||
|
||||
Reference in New Issue
Block a user