wcc/cgen: #135 inferred-float module-global — default untyped_float to f64 (wwstage)
let pi = 3.5; pi * 2.0 (an inferred-type float module-global) was silently miscompiled by wwstage: untyped_float wasn't defaulted, so letemitsize sized it 0 -> no DATAW emitted -> the pi load was dropped, X0 kept a stale spill -> 2.0*2.0 = 4 not 7. cstage became correct via #150-B's sym-repoint (stamps f64 -> MOVSD), so this aligns wwstage UP, byte-identical. wwstage-only: cgen.ww defaultinferredlets gains the untyped_float->f64 arm (mirrors the untyped_int->int arm; the codebase's own #135-deferred carve-out at cgen.ww:1079-1082, unblocked now that #150-B killed the rule-10 divergence it feared), and cgenexpr.ww cgident gets a letfloatprim fallback (the same primitive-TNAME SSoT letemitsize already uses, since a renamed primitive TNAME carries no tinfo stamp). cstage cgen unchanged (w6c md5 unchanged). int-inferred globals stay integer. byte-id 990-997 8/8. test/wcc/824 table-driven. The N_CAST-no-recurse parity (check.c:1276) is filed separately (#19).
This commit is contained in:
@@ -22708,9 +22708,23 @@ fn cgident(c: *cgen, n: *node) void = {
|
||||
let lv: *letvar = c.lets;
|
||||
for (lv != nil) {
|
||||
if (streq(lv.name, nm)) {
|
||||
if (isfloattype(c, lv.tnode)) {
|
||||
// #135: an inferred-float global's tnode is the
|
||||
// defaultinferredlets-renamed "f64"/"f32" N_TNAME whose
|
||||
// .type_ is unstamped (cgen can't build tinfo), so the
|
||||
// isfloattype stamp-read misses it. Fall back to the
|
||||
// name keyword (the letfloatprim SSoT letemitsize uses)
|
||||
// so the float load fires for inferred as for explicit.
|
||||
let isf: bool = isfloattype(c, lv.tnode);
|
||||
let is32: bool = isf32type(c, lv.tnode);
|
||||
if (!isf && lv.tnode != nil
|
||||
&& lv.tnode.kind == nkind.N_TNAME) {
|
||||
let fsz: i32 = letfloatprim(lv.tnode.str);
|
||||
if (fsz > 0) { isf = true; };
|
||||
if (fsz == 4) { is32 = true; };
|
||||
};
|
||||
if (isf) {
|
||||
let mov: str = "MOVSD";
|
||||
if (isf32type(c, lv.tnode)) { mov = "MOVSS"; };
|
||||
if (is32) { mov = "MOVSS"; };
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymnamehint(c, nm, c.curmod);
|
||||
emitline("(SB), CX\n");
|
||||
@@ -39444,6 +39458,30 @@ fn defaultinferredlets(c: *cgen, file: *node) void = {
|
||||
d.lhs.str = "int";
|
||||
};
|
||||
};
|
||||
// #135: the inferred-FLOAT twin, now unblocked. The carve-
|
||||
// out above (deferred to #135) feared a cs≠ww divergence
|
||||
// because cstage USED to integer-type an inferred float
|
||||
// (MOVQ); #150-B fixed cstage to type_default untyped_float
|
||||
// → f64 and load MOVSD, so defaulting here now CONVERGES.
|
||||
// Without it, letemitsize sees "untyped_float" (not in
|
||||
// letfloatprim) → 0 → the global is dropped from collectlets
|
||||
// (no DATAW) and the read falls to cgident's silent bare
|
||||
// return (X0 untouched). Mirror cstage check.c clet
|
||||
// type_default.
|
||||
if (d.lhs != nil && d.rhs != nil
|
||||
&& d.lhs.kind == nkind.N_TNAME
|
||||
&& streq(d.lhs.str, "untyped_float")) {
|
||||
let opnd: *node = d.rhs;
|
||||
if (opnd.kind == nkind.N_UN
|
||||
&& (opnd.op == tkind.TK_PLUS
|
||||
|| opnd.op == tkind.TK_MINUS)) {
|
||||
opnd = opnd.lhs;
|
||||
};
|
||||
if (opnd != nil
|
||||
&& opnd.kind == nkind.N_FLOATLIT) {
|
||||
d.lhs.str = "f64";
|
||||
};
|
||||
};
|
||||
};
|
||||
d = d.next;
|
||||
};
|
||||
|
||||
@@ -1102,6 +1102,30 @@ fn defaultinferredlets(c: *cgen, file: *node) void = {
|
||||
d.lhs.str = "int";
|
||||
};
|
||||
};
|
||||
// #135: the inferred-FLOAT twin, now unblocked. The carve-
|
||||
// out above (deferred to #135) feared a cs≠ww divergence
|
||||
// because cstage USED to integer-type an inferred float
|
||||
// (MOVQ); #150-B fixed cstage to type_default untyped_float
|
||||
// → f64 and load MOVSD, so defaulting here now CONVERGES.
|
||||
// Without it, letemitsize sees "untyped_float" (not in
|
||||
// letfloatprim) → 0 → the global is dropped from collectlets
|
||||
// (no DATAW) and the read falls to cgident's silent bare
|
||||
// return (X0 untouched). Mirror cstage check.c clet
|
||||
// type_default.
|
||||
if (d.lhs != nil && d.rhs != nil
|
||||
&& d.lhs.kind == nkind.N_TNAME
|
||||
&& streq(d.lhs.str, "untyped_float")) {
|
||||
let opnd: *node = d.rhs;
|
||||
if (opnd.kind == nkind.N_UN
|
||||
&& (opnd.op == tkind.TK_PLUS
|
||||
|| opnd.op == tkind.TK_MINUS)) {
|
||||
opnd = opnd.lhs;
|
||||
};
|
||||
if (opnd != nil
|
||||
&& opnd.kind == nkind.N_FLOATLIT) {
|
||||
d.lhs.str = "f64";
|
||||
};
|
||||
};
|
||||
};
|
||||
d = d.next;
|
||||
};
|
||||
|
||||
@@ -1125,9 +1125,23 @@ fn cgident(c: *cgen, n: *node) void = {
|
||||
let lv: *letvar = c.lets;
|
||||
for (lv != nil) {
|
||||
if (streq(lv.name, nm)) {
|
||||
if (isfloattype(c, lv.tnode)) {
|
||||
// #135: an inferred-float global's tnode is the
|
||||
// defaultinferredlets-renamed "f64"/"f32" N_TNAME whose
|
||||
// .type_ is unstamped (cgen can't build tinfo), so the
|
||||
// isfloattype stamp-read misses it. Fall back to the
|
||||
// name keyword (the letfloatprim SSoT letemitsize uses)
|
||||
// so the float load fires for inferred as for explicit.
|
||||
let isf: bool = isfloattype(c, lv.tnode);
|
||||
let is32: bool = isf32type(c, lv.tnode);
|
||||
if (!isf && lv.tnode != nil
|
||||
&& lv.tnode.kind == nkind.N_TNAME) {
|
||||
let fsz: i32 = letfloatprim(lv.tnode.str);
|
||||
if (fsz > 0) { isf = true; };
|
||||
if (fsz == 4) { is32 = true; };
|
||||
};
|
||||
if (isf) {
|
||||
let mov: str = "MOVSD";
|
||||
if (isf32type(c, lv.tnode)) { mov = "MOVSS"; };
|
||||
if (is32) { mov = "MOVSS"; };
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymnamehint(c, nm, c.curmod);
|
||||
emitline("(SB), CX\n");
|
||||
|
||||
@@ -22708,9 +22708,23 @@ fn cgident(c: *cgen, n: *node) void = {
|
||||
let lv: *letvar = c.lets;
|
||||
for (lv != nil) {
|
||||
if (streq(lv.name, nm)) {
|
||||
if (isfloattype(c, lv.tnode)) {
|
||||
// #135: an inferred-float global's tnode is the
|
||||
// defaultinferredlets-renamed "f64"/"f32" N_TNAME whose
|
||||
// .type_ is unstamped (cgen can't build tinfo), so the
|
||||
// isfloattype stamp-read misses it. Fall back to the
|
||||
// name keyword (the letfloatprim SSoT letemitsize uses)
|
||||
// so the float load fires for inferred as for explicit.
|
||||
let isf: bool = isfloattype(c, lv.tnode);
|
||||
let is32: bool = isf32type(c, lv.tnode);
|
||||
if (!isf && lv.tnode != nil
|
||||
&& lv.tnode.kind == nkind.N_TNAME) {
|
||||
let fsz: i32 = letfloatprim(lv.tnode.str);
|
||||
if (fsz > 0) { isf = true; };
|
||||
if (fsz == 4) { is32 = true; };
|
||||
};
|
||||
if (isf) {
|
||||
let mov: str = "MOVSD";
|
||||
if (isf32type(c, lv.tnode)) { mov = "MOVSS"; };
|
||||
if (is32) { mov = "MOVSS"; };
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymnamehint(c, nm, c.curmod);
|
||||
emitline("(SB), CX\n");
|
||||
@@ -39444,6 +39458,30 @@ fn defaultinferredlets(c: *cgen, file: *node) void = {
|
||||
d.lhs.str = "int";
|
||||
};
|
||||
};
|
||||
// #135: the inferred-FLOAT twin, now unblocked. The carve-
|
||||
// out above (deferred to #135) feared a cs≠ww divergence
|
||||
// because cstage USED to integer-type an inferred float
|
||||
// (MOVQ); #150-B fixed cstage to type_default untyped_float
|
||||
// → f64 and load MOVSD, so defaulting here now CONVERGES.
|
||||
// Without it, letemitsize sees "untyped_float" (not in
|
||||
// letfloatprim) → 0 → the global is dropped from collectlets
|
||||
// (no DATAW) and the read falls to cgident's silent bare
|
||||
// return (X0 untouched). Mirror cstage check.c clet
|
||||
// type_default.
|
||||
if (d.lhs != nil && d.rhs != nil
|
||||
&& d.lhs.kind == nkind.N_TNAME
|
||||
&& streq(d.lhs.str, "untyped_float")) {
|
||||
let opnd: *node = d.rhs;
|
||||
if (opnd.kind == nkind.N_UN
|
||||
&& (opnd.op == tkind.TK_PLUS
|
||||
|| opnd.op == tkind.TK_MINUS)) {
|
||||
opnd = opnd.lhs;
|
||||
};
|
||||
if (opnd != nil
|
||||
&& opnd.kind == nkind.N_FLOATLIT) {
|
||||
d.lhs.str = "f64";
|
||||
};
|
||||
};
|
||||
};
|
||||
d = d.next;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user