code wiki / (root) / nx_isa_s390x_gate.nx

nx_isa_s390x_gate.nx source

↩ module page · 945 lines · 38247 B

1// nx_isa_s390x_gate.nx -- THE s390x CONFORMANCE RULER. 2// 3// WHY IT EXISTS: nx_isa_conform_gate measures rv64, mips64, arm64, riscv32 and 4// ppc64le and names s390x in its own output as UNMEASURED. An emulator nobody 5// runs machine code through is a claim, not a capability. This organ decides 6// "we emulate IBM z/Architecture" by RUNNING s390x machine code, in process, 7// against values taken FROM THE z/ARCHITECTURE PRINCIPLES OF OPERATION -- never 8// from nx_emu_s390x.nx itself, which would calibrate the ruler to its subject. 9// 10// SEPARATE ORGAN, NOT A PATCH TO THE INCUMBENT: five other ISA lanes are 11// editing the estate at the same time, and concurrent edits to one file 12// silently clobber each other here. The SHAPE is copied from 13// nx_isa_conform_gate (word emitters, five-way classification, a value-printing 14// report, negative controls, a fixture-reached tooth, a good-when-higher 15// ratchet); nothing is imported from it, so neither can break the other. 16// 17// FIVE OUTCOMES, NEVER ONE: 18// PASS ran and matched the manual-derived expectation 19// WRONG-ANSWER ran to completion and computed the wrong value 20// UNSUPPORTED hit an instruction class the emulator does not implement 21// RAN-OFF-END the interpreter loop ended without an exit syscall 22// FAULT bad pc, out-of-range access, or the step budget 23// Collapsing these into FAIL is what makes a gap unactionable: an unimplemented 24// instruction class and a miscomputed result need OPPOSITE fixes. 25// 26// WHAT MAKES s390x DIFFERENT FROM EVERY OTHER LANE ON THIS BOARD: it is the 27// only VARIABLE-LENGTH ISA here. The instruction length comes from the top two 28// bits of byte 0 (00 -> 2 bytes, 01/10 -> 4, 11 -> 6), so the emitter cannot be 29// a single put_word helper and every branch displacement is a HALFWORD count, 30// not a byte count. A fixture that assumed a fixed width would mis-place every 31// label and the resulting garbage would read as the emulator being incomplete. 32// So there is an explicit tooth asserting the emitter really returns 2, 4 and 6. 33// 34// BIG-ENDIAN. Every field below is written most-significant byte first. Getting 35// this wrong is the silent-fixture defect: the decoder reads garbage, reports 36// UNSUPPORTED, and the FIXTURE's bug wears the emulator's name. 37// 38// THE TWO NEGATIVE CONTROLS THAT ARE NOT DECORATION: nx_emu_s390x used to set 39// its "handled" flag for a whole opcode family BEFORE looking at the extended 40// opcode, so an unimplemented member of that family executed as a silent 41// no-operation and the program ran on to a wrong answer. An unknown SVC number 42// did the same. Both are the UNSUPPORTED-read-as-something-else collapse, so 43// both are bitten here with real guest programs rather than with a synthetic 44// sentinel: a run containing B90F (LRVGR, deliberately not implemented) and a 45// run containing SVC 99 must each come back UNSUPPORTED. If a later generation 46// implements LRVGR, THIS CONTROL MUST BE RE-POINTED at another unimplemented 47// opcode -- a control whose subject has been implemented is a tooth that can 48// never fire again, and it would go on reading green forever. 49// 50// WHY A RATCHET AND NOT A BAR: the emulator is honestly incomplete (no packed 51// decimal, no vector facility, no access registers, no floating point). A gate 52// demanding completeness would be permanently RED and a permanently RED 53// detector is one everyone learns to ignore. The VERDICT is "s390x did not 54// regress against its banked score"; the absolute coverage is PUBLISHED AS A 55// NUMBER beside it. The number is the worklist; the verdict is the fence. 56// 57// license_tier: ORIGINAL 58 59import "nx_gate_verdict.nx" 60import "nx_syscalls.nx" 61import "nx_emu_s390x.nx" 62 63// ---- guest layout THIS ORGAN owns (never read from the emulator) ---- 64const SX_GUEST_BYTES: i64 = 1048576 // 1 MiB guest image 65const SX_ENTRY: i64 = 0 // KAT code is loaded at guest vaddr 0 66const SX_SP: i64 = 983040 // 960 KiB: above the code, inside the image 67const SX_SCRATCH: i64 = 512 // load/store KATs use this guest address 68const SX_MODE_FILE: i64 = 420 // 0644. 420 and 0x1a4 are the SAME constant 69 // and a sweep that greps one misses the other. 70 71// ---- non-completion codes the interpreter returns ------------------- 72// Read out of nx_emu_s390x.nx's own constant table, not guessed: 73// SZ_UNSUPPORTED = -1, SZ_RANOFF = -2, SZ_FAULT = -3. Every exit path masks 74// with 255, so a real answer is 0..255 and cannot collide with these. 75const SX_UNSUPPORTED: i64 = -1 76const SX_RANOFF: i64 = -2 77const SX_FAULT: i64 = -3 78 79// ---- outcome classes ---- 80const SX_O_PASS: i64 = 0 81const SX_O_WRONG: i64 = 1 82const SX_O_UNSUP: i64 = 2 83const SX_O_FAULT: i64 = 3 84const SX_O_RANOFF: i64 = 4 85 86// ---- s390x opcode bytes, from the PoP opcode tables ----------------- 87const SX_OP_BCR: i64 = 0x07 88const SX_OP_SVC: i64 = 0x0A 89const SX_OP_RI: i64 = 0xA7 90const SX_OP_RRE: i64 = 0xB9 91const SX_OP_LA: i64 = 0x41 92const SX_OP_RXY: i64 = 0xE3 93const SX_OP_RSY: i64 = 0xEB 94const SX_OP_RIL: i64 = 0xC0 95 96// RI extended opcodes (the low nibble of byte 1). THE ORDER OF THIS TABLE IS 97// THE WHOLE POINT: A7xA is AHI (32-bit) and A7xB is AGHI (64-bit), and the 98// previous emulator generation had AGHI at A7xA with no A7xB at all. 99const SX_RI_BRC: i64 = 0x4 100const SX_RI_BRAS: i64 = 0x5 101const SX_RI_BRCT: i64 = 0x6 102const SX_RI_BRCTG: i64 = 0x7 103const SX_RI_LHI: i64 = 0x8 104const SX_RI_LGHI: i64 = 0x9 105const SX_RI_AHI: i64 = 0xA 106const SX_RI_AGHI: i64 = 0xB 107const SX_RI_MGHI: i64 = 0xD 108const SX_RI_CHI: i64 = 0xE 109const SX_RI_CGHI: i64 = 0xF 110 111// RRE second opcode byte 112const SX_RRE_LGR: i64 = 0x04 113const SX_RRE_AGR: i64 = 0x08 114const SX_RRE_SGR: i64 = 0x09 115const SX_RRE_MSGR: i64 = 0x0C 116const SX_RRE_CGR: i64 = 0x20 117const SX_RRE_CLGR: i64 = 0x21 118const SX_RRE_NGR: i64 = 0x80 119const SX_RRE_OGR: i64 = 0x81 120const SX_RRE_XGR: i64 = 0x82 121// LRVGR. Present in the architecture, DELIBERATELY not implemented by 122// nx_emu_s390x, and used below as a live UNSUPPORTED control. 123const SX_RRE_LRVGR: i64 = 0x0F 124 125// RXY / RSY trailing opcode byte 126const SX_RXY_LG: i64 = 0x04 127const SX_RXY_STG: i64 = 0x24 128const SX_RSY_SRAG: i64 = 0x0A 129const SX_RSY_SRLG: i64 = 0x0C 130const SX_RSY_SLLG: i64 = 0x0D 131 132// RIL extended opcodes 133const SX_RIL_BRCL: i64 = 0x4 134const SX_RIL_BRASL: i64 = 0x5 135 136// Branch masks. Bit 8 selects CC0, 4 CC1, 2 CC2, 1 CC3. 137const SX_M_ALWAYS: i64 = 15 138const SX_M_EQ: i64 = 8 139const SX_M_NE: i64 = 7 140const SX_M_LOW: i64 = 4 141const SX_M_HIGH: i64 = 2 142 143// Linux s390x syscall numbers used by the fixtures. 144const SX_SVC_EXIT: i64 = 1 145const SX_SVC_UNKNOWN: i64 = 99 // deliberately not implemented 146 147// Register numbers the KATs use by name rather than by digit. 148const SX_R0: i64 = 0 149const SX_R2: i64 = 2 // the exit-status register on Linux s390x 150const SX_R3: i64 = 3 151const SX_R4: i64 = 4 152const SX_R5: i64 = 5 153const SX_R14: i64 = 14 // the architected return-address register 154 155// Instruction lengths, so the variable-length tooth compares against named 156// values rather than against three bare digits. 157const SX_LEN_RR: i64 = 2 158const SX_LEN_RI: i64 = 4 159const SX_LEN_RIL: i64 = 6 160 161// The non-PASS worklist buffer. A count without a worklist is not actionable, 162// and a worklist printed only in the BODY is lost the moment a caller tails the 163// output, so it is accumulated here and printed at the END. 164const SX_NB_BYTES: i64 = 2048 165const SX_NB_OFF: i64 = 0 166const SX_NB_PTR: i64 = 1 167const SX_NB_ROW_MAX: i64 = 256 168const SX_BOX_BYTES: i64 = 32 169 170const SX_NL: i64 = 10 171const SX_SPACE: i64 = 32 172const SX_DIG_LO: i64 = 48 173const SX_DIG_HI: i64 = 57 174const SX_UNSEEDED: i64 = -1 175 176const SX_RATCHET: *u8 = "knowledge/status/isa_s390x.ratchet" 177 178// A bare newline inside a string literal is ambiguous to this lexer, so the 179// byte is CONSTRUCTED. 180func sx_nl() -> i64 { 181 let b: *u8 = sys_mmap(8) 182 b[0] = SX_NL as u8 183 sys_write(1, b, 1) 184 return 0 185} 186 187func sx_guest() -> *u8 { return sys_mmap(SX_GUEST_BYTES) } 188 189// ===== the variable-length BIG-ENDIAN emitters ================== 190// Each returns the NEXT offset, so a KAT reads as a program listing and the 191// instruction widths are carried by the encoder instead of hand-counted at 192// every call site -- a hand-counted length beside an encoding is a second copy 193// of that encoding's shape and the two drift silently. 194 195// RR, 2 bytes: op(8) r1(4) r2(4) 196func sx_rr(c: *u8, off: i64, op: i64, r1: i64, r2: i64) -> i64 { 197 c[off] = op & 0xff 198 c[off + 1] = ((r1 << 4) | r2) & 0xff 199 return off + SX_LEN_RR 200} 201 202// SVC, 2 bytes: 0A ii (ii is an 8-bit syscall number, NOT two nibbles) 203func sx_svc(c: *u8, off: i64, imm: i64) -> i64 { 204 c[off] = SX_OP_SVC & 0xff 205 c[off + 1] = imm & 0xff 206 return off + SX_LEN_RR 207} 208 209// RI, 4 bytes: A7 r1(4) op2(4) i2(16 signed, big-endian) 210func sx_ri(c: *u8, off: i64, r1: i64, op2: i64, i2: i64) -> i64 { 211 c[off] = SX_OP_RI & 0xff 212 c[off + 1] = ((r1 << 4) | op2) & 0xff 213 c[off + 2] = (i2 >> 8) & 0xff 214 c[off + 3] = (i2) & 0xff 215 return off + SX_LEN_RI 216} 217 218// RRE, 4 bytes: B9 op2(8) 00 r1(4) r2(4) 219func sx_rre(c: *u8, off: i64, op2: i64, r1: i64, r2: i64) -> i64 { 220 c[off] = SX_OP_RRE & 0xff 221 c[off + 1] = op2 & 0xff 222 c[off + 2] = 0 as u8 223 c[off + 3] = ((r1 << 4) | r2) & 0xff 224 return off + SX_LEN_RI 225} 226 227// RX-a, 4 bytes: 41 r1(4) x2(4) b2(4) d2(12 unsigned) 228func sx_rx(c: *u8, off: i64, r1: i64, x2: i64, b2: i64, d2: i64) -> i64 { 229 c[off] = SX_OP_LA & 0xff 230 c[off + 1] = ((r1 << 4) | x2) & 0xff 231 c[off + 2] = ((b2 << 4) | ((d2 >> 8) & 0xF)) & 0xff 232 c[off + 3] = (d2) & 0xff 233 return off + SX_LEN_RI 234} 235 236// RXY-a, 6 bytes: E3 r1(4) x2(4) b2(4) DL(12) DH(8) op2(8) 237func sx_rxy(c: *u8, off: i64, r1: i64, x2: i64, b2: i64, d2: i64, op2: i64) -> i64 { 238 c[off] = SX_OP_RXY & 0xff 239 c[off + 1] = ((r1 << 4) | x2) & 0xff 240 c[off + 2] = ((b2 << 4) | ((d2 >> 8) & 0xF)) & 0xff 241 c[off + 3] = (d2) & 0xff 242 c[off + 4] = (d2 >> 12) & 0xff 243 c[off + 5] = op2 & 0xff 244 return off + SX_LEN_RIL 245} 246 247// RSY-a, 6 bytes: EB r1(4) r3(4) b2(4) DL(12) DH(8) op2(8) 248func sx_rsy(c: *u8, off: i64, r1: i64, r3: i64, b2: i64, d2: i64, op2: i64) -> i64 { 249 c[off] = SX_OP_RSY & 0xff 250 c[off + 1] = ((r1 << 4) | r3) & 0xff 251 c[off + 2] = ((b2 << 4) | ((d2 >> 8) & 0xF)) & 0xff 252 c[off + 3] = (d2) & 0xff 253 c[off + 4] = (d2 >> 12) & 0xff 254 c[off + 5] = op2 & 0xff 255 return off + SX_LEN_RIL 256} 257 258// RIL, 6 bytes: C0 r1(4) op2(4) i2(32 signed, big-endian). 259// For the branch forms i2 is a HALFWORD count: target = pc + 2 * i2. 260func sx_ril(c: *u8, off: i64, r1: i64, op2: i64, i2: i64) -> i64 { 261 c[off] = SX_OP_RIL & 0xff 262 c[off + 1] = ((r1 << 4) | op2) & 0xff 263 c[off + 2] = (i2 >> 24) & 0xff 264 c[off + 3] = (i2 >> 16) & 0xff 265 c[off + 4] = (i2 >> 8) & 0xff 266 c[off + 5] = (i2) & 0xff 267 return off + SX_LEN_RIL 268} 269 270func sx_run(m: *u8) -> i64 { return emu_s390x_run_mem(m, SX_GUEST_BYTES, SX_ENTRY, SX_SP) } 271 272// ===== outcome classification =================================== 273 274func sx_classify(got: i64, want: i64) -> i64 { 275 if got == SX_UNSUPPORTED { return SX_O_UNSUP } 276 if got == SX_RANOFF { return SX_O_RANOFF } 277 if got == SX_FAULT { return SX_O_FAULT } 278 if got == want { return SX_O_PASS } 279 return SX_O_WRONG 280} 281 282func sx_outcome_name(o: i64) -> *u8 { 283 if o == SX_O_PASS { return "PASS" as *u8 } 284 if o == SX_O_WRONG { return "WRONG-ANSWER" as *u8 } 285 if o == SX_O_UNSUP { return "UNSUPPORTED-instruction-class" as *u8 } 286 if o == SX_O_RANOFF { return "RAN-OFF-END-no-exit-syscall" as *u8 } 287 return "FAULT-bad-pc-or-step-budget" as *u8 288} 289 290// Print the VALUES, not just the verdict: every vacuous tooth ever caught in 291// this estate was caught by a diagnostic dump and never by a verdict vector. 292func sx_report(kat: *u8, got: i64, want: i64, bx: *i64) -> i64 { 293 let o: i64 = sx_classify(got, want) 294 gv_puts(" KAT s390x." as *u8) 295 gv_puts(kat) 296 gv_puts(" got=" as *u8) 297 gv_num(got) 298 gv_puts(" want=" as *u8) 299 gv_num(want) 300 gv_puts(" " as *u8) 301 gv_puts(sx_outcome_name(o)) 302 sx_nl() 303 if o != SX_O_PASS { 304 let nb: *u8 = bx[SX_NB_PTR] as *u8 305 var p: i64 = bx[SX_NB_OFF] 306 if p < (SX_NB_BYTES - SX_NB_ROW_MAX) { 307 p = gv_cat(nb, p, "s390x." as *u8) 308 p = gv_cat(nb, p, kat) 309 p = gv_cat(nb, p, "=" as *u8) 310 p = gv_cat(nb, p, sx_outcome_name(o)) 311 p = gv_cat(nb, p, "(got=" as *u8) 312 p = gv_catn(nb, p, got) 313 p = gv_cat(nb, p, " want=" as *u8) 314 p = gv_catn(nb, p, want) 315 p = gv_cat(nb, p, ") " as *u8) 316 nb[p] = 0 as u8 317 bx[SX_NB_OFF] = p 318 } 319 } 320 return o 321} 322 323// ===== the KATs ================================================= 324// Every expectation below is derived from the z/Architecture Principles of 325// Operation instruction definitions, and every program ends in SVC 1 so it 326// returns a REAL exit status. The exit status is masked to 8 bits by the ABI, 327// so no two expectations in this file are congruent modulo 256. 328 329// LGHI r,imm + AGR: the two classes the previous generation already had. 330// A regression here indicts a change rather than a missing feature. 331func sx_k_imm_arith() -> i64 { 332 let m: *u8 = sx_guest() 333 var o: i64 = 0 334 o = sx_ri(m, o, SX_R2, SX_RI_LGHI, 40) 335 o = sx_ri(m, o, SX_R3, SX_RI_LGHI, 2) 336 o = sx_rre(m, o, SX_RRE_AGR, SX_R2, SX_R3) // 40 + 2 337 o = sx_svc(m, o, SX_SVC_EXIT) 338 return sx_run(m) 339} 340 341// AGHI at its ARCHITECTED extended opcode A7xB. The previous generation put a 342// 64-bit add at A7xA and had no A7xB case at all, so this exact program came 343// back 40: the instruction assembled correctly and executed as nothing. 344func sx_k_aghi_opcode() -> i64 { 345 let m: *u8 = sx_guest() 346 var o: i64 = 0 347 o = sx_ri(m, o, SX_R2, SX_RI_LGHI, 40) 348 o = sx_ri(m, o, SX_R2, SX_RI_AGHI, 2) 349 o = sx_svc(m, o, SX_SVC_EXIT) 350 return sx_run(m) 351} 352 353// SGR is R1 = R1 - R2. Operand order is the discrimination: an emulator that 354// computed R2 - R1 would return 214 here (8 - 50 masked), not 42. 355func sx_k_sub() -> i64 { 356 let m: *u8 = sx_guest() 357 var o: i64 = 0 358 o = sx_ri(m, o, SX_R2, SX_RI_LGHI, 50) 359 o = sx_ri(m, o, SX_R3, SX_RI_LGHI, 8) 360 o = sx_rre(m, o, SX_RRE_SGR, SX_R2, SX_R3) 361 o = sx_svc(m, o, SX_SVC_EXIT) 362 return sx_run(m) 363} 364 365func sx_k_mul() -> i64 { 366 let m: *u8 = sx_guest() 367 var o: i64 = 0 368 o = sx_ri(m, o, SX_R2, SX_RI_LGHI, 6) 369 o = sx_ri(m, o, SX_R3, SX_RI_LGHI, 7) 370 o = sx_rre(m, o, SX_RRE_MSGR, SX_R2, SX_R3) // 6 * 7 371 o = sx_svc(m, o, SX_SVC_EXIT) 372 return sx_run(m) 373} 374 375// AND then OR then XOR, chained so that swapping ANY TWO of the three gives a 376// different answer. 60 & 15 = 12; 12 | 48 = 60; 60 ^ 18 = 46. 377func sx_k_logic() -> i64 { 378 let m: *u8 = sx_guest() 379 var o: i64 = 0 380 o = sx_ri(m, o, SX_R2, SX_RI_LGHI, 60) 381 o = sx_ri(m, o, SX_R3, SX_RI_LGHI, 15) 382 o = sx_rre(m, o, SX_RRE_NGR, SX_R2, SX_R3) 383 o = sx_ri(m, o, SX_R4, SX_RI_LGHI, 48) 384 o = sx_rre(m, o, SX_RRE_OGR, SX_R2, SX_R4) 385 o = sx_ri(m, o, SX_R5, SX_RI_LGHI, 18) 386 o = sx_rre(m, o, SX_RRE_XGR, SX_R2, SX_R5) 387 o = sx_svc(m, o, SX_SVC_EXIT) 388 return sx_run(m) 389} 390 391// The class the previous generation could not run at all: a LOOP. CGR sets the 392// condition code, BRC with mask 7 (CC1|CC2|CC3) is the architected "branch if 393// not equal". The branch displacement is a signed HALFWORD count from the 394// branch instruction's own address: the loop head is 12 bytes back from the 395// branch at 24, so I2 = -6, not -12. 396func sx_k_branch_loop() -> i64 { 397 let m: *u8 = sx_guest() 398 var o: i64 = 0 399 o = sx_ri(m, o, SX_R2, SX_RI_LGHI, 0) // 0 acc = 0 400 o = sx_ri(m, o, SX_R3, SX_RI_LGHI, 0) // 4 i = 0 401 o = sx_ri(m, o, SX_R4, SX_RI_LGHI, 10) // 8 n = 10 402 let loop_at: i64 = o // 12 403 o = sx_rre(m, o, SX_RRE_AGR, SX_R2, SX_R3) // 12 acc = acc + i 404 o = sx_ri(m, o, SX_R3, SX_RI_AGHI, 1) // 16 i = i + 1 405 o = sx_rre(m, o, SX_RRE_CGR, SX_R3, SX_R4) // 20 compare i, n 406 let br_at: i64 = o // 24 407 o = sx_ri(m, o, SX_M_NE, SX_RI_BRC, (loop_at - br_at) / 2) 408 o = sx_svc(m, o, SX_SVC_EXIT) // 28 0+1+..+9 = 45 409 return sx_run(m) 410} 411 412// BRCTG decrements the full 64-bit register and branches while it is nonzero. 413// Five iterations of "add 3" is 15, which is not congruent to the loop count 414// or to the addend modulo 256, so an off-by-one in either direction shows. 415func sx_k_brctg_loop() -> i64 { 416 let m: *u8 = sx_guest() 417 var o: i64 = 0 418 o = sx_ri(m, o, SX_R2, SX_RI_LGHI, 0) 419 o = sx_ri(m, o, SX_R3, SX_RI_LGHI, 5) 420 let loop_at: i64 = o 421 o = sx_ri(m, o, SX_R2, SX_RI_AGHI, 3) 422 let br_at: i64 = o 423 o = sx_ri(m, o, SX_R3, SX_RI_BRCTG, (loop_at - br_at) / 2) 424 o = sx_svc(m, o, SX_SVC_EXIT) 425 return sx_run(m) 426} 427 428// STG then LG through a base register, with the register reloaded in between so 429// a store that never happened cannot be masked by the value still sitting in 430// r2. The 6-byte RXY form is the variable-length case. 431func sx_k_load_store() -> i64 { 432 let m: *u8 = sx_guest() 433 var o: i64 = 0 434 o = sx_ri(m, o, SX_R4, SX_RI_LGHI, SX_SCRATCH) 435 o = sx_ri(m, o, SX_R2, SX_RI_LGHI, 42) 436 o = sx_rxy(m, o, SX_R2, SX_R0, SX_R4, 0, SX_RXY_STG) 437 o = sx_ri(m, o, SX_R2, SX_RI_LGHI, 7) 438 o = sx_rxy(m, o, SX_R2, SX_R0, SX_R4, 0, SX_RXY_LG) 439 o = sx_svc(m, o, SX_SVC_EXIT) 440 return sx_run(m) 441} 442 443// PoP: a zero in the B or X field means THE ABSENCE OF A BASE, not the contents 444// of general register 0. r0 is loaded with 100 first, so an emulator that reads 445// r[0] as a base returns 142 and one that honours the architecture returns 42. 446func sx_k_base_zero() -> i64 { 447 let m: *u8 = sx_guest() 448 var o: i64 = 0 449 o = sx_ri(m, o, SX_R0, SX_RI_LGHI, 100) 450 o = sx_rx(m, o, SX_R4, SX_R0, SX_R0, 42) // la r4,42 451 o = sx_rre(m, o, SX_RRE_LGR, SX_R2, SX_R4) 452 o = sx_svc(m, o, SX_SVC_EXIT) 453 return sx_run(m) 454} 455 456// SRLG vs SRAG on a NEGATIVE value, and the obvious version of this test CANNOT 457// discriminate: -16 shifted right by 2 has low byte 0xF0 under both, so an 458// emulator implementing only one of them passes. A shift by 60 separates them: 459// logical -> 15, arithmetic -> -1, which the exit mask renders as 255. 460func sx_k_shift_logical() -> i64 { 461 let m: *u8 = sx_guest() 462 var o: i64 = 0 463 o = sx_ri(m, o, SX_R2, SX_RI_LGHI, 0 - 16) 464 o = sx_rsy(m, o, SX_R2, SX_R2, SX_R0, 60, SX_RSY_SRLG) 465 o = sx_svc(m, o, SX_SVC_EXIT) 466 return sx_run(m) 467} 468 469func sx_k_shift_arith() -> i64 { 470 let m: *u8 = sx_guest() 471 var o: i64 = 0 472 o = sx_ri(m, o, SX_R2, SX_RI_LGHI, 0 - 16) 473 o = sx_rsy(m, o, SX_R2, SX_R2, SX_R0, 60, SX_RSY_SRAG) 474 o = sx_svc(m, o, SX_SVC_EXIT) 475 return sx_run(m) 476} 477 478// AHI is the 32-bit form: it addresses bits 32-63 and leaves bits 0-31 alone. 479// The naive test cannot see the difference, because both a 32-bit and a 64-bit 480// AHI leave the same low byte, so this one makes the low half CARRY -- 481// 0xFFFFFFFF + 1 is 0 in 32 bits and 0x100000000 in 64 -- and then projects the 482// result back down through a shift. 483// 484// THE POISON IN r3 IS LOAD-BEARING AND WAS ADDED AFTER A BITE RUN. The first 485// version of this KAT PASSED against the pre-fix emulator FOR THE WRONG REASON: 486// there the two shifts were silent no-operations, r3 defaulted to zero, and 487// zero plus 42 is the right answer. Loading 7 into r3 first means a projecting 488// shift that never ran shows up as 49, a 64-bit AHI as 43, and only the 489// architected behaviour as 42. A green that a broken subject can also produce 490// is not evidence, and only running the ruler against the known-bad found it. 491func sx_k_ahi_is_32bit() -> i64 { 492 let m: *u8 = sx_guest() 493 var o: i64 = 0 494 o = sx_ri(m, o, SX_R2, SX_RI_LGHI, 1) 495 o = sx_rsy(m, o, SX_R2, SX_R2, SX_R0, 32, SX_RSY_SLLG) // r2 = 2^32 496 o = sx_ri(m, o, SX_R2, SX_RI_AGHI, 0 - 1) // r2 = 0xFFFFFFFF 497 o = sx_ri(m, o, SX_R2, SX_RI_AHI, 1) // 32-bit: 0 ; 64-bit: 0x100000000 498 o = sx_ri(m, o, SX_R3, SX_RI_LGHI, 7) // poison the projection target 499 o = sx_rsy(m, o, SX_R3, SX_R2, SX_R0, 32, SX_RSY_SRLG) // 0 correct, 1 wrong, 7 if absent 500 o = sx_ri(m, o, SX_R4, SX_RI_LGHI, 42) 501 o = sx_rre(m, o, SX_RRE_AGR, SX_R3, SX_R4) 502 o = sx_rre(m, o, SX_RRE_LGR, SX_R2, SX_R3) 503 o = sx_svc(m, o, SX_SVC_EXIT) 504 return sx_run(m) 505} 506 507// CGHI sets the condition code from an immediate compare; BRCL is the 6-byte 508// relative-long conditional branch. The skipped instruction loads 99, so a 509// branch that silently does not happen returns 136 rather than 42. 510func sx_k_brcl_cond() -> i64 { 511 let m: *u8 = sx_guest() 512 var o: i64 = 0 513 o = sx_ri(m, o, SX_R2, SX_RI_LGHI, 5) 514 o = sx_ri(m, o, SX_R2, SX_RI_CGHI, 5) 515 let br_at: i64 = o 516 let skip_at: i64 = br_at + SX_LEN_RIL + SX_LEN_RI 517 o = sx_ril(m, o, SX_M_EQ, SX_RIL_BRCL, (skip_at - br_at) / 2) 518 o = sx_ri(m, o, SX_R2, SX_RI_LGHI, 99) 519 o = sx_ri(m, o, SX_R2, SX_RI_AGHI, 37) 520 o = sx_svc(m, o, SX_SVC_EXIT) 521 return sx_run(m) 522} 523 524// BRASL saves the address of the NEXT SEQUENTIAL instruction (pc + 6, because 525// BRASL is six bytes) and BCR 15,r14 returns through it. A return address off 526// by one instruction lands on the callee again or on the exit early, and both 527// give a different status. 528func sx_k_call_return() -> i64 { 529 let m: *u8 = sx_guest() 530 var o: i64 = 0 531 o = sx_ri(m, o, SX_R2, SX_RI_LGHI, 40) 532 let br_at: i64 = o 533 let callee_at: i64 = br_at + SX_LEN_RIL + SX_LEN_RR 534 o = sx_ril(m, o, SX_R14, SX_RIL_BRASL, (callee_at - br_at) / 2) 535 o = sx_svc(m, o, SX_SVC_EXIT) 536 o = sx_ri(m, o, SX_R2, SX_RI_AGHI, 2) 537 o = sx_rr(m, o, SX_OP_BCR, SX_M_ALWAYS, SX_R14) 538 return sx_run(m) 539} 540 541// PoP: BCR with R2 = 0 is a no-operation WHATEVER the mask -- that is how 542// "bcr 15,0" serialises without branching. The previous generation branched to 543// whatever r0 held, which for the common case of zero is an infinite loop back 544// to the entry point, so this program never terminated. 545func sx_k_bcr_zero_is_nop() -> i64 { 546 let m: *u8 = sx_guest() 547 var o: i64 = 0 548 o = sx_ri(m, o, SX_R2, SX_RI_LGHI, 42) 549 o = sx_rr(m, o, SX_OP_BCR, SX_M_ALWAYS, SX_R0) 550 o = sx_svc(m, o, SX_SVC_EXIT) 551 return sx_run(m) 552} 553 554// CLGR is COMPARE LOGICAL: the operands are unsigned. -1 as an unsigned 64-bit 555// value is the largest there is, so the "first operand high" mask MUST branch. 556// 557// THE POLARITY HERE IS LOAD-BEARING AND WAS INVERTED AFTER A BITE RUN. The 558// first version put 42 on the fall-through path, so the pre-fix emulator -- 559// which decoded neither CLGR nor BRC and silently no-opped both -- walked 560// straight into the right answer and the KAT reported PASS on a subject that 561// implements neither instruction. Now the right answer is only reachable BY 562// TAKING THE BRANCH: a signed comparison gives CC1 and exits 7, and an emulator 563// with no conditional branch at all also exits 7. 564func sx_k_clgr_unsigned() -> i64 { 565 let m: *u8 = sx_guest() 566 var o: i64 = 0 567 o = sx_ri(m, o, SX_R2, SX_RI_LGHI, 7) // the wrong answer, pre-loaded 568 o = sx_ri(m, o, SX_R3, SX_RI_LGHI, 0 - 1) 569 o = sx_ri(m, o, SX_R4, SX_RI_LGHI, 1) 570 o = sx_rre(m, o, SX_RRE_CLGR, SX_R3, SX_R4) // unsigned -> CC2 (high) 571 let br_at: i64 = o 572 let hi_at: i64 = br_at + SX_LEN_RI + SX_LEN_RR 573 o = sx_ri(m, o, SX_M_HIGH, SX_RI_BRC, (hi_at - br_at) / 2) 574 o = sx_svc(m, o, SX_SVC_EXIT) // not taken: exit 7 575 o = sx_ri(m, o, SX_R2, SX_RI_LGHI, 42) // hi_at 576 o = sx_svc(m, o, SX_SVC_EXIT) 577 return sx_run(m) 578} 579 580// ===== LIVE negative controls =================================== 581// These two are not synthetic sentinels: they are real guest programs whose 582// only correct outcome is UNSUPPORTED. Both bite the same defect class from 583// opposite ends -- an unimplemented thing that RUNS ANYWAY. 584 585// B90F LRVGR is architecturally real and deliberately unimplemented. If a later 586// generation implements it, RE-POINT this control at another absent opcode. 587func sx_nc_unimplemented_subopcode() -> i64 { 588 let m: *u8 = sx_guest() 589 var o: i64 = 0 590 o = sx_ri(m, o, SX_R2, SX_RI_LGHI, 42) 591 o = sx_rre(m, o, SX_RRE_LRVGR, SX_R2, SX_R2) 592 o = sx_svc(m, o, SX_SVC_EXIT) 593 return sx_run(m) 594} 595 596// An SVC the emulator does not implement must stop and say so, not fall through 597// to the next instruction and exit with a plausible number. 598func sx_nc_unknown_svc() -> i64 { 599 let m: *u8 = sx_guest() 600 var o: i64 = 0 601 o = sx_ri(m, o, SX_R2, SX_RI_LGHI, 42) 602 o = sx_svc(m, o, SX_SVC_UNKNOWN) 603 o = sx_svc(m, o, SX_SVC_EXIT) 604 return sx_run(m) 605} 606 607// ===== ratchet ================================================== 608// One line: "s390x <passed>". UNSEEDED is a NAMED third state, so adopting this 609// fence cannot fail closed on first sight. The metric is GOOD-when-higher, so 610// it TIGHTENS on a rise and must NEVER rewrite its baseline on a fall -- 611// rewriting on a fall is how a ratchet launders itself green. It writes its OWN 612// file and never touches isa_conform.ratchet, which another lane owns. 613 614func sx_match_at(buf: *u8, n: i64, p: i64, s: *u8) -> i64 { 615 var j: i64 = 0 616 var ok: i64 = 1 617 while s[j] != (0 as u8) { 618 if (p + j) >= n { ok = 0 } 619 if ok == 1 { 620 if buf[p + j] != s[j] { ok = 0 } 621 } 622 j = j + 1 623 } 624 if ok == 0 { return -1 } 625 return j 626} 627 628func sx_digits_at(buf: *u8, n: i64, p0: i64) -> i64 { 629 var v: i64 = 0 630 var any: i64 = 0 631 var p: i64 = p0 632 var go: i64 = 1 633 while go == 1 { 634 if p >= n { go = 0 } 635 if go == 1 { 636 let c: i64 = buf[p] & 0xff 637 if c < SX_DIG_LO { go = 0 } 638 if go == 1 { 639 if c > SX_DIG_HI { go = 0 } 640 } 641 if go == 1 { 642 v = v * 10 + (c - SX_DIG_LO) 643 any = 1 644 p = p + 1 645 } 646 } 647 } 648 if any == 0 { return -1 } 649 return v 650} 651 652func sx_ratchet_read() -> i64 { 653 let lenbox: *i64 = sys_mmap(16) as *i64 654 let buf: *u8 = sys_read_file(SX_RATCHET, lenbox) 655 if (buf as i64) == 0 { return SX_UNSEEDED } 656 let n: i64 = lenbox[0] 657 var i: i64 = 0 658 var found: i64 = SX_UNSEEDED 659 while i < n { 660 if found < 0 { 661 let m: i64 = sx_match_at(buf, n, i, "s390x" as *u8) 662 if m > 0 { 663 if (i + m) < n { 664 if buf[i + m] == (SX_SPACE as u8) { 665 found = sx_digits_at(buf, n, i + m + 1) 666 } 667 } 668 } 669 } 670 var adv: i64 = 1 671 while adv == 1 { 672 if i >= n { adv = 0 } 673 if adv == 1 { 674 if buf[i] == (SX_NL as u8) { adv = 0 } 675 i = i + 1 676 } 677 } 678 } 679 return found 680} 681 682// Callers must have already decided that s390x did not regress; this function 683// does not re-check, and the tooth that guards it is asserted in main. 684func sx_ratchet_write(passed: i64) -> i64 { 685 let d: *u8 = sys_mmap(256) 686 var o: i64 = 0 687 o = gv_cat(d, o, "s390x " as *u8) 688 o = gv_catn(d, o, passed) 689 d[o] = SX_NL as u8 690 o = o + 1 691 let fd: i64 = sys_openat_wr(SX_RATCHET, SX_MODE_FILE) 692 if fd < 0 { return -1 } 693 sys_write(fd, d, o) 694 sys_fsync(fd) 695 sys_close(fd) 696 return o 697} 698 699// ===== main ===================================================== 700 701func main(argc: i64, argv: *i64) -> i64 { 702 gv_head("nx_isa_s390x_gate -- IBM z/Architecture conformance, KATs encoded from the Principles of Operation" as *u8) 703 let ctr: *i64 = gv_ctr() 704 705 let bx: *i64 = sys_mmap(SX_BOX_BYTES) as *i64 706 let nbuf: *u8 = sys_mmap(SX_NB_BYTES) 707 nbuf[0] = 0 as u8 708 bx[SX_NB_OFF] = 0 709 bx[SX_NB_PTR] = nbuf as i64 710 711 gv_puts("dispatch=emu_s390x_run_mem (the only surface all ten estate emulators share)" as *u8) 712 sx_nl() 713 gv_puts("outcomes are FIVE-WAY: PASS / WRONG-ANSWER / UNSUPPORTED-instruction-class / RAN-OFF-END / FAULT" as *u8) 714 sx_nl() 715 gv_puts("s390x is BIG-ENDIAN and VARIABLE-LENGTH (2/4/6 bytes); branch displacements are HALFWORD counts" as *u8) 716 sx_nl() 717 sx_nl() 718 719 // ---- the emitter is the fixture, so measure the emitter FIRST ---- 720 // If the encoder is wrong, every UNSUPPORTED below is the fixture's bug 721 // wearing the emulator's name. 722 let ec: *u8 = sx_guest() 723 let l_rr: i64 = sx_svc(ec, 0, SX_SVC_EXIT) 724 let l_ri: i64 = sx_ri(ec, 0, SX_R2, SX_RI_AGHI, 2) 725 let l_ril: i64 = sx_ril(ec, 0, SX_R14, SX_RIL_BRASL, 4) 726 var len_ok: i64 = 0 727 if l_rr == SX_LEN_RR { 728 if l_ri == SX_LEN_RI { 729 if l_ril == SX_LEN_RIL { len_ok = 1 } 730 } 731 } 732 gv_puts(" emitter widths RR=" as *u8) 733 gv_num(l_rr) 734 gv_puts(" RI=" as *u8) 735 gv_num(l_ri) 736 gv_puts(" RIL=" as *u8) 737 gv_num(l_ril) 738 sx_nl() 739 gv_check("fixture-variable-length-emitter-returns-2-4-6" as *u8, len_ok == 1, ctr) 740 741 // The exact byte string the PoP gives for AGHI r2,2 is A7 2B 00 02. This is 742 // the one encoding the previous emulator generation could not execute, so 743 // asserting it byte for byte separates "the fixture emitted the wrong 744 // opcode" from "the emulator does not decode the right one". 745 var eo: i64 = sx_ri(ec, 0, SX_R2, SX_RI_AGHI, 2) 746 var enc_ok: i64 = 0 747 if (ec[0] & 0xff) == SX_OP_RI { 748 if (ec[1] & 0xff) == 0x2B { 749 if (ec[2] & 0xff) == 0 { 750 if (ec[3] & 0xff) == 2 { enc_ok = 1 } 751 } 752 } 753 } 754 gv_check("fixture-encodes-aghi-as-the-manual-byte-string-A7-2B-00-02" as *u8, enc_ok == 1, ctr) 755 sx_nl() 756 757 // ---- the KATs ------------------------------------------------ 758 var sx_pass: i64 = 0 759 var sx_tot: i64 = 0 760 var o: i64 = 0 761 762 o = sx_report("imm-arith-lghi-agr" as *u8, sx_k_imm_arith(), 42, bx) 763 sx_tot = sx_tot + 1 764 if o == SX_O_PASS { sx_pass = sx_pass + 1 } 765 gv_check("isa-s390x-imm-arith" as *u8, o == SX_O_PASS, ctr) 766 767 o = sx_report("aghi-at-extended-opcode-A7xB" as *u8, sx_k_aghi_opcode(), 42, bx) 768 sx_tot = sx_tot + 1 769 if o == SX_O_PASS { sx_pass = sx_pass + 1 } 770 gv_check("isa-s390x-aghi-extended-opcode" as *u8, o == SX_O_PASS, ctr) 771 772 o = sx_report("sub-sgr-operand-order" as *u8, sx_k_sub(), 42, bx) 773 sx_tot = sx_tot + 1 774 if o == SX_O_PASS { sx_pass = sx_pass + 1 } 775 gv_check("isa-s390x-sub" as *u8, o == SX_O_PASS, ctr) 776 777 o = sx_report("mul-msgr" as *u8, sx_k_mul(), 42, bx) 778 sx_tot = sx_tot + 1 779 if o == SX_O_PASS { sx_pass = sx_pass + 1 } 780 gv_check("isa-s390x-mul" as *u8, o == SX_O_PASS, ctr) 781 782 o = sx_report("logic-ngr-ogr-xgr" as *u8, sx_k_logic(), 46, bx) 783 sx_tot = sx_tot + 1 784 if o == SX_O_PASS { sx_pass = sx_pass + 1 } 785 gv_check("isa-s390x-logic" as *u8, o == SX_O_PASS, ctr) 786 787 o = sx_report("branch-loop-cgr-brc" as *u8, sx_k_branch_loop(), 45, bx) 788 sx_tot = sx_tot + 1 789 if o == SX_O_PASS { sx_pass = sx_pass + 1 } 790 gv_check("isa-s390x-branch-loop" as *u8, o == SX_O_PASS, ctr) 791 792 o = sx_report("brctg-count-loop" as *u8, sx_k_brctg_loop(), 15, bx) 793 sx_tot = sx_tot + 1 794 if o == SX_O_PASS { sx_pass = sx_pass + 1 } 795 gv_check("isa-s390x-brctg-loop" as *u8, o == SX_O_PASS, ctr) 796 797 o = sx_report("load-store-stg-lg" as *u8, sx_k_load_store(), 42, bx) 798 sx_tot = sx_tot + 1 799 if o == SX_O_PASS { sx_pass = sx_pass + 1 } 800 gv_check("isa-s390x-load-store" as *u8, o == SX_O_PASS, ctr) 801 802 o = sx_report("base-register-zero-is-no-base" as *u8, sx_k_base_zero(), 42, bx) 803 sx_tot = sx_tot + 1 804 if o == SX_O_PASS { sx_pass = sx_pass + 1 } 805 gv_check("isa-s390x-base-register-zero" as *u8, o == SX_O_PASS, ctr) 806 807 o = sx_report("shift-logical-srlg" as *u8, sx_k_shift_logical(), 15, bx) 808 sx_tot = sx_tot + 1 809 if o == SX_O_PASS { sx_pass = sx_pass + 1 } 810 gv_check("isa-s390x-shift-logical" as *u8, o == SX_O_PASS, ctr) 811 812 o = sx_report("shift-arith-srag" as *u8, sx_k_shift_arith(), 255, bx) 813 sx_tot = sx_tot + 1 814 if o == SX_O_PASS { sx_pass = sx_pass + 1 } 815 gv_check("isa-s390x-shift-arith" as *u8, o == SX_O_PASS, ctr) 816 817 o = sx_report("ahi-is-32-bit-high-half-preserved" as *u8, sx_k_ahi_is_32bit(), 42, bx) 818 sx_tot = sx_tot + 1 819 if o == SX_O_PASS { sx_pass = sx_pass + 1 } 820 gv_check("isa-s390x-ahi-is-32-bit" as *u8, o == SX_O_PASS, ctr) 821 822 o = sx_report("brcl-conditional-long-cghi" as *u8, sx_k_brcl_cond(), 42, bx) 823 sx_tot = sx_tot + 1 824 if o == SX_O_PASS { sx_pass = sx_pass + 1 } 825 gv_check("isa-s390x-brcl-conditional" as *u8, o == SX_O_PASS, ctr) 826 827 o = sx_report("brasl-call-and-bcr-return" as *u8, sx_k_call_return(), 42, bx) 828 sx_tot = sx_tot + 1 829 if o == SX_O_PASS { sx_pass = sx_pass + 1 } 830 gv_check("isa-s390x-call-return" as *u8, o == SX_O_PASS, ctr) 831 832 o = sx_report("bcr-r2-zero-is-a-no-operation" as *u8, sx_k_bcr_zero_is_nop(), 42, bx) 833 sx_tot = sx_tot + 1 834 if o == SX_O_PASS { sx_pass = sx_pass + 1 } 835 gv_check("isa-s390x-bcr-r2-zero-is-nop" as *u8, o == SX_O_PASS, ctr) 836 837 o = sx_report("clgr-is-unsigned" as *u8, sx_k_clgr_unsigned(), 42, bx) 838 sx_tot = sx_tot + 1 839 if o == SX_O_PASS { sx_pass = sx_pass + 1 } 840 gv_check("isa-s390x-clgr-is-unsigned" as *u8, o == SX_O_PASS, ctr) 841 842 // ---- the fixture must have REACHED the emulator --------------- 843 // Asserting an outcome without asserting the fixture ran is how four 844 // vacuous fixtures shipped in this estate in a single day. 845 sx_nl() 846 gv_check("fixture-reached-s390x-emulator-at-all" as *u8, sx_pass > 0, ctr) 847 848 // ---- negative controls --------------------------------------- 849 // If sx_classify were stubbed to return PASS, every green above would be 850 // fake and only these would notice. 851 let ncw: i64 = sx_classify(sx_k_imm_arith(), 41) 852 gv_puts(" neg-control s390x.imm-arith against a deliberately wrong want=41 -> " as *u8) 853 gv_puts(sx_outcome_name(ncw)) 854 sx_nl() 855 gv_check("neg-control-wrong-expectation-must-not-pass" as *u8, ncw == SX_O_WRONG, ctr) 856 857 let ncu: i64 = sx_classify(SX_UNSUPPORTED, -1) 858 gv_check("neg-control-unsupported-sentinel-never-reads-as-pass" as *u8, ncu == SX_O_UNSUP, ctr) 859 860 let ncf: i64 = sx_classify(SX_FAULT, -3) 861 gv_check("neg-control-fault-sentinel-never-reads-as-pass" as *u8, ncf == SX_O_FAULT, ctr) 862 863 let ncr: i64 = sx_classify(SX_RANOFF, -2) 864 gv_check("neg-control-ranoff-sentinel-never-reads-as-pass" as *u8, ncr == SX_O_RANOFF, ctr) 865 866 // LIVE controls: real guest programs whose only correct outcome is 867 // UNSUPPORTED. These are the ones that catch a decoder which sets its 868 // handled flag before it has looked at the extended opcode. 869 let ncs: i64 = sx_nc_unimplemented_subopcode() 870 gv_puts(" neg-control guest program containing B90F LRVGR (unimplemented) returned " as *u8) 871 gv_num(ncs) 872 gv_puts(" -> " as *u8) 873 gv_puts(sx_outcome_name(sx_classify(ncs, 42))) 874 sx_nl() 875 gv_check("neg-control-unimplemented-subopcode-must-not-silently-no-op" as *u8, 876 sx_classify(ncs, 42) == SX_O_UNSUP, ctr) 877 878 let ncv: i64 = sx_nc_unknown_svc() 879 gv_puts(" neg-control guest program containing SVC 99 (unimplemented) returned " as *u8) 880 gv_num(ncv) 881 gv_puts(" -> " as *u8) 882 gv_puts(sx_outcome_name(sx_classify(ncv, 42))) 883 sx_nl() 884 gv_check("neg-control-unknown-svc-must-not-fall-through" as *u8, 885 sx_classify(ncv, 42) == SX_O_UNSUP, ctr) 886 887 // ---- coverage, published as a NUMBER not a verdict ------------ 888 sx_nl() 889 gv_puts("coverage s390x=" as *u8) 890 gv_num(sx_pass) 891 gv_puts("/" as *u8) 892 gv_num(sx_tot) 893 gv_puts(" classes=immediate-arith,extended-opcode-placement,subtract,multiply,logic,compare-and-branch,count-branch,load-store,base-register-zero,shift-logical,shift-arithmetic,32-bit-width,relative-long-branch,call-return,architected-nop,unsigned-compare" as *u8) 894 sx_nl() 895 gv_puts("NOT MEASURED, and therefore NOT CLAIMED: packed decimal, the vector facility, floating point, access registers, PSW/interrupt state, storage keys, LMG/STMG multi-register forms" as *u8) 896 sx_nl() 897 898 // ---- ratchet ------------------------------------------------- 899 let base: i64 = sx_ratchet_read() 900 gv_puts("ratchet s390x base=" as *u8) 901 gv_num(base) 902 gv_puts(" now=" as *u8) 903 gv_num(sx_pass) 904 gv_puts(" (base=-1 is UNSEEDED, a named state: first sight seeds, so adoption cannot fail closed)" as *u8) 905 sx_nl() 906 907 var no_regress: i64 = 1 908 if base >= 0 { 909 if sx_pass < base { no_regress = 0 } 910 } 911 912 var wrote: i64 = 0 913 if no_regress == 1 { 914 if base < 0 { wrote = sx_ratchet_write(sx_pass) } 915 if wrote == 0 { 916 if sx_pass > base { wrote = sx_ratchet_write(sx_pass) } 917 } 918 } 919 gv_puts("ratchet_bytes_written=" as *u8) 920 gv_num(wrote) 921 gv_puts(" (0 = held: neither seeded nor tightened this run)" as *u8) 922 sx_nl() 923 924 // This dialect has no || , so the laundering condition is computed. 925 var laundered: i64 = 0 926 if no_regress == 0 { 927 if wrote > 0 { laundered = 1 } 928 } 929 930 gv_check("ratchet-s390x-did-not-regress" as *u8, no_regress == 1, ctr) 931 gv_check("ratchet-never-rewritten-on-a-regression" as *u8, laundered == 0, ctr) 932 933 // THE WORKLIST, LAST, where a tailing caller can still see it. 934 gv_puts("NONPASS: " as *u8) 935 if bx[SX_NB_OFF] == 0 { 936 gv_puts("none -- every measured KAT matched its Principles-of-Operation expectation" as *u8) 937 } 938 if bx[SX_NB_OFF] > 0 { 939 gv_puts(bx[SX_NB_PTR] as *u8) 940 } 941 sx_nl() 942 943 return gv_verdict("nx_isa_s390x_gate" as *u8, ctr, 944 "IBM z/Architecture conformance measured by running big-endian variable-length machine code through emu_s390x_run_mem against Principles-of-Operation expectations, five-way outcomes, 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) 945}