code wiki / _hdl_build / nx_f32_sse_kat_gate.nx
nx_f32_sse_kat_gate.nx source
↩ module page · 164 lines · 8589 B
1// nx_f32_sse_kat_gate.nx -- SOVEREIGN hardware IEEE-754 f32 scalar run-KAT: the
2// FIRST rung of the CPU+GPU 3D stack, proving REAL single-precision float on the
3// x86 FPU (xmm) end to end (NO gcc / NO qemu / NO binutils). AUTHOR=ORGAN, no-false-green.
4//
5// Why this rung: fx.nx states "NishiLang has no float type today" -- all prior
6// "f32" was fixed-point or soft-float emulated in i64, never the CPU's float ALU.
7// The sovereign x86 game/render stack must run on the actual hardware (CPU SSE
8// float now; GPU f32 next). This gate proves the sovereign assembler now EMITS and
9// the silicon EXECUTES scalar-single ops that an integer-only path CANNOT fake.
10//
11// It assembles four tiny programs with the team's own _offc/nxasm_x86_main.elf and
12// EXECUTES the resulting ELFs (exit code = the computed integer):
13// P0: (3.0+4.0)*5.0 = 35.0 -> cvtss2si -> exit 35 (addss+mulss on the FPU)
14// P1: (3.0+4.0)*6.0 = 42.0 -> cvtss2si -> exit 42 (neg control: different operand, different correct result)
15// P2: 7.0/2.0 = 3.5 -> cvtss2si -> exit 4 (divss + round-half-to-EVEN: real IEEE rounding)
16// P3: 7.0/2.0 = 3.5 -> cvttss2si-> exit 3 (truncate toward zero)
17// GREEN iff p0==35 && p1==42 && p2==4 && p3==3 && p0!=p1 && p2!=p3. The p2!=p3 pair
18// is the killer: ONE 3.5 value converts to 4 (round) and 3 (truncate) ONLY if real
19// hardware IEEE-754 semantics are engaged -- an integer fake cannot produce both.
20// Run-proves SEVEN encoder forms: cvtsi2ss, addss, mulss, divss, cvtss2si, cvttss2si
21// (+ the REX.W encoder twin x86_sse_rr_w). Writes knowledge/status/f32_sse_kat.log.
22// license_tier: ORIGINAL Reuses the run/assemble spine from nx_simd_sse_kat_gate.
23import "nx_syscalls.nx"
24import "nx_gate_verdict.nx"
25
26const ASM_TOOL: *u8 = "_offc/nxasm_x86_main.elf"
27const SV_LOG: *u8 = "knowledge/status/f32_sse_kat.log"
28
29func gw(fd: i64, s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(fd, s, n); return 0 }
30func gwn(fd: i64, v: i64) -> i64 {
31 let bb: *u8 = sys_mmap(28); var m: i64 = v
32 if m < 0 { m = 0 - m; sys_write(fd, "-" as *u8, 1) }
33 let t: *u8 = sys_mmap(28); var k: i64 = 0
34 if m == 0 { t[0] = 48; k = 1 }
35 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
36 var i: i64 = 0
37 while i < k { bb[i] = t[k - 1 - i]; i = i + 1 }
38 sys_write(fd, bb, k); return 0
39}
40
41// fork + mute child stdout/stderr + execve(path, argv); parent waits; returns
42// child's WEXITSTATUS, or 128+signal if it died to a signal (a segfaulted
43// program/tool must NOT decode as a false 0). Clone of the proven sbr_run spine.
44func g_run(path: *u8, argv: *i64) -> i64 {
45 let envp: *i64 = sys_mmap(8 * 4) as *i64
46 envp[0] = "PATH=/usr/bin:/bin" as *u8 as i64
47 envp[1] = 0
48 let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4)
49 let pid: i64 = sys_fork()
50 if pid == 0 {
51 if dn >= 0 { sys_dup3(dn, 1, 0) }
52 if dn >= 0 { sys_dup3(dn, 2, 0) }
53 sys_execve(path, argv, envp)
54 sys_exit(127)
55 }
56 let st: *i64 = sys_mmap(16) as *i64
57 sys_wait4(pid, st, 0)
58 if dn >= 0 { sys_close(dn) }
59 let sig: i64 = st[0] & 0x7f
60 if sig != 0 { return 128 + sig }
61 return (st[0] >> 8) & 0xff
62}
63
64// assemble spath -> elfpath with the team's sovereign assembler, then run the
65// ELF. Returns the program's exit, or 0-200-rc if the assemble step failed
66// (distinct negative => surfaces an encode gap rather than masquerading as a value).
67func asm_and_run(spath: *u8, elfpath: *u8) -> i64 {
68 let aa: *i64 = sys_mmap(8 * 4) as *i64
69 aa[0] = ASM_TOOL as i64
70 aa[1] = spath as i64
71 aa[2] = elfpath as i64
72 aa[3] = 0
73 let rc_a: i64 = g_run(ASM_TOOL, aa)
74 if rc_a != 0 { return 0 - 200 - rc_a }
75 let rr: *i64 = sys_mmap(8 * 4) as *i64
76 rr[0] = elfpath as i64
77 rr[1] = 0
78 return g_run(elfpath, rr)
79}
80
81// Emit one KAT .s. variant 0..3 select the program (see header). Entry = _start;
82// result lands in %rdi; exit via syscall 60. No memory operands -- pure register
83// SSE-scalar so this proves the ALU/convert path, not load/store.
84func write_f32_s(path: *u8, variant: i64) -> i64 {
85 let fd: i64 = sys_openat_wr(path, 0x1a4)
86 if fd < 0 { return 0 - 1 }
87 gw(fd, ".text\n" as *u8)
88 gw(fd, "_start:\n" as *u8)
89 if variant == 0 { // (3+4)*5 = 35.0
90 gw(fd, "movabsq $3, %rax\n" as *u8); gw(fd, "cvtsi2ss %rax, %xmm0\n" as *u8)
91 gw(fd, "movabsq $4, %rax\n" as *u8); gw(fd, "cvtsi2ss %rax, %xmm1\n" as *u8)
92 gw(fd, "addss %xmm1, %xmm0\n" as *u8)
93 gw(fd, "movabsq $5, %rax\n" as *u8); gw(fd, "cvtsi2ss %rax, %xmm2\n" as *u8)
94 gw(fd, "mulss %xmm2, %xmm0\n" as *u8)
95 gw(fd, "cvtss2si %xmm0, %rdi\n" as *u8)
96 }
97 if variant == 1 { // (3+4)*6 = 42.0 (neg control)
98 gw(fd, "movabsq $3, %rax\n" as *u8); gw(fd, "cvtsi2ss %rax, %xmm0\n" as *u8)
99 gw(fd, "movabsq $4, %rax\n" as *u8); gw(fd, "cvtsi2ss %rax, %xmm1\n" as *u8)
100 gw(fd, "addss %xmm1, %xmm0\n" as *u8)
101 gw(fd, "movabsq $6, %rax\n" as *u8); gw(fd, "cvtsi2ss %rax, %xmm2\n" as *u8)
102 gw(fd, "mulss %xmm2, %xmm0\n" as *u8)
103 gw(fd, "cvtss2si %xmm0, %rdi\n" as *u8)
104 }
105 if variant == 2 { // 7.0/2.0 = 3.5 -> round-half-even -> 4
106 gw(fd, "movabsq $7, %rax\n" as *u8); gw(fd, "cvtsi2ss %rax, %xmm0\n" as *u8)
107 gw(fd, "movabsq $2, %rax\n" as *u8); gw(fd, "cvtsi2ss %rax, %xmm1\n" as *u8)
108 gw(fd, "divss %xmm1, %xmm0\n" as *u8)
109 gw(fd, "cvtss2si %xmm0, %rdi\n" as *u8)
110 }
111 if variant == 3 { // 7.0/2.0 = 3.5 -> truncate -> 3
112 gw(fd, "movabsq $7, %rax\n" as *u8); gw(fd, "cvtsi2ss %rax, %xmm0\n" as *u8)
113 gw(fd, "movabsq $2, %rax\n" as *u8); gw(fd, "cvtsi2ss %rax, %xmm1\n" as *u8)
114 gw(fd, "divss %xmm1, %xmm0\n" as *u8)
115 gw(fd, "cvttss2si %xmm0, %rdi\n" as *u8)
116 }
117 gw(fd, "movabsq $60, %rax\n" as *u8)
118 gw(fd, "syscall\n" as *u8)
119 sys_close(fd)
120 return 0
121}
122
123func g_emit(fd: i64, p0: i64, p1: i64, p2: i64, p3: i64, ok: i64) -> i64 {
124 gw(fd, "F32_SSE_KAT rung=R1-f32-cpu authored=organ lane=cpu reg=xmm ops=cvtsi2ss+addss+mulss+divss+cvtss2si+cvttss2si sovereign(nx_cc->nxasm_x86,no-gcc/qemu/binutils) silicon=real p0_35=" as *u8); gwn(fd, p0)
125 gw(fd, " p1_42=" as *u8); gwn(fd, p1)
126 gw(fd, " p2_round4=" as *u8); gwn(fd, p2)
127 gw(fd, " p3_trunc3=" as *u8); gwn(fd, p3)
128 gw(fd, " mul_distinct=" as *u8); if p0 != p1 { gwn(fd, 1) } else { gwn(fd, 0) }
129 gw(fd, " ieee_round_vs_trunc=" as *u8); if p2 != p3 { gwn(fd, 1) } else { gwn(fd, 0) }
130 if ok == 1 { gw(fd, " verdict=GREEN\n" as *u8) } else { gw(fd, " verdict=RED reason=exit-mismatch-or-encode-gap\n" as *u8) }
131 return 0
132}
133
134func main() -> i64 {
135 write_f32_s("/tmp/_f32_kat_0.s" as *u8, 0)
136 let p0: i64 = asm_and_run("/tmp/_f32_kat_0.s" as *u8, "/tmp/_f32_kat_0.elf" as *u8)
137 write_f32_s("/tmp/_f32_kat_1.s" as *u8, 1)
138 let p1: i64 = asm_and_run("/tmp/_f32_kat_1.s" as *u8, "/tmp/_f32_kat_1.elf" as *u8)
139 write_f32_s("/tmp/_f32_kat_2.s" as *u8, 2)
140 let p2: i64 = asm_and_run("/tmp/_f32_kat_2.s" as *u8, "/tmp/_f32_kat_2.elf" as *u8)
141 write_f32_s("/tmp/_f32_kat_3.s" as *u8, 3)
142 let p3: i64 = asm_and_run("/tmp/_f32_kat_3.s" as *u8, "/tmp/_f32_kat_3.elf" as *u8)
143
144 var ok: i64 = 1
145 if p0 != 35 { ok = 0 } // addss + mulss on the FPU
146 if p1 != 42 { ok = 0 } // neg control: different operand -> different correct result
147 if p2 != 4 { ok = 0 } // divss + round-half-to-even (real IEEE rounding)
148 if p3 != 3 { ok = 0 } // cvttss2si truncate
149 if p0 == p1 { ok = 0 } // distinct => not a baked constant
150 if p2 == p3 { ok = 0 } // round != truncate => genuine hardware float semantics
151
152 g_emit(1, p0, p1, p2, p3, ok)
153 let lf: i64 = sys_openat_append(SV_LOG, 420)
154 if lf >= 0 { g_emit(lf, p0, p1, p2, p3, ok); sys_close(lf) }
155 // MIGRATED onto nx_gate_verdict by nx_gate_dry_apply (D001, minimal form): every check
156 // row above is untouched, so the PASS/FAIL vector cannot change; only the hand-rolled
157 // verdict emission is replaced by the ONE shared base class. Proven by nx_gate_migrate verify.
158 let ctr__dry: *i64 = gv_ctr()
159 ctr__dry[0] = ok
160 ctr__dry[1] = 1
161 let rc__dry: i64 = gv_verdict("F32-SSE-KAT-GATE" as *u8, ctr__dry, "teeth unchanged; verdict emission migrated onto the shared base class" as *u8)
162 sys_exit(rc__dry)
163 return rc__dry
164}