code wiki / (root) / nx_rv64c.nx

nx_rv64c.nx source

↩ module page · 438 lines · 17350 B

1// nx_rv64c.nx -- RV64C compressed-instruction decoder. 2// 3// Given a 16-bit RV64C instruction, returns the 32-bit RV64I/M 4// equivalent so nx_rv64_sim can execute it via its existing decode 5// path. This is the minimal change that lets us run binaries 6// produced by stock gcc / clang -- both emit C-extension 7// instructions whenever -march has +c (which is the default on 8// most distros). 9// 10// Without this, every other ld/st/branch in a real binary aborts 11// the simulator. With it, the sim runs unmodified RV64GC code. 12// 13// Encoding reference: RISC-V Unprivileged Spec v20191213-Compressed 14// chapter 16 (https://riscv.org/specifications/), Table 16.4 / 16.5. 15// 16// Layout: 17// bits[1:0] = quadrant (00 / 01 / 10 / 11=non-compressed) 18// bits[15:13] = funct3 within quadrant 19// 20// Approach: 21// - One dispatcher per quadrant (q0_decode / q1_decode / q2_decode) 22// - Each dispatcher pulls funct3 + relevant operand fields, 23// reconstructs a 32-bit RV64I instruction word using the 24// standard R/I/S/B/U/J encoders below. 25// - Helper register-mapping: c.* "rs1'" / "rs2'" / "rd'" fields 26// are 3-bit and map to x8..x15 (s0/s1/a0..a5). 27// 28// Returns the expanded 32-bit instruction on success, or 0 on 29// "illegal compressed insn" (which the caller should treat as a 30// trap / fault). We never return 0 for valid c.nop (which expands 31// to addi x0, x0, 0 = 0x00000013). 32 33// nx_safety_envelope: 34// intended_use: AUTO_APPLIED -- primitive-specific tuning queued 35// sil_target: SIL1 36// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail] 37// verdict: NOT_YET_EVALUATED 38 39import "syscalls.nx" 40 41// ---- field extraction --------------------------------------------- 42 43func nx_c_field(insn: i64, hi: i64, lo: i64) -> i64 { 44 let mask: i64 = (1 << (hi - lo + 1)) - 1 45 return (insn >> lo) & mask 46} 47 48// rs1'/rs2'/rd' (3-bit) -> x8..x15 49func nx_c_reg3(r3: i64) -> i64 { 50 return r3 + 8 51} 52 53// ---- 32-bit instruction encoders (subset we need to emit) -------- 54 55func nx_c_enc_r(opcode: i64, rd: i64, funct3: i64, rs1: i64, rs2: i64, funct7: i64) -> i64 { 56 return (opcode & 0x7F) 57 | ((rd & 0x1F) << 7) 58 | ((funct3 & 0x7) << 12) 59 | ((rs1 & 0x1F) << 15) 60 | ((rs2 & 0x1F) << 20) 61 | ((funct7 & 0x7F) << 25) 62} 63 64func nx_c_enc_i(opcode: i64, rd: i64, funct3: i64, rs1: i64, imm: i64) -> i64 { 65 return (opcode & 0x7F) 66 | ((rd & 0x1F) << 7) 67 | ((funct3 & 0x7) << 12) 68 | ((rs1 & 0x1F) << 15) 69 | ((imm & 0xFFF) << 20) 70} 71 72func nx_c_enc_s(opcode: i64, funct3: i64, rs1: i64, rs2: i64, imm: i64) -> i64 { 73 let imm_lo: i64 = imm & 0x1F 74 let imm_hi: i64 = (imm >> 5) & 0x7F 75 return (opcode & 0x7F) 76 | (imm_lo << 7) 77 | ((funct3 & 0x7) << 12) 78 | ((rs1 & 0x1F) << 15) 79 | ((rs2 & 0x1F) << 20) 80 | (imm_hi << 25) 81} 82 83func nx_c_enc_b(opcode: i64, funct3: i64, rs1: i64, rs2: i64, imm: i64) -> i64 { 84 let bit11: i64 = (imm >> 11) & 0x1 85 let bits1to4: i64 = (imm >> 1) & 0xF 86 let bits5to10: i64 = (imm >> 5) & 0x3F 87 let bit12: i64 = (imm >> 12) & 0x1 88 return (opcode & 0x7F) 89 | (bit11 << 7) 90 | (bits1to4 << 8) 91 | ((funct3 & 0x7) << 12) 92 | ((rs1 & 0x1F) << 15) 93 | ((rs2 & 0x1F) << 20) 94 | (bits5to10 << 25) 95 | (bit12 << 31) 96} 97 98func nx_c_enc_j(opcode: i64, rd: i64, imm: i64) -> i64 { 99 let bits12to19: i64 = (imm >> 12) & 0xFF 100 let bit11: i64 = (imm >> 11) & 0x1 101 let bits1to10: i64 = (imm >> 1) & 0x3FF 102 let bit20: i64 = (imm >> 20) & 0x1 103 return (opcode & 0x7F) 104 | ((rd & 0x1F) << 7) 105 | (bits12to19 << 12) 106 | (bit11 << 20) 107 | (bits1to10 << 21) 108 | (bit20 << 31) 109} 110 111func nx_c_enc_u(opcode: i64, rd: i64, imm: i64) -> i64 { 112 return (opcode & 0x7F) 113 | ((rd & 0x1F) << 7) 114 | ((imm & 0xFFFFF) << 12) 115} 116 117// ---- sign-extend helpers -------------------------------------------- 118 119// sign-extend `value` from `bits` bits. 120func nx_c_sext(value: i64, bits: i64) -> i64 { 121 let m: i64 = 1 << (bits - 1) 122 return (value ^ m) - m 123} 124 125// ---- quadrant 0: 00 --------------------------------------------- 126 127// Quadrant 0 covers c.addi4spn / c.fld / c.lw / c.ld / c.fsd / 128// c.sw / c.sd. We implement the integer ones; FP gets traps for 129// now (sim treats unknown 32-bit op as fault). 130func nx_c_decode_q0(insn: i64) -> i64 { 131 let f3: i64 = nx_c_field(insn, 15, 13) 132 let rd_p: i64 = nx_c_field(insn, 4, 2) // rd' 133 let rs1_p: i64 = nx_c_field(insn, 9, 7) // rs1' 134 let rs2_p: i64 = nx_c_field(insn, 4, 2) // rs2' 135 136 if f3 == 0 { 137 // c.addi4spn: addi rd', x2, nzuimm 138 if insn == 0 { return 0 } // illegal 139 let nz: i64 = ((insn >> 11) & 0x3) << 4 // bits[5:4] -> imm[5:4] 140 let nz2: i64 = ((insn >> 7) & 0xF) << 6 // bits[10:7] -> imm[9:6] 141 let nz3: i64 = ((insn >> 6) & 0x1) << 2 // bit[6] -> imm[2] 142 let nz4: i64 = ((insn >> 5) & 0x1) << 3 // bit[5] -> imm[3] 143 let imm: i64 = nz | nz2 | nz3 | nz4 144 return nx_c_enc_i(0x13, nx_c_reg3(rd_p), 0, 2, imm) 145 } 146 if f3 == 2 { 147 // c.lw: lw rd', offset(rs1') ; offset = uimm[6|2|5:3]<<2 148 let o1: i64 = ((insn >> 5) & 0x1) << 6 149 let o2: i64 = ((insn >> 6) & 0x1) << 2 150 let o3: i64 = ((insn >> 10) & 0x7) << 3 151 let off: i64 = o1 | o2 | o3 152 return nx_c_enc_i(0x3, nx_c_reg3(rd_p), 2, nx_c_reg3(rs1_p), off) 153 } 154 if f3 == 3 { 155 // c.ld: ld rd', offset(rs1') ; offset = uimm[7:6|5:3]<<3 156 let o1: i64 = ((insn >> 5) & 0x3) << 6 157 let o2: i64 = ((insn >> 10) & 0x7) << 3 158 let off: i64 = o1 | o2 159 return nx_c_enc_i(0x3, nx_c_reg3(rd_p), 3, nx_c_reg3(rs1_p), off) 160 } 161 if f3 == 6 { 162 // c.sw: sw rs2', offset(rs1') ; same offset as c.lw 163 let o1: i64 = ((insn >> 5) & 0x1) << 6 164 let o2: i64 = ((insn >> 6) & 0x1) << 2 165 let o3: i64 = ((insn >> 10) & 0x7) << 3 166 let off: i64 = o1 | o2 | o3 167 return nx_c_enc_s(0x23, 2, nx_c_reg3(rs1_p), nx_c_reg3(rs2_p), off) 168 } 169 if f3 == 7 { 170 // c.sd: sd rs2', offset(rs1') ; same offset as c.ld 171 let o1: i64 = ((insn >> 5) & 0x3) << 6 172 let o2: i64 = ((insn >> 10) & 0x7) << 3 173 let off: i64 = o1 | o2 174 return nx_c_enc_s(0x23, 3, nx_c_reg3(rs1_p), nx_c_reg3(rs2_p), off) 175 } 176 return 0 // FP variants (c.fld/c.fsd) -> illegal in this sim today 177} 178 179// ---- quadrant 1: 01 --------------------------------------------- 180 181// Q1 covers c.nop / c.addi / c.addiw / c.li / c.addi16sp / c.lui / 182// arithmetic immediates (srli/srai/andi/sub/xor/or/and/subw/addw), 183// c.j / c.beqz / c.bnez. 184func nx_c_decode_q1(insn: i64) -> i64 { 185 let f3: i64 = nx_c_field(insn, 15, 13) 186 let rd: i64 = nx_c_field(insn, 11, 7) 187 let imm5: i64 = nx_c_field(insn, 6, 2) 188 let bit12: i64 = nx_c_field(insn, 12, 12) 189 190 if f3 == 0 { 191 // c.nop / c.addi: addi rd, rd, sext6(imm) 192 let imm: i64 = nx_c_sext((bit12 << 5) | imm5, 6) 193 return nx_c_enc_i(0x13, rd, 0, rd, imm & 0xFFF) 194 } 195 if f3 == 1 { 196 // c.addiw: addiw rd, rd, sext6(imm) 197 let imm: i64 = nx_c_sext((bit12 << 5) | imm5, 6) 198 return nx_c_enc_i(0x1B, rd, 0, rd, imm & 0xFFF) 199 } 200 if f3 == 2 { 201 // c.li: addi rd, x0, sext6(imm) 202 let imm: i64 = nx_c_sext((bit12 << 5) | imm5, 6) 203 return nx_c_enc_i(0x13, rd, 0, 0, imm & 0xFFF) 204 } 205 if f3 == 3 { 206 // c.addi16sp (rd==2) or c.lui (rd!=0,2) 207 if rd == 2 { 208 let i6: i64 = ((insn >> 6) & 0x1) << 4 // imm[4] 209 let i7: i64 = ((insn >> 5) & 0x1) << 6 // imm[6] 210 let i8: i64 = ((insn >> 3) & 0x3) << 7 // imm[8:7] 211 let i5: i64 = ((insn >> 2) & 0x1) << 5 // imm[5] 212 let i9: i64 = bit12 << 9 // imm[9] 213 let raw: i64 = i6 | i7 | i8 | i5 | i9 214 let imm: i64 = nx_c_sext(raw, 10) 215 return nx_c_enc_i(0x13, 2, 0, 2, imm & 0xFFF) 216 } 217 if rd != 0 { 218 // c.lui: lui rd, sext_to_18(imm)<<12, encoded as U-type 219 let raw: i64 = (bit12 << 17) | (imm5 << 12) 220 let imm: i64 = nx_c_sext(raw, 18) >> 12 // U-imm field 221 return nx_c_enc_u(0x37, rd, imm & 0xFFFFF) 222 } 223 return 0 224 } 225 if f3 == 4 { 226 let f2: i64 = nx_c_field(insn, 11, 10) 227 let rs1_p: i64 = nx_c_field(insn, 9, 7) 228 let rs2_p: i64 = nx_c_field(insn, 4, 2) 229 if f2 == 0 { 230 // c.srli: srli rd', rd', uimm6 231 let sh: i64 = (bit12 << 5) | imm5 232 return nx_c_enc_i(0x13, nx_c_reg3(rs1_p), 5, nx_c_reg3(rs1_p), sh) 233 } 234 if f2 == 1 { 235 // c.srai: srai rd', rd', uimm6 (funct7=0x20 in I-imm form) 236 let sh: i64 = (bit12 << 5) | imm5 237 return nx_c_enc_i(0x13, nx_c_reg3(rs1_p), 5, nx_c_reg3(rs1_p), sh | (0x20 << 5)) 238 } 239 if f2 == 2 { 240 // c.andi: andi rd', rd', sext6 241 let imm: i64 = nx_c_sext((bit12 << 5) | imm5, 6) 242 return nx_c_enc_i(0x13, nx_c_reg3(rs1_p), 7, nx_c_reg3(rs1_p), imm & 0xFFF) 243 } 244 if f2 == 3 { 245 // c.sub/xor/or/and (bit12=0) or c.subw/addw (bit12=1) 246 let f2b: i64 = nx_c_field(insn, 6, 5) 247 let rd_full: i64 = nx_c_reg3(rs1_p) 248 let rs2_full: i64 = nx_c_reg3(rs2_p) 249 if bit12 == 0 { 250 if f2b == 0 { return nx_c_enc_r(0x33, rd_full, 0, rd_full, rs2_full, 0x20) } // sub 251 if f2b == 1 { return nx_c_enc_r(0x33, rd_full, 4, rd_full, rs2_full, 0) } // xor 252 if f2b == 2 { return nx_c_enc_r(0x33, rd_full, 6, rd_full, rs2_full, 0) } // or 253 if f2b == 3 { return nx_c_enc_r(0x33, rd_full, 7, rd_full, rs2_full, 0) } // and 254 } else { 255 if f2b == 0 { return nx_c_enc_r(0x3B, rd_full, 0, rd_full, rs2_full, 0x20) } // subw 256 if f2b == 1 { return nx_c_enc_r(0x3B, rd_full, 0, rd_full, rs2_full, 0) } // addw 257 } 258 return 0 259 } 260 return 0 261 } 262 if f3 == 5 { 263 // c.j: jal x0, offset 264 let bA: i64 = ((insn >> 12) & 0x1) << 11 265 let bB: i64 = ((insn >> 11) & 0x1) << 4 266 let bC: i64 = ((insn >> 9) & 0x3) << 8 267 let bD: i64 = ((insn >> 8) & 0x1) << 10 268 let bE: i64 = ((insn >> 7) & 0x1) << 6 269 let bF: i64 = ((insn >> 6) & 0x1) << 7 270 let bG: i64 = ((insn >> 3) & 0x7) << 1 271 let bH: i64 = ((insn >> 2) & 0x1) << 5 272 let raw: i64 = bA | bB | bC | bD | bE | bF | bG | bH 273 let imm: i64 = nx_c_sext(raw, 12) 274 return nx_c_enc_j(0x6F, 0, imm) 275 } 276 if f3 == 6 { 277 // c.beqz: beq rs1', x0, offset 278 let rs1_p: i64 = nx_c_field(insn, 9, 7) 279 let bA: i64 = ((insn >> 12) & 0x1) << 8 280 let bB: i64 = ((insn >> 10) & 0x3) << 3 281 let bC: i64 = ((insn >> 5) & 0x3) << 6 282 let bD: i64 = ((insn >> 3) & 0x3) << 1 283 let bE: i64 = ((insn >> 2) & 0x1) << 5 284 let raw: i64 = bA | bB | bC | bD | bE 285 let imm: i64 = nx_c_sext(raw, 9) 286 return nx_c_enc_b(0x63, 0, nx_c_reg3(rs1_p), 0, imm) 287 } 288 if f3 == 7 { 289 // c.bnez: bne rs1', x0, offset 290 let rs1_p: i64 = nx_c_field(insn, 9, 7) 291 let bA: i64 = ((insn >> 12) & 0x1) << 8 292 let bB: i64 = ((insn >> 10) & 0x3) << 3 293 let bC: i64 = ((insn >> 5) & 0x3) << 6 294 let bD: i64 = ((insn >> 3) & 0x3) << 1 295 let bE: i64 = ((insn >> 2) & 0x1) << 5 296 let raw: i64 = bA | bB | bC | bD | bE 297 let imm: i64 = nx_c_sext(raw, 9) 298 return nx_c_enc_b(0x63, 1, nx_c_reg3(rs1_p), 0, imm) 299 } 300 return 0 301} 302 303// ---- quadrant 2: 10 --------------------------------------------- 304 305// Q2 covers c.slli, c.fldsp/lwsp/ldsp, c.jr/c.mv/c.ebreak/c.jalr/c.add, 306// c.fsdsp/c.swsp/c.sdsp. 307func nx_c_decode_q2(insn: i64) -> i64 { 308 let f3: i64 = nx_c_field(insn, 15, 13) 309 let rd: i64 = nx_c_field(insn, 11, 7) 310 let bit12: i64 = nx_c_field(insn, 12, 12) 311 let imm5: i64 = nx_c_field(insn, 6, 2) 312 313 if f3 == 0 { 314 // c.slli: slli rd, rd, uimm6 315 let sh: i64 = (bit12 << 5) | imm5 316 return nx_c_enc_i(0x13, rd, 1, rd, sh) 317 } 318 if f3 == 2 { 319 // c.lwsp: lw rd, offset(x2) ; offset = uimm[7:6|4:2]<<2 320 let o1: i64 = ((insn >> 4) & 0x7) << 2 // imm[4:2] 321 let o2: i64 = ((insn >> 12) & 0x1) << 5 // imm[5] 322 let o3: i64 = ((insn >> 2) & 0x3) << 6 // imm[7:6] 323 let off: i64 = o1 | o2 | o3 324 return nx_c_enc_i(0x3, rd, 2, 2, off) 325 } 326 if f3 == 3 { 327 // c.ldsp: ld rd, offset(x2) ; offset = uimm[8:6|4:3]<<3 328 let o1: i64 = ((insn >> 5) & 0x3) << 3 // imm[4:3] 329 let o2: i64 = ((insn >> 12) & 0x1) << 5 // imm[5] 330 let o3: i64 = ((insn >> 2) & 0x7) << 6 // imm[8:6] 331 let off: i64 = o1 | o2 | o3 332 return nx_c_enc_i(0x3, rd, 3, 2, off) 333 } 334 if f3 == 4 { 335 let rs2: i64 = nx_c_field(insn, 6, 2) 336 if bit12 == 0 { 337 if rs2 == 0 { 338 // c.jr: jalr x0, 0(rd) 339 if rd == 0 { return 0 } 340 return nx_c_enc_i(0x67, 0, 0, rd, 0) 341 } 342 // c.mv: add rd, x0, rs2 343 return nx_c_enc_r(0x33, rd, 0, 0, rs2, 0) 344 } 345 if rs2 == 0 { 346 if rd == 0 { 347 // c.ebreak 348 return nx_c_enc_i(0x73, 0, 0, 0, 1) 349 } 350 // c.jalr: jalr x1, 0(rd) 351 return nx_c_enc_i(0x67, 1, 0, rd, 0) 352 } 353 // c.add: add rd, rd, rs2 354 return nx_c_enc_r(0x33, rd, 0, rd, rs2, 0) 355 } 356 if f3 == 6 { 357 // c.swsp: sw rs2, offset(x2) ; offset = uimm[7:6|5:2]<<2 358 let rs2: i64 = nx_c_field(insn, 6, 2) 359 let o1: i64 = ((insn >> 9) & 0xF) << 2 360 let o2: i64 = ((insn >> 7) & 0x3) << 6 361 let off: i64 = o1 | o2 362 return nx_c_enc_s(0x23, 2, 2, rs2, off) 363 } 364 if f3 == 7 { 365 // c.sdsp: sd rs2, offset(x2) ; offset = uimm[8:6|5:3]<<3 366 let rs2: i64 = nx_c_field(insn, 6, 2) 367 let o1: i64 = ((insn >> 10) & 0x7) << 3 368 let o2: i64 = ((insn >> 7) & 0x7) << 6 369 let off: i64 = o1 | o2 370 return nx_c_enc_s(0x23, 3, 2, rs2, off) 371 } 372 return 0 // c.fldsp / c.fsdsp -> illegal in this sim today 373} 374 375// ---- top-level ----------------------------------------------------- 376 377// Returns: 378// * The 32-bit RV64I/M expansion if `insn` is a valid 16-bit RV64C op. 379// * 0 if the 16-bit insn is illegal. 380// The caller must NOT pass non-compressed instructions (low 2 bits 11): 381// it should branch on (insn & 3) != 3 first. 382func nx_rv64c_decode(insn: i64) -> i64 { 383 let q: i64 = insn & 3 384 if q == 0 { return nx_c_decode_q0(insn) } 385 if q == 1 { return nx_c_decode_q1(insn) } 386 if q == 2 { return nx_c_decode_q2(insn) } 387 return 0 // q==3 means caller bug 388} 389 390// True if the bottom 2 bits indicate a 16-bit compressed instruction. 391func nx_rv64c_is_compressed(low_bits: i64) -> i64 { 392 if (low_bits & 3) != 3 { return 1 } 393 return 0 394} 395 396// ---- self-test --------------------------------------------------- 397 398func main() -> i64 { 399 // c.nop = 0x0001 -> addi x0, x0, 0 = 0x00000013 400 if nx_rv64c_decode(0x0001) != 0x00000013 { return __syscall(93, 1, 0, 0, 0, 0, 0) } 401 402 // c.addi x1, 5 = 0x0095 (funct3=000, rd=00001, imm[4:0]=00101, bit12=0) 403 // bits: 000 0 00001 00101 01 = 0000 0000 1001 0101 = 0x0095 404 // expand: addi x1, x1, 5 = 0x00508093 405 if nx_rv64c_decode(0x0095) != 0x00508093 { return __syscall(93, 2, 0, 0, 0, 0, 0) } 406 407 // c.li x1, -1 = 0x57FD (funct3=010, rd=00001, imm[4:0]=11111, bit12=1) 408 // bits: 010 1 00001 11111 01 = 0101 0000 1111 1101 = 0x50FD 409 // expand: addi x1, x0, -1 = 0xFFF00093 410 if nx_rv64c_decode(0x50FD) != 0xFFF00093 { return __syscall(93, 3, 0, 0, 0, 0, 0) } 411 412 // c.jr x1 = 0x8082 (funct3=100, bit12=0, rd/rs1=00001, rs2=00000, op=10) 413 // bits: 100 0 00001 00000 10 = 1000 0000 1000 0010 = 0x8082 414 // expand: jalr x0, 0(x1) = 0x00008067 415 if nx_rv64c_decode(0x8082) != 0x00008067 { return __syscall(93, 4, 0, 0, 0, 0, 0) } 416 417 // c.mv x10, x11 = 0x852E (funct3=100, bit12=0, rd=01010, rs2=01011, op=10) 418 // bits: 100 0 01010 01011 10 = 1000 0101 0010 1110 = 0x852E 419 // expand: add x10, x0, x11 = 0x00B00533 420 if nx_rv64c_decode(0x852E) != 0x00B00533 { return __syscall(93, 5, 0, 0, 0, 0, 0) } 421 422 // c.add x10, x11 = 0x952E (funct3=100, bit12=1, rd=01010, rs2=01011, op=10) 423 // bits: 100 1 01010 01011 10 = 1001 0101 0010 1110 = 0x952E 424 // expand: add x10, x10, x11 = 0x00B50533 425 if nx_rv64c_decode(0x952E) != 0x00B50533 { return __syscall(93, 6, 0, 0, 0, 0, 0) } 426 427 // c.ebreak = 0x9002 (funct3=100, bit12=1, rd=00000, rs2=00000, op=10) 428 // bits: 100 1 00000 00000 10 = 1001 0000 0000 0010 = 0x9002 429 // expand: ebreak = 0x00100073 430 if nx_rv64c_decode(0x9002) != 0x00100073 { return __syscall(93, 7, 0, 0, 0, 0, 0) } 431 432 // is_compressed predicate 433 if nx_rv64c_is_compressed(0x01) != 1 { return __syscall(93, 8, 0, 0, 0, 0, 0) } // q==01 434 if nx_rv64c_is_compressed(0x02) != 1 { return __syscall(93, 9, 0, 0, 0, 0, 0) } // q==10 435 if nx_rv64c_is_compressed(0x03) != 0 { return __syscall(93, 10, 0, 0, 0, 0, 0) } // q==11 = full 436 437 return 0 438}