w6c+selfhost: route f64/f32 struct-field load/store through X0
cgexpr leaves float results in X0, not AX, but the struct-field paths
emitted MOVQ AX,off(BX) and MOVQ off(BX),AX — so every store wrote
garbage and every load read garbage, except by accidental register
survival across an unrelated call. examples/lisp only worked because
parsef's X0 happened to live across the broken MOVQ shuffle into
vfloat; any inserted f64 op between them would silently corrupt.
Wire MOVSD/MOVSS X0,… (and the matching loads) into eight field
sites on both compilers: alloc(T{...}), p.x = v through local/ptr/
global, chained r.sub.x = v, *p = v for *f64, let v: T = T{...},
base.x reads, *T.x reads, and chained a.b.c.x reads.
83/83 lisp_test probes still pass; bootstrap reaches a byte-stable
fixed point at ww3 == ww4.
This commit is contained in:
144
cmd/w6c/cgen.c
144
cmd/w6c/cgen.c
@@ -113,6 +113,24 @@ node_isf32(Node *n)
|
||||
return n && type_isf32(n->type);
|
||||
}
|
||||
|
||||
/* fld_isfloat — true iff f's underlying type is f32 or f64. The cgen
|
||||
* passes float values in X0 (via MOVSD/MOVSS), integer/ptr values in
|
||||
* AX (via MOVQ). Without this check, a field store/load on an f64 slot
|
||||
* runs through AX and the bits never reach the SSE side — see the
|
||||
* vfloat / L.curfval traps documented in examples/lisp/CLAUDE.md.
|
||||
* Sets *isf32 to 1 for f32, 0 for f64. */
|
||||
static int
|
||||
fld_isfloat(Type *t, int *isf32)
|
||||
{
|
||||
if (isf32) *isf32 = 0;
|
||||
if (t == NULL) return 0;
|
||||
if (t->kind == TY_NAMED) t = t->under;
|
||||
if (t == NULL) return 0;
|
||||
if (t->kind == TY_F64) return 1;
|
||||
if (t->kind == TY_F32) { if (isf32) *isf32 = 1; return 1; }
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* struct ≤16B all-INTEGER: 1 or 2 eightbyte regs.
|
||||
* Returns 0 if not a struct or too large. */
|
||||
static int
|
||||
@@ -1324,6 +1342,28 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
default: break; /* others rare */
|
||||
}
|
||||
}
|
||||
/* f64/f32 field, plain `=`: cgexpr left the value in
|
||||
* X0, not AX. Route the store via MOVSD/MOVSS.
|
||||
* Compound ops on float fields aren't wired here —
|
||||
* see CLAUDE.md #8 in examples/lisp; same in the
|
||||
* structlit-init path below. */
|
||||
int b_isf32 = 0;
|
||||
if (n->op == TK_ASSIGN
|
||||
&& fld_isfloat(f->type, &b_isf32)) {
|
||||
int mov = b_isf32 ? A_MOVSS : A_MOVSD;
|
||||
if (via_ptr) {
|
||||
ins2(c, A_MOVQ, amem(D_BP, boff), areg(D_BX));
|
||||
ins2(c, mov, areg(D_X0), amem(D_BX, foff));
|
||||
} else if (is_global) {
|
||||
ins2(c, A_LEAQ,
|
||||
masym(c, base->str), areg(D_BX));
|
||||
ins2(c, mov, areg(D_X0), amem(D_BX, foff));
|
||||
} else {
|
||||
ins2(c, mov, areg(D_X0),
|
||||
amem(D_BP, boff + foff));
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* now store AX into target */
|
||||
if (via_ptr) {
|
||||
ins2(c, A_MOVQ, amem(D_BP, boff), areg(D_BX));
|
||||
@@ -1370,6 +1410,28 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
else if (fsz == 4) store_op = A_MOVL;
|
||||
int foff = (int)f->offset;
|
||||
if (n->op == TK_ASSIGN) {
|
||||
int c_isf32 = 0;
|
||||
if (fld_isfloat(ft, &c_isf32)) {
|
||||
/* f64/f32 chained-store: cgexpr rhs
|
||||
* left the value in X0. Spill to stack
|
||||
* so cgexpr on the inner pointer can
|
||||
* use AX, then reload into X0 and
|
||||
* MOVSD/MOVSS into the slot. */
|
||||
int mov = c_isf32 ? A_MOVSS : A_MOVSD;
|
||||
cgexpr(c, n->rhs, locals);
|
||||
ins2(c, A_SUBQ, aimm(8), areg(D_SP));
|
||||
ins2(c, mov, areg(D_X0),
|
||||
amem(D_SP, 0));
|
||||
cgexpr(c, n->lhs->lhs, locals);
|
||||
ins2(c, A_MOVQ, areg(D_AX),
|
||||
areg(D_BX));
|
||||
ins2(c, mov, amem(D_SP, 0),
|
||||
areg(D_X0));
|
||||
ins2(c, A_ADDQ, aimm(8), areg(D_SP));
|
||||
ins2(c, mov, areg(D_X0),
|
||||
amem(D_BX, foff));
|
||||
break;
|
||||
}
|
||||
if (fu && fu->kind == TY_STR) {
|
||||
/* str rhs: (AX=ptr, BX=len). Stash
|
||||
* both, then load the struct ptr
|
||||
@@ -1555,6 +1617,23 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
Type *pu = (pt && pt->kind == TY_NAMED) ? pt->under : pt;
|
||||
Type *vt = (pu && pu->kind == TY_PTR) ? pu->sub : NULL;
|
||||
if (vt && vt->kind == TY_NAMED) vt = vt->under;
|
||||
/* `*p = v` for *f64 / *f32: cgexpr leaves the value in X0,
|
||||
* not AX. Spill X0 to the stack, evaluate the pointer
|
||||
* (clobbers AX/BX freely), then reload X0 and MOVSD/MOVSS
|
||||
* through the pointer. */
|
||||
int deref_isf32 = 0;
|
||||
if (vt && fld_isfloat(vt, &deref_isf32)) {
|
||||
int mov = deref_isf32 ? A_MOVSS : A_MOVSD;
|
||||
cgexpr(c, n->rhs, locals);
|
||||
ins2(c, A_SUBQ, aimm(8), areg(D_SP));
|
||||
ins2(c, mov, areg(D_X0), amem(D_SP, 0));
|
||||
cgexpr(c, n->lhs->lhs, locals);
|
||||
ins2(c, A_MOVQ, areg(D_AX), areg(D_BX));
|
||||
ins2(c, mov, amem(D_SP, 0), areg(D_X0));
|
||||
ins2(c, A_ADDQ, aimm(8), areg(D_SP));
|
||||
ins2(c, mov, areg(D_X0), amem(D_BX, 0));
|
||||
break;
|
||||
}
|
||||
cgexpr(c, n->rhs, locals); /* AX = value (BX too if str) */
|
||||
ins1(c, A_PUSHQ, areg(D_AX));
|
||||
if (vt && vt->kind == TY_STR) ins1(c, A_PUSHQ, areg(D_BX));
|
||||
@@ -1839,14 +1918,37 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
for (Node *f = v->list; f; f = f->next) {
|
||||
u64 foff = 0;
|
||||
int fsz = 8;
|
||||
Type *ftype = NULL;
|
||||
for (Tfield *fl = u->fields; fl; fl = fl->next) {
|
||||
if (strcmp(fl->name, f->str) == 0) {
|
||||
foff = fl->offset;
|
||||
fsz = (int)(fl->type ? fl->type->size : 8);
|
||||
ftype = fl->type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
cgexpr(c, f->lhs, locals); /* AX = field val */
|
||||
cgexpr(c, f->lhs, locals); /* AX or (AX,BX) or X0 */
|
||||
int f_isf32 = 0;
|
||||
if (fld_isfloat(ftype, &f_isf32)) {
|
||||
int mov = f_isf32 ? A_MOVSS : A_MOVSD;
|
||||
ins2(c, A_MOVQ, amem(D_SP, 0), areg(D_BX));
|
||||
ins2(c, mov, areg(D_X0),
|
||||
amem(D_BX, (int)foff));
|
||||
continue;
|
||||
}
|
||||
/* str-typed field: cgexpr leaves (AX=ptr, BX=len).
|
||||
* Route the heap base through CX so both halves
|
||||
* survive — using BX would clobber len. */
|
||||
Type *fu = (ftype && ftype->kind == TY_NAMED)
|
||||
? ftype->under : ftype;
|
||||
if (fu && fu->kind == TY_STR) {
|
||||
ins2(c, A_MOVQ, amem(D_SP, 0), areg(D_CX));
|
||||
ins2(c, A_MOVQ, areg(D_AX),
|
||||
amem(D_CX, (int)foff + 0));
|
||||
ins2(c, A_MOVQ, areg(D_BX),
|
||||
amem(D_CX, (int)foff + 8));
|
||||
continue;
|
||||
}
|
||||
ins2(c, A_MOVQ, amem(D_SP, 0), areg(D_BX));
|
||||
int op = A_MOVQ;
|
||||
if (fsz == 1) op = A_MOVB;
|
||||
@@ -2765,6 +2867,19 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
areg(D_BX));
|
||||
break;
|
||||
}
|
||||
/* f64/f32 field: route through X0 (MOVSD/MOVSS).
|
||||
* Loading via MOVQ AX would put the bits in the
|
||||
* integer reg, and any downstream consumer that
|
||||
* reads X0 (arg pass, return, arithmetic) would see
|
||||
* stale data. */
|
||||
int e_isf32 = 0;
|
||||
if (fld_isfloat(f->type, &e_isf32)) {
|
||||
int mov = e_isf32 ? A_MOVSS : A_MOVSD;
|
||||
ins2(c, mov,
|
||||
amem(base_reg, base_disp + (int)f->offset),
|
||||
areg(D_X0));
|
||||
break;
|
||||
}
|
||||
int fsz = (int)(f->type ? f->type->size : 8);
|
||||
int signed_field = f->type && (
|
||||
f->type->kind == TY_I8 ||
|
||||
@@ -2822,6 +2937,17 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
ins2(c, A_MOVQ, areg(D_CX), areg(D_BX));
|
||||
break;
|
||||
}
|
||||
/* f64/f32 field via *struct: load into X0.
|
||||
* BX already holds the struct pointer from
|
||||
* the MOVQ amem(D_BP,off) above. */
|
||||
int f_isf32 = 0;
|
||||
if (fld_isfloat(f->type, &f_isf32)) {
|
||||
int mov = f_isf32 ? A_MOVSS : A_MOVSD;
|
||||
ins2(c, mov,
|
||||
amem(D_BX, (int)f->offset),
|
||||
areg(D_X0));
|
||||
break;
|
||||
}
|
||||
int fsz = (int)(f->type ? f->type->size : 8);
|
||||
int signed_field = f->type && (
|
||||
f->type->kind == TY_I8 ||
|
||||
@@ -2867,6 +2993,15 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
areg(D_AX));
|
||||
goto dot_done;
|
||||
}
|
||||
/* f64/f32 chained field: read into X0. */
|
||||
int g_isf32 = 0;
|
||||
if (fld_isfloat(ft, &g_isf32)) {
|
||||
int mov = g_isf32 ? A_MOVSS : A_MOVSD;
|
||||
ins2(c, mov,
|
||||
amem(D_AX, (int)f->offset),
|
||||
areg(D_X0));
|
||||
goto dot_done;
|
||||
}
|
||||
int fsz = (int)(ft ? ft->size : 8);
|
||||
int signed_field = ft && (
|
||||
ft->kind == TY_I8 ||
|
||||
@@ -3291,6 +3426,13 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
|
||||
continue;
|
||||
}
|
||||
cgexpr(c, f->lhs, *locals);
|
||||
int sl_isf32 = 0;
|
||||
if (fld_isfloat(ft, &sl_isf32)) {
|
||||
int mov = sl_isf32 ? A_MOVSS : A_MOVSD;
|
||||
ins2(c, mov, areg(D_X0),
|
||||
amem(D_BP, off + (int)foff));
|
||||
continue;
|
||||
}
|
||||
int op = A_MOVQ;
|
||||
if (fsz == 1) op = A_MOVB;
|
||||
else if (fsz == 4) op = A_MOVL;
|
||||
|
||||
@@ -937,6 +937,19 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
emitdispreg(fi.foff: i64, "BX");
|
||||
emitline(", AX\n");
|
||||
emitline("\tMOVQ\tCX, BX\n");
|
||||
} else { if (isfloattype(c, fi.tnode)) {
|
||||
// f64/f32 via *struct: route through X0.
|
||||
// MOVQ into AX leaves the SSE reg stale
|
||||
// and any downstream consumer (arg
|
||||
// pass, return, arithmetic) reads
|
||||
// garbage.
|
||||
let mov: str = "MOVSD";
|
||||
if (isf32type(c, fi.tnode)) { mov = "MOVSS"; };
|
||||
emitline("\t");
|
||||
emitline(mov);
|
||||
emitline("\t");
|
||||
emitdispreg(fi.foff: i64, "BX");
|
||||
emitline(", X0\n");
|
||||
} else {
|
||||
let op: str = fieldloadop(fi);
|
||||
emitline("\t");
|
||||
@@ -944,7 +957,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
emitline("\t");
|
||||
emitdispreg(fi.foff: i64, "BX");
|
||||
emitline(", AX\n");
|
||||
};
|
||||
}; };
|
||||
return;
|
||||
};
|
||||
fi = fi.finext;
|
||||
@@ -970,6 +983,15 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((lc.off + fi.foff + 8): i64);
|
||||
emitline("(BP), BX\n");
|
||||
} else { if (isfloattype(c, fi.tnode)) {
|
||||
// f64/f32 field: route through X0.
|
||||
let mov: str = "MOVSD";
|
||||
if (isf32type(c, fi.tnode)) { mov = "MOVSS"; };
|
||||
emitline("\t");
|
||||
emitline(mov);
|
||||
emitline("\t");
|
||||
emitoff((lc.off + fi.foff): i64);
|
||||
emitline("(BP), X0\n");
|
||||
} else {
|
||||
let op: str = fieldloadop(fi);
|
||||
emitline("\t");
|
||||
@@ -977,7 +999,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
emitline("\t");
|
||||
emitoff((lc.off + fi.foff): i64);
|
||||
emitline("(BP), AX\n");
|
||||
};
|
||||
}; };
|
||||
return;
|
||||
};
|
||||
fi = fi.finext;
|
||||
@@ -1164,6 +1186,15 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg((fi.foff + 8): i64, "CX");
|
||||
emitline(", BX\n");
|
||||
} else { if (isfloattype(c, fi.tnode)) {
|
||||
// f64/f32 global field: route through X0.
|
||||
let mov: str = "MOVSD";
|
||||
if (isf32type(c, fi.tnode)) { mov = "MOVSS"; };
|
||||
emitline("\t");
|
||||
emitline(mov);
|
||||
emitline("\t");
|
||||
emitdispreg(fi.foff: i64, "CX");
|
||||
emitline(", X0\n");
|
||||
} else {
|
||||
let op: str = fieldloadop(fi);
|
||||
emitline("\t");
|
||||
@@ -1171,7 +1202,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
emitline("\t");
|
||||
emitdispreg(fi.foff: i64, "CX");
|
||||
emitline(", AX\n");
|
||||
};
|
||||
}; };
|
||||
return;
|
||||
};
|
||||
fi = fi.finext;
|
||||
@@ -1221,7 +1252,6 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
for (fi != nil) {
|
||||
if (streq(fi.fname, fld)) {
|
||||
cgexpr(c, lhs); // AX = ptr to inner struct
|
||||
let lop: str = fieldloadop(fi);
|
||||
// str field: load both halves.
|
||||
if (isstrtype(c, fi.tnode)) {
|
||||
emitline("\tMOVQ\t");
|
||||
@@ -1232,6 +1262,18 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
emitline(", AX\n");
|
||||
return;
|
||||
};
|
||||
// f64/f32 chained field: route through X0.
|
||||
if (isfloattype(c, fi.tnode)) {
|
||||
let mov: str = "MOVSD";
|
||||
if (isf32type(c, fi.tnode)) { mov = "MOVSS"; };
|
||||
emitline("\t");
|
||||
emitline(mov);
|
||||
emitline("\t");
|
||||
emitdispreg(fi.foff: i64, "AX");
|
||||
emitline(", X0\n");
|
||||
return;
|
||||
};
|
||||
let lop: str = fieldloadop(fi);
|
||||
emitline("\t");
|
||||
emitline(lop);
|
||||
emitline("\t");
|
||||
@@ -1486,14 +1528,28 @@ fn cgalloc(c: *cgen, n: *node) void = {
|
||||
let fn_: str = fi.fname;
|
||||
if (streq(fn_, fname)) {
|
||||
cgexpr(c, f.lhs);
|
||||
emitline("\tMOVQ\t(SP), BX\n");
|
||||
let sop: str = fieldstoreop(fi);
|
||||
emitline("\t");
|
||||
emitline(sop);
|
||||
emitline("\tAX, ");
|
||||
emitint(fi.foff: i64);
|
||||
emitline("(BX)\n");
|
||||
fi = nil;
|
||||
// alloc(T{ fval = v }) for f64/f32 field: cgexpr left
|
||||
// the value in X0, not AX — route the store via MOVSD/MOVSS.
|
||||
if (isfloattype(c, fi.tnode)) {
|
||||
let mov: str = "MOVSD";
|
||||
if (isf32type(c, fi.tnode)) { mov = "MOVSS"; };
|
||||
emitline("\tMOVQ\t(SP), BX\n");
|
||||
emitline("\t");
|
||||
emitline(mov);
|
||||
emitline("\tX0, ");
|
||||
emitint(fi.foff: i64);
|
||||
emitline("(BX)\n");
|
||||
fi = nil;
|
||||
} else {
|
||||
emitline("\tMOVQ\t(SP), BX\n");
|
||||
let sop: str = fieldstoreop(fi);
|
||||
emitline("\t");
|
||||
emitline(sop);
|
||||
emitline("\tAX, ");
|
||||
emitint(fi.foff: i64);
|
||||
emitline("(BX)\n");
|
||||
fi = nil;
|
||||
};
|
||||
} else {
|
||||
fi = fi.finext;
|
||||
};
|
||||
@@ -1850,6 +1906,8 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
if (n.op == tkind.TK_ASSIGN) {
|
||||
let inner: *node = lhs.lhs;
|
||||
let elemstr: bool = false;
|
||||
let elemfloat: bool = false;
|
||||
let elemf32: bool = false;
|
||||
let storeop: str = "MOVQ";
|
||||
if (inner != nil) {
|
||||
if (inner.kind == nkind.N_IDENT) {
|
||||
@@ -1862,11 +1920,13 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
if (pe != nil) {
|
||||
if (pe.kind == nkind.N_TNAME) {
|
||||
if (streq(pe.str, "str")) { elemstr = true; }
|
||||
else { if (streq(pe.str, "f64")) { elemfloat = true; }
|
||||
else { if (streq(pe.str, "f32")) { elemfloat = true; elemf32 = true; }
|
||||
else {
|
||||
let ps: i32 = primsize(pe.str);
|
||||
if (ps == 1) { storeop = "MOVB"; }
|
||||
else { if (ps == 4) { storeop = "MOVL"; }; };
|
||||
};
|
||||
}; }; };
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -1875,6 +1935,27 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
};
|
||||
};
|
||||
cgexpr(c, n.rhs);
|
||||
// `*p = v` for *f64 / *f32: value sits in X0. Spill
|
||||
// to the stack, evaluate the pointer (clobbers AX),
|
||||
// then reload X0 and MOVSD/MOVSS through the pointer.
|
||||
if (elemfloat) {
|
||||
let mov: str = "MOVSD";
|
||||
if (elemf32) { mov = "MOVSS"; };
|
||||
emitline("\tSUBQ\t$8, SP\n");
|
||||
emitline("\t");
|
||||
emitline(mov);
|
||||
emitline("\tX0, (SP)\n");
|
||||
cgexpr(c, inner);
|
||||
emitline("\tMOVQ\tAX, BX\n");
|
||||
emitline("\t");
|
||||
emitline(mov);
|
||||
emitline("\t(SP), X0\n");
|
||||
emitline("\tADDQ\t$8, SP\n");
|
||||
emitline("\t");
|
||||
emitline(mov);
|
||||
emitline("\tX0, (BX)\n");
|
||||
return;
|
||||
};
|
||||
// Push order matches C cgen
|
||||
// (cmd/w6c/cgen.c:1033-1041): PUSHQ AX
|
||||
// (ptr) first, then PUSHQ BX (len) if
|
||||
@@ -2037,6 +2118,21 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
emitline("\n");
|
||||
return;
|
||||
};
|
||||
// f64/f32 plain `=` via *struct: cgexpr left the
|
||||
// value in X0. Reload struct ptr and MOVSD/MOVSS.
|
||||
if (isfloattype(c, fi.tnode)) {
|
||||
let mov: str = "MOVSD";
|
||||
if (isf32type(c, fi.tnode)) { mov = "MOVSS"; };
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
emitline("\t");
|
||||
emitline(mov);
|
||||
emitline("\tX0, ");
|
||||
emitdispreg(fi.foff: i64, "BX");
|
||||
emitline("\n");
|
||||
return;
|
||||
};
|
||||
};
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
@@ -2064,6 +2160,17 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
let fn_: str = fi.fname;
|
||||
if (streq(fn_, fld)) {
|
||||
cgexpr(c, n.rhs);
|
||||
// f64/f32 direct struct local store: route via X0.
|
||||
if (isfloattype(c, fi.tnode)) {
|
||||
let mov: str = "MOVSD";
|
||||
if (isf32type(c, fi.tnode)) { mov = "MOVSS"; };
|
||||
emitline("\t");
|
||||
emitline(mov);
|
||||
emitline("\tX0, ");
|
||||
emitoff((lc.off + fi.foff): i64);
|
||||
emitline("(BP)\n");
|
||||
return;
|
||||
};
|
||||
let sop: str = fieldstoreop(fi);
|
||||
emitline("\t");
|
||||
emitline(sop);
|
||||
@@ -2174,6 +2281,21 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
emitline("\n");
|
||||
return;
|
||||
};
|
||||
// f64/f32 plain `=` on global struct field: value is
|
||||
// in X0; LEAQ the base into BX and MOVSD/MOVSS.
|
||||
if (isfloattype(c, fi.tnode)) {
|
||||
let mov: str = "MOVSD";
|
||||
if (isf32type(c, fi.tnode)) { mov = "MOVSS"; };
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, bn);
|
||||
emitline("(SB), BX\n");
|
||||
emitline("\t");
|
||||
emitline(mov);
|
||||
emitline("\tX0, ");
|
||||
emitdispreg(fi.foff: i64, "BX");
|
||||
emitline("\n");
|
||||
return;
|
||||
};
|
||||
let sop: str = fieldstoreop(fi);
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, bn);
|
||||
@@ -2268,6 +2390,30 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
emitline("\n");
|
||||
return;
|
||||
};
|
||||
// f64/f32 chained plain `=`: cgexpr rhs left value in
|
||||
// X0. Spill to stack so cgexpr(base) can use AX, then
|
||||
// reload and MOVSD/MOVSS into the slot.
|
||||
if (isfloattype(c, fi.tnode)) {
|
||||
let mov: str = "MOVSD";
|
||||
if (isf32type(c, fi.tnode)) { mov = "MOVSS"; };
|
||||
cgexpr(c, n.rhs);
|
||||
emitline("\tSUBQ\t$8, SP\n");
|
||||
emitline("\t");
|
||||
emitline(mov);
|
||||
emitline("\tX0, (SP)\n");
|
||||
cgexpr(c, base);
|
||||
emitline("\tMOVQ\tAX, BX\n");
|
||||
emitline("\t");
|
||||
emitline(mov);
|
||||
emitline("\t(SP), X0\n");
|
||||
emitline("\tADDQ\t$8, SP\n");
|
||||
emitline("\t");
|
||||
emitline(mov);
|
||||
emitline("\tX0, ");
|
||||
emitdispreg(fi.foff: i64, "BX");
|
||||
emitline("\n");
|
||||
return;
|
||||
};
|
||||
cgexpr(c, n.rhs);
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
cgexpr(c, base);
|
||||
|
||||
@@ -452,13 +452,26 @@ fn cglet(c: *cgen, n: *node) void = {
|
||||
let fn_: str = fi.fname;
|
||||
if (streq(fn_, fname)) {
|
||||
cgexpr(c, fieldnode.lhs);
|
||||
let sop: str = fieldstoreop(fi);
|
||||
emitline("\t");
|
||||
emitline(sop);
|
||||
emitline("\tAX, ");
|
||||
emitoff((off + fi.foff): i64);
|
||||
emitline("(BP)\n");
|
||||
fi = nil;
|
||||
// f64/f32 struct-literal field init: cgexpr left
|
||||
// the value in X0, store via MOVSD/MOVSS.
|
||||
if (isfloattype(c, fi.tnode)) {
|
||||
let mov: str = "MOVSD";
|
||||
if (isf32type(c, fi.tnode)) { mov = "MOVSS"; };
|
||||
emitline("\t");
|
||||
emitline(mov);
|
||||
emitline("\tX0, ");
|
||||
emitoff((off + fi.foff): i64);
|
||||
emitline("(BP)\n");
|
||||
fi = nil;
|
||||
} else {
|
||||
let sop: str = fieldstoreop(fi);
|
||||
emitline("\t");
|
||||
emitline(sop);
|
||||
emitline("\tAX, ");
|
||||
emitoff((off + fi.foff): i64);
|
||||
emitline("(BP)\n");
|
||||
fi = nil;
|
||||
};
|
||||
} else {
|
||||
fi = fi.finext;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user