code wiki / (root) / nx_isa_sparc64_gate.nx

nx_isa_sparc64_gate.nx source

↩ module page · 1007 lines · 42728 B

1// nx_isa_sparc64_gate.nx -- THE SPARC64 CONFORMANCE RULER. 2// 3// WHY IT EXISTS: nx_isa_conform_gate measures five of the estate's ten 4// sovereign ISA emulators and names the other five in one line -- 5// "UNMEASURED: armv7a cortexm loongarch64 s390x sparc64 -- each owes a 6// manual-derived KAT block here". This organ pays that debt for 7// sparc64. It is a SEPARATE organ rather than an edit to the incumbent 8// because five lanes are writing five arch blocks at once and 9// concurrent edits to one file silently clobber each other in this 10// tree; the shape is copied deliberately so the two rulers agree. 11// 12// THE LAW IT OBEYS: a capability asserted and never measured is a 13// claim, not coverage. "We emulate sparc64" is decided by running 14// SPARC V9 machine code, in process, against values taken FROM THE 15// SPARC V9 MANUAL -- never from the emulator's own source, which would 16// calibrate the ruler to the subject it exists to judge. The decoder 17// was read first, but ONLY to learn which instruction classes are 18// implemented versus missing; every expected value below is derived 19// from the manual's encoding tables and its Bicc/BPcc condition table. 20// 21// WHAT THE READ FOUND, AND WHAT WAS BUILT BECAUSE OF IT. On 22// 2026-09-03 nx_emu_sparc64 decoded CALL, SETHI, ADD/SUB/AND/OR/XOR, 23// MULX, SDIVX/UDIVX, two shifts, SAVE/RESTORE, JMPL, Tcc and four 24// load/store forms -- and NOTHING ELSE in format 2. It carried no 25// condition-code state and no branch of any kind, so it could not run a 26// LOOP: the sparc64 claim covered straight-line code only. Four 27// further defects sat in the classes it did decode: 28// 1. op3 0x08 is LDSW in V9 and was decoded as an UNSIGNED load, so 29// the only 32-bit load available answered with the wrong sign 30// extension; the real LDUW (op3 0x00) was absent entirely, so a 31// correct program reported UNSUPPORTED. 32// 2. SRL/SRLX (op3 0x26) was written with this dialect's arithmetic 33// right shift and SRA/SRAX (op3 0x27) was absent -- so the logical 34// and the arithmetic right shift were ONE expression and one of 35// the two was necessarily wrong for every negative operand. 36// 3. The shift count came from the sign-extended 13-bit simm without 37// reading the X bit, so the 32-bit shift forms could not be told 38// apart from the 64-bit ones. 39// 4. No sub-word loads or stores at all (LDUB/LDSB/LDUH/LDSH/STB/STH). 40// All of it was implemented in nx_emu_sparc64 -- condition codes for 41// BOTH %icc and %xcc, the *cc arithmetic that writes them, Bicc and 42// BPcc with the annul rule, ANDN/ORN/XNOR, the corrected shifts and the 43// full sub-word load/store set -- and every one of those classes has a 44// KAT below that a WRONG implementation answers differently. 45// 46// FIVE OUTCOMES, NEVER ONE: 47// PASS ran and matched the manual-derived expectation 48// WRONG-ANSWER ran to completion and computed the wrong value 49// UNSUPPORTED hit an instruction class the emulator does not decode 50// RAN-OFF-END the interpreter loop ended without an exit syscall 51// FAULT bad PC, out-of-range access, or the step budget 52// Collapsing these into "FAIL" is what makes a gap unactionable: an 53// unimplemented class and a miscomputed result need OPPOSITE fixes. 54// 55// HONEST LIMIT OF THE FIVE-WAY SPLIT ON THIS ARCH, MEASURED IN THE 56// SOURCE AND NOT ASSUMED: nx_emu_sparc64 initialises result to 57// SPE_FAULT (-3) and has no -2 sentinel at all, so RAN-OFF-END and 58// step-budget exhaustion are INDISTINGUISHABLE from a bad PC here. 59// That is a real reporting gap and it is printed rather than hidden; 60// rv64 and mips64 do carry the third code. This organ deliberately 61// does NOT invent a fix by mapping FAULT onto RANOFF: that would be a 62// guess wearing a classification. 63// 64// DISPATCH: emu_sparc64_run_mem(mem, size, entry, sp0) is the surface 65// all ten emulators share. _load_elf exists on this one but on only 66// six of the ten, so building on it would silently test a subset. 67// 68// license_tier: ORIGINAL 69 70import "nx_gate_verdict.nx" 71import "nx_syscalls.nx" 72import "nx_emu_sparc64.nx" 73 74// ---- guest layout THIS ORGAN owns (never read from the emulator) ---- 75const IS_GUEST_BYTES: i64 = 1048576 // 1 MiB guest image 76const IS_ENTRY: i64 = 0 // KAT code is loaded at guest vaddr 0 77const IS_SP: i64 = 983040 // 960 KiB: above the code, inside the image 78const IS_MODE_FILE: i64 = 420 // 0644; 420 and 0x1a4 are the SAME constant 79 // and a sweep that greps one misses the other 80 81// ---- non-completion codes, read out of nx_emu_sparc64's own table ---- 82const IS_UNSUPPORTED: i64 = -1 // SPE_UNSUPPORTED 83const IS_RANOFF: i64 = -2 // this emulator never returns it (see header) 84const IS_FAULT: i64 = -3 // SPE_FAULT 85 86const IS_O_PASS: i64 = 0 87const IS_O_WRONG: i64 = 1 88const IS_O_UNSUP: i64 = 2 89const IS_O_FAULT: i64 = 3 90const IS_O_RANOFF: i64 = 4 91 92// The non-PASS worklist. A count without a worklist is not actionable, 93// and a worklist printed only in the BODY is lost the moment a caller 94// tails the output -- so it is accumulated here and printed LAST. 95const IS_NB_BYTES: i64 = 2048 96const IS_NB_OFF: i64 = 0 97const IS_NB_PTR: i64 = 1 98const IS_NB_ROW_MAX: i64 = 256 99const IS_BOX_BYTES: i64 = 32 100 101const IS_NL: i64 = 10 102const IS_SPACE: i64 = 32 103const IS_DIG_LO: i64 = 48 104const IS_DIG_HI: i64 = 57 105const IS_UNSEEDED: i64 = -1 106 107// ===== SPARC V9 encoders, from the manual format tables ============ 108// Format 1 (op=1): CALL, disp30. 109// Format 2 (op=0): the rd FIELD carries a (bit 29) and cond (28..25); 110// op2 selects BPcc(1) Bicc(2) SETHI(4); then disp19 / disp22 / imm22. 111// Format 3 (op=2 arithmetic, op=3 memory): rd, op3, rs1, i, then either 112// simm13 (i=1) or rs2 (i=0). 113const IS_OP_F2: i64 = 0 114const IS_OP_CALL: i64 = 1 115const IS_OP_ALU: i64 = 2 116const IS_OP_MEM: i64 = 3 117 118const IS_M13: i64 = 8191 // 0x1FFF simm13 119const IS_M19: i64 = 524287 // 0x7FFFF disp19 120const IS_M22: i64 = 4194303 // 0x3FFFFF imm22 and disp22 121const IS_M30: i64 = 1073741823 // 0x3FFFFFFF disp30 122const IS_XBIT: i64 = 4096 // bit 12: selects the 64-bit shift form 123 124const IS_OP2_BPCC: i64 = 1 125const IS_OP2_BICC: i64 = 2 126const IS_OP2_SETHI: i64 = 4 127 128// Register-file numbering: 0..7 %g, 8..15 %o, 16..23 %l, 24..31 %i. 129// %o0 (8) is the register the exit trap reads for the status. 130const IS_G0: i64 = 0 131const IS_G1: i64 = 1 132const IS_O0: i64 = 8 133const IS_O1: i64 = 9 134const IS_O2: i64 = 10 135const IS_O3: i64 = 11 136const IS_O6: i64 = 14 137const IS_O7: i64 = 15 138const IS_L0: i64 = 16 139const IS_I0: i64 = 24 140 141const IS_A_ADD: i64 = 0x00 142const IS_A_AND: i64 = 0x01 143const IS_A_OR: i64 = 0x02 144const IS_A_XOR: i64 = 0x03 145const IS_A_SUB: i64 = 0x04 146const IS_A_ANDN: i64 = 0x05 147const IS_A_MULX: i64 = 0x09 148const IS_A_ADDCC: i64 = 0x10 149const IS_A_SUBCC: i64 = 0x14 150const IS_A_SLLX: i64 = 0x25 151const IS_A_SRLX: i64 = 0x26 152const IS_A_SRAX: i64 = 0x27 153const IS_A_JMPL: i64 = 0x38 154const IS_A_TCC: i64 = 0x3a 155const IS_A_SAVE: i64 = 0x3c 156const IS_A_REST: i64 = 0x3d 157const IS_A_FPOP1: i64 = 0x34 // deliberately NOT implemented 158 159const IS_M_LDUW: i64 = 0x00 160const IS_M_LDUB: i64 = 0x01 161const IS_M_STW: i64 = 0x04 162const IS_M_STB: i64 = 0x05 163const IS_M_LDSW: i64 = 0x08 164const IS_M_LDSB: i64 = 0x09 165const IS_M_LDX: i64 = 0x0b 166const IS_M_STX: i64 = 0x0e 167 168const IS_C_E: i64 = 1 // BE / BZ Z 169const IS_C_L: i64 = 3 // BL N xor V 170const IS_C_CS: i64 = 5 // BCS / BLU C 171const IS_C_A: i64 = 8 // BA always 172const IS_C_NE: i64 = 9 // BNE / BNZ not Z 173const IS_CCSEL_ICC: i64 = 0 174const IS_CCSEL_XCC: i64 = 2 175 176// Linux/SPARC: the syscall number is in %g1, arguments from %o0, and 177// the trap instruction is Tcc. 178const IS_SYS_EXIT: i64 = 1 179const IS_TRAP_NR: i64 = 16 // ta 0x10 180 181// A bare newline inside a string literal is ambiguous to this lexer, so 182// the byte is CONSTRUCTED. 183func is_nl() -> i64 { 184 let b: *u8 = sys_mmap(8) 185 b[0] = IS_NL as u8 186 sys_write(1, b, 1) 187 return 0 188} 189 190// SPARC is BIG-ENDIAN with fixed 32-bit instructions. Getting this 191// wrong is the silent-fixture defect: the decoder reads garbage, 192// reports UNSUPPORTED, and the FIXTURE bug reads as the emulator being 193// incomplete. 194func is_put_be32(code: *u8, off: i64, w: i64) -> i64 { 195 code[off] = (w >> 24) & 0xff 196 code[off + 1] = (w >> 16) & 0xff 197 code[off + 2] = (w >> 8) & 0xff 198 code[off + 3] = (w) & 0xff 199 return off + 4 200} 201 202func is_guest() -> *u8 { return sys_mmap(IS_GUEST_BYTES) } 203 204func is_f3i(op: i64, rd: i64, op3: i64, rs1: i64, simm: i64) -> i64 { 205 return (op << 30) | (rd << 25) | (op3 << 19) | (rs1 << 14) | (1 << 13) | (simm & IS_M13) 206} 207func is_f3r(op: i64, rd: i64, op3: i64, rs1: i64, rs2: i64) -> i64 { 208 return (op << 30) | (rd << 25) | (op3 << 19) | (rs1 << 14) | rs2 209} 210// The 64-bit shift forms set the X bit and carry shcnt64 in bits 5..0. 211func is_shx(rd: i64, op3: i64, rs1: i64, cnt: i64) -> i64 { 212 return (IS_OP_ALU << 30) | (rd << 25) | (op3 << 19) | (rs1 << 14) | (1 << 13) | IS_XBIT | (cnt & 63) 213} 214func is_sethi(rd: i64, imm22: i64) -> i64 { 215 return (IS_OP_F2 << 30) | (rd << 25) | (IS_OP2_SETHI << 22) | (imm22 & IS_M22) 216} 217func is_nop() -> i64 { return is_sethi(IS_G0, 0) } 218func is_bicc(a: i64, cond: i64, disp22: i64) -> i64 { 219 return (IS_OP_F2 << 30) | (a << 29) | (cond << 25) | (IS_OP2_BICC << 22) | (disp22 & IS_M22) 220} 221func is_bpcc(a: i64, cond: i64, ccsel: i64, p: i64, disp19: i64) -> i64 { 222 return (IS_OP_F2 << 30) | (a << 29) | (cond << 25) | (IS_OP2_BPCC << 22) | (ccsel << 20) | (p << 19) | (disp19 & IS_M19) 223} 224func is_call(disp30: i64) -> i64 { return (IS_OP_CALL << 30) | (disp30 & IS_M30) } 225 226// %g1 = 1 (SunOS/Linux sparc exit) then the trap. The interpreter masks 227// the status with 0xff, so every expectation in this organ is 0..255 by 228// construction and no expected value can collide with a sentinel. 229func is_exit(m: *u8, off: i64) -> i64 { 230 var p: i64 = off 231 p = is_put_be32(m, p, is_f3i(IS_OP_ALU, IS_G1, IS_A_OR, IS_G0, IS_SYS_EXIT)) 232 p = is_put_be32(m, p, is_f3i(IS_OP_ALU, IS_C_A, IS_A_TCC, IS_G0, IS_TRAP_NR)) 233 return p 234} 235func is_run(m: *u8) -> i64 { return emu_sparc64_run_mem(m, IS_GUEST_BYTES, IS_ENTRY, IS_SP) } 236 237// ===== outcome classification ====================================== 238 239func is_classify(got: i64, want: i64) -> i64 { 240 if got == IS_UNSUPPORTED { return IS_O_UNSUP } 241 if got == IS_RANOFF { return IS_O_RANOFF } 242 if got == IS_FAULT { return IS_O_FAULT } 243 if got == want { return IS_O_PASS } 244 return IS_O_WRONG 245} 246 247func is_outcome_name(o: i64) -> *u8 { 248 if o == IS_O_PASS { return "PASS" as *u8 } 249 if o == IS_O_WRONG { return "WRONG-ANSWER" as *u8 } 250 if o == IS_O_UNSUP { return "UNSUPPORTED-instruction-class" as *u8 } 251 if o == IS_O_RANOFF { return "RAN-OFF-END-no-exit-syscall" as *u8 } 252 return "FAULT-bad-pc-or-step-budget" as *u8 253} 254 255// Print the VALUES, not just the verdict: every vacuous tooth ever 256// caught in this estate was caught by a diagnostic dump and never by a 257// verdict vector. 258func is_report(kat: *u8, got: i64, want: i64, bx: *i64) -> i64 { 259 let o: i64 = is_classify(got, want) 260 gv_puts(" KAT sparc64." as *u8) 261 gv_puts(kat) 262 gv_puts(" got=" as *u8) 263 gv_num(got) 264 gv_puts(" want=" as *u8) 265 gv_num(want) 266 gv_puts(" " as *u8) 267 gv_puts(is_outcome_name(o)) 268 is_nl() 269 if o != IS_O_PASS { 270 let nb: *u8 = bx[IS_NB_PTR] as *u8 271 var p: i64 = bx[IS_NB_OFF] 272 if p < (IS_NB_BYTES - IS_NB_ROW_MAX) { 273 p = gv_cat(nb, p, "sparc64." as *u8) 274 p = gv_cat(nb, p, kat) 275 p = gv_cat(nb, p, "=" as *u8) 276 p = gv_cat(nb, p, is_outcome_name(o)) 277 p = gv_cat(nb, p, "(got=" as *u8) 278 p = gv_catn(nb, p, got) 279 p = gv_cat(nb, p, " want=" as *u8) 280 p = gv_catn(nb, p, want) 281 p = gv_cat(nb, p, ") " as *u8) 282 nb[p] = 0 as u8 283 bx[IS_NB_OFF] = p 284 } 285 } 286 return o 287} 288 289// ===== KATs, every word encoded from the SPARC V9 manual =========== 290// Each program is chosen so a WRONG implementation lands on a DIFFERENT 291// value. The wrong answer is named in the comment beside each, because 292// a test whose right and wrong answers agree is worth nothing and the 293// only way to know which it is, is to have computed both. 294 295// --- arithmetic --------------------------------------------------- 296 297func is_k_imm_arith() -> i64 { // want 42 298 let m: *u8 = is_guest() 299 var o: i64 = 0 300 o = is_put_be32(m, o, is_f3i(IS_OP_ALU, IS_O0, IS_A_ADD, IS_G0, 40)) // add %g0,40,%o0 301 o = is_put_be32(m, o, is_f3i(IS_OP_ALU, IS_O0, IS_A_ADD, IS_O0, 2)) // add %o0,2,%o0 302 o = is_exit(m, o) 303 return is_run(m) 304} 305 306func is_k_sub_reg() -> i64 { // want 42; operand order reversed gives 214 307 let m: *u8 = is_guest() 308 var o: i64 = 0 309 o = is_put_be32(m, o, is_f3i(IS_OP_ALU, IS_O0, IS_A_OR, IS_G0, 50)) 310 o = is_put_be32(m, o, is_f3i(IS_OP_ALU, IS_O1, IS_A_OR, IS_G0, 8)) 311 o = is_put_be32(m, o, is_f3r(IS_OP_ALU, IS_O0, IS_A_SUB, IS_O0, IS_O1)) // sub %o0,%o1,%o0 312 o = is_exit(m, o) 313 return is_run(m) 314} 315 316// 108 xor 53 = 89. Every neighbouring logical op on the same pair is a 317// DIFFERENT number (and 36, or 125, andn 72), so this cannot pass by 318// landing in the wrong branch of the decoder. 319func is_k_xor() -> i64 { // want 89 320 let m: *u8 = is_guest() 321 var o: i64 = 0 322 o = is_put_be32(m, o, is_f3i(IS_OP_ALU, IS_O0, IS_A_OR, IS_G0, 108)) 323 o = is_put_be32(m, o, is_f3i(IS_OP_ALU, IS_O1, IS_A_OR, IS_G0, 53)) 324 o = is_put_be32(m, o, is_f3r(IS_OP_ALU, IS_O0, IS_A_XOR, IS_O0, IS_O1)) 325 o = is_exit(m, o) 326 return is_run(m) 327} 328 329// ANDN was absent before 2026-09-03: 108 and not 53 = 72. Note this is 330// NOT the same as xor for this pair (89), which is exactly why the pair 331// was chosen -- with a = 255 the two collapse and the KAT would be 332// vacuous against a decoder that confused them. 333func is_k_andn() -> i64 { // want 72; plain and gives 36, xor 89 334 let m: *u8 = is_guest() 335 var o: i64 = 0 336 o = is_put_be32(m, o, is_f3i(IS_OP_ALU, IS_O0, IS_A_OR, IS_G0, 108)) 337 o = is_put_be32(m, o, is_f3i(IS_OP_ALU, IS_O1, IS_A_OR, IS_G0, 53)) 338 o = is_put_be32(m, o, is_f3r(IS_OP_ALU, IS_O0, IS_A_ANDN, IS_O0, IS_O1)) 339 o = is_exit(m, o) 340 return is_run(m) 341} 342 343func is_k_mulx() -> i64 { // want 42 344 let m: *u8 = is_guest() 345 var o: i64 = 0 346 o = is_put_be32(m, o, is_f3i(IS_OP_ALU, IS_O0, IS_A_OR, IS_G0, 6)) 347 o = is_put_be32(m, o, is_f3i(IS_OP_ALU, IS_O1, IS_A_OR, IS_G0, 7)) 348 o = is_put_be32(m, o, is_f3r(IS_OP_ALU, IS_O0, IS_A_MULX, IS_O0, IS_O1)) 349 o = is_exit(m, o) 350 return is_run(m) 351} 352 353// SETHI writes imm22 SHIFTED LEFT BY TEN, which is the whole content of 354// the instruction; shifting it back by ten must return the immediate. 355func is_k_sethi() -> i64 { // want 42; a missing shift gives 0 356 let m: *u8 = is_guest() 357 var o: i64 = 0 358 o = is_put_be32(m, o, is_sethi(IS_O0, 42)) 359 o = is_put_be32(m, o, is_shx(IS_O0, IS_A_SRLX, IS_O0, 10)) 360 o = is_exit(m, o) 361 return is_run(m) 362} 363 364// --- shifts: the discrimination that matters ---------------------- 365// -16 shifted right by 60 is 15 logically and -1 arithmetically, and 366// the exit mask renders -1 as 255. The obvious version of this test 367// CANNOT discriminate: -16 >> 2 has low byte 0xFC under both. 368 369func is_k_srlx() -> i64 { // want 15; an arithmetic shift gives 255 370 let m: *u8 = is_guest() 371 var o: i64 = 0 372 o = is_put_be32(m, o, is_f3i(IS_OP_ALU, IS_O0, IS_A_OR, IS_G0, -16)) 373 o = is_put_be32(m, o, is_shx(IS_O0, IS_A_SRLX, IS_O0, 60)) 374 o = is_exit(m, o) 375 return is_run(m) 376} 377 378func is_k_srax() -> i64 { // want 255; a logical shift gives 15 379 let m: *u8 = is_guest() 380 var o: i64 = 0 381 o = is_put_be32(m, o, is_f3i(IS_OP_ALU, IS_O0, IS_A_OR, IS_G0, -16)) 382 o = is_put_be32(m, o, is_shx(IS_O0, IS_A_SRAX, IS_O0, 60)) 383 o = is_exit(m, o) 384 return is_run(m) 385} 386 387// --- control flow: the class that was entirely missing ------------- 388 389// SUM 0..9 = 45 with SUBcc setting %icc and Bicc BNE reading it. This 390// is the KAT that could not exist before: with no cc state and no 391// branch, the emulator could not run a loop at all. 392// 0x00 or %g0,0,%o0 sum 393// 0x04 or %g0,0,%o1 i 394// 0x08 or %g0,10,%o2 n 395// 0x0c add %o0,%o1,%o0 LOOP 396// 0x10 add %o1,1,%o1 397// 0x14 subcc %o1,%o2,%g0 compare, result discarded, cc kept 398// 0x18 bne LOOP disp22 = (0x0c - 0x18)/4 = -3 399// 0x1c nop delay slot 400// 0x20 exit 401func is_k_branch_loop() -> i64 { // want 45 402 let m: *u8 = is_guest() 403 var o: i64 = 0 404 o = is_put_be32(m, o, is_f3i(IS_OP_ALU, IS_O0, IS_A_OR, IS_G0, 0)) 405 o = is_put_be32(m, o, is_f3i(IS_OP_ALU, IS_O1, IS_A_OR, IS_G0, 0)) 406 o = is_put_be32(m, o, is_f3i(IS_OP_ALU, IS_O2, IS_A_OR, IS_G0, 10)) 407 o = is_put_be32(m, o, is_f3r(IS_OP_ALU, IS_O0, IS_A_ADD, IS_O0, IS_O1)) 408 o = is_put_be32(m, o, is_f3i(IS_OP_ALU, IS_O1, IS_A_ADD, IS_O1, 1)) 409 o = is_put_be32(m, o, is_f3r(IS_OP_ALU, IS_G0, IS_A_SUBCC, IS_O1, IS_O2)) 410 o = is_put_be32(m, o, is_bicc(0, IS_C_NE, 0 - 3)) 411 o = is_put_be32(m, o, is_nop()) 412 o = is_exit(m, o) 413 return is_run(m) 414} 415 416// THE DELAY SLOT. The instruction after a taken branch executes anyway; 417// an implementation that jumps straight to the target skips it. 418// 0x00 or %g0,0,%o0 419// 0x04 subcc %g0,%g0,%g0 Z=1 420// 0x08 be SKIP disp22 = (0x14 - 0x08)/4 = 3, TAKEN 421// 0x0c or %g0,7,%o0 DELAY SLOT -- must execute 422// 0x10 or %g0,99,%o0 must be skipped 423// 0x14 add %o0,35,%o0 SKIP: 7 + 35 424// 0x18 exit 425func is_k_delay_slot() -> i64 { // want 42; slot dropped 35; branch missed 134 426 let m: *u8 = is_guest() 427 var o: i64 = 0 428 o = is_put_be32(m, o, is_f3i(IS_OP_ALU, IS_O0, IS_A_OR, IS_G0, 0)) 429 o = is_put_be32(m, o, is_f3r(IS_OP_ALU, IS_G0, IS_A_SUBCC, IS_G0, IS_G0)) 430 o = is_put_be32(m, o, is_bicc(0, IS_C_E, 3)) 431 o = is_put_be32(m, o, is_f3i(IS_OP_ALU, IS_O0, IS_A_OR, IS_G0, 7)) 432 o = is_put_be32(m, o, is_f3i(IS_OP_ALU, IS_O0, IS_A_OR, IS_G0, 99)) 433 o = is_put_be32(m, o, is_f3i(IS_OP_ALU, IS_O0, IS_A_ADD, IS_O0, 35)) 434 o = is_exit(m, o) 435 return is_run(m) 436} 437 438// THE ANNUL BIT, the half of the rule an implementation usually drops: 439// on a CONDITIONAL branch a=1 annuls the delay instruction only when the 440// branch is NOT taken. 441// 0x00 or %g0,5,%o0 442// 0x04 subcc %g0,%g0,%g0 Z=1, so BNE is UNTAKEN 443// 0x08 bne,a SKIP disp22 = (0x1c - 0x08)/4 = 5 444// 0x0c or %g0,0,%o0 ANNULLED -- must NOT execute 445// 0x10 add %o0,37,%o0 5 + 37 446// 0x14 exit 447// 0x1c or %g0,200,%o0 SKIP: poison, reached only if wrongly taken 448// 0x20 exit 449func is_k_annul() -> i64 { // want 42; slot not annulled 37; taken 200 450 let m: *u8 = is_guest() 451 var o: i64 = 0 452 o = is_put_be32(m, o, is_f3i(IS_OP_ALU, IS_O0, IS_A_OR, IS_G0, 5)) 453 o = is_put_be32(m, o, is_f3r(IS_OP_ALU, IS_G0, IS_A_SUBCC, IS_G0, IS_G0)) 454 o = is_put_be32(m, o, is_bicc(1, IS_C_NE, 5)) 455 o = is_put_be32(m, o, is_f3i(IS_OP_ALU, IS_O0, IS_A_OR, IS_G0, 0)) 456 o = is_put_be32(m, o, is_f3i(IS_OP_ALU, IS_O0, IS_A_ADD, IS_O0, 37)) 457 o = is_exit(m, o) 458 o = is_put_be32(m, o, is_f3i(IS_OP_ALU, IS_O0, IS_A_OR, IS_G0, 200)) 459 o = is_exit(m, o) 460 return is_run(m) 461} 462 463// %icc VERSUS %xcc -- the V9 property no 32-bit SPARC has. 2^32 has low 464// 32 bits of ZERO and is nonzero in 64, so after SUBcc the two 465// condition-code fields DISAGREE about Z and only an emulator that keeps 466// both can answer this. 467// 0x00 or %g0,1,%o1 468// 0x04 sllx %o1,32,%o1 o1 = 2^32 469// 0x08 or %g0,0,%o0 470// 0x0c subcc %o1,%g0,%g0 icc.Z=1 xcc.Z=0 471// 0x10 be %xcc,L1 disp19 = 3, NOT taken 472// 0x14 nop 473// 0x18 add %o0,2,%o0 reached only because xcc.Z was 0 474// 0x1c be %icc,L2 disp19 = 3, TAKEN 475// 0x20 nop 476// 0x24 add %o0,100,%o0 skipped only because icc.Z was 1 477// 0x28 add %o0,40,%o0 478// 0x2c exit 479func is_k_icc_vs_xcc() -> i64 { // want 42; xcc for both 142; icc for both 40 480 let m: *u8 = is_guest() 481 var o: i64 = 0 482 o = is_put_be32(m, o, is_f3i(IS_OP_ALU, IS_O1, IS_A_OR, IS_G0, 1)) 483 o = is_put_be32(m, o, is_shx(IS_O1, IS_A_SLLX, IS_O1, 32)) 484 o = is_put_be32(m, o, is_f3i(IS_OP_ALU, IS_O0, IS_A_OR, IS_G0, 0)) 485 o = is_put_be32(m, o, is_f3r(IS_OP_ALU, IS_G0, IS_A_SUBCC, IS_O1, IS_G0)) 486 o = is_put_be32(m, o, is_bpcc(0, IS_C_E, IS_CCSEL_XCC, 1, 3)) 487 o = is_put_be32(m, o, is_nop()) 488 o = is_put_be32(m, o, is_f3i(IS_OP_ALU, IS_O0, IS_A_ADD, IS_O0, 2)) 489 o = is_put_be32(m, o, is_bpcc(0, IS_C_E, IS_CCSEL_ICC, 1, 3)) 490 o = is_put_be32(m, o, is_nop()) 491 o = is_put_be32(m, o, is_f3i(IS_OP_ALU, IS_O0, IS_A_ADD, IS_O0, 100)) 492 o = is_put_be32(m, o, is_f3i(IS_OP_ALU, IS_O0, IS_A_ADD, IS_O0, 40)) 493 o = is_exit(m, o) 494 return is_run(m) 495} 496 497// THE CARRY POLARITY. SPARC sets C on a SUBcc to the BORROW, so BLU 498// means unsigned-less-than -- the OPPOSITE of the ARM convention a 499// reader may be carrying. -1 is unsigned-GREATER than 1 and signed-LESS 500// than 1, so BCS must be untaken while BL is taken; an emulator that 501// inverted C takes the first branch and reports 32. 502// 0x00 or %g0,0,%o0 503// 0x04 or %g0,-1,%o1 504// 0x08 or %g0,1,%o2 505// 0x0c subcc %o1,%o2,%g0 506// 0x10 bcs L1 disp22 = 3, NOT taken (C=0) 507// 0x14 nop 508// 0x18 add %o0,10,%o0 509// 0x1c bl L2 disp22 = 3, TAKEN (N xor V = 1) 510// 0x20 nop 511// 0x24 add %o0,100,%o0 skipped 512// 0x28 add %o0,32,%o0 513// 0x2c exit 514func is_k_carry_polarity() -> i64 { // want 42; inverted carry 32; no signed test 142 515 let m: *u8 = is_guest() 516 var o: i64 = 0 517 o = is_put_be32(m, o, is_f3i(IS_OP_ALU, IS_O0, IS_A_OR, IS_G0, 0)) 518 o = is_put_be32(m, o, is_f3i(IS_OP_ALU, IS_O1, IS_A_OR, IS_G0, 0 - 1)) 519 o = is_put_be32(m, o, is_f3i(IS_OP_ALU, IS_O2, IS_A_OR, IS_G0, 1)) 520 o = is_put_be32(m, o, is_f3r(IS_OP_ALU, IS_G0, IS_A_SUBCC, IS_O1, IS_O2)) 521 o = is_put_be32(m, o, is_bicc(0, IS_C_CS, 3)) 522 o = is_put_be32(m, o, is_nop()) 523 o = is_put_be32(m, o, is_f3i(IS_OP_ALU, IS_O0, IS_A_ADD, IS_O0, 10)) 524 o = is_put_be32(m, o, is_bicc(0, IS_C_L, 3)) 525 o = is_put_be32(m, o, is_nop()) 526 o = is_put_be32(m, o, is_f3i(IS_OP_ALU, IS_O0, IS_A_ADD, IS_O0, 100)) 527 o = is_put_be32(m, o, is_f3i(IS_OP_ALU, IS_O0, IS_A_ADD, IS_O0, 32)) 528 o = is_exit(m, o) 529 return is_run(m) 530} 531 532// CALL sets %o7 to the address OF THE CALL, so the return is %o7 + 8 -- 533// past the call and past its delay slot. An off-by-one lands on the 534// delay slot or back on the caller and the answer changes. 535// 0x00 or %g0,0,%o0 536// 0x04 call SUB disp30 = 5 -> 0x04 + 20 = 0x18 537// 0x08 nop delay slot 538// 0x0c add %o0,2,%o0 40 + 2 539// 0x10 exit 540// 0x18 or %g0,40,%o0 SUB 541// 0x1c jmpl %o7+8,%g0 542// 0x20 nop delay slot 543func is_k_call_ret() -> i64 { // want 42 544 let m: *u8 = is_guest() 545 var o: i64 = 0 546 o = is_put_be32(m, o, is_f3i(IS_OP_ALU, IS_O0, IS_A_OR, IS_G0, 0)) 547 o = is_put_be32(m, o, is_call(5)) 548 o = is_put_be32(m, o, is_nop()) 549 o = is_put_be32(m, o, is_f3i(IS_OP_ALU, IS_O0, IS_A_ADD, IS_O0, 2)) 550 o = is_exit(m, o) 551 o = is_put_be32(m, o, is_f3i(IS_OP_ALU, IS_O0, IS_A_OR, IS_G0, 40)) 552 o = is_put_be32(m, o, is_f3i(IS_OP_ALU, IS_G0, IS_A_JMPL, IS_O7, 8)) 553 o = is_put_be32(m, o, is_nop()) 554 return is_run(m) 555} 556 557// REGISTER WINDOWS, the feature that makes this architecture what it is. 558// SAVE moves to a new window in which the caller %o registers appear as 559// %i; RESTORE reads its SOURCES in the old window and writes its 560// DESTINATION in the new one. An emulator that shifts the window before 561// reading the sources returns something else entirely. 562// 0x00 or %g0,21,%o0 563// 0x04 save %g0,%g0,%g0 cwp+1; the caller %o0 is now %i0 564// 0x08 add %i0,%i0,%l0 21 + 21 565// 0x0c restore %l0,%g0,%o0 cwp-1; reads %l0 here, writes %o0 there 566// 0x10 exit 567func is_k_save_restore() -> i64 { // want 42 568 let m: *u8 = is_guest() 569 var o: i64 = 0 570 o = is_put_be32(m, o, is_f3i(IS_OP_ALU, IS_O0, IS_A_OR, IS_G0, 21)) 571 o = is_put_be32(m, o, is_f3r(IS_OP_ALU, IS_G0, IS_A_SAVE, IS_G0, IS_G0)) 572 o = is_put_be32(m, o, is_f3r(IS_OP_ALU, IS_L0, IS_A_ADD, IS_I0, IS_I0)) 573 o = is_put_be32(m, o, is_f3r(IS_OP_ALU, IS_O0, IS_A_REST, IS_L0, IS_G0)) 574 o = is_exit(m, o) 575 return is_run(m) 576} 577 578// --- memory ------------------------------------------------------- 579 580func is_k_ldx_stx() -> i64 { // want 42 581 let m: *u8 = is_guest() 582 var o: i64 = 0 583 o = is_put_be32(m, o, is_f3i(IS_OP_ALU, IS_O0, IS_A_OR, IS_G0, 42)) 584 o = is_put_be32(m, o, is_f3i(IS_OP_MEM, IS_O0, IS_M_STX, IS_O6, 0 - 8)) 585 o = is_put_be32(m, o, is_f3i(IS_OP_ALU, IS_O0, IS_A_OR, IS_G0, 0)) 586 o = is_put_be32(m, o, is_f3i(IS_OP_MEM, IS_O0, IS_M_LDX, IS_O6, 0 - 8)) 587 o = is_exit(m, o) 588 return is_run(m) 589} 590 591// LDSW (op3 0x08) must SIGN-extend. The low byte of -1 and of 592// 0xFFFFFFFF is identical, so the exit mask hides the difference unless 593// the high half is brought down: an arithmetic shift by 32 leaves -1 for 594// the correct load and 0 for a zero-extending one. 595func is_k_ldsw_sign_extends() -> i64 { // want 42; zero-extending load 43 596 let m: *u8 = is_guest() 597 var o: i64 = 0 598 o = is_put_be32(m, o, is_f3i(IS_OP_ALU, IS_O1, IS_A_OR, IS_G0, 0 - 1)) 599 o = is_put_be32(m, o, is_f3i(IS_OP_MEM, IS_O1, IS_M_STW, IS_O6, 0 - 8)) 600 o = is_put_be32(m, o, is_f3i(IS_OP_MEM, IS_O2, IS_M_LDSW, IS_O6, 0 - 8)) 601 o = is_put_be32(m, o, is_shx(IS_O2, IS_A_SRAX, IS_O2, 32)) 602 o = is_put_be32(m, o, is_f3i(IS_OP_ALU, IS_O0, IS_A_ADD, IS_O2, 43)) 603 o = is_exit(m, o) 604 return is_run(m) 605} 606 607// LDUW (op3 0x00) must ZERO-extend -- and before 2026-09-03 op3 0x00 was 608// not decoded at all in the memory format, so this KAT reported 609// UNSUPPORTED rather than a wrong number. 610func is_k_lduw_zero_extends() -> i64 { // want 42; sign-extending load 41 611 let m: *u8 = is_guest() 612 var o: i64 = 0 613 o = is_put_be32(m, o, is_f3i(IS_OP_ALU, IS_O1, IS_A_OR, IS_G0, 0 - 1)) 614 o = is_put_be32(m, o, is_f3i(IS_OP_MEM, IS_O1, IS_M_STW, IS_O6, 0 - 8)) 615 o = is_put_be32(m, o, is_f3i(IS_OP_MEM, IS_O2, IS_M_LDUW, IS_O6, 0 - 8)) 616 o = is_put_be32(m, o, is_shx(IS_O2, IS_A_SRLX, IS_O2, 32)) 617 o = is_put_be32(m, o, is_f3i(IS_OP_ALU, IS_O0, IS_A_ADD, IS_O2, 42)) 618 o = is_exit(m, o) 619 return is_run(m) 620} 621 622func is_k_stb_ldub() -> i64 { // want 42 623 let m: *u8 = is_guest() 624 var o: i64 = 0 625 o = is_put_be32(m, o, is_f3i(IS_OP_ALU, IS_O1, IS_A_OR, IS_G0, 200)) 626 o = is_put_be32(m, o, is_f3i(IS_OP_MEM, IS_O1, IS_M_STB, IS_O6, 0 - 4)) 627 o = is_put_be32(m, o, is_f3i(IS_OP_MEM, IS_O2, IS_M_LDUB, IS_O6, 0 - 4)) 628 o = is_put_be32(m, o, is_f3i(IS_OP_ALU, IS_O0, IS_A_SUB, IS_O2, 158)) 629 o = is_exit(m, o) 630 return is_run(m) 631} 632 633// LDSB of the byte 200 is -56; shifting arithmetically by 8 leaves -1 634// for the signed load and 0 for an unsigned one. Without the shift the 635// exit mask makes the two indistinguishable -- which is how a sign 636// defect survives an obvious test. 637func is_k_ldsb_sign_extends() -> i64 { // want 42; unsigned load 43 638 let m: *u8 = is_guest() 639 var o: i64 = 0 640 o = is_put_be32(m, o, is_f3i(IS_OP_ALU, IS_O1, IS_A_OR, IS_G0, 200)) 641 o = is_put_be32(m, o, is_f3i(IS_OP_MEM, IS_O1, IS_M_STB, IS_O6, 0 - 4)) 642 o = is_put_be32(m, o, is_f3i(IS_OP_MEM, IS_O3, IS_M_LDSB, IS_O6, 0 - 4)) 643 o = is_put_be32(m, o, is_shx(IS_O3, IS_A_SRAX, IS_O3, 8)) 644 o = is_put_be32(m, o, is_f3i(IS_OP_ALU, IS_O0, IS_A_ADD, IS_O3, 43)) 645 o = is_exit(m, o) 646 return is_run(m) 647} 648 649// --- REAL non-completion controls --------------------------------- 650// These two run the EMULATOR and read its own sentinel, rather than 651// handing a constant to the classifier and calling that a control. A 652// hand-fed sentinel proves the classifier maps -1 to UNSUPPORTED; only 653// these prove the EMULATOR still says so. 654 655// FPop1 (op=2, op3 0x34) is a class this interpreter does not decode and 656// is not on any roadmap here. It must come back UNSUPPORTED and must 657// NEVER be reported as a pass. 658func is_k_unsupported_probe() -> i64 { 659 let m: *u8 = is_guest() 660 var o: i64 = 0 661 o = is_put_be32(m, o, is_f3r(IS_OP_ALU, IS_O0, IS_A_FPOP1, IS_O0, IS_O1)) 662 o = is_exit(m, o) 663 return is_run(m) 664} 665 666// A jump to 2^40, far outside a 1 MiB guest. The interpreter checks the 667// PC before fetching, so this must come back FAULT. 668func is_k_fault_probe() -> i64 { 669 let m: *u8 = is_guest() 670 var o: i64 = 0 671 o = is_put_be32(m, o, is_f3i(IS_OP_ALU, IS_O1, IS_A_OR, IS_G0, 1)) 672 o = is_put_be32(m, o, is_shx(IS_O1, IS_A_SLLX, IS_O1, 40)) 673 o = is_put_be32(m, o, is_f3i(IS_OP_ALU, IS_G0, IS_A_JMPL, IS_O1, 0)) 674 o = is_put_be32(m, o, is_nop()) 675 o = is_exit(m, o) 676 return is_run(m) 677} 678 679// ===== ratchet ===================================================== 680// One line: "sparc64 <passed>". UNSEEDED is a NAMED third state so 681// adopting this fence cannot fail closed on first sight. Polarity is 682// GOOD-WHEN-HIGHER, the inverse of an offender-count ratchet: it 683// TIGHTENS on a rise and must NEVER rewrite its baseline on a fall, 684// because rewriting on a fall is how a ratchet launders itself green. 685// It is a SEPARATE file from isa_conform.ratchet on purpose -- that one 686// belongs to nx_isa_conform_gate and two writers on one baseline is the 687// duplicate-ruler defect. 688const IS_RATCHET: *u8 = "knowledge/status/isa_sparc64.ratchet" 689const IS_ARCH: *u8 = "sparc64" 690 691func is_match_at(buf: *u8, n: i64, p: i64, s: *u8) -> i64 { 692 var j: i64 = 0 693 var ok: i64 = 1 694 while s[j] != (0 as u8) { 695 if (p + j) >= n { ok = 0 } 696 if ok == 1 { 697 if buf[p + j] != s[j] { ok = 0 } 698 } 699 j = j + 1 700 } 701 if ok == 0 { return -1 } 702 return j 703} 704 705func is_digits_at(buf: *u8, n: i64, p0: i64) -> i64 { 706 var v: i64 = 0 707 var any: i64 = 0 708 var p: i64 = p0 709 var go: i64 = 1 710 while go == 1 { 711 if p >= n { go = 0 } 712 if go == 1 { 713 let c: i64 = buf[p] & 0xff 714 if c < IS_DIG_LO { go = 0 } 715 if go == 1 { 716 if c > IS_DIG_HI { go = 0 } 717 } 718 if go == 1 { 719 v = v * 10 + (c - IS_DIG_LO) 720 any = 1 721 p = p + 1 722 } 723 } 724 } 725 if any == 0 { return -1 } 726 return v 727} 728 729func is_ratchet_read() -> i64 { 730 let lenbox: *i64 = sys_mmap(16) as *i64 731 let buf: *u8 = sys_read_file(IS_RATCHET, lenbox) 732 if (buf as i64) == 0 { return IS_UNSEEDED } 733 let n: i64 = lenbox[0] 734 var i: i64 = 0 735 var found: i64 = IS_UNSEEDED 736 while i < n { 737 if found < 0 { 738 let mm: i64 = is_match_at(buf, n, i, IS_ARCH) 739 if mm > 0 { 740 if (i + mm) < n { 741 if buf[i + mm] == (IS_SPACE as u8) { 742 found = is_digits_at(buf, n, i + mm + 1) 743 } 744 } 745 } 746 } 747 var adv: i64 = 1 748 while adv == 1 { 749 if i >= n { adv = 0 } 750 if adv == 1 { 751 if buf[i] == (IS_NL as u8) { adv = 0 } 752 i = i + 1 753 } 754 } 755 } 756 return found 757} 758 759func is_ratchet_write(np: i64) -> i64 { 760 let d: *u8 = sys_mmap(64) 761 var o: i64 = 0 762 o = gv_cat(d, o, "sparc64 " as *u8) 763 o = gv_catn(d, o, np) 764 d[o] = IS_NL as u8 765 o = o + 1 766 let fd: i64 = sys_openat_wr(IS_RATCHET, IS_MODE_FILE) 767 if fd < 0 { return -1 } 768 sys_write(fd, d, o) 769 sys_fsync(fd) 770 sys_close(fd) 771 return o 772} 773 774// ===== main ======================================================== 775 776func main(argc: i64, argv: *i64) -> i64 { 777 gv_head("nx_isa_sparc64_gate -- sovereign SPARC V9 emulator conformance, KATs encoded from the SPARC V9 manual" as *u8) 778 let ctr: *i64 = gv_ctr() 779 780 let bx: *i64 = sys_mmap(IS_BOX_BYTES) as *i64 781 let nbuf: *u8 = sys_mmap(IS_NB_BYTES) 782 nbuf[0] = 0 as u8 783 bx[IS_NB_OFF] = 0 784 bx[IS_NB_PTR] = nbuf as i64 785 786 gv_puts("dispatch=emu_sparc64_run_mem (the surface all 10 emulators share; _load_elf exists here but on only 6 of 10)" as *u8) 787 is_nl() 788 gv_puts("outcomes are FIVE-WAY: PASS / WRONG-ANSWER / UNSUPPORTED-instruction-class / RAN-OFF-END / FAULT" as *u8) 789 is_nl() 790 gv_puts("SCOPE NOTE, measured in nx_emu_sparc64 and not assumed: result is initialised to SPE_FAULT and there is NO -2 sentinel," as *u8) 791 is_nl() 792 gv_puts("so on THIS arch a run-off-the-end and a step-budget exhaustion are INDISTINGUISHABLE from a bad PC. rv64 and mips64 do carry the third code." as *u8) 793 is_nl() 794 is_nl() 795 796 gv_puts("[sparc64] SPARC V9 -- BIG-ENDIAN, fixed 32-bit words, DELAY SLOTS, REGISTER WINDOWS, two condition-code fields" as *u8) 797 is_nl() 798 var sp_pass: i64 = 0 799 var sp_tot: i64 = 0 800 var o: i64 = 0 801 802 o = is_report("imm-arith" as *u8, is_k_imm_arith(), 42, bx) 803 sp_tot = sp_tot + 1 804 if o == IS_O_PASS { sp_pass = sp_pass + 1 } 805 gv_check("isa-sparc64-imm-arith" as *u8, o == IS_O_PASS, ctr) 806 807 o = is_report("sub-reg-operand-order" as *u8, is_k_sub_reg(), 42, bx) 808 sp_tot = sp_tot + 1 809 if o == IS_O_PASS { sp_pass = sp_pass + 1 } 810 gv_check("isa-sparc64-sub-reg-operand-order" as *u8, o == IS_O_PASS, ctr) 811 812 o = is_report("xor" as *u8, is_k_xor(), 89, bx) 813 sp_tot = sp_tot + 1 814 if o == IS_O_PASS { sp_pass = sp_pass + 1 } 815 gv_check("isa-sparc64-xor" as *u8, o == IS_O_PASS, ctr) 816 817 o = is_report("andn-distinct-from-and-and-xor" as *u8, is_k_andn(), 72, bx) 818 sp_tot = sp_tot + 1 819 if o == IS_O_PASS { sp_pass = sp_pass + 1 } 820 gv_check("isa-sparc64-andn-distinct-from-and-and-xor" as *u8, o == IS_O_PASS, ctr) 821 822 o = is_report("mulx" as *u8, is_k_mulx(), 42, bx) 823 sp_tot = sp_tot + 1 824 if o == IS_O_PASS { sp_pass = sp_pass + 1 } 825 gv_check("isa-sparc64-mulx" as *u8, o == IS_O_PASS, ctr) 826 827 o = is_report("sethi-shifts-imm22-left-by-ten" as *u8, is_k_sethi(), 42, bx) 828 sp_tot = sp_tot + 1 829 if o == IS_O_PASS { sp_pass = sp_pass + 1 } 830 gv_check("isa-sparc64-sethi-shifts-imm22-left-by-ten" as *u8, o == IS_O_PASS, ctr) 831 832 o = is_report("srlx-is-logical" as *u8, is_k_srlx(), 15, bx) 833 sp_tot = sp_tot + 1 834 if o == IS_O_PASS { sp_pass = sp_pass + 1 } 835 gv_check("isa-sparc64-srlx-is-logical" as *u8, o == IS_O_PASS, ctr) 836 837 o = is_report("srax-is-arithmetic" as *u8, is_k_srax(), 255, bx) 838 sp_tot = sp_tot + 1 839 if o == IS_O_PASS { sp_pass = sp_pass + 1 } 840 gv_check("isa-sparc64-srax-is-arithmetic" as *u8, o == IS_O_PASS, ctr) 841 842 o = is_report("branch-loop-subcc-plus-bicc" as *u8, is_k_branch_loop(), 45, bx) 843 sp_tot = sp_tot + 1 844 if o == IS_O_PASS { sp_pass = sp_pass + 1 } 845 gv_check("isa-sparc64-branch-loop-subcc-plus-bicc" as *u8, o == IS_O_PASS, ctr) 846 847 o = is_report("branch-delay-slot-executes" as *u8, is_k_delay_slot(), 42, bx) 848 sp_tot = sp_tot + 1 849 if o == IS_O_PASS { sp_pass = sp_pass + 1 } 850 gv_check("isa-sparc64-branch-delay-slot-executes" as *u8, o == IS_O_PASS, ctr) 851 852 o = is_report("annul-bit-skips-slot-only-when-untaken" as *u8, is_k_annul(), 42, bx) 853 sp_tot = sp_tot + 1 854 if o == IS_O_PASS { sp_pass = sp_pass + 1 } 855 gv_check("isa-sparc64-annul-bit-skips-slot-only-when-untaken" as *u8, o == IS_O_PASS, ctr) 856 857 o = is_report("bpcc-icc-and-xcc-are-separate-fields" as *u8, is_k_icc_vs_xcc(), 42, bx) 858 sp_tot = sp_tot + 1 859 if o == IS_O_PASS { sp_pass = sp_pass + 1 } 860 gv_check("isa-sparc64-bpcc-icc-and-xcc-are-separate-fields" as *u8, o == IS_O_PASS, ctr) 861 862 o = is_report("subcc-carry-is-a-borrow" as *u8, is_k_carry_polarity(), 42, bx) 863 sp_tot = sp_tot + 1 864 if o == IS_O_PASS { sp_pass = sp_pass + 1 } 865 gv_check("isa-sparc64-subcc-carry-is-a-borrow" as *u8, o == IS_O_PASS, ctr) 866 867 o = is_report("call-sets-o7-to-its-own-address" as *u8, is_k_call_ret(), 42, bx) 868 sp_tot = sp_tot + 1 869 if o == IS_O_PASS { sp_pass = sp_pass + 1 } 870 gv_check("isa-sparc64-call-sets-o7-to-its-own-address" as *u8, o == IS_O_PASS, ctr) 871 872 o = is_report("save-restore-window-overlap" as *u8, is_k_save_restore(), 42, bx) 873 sp_tot = sp_tot + 1 874 if o == IS_O_PASS { sp_pass = sp_pass + 1 } 875 gv_check("isa-sparc64-save-restore-window-overlap" as *u8, o == IS_O_PASS, ctr) 876 877 o = is_report("ldx-stx-roundtrip" as *u8, is_k_ldx_stx(), 42, bx) 878 sp_tot = sp_tot + 1 879 if o == IS_O_PASS { sp_pass = sp_pass + 1 } 880 gv_check("isa-sparc64-ldx-stx-roundtrip" as *u8, o == IS_O_PASS, ctr) 881 882 o = is_report("ldsw-sign-extends" as *u8, is_k_ldsw_sign_extends(), 42, bx) 883 sp_tot = sp_tot + 1 884 if o == IS_O_PASS { sp_pass = sp_pass + 1 } 885 gv_check("isa-sparc64-ldsw-sign-extends" as *u8, o == IS_O_PASS, ctr) 886 887 o = is_report("lduw-zero-extends" as *u8, is_k_lduw_zero_extends(), 42, bx) 888 sp_tot = sp_tot + 1 889 if o == IS_O_PASS { sp_pass = sp_pass + 1 } 890 gv_check("isa-sparc64-lduw-zero-extends" as *u8, o == IS_O_PASS, ctr) 891 892 o = is_report("stb-ldub-roundtrip" as *u8, is_k_stb_ldub(), 42, bx) 893 sp_tot = sp_tot + 1 894 if o == IS_O_PASS { sp_pass = sp_pass + 1 } 895 gv_check("isa-sparc64-stb-ldub-roundtrip" as *u8, o == IS_O_PASS, ctr) 896 897 o = is_report("ldsb-sign-extends" as *u8, is_k_ldsb_sign_extends(), 42, bx) 898 sp_tot = sp_tot + 1 899 if o == IS_O_PASS { sp_pass = sp_pass + 1 } 900 gv_check("isa-sparc64-ldsb-sign-extends" as *u8, o == IS_O_PASS, ctr) 901 902 // ---- the fixture must have REACHED the emulator --------------- 903 // Asserting an outcome without asserting the fixture ran is how four 904 // vacuous fixtures shipped in this estate in a single day. 905 is_nl() 906 gv_check("fixture-reached-sparc64-emulator-at-all" as *u8, sp_pass > 0, ctr) 907 908 // ---- negative controls ---------------------------------------- 909 // The first three interrogate the CLASSIFIER: if is_classify were 910 // stubbed to return PASS, every green above would be fake and only 911 // these would notice. The last two interrogate the EMULATOR, by 912 // running real machine code whose only correct answers are a 913 // refusal and a fault -- a hand-fed sentinel cannot do that. 914 let ncw: i64 = is_classify(is_k_imm_arith(), 41) 915 gv_puts(" neg-control sparc64.imm-arith against a deliberately wrong want=41 -> " as *u8) 916 gv_puts(is_outcome_name(ncw)) 917 is_nl() 918 gv_check("neg-control-wrong-expectation-must-not-pass" as *u8, ncw == IS_O_WRONG, ctr) 919 920 let ncu: i64 = is_classify(IS_UNSUPPORTED, 42) 921 gv_check("neg-control-unsupported-sentinel-never-reads-as-pass" as *u8, ncu == IS_O_UNSUP, ctr) 922 923 let ncf: i64 = is_classify(IS_FAULT, 42) 924 gv_check("neg-control-fault-sentinel-never-reads-as-pass" as *u8, ncf == IS_O_FAULT, ctr) 925 926 let rawu: i64 = is_k_unsupported_probe() 927 let ru: i64 = is_classify(rawu, 42) 928 gv_puts(" neg-control-live sparc64.fpop1 raw=" as *u8) 929 gv_num(rawu) 930 gv_puts(" -> " as *u8) 931 gv_puts(is_outcome_name(ru)) 932 is_nl() 933 gv_check("neg-control-live-undecoded-class-reports-UNSUPPORTED-not-a-number" as *u8, ru == IS_O_UNSUP, ctr) 934 935 let rawf: i64 = is_k_fault_probe() 936 let rf: i64 = is_classify(rawf, 42) 937 gv_puts(" neg-control-live sparc64.jump-to-2^40 raw=" as *u8) 938 gv_num(rawf) 939 gv_puts(" -> " as *u8) 940 gv_puts(is_outcome_name(rf)) 941 is_nl() 942 gv_check("neg-control-live-out-of-range-pc-reports-FAULT-not-a-number" as *u8, rf == IS_O_FAULT, ctr) 943 944 // ---- coverage, published as a NUMBER not a verdict ------------- 945 is_nl() 946 gv_puts("coverage sparc64=" as *u8) 947 gv_num(sp_pass) 948 gv_puts("/" as *u8) 949 gv_num(sp_tot) 950 gv_puts(" arch=sparc64 endianness=big word=32 delay_slots=yes register_windows=yes cc_fields=icc+xcc" as *u8) 951 is_nl() 952 gv_puts("classes measured: imm-arith sub logic andn mulx sethi shift-logical shift-arith branch-loop delay-slot annul bpcc-icc-vs-xcc carry-borrow call-jmpl save-restore ldx-stx ldsw lduw stb-ldub ldsb" as *u8) 953 is_nl() 954 gv_puts("classes STILL UNDECODED by nx_emu_sparc64 and therefore NOT claimed: floating point, BPr branch-on-register, MOVcc, RETURN, LDSTUB/SWAP/CAS, alternate-space loads, Tcc other than an unconditional trap, and the window-overflow/underflow traps." as *u8) 955 is_nl() 956 957 // ---- ratchet --------------------------------------------------- 958 let base: i64 = is_ratchet_read() 959 gv_puts("ratchet sparc64 base=" as *u8) 960 gv_num(base) 961 gv_puts(" now=" as *u8) 962 gv_num(sp_pass) 963 gv_puts(" (base=-1 is UNSEEDED, a named state: first sight seeds, so adoption cannot fail closed)" as *u8) 964 is_nl() 965 966 var no_regress: i64 = 1 967 if base >= 0 { 968 if sp_pass < base { no_regress = 0 } 969 } 970 971 // Seed when unseeded; tighten only on a rise; NEVER rewrite on a 972 // fall, which is the direction that would launder a regression. 973 var wrote: i64 = 0 974 if no_regress == 1 { 975 if base < 0 { wrote = is_ratchet_write(sp_pass) } 976 if wrote == 0 { 977 if sp_pass > base { wrote = is_ratchet_write(sp_pass) } 978 } 979 } 980 gv_puts("ratchet_bytes_written=" as *u8) 981 gv_num(wrote) 982 gv_puts(" (0 = held: neither seeded nor tightened this run)" as *u8) 983 is_nl() 984 985 // This dialect has no or-operator, so the laundering condition is 986 // COMPUTED rather than expressed. 987 var laundered: i64 = 0 988 if no_regress == 0 { 989 if wrote > 0 { laundered = 1 } 990 } 991 992 gv_check("ratchet-sparc64-did-not-regress" as *u8, no_regress == 1, ctr) 993 gv_check("ratchet-never-rewritten-on-a-regression" as *u8, laundered == 0, ctr) 994 995 // THE WORKLIST, LAST, where a tailing caller can still see it. 996 gv_puts("NONPASS: " as *u8) 997 if bx[IS_NB_OFF] == 0 { 998 gv_puts("none -- every sparc64 KAT matched its manual-derived expectation" as *u8) 999 } 1000 if bx[IS_NB_OFF] > 0 { 1001 gv_puts(bx[IS_NB_PTR] as *u8) 1002 } 1003 is_nl() 1004 1005 return gv_verdict("nx_isa_sparc64_gate" as *u8, ctr, 1006 "sparc64 conformance measured by running SPARC V9 machine code against manual-derived expectations with five-way outcomes, live UNSUPPORTED and FAULT controls taken from the emulator itself, coverage published as a number, fenced by a good-when-higher ratchet that seeds on first sight and never rewrites on a fall" as *u8) 1007}