code wiki / (root) / nx_isa_conform_gate.nx

nx_isa_conform_gate.nx source

↩ module page · 1094 lines · 44918 B

1// nx_isa_conform_gate.nx -- THE ISA CONFORMANCE RULER. 2// 3// WHY IT EXISTS: the estate carries TEN sovereign ISA emulators in 4// NishiLang (rv64 arm64 armv7a cortexm loongarch64 mips64 ppc64le 5// riscv32 s390x sparc64). Measured 2026-09-03 by nx_catalog: all ten 6// are SOURCE-ONLY -- 0 built, 0 promoted, 0 registered, 0 invoked -- 7// ONE of them had a KAT (rv64), two more had x86 tests, and the only 8// differential proof in the estate was bench/gate_nx_emu_diff.sh, a 9// SHELL script that needs WSL plus riscv64 binutils plus qemu in order 10// to judge anything. Eight emulators had never been measured at all. 11// 12// THE LAW THIS ORGAN OBEYS: a capability asserted and never measured is 13// a claim, not coverage. "We emulate MIPS64" is decided by running 14// MIPS64 machine code, in process, against values taken FROM THE ISA 15// MANUAL -- never from the emulator's own source, which would calibrate 16// the ruler to the subject it is meant to judge. 17// 18// FIVE OUTCOMES, NEVER ONE: 19// PASS ran and matched the manual-derived expectation 20// WRONG-ANSWER ran to completion and computed the wrong value 21// UNSUPPORTED hit an instruction class the emulator does not implement 22// RAN-OFF-END the interpreter loop ended without an exit syscall 23// FAULT bad PC, out-of-range access, or the step budget 24// Collapsing these into "FAIL" is what makes a gap unactionable: an 25// unimplemented instruction class and a miscomputed result need 26// OPPOSITE fixes, and only the first is a missing feature. 27// 28// CORRECTION, 2026-09-03: the first cut of this header claimed "neither 29// emulator can return -2, so the codes do not collide with a real 30// answer". THAT WAS FALSE and I wrote it without reading far enough: 31// nx_emu_rv64.nx line 50 defines EMU_RV_RANOFF = -2 and line 123 uses 32// it as the INITIAL value of result, so -2 is exactly what comes back 33// when the loop ends with no exit syscall. Treating it as WRONG-ANSWER 34// would have reported a structural non-completion as an arithmetic 35// disagreement -- the same two-conditions-under-one-negative-word 36// defect this organ was written to refuse, committed inside the organ 37// that refuses it. Both exit paths mask with & 0xff (rv64 line 302, 38// mips64 SYSCALL), so a real answer is always 0..255 and the three 39// negative sentinels cannot collide with one. 40// 41// WHY A RATCHET AND NOT A BAR: several emulators are genuinely 42// incomplete today. When this organ was written mips64 implemented NO 43// conditional branch at all and could not run a loop; the ruler named 44// that gap, it was fixed the same day, and mips64 now measures 7/7. 45// Eight of the ten emulators are still entirely unmeasured. 46// A gate demanding completeness would be permanently RED, and a 47// permanently RED detector is one everyone learns to ignore. So the 48// VERDICT is "no arch regressed against its banked score" and the 49// absolute coverage is PUBLISHED AS A NUMBER beside it. The number is 50// the worklist; the verdict is the fence. 51// 52// DISPATCH, MEASURED NOT ASSUMED: emu_<arch>_run_mem(mem, size, entry, 53// sp0) is the ONLY surface all ten emulators share -- 54// run_mem 10 of 10 55// run 6 of 10 (rv64 arm64 s390x armv7a cortexm riscv32) 56// load_elf 6 of 10 (rv64 mips64 arm64 ppc64le loongarch64 sparc64) 57// The two sixes are DIFFERENT SETS, so anything built on _run or on 58// _load_elf silently tests a subset while looking general. This organ 59// uses _run_mem only. 60// 61// license_tier: ORIGINAL 62 63import "nx_gate_verdict.nx" 64import "nx_syscalls.nx" 65import "nx_emu_rv64.nx" 66import "nx_emu_mips64.nx" 67import "nx_emu_arm64.nx" 68import "nx_emu_riscv32.nx" 69import "nx_emu_ppc64le.nx" 70 71// ---- guest layout THIS ORGAN owns (never read from an emulator) ---- 72const IC_GUEST_BYTES: i64 = 1048576 // 1 MiB guest image 73const IC_ENTRY: i64 = 0 // KAT code is loaded at guest vaddr 0 74const IC_SP: i64 = 983040 // 960 KiB: above the code, inside the image 75const IC_MODE_FILE: i64 = 420 // 0644. Mirrors the consolidated MODE_0644; 76 // 420 and 0x1a4 are the SAME constant and a 77 // sweep that greps one misses the other. 78 79// ---- non-completion codes the interpreters return ---- 80// Read out of the emulators' own constant tables, not guessed: 81// EMU_RV_UNSUPPORTED/MIE_UNSUPPORTED = -1, EMU_RV_RANOFF = -2, 82// EMU_RV_FAULT/MIE_FAULT = -3. 83const IC_UNSUPPORTED: i64 = -1 84const IC_RANOFF: i64 = -2 85const IC_FAULT: i64 = -3 86 87// ---- outcome classes ---- 88const IC_O_PASS: i64 = 0 89const IC_O_WRONG: i64 = 1 90const IC_O_UNSUP: i64 = 2 91const IC_O_FAULT: i64 = 3 92const IC_O_RANOFF: i64 = 4 93 94// The non-PASS worklist buffer. A count without a worklist is not 95// actionable, and a worklist printed only in the BODY is lost the 96// moment a caller tails the output -- which is exactly how the first 97// run of this gate told me "rv64=5/6" and refused to say which one. 98// So the list is accumulated here and printed at the END, where every 99// positional reader looks. 100const IC_NB_BYTES: i64 = 2048 101const IC_NB_OFF: i64 = 0 102const IC_NB_PTR: i64 = 1 103// The longest single worklist row: two names, the outcome word, two 104// numbers and the punctuation. The write guard is DERIVED from this and 105// the buffer size rather than being a hand-picked cutoff -- a 106// hand-counted length beside a literal is a second copy of that 107// literal's shape, and the two drift silently. 108const IC_NB_ROW_MAX: i64 = 256 109const IC_BOX_BYTES: i64 = 32 110 111const IC_NL: i64 = 10 112const IC_SPACE: i64 = 32 113const IC_DIG_LO: i64 = 48 114const IC_DIG_HI: i64 = 57 115const IC_UNSEEDED: i64 = -1 116 117// A bare newline inside a string literal is ambiguous to this lexer, so 118// the byte is CONSTRUCTED. Same reason the estate's gallery emitter had 119// 780 unseparated ids once. 120func ic_nl() -> i64 { 121 let b: *u8 = sys_mmap(8) 122 b[0] = IC_NL as u8 123 sys_write(1, b, 1) 124 return 0 125} 126 127// ===== word emitters ============================================ 128// Endianness is a PROPERTY OF THE TARGET ISA, so it is encoded per 129// architecture. rv64 is little-endian; MIPS64 n64 here is big-endian. 130// Getting this wrong is the silent-fixture defect: the emulator decodes 131// garbage, reports UNSUPPORTED, and the FIXTURE's bug reads as the 132// emulator being incomplete. 133 134func ic_put_le32(code: *u8, off: i64, w: i64) -> i64 { 135 code[off] = (w) & 0xff 136 code[off + 1] = (w >> 8) & 0xff 137 code[off + 2] = (w >> 16) & 0xff 138 code[off + 3] = (w >> 24) & 0xff 139 return off + 4 140} 141 142func ic_put_be32(code: *u8, off: i64, w: i64) -> i64 { 143 code[off] = (w >> 24) & 0xff 144 code[off + 1] = (w >> 16) & 0xff 145 code[off + 2] = (w >> 8) & 0xff 146 code[off + 3] = (w) & 0xff 147 return off + 4 148} 149 150func ic_guest() -> *u8 { return sys_mmap(IC_GUEST_BYTES) } 151 152// ===== outcome classification =================================== 153 154func ic_classify(got: i64, want: i64) -> i64 { 155 if got == IC_UNSUPPORTED { return IC_O_UNSUP } 156 if got == IC_RANOFF { return IC_O_RANOFF } 157 if got == IC_FAULT { return IC_O_FAULT } 158 if got == want { return IC_O_PASS } 159 return IC_O_WRONG 160} 161 162func ic_outcome_name(o: i64) -> *u8 { 163 if o == IC_O_PASS { return "PASS" as *u8 } 164 if o == IC_O_WRONG { return "WRONG-ANSWER" as *u8 } 165 if o == IC_O_UNSUP { return "UNSUPPORTED-instruction-class" as *u8 } 166 if o == IC_O_RANOFF { return "RAN-OFF-END-no-exit-syscall" as *u8 } 167 return "FAULT-bad-pc-or-step-budget" as *u8 168} 169 170// Print the VALUES, not just the verdict: every vacuous tooth ever 171// caught in this estate was caught by a diagnostic dump and never by a 172// verdict vector. 173func ic_report(arch: *u8, kat: *u8, got: i64, want: i64, bx: *i64) -> i64 { 174 let o: i64 = ic_classify(got, want) 175 gv_puts(" KAT " as *u8) 176 gv_puts(arch) 177 gv_puts("." as *u8) 178 gv_puts(kat) 179 gv_puts(" got=" as *u8) 180 gv_num(got) 181 gv_puts(" want=" as *u8) 182 gv_num(want) 183 gv_puts(" " as *u8) 184 gv_puts(ic_outcome_name(o)) 185 ic_nl() 186 if o != IC_O_PASS { 187 let nb: *u8 = bx[IC_NB_PTR] as *u8 188 var p: i64 = bx[IC_NB_OFF] 189 if p < (IC_NB_BYTES - IC_NB_ROW_MAX) { 190 p = gv_cat(nb, p, arch) 191 p = gv_cat(nb, p, "." as *u8) 192 p = gv_cat(nb, p, kat) 193 p = gv_cat(nb, p, "=" as *u8) 194 p = gv_cat(nb, p, ic_outcome_name(o)) 195 p = gv_cat(nb, p, "(got=" as *u8) 196 p = gv_catn(nb, p, got) 197 p = gv_cat(nb, p, " want=" as *u8) 198 p = gv_catn(nb, p, want) 199 p = gv_cat(nb, p, ") " as *u8) 200 nb[p] = 0 as u8 201 bx[IC_NB_OFF] = p 202 } 203 } 204 return o 205} 206 207// ===== RV64 KATs ================================================ 208// RV64I encodings per the RISC-V unprivileged ISA manual. The first 209// three programs reuse words already oracle-verified against 210// riscv64-as and qemu-riscv64 by the incumbent harness, so a 211// disagreement on THOSE indicts a regression rather than a fresh 212// encoding mistake of mine. 213 214func ic_rv64_imm_arith() -> i64 { 215 let m: *u8 = ic_guest() 216 var o: i64 = 0 217 o = ic_put_le32(m, o, 0x02800513) // addi a0,zero,40 218 o = ic_put_le32(m, o, 0x00200293) // addi t0,zero,2 219 o = ic_put_le32(m, o, 0x00550533) // add a0,a0,t0 -> 42 220 o = ic_put_le32(m, o, 0x05d00893) // addi a7,zero,93 (exit) 221 o = ic_put_le32(m, o, 0x00000073) // ecall 222 return emu_rv64_run_mem(m, IC_GUEST_BYTES, IC_ENTRY, IC_SP) 223} 224 225func ic_rv64_sub() -> i64 { 226 let m: *u8 = ic_guest() 227 var o: i64 = 0 228 o = ic_put_le32(m, o, 0x03200513) // addi a0,zero,50 229 o = ic_put_le32(m, o, 0x00800293) // addi t0,zero,8 230 o = ic_put_le32(m, o, 0x40550533) // sub a0,a0,t0 -> 42 231 o = ic_put_le32(m, o, 0x05d00893) 232 o = ic_put_le32(m, o, 0x00000073) 233 return emu_rv64_run_mem(m, IC_GUEST_BYTES, IC_ENTRY, IC_SP) 234} 235 236func ic_rv64_branch_loop() -> i64 { 237 let m: *u8 = ic_guest() 238 var o: i64 = 0 239 o = ic_put_le32(m, o, 0x00000513) // li a0,0 240 o = ic_put_le32(m, o, 0x00000293) // li t0,0 241 o = ic_put_le32(m, o, 0x00a00313) // li t1,10 242 o = ic_put_le32(m, o, 0x00550533) // loop: add a0,a0,t0 243 o = ic_put_le32(m, o, 0x00128293) // addi t0,t0,1 244 o = ic_put_le32(m, o, 0xfe629ce3) // bne t0,t1,loop -> sum 0..9 = 45 245 o = ic_put_le32(m, o, 0x05d00893) 246 o = ic_put_le32(m, o, 0x00000073) 247 return emu_rv64_run_mem(m, IC_GUEST_BYTES, IC_ENTRY, IC_SP) 248} 249 250// SRLI vs SRAI on a NEGATIVE value is the discrimination that matters, 251// and the obvious version of this test CANNOT make it: -16 shifted 252// right by 2 has low byte 0xFC under BOTH a logical and an arithmetic 253// shift, so an emulator that implements only one of them passes. A 254// shift by 60 separates them: logical -> 15, arithmetic -> -1, which 255// the exit-status mask renders as 255. 256func ic_rv64_shift_logical() -> i64 { 257 let m: *u8 = ic_guest() 258 var o: i64 = 0 259 o = ic_put_le32(m, o, 0xff000513) // addi a0,zero,-16 260 o = ic_put_le32(m, o, 0x03c55513) // srli a0,a0,60 -> 15 261 o = ic_put_le32(m, o, 0x05d00893) 262 o = ic_put_le32(m, o, 0x00000073) 263 return emu_rv64_run_mem(m, IC_GUEST_BYTES, IC_ENTRY, IC_SP) 264} 265 266func ic_rv64_shift_arith() -> i64 { 267 let m: *u8 = ic_guest() 268 var o: i64 = 0 269 o = ic_put_le32(m, o, 0xff000513) // addi a0,zero,-16 270 o = ic_put_le32(m, o, 0x43c55513) // srai a0,a0,60 -> -1 -> 255 271 o = ic_put_le32(m, o, 0x05d00893) 272 o = ic_put_le32(m, o, 0x00000073) 273 return emu_rv64_run_mem(m, IC_GUEST_BYTES, IC_ENTRY, IC_SP) 274} 275 276func ic_rv64_load_store() -> i64 { 277 let m: *u8 = ic_guest() 278 var o: i64 = 0 279 o = ic_put_le32(m, o, 0x02a00513) // addi a0,zero,42 280 o = ic_put_le32(m, o, 0xfea13c23) // sd a0,-8(sp) 281 o = ic_put_le32(m, o, 0xff813503) // ld a0,-8(sp) -> 42 282 o = ic_put_le32(m, o, 0x05d00893) 283 o = ic_put_le32(m, o, 0x00000073) 284 return emu_rv64_run_mem(m, IC_GUEST_BYTES, IC_ENTRY, IC_SP) 285} 286 287// ===== MIPS64 encoders (from the MIPS64 instruction manual) ====== 288// R-type: opcode 0, funct in bits 5..0. I-type: opcode in bits 31..26. 289// MIPS64 n64 exit is v0(r2)=5058 with the status in a0(r4). 290 291func ic_mi_addiu(rt: i64, rs: i64, imm: i64) -> i64 { 292 return (0x09 << 26) | (rs << 21) | (rt << 16) | (imm & 0xffff) 293} 294 295func ic_mi_ori(rt: i64, rs: i64, imm: i64) -> i64 { 296 return (0x0d << 26) | (rs << 21) | (rt << 16) | (imm & 0xffff) 297} 298 299func ic_mi_rtype(rs: i64, rt: i64, rd: i64, sa: i64, fn: i64) -> i64 { 300 return (rs << 21) | (rt << 16) | (rd << 11) | (sa << 6) | fn 301} 302 303func ic_mi_beq(rs: i64, rt: i64, off: i64) -> i64 { 304 return (0x04 << 26) | (rs << 21) | (rt << 16) | (off & 0xffff) 305} 306 307// Every MIPS64 KAT ends with this two-word exit sequence. 308func ic_mi_exit(m: *u8, o0: i64) -> i64 { 309 var o: i64 = o0 310 o = ic_put_be32(m, o, ic_mi_addiu(2, 0, 5058)) // li v0,5058 (exit) 311 o = ic_put_be32(m, o, ic_mi_rtype(0, 0, 0, 0, 0x0c)) // syscall 312 return o 313} 314 315func ic_mips64_imm_arith() -> i64 { 316 let m: *u8 = ic_guest() 317 var o: i64 = 0 318 o = ic_put_be32(m, o, ic_mi_addiu(4, 0, 40)) // li a0,40 319 o = ic_put_be32(m, o, ic_mi_addiu(5, 0, 2)) // li a1,2 320 o = ic_put_be32(m, o, ic_mi_rtype(4, 5, 4, 0, 0x2d)) // daddu a0,a0,a1 -> 42 321 o = ic_mi_exit(m, o) 322 return emu_mips64_run_mem(m, IC_GUEST_BYTES, IC_ENTRY, IC_SP) 323} 324 325func ic_mips64_sub() -> i64 { 326 let m: *u8 = ic_guest() 327 var o: i64 = 0 328 o = ic_put_be32(m, o, ic_mi_addiu(4, 0, 50)) 329 o = ic_put_be32(m, o, ic_mi_addiu(5, 0, 8)) 330 o = ic_put_be32(m, o, ic_mi_rtype(4, 5, 4, 0, 0x2f)) // dsubu a0,a0,a1 -> 42 331 o = ic_mi_exit(m, o) 332 return emu_mips64_run_mem(m, IC_GUEST_BYTES, IC_ENTRY, IC_SP) 333} 334 335func ic_mips64_logic() -> i64 { 336 let m: *u8 = ic_guest() 337 var o: i64 = 0 338 o = ic_put_be32(m, o, ic_mi_addiu(4, 0, 240)) 339 o = ic_put_be32(m, o, ic_mi_ori(4, 4, 15)) // 240 or 15 = 255 340 o = ic_put_be32(m, o, ic_mi_addiu(5, 0, 60)) 341 o = ic_put_be32(m, o, ic_mi_rtype(4, 5, 4, 0, 0x24)) // and a0,a0,a1 -> 60 342 o = ic_mi_exit(m, o) 343 return emu_mips64_run_mem(m, IC_GUEST_BYTES, IC_ENTRY, IC_SP) 344} 345 346func ic_mips64_shift() -> i64 { 347 let m: *u8 = ic_guest() 348 var o: i64 = 0 349 o = ic_put_be32(m, o, ic_mi_addiu(4, 0, 1)) 350 o = ic_put_be32(m, o, ic_mi_rtype(0, 4, 4, 20, 0x38)) // dsll a0,a0,20 351 o = ic_put_be32(m, o, ic_mi_rtype(0, 4, 4, 13, 0x3a)) // dsrl a0,a0,13 -> 128 352 o = ic_mi_exit(m, o) 353 return emu_mips64_run_mem(m, IC_GUEST_BYTES, IC_ENTRY, IC_SP) 354} 355 356// DSRL must be LOGICAL where DSRA is arithmetic. The obvious test cannot tell 357// them apart: MIPS encodes the shift amount in a FIVE-bit field, so the largest 358// single DSRL is 31, and -16 shifted right by 31 has low byte 0xFF under BOTH 359// shifts -- the same trap as the rv64 pair, one field-width narrower. Shifting 360// TWICE by 31 separates them: logical gives 0x1FFFFFFFF then 3, arithmetic 361// gives -1 then -1 (255 after the exit mask). 362func ic_mips64_shift_logical() -> i64 { 363 let m: *u8 = ic_guest() 364 var o: i64 = 0 365 o = ic_put_be32(m, o, ic_mi_addiu(4, 0, 0 - 16)) // li a0,-16 366 o = ic_put_be32(m, o, ic_mi_rtype(0, 4, 4, 31, 0x3a)) // dsrl a0,a0,31 367 o = ic_put_be32(m, o, ic_mi_rtype(0, 4, 4, 31, 0x3a)) // dsrl a0,a0,31 -> 3 368 o = ic_mi_exit(m, o) 369 return emu_mips64_run_mem(m, IC_GUEST_BYTES, IC_ENTRY, IC_SP) 370} 371 372// ADDIU is a 32-BIT add whose result is sign-extended to 64 bits; DADDIU is the 373// 64-bit form. Truncating to 32 bits NEVER changes the low byte, so no direct 374// comparison of exit statuses can separate them -- the difference lives entirely 375// in bits 32..63 and has to be shifted down to be seen. Build 0x80000000 (bit 31 376// set, bits 32+ clear), pass it through ADDIU, then shift right twice by 31: a 377// correct sign-extending ADDIU yields 3, an ADDIU that is secretly DADDIU yields 0. 378func ic_mips64_addiu32() -> i64 { 379 let m: *u8 = ic_guest() 380 var o: i64 = 0 381 o = ic_put_be32(m, o, ic_mi_addiu(4, 0, 1)) // li a0,1 382 o = ic_put_be32(m, o, ic_mi_rtype(0, 4, 4, 31, 0x38)) // dsll a0,a0,31 -> 0x80000000 383 o = ic_put_be32(m, o, ic_mi_addiu(5, 4, 0)) // addiu a1,a0,0 -> sign-extends 384 o = ic_put_be32(m, o, ic_mi_rtype(0, 5, 5, 31, 0x3a)) // dsrl a1,a1,31 385 o = ic_put_be32(m, o, ic_mi_rtype(0, 5, 5, 31, 0x3a)) // dsrl a1,a1,31 -> 3 386 o = ic_put_be32(m, o, ic_mi_rtype(5, 0, 4, 0, 0x25)) // or a0,a1,zero 387 o = ic_mi_exit(m, o) 388 return emu_mips64_run_mem(m, IC_GUEST_BYTES, IC_ENTRY, IC_SP) 389} 390 391// Conditional branch WITH its delay slot honoured: the MIPS target is 392// (pc + 4) + (offset << 2) and the delay-slot instruction runs 393// REGARDLESS, so offset=2 lands past both the delay slot and the poison 394// instruction. This KAT is encoded CORRECTLY on purpose even though it 395// is expected to report UNSUPPORTED today: a fixture written to match 396// the current emulator would have to be rewritten the day someone 397// implements BEQ, and would silently bless whatever that 398// implementation happened to do. 399func ic_mips64_branch() -> i64 { 400 let m: *u8 = ic_guest() 401 var o: i64 = 0 402 o = ic_put_be32(m, o, ic_mi_addiu(4, 0, 42)) // li a0,42 403 o = ic_put_be32(m, o, ic_mi_beq(0, 0, 2)) // beq zero,zero,+2 404 o = ic_put_be32(m, o, ic_mi_rtype(0, 0, 0, 0, 0x00)) // nop (delay slot, always runs) 405 o = ic_put_be32(m, o, ic_mi_addiu(4, 0, 99)) // li a0,99 (must be skipped) 406 o = ic_mi_exit(m, o) 407 return emu_mips64_run_mem(m, IC_GUEST_BYTES, IC_ENTRY, IC_SP) 408} 409 410// ===== AArch64 KATs ============================================= 411// A64 encodings per the Arm Architecture Reference Manual. Register 412// field 31 is XZR, and the Linux aarch64 exit syscall is 93 in x8 with 413// the status in x0 -- the same number as RISC-V, which is a coincidence 414// of the Linux generic ABI and not of the two ISAs. 415// MOVZ Xd,#imm16 0xD2800000 | hw<<21 | imm16<<5 | Rd 416// MOVK Xd,#imm16,LSL#h 0xF2800000 | hw<<21 | imm16<<5 | Rd (keeps other lanes) 417// ADD Xd,Xn,Xm 0x8B000000 | Rm<<16 | Rn<<5 | Rd 418// MADD Xd,Xn,Xm,Xa 0x9B000000 | Rm<<16 | Ra<<10 | Rn<<5 | Rd 419// CBZ Xt,+imm19 0xB4000000 | imm19<<5 | Rt (offset in INSTRUCTIONS) 420// SVC #0 0xD4000001 421 422func ic_a64_movz(rd: i64, imm: i64, hw: i64) -> i64 { 423 return 0xD2800000 | (hw << 21) | ((imm & 0xffff) << 5) | rd 424} 425func ic_a64_movk(rd: i64, imm: i64, hw: i64) -> i64 { 426 return 0xF2800000 | (hw << 21) | ((imm & 0xffff) << 5) | rd 427} 428func ic_a64_add(rd: i64, rn: i64, rm: i64) -> i64 { 429 return 0x8B000000 | (rm << 16) | (rn << 5) | rd 430} 431func ic_a64_madd(rd: i64, rn: i64, rm: i64, ra: i64) -> i64 { 432 return 0x9B000000 | (rm << 16) | (ra << 10) | (rn << 5) | rd 433} 434func ic_a64_cbz(rt: i64, off: i64) -> i64 { 435 return 0xB4000000 | ((off & 0x7ffff) << 5) | rt 436} 437 438// Every AArch64 KAT ends with this two-word exit sequence. 439func ic_a64_exit(m: *u8, o0: i64) -> i64 { 440 var o: i64 = o0 441 o = ic_put_le32(m, o, ic_a64_movz(8, 93, 0)) // mov x8,#93 (exit) 442 o = ic_put_le32(m, o, 0xD4000001) // svc #0 443 return o 444} 445 446func ic_arm64_imm_arith() -> i64 { 447 let m: *u8 = ic_guest() 448 var o: i64 = 0 449 o = ic_put_le32(m, o, ic_a64_movz(0, 40, 0)) // mov x0,#40 450 o = ic_put_le32(m, o, ic_a64_movz(1, 2, 0)) // mov x1,#2 451 o = ic_put_le32(m, o, ic_a64_add(0, 0, 1)) // add x0,x0,x1 -> 42 452 o = ic_a64_exit(m, o) 453 return emu_arm64_run_mem(m, IC_GUEST_BYTES, IC_ENTRY, IC_SP) 454} 455 456// MOVK must KEEP the lanes it is not writing. A MOVK implemented as a 457// second MOVZ would wipe the low half and return 0 -- which the exit 458// mask would render as 0, cleanly distinguishable from 42. 459func ic_arm64_movk_keeps_lanes() -> i64 { 460 let m: *u8 = ic_guest() 461 var o: i64 = 0 462 o = ic_put_le32(m, o, ic_a64_movz(0, 42, 0)) // mov x0,#42 463 o = ic_put_le32(m, o, ic_a64_movk(0, 1, 1)) // movk x0,#1,lsl#16 -> 0x1002A 464 o = ic_a64_exit(m, o) // exit status masks to 42 465 return emu_arm64_run_mem(m, IC_GUEST_BYTES, IC_ENTRY, IC_SP) 466} 467 468func ic_arm64_madd() -> i64 { 469 let m: *u8 = ic_guest() 470 var o: i64 = 0 471 o = ic_put_le32(m, o, ic_a64_movz(1, 6, 0)) // mov x1,#6 472 o = ic_put_le32(m, o, ic_a64_movz(2, 7, 0)) // mov x2,#7 473 o = ic_put_le32(m, o, ic_a64_movz(3, 0, 0)) // mov x3,#0 474 o = ic_put_le32(m, o, ic_a64_madd(0, 1, 2, 3)) // madd x0,x1,x2,x3 -> 42 475 o = ic_a64_exit(m, o) 476 return emu_arm64_run_mem(m, IC_GUEST_BYTES, IC_ENTRY, IC_SP) 477} 478 479// CBZ with a TAKEN branch. AArch64 has no delay slot, so the offset is 480// measured in instructions from the branch itself: +2 skips exactly the 481// one poison instruction that follows. 482func ic_arm64_cbz_taken() -> i64 { 483 let m: *u8 = ic_guest() 484 var o: i64 = 0 485 o = ic_put_le32(m, o, ic_a64_movz(0, 42, 0)) // mov x0,#42 486 o = ic_put_le32(m, o, ic_a64_movz(1, 0, 0)) // mov x1,#0 487 o = ic_put_le32(m, o, ic_a64_cbz(1, 2)) // cbz x1,+2 (taken) 488 o = ic_put_le32(m, o, ic_a64_movz(0, 99, 0)) // mov x0,#99 (must be skipped) 489 o = ic_a64_exit(m, o) 490 return emu_arm64_run_mem(m, IC_GUEST_BYTES, IC_ENTRY, IC_SP) 491} 492 493 494// ===== RISC-V RV32 KATs ========================================= 495// RV32I shares its base encoding with RV64I, so the arithmetic words here are 496// the SAME oracle-verified words the rv64 block uses -- that is the point of a 497// shared base ISA and it makes these the cheapest KATs on the board. What is 498// NOT shared is the register width: RV32 masks every result to 32 bits. 499// The branch loop exercises conditional branches that DID NOT EXIST in this 500// interpreter until 2026-09-03; before that RV32 could not run a loop at all. 501 502func ic_riscv32_imm_arith() -> i64 { 503 let m: *u8 = ic_guest() 504 var o: i64 = 0 505 o = ic_put_le32(m, o, 0x02800513) // addi a0,zero,40 506 o = ic_put_le32(m, o, 0x00200293) // addi t0,zero,2 507 o = ic_put_le32(m, o, 0x00550533) // add a0,a0,t0 -> 42 508 o = ic_put_le32(m, o, 0x05d00893) // addi a7,zero,93 509 o = ic_put_le32(m, o, 0x00000073) // ecall 510 return emu_riscv32_run_mem(m, IC_GUEST_BYTES, IC_ENTRY, IC_SP) 511} 512 513func ic_riscv32_sub() -> i64 { 514 let m: *u8 = ic_guest() 515 var o: i64 = 0 516 o = ic_put_le32(m, o, 0x03200513) // addi a0,zero,50 517 o = ic_put_le32(m, o, 0x00800293) // addi t0,zero,8 518 o = ic_put_le32(m, o, 0x40550533) // sub a0,a0,t0 -> 42 519 o = ic_put_le32(m, o, 0x05d00893) 520 o = ic_put_le32(m, o, 0x00000073) 521 return emu_riscv32_run_mem(m, IC_GUEST_BYTES, IC_ENTRY, IC_SP) 522} 523 524func ic_riscv32_logic() -> i64 { 525 let m: *u8 = ic_guest() 526 var o: i64 = 0 527 o = ic_put_le32(m, o, 0x0f000513) // addi a0,zero,240 528 o = ic_put_le32(m, o, 0x00f00293) // addi t0,zero,15 529 o = ic_put_le32(m, o, 0x00556533) // or a0,a0,t0 -> 255 530 o = ic_put_le32(m, o, 0x03c00313) // addi t1,zero,60 531 o = ic_put_le32(m, o, 0x00657533) // and a0,a0,t1 -> 60 532 o = ic_put_le32(m, o, 0x05d00893) 533 o = ic_put_le32(m, o, 0x00000073) 534 return emu_riscv32_run_mem(m, IC_GUEST_BYTES, IC_ENTRY, IC_SP) 535} 536 537func ic_riscv32_mul() -> i64 { 538 let m: *u8 = ic_guest() 539 var o: i64 = 0 540 o = ic_put_le32(m, o, 0x00600513) // addi a0,zero,6 541 o = ic_put_le32(m, o, 0x00700293) // addi t0,zero,7 542 o = ic_put_le32(m, o, 0x02550533) // mul a0,a0,t0 -> 42 (M extension, funct7=1) 543 o = ic_put_le32(m, o, 0x05d00893) 544 o = ic_put_le32(m, o, 0x00000073) 545 return emu_riscv32_run_mem(m, IC_GUEST_BYTES, IC_ENTRY, IC_SP) 546} 547 548func ic_riscv32_load_store() -> i64 { 549 let m: *u8 = ic_guest() 550 var o: i64 = 0 551 o = ic_put_le32(m, o, 0x02a00513) // addi a0,zero,42 552 o = ic_put_le32(m, o, 0xfea12c23) // sw a0,-8(sp) (32-bit store, funct3=2) 553 o = ic_put_le32(m, o, 0xff812503) // lw a0,-8(sp) -> 42 554 o = ic_put_le32(m, o, 0x05d00893) 555 o = ic_put_le32(m, o, 0x00000073) 556 return emu_riscv32_run_mem(m, IC_GUEST_BYTES, IC_ENTRY, IC_SP) 557} 558 559func ic_riscv32_branch_loop() -> i64 { 560 let m: *u8 = ic_guest() 561 var o: i64 = 0 562 o = ic_put_le32(m, o, 0x00000513) // li a0,0 563 o = ic_put_le32(m, o, 0x00000293) // li t0,0 564 o = ic_put_le32(m, o, 0x00a00313) // li t1,10 565 o = ic_put_le32(m, o, 0x00550533) // loop: add a0,a0,t0 566 o = ic_put_le32(m, o, 0x00128293) // addi t0,t0,1 567 o = ic_put_le32(m, o, 0xfe629ce3) // bne t0,t1,loop -> sum 0..9 = 45 568 o = ic_put_le32(m, o, 0x05d00893) 569 o = ic_put_le32(m, o, 0x00000073) 570 return emu_riscv32_run_mem(m, IC_GUEST_BYTES, IC_ENTRY, IC_SP) 571} 572 573 574// ===== PowerPC64 LE KATs ======================================== 575// Power ISA encodings. PPC numbers bits MSB=0, so a field at PPC bits [a..b] 576// sits at standard bits [31-b .. 31-a]; the emulator's own header states the 577// derivation and this block follows it: primary opcode = (w >> 26) & 0x3F, 578// RT/RS = (w >> 21) & 0x1F, RA = (w >> 16) & 0x1F, RB = (w >> 11) & 0x1F, 579// XO = (w >> 1) & 0x3FF. 580// ppc64le stores INSTRUCTIONS little-endian, so these are emitted LE even 581// though Power is a big-endian architecture by heritage -- getting that 582// backwards is the silent-fixture defect, and it is why the emitter is chosen 583// per ISA rather than per family. 584// Linux ppc exit is sc with r0 = 1 and the status in r3. 585// li RT,v = addi RT,0,v opcode 14 586// add RT,RA,RB opcode 31 XO 266 subf RT,RA,RB opcode 31 XO 40 (RB - RA) 587// mulld opcode 31 XO 233 or RA,RS,RB opcode 31 XO 444 (dest RA) 588// and RA,RS,RB opcode 31 XO 28 sc opcode 17 589 590func ic_pp_exit(m: *u8, o0: i64) -> i64 { 591 var o: i64 = o0 592 o = ic_put_le32(m, o, 0x38000001) // li r0,1 (exit) 593 o = ic_put_le32(m, o, 0x44000002) // sc 594 return o 595} 596 597func ic_ppc64le_imm_arith() -> i64 { 598 let m: *u8 = ic_guest() 599 var o: i64 = 0 600 o = ic_put_le32(m, o, 0x38600028) // li r3,40 601 o = ic_put_le32(m, o, 0x38800002) // li r4,2 602 o = ic_put_le32(m, o, 0x7c632214) // add r3,r3,r4 -> 42 603 o = ic_pp_exit(m, o) 604 return emu_ppc64le_run_mem(m, IC_GUEST_BYTES, IC_ENTRY, IC_SP) 605} 606 607// subf computes RB - RA, NOT RA - RB. Encoding it the intuitive way round is 608// the classic Power mistake and would silently yield -42 here. 609func ic_ppc64le_sub() -> i64 { 610 let m: *u8 = ic_guest() 611 var o: i64 = 0 612 o = ic_put_le32(m, o, 0x38600032) // li r3,50 613 o = ic_put_le32(m, o, 0x38800008) // li r4,8 614 o = ic_put_le32(m, o, 0x7c641850) // subf r3,r4,r3 -> r3 - r4 = 42 615 o = ic_pp_exit(m, o) 616 return emu_ppc64le_run_mem(m, IC_GUEST_BYTES, IC_ENTRY, IC_SP) 617} 618 619func ic_ppc64le_mul() -> i64 { 620 let m: *u8 = ic_guest() 621 var o: i64 = 0 622 o = ic_put_le32(m, o, 0x38600006) // li r3,6 623 o = ic_put_le32(m, o, 0x38800007) // li r4,7 624 o = ic_put_le32(m, o, 0x7c6321d2) // mulld r3,r3,r4 -> 42 625 o = ic_pp_exit(m, o) 626 return emu_ppc64le_run_mem(m, IC_GUEST_BYTES, IC_ENTRY, IC_SP) 627} 628 629// or and and write to RA, with the SOURCE in the RT/RS field -- the operand 630// roles are the reverse of every other arch on this board. 631func ic_ppc64le_logic() -> i64 { 632 let m: *u8 = ic_guest() 633 var o: i64 = 0 634 o = ic_put_le32(m, o, 0x386000f0) // li r3,240 635 o = ic_put_le32(m, o, 0x3880000f) // li r4,15 636 o = ic_put_le32(m, o, 0x7c632378) // or r3,r3,r4 -> 255 637 o = ic_put_le32(m, o, 0x38a0003c) // li r5,60 638 o = ic_put_le32(m, o, 0x7c632838) // and r3,r3,r5 -> 60 639 o = ic_pp_exit(m, o) 640 return emu_ppc64le_run_mem(m, IC_GUEST_BYTES, IC_ENTRY, IC_SP) 641} 642 643// ===== ratchet ================================================== 644// One line per arch: "<arch> <passed>". UNSEEDED is a NAMED third 645// state, so adopting this fence cannot fail closed on first sight. 646// Polarity matters and inverts from an offender-count ratchet: this 647// metric is GOOD-when-higher, so it TIGHTENS on a rise and must NEVER 648// rewrite its baseline on a fall -- rewriting on a fall is how a 649// ratchet launders itself green. 650 651const IC_RATCHET: *u8 = "knowledge/status/isa_conform.ratchet" 652 653func ic_match_at(buf: *u8, n: i64, p: i64, s: *u8) -> i64 { 654 var j: i64 = 0 655 var ok: i64 = 1 656 while s[j] != (0 as u8) { 657 if (p + j) >= n { ok = 0 } 658 if ok == 1 { 659 if buf[p + j] != s[j] { ok = 0 } 660 } 661 j = j + 1 662 } 663 if ok == 0 { return -1 } 664 return j 665} 666 667func ic_digits_at(buf: *u8, n: i64, p0: i64) -> i64 { 668 var v: i64 = 0 669 var any: i64 = 0 670 var p: i64 = p0 671 var go: i64 = 1 672 while go == 1 { 673 if p >= n { go = 0 } 674 if go == 1 { 675 let c: i64 = buf[p] & 0xff 676 if c < IC_DIG_LO { go = 0 } 677 if go == 1 { 678 if c > IC_DIG_HI { go = 0 } 679 } 680 if go == 1 { 681 v = v * 10 + (c - IC_DIG_LO) 682 any = 1 683 p = p + 1 684 } 685 } 686 } 687 if any == 0 { return -1 } 688 return v 689} 690 691func ic_ratchet_read(arch: *u8) -> i64 { 692 let lenbox: *i64 = sys_mmap(16) as *i64 693 let buf: *u8 = sys_read_file(IC_RATCHET, lenbox) 694 if (buf as i64) == 0 { return IC_UNSEEDED } 695 let n: i64 = lenbox[0] 696 var i: i64 = 0 697 var found: i64 = IC_UNSEEDED 698 while i < n { 699 if found < 0 { 700 let m: i64 = ic_match_at(buf, n, i, arch) 701 if m > 0 { 702 if (i + m) < n { 703 if buf[i + m] == (IC_SPACE as u8) { 704 found = ic_digits_at(buf, n, i + m + 1) 705 } 706 } 707 } 708 } 709 var adv: i64 = 1 710 while adv == 1 { 711 if i >= n { adv = 0 } 712 if adv == 1 { 713 if buf[i] == (IC_NL as u8) { adv = 0 } 714 i = i + 1 715 } 716 } 717 } 718 return found 719} 720 721// Writes the whole two-arch baseline. Callers must have already decided 722// that NO arch regressed -- this function does not re-check, and the 723// tooth that guards it is bite-proven in nx_isa_conform_bite_gate. 724func ic_ratchet_write(rv: i64, mi: i64, a64: i64, r32: i64) -> i64 { 725 let d: *u8 = sys_mmap(256) 726 var o: i64 = 0 727 o = gv_cat(d, o, "rv64 " as *u8) 728 o = gv_catn(d, o, rv) 729 d[o] = IC_NL as u8 730 o = o + 1 731 o = gv_cat(d, o, "mips64 " as *u8) 732 o = gv_catn(d, o, mi) 733 d[o] = IC_NL as u8 734 o = o + 1 735 o = gv_cat(d, o, "arm64 " as *u8) 736 o = gv_catn(d, o, a64) 737 d[o] = IC_NL as u8 738 o = o + 1 739 o = gv_cat(d, o, "riscv32 " as *u8) 740 o = gv_catn(d, o, r32) 741 d[o] = IC_NL as u8 742 o = o + 1 743 let fd: i64 = sys_openat_wr(IC_RATCHET, IC_MODE_FILE) 744 if fd < 0 { return -1 } 745 sys_write(fd, d, o) 746 sys_fsync(fd) 747 sys_close(fd) 748 return o 749} 750 751// ===== main ===================================================== 752 753func main(argc: i64, argv: *i64) -> i64 { 754 gv_head("nx_isa_conform_gate -- sovereign ISA emulator conformance, KATs encoded from the ISA manuals" as *u8) 755 let ctr: *i64 = gv_ctr() 756 757 // The non-PASS worklist: buffer address and write offset in one box. 758 let bx: *i64 = sys_mmap(IC_BOX_BYTES) as *i64 759 let nbuf: *u8 = sys_mmap(IC_NB_BYTES) 760 nbuf[0] = 0 as u8 761 bx[IC_NB_OFF] = 0 762 bx[IC_NB_PTR] = nbuf as i64 763 764 gv_puts("dispatch=emu_<arch>_run_mem (the only surface all 10 emulators share: run 6/10, load_elf a DIFFERENT 6/10)" as *u8) 765 ic_nl() 766 gv_puts("outcomes are FIVE-WAY: PASS / WRONG-ANSWER / UNSUPPORTED-instruction-class / RAN-OFF-END / FAULT" as *u8) 767 ic_nl() 768 ic_nl() 769 770 // ---- rv64 ---- 771 gv_puts("[rv64] RISC-V RV64I -- little-endian, no delay slots" as *u8) 772 ic_nl() 773 var rv_pass: i64 = 0 774 var rv_tot: i64 = 0 775 var o: i64 = 0 776 777 o = ic_report("rv64" as *u8, "imm-arith" as *u8, ic_rv64_imm_arith(), 42, bx) 778 rv_tot = rv_tot + 1 779 if o == IC_O_PASS { rv_pass = rv_pass + 1 } 780 gv_check("isa-rv64-imm-arith" as *u8, o == IC_O_PASS, ctr) 781 782 o = ic_report("rv64" as *u8, "sub" as *u8, ic_rv64_sub(), 42, bx) 783 rv_tot = rv_tot + 1 784 if o == IC_O_PASS { rv_pass = rv_pass + 1 } 785 gv_check("isa-rv64-sub" as *u8, o == IC_O_PASS, ctr) 786 787 o = ic_report("rv64" as *u8, "branch-loop" as *u8, ic_rv64_branch_loop(), 45, bx) 788 rv_tot = rv_tot + 1 789 if o == IC_O_PASS { rv_pass = rv_pass + 1 } 790 gv_check("isa-rv64-branch-loop" as *u8, o == IC_O_PASS, ctr) 791 792 o = ic_report("rv64" as *u8, "shift-logical" as *u8, ic_rv64_shift_logical(), 15, bx) 793 rv_tot = rv_tot + 1 794 if o == IC_O_PASS { rv_pass = rv_pass + 1 } 795 gv_check("isa-rv64-shift-logical" as *u8, o == IC_O_PASS, ctr) 796 797 o = ic_report("rv64" as *u8, "shift-arith" as *u8, ic_rv64_shift_arith(), 255, bx) 798 rv_tot = rv_tot + 1 799 if o == IC_O_PASS { rv_pass = rv_pass + 1 } 800 gv_check("isa-rv64-shift-arith" as *u8, o == IC_O_PASS, ctr) 801 802 o = ic_report("rv64" as *u8, "load-store" as *u8, ic_rv64_load_store(), 42, bx) 803 rv_tot = rv_tot + 1 804 if o == IC_O_PASS { rv_pass = rv_pass + 1 } 805 gv_check("isa-rv64-load-store" as *u8, o == IC_O_PASS, ctr) 806 807 // ---- mips64 ---- 808 ic_nl() 809 gv_puts("[mips64] MIPS64 n64 -- BIG-endian, PC/nPC delay-slot model" as *u8) 810 ic_nl() 811 var mi_pass: i64 = 0 812 var mi_tot: i64 = 0 813 814 o = ic_report("mips64" as *u8, "imm-arith" as *u8, ic_mips64_imm_arith(), 42, bx) 815 mi_tot = mi_tot + 1 816 if o == IC_O_PASS { mi_pass = mi_pass + 1 } 817 gv_check("isa-mips64-imm-arith" as *u8, o == IC_O_PASS, ctr) 818 819 o = ic_report("mips64" as *u8, "sub" as *u8, ic_mips64_sub(), 42, bx) 820 mi_tot = mi_tot + 1 821 if o == IC_O_PASS { mi_pass = mi_pass + 1 } 822 gv_check("isa-mips64-sub" as *u8, o == IC_O_PASS, ctr) 823 824 o = ic_report("mips64" as *u8, "logic" as *u8, ic_mips64_logic(), 60, bx) 825 mi_tot = mi_tot + 1 826 if o == IC_O_PASS { mi_pass = mi_pass + 1 } 827 gv_check("isa-mips64-logic" as *u8, o == IC_O_PASS, ctr) 828 829 o = ic_report("mips64" as *u8, "shift" as *u8, ic_mips64_shift(), 128, bx) 830 mi_tot = mi_tot + 1 831 if o == IC_O_PASS { mi_pass = mi_pass + 1 } 832 gv_check("isa-mips64-shift" as *u8, o == IC_O_PASS, ctr) 833 834 o = ic_report("mips64" as *u8, "shift-logical-vs-arith" as *u8, ic_mips64_shift_logical(), 3, bx) 835 mi_tot = mi_tot + 1 836 if o == IC_O_PASS { mi_pass = mi_pass + 1 } 837 gv_check("isa-mips64-shift-logical-vs-arith" as *u8, o == IC_O_PASS, ctr) 838 839 o = ic_report("mips64" as *u8, "addiu-is-32-bit" as *u8, ic_mips64_addiu32(), 3, bx) 840 mi_tot = mi_tot + 1 841 if o == IC_O_PASS { mi_pass = mi_pass + 1 } 842 gv_check("isa-mips64-addiu-is-32-bit" as *u8, o == IC_O_PASS, ctr) 843 844 // Reported and COUNTED. This was deliberately NOT a gv_check while the 845 // emulator had no conditional branch at all, because a tooth that cannot 846 // pass makes a detector permanently RED. The branches landed 2026-09-03, 847 // so it is a full tooth now -- which is exactly the sequence a ratchet is 848 // supposed to produce: name the gap, close the gap, then fence it. 849 let mb: i64 = ic_report("mips64" as *u8, "branch-delay-slot" as *u8, ic_mips64_branch(), 42, bx) 850 mi_tot = mi_tot + 1 851 if mb == IC_O_PASS { mi_pass = mi_pass + 1 } 852 gv_check("isa-mips64-branch-delay-slot" as *u8, mb == IC_O_PASS, ctr) 853 854 855 // ---- arm64 ---- 856 ic_nl() 857 gv_puts("[arm64] Arm A64 -- little-endian, no delay slots, XZR is register 31" as *u8) 858 ic_nl() 859 var a64_pass: i64 = 0 860 var a64_tot: i64 = 0 861 862 o = ic_report("arm64" as *u8, "imm-arith" as *u8, ic_arm64_imm_arith(), 42, bx) 863 a64_tot = a64_tot + 1 864 if o == IC_O_PASS { a64_pass = a64_pass + 1 } 865 gv_check("isa-arm64-imm-arith" as *u8, o == IC_O_PASS, ctr) 866 867 o = ic_report("arm64" as *u8, "movk-keeps-lanes" as *u8, ic_arm64_movk_keeps_lanes(), 42, bx) 868 a64_tot = a64_tot + 1 869 if o == IC_O_PASS { a64_pass = a64_pass + 1 } 870 gv_check("isa-arm64-movk-keeps-lanes" as *u8, o == IC_O_PASS, ctr) 871 872 o = ic_report("arm64" as *u8, "madd" as *u8, ic_arm64_madd(), 42, bx) 873 a64_tot = a64_tot + 1 874 if o == IC_O_PASS { a64_pass = a64_pass + 1 } 875 gv_check("isa-arm64-madd" as *u8, o == IC_O_PASS, ctr) 876 877 o = ic_report("arm64" as *u8, "cbz-taken" as *u8, ic_arm64_cbz_taken(), 42, bx) 878 a64_tot = a64_tot + 1 879 if o == IC_O_PASS { a64_pass = a64_pass + 1 } 880 gv_check("isa-arm64-cbz-taken" as *u8, o == IC_O_PASS, ctr) 881 882 883 // ---- riscv32 ---- 884 ic_nl() 885 gv_puts("[riscv32] RISC-V RV32IM -- little-endian, 32-bit XLEN, shares the RV64 base encoding" as *u8) 886 ic_nl() 887 var r32_pass: i64 = 0 888 var r32_tot: i64 = 0 889 890 o = ic_report("riscv32" as *u8, "imm-arith" as *u8, ic_riscv32_imm_arith(), 42, bx) 891 r32_tot = r32_tot + 1 892 if o == IC_O_PASS { r32_pass = r32_pass + 1 } 893 gv_check("isa-riscv32-imm-arith" as *u8, o == IC_O_PASS, ctr) 894 895 o = ic_report("riscv32" as *u8, "sub" as *u8, ic_riscv32_sub(), 42, bx) 896 r32_tot = r32_tot + 1 897 if o == IC_O_PASS { r32_pass = r32_pass + 1 } 898 gv_check("isa-riscv32-sub" as *u8, o == IC_O_PASS, ctr) 899 900 o = ic_report("riscv32" as *u8, "logic" as *u8, ic_riscv32_logic(), 60, bx) 901 r32_tot = r32_tot + 1 902 if o == IC_O_PASS { r32_pass = r32_pass + 1 } 903 gv_check("isa-riscv32-logic" as *u8, o == IC_O_PASS, ctr) 904 905 o = ic_report("riscv32" as *u8, "mul" as *u8, ic_riscv32_mul(), 42, bx) 906 r32_tot = r32_tot + 1 907 if o == IC_O_PASS { r32_pass = r32_pass + 1 } 908 gv_check("isa-riscv32-mul" as *u8, o == IC_O_PASS, ctr) 909 910 o = ic_report("riscv32" as *u8, "load-store" as *u8, ic_riscv32_load_store(), 42, bx) 911 r32_tot = r32_tot + 1 912 if o == IC_O_PASS { r32_pass = r32_pass + 1 } 913 gv_check("isa-riscv32-load-store" as *u8, o == IC_O_PASS, ctr) 914 915 o = ic_report("riscv32" as *u8, "branch-loop" as *u8, ic_riscv32_branch_loop(), 45, bx) 916 r32_tot = r32_tot + 1 917 if o == IC_O_PASS { r32_pass = r32_pass + 1 } 918 gv_check("isa-riscv32-branch-loop" as *u8, o == IC_O_PASS, ctr) 919 920 921 // ---- ppc64le ---- 922 ic_nl() 923 gv_puts("[ppc64le] PowerPC64 little-endian -- Power ISA, MSB=0 bit numbering, sc syscall" as *u8) 924 ic_nl() 925 var pp_pass: i64 = 0 926 var pp_tot: i64 = 0 927 928 o = ic_report("ppc64le" as *u8, "imm-arith" as *u8, ic_ppc64le_imm_arith(), 42, bx) 929 pp_tot = pp_tot + 1 930 if o == IC_O_PASS { pp_pass = pp_pass + 1 } 931 gv_check("isa-ppc64le-imm-arith" as *u8, o == IC_O_PASS, ctr) 932 933 o = ic_report("ppc64le" as *u8, "subf-operand-order" as *u8, ic_ppc64le_sub(), 42, bx) 934 pp_tot = pp_tot + 1 935 if o == IC_O_PASS { pp_pass = pp_pass + 1 } 936 gv_check("isa-ppc64le-subf-operand-order" as *u8, o == IC_O_PASS, ctr) 937 938 o = ic_report("ppc64le" as *u8, "mulld" as *u8, ic_ppc64le_mul(), 42, bx) 939 pp_tot = pp_tot + 1 940 if o == IC_O_PASS { pp_pass = pp_pass + 1 } 941 gv_check("isa-ppc64le-mulld" as *u8, o == IC_O_PASS, ctr) 942 943 o = ic_report("ppc64le" as *u8, "logic-dest-is-RA" as *u8, ic_ppc64le_logic(), 60, bx) 944 pp_tot = pp_tot + 1 945 if o == IC_O_PASS { pp_pass = pp_pass + 1 } 946 gv_check("isa-ppc64le-logic-dest-is-RA" as *u8, o == IC_O_PASS, ctr) 947 948 // ---- the fixture must have REACHED the condition ------------- 949 // Asserting an outcome without asserting the fixture ran is how 950 // four vacuous fixtures shipped in this estate in a single day. 951 ic_nl() 952 gv_check("fixture-reached-rv64-emulator-at-all" as *u8, rv_pass > 0, ctr) 953 gv_check("fixture-reached-mips64-emulator-at-all" as *u8, mi_pass > 0, ctr) 954 gv_check("fixture-reached-arm64-emulator-at-all" as *u8, a64_pass > 0, ctr) 955 gv_check("fixture-reached-riscv32-emulator-at-all" as *u8, r32_pass > 0, ctr) 956 gv_check("fixture-reached-ppc64le-emulator-at-all" as *u8, pp_pass > 0, ctr) 957 958 // ---- negative controls --------------------------------------- 959 // A comparison that cannot fail proves nothing. If ic_classify were 960 // stubbed to return PASS, every green above would be fake and only 961 // these two teeth would notice. 962 let ncw: i64 = ic_classify(ic_rv64_imm_arith(), 41) 963 gv_puts(" neg-control rv64.imm-arith against a deliberately wrong want=41 -> " as *u8) 964 gv_puts(ic_outcome_name(ncw)) 965 ic_nl() 966 gv_check("neg-control-wrong-expectation-must-not-pass" as *u8, ncw == IC_O_WRONG, ctr) 967 968 let ncu: i64 = ic_classify(IC_UNSUPPORTED, -1) 969 gv_check("neg-control-unsupported-never-reads-as-pass" as *u8, ncu == IC_O_UNSUP, ctr) 970 971 let ncf: i64 = ic_classify(IC_FAULT, -3) 972 gv_check("neg-control-fault-never-reads-as-pass" as *u8, ncf == IC_O_FAULT, ctr) 973 974 // ---- coverage, published as a NUMBER not a verdict ------------ 975 ic_nl() 976 gv_puts("coverage rv64=" as *u8) 977 gv_num(rv_pass) 978 gv_puts("/" as *u8) 979 gv_num(rv_tot) 980 gv_puts(" mips64=" as *u8) 981 gv_num(mi_pass) 982 gv_puts("/" as *u8) 983 gv_num(mi_tot) 984 gv_puts(" arm64=" as *u8) 985 gv_num(a64_pass) 986 gv_puts("/" as *u8) 987 gv_num(a64_tot) 988 gv_puts(" riscv32=" as *u8) 989 gv_num(r32_pass) 990 gv_puts("/" as *u8) 991 gv_num(r32_tot) 992 gv_puts(" ppc64le=" as *u8) 993 gv_num(pp_pass) 994 gv_puts("/" as *u8) 995 gv_num(pp_tot) 996 gv_puts(" archs_measured=5 archs_present=10 archs_unmeasured=5" as *u8) 997 ic_nl() 998 gv_puts("UNMEASURED: armv7a cortexm loongarch64 s390x sparc64 -- each owes a manual-derived KAT block here" as *u8) 999 ic_nl() 1000 1001 // ---- ratchet ------------------------------------------------- 1002 let rv_base: i64 = ic_ratchet_read("rv64" as *u8) 1003 let mi_base: i64 = ic_ratchet_read("mips64" as *u8) 1004 let a64_base: i64 = ic_ratchet_read("arm64" as *u8) 1005 let r32_base: i64 = ic_ratchet_read("riscv32" as *u8) 1006 gv_puts("ratchet rv64 base=" as *u8) 1007 gv_num(rv_base) 1008 gv_puts(" now=" as *u8) 1009 gv_num(rv_pass) 1010 gv_puts(" | mips64 base=" as *u8) 1011 gv_num(mi_base) 1012 gv_puts(" now=" as *u8) 1013 gv_num(mi_pass) 1014 gv_puts(" | arm64 base=" as *u8) 1015 gv_num(a64_base) 1016 gv_puts(" now=" as *u8) 1017 gv_num(a64_pass) 1018 gv_puts(" | riscv32 base=" as *u8) 1019 gv_num(r32_base) 1020 gv_puts(" now=" as *u8) 1021 gv_num(r32_pass) 1022 gv_puts(" (base=-1 is UNSEEDED, a named state: first sight seeds, so adoption cannot fail closed)" as *u8) 1023 ic_nl() 1024 1025 var no_regress: i64 = 1 1026 if rv_base >= 0 { 1027 if rv_pass < rv_base { no_regress = 0 } 1028 } 1029 if mi_base >= 0 { 1030 if mi_pass < mi_base { no_regress = 0 } 1031 } 1032 if a64_base >= 0 { 1033 if a64_pass < a64_base { no_regress = 0 } 1034 } 1035 if r32_base >= 0 { 1036 if r32_pass < r32_base { no_regress = 0 } 1037 } 1038 1039 // Seed when unseeded; tighten only on a rise; NEVER rewrite on a 1040 // fall, which is the direction that would launder a regression. 1041 var wrote: i64 = 0 1042 if no_regress == 1 { 1043 if rv_base < 0 { wrote = ic_ratchet_write(rv_pass, mi_pass, a64_pass, r32_pass) } 1044 if wrote == 0 { 1045 if mi_base < 0 { wrote = ic_ratchet_write(rv_pass, mi_pass, a64_pass, r32_pass) } 1046 } 1047 if wrote == 0 { 1048 if rv_pass > rv_base { wrote = ic_ratchet_write(rv_pass, mi_pass, a64_pass, r32_pass) } 1049 } 1050 if wrote == 0 { 1051 if mi_pass > mi_base { wrote = ic_ratchet_write(rv_pass, mi_pass, a64_pass, r32_pass) } 1052 } 1053 if wrote == 0 { 1054 if a64_base < 0 { wrote = ic_ratchet_write(rv_pass, mi_pass, a64_pass, r32_pass) } 1055 } 1056 if wrote == 0 { 1057 if a64_pass > a64_base { wrote = ic_ratchet_write(rv_pass, mi_pass, a64_pass, r32_pass) } 1058 } 1059 if wrote == 0 { 1060 if r32_base < 0 { wrote = ic_ratchet_write(rv_pass, mi_pass, a64_pass, r32_pass) } 1061 } 1062 if wrote == 0 { 1063 if r32_pass > r32_base { wrote = ic_ratchet_write(rv_pass, mi_pass, a64_pass, r32_pass) } 1064 } 1065 } 1066 gv_puts("ratchet_bytes_written=" as *u8) 1067 gv_num(wrote) 1068 gv_puts(" (0 = held: neither seeded nor tightened this run)" as *u8) 1069 ic_nl() 1070 1071 // This dialect has no || (zero occurrences across nx_ir.nx, 1072 // nx_gate_verdict.nx and nx_emu_arm64.nx -- measured, not assumed), 1073 // so the laundering condition is computed rather than expressed. 1074 var laundered: i64 = 0 1075 if no_regress == 0 { 1076 if wrote > 0 { laundered = 1 } 1077 } 1078 1079 gv_check("ratchet-no-arch-regressed" as *u8, no_regress == 1, ctr) 1080 gv_check("ratchet-never-rewritten-on-a-regression" as *u8, laundered == 0, ctr) 1081 1082 // THE WORKLIST, LAST, where a tailing caller can still see it. 1083 gv_puts("NONPASS: " as *u8) 1084 if bx[IC_NB_OFF] == 0 { 1085 gv_puts("none -- every measured KAT matched its manual-derived expectation" as *u8) 1086 } 1087 if bx[IC_NB_OFF] > 0 { 1088 gv_puts(bx[IC_NB_PTR] as *u8) 1089 } 1090 ic_nl() 1091 1092 return gv_verdict("nx_isa_conform_gate" as *u8, ctr, 1093 "per-ISA conformance measured against manual-derived KATs with four-way outcomes, coverage published as a number, fenced by a good-when-higher per-arch ratchet that seeds on first sight and never rewrites on a fall" as *u8) 1094}