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}