nx_riscv_lib.nx source
↩ module page · 135 lines · 9843 B
1// nx_riscv_lib.nx -- GENERAL SOVEREIGN RISC-V CAPABILITIES (the shared ecosystem library for the rv64im toolchain).
2// Extracted (rule 15) from nx_riscv_{emit,decode,elf,emu,codegen,loop} so the encoder / lowerer / ELF writer /
3// emulator are ONE general capability instead of N copies. Pure library: no main. Imports only nx_syscalls.
4// ENCODERS enc_r/enc_i/enc_u/enc_b (rv64im instruction encoding)
5// BYTES put32/put64, rd64, memrd/memwr (little-endian buffer + simulated-memory I/O)
6// SEXT sext12/sext32/sext_b (sign extension)
7// ELF build_header (rv64 ELF64 + PT_LOAD header)
8// CODEGEN lower (IR -> RISC-V, incl control flow: LABEL/BLE/ADDI)
9// EMULATOR run (full rv64im interpreter: arith/lui/ld/sd/branch/jal/jalr/ecall)
10// UI g_puts/g_pn/ck (shared output helpers)
11import "nx_syscalls.nx"
12import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc)
13const K_MAGIC_4095: i64 = 4095
14const K_MAGIC_2048: i64 = 2048
15const K_MAGIC_4096: i64 = 4096
16const K_MAGIC_8192: i64 = 8192
17const K_MAGIC_200000: i64 = 200000
18const K_MAGIC_200001: i64 = 200001
19
20// ---- UI ----
21func 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 }
22// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer
23// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the
24// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls).
25// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign.
26func g_pn(v: i64) -> i64 { nxi_out(v); return 0 }
27func 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 }
28
29// ---- rv64im ENCODERS ----
30func enc_r(op: i64, f3: i64, f7: i64, rd: i64, rs1: i64, rs2: i64) -> i64 { return (f7<<25)|(rs2<<20)|(rs1<<15)|(f3<<12)|(rd<<7)|op }
31func enc_i(op: i64, f3: i64, rd: i64, rs1: i64, imm: i64) -> i64 { return ((imm&K_MAGIC_4095)<<20)|(rs1<<15)|(f3<<12)|(rd<<7)|op }
32func enc_u(op: i64, rd: i64, imm20: i64) -> i64 { return ((imm20&0xFFFFF)<<12)|(rd<<7)|op }
33func enc_b(op: i64, f3: i64, rs1: i64, rs2: i64, imm: i64) -> i64 {
34 let b12: i64=(imm>>12)&1; let b11: i64=(imm>>11)&1; let b105: i64=(imm>>5)&63; let b41: i64=(imm>>1)&15
35 return (b12<<31)|(b105<<25)|(rs2<<20)|(rs1<<15)|(f3<<12)|(b41<<8)|(b11<<7)|op
36}
37func enc_s(op: i64, f3: i64, rs1: i64, rs2: i64, imm: i64) -> i64 { let hi: i64=(imm>>5)&127; let lo: i64=imm&31; return (hi<<25)|(rs2<<20)|(rs1<<15)|(f3<<12)|(lo<<7)|op }
38
39// ---- rv64im DECODERS + instruction identify (unified numbering for the whole toolchain) ----
40func d_op(w: i64) -> i64 { return w&127 }
41func d_rd(w: i64) -> i64 { return (w>>7)&31 }
42func d_f3(w: i64) -> i64 { return (w>>12)&7 }
43func d_rs1(w: i64) -> i64 { return (w>>15)&31 }
44func d_rs2(w: i64) -> i64 { return (w>>20)&31 }
45func d_f7(w: i64) -> i64 { return (w>>25)&127 }
46func d_immi(w: i64) -> i64 { return (w>>20)&K_MAGIC_4095 }
47// ident: 1 add 2 sub 3 mul 4 addi 5 ld 6 sd 7 srai 8 jalr 9 ecall 10 lui (0 unknown)
48func ident(w: i64) -> i64 {
49 let op: i64=w&127; let f3: i64=(w>>12)&7; let f7: i64=(w>>25)&127
50 if op==0x33 { if f3==0 { if f7==0 { return 1 } if f7==0x20 { return 2 } if f7==1 { return 3 } } }
51 if op==0x13 { if f3==0 { return 4 } if f3==5 { return 7 } }
52 if op==0x03 { if f3==3 { return 5 } }
53 if op==0x23 { if f3==3 { return 6 } }
54 if op==0x67 { return 8 }
55 if op==0x73 { return 9 }
56 if op==0x37 { return 10 }
57 return 0
58}
59
60// ---- BYTE / MEMORY I/O (little-endian) ----
61func put8(m: *u8, p: i64, v: i64) -> i64 { m[p]=(v&255) as u8; return p+1 }
62func put16(m: *u8, p: i64, v: i64) -> i64 { m[p]=(v&255) as u8; m[p+1]=((v>>8)&255) as u8; return p+2 }
63func put32(m: *u8, p: i64, w: i64) -> i64 { m[p]=(w&255) as u8; m[p+1]=((w>>8)&255) as u8; m[p+2]=((w>>16)&255) as u8; m[p+3]=((w>>24)&255) as u8; return p+4 }
64func rd16(m: *u8, o: i64) -> i64 { return (m[o] as i64)|((m[o+1] as i64)<<8) }
65func rd32(m: *u8, o: i64) -> i64 { return (m[o] as i64)|((m[o+1] as i64)<<8)|((m[o+2] as i64)<<16)|((m[o+3] as i64)<<24) }
66func put64(m: *u8, p: i64, v: i64) -> i64 { var i: i64=0; while i<8 { m[p+i]=((v>>(i*8))&255) as u8; i=i+1 } return p+8 }
67func rd64(m: *u8, o: i64) -> i64 { var v: i64=0; var i: i64=0; while i<8 { v=v|((m[o+i] as i64)<<(i*8)); i=i+1 } return v }
68func memrd32(m: *u8, idx: i64) -> i64 { return ((m[idx] as i64)|((m[idx+1] as i64)<<8)|((m[idx+2] as i64)<<16)|((m[idx+3] as i64)<<24)) & 0xFFFFFFFF }
69func memrd64(m: *u8, idx: i64) -> i64 { var v: i64=0; var i: i64=0; while i<8 { v=v|((m[idx+i] as i64)<<(i*8)); i=i+1 } return v }
70func memwr32(m: *u8, idx: i64, w: i64) -> i64 { m[idx]=(w&255) as u8; m[idx+1]=((w>>8)&255) as u8; m[idx+2]=((w>>16)&255) as u8; m[idx+3]=((w>>24)&255) as u8; return 0 }
71func memwr64(m: *u8, idx: i64, w: i64) -> i64 { var i: i64=0; while i<8 { m[idx+i]=((w>>(i*8))&255) as u8; i=i+1 } return 0 }
72
73// ---- SIGN EXTENSION ----
74func sext12(v: i64) -> i64 { let x: i64=v&K_MAGIC_4095; if (x&K_MAGIC_2048)!=0 { return x-K_MAGIC_4096 } return x }
75func sext32(v: i64) -> i64 { let x: i64=v&0xFFFFFFFF; if (x&0x80000000)!=0 { return x-0x100000000 } return x }
76func sext_b(w: i64) -> i64 { let imm: i64=(((w>>31)&1)<<12)|(((w>>7)&1)<<11)|(((w>>25)&63)<<5)|(((w>>8)&15)<<1); if (imm&K_MAGIC_4096)!=0 { return imm-K_MAGIC_8192 } return imm }
77
78// ---- ELF64 (rv64, single PT_LOAD R+X, no-FPU ABI) ----
79func build_header(m: *u8, total: i64, entry: i64, base: i64) -> i64 {
80 var p: i64=0
81 m[0]=0x7F as u8; m[1]=69 as u8; m[2]=76 as u8; m[3]=70 as u8; m[4]=2 as u8; m[5]=1 as u8; m[6]=1 as u8
82 p=7; while p<16 { m[p]=0 as u8; p=p+1 }
83 m[16]=2 as u8; m[17]=0 as u8; m[18]=243 as u8; m[19]=0 as u8
84 p=put32(m,20,1); p=put64(m,24,entry); p=put64(m,32,64); p=put64(m,40,0); p=put32(m,48,0)
85 m[52]=64 as u8; m[53]=0 as u8; m[54]=56 as u8; m[55]=0 as u8; m[56]=1 as u8; m[57]=0 as u8
86 m[58]=0 as u8; m[59]=0 as u8; m[60]=0 as u8; m[61]=0 as u8; m[62]=0 as u8; m[63]=0 as u8
87 p=put32(m,64,1); p=put32(m,68,5); p=put64(m,72,0); p=put64(m,80,base); p=put64(m,88,base); p=put64(m,96,total); p=put64(m,104,total); p=put64(m,112,0x1000)
88 return 0
89}
90
91// ---- CODEGEN: IR -> RISC-V (incl control flow). IR ops: 0 CONST 1 MUL 2 ADD 3 SUB 4 SHR 5 RET 6 LABEL 7 BLE 8 ADDI ----
92func lower(m: *u8, off: i64, irop: *i64, irdst: *i64, irs1: *i64, irs2: *i64, irimm: *i64, n: i64, preg: *i64, labelpos: *i64) -> i64 {
93 var p: i64=off; var i: i64=0
94 while i<n {
95 let o: i64=irop[i]
96 if o==0 { let val: i64=irimm[i]; let hi: i64=(val>>12)&0xFFFFF; let lo: i64=val&K_MAGIC_4095
97 p=put32(m,p, enc_u(0x37, preg[irdst[i]], hi)); if lo!=0 { p=put32(m,p, enc_i(0x13,0,preg[irdst[i]],preg[irdst[i]],lo)) } }
98 if o==1 { p=put32(m,p, enc_r(0x33,0,1, preg[irdst[i]], preg[irs1[i]], preg[irs2[i]])) }
99 if o==2 { p=put32(m,p, enc_r(0x33,0,0, preg[irdst[i]], preg[irs1[i]], preg[irs2[i]])) }
100 if o==3 { p=put32(m,p, enc_r(0x33,0,0x20, preg[irdst[i]], preg[irs1[i]], preg[irs2[i]])) }
101 if o==4 { p=put32(m,p, enc_i(0x13,5, preg[irdst[i]], preg[irs1[i]], 0x400|irimm[i])) }
102 if o==5 { p=put32(m,p, enc_i(0x13,0,10,preg[irs1[i]],0)); p=put32(m,p, enc_i(0x13,0,17,0,93)); p=put32(m,p, 0x73) }
103 if o==6 { labelpos[irimm[i]]=p }
104 if o==7 { let boff: i64=labelpos[irimm[i]]-p; p=put32(m,p, enc_b(0x63,5, preg[irs2[i]], preg[irs1[i]], boff)) }
105 if o==8 { p=put32(m,p, enc_i(0x13,0, preg[irdst[i]], preg[irs1[i]], irimm[i])) }
106 i=i+1
107 }
108 return (p-off)/4
109}
110
111// ---- EMULATOR: full rv64im interpreter; returns the exit code (a0 at ecall a7==93) or -1 ----
112func run(m: *u8, entry: i64, BASE: i64) -> i64 {
113 let reg: *i64=sys_mmap(32*8) as *i64; var i: i64=0; while i<32 { reg[i]=0; i=i+1 }
114 var pc: i64=entry; var steps: i64=0; var ec: i64=0-1
115 while steps<K_MAGIC_200000 {
116 let w: i64=memrd32(m, pc-BASE)
117 let op: i64=w&127; let rd: i64=(w>>7)&31; let f3: i64=(w>>12)&7; let rs1: i64=(w>>15)&31; let rs2: i64=(w>>20)&31; let f7: i64=(w>>25)&127
118 var nextpc: i64=pc+4
119 if op==0x13 { if f3==0 { reg[rd]=reg[rs1]+sext12(w>>20) } if f3==1 { reg[rd]=reg[rs1]<<((w>>20)&63) } if f3==5 { reg[rd]=reg[rs1]>>((w>>20)&63) } }
120 if op==0x33 { if f3==0 { if f7==0 { reg[rd]=reg[rs1]+reg[rs2] } if f7==1 { reg[rd]=reg[rs1]*reg[rs2] } if f7==0x20 { reg[rd]=reg[rs1]-reg[rs2] } } if f3==1 { reg[rd]=reg[rs1]<<(reg[rs2]&63) } if f3==7 { reg[rd]=reg[rs1]®[rs2] } if f3==6 { reg[rd]=reg[rs1]|reg[rs2] } if f3==4 { reg[rd]=reg[rs1]^reg[rs2] } }
121 if op==0x37 { reg[rd]=sext32(w&0xFFFFF000) }
122 if op==0x03 { if f3==3 { reg[rd]=memrd64(m, (reg[rs1]+sext12(w>>20))-BASE) } }
123 if op==0x23 { let imm: i64=sext12( (((w>>25)&127)<<5) | ((w>>7)&31) ); if f3==3 { memwr64(m, (reg[rs1]+imm)-BASE, reg[rs2]) } }
124 if op==0x63 { let imm: i64=sext_b(w); var take: i64=0
125 if f3==0 { if reg[rs1]==reg[rs2] { take=1 } } if f3==1 { if reg[rs1]!=reg[rs2] { take=1 } }
126 if f3==4 { if reg[rs1]<reg[rs2] { take=1 } } if f3==5 { if reg[rs1]>=reg[rs2] { take=1 } }
127 if take==1 { nextpc=pc+imm } }
128 if op==0x6F { let imm: i64=(((w>>31)&1)<<20)|(((w>>12)&255)<<12)|(((w>>20)&1)<<11)|(((w>>21)&1023)<<1); var s: i64=imm; if (imm&0x100000)!=0 { s=imm-0x200000 } reg[rd]=pc+4; nextpc=pc+s }
129 if op==0x67 { let t: i64=pc+4; nextpc=(reg[rs1]+sext12(w>>20))&(0-2); reg[rd]=t }
130 if op==0x73 { if reg[17]==93 { ec=reg[10]; steps=K_MAGIC_200001 } }
131 reg[0]=0
132 if steps<K_MAGIC_200001 { pc=nextpc; steps=steps+1 }
133 }
134 return ec
135}