isa.h (30893B)
1 /* RV64 instruction encoders + descriptor table — single source of truth 2 * for every instruction the encoder, decoder, and disassembler need to 3 * agree on. Mirrors the aa64_isa.[ch] pattern. 4 * 5 * The bottom of this header (after the `rv_*` inline encoders) declares 6 * the format-kind enum and per-format pack/unpack helpers. The 7 * descriptor table itself lives in isa.c. */ 8 9 #ifndef KIT_RV64_ISA_H 10 #define KIT_RV64_ISA_H 11 12 #include "core/core.h" 13 #include "core/slice.h" 14 #include "core/strbuf.h" 15 16 /* ---- Named registers (DWARF / psABI numbering matches HW) ---- */ 17 enum { 18 RV_X0 = 0, 19 RV_ZERO = 0, 20 RV_X1 = 1, 21 RV_RA = 1, 22 RV_X2 = 2, 23 RV_SP = 2, 24 RV_X3 = 3, 25 RV_GP = 3, 26 RV_X4 = 4, 27 RV_TP = 4, 28 RV_X5 = 5, 29 RV_T0 = 5, 30 RV_X6 = 6, 31 RV_T1 = 6, 32 RV_X7 = 7, 33 RV_T2 = 7, 34 RV_X8 = 8, 35 RV_S0 = 8, 36 RV_FP = 8, 37 RV_X9 = 9, 38 RV_S1 = 9, 39 RV_X10 = 10, 40 RV_A0 = 10, 41 RV_X11 = 11, 42 RV_A1 = 11, 43 RV_X12 = 12, 44 RV_A2 = 12, 45 RV_X13 = 13, 46 RV_A3 = 13, 47 RV_X14 = 14, 48 RV_A4 = 14, 49 RV_X15 = 15, 50 RV_A5 = 15, 51 RV_X16 = 16, 52 RV_A6 = 16, 53 RV_X17 = 17, 54 RV_A7 = 17, 55 RV_X18 = 18, 56 RV_S2 = 18, 57 RV_X27 = 27, 58 RV_S11 = 27, 59 RV_X28 = 28, 60 RV_T3 = 28, 61 RV_X29 = 29, 62 RV_T4 = 29, 63 RV_X30 = 30, 64 RV_T5 = 30, 65 RV_X31 = 31, 66 RV_T6 = 31, 67 }; 68 69 #define RV_NOP 0x00000013u /* ADDI x0, x0, 0 */ 70 71 /* ---- Format helpers ---- 72 * 73 * R-type: funct7(31:25) rs2(24:20) rs1(19:15) funct3(14:12) rd(11:7) op(6:0) 74 * I-type: imm(31:20) rs1(19:15) funct3(14:12) rd(11:7) op(6:0) 75 * S-type: imm[11:5](31:25) rs2(24:20) rs1(19:15) funct3(14:12) imm[4:0](11:7) 76 * op(6:0) B-type: imm[12](31) imm[10:5](30:25) rs2(24:20) rs1(19:15) 77 * funct3(14:12) imm[4:1](11:8) imm[11](7) op(6:0) U-type: imm[31:12](31:12) 78 * rd(11:7) op(6:0) J-type: imm[20](31) imm[10:1](30:21) imm[11](20) 79 * imm[19:12](19:12) rd(11:7) op(6:0) 80 */ 81 82 static inline u32 rv_r(u32 funct7, u32 rs2, u32 rs1, u32 funct3, u32 rd, 83 u32 op) { 84 return ((funct7 & 0x7fu) << 25) | ((rs2 & 0x1fu) << 20) | 85 ((rs1 & 0x1fu) << 15) | ((funct3 & 0x7u) << 12) | ((rd & 0x1fu) << 7) | 86 (op & 0x7fu); 87 } 88 static inline u32 rv_i(i32 imm12, u32 rs1, u32 funct3, u32 rd, u32 op) { 89 return (((u32)imm12 & 0xfffu) << 20) | ((rs1 & 0x1fu) << 15) | 90 ((funct3 & 0x7u) << 12) | ((rd & 0x1fu) << 7) | (op & 0x7fu); 91 } 92 /* S/B/J immediate bit-scramble — the immediate-field bits only (no 93 * register/funct/op). Single source of truth for both the codegen encoders 94 * (rv_s/rv_b/rv_j below) and the assembler's match-word overlay encoders 95 * (enc_s/enc_b/enc_j in asm.c), which were byte-identical reimplementations. */ 96 static inline u32 rv_imm_s(i32 imm12) { 97 u32 ui = (u32)imm12 & 0xfffu; 98 return ((ui >> 5) << 25) | ((ui & 0x1fu) << 7); 99 } 100 static inline u32 rv_imm_b(i32 imm13) { 101 u32 ui = (u32)imm13; 102 return (((ui >> 12) & 1u) << 31) | (((ui >> 5) & 0x3fu) << 25) | 103 (((ui >> 1) & 0xfu) << 8) | (((ui >> 11) & 1u) << 7); 104 } 105 static inline u32 rv_imm_j(i32 imm21) { 106 u32 ui = (u32)imm21; 107 return (((ui >> 20) & 1u) << 31) | (((ui >> 1) & 0x3ffu) << 21) | 108 (((ui >> 11) & 1u) << 20) | (((ui >> 12) & 0xffu) << 12); 109 } 110 111 static inline u32 rv_s(i32 imm12, u32 rs2, u32 rs1, u32 funct3, u32 op) { 112 return rv_imm_s(imm12) | ((rs2 & 0x1fu) << 20) | ((rs1 & 0x1fu) << 15) | 113 ((funct3 & 0x7u) << 12) | (op & 0x7fu); 114 } 115 static inline u32 rv_b(i32 imm13, u32 rs2, u32 rs1, u32 funct3, u32 op) { 116 return rv_imm_b(imm13) | ((rs2 & 0x1fu) << 20) | ((rs1 & 0x1fu) << 15) | 117 ((funct3 & 0x7u) << 12) | (op & 0x7fu); 118 } 119 static inline u32 rv_u(u32 imm32_hi20, u32 rd, u32 op) { 120 return (imm32_hi20 & 0xfffff000u) | ((rd & 0x1fu) << 7) | (op & 0x7fu); 121 } 122 static inline u32 rv_j(i32 imm21, u32 rd, u32 op) { 123 return rv_imm_j(imm21) | ((rd & 0x1fu) << 7) | (op & 0x7fu); 124 } 125 126 /* ---- Integer ops (RV32I/RV64I) ---- */ 127 128 #define RV_OP 0x33u 129 #define RV_OP_IMM 0x13u 130 #define RV_OP_32 0x3bu 131 #define RV_OP_IMM_32 0x1bu 132 #define RV_LUI 0x37u 133 #define RV_AUIPC 0x17u 134 #define RV_LOAD 0x03u 135 #define RV_STORE 0x23u 136 #define RV_BRANCH 0x63u 137 #define RV_JAL 0x6fu 138 #define RV_JALR 0x67u 139 #define RV_LOAD_FP 0x07u 140 #define RV_STORE_FP 0x27u 141 #define RV_OP_FP 0x53u 142 #define RV_MADD 0x43u 143 #define RV_MSUB 0x47u 144 #define RV_NMSUB 0x4bu 145 #define RV_NMADD 0x4fu 146 #define RV_AMO 0x2fu 147 #define RV_FENCE 0x0fu 148 #define RV_SYSTEM 0x73u 149 150 static inline u32 rv_add(u32 rd, u32 rs1, u32 rs2) { 151 return rv_r(0x00, rs2, rs1, 0x0, rd, RV_OP); 152 } 153 static inline u32 rv_sub(u32 rd, u32 rs1, u32 rs2) { 154 return rv_r(0x20, rs2, rs1, 0x0, rd, RV_OP); 155 } 156 static inline u32 rv_sll(u32 rd, u32 rs1, u32 rs2) { 157 return rv_r(0x00, rs2, rs1, 0x1, rd, RV_OP); 158 } 159 static inline u32 rv_slt(u32 rd, u32 rs1, u32 rs2) { 160 return rv_r(0x00, rs2, rs1, 0x2, rd, RV_OP); 161 } 162 static inline u32 rv_sltu(u32 rd, u32 rs1, u32 rs2) { 163 return rv_r(0x00, rs2, rs1, 0x3, rd, RV_OP); 164 } 165 static inline u32 rv_xor(u32 rd, u32 rs1, u32 rs2) { 166 return rv_r(0x00, rs2, rs1, 0x4, rd, RV_OP); 167 } 168 static inline u32 rv_srl(u32 rd, u32 rs1, u32 rs2) { 169 return rv_r(0x00, rs2, rs1, 0x5, rd, RV_OP); 170 } 171 static inline u32 rv_sra(u32 rd, u32 rs1, u32 rs2) { 172 return rv_r(0x20, rs2, rs1, 0x5, rd, RV_OP); 173 } 174 static inline u32 rv_or(u32 rd, u32 rs1, u32 rs2) { 175 return rv_r(0x00, rs2, rs1, 0x6, rd, RV_OP); 176 } 177 static inline u32 rv_and(u32 rd, u32 rs1, u32 rs2) { 178 return rv_r(0x00, rs2, rs1, 0x7, rd, RV_OP); 179 } 180 181 static inline u32 rv_addw(u32 rd, u32 rs1, u32 rs2) { 182 return rv_r(0x00, rs2, rs1, 0x0, rd, RV_OP_32); 183 } 184 static inline u32 rv_subw(u32 rd, u32 rs1, u32 rs2) { 185 return rv_r(0x20, rs2, rs1, 0x0, rd, RV_OP_32); 186 } 187 static inline u32 rv_sllw(u32 rd, u32 rs1, u32 rs2) { 188 return rv_r(0x00, rs2, rs1, 0x1, rd, RV_OP_32); 189 } 190 static inline u32 rv_srlw(u32 rd, u32 rs1, u32 rs2) { 191 return rv_r(0x00, rs2, rs1, 0x5, rd, RV_OP_32); 192 } 193 static inline u32 rv_sraw(u32 rd, u32 rs1, u32 rs2) { 194 return rv_r(0x20, rs2, rs1, 0x5, rd, RV_OP_32); 195 } 196 197 static inline u32 rv_addi(u32 rd, u32 rs1, i32 imm) { 198 return rv_i(imm, rs1, 0x0, rd, RV_OP_IMM); 199 } 200 static inline u32 rv_slti(u32 rd, u32 rs1, i32 imm) { 201 return rv_i(imm, rs1, 0x2, rd, RV_OP_IMM); 202 } 203 static inline u32 rv_sltiu(u32 rd, u32 rs1, i32 imm) { 204 return rv_i(imm, rs1, 0x3, rd, RV_OP_IMM); 205 } 206 static inline u32 rv_xori(u32 rd, u32 rs1, i32 imm) { 207 return rv_i(imm, rs1, 0x4, rd, RV_OP_IMM); 208 } 209 static inline u32 rv_ori(u32 rd, u32 rs1, i32 imm) { 210 return rv_i(imm, rs1, 0x6, rd, RV_OP_IMM); 211 } 212 static inline u32 rv_andi(u32 rd, u32 rs1, i32 imm) { 213 return rv_i(imm, rs1, 0x7, rd, RV_OP_IMM); 214 } 215 216 /* Shift-immediate forms. RV64I uses a 6-bit shamt in bits 25:20 and a 217 * 6-bit funct6 in bits 31:26 (so the funct7-vs-shamt[5] split that 218 * rv_r() does is wrong here — we hand-assemble these). */ 219 static inline u32 rv_slli(u32 rd, u32 rs1, u32 sh) { 220 return (0x00u << 26) | ((sh & 0x3fu) << 20) | ((rs1 & 0x1fu) << 15) | 221 (0x1u << 12) | ((rd & 0x1fu) << 7) | RV_OP_IMM; 222 } 223 static inline u32 rv_srli(u32 rd, u32 rs1, u32 sh) { 224 return (0x00u << 26) | ((sh & 0x3fu) << 20) | ((rs1 & 0x1fu) << 15) | 225 (0x5u << 12) | ((rd & 0x1fu) << 7) | RV_OP_IMM; 226 } 227 static inline u32 rv_srai(u32 rd, u32 rs1, u32 sh) { 228 return (0x10u << 26) | ((sh & 0x3fu) << 20) | ((rs1 & 0x1fu) << 15) | 229 (0x5u << 12) | ((rd & 0x1fu) << 7) | RV_OP_IMM; 230 } 231 232 static inline u32 rv_addiw(u32 rd, u32 rs1, i32 imm) { 233 return rv_i(imm, rs1, 0x0, rd, RV_OP_IMM_32); 234 } 235 static inline u32 rv_slliw(u32 rd, u32 rs1, u32 sh) { 236 return rv_r(0x00, sh & 0x1fu, rs1, 0x1, rd, RV_OP_IMM_32); 237 } 238 static inline u32 rv_srliw(u32 rd, u32 rs1, u32 sh) { 239 return rv_r(0x00, sh & 0x1fu, rs1, 0x5, rd, RV_OP_IMM_32); 240 } 241 static inline u32 rv_sraiw(u32 rd, u32 rs1, u32 sh) { 242 return rv_r(0x20, sh & 0x1fu, rs1, 0x5, rd, RV_OP_IMM_32); 243 } 244 245 static inline u32 rv_lui(u32 rd, u32 imm20) { 246 return ((imm20 & 0xfffffu) << 12) | ((rd & 0x1fu) << 7) | RV_LUI; 247 } 248 static inline u32 rv_auipc(u32 rd, u32 imm20) { 249 return ((imm20 & 0xfffffu) << 12) | ((rd & 0x1fu) << 7) | RV_AUIPC; 250 } 251 252 /* M extension */ 253 static inline u32 rv_mul(u32 rd, u32 rs1, u32 rs2) { 254 return rv_r(0x01, rs2, rs1, 0x0, rd, RV_OP); 255 } 256 static inline u32 rv_mulh(u32 rd, u32 rs1, u32 rs2) { 257 return rv_r(0x01, rs2, rs1, 0x1, rd, RV_OP); 258 } 259 static inline u32 rv_mulhsu(u32 rd, u32 rs1, u32 rs2) { 260 return rv_r(0x01, rs2, rs1, 0x2, rd, RV_OP); 261 } 262 static inline u32 rv_mulhu(u32 rd, u32 rs1, u32 rs2) { 263 return rv_r(0x01, rs2, rs1, 0x3, rd, RV_OP); 264 } 265 static inline u32 rv_div(u32 rd, u32 rs1, u32 rs2) { 266 return rv_r(0x01, rs2, rs1, 0x4, rd, RV_OP); 267 } 268 static inline u32 rv_divu(u32 rd, u32 rs1, u32 rs2) { 269 return rv_r(0x01, rs2, rs1, 0x5, rd, RV_OP); 270 } 271 static inline u32 rv_rem(u32 rd, u32 rs1, u32 rs2) { 272 return rv_r(0x01, rs2, rs1, 0x6, rd, RV_OP); 273 } 274 static inline u32 rv_remu(u32 rd, u32 rs1, u32 rs2) { 275 return rv_r(0x01, rs2, rs1, 0x7, rd, RV_OP); 276 } 277 static inline u32 rv_mulw(u32 rd, u32 rs1, u32 rs2) { 278 return rv_r(0x01, rs2, rs1, 0x0, rd, RV_OP_32); 279 } 280 static inline u32 rv_divw(u32 rd, u32 rs1, u32 rs2) { 281 return rv_r(0x01, rs2, rs1, 0x4, rd, RV_OP_32); 282 } 283 static inline u32 rv_divuw(u32 rd, u32 rs1, u32 rs2) { 284 return rv_r(0x01, rs2, rs1, 0x5, rd, RV_OP_32); 285 } 286 static inline u32 rv_remw(u32 rd, u32 rs1, u32 rs2) { 287 return rv_r(0x01, rs2, rs1, 0x6, rd, RV_OP_32); 288 } 289 static inline u32 rv_remuw(u32 rd, u32 rs1, u32 rs2) { 290 return rv_r(0x01, rs2, rs1, 0x7, rd, RV_OP_32); 291 } 292 293 /* Zba (address-generation) subset — assumed available on rv64 targets. 294 * SH{1,2,3}ADD rd, rs1, rs2 computes rd = (rs1 << {1,2,3}) + rs2 in one 295 * instruction (funct7=0x10, opcode=OP). Used by load/store to fold an 296 * indexed effective address `base + (index << log2_scale)` into a single 297 * scratch register without an explicit shift+add pair. */ 298 static inline u32 rv_sh1add(u32 rd, u32 rs1, u32 rs2) { 299 return rv_r(0x10, rs2, rs1, 0x2, rd, RV_OP); 300 } 301 static inline u32 rv_sh2add(u32 rd, u32 rs1, u32 rs2) { 302 return rv_r(0x10, rs2, rs1, 0x4, rd, RV_OP); 303 } 304 static inline u32 rv_sh3add(u32 rd, u32 rs1, u32 rs2) { 305 return rv_r(0x10, rs2, rs1, 0x6, rd, RV_OP); 306 } 307 308 /* Loads (funct3: 0=LB,1=LH,2=LW,3=LD,4=LBU,5=LHU,6=LWU) */ 309 static inline u32 rv_lb(u32 rd, u32 rs1, i32 imm) { 310 return rv_i(imm, rs1, 0x0, rd, RV_LOAD); 311 } 312 static inline u32 rv_lh(u32 rd, u32 rs1, i32 imm) { 313 return rv_i(imm, rs1, 0x1, rd, RV_LOAD); 314 } 315 static inline u32 rv_lw(u32 rd, u32 rs1, i32 imm) { 316 return rv_i(imm, rs1, 0x2, rd, RV_LOAD); 317 } 318 static inline u32 rv_ld(u32 rd, u32 rs1, i32 imm) { 319 return rv_i(imm, rs1, 0x3, rd, RV_LOAD); 320 } 321 static inline u32 rv_lbu(u32 rd, u32 rs1, i32 imm) { 322 return rv_i(imm, rs1, 0x4, rd, RV_LOAD); 323 } 324 static inline u32 rv_lhu(u32 rd, u32 rs1, i32 imm) { 325 return rv_i(imm, rs1, 0x5, rd, RV_LOAD); 326 } 327 static inline u32 rv_lwu(u32 rd, u32 rs1, i32 imm) { 328 return rv_i(imm, rs1, 0x6, rd, RV_LOAD); 329 } 330 331 /* Stores (funct3: 0=SB,1=SH,2=SW,3=SD) */ 332 static inline u32 rv_sb(u32 rs2, u32 rs1, i32 imm) { 333 return rv_s(imm, rs2, rs1, 0x0, RV_STORE); 334 } 335 static inline u32 rv_sh(u32 rs2, u32 rs1, i32 imm) { 336 return rv_s(imm, rs2, rs1, 0x1, RV_STORE); 337 } 338 static inline u32 rv_sw(u32 rs2, u32 rs1, i32 imm) { 339 return rv_s(imm, rs2, rs1, 0x2, RV_STORE); 340 } 341 static inline u32 rv_sd(u32 rs2, u32 rs1, i32 imm) { 342 return rv_s(imm, rs2, rs1, 0x3, RV_STORE); 343 } 344 345 /* Branches */ 346 static inline u32 rv_beq(u32 rs1, u32 rs2, i32 imm) { 347 return rv_b(imm, rs2, rs1, 0x0, RV_BRANCH); 348 } 349 static inline u32 rv_bne(u32 rs1, u32 rs2, i32 imm) { 350 return rv_b(imm, rs2, rs1, 0x1, RV_BRANCH); 351 } 352 static inline u32 rv_blt(u32 rs1, u32 rs2, i32 imm) { 353 return rv_b(imm, rs2, rs1, 0x4, RV_BRANCH); 354 } 355 static inline u32 rv_bge(u32 rs1, u32 rs2, i32 imm) { 356 return rv_b(imm, rs2, rs1, 0x5, RV_BRANCH); 357 } 358 static inline u32 rv_bltu(u32 rs1, u32 rs2, i32 imm) { 359 return rv_b(imm, rs2, rs1, 0x6, RV_BRANCH); 360 } 361 static inline u32 rv_bgeu(u32 rs1, u32 rs2, i32 imm) { 362 return rv_b(imm, rs2, rs1, 0x7, RV_BRANCH); 363 } 364 365 /* Jumps */ 366 static inline u32 rv_jal(u32 rd, i32 imm21) { return rv_j(imm21, rd, RV_JAL); } 367 static inline u32 rv_jalr(u32 rd, u32 rs1, i32 imm) { 368 return rv_i(imm, rs1, 0x0, rd, RV_JALR); 369 } 370 371 /* Convenience: jr / ret / j / nop */ 372 static inline u32 rv_jr(u32 rs1) { return rv_jalr(RV_ZERO, rs1, 0); } 373 static inline u32 rv_ret_(void) { return rv_jalr(RV_ZERO, RV_RA, 0); } 374 static inline u32 rv_nop(void) { return RV_NOP; } 375 376 /* System */ 377 static inline u32 rv_ecall(void) { return rv_i(0, 0, 0, 0, RV_SYSTEM); } 378 static inline u32 rv_ebreak(void) { return rv_i(1, 0, 0, 0, RV_SYSTEM); } 379 /* WFI: wait-for-interrupt, SYSTEM funct12=0x105 (privileged). */ 380 static inline u32 rv_wfi(void) { return 0x10500073u; } 381 382 /* FENCE: pred/succ each 4 bits in imm[11:8]/imm[7:4]. fm bits 11:8 of imm */ 383 static inline u32 rv_fence_rw_rw(void) { 384 return rv_i((i32)0x033, 0, 0, 0, RV_FENCE); 385 } 386 /* FENCE.I: instruction-stream sync (Zifencei). funct3=1 in the MISC-MEM major 387 * opcode (0x0F). Used to lower the ISB intrinsic. */ 388 static inline u32 rv_fence_i(void) { return 0x0000100Fu; } 389 /* PAUSE (Zihintpause): a FENCE with pred=W, succ=none. Used for cpu_yield; 390 * decodes as a plain FENCE on hardware lacking the extension, which is a safe 391 * (stronger) no-op hint. */ 392 static inline u32 rv_pause(void) { return 0x0100000Fu; } 393 394 /* ---- FP (F + D extensions) ---- 395 * funct7 layout: bits[6:2] op-major (e.g. 0x00 FADD, 0x01 FSUB, ...); 396 * bits[1:0] = fmt (00=S, 01=D). rm (rounding mode) in funct3; 0x7 = DYN. */ 397 398 #define RV_FMT_S 0u 399 #define RV_FMT_D 1u 400 401 static inline u32 rv_fadd(u32 fmt, u32 rd, u32 rs1, u32 rs2) { 402 return rv_r((0x00u << 2) | fmt, rs2, rs1, 0x7, rd, RV_OP_FP); 403 } 404 static inline u32 rv_fsub(u32 fmt, u32 rd, u32 rs1, u32 rs2) { 405 return rv_r((0x01u << 2) | fmt, rs2, rs1, 0x7, rd, RV_OP_FP); 406 } 407 static inline u32 rv_fmul(u32 fmt, u32 rd, u32 rs1, u32 rs2) { 408 return rv_r((0x02u << 2) | fmt, rs2, rs1, 0x7, rd, RV_OP_FP); 409 } 410 static inline u32 rv_fdiv(u32 fmt, u32 rd, u32 rs1, u32 rs2) { 411 return rv_r((0x03u << 2) | fmt, rs2, rs1, 0x7, rd, RV_OP_FP); 412 } 413 /* FSGNJ.fmt rd, rs1, rs2 — used to implement FMV.fmt rd, rs (sgnj rs, rs). */ 414 static inline u32 rv_fsgnj(u32 fmt, u32 rd, u32 rs1, u32 rs2) { 415 return rv_r((0x04u << 2) | fmt, rs2, rs1, 0x0, rd, RV_OP_FP); 416 } 417 static inline u32 rv_fsgnjn(u32 fmt, u32 rd, u32 rs1, u32 rs2) { 418 return rv_r((0x04u << 2) | fmt, rs2, rs1, 0x1, rd, RV_OP_FP); 419 } 420 /* FCVT — integer/FP conversions. funct7 = 0x18..0x1d depending on direction; 421 * rs2 encodes the partner type: 422 * 0x60(W <- S) 0x61(W <- D) 423 * 0x68(S <- W) 0x69(D <- W) etc 424 * We assemble explicitly via rv_r to be obvious. */ 425 static inline u32 rv_fcvt(u32 funct7, u32 rs2_sel, u32 rd, u32 rs1, u32 rm) { 426 return rv_r(funct7, rs2_sel, rs1, rm, rd, RV_OP_FP); 427 } 428 /* FCVT.W.S rd, rs1 (signed i32 from f32, rtz=001) : funct7=0x60 rs2=0 */ 429 static inline u32 rv_fcvt_w_s(u32 rd, u32 rs1) { 430 return rv_fcvt(0x60, 0x0, rd, rs1, 0x1); 431 } 432 static inline u32 rv_fcvt_wu_s(u32 rd, u32 rs1) { 433 return rv_fcvt(0x60, 0x1, rd, rs1, 0x1); 434 } 435 static inline u32 rv_fcvt_l_s(u32 rd, u32 rs1) { 436 return rv_fcvt(0x60, 0x2, rd, rs1, 0x1); 437 } 438 static inline u32 rv_fcvt_lu_s(u32 rd, u32 rs1) { 439 return rv_fcvt(0x60, 0x3, rd, rs1, 0x1); 440 } 441 static inline u32 rv_fcvt_w_d(u32 rd, u32 rs1) { 442 return rv_fcvt(0x61, 0x0, rd, rs1, 0x1); 443 } 444 static inline u32 rv_fcvt_wu_d(u32 rd, u32 rs1) { 445 return rv_fcvt(0x61, 0x1, rd, rs1, 0x1); 446 } 447 static inline u32 rv_fcvt_l_d(u32 rd, u32 rs1) { 448 return rv_fcvt(0x61, 0x2, rd, rs1, 0x1); 449 } 450 static inline u32 rv_fcvt_lu_d(u32 rd, u32 rs1) { 451 return rv_fcvt(0x61, 0x3, rd, rs1, 0x1); 452 } 453 static inline u32 rv_fcvt_s_w(u32 rd, u32 rs1) { 454 return rv_fcvt(0x68, 0x0, rd, rs1, 0x7); 455 } 456 static inline u32 rv_fcvt_s_wu(u32 rd, u32 rs1) { 457 return rv_fcvt(0x68, 0x1, rd, rs1, 0x7); 458 } 459 static inline u32 rv_fcvt_s_l(u32 rd, u32 rs1) { 460 return rv_fcvt(0x68, 0x2, rd, rs1, 0x7); 461 } 462 static inline u32 rv_fcvt_s_lu(u32 rd, u32 rs1) { 463 return rv_fcvt(0x68, 0x3, rd, rs1, 0x7); 464 } 465 static inline u32 rv_fcvt_d_w(u32 rd, u32 rs1) { 466 return rv_fcvt(0x69, 0x0, rd, rs1, 0x7); 467 } 468 static inline u32 rv_fcvt_d_wu(u32 rd, u32 rs1) { 469 return rv_fcvt(0x69, 0x1, rd, rs1, 0x7); 470 } 471 static inline u32 rv_fcvt_d_l(u32 rd, u32 rs1) { 472 return rv_fcvt(0x69, 0x2, rd, rs1, 0x7); 473 } 474 static inline u32 rv_fcvt_d_lu(u32 rd, u32 rs1) { 475 return rv_fcvt(0x69, 0x3, rd, rs1, 0x7); 476 } 477 /* FCVT.S.D / FCVT.D.S */ 478 static inline u32 rv_fcvt_s_d(u32 rd, u32 rs1) { 479 return rv_fcvt(0x20, 0x1, rd, rs1, 0x7); 480 } 481 static inline u32 rv_fcvt_d_s(u32 rd, u32 rs1) { 482 return rv_fcvt(0x21, 0x0, rd, rs1, 0x7); 483 } 484 485 /* FMV.X.W / FMV.W.X / FMV.X.D / FMV.D.X — bitcast between GPR and FPR. */ 486 static inline u32 rv_fmv_x_w(u32 rd, u32 rs1) { 487 return rv_fcvt(0x70, 0x0, rd, rs1, 0x0); 488 } 489 static inline u32 rv_fmv_w_x(u32 rd, u32 rs1) { 490 return rv_fcvt(0x78, 0x0, rd, rs1, 0x0); 491 } 492 static inline u32 rv_fmv_x_d(u32 rd, u32 rs1) { 493 return rv_fcvt(0x71, 0x0, rd, rs1, 0x0); 494 } 495 static inline u32 rv_fmv_d_x(u32 rd, u32 rs1) { 496 return rv_fcvt(0x79, 0x0, rd, rs1, 0x0); 497 } 498 499 /* FP compares — rd is integer GPR. funct7 = 0x50/0x51 (S/D). rm: 0=LE, 1=LT, 500 * 2=EQ. */ 501 static inline u32 rv_feq_s(u32 rd, u32 rs1, u32 rs2) { 502 return rv_r(0x50, rs2, rs1, 0x2, rd, RV_OP_FP); 503 } 504 static inline u32 rv_flt_s(u32 rd, u32 rs1, u32 rs2) { 505 return rv_r(0x50, rs2, rs1, 0x1, rd, RV_OP_FP); 506 } 507 static inline u32 rv_fle_s(u32 rd, u32 rs1, u32 rs2) { 508 return rv_r(0x50, rs2, rs1, 0x0, rd, RV_OP_FP); 509 } 510 static inline u32 rv_feq_d(u32 rd, u32 rs1, u32 rs2) { 511 return rv_r(0x51, rs2, rs1, 0x2, rd, RV_OP_FP); 512 } 513 static inline u32 rv_flt_d(u32 rd, u32 rs1, u32 rs2) { 514 return rv_r(0x51, rs2, rs1, 0x1, rd, RV_OP_FP); 515 } 516 static inline u32 rv_fle_d(u32 rd, u32 rs1, u32 rs2) { 517 return rv_r(0x51, rs2, rs1, 0x0, rd, RV_OP_FP); 518 } 519 520 static inline u32 rv_flw(u32 rd, u32 rs1, i32 imm) { 521 return rv_i(imm, rs1, 0x2, rd, RV_LOAD_FP); 522 } 523 static inline u32 rv_fld(u32 rd, u32 rs1, i32 imm) { 524 return rv_i(imm, rs1, 0x3, rd, RV_LOAD_FP); 525 } 526 static inline u32 rv_fsw(u32 rs2, u32 rs1, i32 imm) { 527 return rv_s(imm, rs2, rs1, 0x2, RV_STORE_FP); 528 } 529 static inline u32 rv_fsd(u32 rs2, u32 rs1, i32 imm) { 530 return rv_s(imm, rs2, rs1, 0x3, RV_STORE_FP); 531 } 532 533 /* ---- A extension (LR/SC + AMO) ---- 534 * AMO funct7 layout: aq(26) rl(25) funct5(31:27) op-specific. 535 * funct3 selects width: 0x2 = W (32-bit), 0x3 = D (64-bit). */ 536 static inline u32 rv_amo(u32 funct5, u32 aq, u32 rl, u32 rd, u32 rs1, u32 rs2, 537 u32 funct3) { 538 u32 funct7 = (funct5 << 2) | ((aq & 1u) << 1) | (rl & 1u); 539 return rv_r(funct7, rs2, rs1, funct3, rd, RV_AMO); 540 } 541 static inline u32 rv_lr_w(u32 rd, u32 rs1, u32 aq, u32 rl) { 542 return rv_amo(0x02, aq, rl, rd, rs1, 0, 0x2); 543 } 544 static inline u32 rv_lr_d(u32 rd, u32 rs1, u32 aq, u32 rl) { 545 return rv_amo(0x02, aq, rl, rd, rs1, 0, 0x3); 546 } 547 static inline u32 rv_sc_w(u32 rd, u32 rs1, u32 rs2, u32 aq, u32 rl) { 548 return rv_amo(0x03, aq, rl, rd, rs1, rs2, 0x2); 549 } 550 static inline u32 rv_sc_d(u32 rd, u32 rs1, u32 rs2, u32 aq, u32 rl) { 551 return rv_amo(0x03, aq, rl, rd, rs1, rs2, 0x3); 552 } 553 554 /* Other A-extension AMO funct5 codes (W and D widths via funct3). */ 555 #define RV_AMO_SWAP 0x01u 556 #define RV_AMO_ADD 0x00u 557 #define RV_AMO_XOR 0x04u 558 #define RV_AMO_AND 0x0Cu 559 #define RV_AMO_OR 0x08u 560 #define RV_AMO_MIN 0x10u 561 #define RV_AMO_MAX 0x14u 562 #define RV_AMO_MINU 0x18u 563 #define RV_AMO_MAXU 0x1Cu 564 565 /* User-mode read-only performance CSRs (Zicntr). RDCYCLE rd is the canonical 566 * `csrrs rd, cycle, x0`. The high halves (0xC80+) are rv32-only. */ 567 #define RV_CSR_CYCLE 0xC00u 568 #define RV_CSR_TIME 0xC01u 569 #define RV_CSR_INSTRET 0xC02u 570 571 /* Zicsr — CSR instructions. csr in imm[11:0]; funct3 selects op. 572 * csrrw=1, csrrs=2, csrrc=3, csrrwi=5, csrrsi=6, csrrci=7 */ 573 static inline u32 rv_csrrw(u32 rd, u32 csr, u32 rs1) { 574 return rv_i((i32)(csr & 0xfffu), rs1, 0x1, rd, RV_SYSTEM); 575 } 576 static inline u32 rv_csrrs(u32 rd, u32 csr, u32 rs1) { 577 return rv_i((i32)(csr & 0xfffu), rs1, 0x2, rd, RV_SYSTEM); 578 } 579 static inline u32 rv_csrrc(u32 rd, u32 csr, u32 rs1) { 580 return rv_i((i32)(csr & 0xfffu), rs1, 0x3, rd, RV_SYSTEM); 581 } 582 static inline u32 rv_csrrwi(u32 rd, u32 csr, u32 uimm) { 583 return rv_i((i32)(csr & 0xfffu), uimm & 0x1fu, 0x5, rd, RV_SYSTEM); 584 } 585 static inline u32 rv_csrrsi(u32 rd, u32 csr, u32 uimm) { 586 return rv_i((i32)(csr & 0xfffu), uimm & 0x1fu, 0x6, rd, RV_SYSTEM); 587 } 588 static inline u32 rv_csrrci(u32 rd, u32 csr, u32 uimm) { 589 return rv_i((i32)(csr & 0xfffu), uimm & 0x1fu, 0x7, rd, RV_SYSTEM); 590 } 591 592 /* =================================================================== 593 * Format kinds — one per encoding family the descriptor table dispatches 594 * on. R-type splits by funct3/funct7 selectors; I/S/B/U/J each carry a 595 * distinct immediate layout. The C-extension formats (CR/CI/CSS/CIW/CL/ 596 * CS/CB/CJ) are 16-bit; the disassembler picks 16 vs 32 by checking the 597 * bottom two bits of the first halfword (00/01/10 → compressed, 11 → 32). 598 * =================================================================== */ 599 typedef enum Rv64Format { 600 RV64_FMT_R, /* funct7 rs2 rs1 funct3 rd op — most ALU ops */ 601 RV64_FMT_R4, /* fused FMA: rs3 funct2 rs2 rs1 funct3 rd op */ 602 RV64_FMT_I, /* imm[11:0] rs1 funct3 rd op — ALU-imm, loads, jalr */ 603 RV64_FMT_I_SHIFT, /* shift-imm (shamt6/funct6) — RV64 SLLI/SRLI/SRAI */ 604 RV64_FMT_I_SHIFTW, /* RV32 word-shift (shamt5/funct7) — SLLIW/SRLIW/SRAIW */ 605 RV64_FMT_S, /* store */ 606 RV64_FMT_B, /* branch */ 607 RV64_FMT_U, /* LUI/AUIPC */ 608 RV64_FMT_J, /* JAL */ 609 RV64_FMT_LOAD, /* I-type load: rd, imm(rs1) — printer uses memory syntax */ 610 RV64_FMT_STORE, /* S-type store: rs2, imm(rs1) */ 611 RV64_FMT_JALR, /* JALR: rd, imm(rs1) — memory-style operand syntax */ 612 RV64_FMT_FENCE, /* FENCE pred,succ */ 613 RV64_FMT_SYSTEM, /* ECALL/EBREAK — no operands */ 614 RV64_FMT_FP_RM, /* FP arithmetic with rm: funct7 rs2 rs1 rm rd op */ 615 RV64_FMT_FP_R, /* FP R-type without rm-as-mnemonic-suffix (cmp/sgnj) */ 616 RV64_FMT_FP_CVT, /* FP conversion: rs2 is type selector, rs1 is src */ 617 RV64_FMT_FP_LOAD, /* fld/flw — rd[FP], imm(rs1) */ 618 RV64_FMT_FP_STORE, /* fsd/fsw — rs2[FP], imm(rs1) */ 619 RV64_FMT_AMO, /* atomic: rd, rs2, (rs1) */ 620 RV64_FMT_LR, /* LR.W/D: rd, (rs1) — no rs2 */ 621 RV64_FMT_CSR, /* csrr*: rd, csr, rs1 */ 622 RV64_FMT_CSRI, /* csrr*i: rd, csr, uimm5 */ 623 /* ---- Compressed (16-bit) formats ---- */ 624 RV64_FMT_CR, /* funct4 rd/rs1 rs2 op (e.g. C.MV, C.ADD, C.JR, C.JALR) */ 625 RV64_FMT_CI, /* funct3 imm rd/rs1 imm op (e.g. C.ADDI, C.LI, C.LUI) */ 626 RV64_FMT_CSS, /* funct3 imm rs2 op (stack store: C.SDSP, C.SWSP) */ 627 RV64_FMT_CIW, /* funct3 imm rd' op (C.ADDI4SPN) */ 628 RV64_FMT_CL, /* funct3 imm rs1' imm rd' op (C.LD, C.LW) */ 629 RV64_FMT_CS, /* funct3 imm rs1' imm rs2' op (C.SD, C.SW) */ 630 RV64_FMT_CA, /* funct6 rd'/rs1' funct2 rs2' op (C.AND, C.OR, ...) */ 631 RV64_FMT_CB, /* branch: funct3 imm rs1' imm op (C.BEQZ, C.BNEZ) */ 632 RV64_FMT_CJ, /* jump: funct3 imm op (C.J, C.JAL_unused on RV64) */ 633 RV64_FMT_C_NONE, /* known opcode with no operands (C.NOP, C.EBREAK) */ 634 /* Assembler-only multi-word pseudo-instruction (call/tail/la/lla). The 635 * descriptor's `match` is unused; the assembler dispatches on mnemonic 636 * and emits the AUIPC+JALR / AUIPC+ADDI expansion directly. */ 637 RV64_FMT_PSEUDO, 638 /* Assembler-only 2-operand CSR pseudo-instructions. The descriptor's `match` 639 * pins funct3+opcode (like the full-form csrr* rows); the assembler 640 * dispatches on the mnemonic to decide the operand shape (which of rd/rs1 is 641 * the implicit x0) and which operands are present. Reg form: csrr/csrw/csrs/ 642 * csrc; imm form: csrwi/csrsi/csrci. */ 643 RV64_FMT_CSR_PSEUDO, 644 } Rv64Format; 645 646 typedef enum Rv64DecodedOpcode { 647 RV64_DEC_UNKNOWN = 0, 648 RV64_DEC_ADDI, 649 RV64_DEC_ADD, 650 RV64_DEC_AUIPC, 651 RV64_DEC_LD, 652 RV64_DEC_SD, 653 RV64_DEC_JALR, 654 RV64_DEC_ECALL, 655 RV64_DEC_EBREAK, 656 } Rv64DecodedOpcode; 657 658 /* ---- AsmFlags column on Rv64InsnDesc ---- */ 659 #define RV64_ASMFL_ALIAS 0x01u /* row is an alias (preferred print form) */ 660 #define RV64_ASMFL_FP 0x02u /* operands take f-register prefix */ 661 #define RV64_ASMFL_NORM 0x04u /* FP_RM row prints without rm suffix */ 662 #define RV64_ASMFL_C16 0x08u /* 16-bit compressed instruction */ 663 /* Assembler-only multi-word pseudo (call/tail/la/lla). These expand to 664 * several 32-bit words and never participate in disassembly — the decoder 665 * sees the individual auipc/jalr/addi words instead. rv64_disasm_find 666 * skips rows carrying this flag. */ 667 #define RV64_ASMFL_PSEUDO 0x10u 668 669 /* ---- Availability column (`av`) on Rv64InsnDesc ---- 670 * Which XLEN variant(s) a row is valid on. A row with av==0 is implicitly 671 * available on BOTH (this is how the ~200 untouched `{0, 0}` pad 672 * initializers stay correct — av lands in the first pad byte and reads 0). 673 * Rows whose encoding only exists / changes meaning per XLEN are tagged 674 * explicitly; the find functions skip a row when its av is set and excludes 675 * the wanted arch. */ 676 #define RV_AV_RV32 0x1u 677 #define RV_AV_RV64 0x2u 678 #define RV_AV_BOTH (RV_AV_RV32 | RV_AV_RV64) 679 680 /* =================================================================== 681 * Per-format field structs + pack/unpack pure functions. 682 * =================================================================== */ 683 684 typedef struct Rv64R { 685 u32 funct7, rs2, rs1, funct3, rd, op; 686 } Rv64R; 687 typedef struct Rv64I { 688 u32 imm12, rs1, funct3, rd, op; 689 } Rv64I; 690 typedef struct Rv64S { 691 u32 imm12, rs2, rs1, funct3, op; 692 } Rv64S; 693 typedef struct Rv64B { 694 u32 imm13, rs2, rs1, funct3, op; 695 } Rv64B; 696 typedef struct Rv64U { 697 u32 imm32_hi20, rd, op; 698 } Rv64U; 699 typedef struct Rv64J { 700 u32 imm21, rd, op; 701 } Rv64J; 702 703 static inline Rv64R rv64_r_unpack(u32 w) { 704 Rv64R f; 705 f.funct7 = (w >> 25) & 0x7fu; 706 f.rs2 = (w >> 20) & 0x1fu; 707 f.rs1 = (w >> 15) & 0x1fu; 708 f.funct3 = (w >> 12) & 0x7u; 709 f.rd = (w >> 7) & 0x1fu; 710 f.op = w & 0x7fu; 711 return f; 712 } 713 static inline Rv64I rv64_i_unpack(u32 w) { 714 Rv64I f; 715 f.imm12 = (w >> 20) & 0xfffu; 716 f.rs1 = (w >> 15) & 0x1fu; 717 f.funct3 = (w >> 12) & 0x7u; 718 f.rd = (w >> 7) & 0x1fu; 719 f.op = w & 0x7fu; 720 return f; 721 } 722 static inline Rv64S rv64_s_unpack(u32 w) { 723 Rv64S f; 724 f.imm12 = (((w >> 25) & 0x7fu) << 5) | ((w >> 7) & 0x1fu); 725 f.rs2 = (w >> 20) & 0x1fu; 726 f.rs1 = (w >> 15) & 0x1fu; 727 f.funct3 = (w >> 12) & 0x7u; 728 f.op = w & 0x7fu; 729 return f; 730 } 731 static inline Rv64B rv64_b_unpack(u32 w) { 732 Rv64B f; 733 f.imm13 = (((w >> 31) & 1u) << 12) | (((w >> 7) & 1u) << 11) | 734 (((w >> 25) & 0x3fu) << 5) | (((w >> 8) & 0xfu) << 1); 735 f.rs2 = (w >> 20) & 0x1fu; 736 f.rs1 = (w >> 15) & 0x1fu; 737 f.funct3 = (w >> 12) & 0x7u; 738 f.op = w & 0x7fu; 739 return f; 740 } 741 static inline Rv64U rv64_u_unpack(u32 w) { 742 Rv64U f; 743 f.imm32_hi20 = w & 0xfffff000u; 744 f.rd = (w >> 7) & 0x1fu; 745 f.op = w & 0x7fu; 746 return f; 747 } 748 static inline Rv64J rv64_j_unpack(u32 w) { 749 Rv64J f; 750 f.imm21 = (((w >> 31) & 1u) << 20) | (((w >> 12) & 0xffu) << 12) | 751 (((w >> 20) & 1u) << 11) | (((w >> 21) & 0x3ffu) << 1); 752 f.rd = (w >> 7) & 0x1fu; 753 f.op = w & 0x7fu; 754 return f; 755 } 756 757 /* Sign-extend an n-bit value held in the low bits of v to i64. */ 758 static inline i64 rv64_sext(u64 v, u32 nbits) { 759 u64 mask = (nbits >= 64u) ? ~0ull : ((1ull << nbits) - 1ull); 760 v &= mask; 761 u64 sign = (nbits == 0u) ? 0ull : (1ull << (nbits - 1u)); 762 if (v & sign) v |= ~mask; 763 return (i64)v; 764 } 765 766 /* =================================================================== 767 * Compressed (RV64C) helpers — 16-bit instructions. 768 * 769 * Layout (per RVC quadrant): bits[1:0] (op) select the quadrant: 770 * 00 → Q0 (stack-relative & load/store narrow), 771 * 01 → Q1 (constant/branch), 772 * 10 → Q2 (stack pointer access & jumps & MV/ADD). 773 * 11 is reserved for 32-bit (uncompressed) instructions, so the 774 * disassembler picks 16-bit when (halfword & 3) != 3. 775 * 776 * The "narrow" register fields rs1' / rs2' / rd' are 3-bit and encode 777 * x8..x15; macro RVC_REG3 unfolds: r' → 8 + r'. */ 778 #define RVC_REG3(r3) ((u32)(8u + ((r3) & 7u))) 779 780 typedef struct Rv64C { 781 u32 word; 782 } Rv64C; /* 16-bit halfword in low 16 bits */ 783 784 /* =================================================================== 785 * Descriptor table. 786 * =================================================================== */ 787 788 typedef struct Rv64InsnDesc { 789 Slice mnemonic; 790 u32 match; 791 u32 mask; 792 u8 fmt; /* Rv64Format */ 793 u8 flags; /* RV64_ASMFL_* */ 794 u8 av; /* RV_AV_* availability mask; 0 == available on BOTH */ 795 u8 pad[1]; 796 } Rv64InsnDesc; 797 798 extern const Rv64InsnDesc rv64_insn_table[]; 799 extern const u32 rv64_insn_table_n; 800 801 /* Standard CSR name <-> number table (shared by RV32 and RV64). */ 802 typedef struct Rv64CsrName { 803 const char* name; 804 u16 num; 805 } Rv64CsrName; 806 extern const Rv64CsrName rv64_csr_names[]; 807 extern const u32 rv64_csr_names_n; 808 /* name -> number: returns 1 + writes *num_out on a hit, else 0. */ 809 int rv64_csr_num_from_name(Slice name, u16* num_out); 810 /* number -> canonical name, or NULL if not in the table. */ 811 const char* rv64_csr_name_from_num(u16 num); 812 813 /* Linear-scan lookup. Returns the matching descriptor or NULL. First 814 * match wins; ordering puts more-specific entries (aliases, fixed-Rd 815 * forms) before broader ones. `av_wanted` is the RV_AV_* mask of the 816 * decoding arch (RV_AV_RV32 / RV_AV_RV64); rows whose av is set and 817 * excludes it are skipped. Pass RV_AV_RV64 to reproduce the historical 818 * rv64-only behavior exactly. */ 819 const Rv64InsnDesc* rv64_disasm_find(u32 word, u8 av_wanted); 820 821 /* Compressed-instruction (16-bit) variant. Pass the halfword in the low 822 * 16 bits of `word`. `av_wanted` branches the ambiguous quadrant slots 823 * whose meaning differs between rv32 and rv64. Returns NULL if no 824 * descriptor matches. 825 * 826 * Synthesized C-format descriptors are written into the caller-owned 827 * `scratch` and that pointer is returned; fixed rows return a pointer into 828 * the module's static table instead. `scratch` must outlive the returned 829 * pointer's use. (Keeping the scratch caller-owned avoids a mutable 830 * function-local static and keeps the decode path reentrant.) */ 831 const Rv64InsnDesc* rv64_disasm_find_c(u32 word, u8 av_wanted, 832 Rv64InsnDesc* scratch); 833 834 /* Mnemonic → descriptor for the assembler. Returns NULL if not found. 835 * Ignores ALIAS-only rows when those would produce ambiguous parses 836 * (the canonical form is always reachable). `av_wanted` filters rows by 837 * target arch so e.g. `ld`/`addiw` are not assemblable under rv32. */ 838 const Rv64InsnDesc* rv64_asm_find(Slice mnemonic, u8 av_wanted); 839 840 /* =================================================================== 841 * Operand print / parse dispatch. 842 * 843 * rv64_print_operands renders the operand text (everything after the 844 * mnemonic) for `word` into `sb`, using `desc->fmt` to dispatch. 845 * Mnemonic itself is in `desc->mnemonic`; the caller writes it before 846 * calling this helper. `vaddr` is the instruction's virtual address for 847 * PC-relative formats; pass 0 if not known. */ 848 void rv64_print_operands(StrBuf* sb, const Rv64InsnDesc* desc, u32 word, 849 u64 vaddr); 850 851 #endif /* KIT_RV64_ISA_H */