wcc: retire exprfloatkind, read the checker stamp directly
exprfloatkind was wwstage cgen's structural float-classifier — a workaround for the checker stamp being untrustworthy. With the previous commit arming the asserttyped bail, every checked value-node is now stamped (or cited-exempt), so its job collapses to a 2-liner reading n.type_ — the same path cstage cgen has always taken. Retire it: inline the stamp-read at its eight sites (cgcast, cgun, cgbin lhs+rhs, cgcall pop, pushargsrev, cgwidentaggedstorebp, cgreturn x2 collapsed), delete the wrapper, and delete the two residual sibling-evidence loud-aborts (cgbin float-arith, cgwidentaggedstorebp float-arm) — their operands are real source value-exprs the armed bail now stamps, so the guards can never fire. One synth-post-checker value-node remained outside the bail's reach: the variadic-slice descriptor pushed in pushargsrev/cgcall (cgenexpr.ww). Stamp it at synthesis with the variadic param's []T slice tinfo so the inlined reads see a stamped node, no nil special-case. Byte-id-neutral by design (slice tinfo and nil both read non-float); 990-997 confirm. Closes the bail-rearm arc — wwstage now reads the same float-class SSoT cstage does, the gate-blind float-classification family is closed, and the build+test corpus is asserttyped-clean by construction.
This commit is contained in:
@@ -414,7 +414,12 @@ fn cgtypeassert(c: *cgen, n: *node) void = {
|
||||
};
|
||||
|
||||
fn cgcast(c: *cgen, n: *node) void = {
|
||||
let srcfk: i32 = exprfloatkind(c, n.lhs);
|
||||
let srcfk: i32 = 0;
|
||||
if (n.lhs != nil) {
|
||||
let st: *tinfo = n.lhs.type_: *tinfo;
|
||||
if (typeisf32(st)) { srcfk = 1; }
|
||||
else { if (typeisfloat(st)) { srcfk = 2; }; };
|
||||
};
|
||||
let dstf64: bool = isfloattype(c, n.rhs);
|
||||
let dstf32: bool = isf32type(c, n.rhs);
|
||||
let dstfk: i32 = 0;
|
||||
@@ -2653,7 +2658,12 @@ fn cgun(c: *cgen, n: *node) void = {
|
||||
// then apply the unary op. AMP / STAR override AX with the
|
||||
// address / deref. The wasted load before AMP keeps our asm
|
||||
// byte-identical to the C version.
|
||||
let fk: i32 = exprfloatkind(c, n.lhs);
|
||||
let fk: i32 = 0;
|
||||
if (n.lhs != nil) {
|
||||
let lt: *tinfo = n.lhs.type_: *tinfo;
|
||||
if (typeisf32(lt)) { fk = 1; }
|
||||
else { if (typeisfloat(lt)) { fk = 2; }; };
|
||||
};
|
||||
if (n.op == tkind.TK_MINUS && fk != 0) {
|
||||
// Float negate: X0 = 0 - X0. Stash orig, load 0.0, subtract.
|
||||
// Zero bit pattern equals 0.0 for both f32 and f64 so we
|
||||
@@ -3119,60 +3129,27 @@ fn cgbin(c: *cgen, n: *node) void = {
|
||||
// general FP register saver. ADDSD/SUBSD/MULSD/DIVSD pick SS
|
||||
// variants for f32. Comparison uses UCOMISD + JCC and falls
|
||||
// out to the existing CMPQ-based path below.
|
||||
let lfk: i32 = exprfloatkind(c, n.lhs);
|
||||
let rfk: i32 = exprfloatkind(c, n.rhs);
|
||||
// Value-class read off the checker stamp (n.type_) — the SSoT
|
||||
// shared with cstage cgen.c node_isfloat / type_isf32. The armed
|
||||
// asserttyped bail (check.ww) guarantees every checked value-node
|
||||
// is stamped, so the read can't see a nil-typed float operand;
|
||||
// the sibling-evidence loud-aborts that used to pin that contract
|
||||
// are therefore dead and removed.
|
||||
let lfk: i32 = 0;
|
||||
if (n.lhs != nil) {
|
||||
let llt: *tinfo = n.lhs.type_: *tinfo;
|
||||
if (typeisf32(llt)) { lfk = 1; }
|
||||
else { if (typeisfloat(llt)) { lfk = 2; }; };
|
||||
};
|
||||
let rfk: i32 = 0;
|
||||
if (n.rhs != nil) {
|
||||
let rrt: *tinfo = n.rhs.type_: *tinfo;
|
||||
if (typeisf32(rrt)) { rfk = 1; }
|
||||
else { if (typeisfloat(rrt)) { rfk = 2; }; };
|
||||
};
|
||||
let fk: i32 = lfk;
|
||||
if (fk == 0) { fk = rfk; };
|
||||
if (fk != 0) {
|
||||
// #121 (Package B) RESIDUAL sibling-evidence guard. The
|
||||
// exprfloatkind collapse reads n.type_ as the SSoT; an
|
||||
// UNSTAMPED operand (type_==nil) misclassifies as 0 and would
|
||||
// silently take the integer arm of a float binop — exactly the
|
||||
// gate-blind miscompile the structural oracle used to mask
|
||||
// (and the bridge proved corpus-clean for at 1c4cea4). With
|
||||
// the structural net gone, a future N_DOT-callee float-tuple
|
||||
// destructure (`let (frac, exp) = math.frexpf64(x);` —
|
||||
// blocked on #16/#17 N_DOT-callee stamp) would leave its
|
||||
// bindings unstamped → fall through here without this assert.
|
||||
// Predicate: operand whose own efk==0 (not classified as
|
||||
// float) AND type_==nil (UNSTAMPED, not a stamped non-float
|
||||
// like an int passed through a deliberate path). Sibling
|
||||
// evidence: the binop is float (fk!=0), so the operand should
|
||||
// either be float (efk!=0) or a stamped non-float (e.g. int);
|
||||
// nil-typed is the dangerous case. cgcast (cgenexpr.ww:417) is
|
||||
// EXCLUDED — int→float source is legitimately a CVTSI2SD
|
||||
// target. Loud-abort idiom mirrors cgenstmt.ww:1405/1475 +
|
||||
// asserttyped file:line at check.ww:3340-3344.
|
||||
if (lfk == 0) { if (n.lhs != nil) { if (n.lhs.type_ == nil) {
|
||||
let msg: str = "cgbin float-arith: lhs operand unstamped (#121 sibling-evidence) at ";
|
||||
os.write(2, msg.ptr, msg.len: u64);
|
||||
if (n.lhs.file.len > 0) {
|
||||
os.write(2, n.lhs.file.ptr, n.lhs.file.len: u64);
|
||||
os.write(2, ":".ptr, 1u64);
|
||||
let ls: str = strconv.i32tos(n.lhs.line, strconv.base.DEC);
|
||||
os.write(2, ls.ptr, ls.len: u64);
|
||||
os.write(2, " ".ptr, 1u64);
|
||||
};
|
||||
let kn: str = nkname(n.lhs.kind);
|
||||
os.write(2, kn.ptr, kn.len: u64);
|
||||
os.write(2, "\n".ptr, 1u64);
|
||||
os.exit(1);
|
||||
}; }; };
|
||||
if (rfk == 0) { if (n.rhs != nil) { if (n.rhs.type_ == nil) {
|
||||
let msg: str = "cgbin float-arith: rhs operand unstamped (#121 sibling-evidence) at ";
|
||||
os.write(2, msg.ptr, msg.len: u64);
|
||||
if (n.rhs.file.len > 0) {
|
||||
os.write(2, n.rhs.file.ptr, n.rhs.file.len: u64);
|
||||
os.write(2, ":".ptr, 1u64);
|
||||
let ls: str = strconv.i32tos(n.rhs.line, strconv.base.DEC);
|
||||
os.write(2, ls.ptr, ls.len: u64);
|
||||
os.write(2, " ".ptr, 1u64);
|
||||
};
|
||||
let kn: str = nkname(n.rhs.kind);
|
||||
os.write(2, kn.ptr, kn.len: u64);
|
||||
os.write(2, "\n".ptr, 1u64);
|
||||
os.exit(1);
|
||||
}; }; };
|
||||
let mov: str = "MOVSD";
|
||||
if (fk == 1) { mov = "MOVSS"; };
|
||||
if (n.op == tkind.TK_PLUS ||
|
||||
@@ -3878,6 +3855,13 @@ fn cgcall(c: *cgen, n: *node) void = {
|
||||
let sn: *node = newnode(nkind.N_IDENT,
|
||||
"", 0, 0);
|
||||
sn.str = sname;
|
||||
// Synthesised after the checker has run, so the
|
||||
// asserttyped bail (check.ww) never stamps it.
|
||||
// Stamp the variadic param's []T slice tinfo
|
||||
// (resolvefnbody resolve-walks varp.lhs) so the
|
||||
// downstream value-class reads see a non-nil
|
||||
// stamp — the one cgen node the bail can't cover.
|
||||
sn.type_ = varp.lhs.type_;
|
||||
if (prevarg == nil) { n.list = sn; }
|
||||
else { prevarg.next = sn; };
|
||||
};
|
||||
@@ -3916,7 +3900,12 @@ fn cgcall(c: *cgen, n: *node) void = {
|
||||
let popped: i32 = 0;
|
||||
let stackslots: i32 = 0;
|
||||
for (a != nil) {
|
||||
let fk: i32 = exprfloatkind(c, a);
|
||||
let fk: i32 = 0;
|
||||
if (a != nil) {
|
||||
let at: *tinfo = a.type_: *tinfo;
|
||||
if (typeisf32(at)) { fk = 1; }
|
||||
else { if (typeisfloat(at)) { fk = 2; }; };
|
||||
};
|
||||
if (fk != 0) {
|
||||
let mov: str = "MOVSD";
|
||||
if (fk == 1) { mov = "MOVSS"; };
|
||||
|
||||
Reference in New Issue
Block a user