code wiki / _hdl_build / nx_rv64_mulh_gate.nx

nx_rv64_mulh_gate.nx source

↩ module page · 84 lines · 7245 B

1// nx_rv64_mulh_gate.nx -- GATE: the HIGH-MULTIPLY half of the M extension (MULH/MULHU/MULHSU) -- the 64-bit high word 2// of a 64x64->128 product, in signed / unsigned / signed*unsigned forms. Completes RV64IM (was: unimpl->halt). The 3// interpreter computes the 128-bit high via Hacker's Delight 32-bit-half decomposition (NishiLang is 64-bit only, and 4// its '>>' is arithmetic -> every carry shift is masked); the JIT uses x86 one-operand imul/mul (high in rdx). MULHSU 5// has no x86 form -> fail-safe tiers to the interpreter. 6// T1 MULHU (unsigned high): (2^64-1)^2, (2^32)^2, and the case that DIFFERS from signed. 7// T2 MULH (signed high): the sign corrections -- (-1)*(-1) high=0, (-1)*2 high=-1, positive overflow. 8// T3 MULHSU (signed*unsigned high): differs from BOTH MULH and MULHU on the same operands. 9// T4 JIT MULH/MULHU == interpreter (imul/mul high-word extraction is correct). 10// T5 MULHSU fail-safe TIERS: the JIT declines (-1), the interpreter computes it. 11// expect_exit: 0 12import "nx_syscalls.nx" 13import "nx_rv64_fast.nx" 14import "nx_rv64_jit.nx" 15 16func g_puts(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 17func g_ph(v: i64) -> i64 { g_puts("0x" as *u8); let b: *u8=sys_mmap(20); var i: i64=15; var any: i64=0; while i>=0 { let nib: i64=(v>>(i*4))&15; if nib!=0 { any=1 } if any==1 { if nib<10 { b[0]=(48+nib) as u8 } else { b[0]=(87+nib) as u8 } sys_write(1,b,1) } i=i-1 } if any==0 { sys_write(1,"0" as *u8,1) } return 0 } 18func ck(name: *u8, c: i64) -> i64 { if c==1 { g_puts(" PASS " as *u8) } else { g_puts(" FAIL " as *u8) } g_puts(name); g_puts("\n" as *u8); return c } 19const MEMSZ: i64 = 4096 20 21// run [li x1,a; li x2,b; kind x3,x1,x2] through the JIT; return reg[3] via rj, and the compile rc via rcbox. 22func jit_m(kind: i64, a: i64, b: i64, opk: *i64, rd: *i64, rs1: *i64, rs2: *i64, imm: *i64, rj: *i64, rcbox: *i64) -> i64 { 23 opk[0]=FK_ADDI; rd[0]=1; rs1[0]=0; rs2[0]=0; imm[0]=a 24 opk[1]=FK_ADDI; rd[1]=2; rs1[1]=0; rs2[1]=0; imm[1]=b 25 opk[2]=kind; rd[2]=3; rs1[2]=1; rs2[2]=2; imm[2]=0 26 let x86: *u8=sys_mmap(4096); let x86off: *i64=sys_mmap(64*8) as *i64 27 jit_set_membase(0, MEMSZ) 28 let xlen: i64=jit_compile(opk, rd, rs1, rs2, imm, 3, x86, x86off); rcbox[0]=xlen 29 var z: i64=0; while z<32 { rj[z]=0; z=z+1 } 30 if xlen>0 { jit_run(x86, xlen, rj) } 31 return rj[3] 32} 33 34func main() -> i64 { 35 g_puts("nx_rv64_mulh_gate (RV64 high-multiply MULH/MULHU/MULHSU: 128-bit high word, signed/unsigned/mixed; completes RV64IM)\n" as *u8) 36 var pass: i64=0; var total: i64=0 37 let opk: *i64=sys_mmap(64*8) as *i64; let rd: *i64=sys_mmap(64*8) as *i64; let rs1: *i64=sys_mmap(64*8) as *i64; let rs2: *i64=sys_mmap(64*8) as *i64; let imm: *i64=sys_mmap(64*8) as *i64 38 let rj: *i64=sys_mmap(32*8) as *i64; let rc: *i64=sys_mmap(8) as *i64 39 let M1: i64 = 0-1 // 0xFFFFFFFFFFFFFFFF (unsigned 2^64-1) 40 41 // T1: MULHU. (2^64-1)^2 high = 2^64-2 = 0xFFFFFFFFFFFFFFFE. (2^32)^2 = 2^64 -> high 1. (2^64-1)*2 high = 1. 42 var t1: i64=0 43 if mulhu64(M1,M1)==(0-2) { if mulhu64(1<<32,1<<32)==1 { if mulhu64(M1,2)==1 { t1=1 } } } 44 g_puts(" T1 MULHU: (2^64-1)^2 hi="); g_ph(mulhu64(M1,M1)); g_puts(" (=0xff..fe) (2^32)^2 hi="); g_ph(mulhu64(1<<32,1<<32)); g_puts(" (2^64-1)*2 hi="); g_ph(mulhu64(M1,2)); g_puts("\n" as *u8) 45 pass=pass+ck("T1: MULHU unsigned high word -- (2^64-1)^2, (2^32)^2, (2^64-1)*2 all exact" as *u8, t1); total=total+1 46 47 // T2: MULH. (-1)*(-1)=1 -> high 0. (-1)*2=-2 -> high -1 (sign extension). (2^62)*4 = 2^64 -> high 1. 48 var t2: i64=0 49 if mulh64(0-1,0-1)==0 { if mulh64(0-1,2)==(0-1) { if mulh64(1<<62,4)==1 { t2=1 } } } 50 g_puts(" T2 MULH: (-1)*(-1) hi="); g_ph(mulh64(0-1,0-1)); g_puts(" (-1)*2 hi="); g_ph(mulh64(0-1,2)); g_puts(" (2^62)*4 hi="); g_ph(mulh64(1<<62,4)); g_puts("\n" as *u8) 51 pass=pass+ck("T2: MULH signed high word -- sign corrections give (-1)(-1)hi=0, (-1)(2)hi=-1, (2^62)(4)hi=1" as *u8, t2); total=total+1 52 53 // T3: MULHSU (a signed, b unsigned) differs from BOTH. a=-1, b=2: MULHSU hi = -1; but MULHU(-1,2)=1, MULH(-1,2)=-1. 54 // a=-1, b=2^63: MULHSU = high of -(2^63) = -1; MULHU(-1, 2^63) = high of (2^64-1)*2^63 = 2^63-1. 55 var t3: i64=0 56 if mulhsu64(0-1,2)==(0-1) { if mulhu64(0-1,2)==1 { // MULHSU != MULHU 57 if mulhsu64(0-1,1<<63)==(0-1) { if mulhu64(0-1,1<<63)==((1<<63)-1) { t3=1 } } } } // and the 2^63 case 58 g_puts(" T3 MULHSU(-1,2) hi="); g_ph(mulhsu64(0-1,2)); g_puts(" vs MULHU(-1,2)="); g_ph(mulhu64(0-1,2)); g_puts(" | MULHSU(-1,2^63) hi="); g_ph(mulhsu64(0-1,1<<63)); g_puts("\n" as *u8) 59 pass=pass+ck("T3: MULHSU signed*unsigned high -- differs from both MULH and MULHU on the same operands (correct)" as *u8, t3); total=total+1 60 61 // T4: JIT MULH/MULHU == interpreter (values expressible via li: -1, 2). 62 let jmulh_11: i64=jit_m(FK_MULH,0-1,0-1,opk,rd,rs1,rs2,imm,rj,rc); let rc1: i64=rc[0] 63 let jmulhu_11: i64=jit_m(FK_MULHU,0-1,0-1,opk,rd,rs1,rs2,imm,rj,rc); let rc2: i64=rc[0] 64 let jmulhu_12: i64=jit_m(FK_MULHU,0-1,2,opk,rd,rs1,rs2,imm,rj,rc); let rc3: i64=rc[0] 65 g_puts(" T4 JIT: MULH(-1,-1)="); g_ph(jmulh_11); g_puts(" MULHU(-1,-1)="); g_ph(jmulhu_11); g_puts(" MULHU(-1,2)="); g_ph(jmulhu_12); g_puts(" (expect 0, 0xff..fe, 1)\n" as *u8) 66 var t4: i64=0; if rc1>0 { if jmulh_11==mulh64(0-1,0-1) { if jmulhu_11==mulhu64(0-1,0-1) { if jmulhu_12==mulhu64(0-1,2) { t4=1 } } } } 67 pass=pass+ck("T4: JIT MULH/MULHU (x86 imul/mul high word) == the interpreter -- native high-multiply is correct" as *u8, t4); total=total+1 68 69 // T5: MULHSU fail-safe tiers -- the JIT declines (rc=-1), the interpreter computes it. 70 let jmulhsu: i64=jit_m(FK_MULHSU,0-1,2,opk,rd,rs1,rs2,imm,rj,rc); let rc5: i64=rc[0] 71 g_puts(" T5 MULHSU: jit_compile rc="); g_ph(rc5); g_puts(" (-1=declines->tier) interp MULHSU(-1,2)="); g_ph(mulhsu64(0-1,2)); g_puts("\n" as *u8) 72 var t5: i64=0; if rc5==(0-1) { if mulhsu64(0-1,2)==(0-1) { t5=1 } } 73 pass=pass+ck("T5: MULHSU fail-safe TIERS -- JIT declines (-1, no x86 form), interpreter computes it correctly" as *u8, t5); total=total+1 74 75 var okall: i64=0; if pass==total { okall=1 } 76 g_puts("---- nx_rv64_mulh_gate: passed "); g_pn_x(pass); g_puts(" / "); g_pn_x(total); g_puts(" ----\n" as *u8) 77 if okall==1 { 78 let logf: i64=sys_openat_append("knowledge/status/rv64_mulh.log" as *u8, 420) 79 if logf>=0 { let z: i64=sys_write(logf,"NXRV64MULH GREEN: high-multiply MULH/MULHU/MULHSU -- 128-bit high word (Hacker's Delight interp + x86 imul/mul JIT); signed/unsigned/mixed exact, MULHSU fail-safe tiers. RV64IM COMPLETE\n" as *u8,178); sys_close(logf) } 80 g_puts("verdict=GREEN (high-multiply MULH/MULHU/MULHSU: 128-bit high word signed/unsigned/mixed, JIT imul/mul + interp Hacker's-Delight, MULHSU fail-safe tiers; RV64IM COMPLETE)\n" as *u8); sys_exit(0); return 0 81 } 82 g_puts("verdict=RED\n" as *u8); sys_exit(1); return 1 83} 84func g_pn_x(v: i64) -> i64 { let b: *u8=sys_mmap(28); var x: i64=v; if x==0{b[0]=48;sys_write(1,b,1);return 0} var d: i64=0; var y: i64=x; while y>0{d=d+1;y=y/10} var i: i64=d-1; y=x; while i>=0{b[i]=(48+(y%10)) as u8;y=y/10;i=i-1} sys_write(1,b,d); return 0 }