diff --git a/src/decode.c b/src/decode.c index a50056c4..822137aa 100644 --- a/src/decode.c +++ b/src/decode.c @@ -1255,7 +1255,7 @@ static inline bool op_caddi(rv_insn_t *ir, const uint32_t insn) /* dispatch from rd/rs1 field */ switch (ir->rd) { case 0: /* C.NOP */ - ir->opcode = rv_insn_nop; + ir->opcode = rv_insn_cnop; break; default: /* C.ADDI */ /* Add 6-bit signed immediate to rds, serving as NOP for X0 register. */ @@ -1320,7 +1320,7 @@ static inline bool op_clui(rv_insn_t *ir, const uint32_t insn) /* dispatch from rd/rs1 region */ switch (ir->rd) { case 0: /* Code point: rd = x0 is HINTS */ - ir->opcode = rv_insn_nop; + ir->opcode = rv_insn_cnop; break; case 2: { /* C.ADDI16SP */ ir->imm = c_decode_caddi16sp_nzimm(insn); @@ -1381,7 +1381,7 @@ static inline bool op_cmisc_alu(rv_insn_t *ir, const uint32_t insn) /* Code point: rd = x0 is HINTS * Code point: shamt = 0 is HINTS */ - ir->opcode = (!ir->rs1 || !ir->shamt) ? rv_insn_nop : rv_insn_csrli; + ir->opcode = (!ir->rs1 || !ir->shamt) ? rv_insn_cnop : rv_insn_csrli; break; case 1: /* C.SRAI */ ir->shamt = c_decode_cbtype_shamt(insn); @@ -1448,7 +1448,7 @@ static inline bool op_cslli(rv_insn_t *ir, const uint32_t insn) tmp |= (insn & FCI_IMM_6_2) >> 2; ir->imm = tmp; ir->rd = c_decode_rd(insn); - ir->opcode = ir->rd ? rv_insn_cslli : rv_insn_nop; + ir->opcode = ir->rd ? rv_insn_cslli : rv_insn_cnop; return true; } @@ -1470,7 +1470,7 @@ static inline bool op_clwsp(rv_insn_t *ir, const uint32_t insn) ir->rd = c_decode_rd(insn); /* reserved for rd = x0 */ - ir->opcode = ir->rd ? rv_insn_clwsp : rv_insn_nop; + ir->opcode = ir->rd ? rv_insn_clwsp : rv_insn_cnop; return true; } @@ -1594,7 +1594,7 @@ static inline bool op_ccr(rv_insn_t *ir, const uint32_t insn) break; default: /* C.MV */ /* Code point: rd = x0 is HINTS */ - ir->opcode = ir->rd ? rv_insn_cmv : rv_insn_nop; + ir->opcode = ir->rd ? rv_insn_cmv : rv_insn_cnop; break; } break; @@ -1603,12 +1603,12 @@ static inline bool op_ccr(rv_insn_t *ir, const uint32_t insn) ir->opcode = rv_insn_ebreak; else if (ir->rs1 && ir->rs2) { /* C.ADD */ /* Code point: rd = x0 is HINTS */ - ir->opcode = ir->rd ? rv_insn_cadd : rv_insn_nop; + ir->opcode = ir->rd ? rv_insn_cadd : rv_insn_cnop; } else if (ir->rs1 && !ir->rs2) /* C.JALR */ ir->opcode = rv_insn_cjalr; else { /* rs2 != x0 AND rs1 = x0 */ /* Hint */ - ir->opcode = rv_insn_nop; + ir->opcode = rv_insn_cnop; } break; default: @@ -1726,10 +1726,9 @@ bool rv_decode(rv_insn_t *ir, uint32_t insn) /* If the last 2-bit is one of 0b00, 0b01, and 0b10, it is * a 16-bit instruction. */ - if ((insn & FC_OPCODE) != 3) { + if (is_compressed(insn)) { insn &= 0x0000FFFF; const uint16_t c_index = (insn & FC_FUNC3) >> 11 | (insn & FC_OPCODE); - ir->insn_len = INSN_16; /* decode instruction (compressed instructions) */ const decode_t op = rvc_jump_table[c_index]; @@ -1740,7 +1739,6 @@ bool rv_decode(rv_insn_t *ir, uint32_t insn) /* standard uncompressed instruction */ const uint32_t index = (insn & INSN_6_2) >> 2; - ir->insn_len = INSN_32; /* decode instruction */ const decode_t op = rv_jump_table[index]; diff --git a/src/decode.h b/src/decode.h index 46a15753..874d8fe4 100644 --- a/src/decode.h +++ b/src/decode.h @@ -28,160 +28,161 @@ enum op_field { #define ENC_GEN(X, A) ENCN(X, A) #define ENC(...) ENC_GEN(ENC, COUNT_VARARGS(__VA_ARGS__))(__VA_ARGS__) -/* RISC-V instruction list in format _(instruction-name, can-branch, reg-mask) +/* RISC-V instruction list in format _(instruction-name, can-branch, insn_len, + * reg-mask) */ /* clang-format off */ -#define RV_INSN_LIST \ - _(nop, 0, ENC(rs1, rd)) \ - /* RV32I Base Instruction Set */ \ - _(lui, 0, ENC(rd)) \ - _(auipc, 0, ENC(rd)) \ - _(jal, 1, ENC(rd)) \ - _(jalr, 1, ENC(rs1, rd)) \ - _(beq, 1, ENC(rs1, rs2)) \ - _(bne, 1, ENC(rs1, rs2)) \ - _(blt, 1, ENC(rs1, rs2)) \ - _(bge, 1, ENC(rs1, rs2)) \ - _(bltu, 1, ENC(rs1, rs2)) \ - _(bgeu, 1, ENC(rs1, rs2)) \ - _(lb, 0, ENC(rs1, rd)) \ - _(lh, 0, ENC(rs1, rd)) \ - _(lw, 0, ENC(rs1, rd)) \ - _(lbu, 0, ENC(rs1, rd)) \ - _(lhu, 0, ENC(rs1, rd)) \ - _(sb, 0, ENC(rs1, rs2)) \ - _(sh, 0, ENC(rs1, rs2)) \ - _(sw, 0, ENC(rs1, rs2)) \ - _(addi, 0, ENC(rs1, rd)) \ - _(slti, 0, ENC(rs1, rd)) \ - _(sltiu, 0, ENC(rs1, rd)) \ - _(xori, 0, ENC(rs1, rd)) \ - _(ori, 0, ENC(rs1, rd)) \ - _(andi, 0, ENC(rs1, rd)) \ - _(slli, 0, ENC(rs1, rd)) \ - _(srli, 0, ENC(rs1, rd)) \ - _(srai, 0, ENC(rs1, rd)) \ - _(add, 0, ENC(rs1, rs2, rd)) \ - _(sub, 0, ENC(rs1, rs2, rd)) \ - _(sll, 0, ENC(rs1, rs2, rd)) \ - _(slt, 0, ENC(rs1, rs2, rd)) \ - _(sltu, 0, ENC(rs1, rs2, rd)) \ - _(xor, 0, ENC(rs1, rs2, rd)) \ - _(srl, 0, ENC(rs1, rs2, rd)) \ - _(sra, 0, ENC(rs1, rs2, rd)) \ - _(or, 0, ENC(rs1, rs2, rd)) \ - _(and, 0, ENC(rs1, rs2, rd)) \ - _(ecall, 1, ENC(rs1, rd)) \ - _(ebreak, 1, ENC(rs1, rd)) \ - /* RISC-V Privileged Instruction */ \ - _(wfi, 0, ENC(rs1, rd)) \ - _(uret, 0, ENC(rs1, rd)) \ - _(sret, 0, ENC(rs1, rd)) \ - _(hret, 0, ENC(rs1, rd)) \ - _(mret, 1, ENC(rs1, rd)) \ - /* RV32 Zifencei Standard Extension */ \ - IIF(RV32_HAS(Zifencei))( \ - _(fencei, 1, ENC(rs1, rd)) \ - ) \ - /* RV32 Zicsr Standard Extension */ \ - IIF(RV32_HAS(Zicsr))( \ - _(csrrw, 0, ENC(rs1, rd)) \ - _(csrrs, 0, ENC(rs1, rd)) \ - _(csrrc, 0, ENC(rs1, rd)) \ - _(csrrwi, 0, ENC(rs1, rd)) \ - _(csrrsi, 0, ENC(rs1, rd)) \ - _(csrrci, 0, ENC(rs1, rd)) \ - ) \ - /* RV32M Standard Extension */ \ - IIF(RV32_HAS(EXT_M))( \ - _(mul, 0, ENC(rs1, rs2, rd)) \ - _(mulh, 0, ENC(rs1, rs2, rd)) \ - _(mulhsu, 0, ENC(rs1, rs2, rd)) \ - _(mulhu, 0, ENC(rs1, rs2, rd)) \ - _(div, 0, ENC(rs1, rs2, rd)) \ - _(divu, 0, ENC(rs1, rs2, rd)) \ - _(rem, 0, ENC(rs1, rs2, rd)) \ - _(remu, 0, ENC(rs1, rs2, rd)) \ - ) \ - /* RV32A Standard Extension */ \ - IIF(RV32_HAS(EXT_A))( \ - _(lrw, 0, ENC(rs1, rs2, rd)) \ - _(scw, 0, ENC(rs1, rs2, rd)) \ - _(amoswapw, 0, ENC(rs1, rs2, rd)) \ - _(amoaddw, 0, ENC(rs1, rs2, rd)) \ - _(amoxorw, 0, ENC(rs1, rs2, rd)) \ - _(amoandw, 0, ENC(rs1, rs2, rd)) \ - _(amoorw, 0, ENC(rs1, rs2, rd)) \ - _(amominw, 0, ENC(rs1, rs2, rd)) \ - _(amomaxw, 0, ENC(rs1, rs2, rd)) \ - _(amominuw, 0, ENC(rs1, rs2, rd)) \ - _(amomaxuw, 0, ENC(rs1, rs2, rd)) \ - ) \ - /* RV32F Standard Extension */ \ - IIF(RV32_HAS(EXT_F))( \ - _(flw, 0, ENC(rs1, rd)) \ - _(fsw, 0, ENC(rs1, rs2)) \ - _(fmadds, 0, ENC(rs1, rs2, rs3, rd)) \ - _(fmsubs, 0, ENC(rs1, rs2, rs3, rd)) \ - _(fnmsubs, 0, ENC(rs1, rs2, rs3, rd)) \ - _(fnmadds, 0, ENC(rs1, rs2, rs3, rd)) \ - _(fadds, 0, ENC(rs1, rs2, rd)) \ - _(fsubs, 0, ENC(rs1, rs2, rd)) \ - _(fmuls, 0, ENC(rs1, rs2, rd)) \ - _(fdivs, 0, ENC(rs1, rs2, rd)) \ - _(fsqrts, 0, ENC(rs1, rs2, rd)) \ - _(fsgnjs, 0, ENC(rs1, rs2, rd)) \ - _(fsgnjns, 0, ENC(rs1, rs2, rd)) \ - _(fsgnjxs, 0, ENC(rs1, rs2, rd)) \ - _(fmins, 0, ENC(rs1, rs2, rd)) \ - _(fmaxs, 0, ENC(rs1, rs2, rd)) \ - _(fcvtws, 0, ENC(rs1, rs2, rd)) \ - _(fcvtwus, 0, ENC(rs1, rs2, rd)) \ - _(fmvxw, 0, ENC(rs1, rs2, rd)) \ - _(feqs, 0, ENC(rs1, rs2, rd)) \ - _(flts, 0, ENC(rs1, rs2, rd)) \ - _(fles, 0, ENC(rs1, rs2, rd)) \ - _(fclasss, 0, ENC(rs1, rs2, rd)) \ - _(fcvtsw, 0, ENC(rs1, rs2, rd)) \ - _(fcvtswu, 0, ENC(rs1, rs2, rd)) \ - _(fmvwx, 0, ENC(rs1, rs2, rd)) \ - ) \ - /* RV32C Standard Extension */ \ - IIF(RV32_HAS(EXT_C))( \ - _(caddi4spn, 0, ENC(rd)) \ - _(clw, 0, ENC(rs1, rd)) \ - _(csw, 0, ENC(rs1, rs2)) \ - /* cnop is mapped to nop */ \ - _(caddi, 0, ENC(rd)) \ - _(cjal, 1, ENC()) \ - _(cli, 0, ENC(rd)) \ - _(caddi16sp, 0, ENC()) \ - _(clui, 0, ENC(rd)) \ - _(csrli, 0, ENC(rs1)) \ - _(csrai, 0, ENC(rs1)) \ - _(candi, 0, ENC(rs1)) \ - _(csub, 0, ENC(rs1, rs2, rd)) \ - _(cxor, 0, ENC(rs1, rs2, rd)) \ - _(cor, 0, ENC(rs1, rs2, rd)) \ - _(cand, 0, ENC(rs1, rs2, rd)) \ - _(cj, 1, ENC()) \ - _(cbeqz, 1, ENC(rs1)) \ - _(cbnez, 1, ENC(rs1)) \ - _(cslli, 0, ENC(rd)) \ - _(clwsp, 0, ENC(rd)) \ - _(cjr, 1, ENC(rs1, rs2, rd)) \ - _(cmv, 0, ENC(rs1, rs2, rd)) \ - _(cebreak, 1, ENC(rs1, rs2, rd)) \ - _(cjalr, 1, ENC(rs1, rs2, rd)) \ - _(cadd, 0, ENC(rs1, rs2, rd)) \ - _(cswsp, 0, ENC(rs2)) \ +#define RV_INSN_LIST \ + _(nop, 0, 4, ENC(rs1, rd)) \ + /* RV32I Base Instruction Set */ \ + _(lui, 0, 4, ENC(rd)) \ + _(auipc, 0, 4, ENC(rd)) \ + _(jal, 1, 4, ENC(rd)) \ + _(jalr, 1, 4, ENC(rs1, rd)) \ + _(beq, 1, 4, ENC(rs1, rs2)) \ + _(bne, 1, 4, ENC(rs1, rs2)) \ + _(blt, 1, 4, ENC(rs1, rs2)) \ + _(bge, 1, 4, ENC(rs1, rs2)) \ + _(bltu, 1, 4, ENC(rs1, rs2)) \ + _(bgeu, 1, 4, ENC(rs1, rs2)) \ + _(lb, 0, 4, ENC(rs1, rd)) \ + _(lh, 0, 4, ENC(rs1, rd)) \ + _(lw, 0, 4, ENC(rs1, rd)) \ + _(lbu, 0, 4, ENC(rs1, rd)) \ + _(lhu, 0, 4, ENC(rs1, rd)) \ + _(sb, 0, 4, ENC(rs1, rs2)) \ + _(sh, 0, 4, ENC(rs1, rs2)) \ + _(sw, 0, 4, ENC(rs1, rs2)) \ + _(addi, 0, 4, ENC(rs1, rd)) \ + _(slti, 0, 4, ENC(rs1, rd)) \ + _(sltiu, 0, 4, ENC(rs1, rd)) \ + _(xori, 0, 4, ENC(rs1, rd)) \ + _(ori, 0, 4, ENC(rs1, rd)) \ + _(andi, 0, 4, ENC(rs1, rd)) \ + _(slli, 0, 4, ENC(rs1, rd)) \ + _(srli, 0, 4, ENC(rs1, rd)) \ + _(srai, 0, 4, ENC(rs1, rd)) \ + _(add, 0, 4, ENC(rs1, rs2, rd)) \ + _(sub, 0, 4, ENC(rs1, rs2, rd)) \ + _(sll, 0, 4, ENC(rs1, rs2, rd)) \ + _(slt, 0, 4, ENC(rs1, rs2, rd)) \ + _(sltu, 0, 4, ENC(rs1, rs2, rd)) \ + _(xor, 0, 4, ENC(rs1, rs2, rd)) \ + _(srl, 0, 4, ENC(rs1, rs2, rd)) \ + _(sra, 0, 4, ENC(rs1, rs2, rd)) \ + _(or, 0, 4, ENC(rs1, rs2, rd)) \ + _(and, 0, 4, ENC(rs1, rs2, rd)) \ + _(ecall, 1, 4, ENC(rs1, rd)) \ + _(ebreak, 1, 4, ENC(rs1, rd)) \ + /* RISC-V Privileged Instruction */ \ + _(wfi, 0, 4, ENC(rs1, rd)) \ + _(uret, 0, 4, ENC(rs1, rd)) \ + _(sret, 0, 4, ENC(rs1, rd)) \ + _(hret, 0, 4, ENC(rs1, rd)) \ + _(mret, 1, 4, ENC(rs1, rd)) \ + /* RV32 Zifencei Standard Extension */ \ + IIF(RV32_HAS(Zifencei))( \ + _(fencei, 1, 4, ENC(rs1, rd)) \ + ) \ + /* RV32 Zicsr Standard Extension */ \ + IIF(RV32_HAS(Zicsr))( \ + _(csrrw, 0, 4, ENC(rs1, rd)) \ + _(csrrs, 0, 4, ENC(rs1, rd)) \ + _(csrrc, 0, 4, ENC(rs1, rd)) \ + _(csrrwi, 0, 4, ENC(rs1, rd)) \ + _(csrrsi, 0, 4, ENC(rs1, rd)) \ + _(csrrci, 0, 4, ENC(rs1, rd)) \ + ) \ + /* RV32M Standard Extension */ \ + IIF(RV32_HAS(EXT_M))( \ + _(mul, 0, 4, ENC(rs1, rs2, rd)) \ + _(mulh, 0, 4, ENC(rs1, rs2, rd)) \ + _(mulhsu, 0, 4, ENC(rs1, rs2, rd)) \ + _(mulhu, 0, 4, ENC(rs1, rs2, rd)) \ + _(div, 0, 4, ENC(rs1, rs2, rd)) \ + _(divu, 0, 4, ENC(rs1, rs2, rd)) \ + _(rem, 0, 4, ENC(rs1, rs2, rd)) \ + _(remu, 0, 4, ENC(rs1, rs2, rd)) \ + ) \ + /* RV32A Standard Extension */ \ + IIF(RV32_HAS(EXT_A))( \ + _(lrw, 0, 4, ENC(rs1, rs2, rd)) \ + _(scw, 0, 4, ENC(rs1, rs2, rd)) \ + _(amoswapw, 0, 4, ENC(rs1, rs2, rd)) \ + _(amoaddw, 0, 4, ENC(rs1, rs2, rd)) \ + _(amoxorw, 0, 4, ENC(rs1, rs2, rd)) \ + _(amoandw, 0, 4, ENC(rs1, rs2, rd)) \ + _(amoorw, 0, 4, ENC(rs1, rs2, rd)) \ + _(amominw, 0, 4, ENC(rs1, rs2, rd)) \ + _(amomaxw, 0, 4, ENC(rs1, rs2, rd)) \ + _(amominuw, 0, 4, ENC(rs1, rs2, rd)) \ + _(amomaxuw, 0, 4, ENC(rs1, rs2, rd)) \ + ) \ + /* RV32F Standard Extension */ \ + IIF(RV32_HAS(EXT_F))( \ + _(flw, 0, 4, ENC(rs1, rd)) \ + _(fsw, 0, 4, ENC(rs1, rs2)) \ + _(fmadds, 0, 4, ENC(rs1, rs2, rs3, rd)) \ + _(fmsubs, 0, 4, ENC(rs1, rs2, rs3, rd)) \ + _(fnmsubs, 0, 4, ENC(rs1, rs2, rs3, rd)) \ + _(fnmadds, 0, 4, ENC(rs1, rs2, rs3, rd)) \ + _(fadds, 0, 4, ENC(rs1, rs2, rd)) \ + _(fsubs, 0, 4, ENC(rs1, rs2, rd)) \ + _(fmuls, 0, 4, ENC(rs1, rs2, rd)) \ + _(fdivs, 0, 4, ENC(rs1, rs2, rd)) \ + _(fsqrts, 0, 4, ENC(rs1, rs2, rd)) \ + _(fsgnjs, 0, 4, ENC(rs1, rs2, rd)) \ + _(fsgnjns, 0, 4, ENC(rs1, rs2, rd)) \ + _(fsgnjxs, 0, 4, ENC(rs1, rs2, rd)) \ + _(fmins, 0, 4, ENC(rs1, rs2, rd)) \ + _(fmaxs, 0, 4, ENC(rs1, rs2, rd)) \ + _(fcvtws, 0, 4, ENC(rs1, rs2, rd)) \ + _(fcvtwus, 0, 4, ENC(rs1, rs2, rd)) \ + _(fmvxw, 0, 4, ENC(rs1, rs2, rd)) \ + _(feqs, 0, 4, ENC(rs1, rs2, rd)) \ + _(flts, 0, 4, ENC(rs1, rs2, rd)) \ + _(fles, 0, 4, ENC(rs1, rs2, rd)) \ + _(fclasss, 0, 4, ENC(rs1, rs2, rd)) \ + _(fcvtsw, 0, 4, ENC(rs1, rs2, rd)) \ + _(fcvtswu, 0, 4, ENC(rs1, rs2, rd)) \ + _(fmvwx, 0, 4, ENC(rs1, rs2, rd)) \ + ) \ + /* RV32C Standard Extension */ \ + IIF(RV32_HAS(EXT_C))( \ + _(caddi4spn, 0, 2, ENC(rd)) \ + _(clw, 0, 2, ENC(rs1, rd)) \ + _(csw, 0, 2, ENC(rs1, rs2)) \ + _(cnop, 0, 2, ENC()) \ + _(caddi, 0, 2, ENC(rd)) \ + _(cjal, 1, 2, ENC()) \ + _(cli, 0, 2, ENC(rd)) \ + _(caddi16sp, 0, 2, ENC()) \ + _(clui, 0, 2, ENC(rd)) \ + _(csrli, 0, 2, ENC(rs1)) \ + _(csrai, 0, 2, ENC(rs1)) \ + _(candi, 0, 2, ENC(rs1)) \ + _(csub, 0, 2, ENC(rs1, rs2, rd)) \ + _(cxor, 0, 2, ENC(rs1, rs2, rd)) \ + _(cor, 0, 2, ENC(rs1, rs2, rd)) \ + _(cand, 0, 2, ENC(rs1, rs2, rd)) \ + _(cj, 1, 2, ENC()) \ + _(cbeqz, 1, 2, ENC(rs1)) \ + _(cbnez, 1, 2, ENC(rs1)) \ + _(cslli, 0, 2, ENC(rd)) \ + _(clwsp, 0, 2, ENC(rd)) \ + _(cjr, 1, 2, ENC(rs1, rs2, rd)) \ + _(cmv, 0, 2, ENC(rs1, rs2, rd)) \ + _(cebreak, 1, 2, ENC(rs1, rs2, rd)) \ + _(cjalr, 1, 2, ENC(rs1, rs2, rd)) \ + _(cadd, 0, 2, ENC(rs1, rs2, rd)) \ + _(cswsp, 0, 2, ENC(rs2)) \ ) /* clang-format on */ /* clang-format off */ /* IR list */ enum { -#define _(inst, can_branch, reg_mask) rv_insn_##inst, +#define _(inst, can_branch, insn_len, reg_mask) rv_insn_##inst, RV_INSN_LIST #undef _ N_RV_INSNS @@ -245,11 +246,6 @@ enum { }; /* clang-format on */ -enum { - INSN_16 = 2, - INSN_32 = 4, -}; - typedef struct { int32_t imm; uint8_t rd, rs1, rs2; @@ -274,9 +270,6 @@ typedef struct rv_insn { uint32_t pc; - /* instruction length */ - uint8_t insn_len; - /* Tail-call optimization (TCO) allows a C function to replace a function * call to another function or itself, followed by a simple return of the * function's result, with a direct jump to the target function. This @@ -291,7 +284,7 @@ typedef struct rv_insn { * self-recursive version, enabling the compiler to leverage TCO. */ struct rv_insn *next; - bool (*impl)(riscv_t *, const struct rv_insn *); + bool (*impl)(riscv_t *, const struct rv_insn *, uint64_t, uint32_t); /* Two pointers, 'branch_taken' and 'branch_untaken', are employed to * avoid the overhead associated with aggressive memory copying. Instead diff --git a/src/emulate.c b/src/emulate.c index c77a77a7..3024e782 100644 --- a/src/emulate.c +++ b/src/emulate.c @@ -61,7 +61,7 @@ enum { static void rv_exception_default_handler(riscv_t *rv) { - rv->csr_mepc += rv->compressed ? INSN_16 : INSN_32; + rv->csr_mepc += rv->compressed ? 2 : 4; rv->PC = rv->csr_mepc; /* mret */ } @@ -113,9 +113,11 @@ RV_EXCEPTION_LIST #define RV_EXC_MISALIGN_HANDLER(mask_or_pc, type, compress, IO) \ IIF(IO) \ (if (!rv->io.allow_misalign && unlikely(addr & (mask_or_pc))), \ - if (unlikely(insn_is_misaligned(rv->PC)))) \ + if (unlikely(insn_is_misaligned(PC)))) \ { \ rv->compressed = compress; \ + rv->csr_cycle = cycle; \ + rv->PC = PC; \ rv_except_##type##_misaligned(rv, IIF(IO)(addr, mask_or_pc)); \ return false; \ } @@ -348,9 +350,18 @@ FORCE_INLINE bool insn_is_misaligned(uint32_t pc) ); } +/* instruction length information for each RISC-V instruction */ +enum { +#define _(inst, can_branch, insn_len, reg_mask) \ + __rv_insn_##inst##_len = insn_len, + RV_INSN_LIST +#undef _ +}; + /* can-branch information for each RISC-V instruction */ enum { -#define _(inst, can_branch, reg_mask) __rv_insn_##inst##_canbranch = can_branch, +#define _(inst, can_branch, insn_len, reg_mask) \ + __rv_insn_##inst##_canbranch = can_branch, RV_INSN_LIST #undef _ }; @@ -368,17 +379,21 @@ static bool branch_taken = false; static uint32_t last_pc = 0; /* Interpreter-based execution path */ -#define RVOP(inst, code) \ - static bool do_##inst(riscv_t *rv, const rv_insn_t *ir) \ - { \ - rv->csr_cycle++; \ - code; \ - nextop: \ - rv->PC += ir->insn_len; \ - if (unlikely(RVOP_NO_NEXT(ir))) \ - return true; \ - const rv_insn_t *next = ir->next; \ - MUST_TAIL return next->impl(rv, next); \ +#define RVOP(inst, code) \ + static bool do_##inst(riscv_t *rv, const rv_insn_t *ir, uint64_t cycle, \ + uint32_t PC) \ + { \ + cycle++; \ + code; \ + nextop: \ + PC += __rv_insn_##inst##_len; \ + if (unlikely(RVOP_NO_NEXT(ir))) { \ + rv->csr_cycle = cycle; \ + rv->PC = PC; \ + return true; \ + } \ + const rv_insn_t *next = ir->next; \ + MUST_TAIL return next->impl(rv, next, cycle, PC); \ } #include "rv32_template.c" @@ -408,36 +423,42 @@ enum { }; /* multiple lui */ -static bool do_fuse1(riscv_t *rv, rv_insn_t *ir) +static bool do_fuse1(riscv_t *rv, rv_insn_t *ir, uint64_t cycle, uint32_t PC) { - rv->csr_cycle += ir->imm2; + cycle += ir->imm2; opcode_fuse_t *fuse = ir->fuse; for (int i = 0; i < ir->imm2; i++) rv->X[fuse[i].rd] = fuse[i].imm; - rv->PC += ir->imm2 * ir->insn_len; - if (unlikely(RVOP_NO_NEXT(ir))) + PC += ir->imm2 * 4; + if (unlikely(RVOP_NO_NEXT(ir))) { + rv->csr_cycle = cycle; + rv->PC = PC; return true; + } const rv_insn_t *next = ir->next; - MUST_TAIL return next->impl(rv, next); + MUST_TAIL return next->impl(rv, next, cycle, PC); } /* LUI + ADD */ -static bool do_fuse2(riscv_t *rv, rv_insn_t *ir) +static bool do_fuse2(riscv_t *rv, rv_insn_t *ir, uint64_t cycle, uint32_t PC) { - rv->csr_cycle += 2; + cycle += 2; rv->X[ir->rd] = ir->imm; rv->X[ir->rs2] = rv->X[ir->rd] + rv->X[ir->rs1]; - rv->PC += 2 * ir->insn_len; - if (unlikely(RVOP_NO_NEXT(ir))) + PC += 8; + if (unlikely(RVOP_NO_NEXT(ir))) { + rv->csr_cycle = cycle; + rv->PC = PC; return true; + } const rv_insn_t *next = ir->next; - MUST_TAIL return next->impl(rv, next); + MUST_TAIL return next->impl(rv, next, cycle, PC); } /* multiple SW */ -static bool do_fuse3(riscv_t *rv, rv_insn_t *ir) +static bool do_fuse3(riscv_t *rv, rv_insn_t *ir, uint64_t cycle, uint32_t PC) { - rv->csr_cycle += ir->imm2; + cycle += ir->imm2; opcode_fuse_t *fuse = ir->fuse; /* The memory addresses of the sw instructions are contiguous, thus only * the first SW instruction needs to be checked to determine if its memory @@ -448,17 +469,20 @@ static bool do_fuse3(riscv_t *rv, rv_insn_t *ir) RV_EXC_MISALIGN_HANDLER(3, store, false, 1); rv->io.mem_write_w(addr, rv->X[fuse[i].rs2]); } - rv->PC += ir->imm2 * ir->insn_len; - if (unlikely(RVOP_NO_NEXT(ir))) + PC += ir->imm2 * 4; + if (unlikely(RVOP_NO_NEXT(ir))) { + rv->csr_cycle = cycle; + rv->PC = PC; return true; + } const rv_insn_t *next = ir->next; - MUST_TAIL return next->impl(rv, next); + MUST_TAIL return next->impl(rv, next, cycle, PC); } /* multiple LW */ -static bool do_fuse4(riscv_t *rv, rv_insn_t *ir) +static bool do_fuse4(riscv_t *rv, rv_insn_t *ir, uint64_t cycle, uint32_t PC) { - rv->csr_cycle += ir->imm2; + cycle += ir->imm2; opcode_fuse_t *fuse = ir->fuse; /* The memory addresses of the lw instructions are contiguous, therefore * only the first LW instruction needs to be checked to determine if its @@ -469,59 +493,80 @@ static bool do_fuse4(riscv_t *rv, rv_insn_t *ir) RV_EXC_MISALIGN_HANDLER(3, load, false, 1); rv->X[fuse[i].rd] = rv->io.mem_read_w(addr); } - rv->PC += ir->imm2 * ir->insn_len; - if (unlikely(RVOP_NO_NEXT(ir))) + PC += ir->imm2 * 4; + if (unlikely(RVOP_NO_NEXT(ir))) { + rv->csr_cycle = cycle; + rv->PC = PC; return true; + } const rv_insn_t *next = ir->next; - MUST_TAIL return next->impl(rv, next); + MUST_TAIL return next->impl(rv, next, cycle, PC); } /* memset */ -static bool do_fuse5(riscv_t *rv, const rv_insn_t *ir) +static bool do_fuse5(riscv_t *rv, + const rv_insn_t *ir, + uint64_t cycle, + uint32_t PC) { - rv->csr_cycle += 2; + cycle += 2; memory_t *m = ((state_t *) rv->userdata)->mem; memset((char *) m->mem_base + rv->X[rv_reg_a0], rv->X[rv_reg_a1], rv->X[rv_reg_a2]); - rv->PC = rv->X[rv_reg_ra] & ~1U; - if (unlikely(RVOP_NO_NEXT(ir))) + PC = rv->X[rv_reg_ra] & ~1U; + if (unlikely(RVOP_NO_NEXT(ir))) { + rv->csr_cycle = cycle; + rv->PC = PC; return true; + } const rv_insn_t *next = ir->next; - MUST_TAIL return next->impl(rv, next); + MUST_TAIL return next->impl(rv, next, cycle, PC); } /* memcpy */ -static bool do_fuse6(riscv_t *rv, const rv_insn_t *ir) +static bool do_fuse6(riscv_t *rv, + const rv_insn_t *ir, + uint64_t cycle, + uint32_t PC) { - rv->csr_cycle += 2; + cycle += 2; memory_t *m = ((state_t *) rv->userdata)->mem; memcpy((char *) m->mem_base + rv->X[rv_reg_a0], (char *) m->mem_base + rv->X[rv_reg_a1], rv->X[rv_reg_a2]); - rv->PC = rv->X[rv_reg_ra] & ~1U; - if (unlikely(RVOP_NO_NEXT(ir))) + PC = rv->X[rv_reg_ra] & ~1U; + if (unlikely(RVOP_NO_NEXT(ir))) { + rv->csr_cycle = cycle; + rv->PC = PC; return true; + } const rv_insn_t *next = ir->next; - MUST_TAIL return next->impl(rv, next); + MUST_TAIL return next->impl(rv, next, cycle, PC); } /* multiple shift immediate */ -static bool do_fuse7(riscv_t *rv, const rv_insn_t *ir) +static bool do_fuse7(riscv_t *rv, + const rv_insn_t *ir, + uint64_t cycle, + uint32_t PC) { - rv->csr_cycle += ir->imm2; + cycle += ir->imm2; opcode_fuse_t *fuse = ir->fuse; for (int i = 0; i < ir->imm2; i++) shift_func(rv, (const rv_insn_t *) (&fuse[i])); - rv->PC += ir->imm2 * ir->insn_len; - if (unlikely(RVOP_NO_NEXT(ir))) + PC += ir->imm2 * 4; + if (unlikely(RVOP_NO_NEXT(ir))) { + rv->csr_cycle = cycle; + rv->PC = PC; return true; + } const rv_insn_t *next = ir->next; - MUST_TAIL return next->impl(rv, next); + MUST_TAIL return next->impl(rv, next, cycle, PC); } /* clang-format off */ static const void *dispatch_table[] = { /* RV32 instructions */ -#define _(inst, can_branch, reg_mask) [rv_insn_##inst] = do_##inst, +#define _(inst, can_branch, insn_len, reg_mask) [rv_insn_##inst] = do_##inst, RV_INSN_LIST #undef _ /* Macro operation fusion instructions */ @@ -534,7 +579,8 @@ static const void *dispatch_table[] = { FORCE_INLINE bool insn_is_branch(uint8_t opcode) { switch (opcode) { -#define _(inst, can_branch, reg_mask) IIF(can_branch)(case rv_insn_##inst:, ) +#define _(inst, can_branch, insn_len, reg_mask) \ + IIF(can_branch)(case rv_insn_##inst:, ) RV_INSN_LIST #undef _ return true; @@ -582,14 +628,14 @@ static void block_translate(riscv_t *rv, block_map_t *map, block_t *block) /* decode the instruction */ if (!rv_decode(ir, insn)) { - rv->compressed = (ir->insn_len == INSN_16); + rv->compressed = is_compressed(insn); rv_except_illegal_insn(rv, insn); break; } ir->impl = dispatch_table[ir->opcode]; ir->pc = block->pc_end; /* compute the end of pc */ - block->pc_end += ir->insn_len; + block->pc_end += is_compressed(insn) ? 2 : 4; block->n_insn++; prev_ir = ir; /* stop on branch */ @@ -875,7 +921,7 @@ typedef struct { /* clang-format off */ static const void *constopt_table[] = { /* RV32 instructions */ -#define _(inst, can_branch, reg_mask) [rv_insn_##inst] = constopt_##inst, +#define _(inst, can_branch, insn_len, reg_mask) [rv_insn_##inst] = constopt_##inst, RV_INSN_LIST #undef _ }; @@ -986,9 +1032,8 @@ void rv_step(riscv_t *rv, int32_t cycles) /* execute the block */ const rv_insn_t *ir = block->ir_head; - if (unlikely(!ir->impl(rv, ir))) + if (unlikely(!ir->impl(rv, ir, rv->csr_cycle, rv->PC))) break; - prev = block; } } diff --git a/src/riscv_private.h b/src/riscv_private.h index 149986c3..c5eecc31 100644 --- a/src/riscv_private.h +++ b/src/riscv_private.h @@ -139,3 +139,9 @@ FORCE_INLINE uint32_t sign_extend_b(const uint32_t x) { return (int32_t) ((int8_t) x); } + +/* Detect the instruction is RV32C or not */ +FORCE_INLINE bool is_compressed(uint32_t insn) +{ + return (insn & FC_OPCODE) != 3; +} diff --git a/src/rv32_constopt.c b/src/rv32_constopt.c index d3025c81..bccdd107 100644 --- a/src/rv32_constopt.c +++ b/src/rv32_constopt.c @@ -33,7 +33,7 @@ CONSTOPT(auipc, { CONSTOPT(jal, { if (ir->rd) { info->is_constant[ir->rd] = true; - info->const_val[ir->rd] = ir->pc + ir->insn_len; + info->const_val[ir->rd] = ir->pc + 4; } }) @@ -47,7 +47,7 @@ CONSTOPT(jal, { CONSTOPT(jalr, { if (ir->rd) { info->is_constant[ir->rd] = true; - info->const_val[ir->rd] = ir->pc + ir->insn_len; + info->const_val[ir->rd] = ir->pc + 4; } }) @@ -56,7 +56,7 @@ CONSTOPT(jalr, { if (info->is_constant[ir->rs1] && info->is_constant[ir->rs2]) { \ if ((type) info->const_val[ir->rs1] cond \ (type) info->const_val[ir->rs2]) \ - ir->imm = ir->insn_len; \ + ir->imm = 4; \ ir->opcode = rv_insn_jal; \ ir->impl = dispatch_table[ir->opcode]; \ } @@ -717,7 +717,8 @@ CONSTOPT(clw, { info->is_constant[ir->rd] = false; }) */ CONSTOPT(csw, {}) -/* C.NOP is mapped to NOP */ +/* C.NOP */ +CONSTOPT(cnop, {}) /* C.ADDI adds the non-zero sign-extended 6-bit immediate to the value in * register rd then writes the result to rd. C.ADDI expands into @@ -737,7 +738,7 @@ CONSTOPT(caddi, { /* C.JAL */ CONSTOPT(cjal, { info->is_constant[rv_reg_ra] = true; - info->const_val[rv_reg_ra] = ir->pc + ir->insn_len; + info->const_val[rv_reg_ra] = ir->pc + 2; }) /* C.LI loads the sign-extended 6-bit immediate, imm, into register rd. @@ -881,7 +882,7 @@ CONSTOPT(cj, {}) CONSTOPT(cbeqz, { if (info->is_constant[ir->rs1]) { if (info->const_val[ir->rs1]) - ir->imm = ir->insn_len; + ir->imm = 2; ir->opcode = rv_insn_cj; ir->impl = dispatch_table[ir->opcode]; } @@ -891,7 +892,7 @@ CONSTOPT(cbeqz, { CONSTOPT(cbnez, { if (info->is_constant[ir->rs1]) { if (!info->const_val[ir->rs1]) - ir->imm = ir->insn_len; + ir->imm = 2; ir->opcode = rv_insn_cj; ir->impl = dispatch_table[ir->opcode]; } @@ -933,7 +934,7 @@ CONSTOPT(cebreak, {}) /* C.JALR */ CONSTOPT(cjalr, { info->is_constant[rv_reg_ra] = true; - info->const_val[ir->rd] = ir->pc + ir->insn_len; + info->const_val[ir->rd] = ir->pc + 2; }) /* C.ADD adds the values in registers rd and rs2 and writes the result to diff --git a/src/rv32_template.c b/src/rv32_template.c index fddcbb74..93131c98 100644 --- a/src/rv32_template.c +++ b/src/rv32_template.c @@ -15,23 +15,25 @@ RVOP(lui, { rv->X[ir->rd] = ir->imm; }) * lowest 12 bits with zeros, adds this offset to the address of the AUIPC * instruction, then places the result in register rd. */ -RVOP(auipc, { rv->X[ir->rd] = ir->imm + rv->PC; }) +RVOP(auipc, { rv->X[ir->rd] = ir->imm + PC; }) /* JAL: Jump and Link * store successor instruction address into rd. * add next J imm (offset) to pc. */ RVOP(jal, { - const uint32_t pc = rv->PC; + const uint32_t pc = PC; /* Jump */ - rv->PC += ir->imm; + PC += ir->imm; /* link with return address */ if (ir->rd) - rv->X[ir->rd] = pc + ir->insn_len; + rv->X[ir->rd] = pc + 4; /* check instruction misaligned */ RV_EXC_MISALIGN_HANDLER(pc, insn, false, 0); if (ir->branch_taken) - return ir->branch_taken->impl(rv, ir->branch_taken); + return ir->branch_taken->impl(rv, ir->branch_taken, cycle, PC); + rv->csr_cycle = cycle; + rv->PC = PC; return true; }) @@ -43,39 +45,43 @@ RVOP(jal, { * not required. */ RVOP(jalr, { - const uint32_t pc = rv->PC; + const uint32_t pc = PC; /* jump */ - rv->PC = (rv->X[ir->rs1] + ir->imm) & ~1U; + PC = (rv->X[ir->rs1] + ir->imm) & ~1U; /* link */ if (ir->rd) - rv->X[ir->rd] = pc + ir->insn_len; + rv->X[ir->rd] = pc + 4; /* check instruction misaligned */ RV_EXC_MISALIGN_HANDLER(pc, insn, false, 0); - block_t *block = block_find(&rv->block_map, rv->PC); + block_t *block = block_find(&rv->block_map, PC); if (block) - return block->ir_head->impl(rv, block->ir_head); + return block->ir_head->impl(rv, block->ir_head, cycle, PC); + rv->csr_cycle = cycle; + rv->PC = PC; return true; }) /* clang-format off */ -#define BRANCH_FUNC(type, cond) \ - const uint32_t pc = rv->PC; \ - if ((type) rv->X[ir->rs1] cond (type) rv->X[ir->rs2]) { \ - branch_taken = false; \ - if (!ir->branch_untaken) \ - goto nextop; \ - rv->PC += ir->insn_len; \ - last_pc = rv->PC; \ - return ir->branch_untaken->impl(rv, ir->branch_untaken); \ - } \ - branch_taken = true; \ - rv->PC += ir->imm; \ - /* check instruction misaligned */ \ - RV_EXC_MISALIGN_HANDLER(pc, insn, false, 0); \ - if (ir->branch_taken) { \ - last_pc = rv->PC; \ - return ir->branch_taken->impl(rv, ir->branch_taken); \ - } \ +#define BRANCH_FUNC(type, cond) \ + const uint32_t pc = PC; \ + if ((type) rv->X[ir->rs1] cond (type)rv->X[ir->rs2]) { \ + branch_taken = false; \ + if (!ir->branch_untaken) \ + goto nextop; \ + PC += 4; \ + last_pc = PC; \ + return ir->branch_untaken->impl(rv, ir->branch_untaken, cycle, PC); \ + } \ + branch_taken = true; \ + PC += ir->imm; \ + /* check instruction misaligned */ \ + RV_EXC_MISALIGN_HANDLER(pc, insn, false, 0); \ + if (ir->branch_taken) { \ + last_pc = PC; \ + return ir->branch_taken->impl(rv, ir->branch_taken, cycle, PC); \ + } \ + rv->csr_cycle = cycle; \ + rv->PC = PC; \ return true; /* clang-format on */ @@ -248,6 +254,8 @@ RVOP(and, { rv->X[ir->rd] = rv->X[ir->rs1] & rv->X[ir->rs2]; }) /* ECALL: Environment Call */ RVOP(ecall, { rv->compressed = false; + rv->csr_cycle = cycle; + rv->PC = PC; rv->io.on_ecall(rv); return true; }) @@ -255,6 +263,8 @@ RVOP(ecall, { /* EBREAK: Environment Break */ RVOP(ebreak, { rv->compressed = false; + rv->csr_cycle = cycle; + rv->PC = PC; rv->io.on_ebreak(rv); return true; }) @@ -292,8 +302,10 @@ RVOP(mret, { #if RV32_HAS(Zifencei) /* RV32 Zifencei Standard Extension */ RVOP(fencei, { - rv->PC += ir->insn_len; + PC += 4; /* FIXME: fill real implementations */ + rv->csr_cycle = cycle; + rv->PC = PC; return true; }) #endif @@ -795,7 +807,8 @@ RVOP(csw, { rv->io.mem_write_w(addr, rv->X[ir->rs2]); }) -/* C.NOP is mapped to NOP */ +/* C.NOP */ +RVOP(cnop, {/* no operation */}) /* C.ADDI adds the non-zero sign-extended 6-bit immediate to the value in * register rd then writes the result to rd. C.ADDI expands into @@ -807,11 +820,13 @@ RVOP(caddi, { rv->X[ir->rd] += (int16_t) ir->imm; }) /* C.JAL */ RVOP(cjal, { - rv->X[rv_reg_ra] = rv->PC + ir->insn_len; - rv->PC += ir->imm; - RV_EXC_MISALIGN_HANDLER(rv->PC, insn, true, 0); + rv->X[rv_reg_ra] = PC + 2; + PC += ir->imm; + RV_EXC_MISALIGN_HANDLER(PC, insn, true, 0); if (ir->branch_taken) - return ir->branch_taken->impl(rv, ir->branch_taken); + return ir->branch_taken->impl(rv, ir->branch_taken, cycle, PC); + rv->csr_cycle = cycle; + rv->PC = PC; return true; }) @@ -876,10 +891,12 @@ RVOP(cand, { rv->X[ir->rd] = rv->X[ir->rs1] & rv->X[ir->rs2]; }) * C.J expands to jal x0, offset[11:1]. */ RVOP(cj, { - rv->PC += ir->imm; - RV_EXC_MISALIGN_HANDLER(rv->PC, insn, true, 0); + PC += ir->imm; + RV_EXC_MISALIGN_HANDLER(PC, insn, true, 0); if (ir->branch_taken) - return ir->branch_taken->impl(rv, ir->branch_taken); + return ir->branch_taken->impl(rv, ir->branch_taken, cycle, PC); + rv->csr_cycle = cycle; + rv->PC = PC; return true; }) @@ -893,16 +910,18 @@ RVOP(cbeqz, { branch_taken = false; if (!ir->branch_untaken) goto nextop; - rv->PC += ir->insn_len; - last_pc = rv->PC; - return ir->branch_untaken->impl(rv, ir->branch_untaken); + PC += 2; + last_pc = PC; + return ir->branch_untaken->impl(rv, ir->branch_untaken, cycle, PC); } branch_taken = true; - rv->PC += (uint32_t) ir->imm; + PC += (uint32_t) ir->imm; if (ir->branch_taken) { - last_pc = rv->PC; - return ir->branch_taken->impl(rv, ir->branch_taken); + last_pc = PC; + return ir->branch_taken->impl(rv, ir->branch_taken, cycle, PC); } + rv->csr_cycle = cycle; + rv->PC = PC; return true; }) @@ -912,16 +931,18 @@ RVOP(cbnez, { branch_taken = false; if (!ir->branch_untaken) goto nextop; - rv->PC += ir->insn_len; - last_pc = rv->PC; - return ir->branch_untaken->impl(rv, ir->branch_untaken); + PC += 2; + last_pc = PC; + return ir->branch_untaken->impl(rv, ir->branch_untaken, cycle, PC); } branch_taken = true; - rv->PC += (uint32_t) ir->imm; + PC += (uint32_t) ir->imm; if (ir->branch_taken) { - last_pc = rv->PC; - return ir->branch_taken->impl(rv, ir->branch_taken); + last_pc = PC; + return ir->branch_taken->impl(rv, ir->branch_taken, cycle, PC); } + rv->csr_cycle = cycle; + rv->PC = PC; return true; }) @@ -940,10 +961,12 @@ RVOP(clwsp, { /* C.JR */ RVOP(cjr, { - rv->PC = rv->X[ir->rs1]; - block_t *block = block_find(&rv->block_map, rv->PC); + PC = rv->X[ir->rs1]; + block_t *block = block_find(&rv->block_map, PC); if (block) - return block->ir_head->impl(rv, block->ir_head); + return block->ir_head->impl(rv, block->ir_head, cycle, PC); + rv->csr_cycle = cycle; + rv->PC = PC; return true; }) @@ -953,6 +976,8 @@ RVOP(cmv, { rv->X[ir->rd] = rv->X[ir->rs2]; }) /* C.EBREAK */ RVOP(cebreak, { rv->compressed = true; + rv->csr_cycle = cycle; + rv->PC = PC; rv->io.on_ebreak(rv); return true; }) @@ -961,12 +986,14 @@ RVOP(cebreak, { RVOP(cjalr, { /* Unconditional jump and store PC+2 to ra */ const int32_t jump_to = rv->X[ir->rs1]; - rv->X[rv_reg_ra] = rv->PC + ir->insn_len; - rv->PC = jump_to; - RV_EXC_MISALIGN_HANDLER(rv->PC, insn, true, 0); - block_t *block = block_find(&rv->block_map, rv->PC); + rv->X[rv_reg_ra] = PC + 2; + PC = jump_to; + RV_EXC_MISALIGN_HANDLER(PC, insn, true, 0); + block_t *block = block_find(&rv->block_map, PC); if (block) - return block->ir_head->impl(rv, block->ir_head); + return block->ir_head->impl(rv, block->ir_head, cycle, PC); + rv->csr_cycle = cycle; + rv->PC = PC; return true; }) diff --git a/tools/rv_histogram.c b/tools/rv_histogram.c index 1ff5510d..ec1bd8c4 100644 --- a/tools/rv_histogram.c +++ b/tools/rv_histogram.c @@ -39,8 +39,8 @@ typedef struct { } rv_hist_t; static rv_hist_t rv_insn_stats[] = { -#define _(inst, can_branch, reg_mask) {#inst, 0, reg_mask}, - RV_INSN_LIST _(unknown, 0, 0) +#define _(inst, can_branch, insn_len, reg_mask) {#inst, 0, reg_mask}, + RV_INSN_LIST _(unknown, 0, 0, 0) #undef _ };