code wiki / _hdl_build / nx_isa_cf_sov.nx
nx_isa_cf_sov.nx source
↩ module page · 409 lines · 20800 B
1// nx_isa_cf_sov.nx -- CONTROL-FLOW rung: derive programs that BRANCH, run them on god.
2//
3// The straight-line ladder (nx_isa_equiv_sov) exercised god's ALU only. This rung exercises its
4// CONTROL FLOW -- the PC takes a data-dependent path through a real RV64 conditional branch.
5//
6// (1) the team COMPOSES a random non-trivial 2-way conditional target T:
7// result = if cmp(a,b) then ARM_then(a,b) else ARM_else(a,b)
8// cmp in {a<b, a>=b, a==b, a!=b} (the sim's BLT/BGE/BEQ/BNE); each ARM is a 2-op chain over
9// {ADD,SUB,MUL} ONLY -- no SLT -- so the BRANCH is the sole source of conditionality (the
10// program is NOT expressible straight-line over this op set),
11// (2) the team DERIVES a program G of the same shape by GA on T's TRAIN examples (sees only points),
12// (3) ENCODE both to real RV64 machine code (a forward BLT/BGE/BEQ/BNE + a forward JAL skipping the
13// other arm), run BOTH on rv64im_min_sim (god) over 12 HELD-OUT inputs, verify sim(T)==sim(G).
14//
15// B-type / J-type encoders are derived to round-trip the decoder's imm_b / imm_j
16// (rv64im_min_decoder.nx:145-175); branch semantics match the sim's step loop (BRANCH funct3,
17// rv64im_min_sim.nx:913-918). Controls: FAITHFUL (hand-built max / conditional programs with known
18// answers prove the branch encoding) + NEGATIVE (a perturbed G the differential must catch).
19// Only FORWARD branches this rung; backward branches (loops) are the next. license_tier: ORIGINAL
20import "nx_syscalls.nx"
21import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc)
22import "nishi_hdl_primitives.nx"
23import "rv64im_min_decoder.nx"
24import "rv64im_min_alu.nx"
25import "rv64im_min_regfile.nx"
26import "rv64im_min_csr.nx"
27import "rv64im_min_clint.nx"
28import "rv64im_min_uart.nx"
29import "rv64im_min_sim.nx"
30const EVO_MAGIC_2862933555777941757: i64 = 2862933555777941757
31const EVO_MAGIC_3037000493: i64 = 3037000493
32const EVO_MAGIC_1000000000000: i64 = 1000000000000
33const EVO_MAGIC_10000000000000: i64 = 10000000000000
34const EVO_MAGIC_1000000000: i64 = 1000000000
35const EVO_MAGIC_2654435761: i64 = 2654435761
36const EVO_MAGIC_12345: i64 = 12345
37
38const GLEN: i64 = 13 // [cmp] [then0 op,rs1,rs2] [then1 ...] [else0 ...] [else1 ...]
39const EVO_P: i64 = 384
40const EVO_G: i64 = 1600
41const EVO_T: i64 = 5
42const NTRAIN: i64 = 8
43const NHELD: i64 = 12
44
45const IR_MEM_BASE: i64 = 0x80000000
46const IR_MEM_SIZE: i64 = 8192
47const IR_TX_CAP: i64 = 256
48const RES_OFF: i64 = 0x700
49
50func cf_rand(s: *i64) -> i64 { s[0] = s[0] * EVO_MAGIC_2862933555777941757 + EVO_MAGIC_3037000493; return (s[0] >> 17) & 0x3fffffff }
51func cf_abs(x: i64) -> i64 { if x < 0 { return 0 - x } return x }
52
53// train + held-out points (disjoint); both sets include a<b AND a>=b cases so both arms get exercised.
54func cf_ta(i: i64) -> i64 { if i==0 {return 6} if i==1 {return 4} if i==2 {return 9} if i==3 {return 5} if i==4 {return 7} if i==5 {return 2} if i==6 {return 8} return 3 }
55func cf_tb(i: i64) -> i64 { if i==0 {return 4} if i==1 {return 7} if i==2 {return 2} if i==3 {return 9} if i==4 {return 7} if i==5 {return 5} if i==6 {return 8} return 6 }
56func cf_ha(i: i64) -> i64 { if i==0 {return 3} if i==1 {return 8} if i==2 {return 11} if i==3 {return 2} if i==4 {return 10} if i==5 {return 13} if i==6 {return 7} if i==7 {return 4} if i==8 {return 12} if i==9 {return 6} if i==10 {return 9} return 5 }
57func cf_hb(i: i64) -> i64 { if i==0 {return 5} if i==1 {return 2} if i==2 {return 7} if i==3 {return 11} if i==4 {return 4} if i==5 {return 13} if i==6 {return 9} if i==7 {return 4} if i==8 {return 2} if i==9 {return 13} if i==10 {return 9} return 10 }
58
59// ---- interpreter (the reference; GUIDES search + is a faithfulness control, NOT the verdict) ----
60func cf_aluop(op: i64, x: i64, y: i64) -> i64 {
61 if op == 0 { return x + y } // ADD
62 if op == 1 { return x - y } // SUB
63 return x * y // MUL
64}
65func cf_reg(idx: i64, a: i64, b: i64, acc: i64) -> i64 {
66 if idx == 0 { return 0 }
67 if idx == 1 { return a }
68 if idx == 2 { return b }
69 return acc
70}
71// branch condition: 1 if the branch is TAKEN (-> THEN arm).
72func cf_cmp(c: i64, a: i64, b: i64) -> i64 {
73 if c == 0 { if a < b { return 1 } return 0 } // BLT
74 if c == 1 { if a >= b { return 1 } return 0 } // BGE
75 if c == 2 { if a == b { return 1 } return 0 } // BEQ
76 if a != b { return 1 } return 0 // BNE
77}
78// evaluate a 2-op arm starting at gen[base] (acc = 0 at entry, like x3 zeroed before the branch).
79func cf_arm(gen: *i64, base: i64, a: i64, b: i64) -> i64 {
80 var acc: i64 = 0
81 var j: i64 = 0
82 while j < 2 {
83 let op: i64 = gen[base + j*3]
84 let rs1: i64 = gen[base + j*3 + 1]
85 let rs2: i64 = gen[base + j*3 + 2]
86 acc = cf_aluop(op, cf_reg(rs1, a, b, acc), cf_reg(rs2, a, b, acc))
87 j = j + 1
88 }
89 return acc
90}
91func cf_eval(gen: *i64, a: i64, b: i64) -> i64 {
92 if cf_cmp(gen[0], a, b) == 1 { return cf_arm(gen, 1, a, b) } // THEN at slots 1..6
93 return cf_arm(gen, 7, a, b) // ELSE at slots 7..12
94}
95
96// ---- RV64 encoders (rv_branch/rv_jal round-trip the decoder's imm_b/imm_j) ----
97func rv_addi(rd: i64, rs1: i64, imm: i64) -> i64 { return ((imm & 0xFFF) << 20) | (rs1 << 15) | (rd << 7) | 0x13 }
98func rv_lui(rd: i64, imm20: i64) -> i64 { return ((imm20 & 0xFFFFF) << 12) | (rd << 7) | 0x37 }
99func rv_rtype(f7: i64, rs2: i64, rs1: i64, f3: i64, rd: i64) -> i64 { return (f7 << 25) | (rs2 << 20) | (rs1 << 15) | (f3 << 12) | (rd << 7) | 0x33 }
100func rv_store_imm(rs2: i64, rs1: i64, f3: i64, imm: i64) -> i64 { return (((imm >> 5) & 0x7f) << 25) | (rs2 << 20) | (rs1 << 15) | (f3 << 12) | ((imm & 0x1f) << 7) | 0x23 }
101func rv_branch(f3: i64, rs1: i64, rs2: i64, imm: i64) -> i64 {
102 let b12: i64 = (imm >> 12) & 1
103 let b11: i64 = (imm >> 11) & 1
104 let b10_5: i64 = (imm >> 5) & 0x3f
105 let b4_1: i64 = (imm >> 1) & 0xf
106 return (b12 << 31) | (b10_5 << 25) | (rs2 << 20) | (rs1 << 15) | (f3 << 12) | (b4_1 << 8) | (b11 << 7) | 0x63
107}
108func rv_jal(rd: i64, imm: i64) -> i64 {
109 let b20: i64 = (imm >> 20) & 1
110 let b19_12: i64 = (imm >> 12) & 0xff
111 let b11: i64 = (imm >> 11) & 1
112 let b10_1: i64 = (imm >> 1) & 0x3ff
113 return (b20 << 31) | (b10_1 << 21) | (b11 << 20) | (b19_12 << 12) | (rd << 7) | 0x6f
114}
115// arm-op index -> R-type funct3/funct7 (ADD/SUB/MUL only).
116func cf_f3(op: i64) -> i64 { return 0 }
117func cf_f7(op: i64) -> i64 { if op == 1 { return 0x20 } if op == 2 { return 0x01 } return 0x00 }
118// branch cmp index -> BRANCH funct3 (BLT=4 BGE=5 BEQ=0 BNE=1).
119func cf_bf3(c: i64) -> i64 { if c == 0 { return 4 } if c == 1 { return 5 } if c == 2 { return 0 } return 1 }
120func rv_w32(buf: *u8, off: i64, w: i64) -> i64 {
121 buf[off] = (w & 0xff) as u8
122 buf[off+1] = ((w >> 8) & 0xff) as u8
123 buf[off+2] = ((w >> 16) & 0xff) as u8
124 buf[off+3] = ((w >> 24) & 0xff) as u8
125 return off + 4
126}
127// emit two R-type arm instructions (each x3 = op(x[rs1], x[rs2])) at byte offset o; return new o.
128func cf_emit_arm(mem: *u8, o: i64, gen: *i64, base: i64) -> i64 {
129 var j: i64 = 0
130 while j < 2 {
131 let op: i64 = gen[base + j*3]
132 let rs1: i64 = gen[base + j*3 + 1]
133 let rs2: i64 = gen[base + j*3 + 2]
134 o = rv_w32(mem, o, rv_rtype(cf_f7(op), rs2, rs1, cf_f3(op), 3))
135 j = j + 1
136 }
137 return o
138}
139// emit the full conditional program (layout fixed; forward branch + forward jal).
140// 0 addi x1,a 4 addi x2,b 8 lui x5,RAMbase 12 addi x3,0
141// 16 B<cmp> x1,x2,+16 -> taken to 32 (THEN); fall to 20 (ELSE)
142// 20 else0 24 else1 28 jal +12 -> 40
143// 32 then0 36 then1
144// 40 sd x3,RES_OFF(x5) 44 lui x7,0x100 48 lui x6,0x5 52 addi x6,0x555 56 sw x6,0(x7) 60 jal 0
145func cf_emit(mem: *u8, gen: *i64, a: i64, b: i64) -> i64 {
146 rv_w32(mem, 0, rv_addi(1, 0, a))
147 rv_w32(mem, 4, rv_addi(2, 0, b))
148 rv_w32(mem, 8, rv_lui(5, 0x80000))
149 rv_w32(mem, 12, rv_addi(3, 0, 0))
150 rv_w32(mem, 16, rv_branch(cf_bf3(gen[0]), 1, 2, 16)) // taken -> 32
151 cf_emit_arm(mem, 20, gen, 7) // ELSE (slots 7..12) at 20,24
152 rv_w32(mem, 28, rv_jal(0, 12)) // -> 40 (skip THEN)
153 cf_emit_arm(mem, 32, gen, 1) // THEN (slots 1..6) at 32,36
154 rv_w32(mem, 40, rv_store_imm(3, 5, 3, RES_OFF)) // sd x3, RES_OFF(x5)
155 rv_w32(mem, 44, rv_lui(7, 0x100))
156 rv_w32(mem, 48, rv_lui(6, 0x5))
157 rv_w32(mem, 52, rv_addi(6, 6, 0x555))
158 rv_w32(mem, 56, rv_store_imm(6, 7, 2, 0)) // sw x6, 0(x7) -> finisher halt
159 rv_w32(mem, 60, 0x6F) // jal x0,0 spin
160 return 0
161}
162func cf_read_i64(mem: *u8, off: i64) -> i64 {
163 var v: i64 = 0; var i: i64 = 0
164 while i < 8 { v = v | ((mem[off + i] as i64) << (i * 8)); i = i + 1 }
165 return v
166}
167// RUN a conditional genome on the GENESIS SIM (fresh devices each call) -> its 64-bit result.
168func cf_run_prog(gen: *i64, a: i64, b: i64) -> i64 {
169 let rf_storage: *i64 = (sys_mmap(8 * NX_RV64IM_RF_N_REGS)) as *i64
170 let csr_storage: *i64 = (sys_mmap(8 * NX_CSR_SLOT_N)) as *i64
171 let clint_storage: *i64 = (sys_mmap(8 * NX_CLINT_SLOT_N)) as *i64
172 let uart_storage: *i64 = (sys_mmap(8 * NX_UART_SLOT_N)) as *i64
173 let mem: *u8 = sys_mmap(IR_MEM_SIZE)
174 let tx_buf: *u8 = sys_mmap(IR_TX_CAP)
175 let rf: *NxRv64imRegfile = (sys_mmap(64)) as *NxRv64imRegfile
176 let csr: *NxRv64imCsrFile = (sys_mmap(64)) as *NxRv64imCsrFile
177 let clint: *NxClint = (sys_mmap(64)) as *NxClint
178 let uart: *NxUart = (sys_mmap(64)) as *NxUart
179 let sim: *NxRv64imSim = (sys_mmap(128)) as *NxRv64imSim
180 nx_rv64im_rf_init(rf, rf_storage)
181 nx_rv64im_csr_init(csr, csr_storage, 0)
182 nx_clint_init(clint, clint_storage)
183 nx_uart_init(uart, uart_storage, tx_buf, IR_TX_CAP)
184 nx_rv64im_sim_init(sim, rf, csr, clint, uart, IR_MEM_BASE, mem, IR_MEM_SIZE, 0)
185 cf_emit(mem, gen, a, b)
186 nx_rv64im_sim_run(sim, 400)
187 return cf_read_i64(mem, RES_OFF)
188}
189
190// ---- compose / derive ------------------------------------------------------------------
191// per-slot valid random value (slot0 = cmp 0..3; arm-op slots 1,4,7,10 = 0..2; rest = rs 0..3).
192func cf_randslot(idx: i64, state: *i64) -> i64 {
193 if idx == 0 { return cf_rand(state) % 4 }
194 if idx == 1 { return cf_rand(state) % 3 }
195 if idx == 4 { return cf_rand(state) % 3 }
196 if idx == 7 { return cf_rand(state) % 3 }
197 if idx == 10 { return cf_rand(state) % 3 }
198 return cf_rand(state) % 4
199}
200func cf_fill(gen: *i64, base: i64, state: *i64) -> i64 {
201 var i: i64 = 0
202 while i < GLEN { gen[base + i] = cf_randslot(i, state); i = i + 1 }
203 return 0
204}
205// COMPOSE: a target where (a) both branches are exercised across train, (b) the two arms differ
206// on >=1 train point (so the branch CHANGES the answer), (c) output is non-constant. Else reroll.
207func cf_compose(state: *i64, T: *i64) -> i64 {
208 var tries: i64 = 0
209 while tries < 600 {
210 cf_fill(T, 0, state)
211 var took: i64 = 0; var fell: i64 = 0; var armdiff: i64 = 0; var nonconst: i64 = 0
212 var i: i64 = 0
213 let y0: i64 = cf_eval(T, cf_ta(0), cf_tb(0))
214 while i < NTRAIN {
215 let a: i64 = cf_ta(i); let b: i64 = cf_tb(i)
216 if cf_cmp(T[0], a, b) == 1 { took = 1 } else { fell = 1 }
217 if cf_arm(T, 1, a, b) != cf_arm(T, 7, a, b) { armdiff = 1 }
218 if cf_eval(T, a, b) != y0 { nonconst = 1 }
219 i = i + 1
220 }
221 if took == 1 { if fell == 1 { if armdiff == 1 { if nonconst == 1 { return 1 } } } }
222 tries = tries + 1
223 }
224 return 0
225}
226// eval a candidate stored at pop[base..base+GLEN].
227func cf_eval_at(pop: *i64, base: i64, a: i64, b: i64) -> i64 {
228 if cf_cmp(pop[base], a, b) == 1 { return cf_arm(pop, base + 1, a, b) }
229 return cf_arm(pop, base + 7, a, b)
230}
231func cf_fit(pop: *i64, base: i64, T: *i64) -> i64 {
232 var err: i64 = 0; var i: i64 = 0
233 while i < NTRAIN {
234 let a: i64 = cf_ta(i); let b: i64 = cf_tb(i)
235 let r: i64 = cf_eval_at(pop, base, a, b)
236 let y: i64 = cf_eval(T, a, b)
237 var d: i64 = cf_abs(r - y)
238 if r > EVO_MAGIC_1000000000000 { d = EVO_MAGIC_10000000000000 }
239 if r < (0 - EVO_MAGIC_1000000000000) { d = EVO_MAGIC_10000000000000 }
240 err = err + d; i = i + 1
241 }
242 return err
243}
244func cf_tourney(fit: *i64, state: *i64) -> i64 {
245 var bi: i64 = cf_rand(state) % EVO_P; var bd: i64 = fit[bi]; var k: i64 = 1
246 while k < EVO_T { let i: i64 = cf_rand(state) % EVO_P; if fit[i] < bd { bd = fit[i]; bi = i } k = k + 1 }
247 return bi
248}
249func cf_mut(gen: *i64, base: i64, state: *i64) -> i64 {
250 let idx: i64 = cf_rand(state) % GLEN
251 gen[base + idx] = cf_randslot(idx, state)
252 return 0
253}
254func cf_derive(T: *i64, state: *i64, G: *i64) -> i64 {
255 let pop: *i64 = sys_mmap(EVO_P * GLEN * 8) as *i64
256 let nxt: *i64 = sys_mmap(EVO_P * GLEN * 8) as *i64
257 let fit: *i64 = sys_mmap(EVO_P * 8) as *i64
258 var p: i64 = 0
259 while p < EVO_P { cf_fill(pop, p*GLEN, state); p = p + 1 }
260 var best_err: i64 = EVO_MAGIC_1000000000
261 var g: i64 = 0
262 while g < EVO_G {
263 var gbest: i64 = EVO_MAGIC_1000000000; var gbp: i64 = 0
264 p = 0
265 while p < EVO_P { let e: i64 = cf_fit(pop, p*GLEN, T); fit[p] = e; if e < gbest { gbest = e; gbp = p } p = p + 1 }
266 if gbest < best_err { best_err = gbest; var j: i64 = 0; while j < GLEN { G[j] = pop[gbp*GLEN + j]; j = j + 1 } }
267 if best_err == 0 { g = EVO_G } else {
268 var j2: i64 = 0; while j2 < GLEN { nxt[j2] = G[j2]; j2 = j2 + 1 }
269 p = 1
270 while p < EVO_P {
271 let pa: i64 = cf_tourney(fit, state) * GLEN
272 let pb: i64 = cf_tourney(fit, state) * GLEN
273 let cut: i64 = (cf_rand(state) % (GLEN - 1)) + 1
274 var jj: i64 = 0
275 while jj < GLEN { if jj < cut { nxt[p*GLEN + jj] = pop[pa + jj] } else { nxt[p*GLEN + jj] = pop[pb + jj] } jj = jj + 1 }
276 cf_mut(nxt, p*GLEN, state)
277 p = p + 1
278 }
279 var c: i64 = 0; while c < EVO_P * GLEN { pop[c] = nxt[c]; c = c + 1 }
280 g = g + 1
281 }
282 }
283 return best_err
284}
285
286// ---- print -----------------------------------------------------------------------------
287func cf_p(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
288// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer
289// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the
290// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls).
291// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign.
292func cf_pn(v: i64) -> i64 { nxi_out(v); return 0 }
293
294// CONTROL 1 (faithful): hand-built conditional programs with KNOWN answers, run on god, must match.
295// P1 = max(a,b): if a>=b (BGE,cmp1) then a else b. THEN = a+0 then noop; ELSE = b+0 then noop.
296// P2 = if a==b (BEQ,cmp2) then a*a else a+b. THEN = a*a noop; ELSE = a+b noop.
297func cf_set(g: *i64, c: i64, t0: i64,t1: i64,t2: i64, t3: i64,t4: i64,t5: i64, e0: i64,e1: i64,e2: i64, e3: i64,e4: i64,e5: i64) -> i64 {
298 g[0]=c; g[1]=t0; g[2]=t1; g[3]=t2; g[4]=t3; g[5]=t4; g[6]=t5; g[7]=e0; g[8]=e1; g[9]=e2; g[10]=e3; g[11]=e4; g[12]=e5
299 return 0
300}
301func cf_faithful() -> i64 {
302 let g: *i64 = sys_mmap(GLEN * 8) as *i64
303 var ok: i64 = 0; var tot: i64 = 0
304 // P1 max: cmp1(BGE); THEN= ADD a,x0 (a) then ADD acc,x0 (noop); ELSE= ADD b,x0 (b) then noop.
305 cf_set(g, 1, 0,1,0, 0,3,0, 0,2,0, 0,3,0)
306 var i: i64 = 0
307 while i < NHELD {
308 let a: i64 = cf_ha(i); let b: i64 = cf_hb(i)
309 var exp: i64 = b; if a >= b { exp = a }
310 if cf_run_prog(g, a, b) == exp { ok = ok + 1 }
311 tot = tot + 1; i = i + 1
312 }
313 // P2: cmp2(BEQ); THEN= MUL a,a then noop; ELSE= ADD a,b then noop.
314 cf_set(g, 2, 2,1,1, 0,3,0, 0,1,2, 0,3,0)
315 i = 0
316 while i < NHELD {
317 let a: i64 = cf_ha(i); let b: i64 = cf_hb(i)
318 var exp: i64 = a + b; if a == b { exp = a * a }
319 if cf_run_prog(g, a, b) == exp { ok = ok + 1 }
320 tot = tot + 1; i = i + 1
321 }
322 cf_p(" control-faithful: "); cf_pn(ok); cf_p("/"); cf_pn(tot); cf_p(" hand-built conditional programs run on god == known answer (max, eq-branch)\n")
323 if ok == tot { return 1 }
324 return 0
325}
326
327// the HARDWARE differential: both genomes run on god over held-out; count where sim(A)==sim(B).
328func cf_hw_agree(A: *i64, B: *i64) -> i64 {
329 var agree: i64 = 0; var i: i64 = 0
330 while i < NHELD { if cf_run_prog(A, cf_ha(i), cf_hb(i)) == cf_run_prog(B, cf_ha(i), cf_hb(i)) { agree = agree + 1 } i = i + 1 }
331 return agree
332}
333func cf_hw_faithful(T: *i64) -> i64 {
334 var ok: i64 = 0; var i: i64 = 0
335 while i < NHELD { if cf_run_prog(T, cf_ha(i), cf_hb(i)) == cf_eval(T, cf_ha(i), cf_hb(i)) { ok = ok + 1 } i = i + 1 }
336 return ok
337}
338// count held-out points where the branch is TAKEN (proves the conditional actually exercises control flow).
339func cf_branch_taken(T: *i64) -> i64 {
340 var n: i64 = 0; var i: i64 = 0
341 while i < NHELD { if cf_cmp(T[0], cf_ha(i), cf_hb(i)) == 1 { n = n + 1 } i = i + 1 }
342 return n
343}
344
345func cf_seed(seed: i64) -> i64 {
346 let state: *i64 = sys_mmap(8) as *i64; state[0] = seed * EVO_MAGIC_2654435761 + EVO_MAGIC_12345
347 let T: *i64 = sys_mmap(GLEN * 8) as *i64
348 let G: *i64 = sys_mmap(GLEN * 8) as *i64
349 if cf_compose(state, T) != 1 { cf_p("seed="); cf_pn(seed); cf_p(" COMPOSE-FAIL\n"); return 0 }
350 let de: i64 = cf_derive(T, state, G)
351 var imatch: i64 = 0; var i: i64 = 0
352 while i < NHELD { if cf_eval_at(G, 0, cf_ha(i), cf_hb(i)) == cf_eval(T, cf_ha(i), cf_hb(i)) { imatch = imatch + 1 } i = i + 1 }
353 let bt: i64 = cf_branch_taken(T)
354 let hf: i64 = cf_hw_faithful(T)
355 let hw: i64 = cf_hw_agree(T, G)
356 cf_p("seed="); cf_pn(seed); cf_p(" derive_err="); cf_pn(de)
357 cf_p(" branch_taken="); cf_pn(bt); cf_p("/"); cf_pn(NHELD)
358 cf_p(" interp_held="); cf_pn(imatch); cf_p("/"); cf_pn(NHELD)
359 cf_p(" sim(T)==interp="); cf_pn(hf); cf_p("/"); cf_pn(NHELD)
360 cf_p(" sim(T)==sim(G)="); cf_pn(hw); cf_p("/"); cf_pn(NHELD)
361 // require both branch paths actually used on held-out (1..NHELD-1 taken), not a degenerate always-one-arm.
362 if de == 0 { if imatch == NHELD { if hf == NHELD { if hw == NHELD { if bt > 0 { if bt < NHELD {
363 cf_p(" GREEN\n"); return 1
364 }}}}}}
365 cf_p(" RED\n"); return 0
366}
367
368// CONTROL 2 (negative): perturb G's cmp (flip to another comparison) so the INTERPRETER says it
369// differs from T on some held-out point; confirm the HARDWARE differential CATCHES it.
370func cf_negctl(seed: i64) -> i64 {
371 let state: *i64 = sys_mmap(8) as *i64; state[0] = seed * EVO_MAGIC_2654435761 + 777
372 let T: *i64 = sys_mmap(GLEN * 8) as *i64
373 let G: *i64 = sys_mmap(GLEN * 8) as *i64
374 let Gp: *i64 = sys_mmap(GLEN * 8) as *i64
375 if cf_compose(state, T) != 1 { cf_p(" control-negative: COMPOSE-FAIL\n"); return 0 }
376 cf_derive(T, state, G)
377 var found: i64 = 0; var cand: i64 = 0
378 while cand < 4 {
379 var c: i64 = 0; while c < GLEN { Gp[c] = G[c]; c = c + 1 }
380 Gp[0] = cand
381 var differs: i64 = 0; var i: i64 = 0
382 while i < NHELD { if cf_eval_at(Gp, 0, cf_ha(i), cf_hb(i)) != cf_eval(T, cf_ha(i), cf_hb(i)) { differs = 1 } i = i + 1 }
383 if differs == 1 { found = 1; cand = 4 } else { cand = cand + 1 }
384 }
385 if found == 0 { cf_p(" control-negative: no distinguishing cmp (inconclusive)\n"); return 0 }
386 let agree: i64 = cf_hw_agree(T, Gp)
387 cf_p(" control-negative: sim(T)==sim(G_wrongcmp)="); cf_pn(agree); cf_p("/"); cf_pn(NHELD)
388 if agree < NHELD { cf_p(" -> differential CAUGHT the wrong branch (good)\n"); return 1 }
389 cf_p(" -> differential MISSED it (test broken)\n"); return 0
390}
391
392func main() -> i64 {
393 cf_p("ISACFSOV: control-flow rung -- derive BRANCHING programs (if cmp then ARM1 else ARM2), run both on god\n")
394 let cfaith: i64 = cf_faithful()
395 var ok: i64 = 0
396 if cf_seed(101) == 1 { ok = ok + 1 }
397 if cf_seed(202) == 1 { ok = ok + 1 }
398 if cf_seed(303) == 1 { ok = ok + 1 }
399 if cf_seed(404) == 1 { ok = ok + 1 }
400 let nc: i64 = cf_negctl(101)
401 cf_p("ISACFSOV: "); cf_pn(ok); cf_p("/4 seeds: team-composed CONDITIONAL == team-derived program, PROVEN on rv64im_min_sim (both arms exercised)\n")
402 if cfaith == 1 { if ok == 4 { if nc == 1 {
403 cf_p("ISACFSOV GREEN: faithful-encode control PASS + negative control CAUGHT a wrong branch + 4/4 hardware-verified equivalences -- god's BRANCH unit exercised\n")
404 sys_exit(0); return 0
405 }}}
406 cf_p("ISACFSOV RED\n")
407 sys_exit(1)
408 return 1
409}