code wiki / (root) / nx_riscv_lib.nx

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]&reg[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}