code wiki / _hdl_build / nx_eqsat_constfold_test.nx

nx_eqsat_constfold_test.nx source

↩ module page · 193 lines · 11395 B

1// nx_eqsat_constfold_test.nx -- the CONST-FOLD GATE: proves the egg-style e-class 2// ANALYSIS (constant folding / propagation) newly integrated into nx_eqsat.nx is 3// (a) SOUND BY EVALUATION (folds via the PROVEN nx_gsim_eval_cell oracle), 4// (b) CERTIFIED through membership-as-proof (cites NX_EQSAT_RULE_CONSTFOLD, admitted 5// to the proven-sound allow-list + lockstep), and 6// (c) LOAD-BEARING (a wrong fold / unmapped op / unproven rule is REFUSED). 7// 8// FAIL LOUD known-answer; exit 0 iff every check holds. 9// 10// STEPS (each self-asserts; mismatch => sys_exit(nonzero)): 11// STEP 0 LOCKSTEP: MP_RULE_CONSTFOLD == NX_EQSAT_RULE_CONSTFOLD (and the whole 12// id table agrees), so the live log the engine writes is never mislabeled. 13// Also: MP_RULE_CONSTFOLD must be in the proven-sound allow-list. 14// STEP 1 POSITIVE (add 2 3)->5, LIVE + CERTIFIED: enable prov + constfold, run the 15// REAL saturator, assert find(add23)==find(5), the log contains RULE_CONSTFOLD, 16// and mp_certify_live CERTIFIES (member && sound). 17// STEP 2 MORE OPS: (mul 6 7)->42 and (shl 1 4)->16 fold + certify (same engine path). 18// STEP 3 NEGATIVE -- WRONG FOLD REFUSED: (a) an unmapped op (DIV) never folds even 19// with const children (nx_eqsat_op_to_gate_kind(DIV) < 0) -- find stays 20// distinct from any const; (b) an UNPROVEN rule id (BOGUS) is NOT in the 21// allow-list so mp_log_all_sound would refuse it -- a back-door wrong fold 22// cannot certify; (c) folding is OFF by default (no enable => no fold => no 23// CONSTFOLD in the log) so the feature is opt-in by construction. 24// 25// Known answer (FAIL LOUD): prints "5 42 16 1 1 1 " == folded(add 2 3), folded(mul 6 7), 26// folded(shl 1 4), cert_add, cert_mul, cert_shl. exit 0 iff all steps hold. 27 28import "nx_eqsat_membership_proof.nx" 29 30func _emit_num(v: i64) -> i64 { 31 let b: *u8 = sys_mmap(28); var n: i64 = v; if n < 0 { n = 0 - n } 32 let t2: *u8 = sys_mmap(28); var t: i64 = 0 33 if n == 0 { t2[0] = 48; t = 1 } 34 while n > 0 { t2[t] = 48 + (n % 10); n = n / 10; t = t + 1 } 35 var i: i64 = 0; while i < t { b[i] = t2[t - 1 - i]; i = i + 1 } 36 b[t] = 32; sys_write(1, b, t + 1); return 0 37} 38func _nl() -> i64 { let z: *u8 = sys_mmap(2); z[0] = 10; sys_write(1, z, 1); return 0 } 39 40// Does the live provenance log contain rule-id `want`? 41func _log_has(g: *NxEGraph, want: i64) -> i64 { 42 var i: i64 = 0 43 while i < g.n_prov { 44 if g.prov[i] == want { return 1 } 45 i = i + 1 46 } 47 return 0 48} 49 50// Build a fresh e-graph that folds a binary op of two constants, return the 51// folded value read from the resulting class's best_node + whether it CERTIFIES. 52// cert_out[0] = mp_certify_live verdict. Returns the folded constant value. 53func _fold_binop(op: i64, a: i64, b: i64, cert_out: *i64) -> i64 { 54 let cap_nodes: i64 = 64 55 let cap_cls: i64 = 64 56 let nodes: *NxENode = sys_mmap(cap_nodes * 64) as *NxENode 57 let classes: *NxEClass = sys_mmap(cap_cls * 32) as *NxEClass 58 let eg: *NxEGraph = sys_mmap(128) as *NxEGraph 59 if nx_eqsat_init(eg, nodes, cap_nodes, classes, cap_cls) != NX_EQSAT_OK { sys_exit(80); return 80 } 60 // enable provenance + const-fold BEFORE adding the op node (the hook fires at add) 61 let plog: *i64 = sys_mmap(64 * 8) as *i64 62 if nx_eqsat_enable_prov(eg, plog, 64) != NX_EQSAT_OK { sys_exit(81); return 81 } 63 if nx_eqsat_enable_constfold(eg) != NX_EQSAT_OK { sys_exit(82); return 82 } 64 let ca: i64 = nx_eqsat_add_const(eg, a) 65 let cb: i64 = nx_eqsat_add_const(eg, b) 66 let top: i64 = nx_eqsat_add_binary(eg, op, ca, cb) // hook folds here 67 // The expected constant (added separately) for the membership endpoint. 68 // gsim is the engine's oracle; recompute the same value via the SAME oracle. 69 let expect: i64 = nx_gsim_eval_cell(nx_eqsat_op_to_gate_kind(op), a, b, 0) 70 let cexp: i64 = nx_eqsat_add_const(eg, expect) 71 // run the real saturator (idempotent; the fold already merged at add-time) 72 nx_eqsat_saturate(eg, 16) 73 // certify the equivalence top == CONST(expect) STRICTLY FROM THE LOG. 74 let cert: *NxMpCertificate = sys_mmap(64) as *NxMpCertificate 75 let cok: i64 = mp_certify_live(eg, top, cexp, cert) 76 cert_out[0] = cok 77 // sanity: the log must contain RULE_CONSTFOLD (the merge was const-fold) 78 if _log_has(eg, NX_EQSAT_RULE_CONSTFOLD) != 1 { sys_exit(83); return 83 } 79 // read the folded value off top's canonical best_node 80 let canon: i64 = nx_eqsat_find(eg, top) 81 let bn: i64 = eg.classes[canon].best_node 82 if eg.nodes[bn].op != NX_EQ_OP_CONST { sys_exit(84); return 84 } 83 return eg.nodes[bn].payload 84} 85 86func main() -> i64 { 87 // ==================================================================== 88 // STEP 0: LOCKSTEP + allow-list admission. 89 // ==================================================================== 90 if mp_rule_ids_lockstep() != 1 { sys_exit(1); return 1 } 91 if MP_RULE_CONSTFOLD != NX_EQSAT_RULE_CONSTFOLD { sys_exit(2); return 2 } 92 if mp_is_sound_rule(MP_RULE_CONSTFOLD) != 1 { sys_exit(3); return 3 } // admitted 93 if NX_EQSAT_RULE_N != 12 { sys_exit(4); return 4 } // RULE_N bumped (10 constfold + 11 shift_merge) 94 95 // ==================================================================== 96 // STEP 1 + STEP 2: positive folds + certificates. 97 // ==================================================================== 98 let ca: *i64 = sys_mmap(8) as *i64 99 let cm: *i64 = sys_mmap(8) as *i64 100 let cs: *i64 = sys_mmap(8) as *i64 101 let f_add: i64 = _fold_binop(NX_EQ_OP_ADD, 2, 3, ca) // 2+3 = 5 102 let f_mul: i64 = _fold_binop(NX_EQ_OP_MUL, 6, 7, cm) // 6*7 = 42 103 let f_shl: i64 = _fold_binop(NX_EQ_OP_SHL, 1, 4, cs) // 1<<4 = 16 104 105 _emit_num(f_add); _emit_num(f_mul); _emit_num(f_shl) 106 _emit_num(ca[0]); _emit_num(cm[0]); _emit_num(cs[0]) 107 _nl() 108 109 // ==================================================================== 110 // STEP 3a: NEGATIVE -- an UNMAPPED op (DIV) never folds (no faithful cell). 111 // ==================================================================== 112 if nx_eqsat_op_to_gate_kind(NX_EQ_OP_DIV) >= 0 { sys_exit(10); return 10 } // DIV refused 113 if nx_eqsat_op_to_gate_kind(NX_EQ_OP_REM) >= 0 { sys_exit(11); return 11 } // REM refused 114 if nx_eqsat_op_to_gate_kind(NX_EQ_OP_SHR) >= 0 { sys_exit(12); return 12 } // SHR refused (logical!=arith) 115 if nx_eqsat_op_to_gate_kind(NX_EQ_OP_SAR) >= 0 { sys_exit(13); return 13 } // SAR refused 116 if nx_eqsat_op_to_gate_kind(NX_EQ_OP_NEG) >= 0 { sys_exit(14); return 14 } // NEG refused (NOT != neg) 117 // build (div 12 3) with const children + const-fold ON: it must NOT fold. 118 let cap_nodes: i64 = 64 119 let cap_cls: i64 = 64 120 let n2: *NxENode = sys_mmap(cap_nodes * 64) as *NxENode 121 let c2: *NxEClass = sys_mmap(cap_cls * 32) as *NxEClass 122 let eg2: *NxEGraph = sys_mmap(128) as *NxEGraph 123 if nx_eqsat_init(eg2, n2, cap_nodes, c2, cap_cls) != NX_EQSAT_OK { sys_exit(15); return 15 } 124 let plog2: *i64 = sys_mmap(64 * 8) as *i64 125 nx_eqsat_enable_prov(eg2, plog2, 64) 126 nx_eqsat_enable_constfold(eg2) 127 let d12: i64 = nx_eqsat_add_const(eg2, 12) 128 let d3: i64 = nx_eqsat_add_const(eg2, 3) 129 let divv: i64 = nx_eqsat_add_binary(eg2, NX_EQ_OP_DIV, d12, d3) // would-be 4, but REFUSED 130 let four: i64 = nx_eqsat_add_const(eg2, 4) 131 nx_eqsat_saturate(eg2, 16) 132 if nx_eqsat_find(eg2, divv) == nx_eqsat_find(eg2, four) { sys_exit(16); return 16 } // must NOT fold 133 if _log_has(eg2, NX_EQSAT_RULE_CONSTFOLD) != 0 { sys_exit(17); return 17 } // no constfold logged 134 // the DIV node's best_node must still be a DIV (no const merged in) 135 let dc: i64 = nx_eqsat_find(eg2, divv) 136 if eg2.nodes[eg2.classes[dc].best_node].op != NX_EQ_OP_DIV { sys_exit(18); return 18 } 137 138 // ==================================================================== 139 // STEP 3b: NEGATIVE -- an UNPROVEN rule id is refused by the certifier. A 140 // back-door merge citing RULE_NONE (the legacy 2-arg union) poisons the log, 141 // so mp_log_all_sound refuses it -> the certificate cannot pass even though 142 // the endpoints ARE in the same class (membership holds). 143 // ==================================================================== 144 let n3: *NxENode = sys_mmap(cap_nodes * 64) as *NxENode 145 let c3a: *NxEClass = sys_mmap(cap_cls * 32) as *NxEClass 146 let eg3: *NxEGraph = sys_mmap(128) as *NxEGraph 147 if nx_eqsat_init(eg3, n3, cap_nodes, c3a, cap_cls) != NX_EQSAT_OK { sys_exit(20); return 20 } 148 let plog3: *i64 = sys_mmap(64 * 8) as *i64 149 nx_eqsat_enable_prov(eg3, plog3, 64) 150 let va: i64 = nx_eqsat_add_var(eg3, 0) 151 let vb: i64 = nx_eqsat_add_var(eg3, 1) 152 // merge two UNEQUAL vars through the RULE_NONE back door (an unsound merge). 153 nx_eqsat_union(eg3, va, vb) 154 let certN: *NxMpCertificate = sys_mmap(64) as *NxMpCertificate 155 let cokN: i64 = mp_certify_live(eg3, va, vb, certN) 156 if certN.member != 1 { sys_exit(21); return 21 } // they ARE merged (membership holds) 157 if certN.sound != 0 { sys_exit(22); return 22 } // but RULE_NONE poisons soundness 158 if cokN != 0 { sys_exit(23); return 23 } // certificate MUST fail (unproven rule) 159 // RULE_NONE / BOGUS must not be in the proven-sound allow-list. 160 if mp_is_sound_rule(MP_RULE_NONE) != 0 { sys_exit(24); return 24 } 161 if mp_is_sound_rule(MP_RULE_BOGUS_MUL3) != 0 { sys_exit(25); return 25 } 162 163 // ==================================================================== 164 // STEP 3c: const-fold is OFF by default (opt-in). With NO enable call, an 165 // all-const op must NOT fold and must NOT log a CONSTFOLD id (byte-identical 166 // backward-compat by construction). 167 // ==================================================================== 168 let n4: *NxENode = sys_mmap(cap_nodes * 64) as *NxENode 169 let c4: *NxEClass = sys_mmap(cap_cls * 32) as *NxEClass 170 let eg4: *NxEGraph = sys_mmap(128) as *NxEGraph 171 if nx_eqsat_init(eg4, n4, cap_nodes, c4, cap_cls) != NX_EQSAT_OK { sys_exit(30); return 30 } 172 let plog4: *i64 = sys_mmap(64 * 8) as *i64 173 nx_eqsat_enable_prov(eg4, plog4, 64) 174 // NOTE: NO nx_eqsat_enable_constfold call -- folding stays OFF. 175 let oa: i64 = nx_eqsat_add_const(eg4, 2) 176 let ob: i64 = nx_eqsat_add_const(eg4, 3) 177 let osum: i64 = nx_eqsat_add_binary(eg4, NX_EQ_OP_ADD, oa, ob) 178 let ofive: i64 = nx_eqsat_add_const(eg4, 5) 179 nx_eqsat_saturate(eg4, 16) 180 if nx_eqsat_find(eg4, osum) == nx_eqsat_find(eg4, ofive) { sys_exit(31); return 31 } // OFF => no fold 181 if _log_has(eg4, NX_EQSAT_RULE_CONSTFOLD) != 0 { sys_exit(32); return 32 } // nothing logged 182 183 // ==================================================================== 184 // FINAL known-answer gate (FAIL LOUD). 185 // ==================================================================== 186 if f_add != 5 { sys_exit(40); return 40 } // 2 + 3 = 5 187 if f_mul != 42 { sys_exit(41); return 41 } // 6 * 7 = 42 188 if f_shl != 16 { sys_exit(42); return 42 } // 1 << 4 = 16 189 if ca[0] != 1 { sys_exit(43); return 43 } // (add 2 3)==5 certified (live) 190 if cm[0] != 1 { sys_exit(44); return 44 } // (mul 6 7)==42 certified 191 if cs[0] != 1 { sys_exit(45); return 45 } // (shl 1 4)==16 certified 192 sys_exit(0); return 0 193}