diff options
Diffstat (limited to 'target/riscv/translate.c')
-rw-r--r-- | target/riscv/translate.c | 2582 |
1 files changed, 999 insertions, 1583 deletions
diff --git a/target/riscv/translate.c b/target/riscv/translate.c index 18d7b6d147..9ff09ebdb6 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -19,7 +19,7 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "cpu.h" -#include "tcg-op.h" +#include "tcg/tcg-op.h" #include "disas/disas.h" #include "exec/cpu_ldst.h" #include "exec/exec-all.h" @@ -28,1791 +28,1185 @@ #include "exec/translator.h" #include "exec/log.h" +#include "semihosting/semihost.h" #include "instmap.h" +#include "internals.h" + +#define HELPER_H "helper.h" +#include "exec/helper-info.c.inc" +#undef HELPER_H /* global register indices */ -static TCGv cpu_gpr[32], cpu_pc; +static TCGv cpu_gpr[32], cpu_gprh[32], cpu_pc, cpu_vl, cpu_vstart; static TCGv_i64 cpu_fpr[32]; /* assume F and D extensions */ static TCGv load_res; static TCGv load_val; +/* globals for PM CSRs */ +static TCGv pm_mask; +static TCGv pm_base; -#include "exec/gen-icount.h" +/* + * If an operation is being performed on less than TARGET_LONG_BITS, + * it may require the inputs to be sign- or zero-extended; which will + * depend on the exact operation being performed. + */ +typedef enum { + EXT_NONE, + EXT_SIGN, + EXT_ZERO, +} DisasExtend; typedef struct DisasContext { DisasContextBase base; - /* pc_succ_insn points to the instruction following base.pc_next */ - target_ulong pc_succ_insn; + target_ulong cur_insn_len; + target_ulong pc_save; + target_ulong priv_ver; + RISCVMXL misa_mxl_max; + RISCVMXL xl; + RISCVMXL address_xl; + uint32_t misa_ext; uint32_t opcode; - uint32_t flags; + RISCVExtStatus mstatus_fs; + RISCVExtStatus mstatus_vs; uint32_t mem_idx; - /* Remember the rounding mode encoded in the previous fp instruction, - which we have already installed into env->fp_status. Or -1 for - no previous fp instruction. Note that we exit the TB when writing - to any system register, which includes CSR_FRM, so we do not have - to reset this known value. */ + uint32_t priv; + /* + * Remember the rounding mode encoded in the previous fp instruction, + * which we have already installed into env->fp_status. Or -1 for + * no previous fp instruction. Note that we exit the TB when writing + * to any system register, which includes CSR_FRM, so we do not have + * to reset this known value. + */ int frm; + RISCVMXL ol; + bool virt_inst_excp; + bool virt_enabled; + const RISCVCPUConfig *cfg_ptr; + /* vector extension */ + bool vill; + /* + * Encode LMUL to lmul as follows: + * LMUL vlmul lmul + * 1 000 0 + * 2 001 1 + * 4 010 2 + * 8 011 3 + * - 100 - + * 1/8 101 -3 + * 1/4 110 -2 + * 1/2 111 -1 + */ + int8_t lmul; + uint8_t sew; + uint8_t vta; + uint8_t vma; + bool cfg_vta_all_1s; + bool vstart_eq_zero; + bool vl_eq_vlmax; + CPUState *cs; + TCGv zero; + /* PointerMasking extension */ + bool pm_mask_enabled; + bool pm_base_enabled; + /* Ztso */ + bool ztso; + /* Use icount trigger for native debug */ + bool itrigger; + /* FRM is known to contain a valid value. */ + bool frm_valid; + bool insn_start_updated; } DisasContext; -/* convert riscv funct3 to qemu memop for load/store */ -static const int tcg_memop_lookup[8] = { - [0 ... 7] = -1, - [0] = MO_SB, - [1] = MO_TESW, - [2] = MO_TESL, - [4] = MO_UB, - [5] = MO_TEUW, -#ifdef TARGET_RISCV64 - [3] = MO_TEQ, - [6] = MO_TEUL, +static inline bool has_ext(DisasContext *ctx, uint32_t ext) +{ + return ctx->misa_ext & ext; +} + +#ifdef TARGET_RISCV32 +#define get_xl(ctx) MXL_RV32 +#elif defined(CONFIG_USER_ONLY) +#define get_xl(ctx) MXL_RV64 +#else +#define get_xl(ctx) ((ctx)->xl) #endif -}; -#ifdef TARGET_RISCV64 -#define CASE_OP_32_64(X) case X: case glue(X, W) +#ifdef TARGET_RISCV32 +#define get_address_xl(ctx) MXL_RV32 +#elif defined(CONFIG_USER_ONLY) +#define get_address_xl(ctx) MXL_RV64 #else -#define CASE_OP_32_64(X) case X +#define get_address_xl(ctx) ((ctx)->address_xl) #endif -static void generate_exception(DisasContext *ctx, int excp) +/* The word size for this machine mode. */ +static inline int __attribute__((unused)) get_xlen(DisasContext *ctx) { - tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next); - TCGv_i32 helper_tmp = tcg_const_i32(excp); - gen_helper_raise_exception(cpu_env, helper_tmp); - tcg_temp_free_i32(helper_tmp); - ctx->base.is_jmp = DISAS_NORETURN; + return 16 << get_xl(ctx); } -static void generate_exception_mbadaddr(DisasContext *ctx, int excp) +/* The operation length, as opposed to the xlen. */ +#ifdef TARGET_RISCV32 +#define get_ol(ctx) MXL_RV32 +#else +#define get_ol(ctx) ((ctx)->ol) +#endif + +static inline int get_olen(DisasContext *ctx) { - tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next); - tcg_gen_st_tl(cpu_pc, cpu_env, offsetof(CPURISCVState, badaddr)); - TCGv_i32 helper_tmp = tcg_const_i32(excp); - gen_helper_raise_exception(cpu_env, helper_tmp); - tcg_temp_free_i32(helper_tmp); - ctx->base.is_jmp = DISAS_NORETURN; + return 16 << get_ol(ctx); } -static void gen_exception_debug(void) +/* The maximum register length */ +#ifdef TARGET_RISCV32 +#define get_xl_max(ctx) MXL_RV32 +#else +#define get_xl_max(ctx) ((ctx)->misa_mxl_max) +#endif + +/* + * RISC-V requires NaN-boxing of narrower width floating point values. + * This applies when a 32-bit value is assigned to a 64-bit FP register. + * For consistency and simplicity, we nanbox results even when the RVD + * extension is not present. + */ +static void gen_nanbox_s(TCGv_i64 out, TCGv_i64 in) { - TCGv_i32 helper_tmp = tcg_const_i32(EXCP_DEBUG); - gen_helper_raise_exception(cpu_env, helper_tmp); - tcg_temp_free_i32(helper_tmp); + tcg_gen_ori_i64(out, in, MAKE_64BIT_MASK(32, 32)); } -static void gen_exception_illegal(DisasContext *ctx) +static void gen_nanbox_h(TCGv_i64 out, TCGv_i64 in) { - generate_exception(ctx, RISCV_EXCP_ILLEGAL_INST); + tcg_gen_ori_i64(out, in, MAKE_64BIT_MASK(16, 48)); } -static void gen_exception_inst_addr_mis(DisasContext *ctx) +/* + * A narrow n-bit operation, where n < FLEN, checks that input operands + * are correctly Nan-boxed, i.e., all upper FLEN - n bits are 1. + * If so, the least-significant bits of the input are used, otherwise the + * input value is treated as an n-bit canonical NaN (v2.2 section 9.2). + * + * Here, the result is always nan-boxed, even the canonical nan. + */ +static void gen_check_nanbox_h(TCGv_i64 out, TCGv_i64 in) { - generate_exception_mbadaddr(ctx, RISCV_EXCP_INST_ADDR_MIS); + TCGv_i64 t_max = tcg_constant_i64(0xffffffffffff0000ull); + TCGv_i64 t_nan = tcg_constant_i64(0xffffffffffff7e00ull); + + tcg_gen_movcond_i64(TCG_COND_GEU, out, in, t_max, in, t_nan); } -static inline bool use_goto_tb(DisasContext *ctx, target_ulong dest) +static void gen_check_nanbox_s(TCGv_i64 out, TCGv_i64 in) { - if (unlikely(ctx->base.singlestep_enabled)) { - return false; - } + TCGv_i64 t_max = tcg_constant_i64(0xffffffff00000000ull); + TCGv_i64 t_nan = tcg_constant_i64(0xffffffff7fc00000ull); -#ifndef CONFIG_USER_ONLY - return (ctx->base.tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK); -#else - return true; -#endif + tcg_gen_movcond_i64(TCG_COND_GEU, out, in, t_max, in, t_nan); } -static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) +static void decode_save_opc(DisasContext *ctx) { - if (use_goto_tb(ctx, dest)) { - /* chaining is only allowed when the jump is to the same page */ - tcg_gen_goto_tb(n); - tcg_gen_movi_tl(cpu_pc, dest); - tcg_gen_exit_tb(ctx->base.tb, n); + assert(!ctx->insn_start_updated); + ctx->insn_start_updated = true; + tcg_set_insn_start_param(ctx->base.insn_start, 1, ctx->opcode); +} + +static void gen_pc_plus_diff(TCGv target, DisasContext *ctx, + target_long diff) +{ + target_ulong dest = ctx->base.pc_next + diff; + + assert(ctx->pc_save != -1); + if (tb_cflags(ctx->base.tb) & CF_PCREL) { + tcg_gen_addi_tl(target, cpu_pc, dest - ctx->pc_save); + if (get_xl(ctx) == MXL_RV32) { + tcg_gen_ext32s_tl(target, target); + } } else { - tcg_gen_movi_tl(cpu_pc, dest); - if (ctx->base.singlestep_enabled) { - gen_exception_debug(); - } else { - tcg_gen_lookup_and_goto_ptr(); + if (get_xl(ctx) == MXL_RV32) { + dest = (int32_t)dest; } + tcg_gen_movi_tl(target, dest); } } -/* Wrapper for getting reg values - need to check of reg is zero since - * cpu_gpr[0] is not actually allocated - */ -static inline void gen_get_gpr(TCGv t, int reg_num) +static void gen_update_pc(DisasContext *ctx, target_long diff) { - if (reg_num == 0) { - tcg_gen_movi_tl(t, 0); - } else { - tcg_gen_mov_tl(t, cpu_gpr[reg_num]); - } + gen_pc_plus_diff(cpu_pc, ctx, diff); + ctx->pc_save = ctx->base.pc_next + diff; } -/* Wrapper for setting reg values - need to check of reg is zero since - * cpu_gpr[0] is not actually allocated. this is more for safety purposes, - * since we usually avoid calling the OP_TYPE_gen function if we see a write to - * $zero - */ -static inline void gen_set_gpr(int reg_num_dst, TCGv t) +static void generate_exception(DisasContext *ctx, int excp) +{ + gen_update_pc(ctx, 0); + gen_helper_raise_exception(tcg_env, tcg_constant_i32(excp)); + ctx->base.is_jmp = DISAS_NORETURN; +} + +static void gen_exception_illegal(DisasContext *ctx) { - if (reg_num_dst != 0) { - tcg_gen_mov_tl(cpu_gpr[reg_num_dst], t); + tcg_gen_st_i32(tcg_constant_i32(ctx->opcode), tcg_env, + offsetof(CPURISCVState, bins)); + if (ctx->virt_inst_excp) { + generate_exception(ctx, RISCV_EXCP_VIRT_INSTRUCTION_FAULT); + } else { + generate_exception(ctx, RISCV_EXCP_ILLEGAL_INST); } } -static void gen_mulhsu(TCGv ret, TCGv arg1, TCGv arg2) +static void gen_exception_inst_addr_mis(DisasContext *ctx, TCGv target) { - TCGv rl = tcg_temp_new(); - TCGv rh = tcg_temp_new(); + tcg_gen_st_tl(target, tcg_env, offsetof(CPURISCVState, badaddr)); + generate_exception(ctx, RISCV_EXCP_INST_ADDR_MIS); +} - tcg_gen_mulu2_tl(rl, rh, arg1, arg2); - /* fix up for one negative */ - tcg_gen_sari_tl(rl, arg1, TARGET_LONG_BITS - 1); - tcg_gen_and_tl(rl, rl, arg2); - tcg_gen_sub_tl(ret, rh, rl); +static void lookup_and_goto_ptr(DisasContext *ctx) +{ +#ifndef CONFIG_USER_ONLY + if (ctx->itrigger) { + gen_helper_itrigger_match(tcg_env); + } +#endif + tcg_gen_lookup_and_goto_ptr(); +} - tcg_temp_free(rl); - tcg_temp_free(rh); +static void exit_tb(DisasContext *ctx) +{ +#ifndef CONFIG_USER_ONLY + if (ctx->itrigger) { + gen_helper_itrigger_match(tcg_env); + } +#endif + tcg_gen_exit_tb(NULL, 0); } -static void gen_fsgnj(DisasContext *ctx, uint32_t rd, uint32_t rs1, - uint32_t rs2, int rm, uint64_t min) +static void gen_goto_tb(DisasContext *ctx, int n, target_long diff) { - switch (rm) { - case 0: /* fsgnj */ - if (rs1 == rs2) { /* FMOV */ - tcg_gen_mov_i64(cpu_fpr[rd], cpu_fpr[rs1]); - } else { - tcg_gen_deposit_i64(cpu_fpr[rd], cpu_fpr[rs2], cpu_fpr[rs1], - 0, min == INT32_MIN ? 31 : 63); - } - break; - case 1: /* fsgnjn */ - if (rs1 == rs2) { /* FNEG */ - tcg_gen_xori_i64(cpu_fpr[rd], cpu_fpr[rs1], min); + target_ulong dest = ctx->base.pc_next + diff; + + /* + * Under itrigger, instruction executes one by one like singlestep, + * direct block chain benefits will be small. + */ + if (translator_use_goto_tb(&ctx->base, dest) && !ctx->itrigger) { + /* + * For pcrel, the pc must always be up-to-date on entry to + * the linked TB, so that it can use simple additions for all + * further adjustments. For !pcrel, the linked TB is compiled + * to know its full virtual address, so we can delay the + * update to pc to the unlinked path. A long chain of links + * can thus avoid many updates to the PC. + */ + if (tb_cflags(ctx->base.tb) & CF_PCREL) { + gen_update_pc(ctx, diff); + tcg_gen_goto_tb(n); } else { - TCGv_i64 t0 = tcg_temp_new_i64(); - tcg_gen_not_i64(t0, cpu_fpr[rs2]); - tcg_gen_deposit_i64(cpu_fpr[rd], t0, cpu_fpr[rs1], - 0, min == INT32_MIN ? 31 : 63); - tcg_temp_free_i64(t0); + tcg_gen_goto_tb(n); + gen_update_pc(ctx, diff); } - break; - case 2: /* fsgnjx */ - if (rs1 == rs2) { /* FABS */ - tcg_gen_andi_i64(cpu_fpr[rd], cpu_fpr[rs1], ~min); - } else { - TCGv_i64 t0 = tcg_temp_new_i64(); - tcg_gen_andi_i64(t0, cpu_fpr[rs2], min); - tcg_gen_xor_i64(cpu_fpr[rd], cpu_fpr[rs1], t0); - tcg_temp_free_i64(t0); - } - break; - default: - gen_exception_illegal(ctx); + tcg_gen_exit_tb(ctx->base.tb, n); + } else { + gen_update_pc(ctx, diff); + lookup_and_goto_ptr(ctx); } } -static void gen_arith(DisasContext *ctx, uint32_t opc, int rd, int rs1, - int rs2) +/* + * Wrappers for getting reg values. + * + * The $zero register does not have cpu_gpr[0] allocated -- we supply the + * constant zero as a source, and an uninitialized sink as destination. + * + * Further, we may provide an extension for word operations. + */ +static TCGv get_gpr(DisasContext *ctx, int reg_num, DisasExtend ext) { - TCGv source1, source2, cond1, cond2, zeroreg, resultopt1; - source1 = tcg_temp_new(); - source2 = tcg_temp_new(); - gen_get_gpr(source1, rs1); - gen_get_gpr(source2, rs2); + TCGv t; - switch (opc) { - CASE_OP_32_64(OPC_RISC_ADD): - tcg_gen_add_tl(source1, source1, source2); - break; - CASE_OP_32_64(OPC_RISC_SUB): - tcg_gen_sub_tl(source1, source1, source2); - break; -#if defined(TARGET_RISCV64) - case OPC_RISC_SLLW: - tcg_gen_andi_tl(source2, source2, 0x1F); - tcg_gen_shl_tl(source1, source1, source2); - break; -#endif - case OPC_RISC_SLL: - tcg_gen_andi_tl(source2, source2, TARGET_LONG_BITS - 1); - tcg_gen_shl_tl(source1, source1, source2); - break; - case OPC_RISC_SLT: - tcg_gen_setcond_tl(TCG_COND_LT, source1, source1, source2); - break; - case OPC_RISC_SLTU: - tcg_gen_setcond_tl(TCG_COND_LTU, source1, source1, source2); - break; - case OPC_RISC_XOR: - tcg_gen_xor_tl(source1, source1, source2); - break; -#if defined(TARGET_RISCV64) - case OPC_RISC_SRLW: - /* clear upper 32 */ - tcg_gen_ext32u_tl(source1, source1); - tcg_gen_andi_tl(source2, source2, 0x1F); - tcg_gen_shr_tl(source1, source1, source2); - break; -#endif - case OPC_RISC_SRL: - tcg_gen_andi_tl(source2, source2, TARGET_LONG_BITS - 1); - tcg_gen_shr_tl(source1, source1, source2); - break; -#if defined(TARGET_RISCV64) - case OPC_RISC_SRAW: - /* first, trick to get it to act like working on 32 bits (get rid of - upper 32, sign extend to fill space) */ - tcg_gen_ext32s_tl(source1, source1); - tcg_gen_andi_tl(source2, source2, 0x1F); - tcg_gen_sar_tl(source1, source1, source2); - break; -#endif - case OPC_RISC_SRA: - tcg_gen_andi_tl(source2, source2, TARGET_LONG_BITS - 1); - tcg_gen_sar_tl(source1, source1, source2); - break; - case OPC_RISC_OR: - tcg_gen_or_tl(source1, source1, source2); - break; - case OPC_RISC_AND: - tcg_gen_and_tl(source1, source1, source2); - break; - CASE_OP_32_64(OPC_RISC_MUL): - tcg_gen_mul_tl(source1, source1, source2); - break; - case OPC_RISC_MULH: - tcg_gen_muls2_tl(source2, source1, source1, source2); - break; - case OPC_RISC_MULHSU: - gen_mulhsu(source1, source1, source2); - break; - case OPC_RISC_MULHU: - tcg_gen_mulu2_tl(source2, source1, source1, source2); - break; -#if defined(TARGET_RISCV64) - case OPC_RISC_DIVW: - tcg_gen_ext32s_tl(source1, source1); - tcg_gen_ext32s_tl(source2, source2); - /* fall through to DIV */ -#endif - case OPC_RISC_DIV: - /* Handle by altering args to tcg_gen_div to produce req'd results: - * For overflow: want source1 in source1 and 1 in source2 - * For div by zero: want -1 in source1 and 1 in source2 -> -1 result */ - cond1 = tcg_temp_new(); - cond2 = tcg_temp_new(); - zeroreg = tcg_const_tl(0); - resultopt1 = tcg_temp_new(); - - tcg_gen_movi_tl(resultopt1, (target_ulong)-1); - tcg_gen_setcondi_tl(TCG_COND_EQ, cond2, source2, (target_ulong)(~0L)); - tcg_gen_setcondi_tl(TCG_COND_EQ, cond1, source1, - ((target_ulong)1) << (TARGET_LONG_BITS - 1)); - tcg_gen_and_tl(cond1, cond1, cond2); /* cond1 = overflow */ - tcg_gen_setcondi_tl(TCG_COND_EQ, cond2, source2, 0); /* cond2 = div 0 */ - /* if div by zero, set source1 to -1, otherwise don't change */ - tcg_gen_movcond_tl(TCG_COND_EQ, source1, cond2, zeroreg, source1, - resultopt1); - /* if overflow or div by zero, set source2 to 1, else don't change */ - tcg_gen_or_tl(cond1, cond1, cond2); - tcg_gen_movi_tl(resultopt1, (target_ulong)1); - tcg_gen_movcond_tl(TCG_COND_EQ, source2, cond1, zeroreg, source2, - resultopt1); - tcg_gen_div_tl(source1, source1, source2); - - tcg_temp_free(cond1); - tcg_temp_free(cond2); - tcg_temp_free(zeroreg); - tcg_temp_free(resultopt1); - break; -#if defined(TARGET_RISCV64) - case OPC_RISC_DIVUW: - tcg_gen_ext32u_tl(source1, source1); - tcg_gen_ext32u_tl(source2, source2); - /* fall through to DIVU */ -#endif - case OPC_RISC_DIVU: - cond1 = tcg_temp_new(); - zeroreg = tcg_const_tl(0); - resultopt1 = tcg_temp_new(); - - tcg_gen_setcondi_tl(TCG_COND_EQ, cond1, source2, 0); - tcg_gen_movi_tl(resultopt1, (target_ulong)-1); - tcg_gen_movcond_tl(TCG_COND_EQ, source1, cond1, zeroreg, source1, - resultopt1); - tcg_gen_movi_tl(resultopt1, (target_ulong)1); - tcg_gen_movcond_tl(TCG_COND_EQ, source2, cond1, zeroreg, source2, - resultopt1); - tcg_gen_divu_tl(source1, source1, source2); - - tcg_temp_free(cond1); - tcg_temp_free(zeroreg); - tcg_temp_free(resultopt1); - break; -#if defined(TARGET_RISCV64) - case OPC_RISC_REMW: - tcg_gen_ext32s_tl(source1, source1); - tcg_gen_ext32s_tl(source2, source2); - /* fall through to REM */ -#endif - case OPC_RISC_REM: - cond1 = tcg_temp_new(); - cond2 = tcg_temp_new(); - zeroreg = tcg_const_tl(0); - resultopt1 = tcg_temp_new(); - - tcg_gen_movi_tl(resultopt1, 1L); - tcg_gen_setcondi_tl(TCG_COND_EQ, cond2, source2, (target_ulong)-1); - tcg_gen_setcondi_tl(TCG_COND_EQ, cond1, source1, - (target_ulong)1 << (TARGET_LONG_BITS - 1)); - tcg_gen_and_tl(cond2, cond1, cond2); /* cond1 = overflow */ - tcg_gen_setcondi_tl(TCG_COND_EQ, cond1, source2, 0); /* cond2 = div 0 */ - /* if overflow or div by zero, set source2 to 1, else don't change */ - tcg_gen_or_tl(cond2, cond1, cond2); - tcg_gen_movcond_tl(TCG_COND_EQ, source2, cond2, zeroreg, source2, - resultopt1); - tcg_gen_rem_tl(resultopt1, source1, source2); - /* if div by zero, just return the original dividend */ - tcg_gen_movcond_tl(TCG_COND_EQ, source1, cond1, zeroreg, resultopt1, - source1); - - tcg_temp_free(cond1); - tcg_temp_free(cond2); - tcg_temp_free(zeroreg); - tcg_temp_free(resultopt1); + if (reg_num == 0) { + return ctx->zero; + } + + switch (get_ol(ctx)) { + case MXL_RV32: + switch (ext) { + case EXT_NONE: + break; + case EXT_SIGN: + t = tcg_temp_new(); + tcg_gen_ext32s_tl(t, cpu_gpr[reg_num]); + return t; + case EXT_ZERO: + t = tcg_temp_new(); + tcg_gen_ext32u_tl(t, cpu_gpr[reg_num]); + return t; + default: + g_assert_not_reached(); + } break; -#if defined(TARGET_RISCV64) - case OPC_RISC_REMUW: - tcg_gen_ext32u_tl(source1, source1); - tcg_gen_ext32u_tl(source2, source2); - /* fall through to REMU */ -#endif - case OPC_RISC_REMU: - cond1 = tcg_temp_new(); - zeroreg = tcg_const_tl(0); - resultopt1 = tcg_temp_new(); - - tcg_gen_movi_tl(resultopt1, (target_ulong)1); - tcg_gen_setcondi_tl(TCG_COND_EQ, cond1, source2, 0); - tcg_gen_movcond_tl(TCG_COND_EQ, source2, cond1, zeroreg, source2, - resultopt1); - tcg_gen_remu_tl(resultopt1, source1, source2); - /* if div by zero, just return the original dividend */ - tcg_gen_movcond_tl(TCG_COND_EQ, source1, cond1, zeroreg, resultopt1, - source1); - - tcg_temp_free(cond1); - tcg_temp_free(zeroreg); - tcg_temp_free(resultopt1); + case MXL_RV64: + case MXL_RV128: break; default: - gen_exception_illegal(ctx); - return; + g_assert_not_reached(); } + return cpu_gpr[reg_num]; +} - if (opc & 0x8) { /* sign extend for W instructions */ - tcg_gen_ext32s_tl(source1, source1); +static TCGv get_gprh(DisasContext *ctx, int reg_num) +{ + assert(get_xl(ctx) == MXL_RV128); + if (reg_num == 0) { + return ctx->zero; } + return cpu_gprh[reg_num]; +} - gen_set_gpr(rd, source1); - tcg_temp_free(source1); - tcg_temp_free(source2); +static TCGv dest_gpr(DisasContext *ctx, int reg_num) +{ + if (reg_num == 0 || get_olen(ctx) < TARGET_LONG_BITS) { + return tcg_temp_new(); + } + return cpu_gpr[reg_num]; } -static void gen_arith_imm(DisasContext *ctx, uint32_t opc, int rd, - int rs1, target_long imm) +static TCGv dest_gprh(DisasContext *ctx, int reg_num) { - TCGv source1 = tcg_temp_new(); - int shift_len = TARGET_LONG_BITS; - int shift_a; + if (reg_num == 0) { + return tcg_temp_new(); + } + return cpu_gprh[reg_num]; +} - gen_get_gpr(source1, rs1); +static void gen_set_gpr(DisasContext *ctx, int reg_num, TCGv t) +{ + if (reg_num != 0) { + switch (get_ol(ctx)) { + case MXL_RV32: + tcg_gen_ext32s_tl(cpu_gpr[reg_num], t); + break; + case MXL_RV64: + case MXL_RV128: + tcg_gen_mov_tl(cpu_gpr[reg_num], t); + break; + default: + g_assert_not_reached(); + } - switch (opc) { - case OPC_RISC_ADDI: -#if defined(TARGET_RISCV64) - case OPC_RISC_ADDIW: -#endif - tcg_gen_addi_tl(source1, source1, imm); - break; - case OPC_RISC_SLTI: - tcg_gen_setcondi_tl(TCG_COND_LT, source1, source1, imm); - break; - case OPC_RISC_SLTIU: - tcg_gen_setcondi_tl(TCG_COND_LTU, source1, source1, imm); - break; - case OPC_RISC_XORI: - tcg_gen_xori_tl(source1, source1, imm); - break; - case OPC_RISC_ORI: - tcg_gen_ori_tl(source1, source1, imm); - break; - case OPC_RISC_ANDI: - tcg_gen_andi_tl(source1, source1, imm); - break; -#if defined(TARGET_RISCV64) - case OPC_RISC_SLLIW: - shift_len = 32; - /* FALLTHRU */ -#endif - case OPC_RISC_SLLI: - if (imm >= shift_len) { - goto do_illegal; + if (get_xl_max(ctx) == MXL_RV128) { + tcg_gen_sari_tl(cpu_gprh[reg_num], cpu_gpr[reg_num], 63); } - tcg_gen_shli_tl(source1, source1, imm); - break; -#if defined(TARGET_RISCV64) - case OPC_RISC_SHIFT_RIGHT_IW: - shift_len = 32; - /* FALLTHRU */ -#endif - case OPC_RISC_SHIFT_RIGHT_I: - /* differentiate on IMM */ - shift_a = imm & 0x400; - imm &= 0x3ff; - if (imm >= shift_len) { - goto do_illegal; + } +} + +static void gen_set_gpri(DisasContext *ctx, int reg_num, target_long imm) +{ + if (reg_num != 0) { + switch (get_ol(ctx)) { + case MXL_RV32: + tcg_gen_movi_tl(cpu_gpr[reg_num], (int32_t)imm); + break; + case MXL_RV64: + case MXL_RV128: + tcg_gen_movi_tl(cpu_gpr[reg_num], imm); + break; + default: + g_assert_not_reached(); } - if (imm != 0) { - if (shift_a) { - /* SRAI[W] */ - tcg_gen_sextract_tl(source1, source1, imm, shift_len - imm); - } else { - /* SRLI[W] */ - tcg_gen_extract_tl(source1, source1, imm, shift_len - imm); - } - /* No further sign-extension needed for W instructions. */ - opc &= ~0x8; + + if (get_xl_max(ctx) == MXL_RV128) { + tcg_gen_movi_tl(cpu_gprh[reg_num], -(imm < 0)); } - break; - default: - do_illegal: - gen_exception_illegal(ctx); - return; } +} - if (opc & 0x8) { /* sign-extend for W instructions */ - tcg_gen_ext32s_tl(source1, source1); +static void gen_set_gpr128(DisasContext *ctx, int reg_num, TCGv rl, TCGv rh) +{ + assert(get_ol(ctx) == MXL_RV128); + if (reg_num != 0) { + tcg_gen_mov_tl(cpu_gpr[reg_num], rl); + tcg_gen_mov_tl(cpu_gprh[reg_num], rh); } - - gen_set_gpr(rd, source1); - tcg_temp_free(source1); } -static void gen_jal(CPURISCVState *env, DisasContext *ctx, int rd, - target_ulong imm) +static TCGv_i64 get_fpr_hs(DisasContext *ctx, int reg_num) { - target_ulong next_pc; + if (!ctx->cfg_ptr->ext_zfinx) { + return cpu_fpr[reg_num]; + } - /* check misaligned: */ - next_pc = ctx->base.pc_next + imm; - if (!riscv_has_ext(env, RVC)) { - if ((next_pc & 0x3) != 0) { - gen_exception_inst_addr_mis(ctx); - return; - } + if (reg_num == 0) { + return tcg_constant_i64(0); } - if (rd != 0) { - tcg_gen_movi_tl(cpu_gpr[rd], ctx->pc_succ_insn); + switch (get_xl(ctx)) { + case MXL_RV32: +#ifdef TARGET_RISCV32 + { + TCGv_i64 t = tcg_temp_new_i64(); + tcg_gen_ext_i32_i64(t, cpu_gpr[reg_num]); + return t; + } +#else + /* fall through */ + case MXL_RV64: + return cpu_gpr[reg_num]; +#endif + default: + g_assert_not_reached(); } - - gen_goto_tb(ctx, 0, ctx->base.pc_next + imm); /* must use this for safety */ - ctx->base.is_jmp = DISAS_NORETURN; } -static void gen_jalr(CPURISCVState *env, DisasContext *ctx, uint32_t opc, - int rd, int rs1, target_long imm) +static TCGv_i64 get_fpr_d(DisasContext *ctx, int reg_num) { - /* no chaining with JALR */ - TCGLabel *misaligned = NULL; - TCGv t0 = tcg_temp_new(); - - switch (opc) { - case OPC_RISC_JALR: - gen_get_gpr(cpu_pc, rs1); - tcg_gen_addi_tl(cpu_pc, cpu_pc, imm); - tcg_gen_andi_tl(cpu_pc, cpu_pc, (target_ulong)-2); + if (!ctx->cfg_ptr->ext_zfinx) { + return cpu_fpr[reg_num]; + } - if (!riscv_has_ext(env, RVC)) { - misaligned = gen_new_label(); - tcg_gen_andi_tl(t0, cpu_pc, 0x2); - tcg_gen_brcondi_tl(TCG_COND_NE, t0, 0x0, misaligned); - } + if (reg_num == 0) { + return tcg_constant_i64(0); + } + switch (get_xl(ctx)) { + case MXL_RV32: + { + TCGv_i64 t = tcg_temp_new_i64(); + tcg_gen_concat_tl_i64(t, cpu_gpr[reg_num], cpu_gpr[reg_num + 1]); + return t; + } +#ifdef TARGET_RISCV64 + case MXL_RV64: + return cpu_gpr[reg_num]; +#endif + default: + g_assert_not_reached(); + } +} - if (rd != 0) { - tcg_gen_movi_tl(cpu_gpr[rd], ctx->pc_succ_insn); - } - tcg_gen_lookup_and_goto_ptr(); +static TCGv_i64 dest_fpr(DisasContext *ctx, int reg_num) +{ + if (!ctx->cfg_ptr->ext_zfinx) { + return cpu_fpr[reg_num]; + } - if (misaligned) { - gen_set_label(misaligned); - gen_exception_inst_addr_mis(ctx); - } - ctx->base.is_jmp = DISAS_NORETURN; - break; + if (reg_num == 0) { + return tcg_temp_new_i64(); + } + switch (get_xl(ctx)) { + case MXL_RV32: + return tcg_temp_new_i64(); +#ifdef TARGET_RISCV64 + case MXL_RV64: + return cpu_gpr[reg_num]; +#endif default: - gen_exception_illegal(ctx); - break; + g_assert_not_reached(); } - tcg_temp_free(t0); } -static void gen_branch(CPURISCVState *env, DisasContext *ctx, uint32_t opc, - int rs1, int rs2, target_long bimm) +/* assume it is nanboxing (for normal) or sign-extended (for zfinx) */ +static void gen_set_fpr_hs(DisasContext *ctx, int reg_num, TCGv_i64 t) { - TCGLabel *l = gen_new_label(); - TCGv source1, source2; - source1 = tcg_temp_new(); - source2 = tcg_temp_new(); - gen_get_gpr(source1, rs1); - gen_get_gpr(source2, rs2); - - switch (opc) { - case OPC_RISC_BEQ: - tcg_gen_brcond_tl(TCG_COND_EQ, source1, source2, l); - break; - case OPC_RISC_BNE: - tcg_gen_brcond_tl(TCG_COND_NE, source1, source2, l); - break; - case OPC_RISC_BLT: - tcg_gen_brcond_tl(TCG_COND_LT, source1, source2, l); - break; - case OPC_RISC_BGE: - tcg_gen_brcond_tl(TCG_COND_GE, source1, source2, l); - break; - case OPC_RISC_BLTU: - tcg_gen_brcond_tl(TCG_COND_LTU, source1, source2, l); - break; - case OPC_RISC_BGEU: - tcg_gen_brcond_tl(TCG_COND_GEU, source1, source2, l); - break; - default: - gen_exception_illegal(ctx); + if (!ctx->cfg_ptr->ext_zfinx) { + tcg_gen_mov_i64(cpu_fpr[reg_num], t); return; } - tcg_temp_free(source1); - tcg_temp_free(source2); - - gen_goto_tb(ctx, 1, ctx->pc_succ_insn); - gen_set_label(l); /* branch taken */ - if (!riscv_has_ext(env, RVC) && ((ctx->base.pc_next + bimm) & 0x3)) { - /* misaligned */ - gen_exception_inst_addr_mis(ctx); - } else { - gen_goto_tb(ctx, 0, ctx->base.pc_next + bimm); + if (reg_num != 0) { + switch (get_xl(ctx)) { + case MXL_RV32: +#ifdef TARGET_RISCV32 + tcg_gen_extrl_i64_i32(cpu_gpr[reg_num], t); + break; +#else + /* fall through */ + case MXL_RV64: + tcg_gen_mov_i64(cpu_gpr[reg_num], t); + break; +#endif + default: + g_assert_not_reached(); + } } - ctx->base.is_jmp = DISAS_NORETURN; } -static void gen_load(DisasContext *ctx, uint32_t opc, int rd, int rs1, - target_long imm) +static void gen_set_fpr_d(DisasContext *ctx, int reg_num, TCGv_i64 t) { - TCGv t0 = tcg_temp_new(); - TCGv t1 = tcg_temp_new(); - gen_get_gpr(t0, rs1); - tcg_gen_addi_tl(t0, t0, imm); - int memop = tcg_memop_lookup[(opc >> 12) & 0x7]; - - if (memop < 0) { - gen_exception_illegal(ctx); + if (!ctx->cfg_ptr->ext_zfinx) { + tcg_gen_mov_i64(cpu_fpr[reg_num], t); return; } - tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, memop); - gen_set_gpr(rd, t1); - tcg_temp_free(t0); - tcg_temp_free(t1); + if (reg_num != 0) { + switch (get_xl(ctx)) { + case MXL_RV32: +#ifdef TARGET_RISCV32 + tcg_gen_extr_i64_i32(cpu_gpr[reg_num], cpu_gpr[reg_num + 1], t); + break; +#else + tcg_gen_ext32s_i64(cpu_gpr[reg_num], t); + tcg_gen_sari_i64(cpu_gpr[reg_num + 1], t, 32); + break; + case MXL_RV64: + tcg_gen_mov_i64(cpu_gpr[reg_num], t); + break; +#endif + default: + g_assert_not_reached(); + } + } } -static void gen_store(DisasContext *ctx, uint32_t opc, int rs1, int rs2, - target_long imm) +static void gen_jal(DisasContext *ctx, int rd, target_ulong imm) { - TCGv t0 = tcg_temp_new(); - TCGv dat = tcg_temp_new(); - gen_get_gpr(t0, rs1); - tcg_gen_addi_tl(t0, t0, imm); - gen_get_gpr(dat, rs2); - int memop = tcg_memop_lookup[(opc >> 12) & 0x7]; + TCGv succ_pc = dest_gpr(ctx, rd); - if (memop < 0) { - gen_exception_illegal(ctx); - return; + /* check misaligned: */ + if (!has_ext(ctx, RVC) && !ctx->cfg_ptr->ext_zca) { + if ((imm & 0x3) != 0) { + TCGv target_pc = tcg_temp_new(); + gen_pc_plus_diff(target_pc, ctx, imm); + gen_exception_inst_addr_mis(ctx, target_pc); + return; + } } - tcg_gen_qemu_st_tl(dat, t0, ctx->mem_idx, memop); - tcg_temp_free(t0); - tcg_temp_free(dat); + gen_pc_plus_diff(succ_pc, ctx, ctx->cur_insn_len); + gen_set_gpr(ctx, rd, succ_pc); + + gen_goto_tb(ctx, 0, imm); /* must use this for safety */ + ctx->base.is_jmp = DISAS_NORETURN; } -static void gen_fp_load(DisasContext *ctx, uint32_t opc, int rd, - int rs1, target_long imm) +/* Compute a canonical address from a register plus offset. */ +static TCGv get_address(DisasContext *ctx, int rs1, int imm) { - TCGv t0; - - if (!(ctx->flags & TB_FLAGS_FP_ENABLE)) { - gen_exception_illegal(ctx); - return; + TCGv addr = tcg_temp_new(); + TCGv src1 = get_gpr(ctx, rs1, EXT_NONE); + + tcg_gen_addi_tl(addr, src1, imm); + if (ctx->pm_mask_enabled) { + tcg_gen_andc_tl(addr, addr, pm_mask); + } else if (get_address_xl(ctx) == MXL_RV32) { + tcg_gen_ext32u_tl(addr, addr); + } + if (ctx->pm_base_enabled) { + tcg_gen_or_tl(addr, addr, pm_base); } - t0 = tcg_temp_new(); - gen_get_gpr(t0, rs1); - tcg_gen_addi_tl(t0, t0, imm); + return addr; +} - switch (opc) { - case OPC_RISC_FLW: - tcg_gen_qemu_ld_i64(cpu_fpr[rd], t0, ctx->mem_idx, MO_TEUL); - /* RISC-V requires NaN-boxing of narrower width floating point values */ - tcg_gen_ori_i64(cpu_fpr[rd], cpu_fpr[rd], 0xffffffff00000000ULL); - break; - case OPC_RISC_FLD: - tcg_gen_qemu_ld_i64(cpu_fpr[rd], t0, ctx->mem_idx, MO_TEQ); - break; - default: - gen_exception_illegal(ctx); - break; +/* Compute a canonical address from a register plus reg offset. */ +static TCGv get_address_indexed(DisasContext *ctx, int rs1, TCGv offs) +{ + TCGv addr = tcg_temp_new(); + TCGv src1 = get_gpr(ctx, rs1, EXT_NONE); + + tcg_gen_add_tl(addr, src1, offs); + if (ctx->pm_mask_enabled) { + tcg_gen_andc_tl(addr, addr, pm_mask); + } else if (get_xl(ctx) == MXL_RV32) { + tcg_gen_ext32u_tl(addr, addr); + } + if (ctx->pm_base_enabled) { + tcg_gen_or_tl(addr, addr, pm_base); } - tcg_temp_free(t0); + return addr; } -static void gen_fp_store(DisasContext *ctx, uint32_t opc, int rs1, - int rs2, target_long imm) +#ifndef CONFIG_USER_ONLY +/* + * We will have already diagnosed disabled state, + * and need to turn initial/clean into dirty. + */ +static void mark_fs_dirty(DisasContext *ctx) { - TCGv t0; + TCGv tmp; - if (!(ctx->flags & TB_FLAGS_FP_ENABLE)) { - gen_exception_illegal(ctx); + if (!has_ext(ctx, RVF)) { return; } - t0 = tcg_temp_new(); - gen_get_gpr(t0, rs1); - tcg_gen_addi_tl(t0, t0, imm); + if (ctx->mstatus_fs != EXT_STATUS_DIRTY) { + /* Remember the state change for the rest of the TB. */ + ctx->mstatus_fs = EXT_STATUS_DIRTY; - switch (opc) { - case OPC_RISC_FSW: - tcg_gen_qemu_st_i64(cpu_fpr[rs2], t0, ctx->mem_idx, MO_TEUL); - break; - case OPC_RISC_FSD: - tcg_gen_qemu_st_i64(cpu_fpr[rs2], t0, ctx->mem_idx, MO_TEQ); - break; - default: - gen_exception_illegal(ctx); - break; - } + tmp = tcg_temp_new(); + tcg_gen_ld_tl(tmp, tcg_env, offsetof(CPURISCVState, mstatus)); + tcg_gen_ori_tl(tmp, tmp, MSTATUS_FS); + tcg_gen_st_tl(tmp, tcg_env, offsetof(CPURISCVState, mstatus)); - tcg_temp_free(t0); + if (ctx->virt_enabled) { + tcg_gen_ld_tl(tmp, tcg_env, offsetof(CPURISCVState, mstatus_hs)); + tcg_gen_ori_tl(tmp, tmp, MSTATUS_FS); + tcg_gen_st_tl(tmp, tcg_env, offsetof(CPURISCVState, mstatus_hs)); + } + } } +#else +static inline void mark_fs_dirty(DisasContext *ctx) { } +#endif -static void gen_atomic(DisasContext *ctx, uint32_t opc, - int rd, int rs1, int rs2) +#ifndef CONFIG_USER_ONLY +/* + * We will have already diagnosed disabled state, + * and need to turn initial/clean into dirty. + */ +static void mark_vs_dirty(DisasContext *ctx) { - TCGv src1, src2, dat; - TCGLabel *l1, *l2; - TCGMemOp mop; - bool aq, rl; - - /* Extract the size of the atomic operation. */ - switch (extract32(opc, 12, 3)) { - case 2: /* 32-bit */ - mop = MO_ALIGN | MO_TESL; - break; -#if defined(TARGET_RISCV64) - case 3: /* 64-bit */ - mop = MO_ALIGN | MO_TEQ; - break; -#endif - default: - gen_exception_illegal(ctx); - return; - } - rl = extract32(opc, 25, 1); - aq = extract32(opc, 26, 1); - - src1 = tcg_temp_new(); - src2 = tcg_temp_new(); - - switch (MASK_OP_ATOMIC_NO_AQ_RL_SZ(opc)) { - case OPC_RISC_LR: - /* Put addr in load_res, data in load_val. */ - gen_get_gpr(src1, rs1); - if (rl) { - tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); - } - tcg_gen_qemu_ld_tl(load_val, src1, ctx->mem_idx, mop); - if (aq) { - tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); - } - tcg_gen_mov_tl(load_res, src1); - gen_set_gpr(rd, load_val); - break; + TCGv tmp; - case OPC_RISC_SC: - l1 = gen_new_label(); - l2 = gen_new_label(); - dat = tcg_temp_new(); - - gen_get_gpr(src1, rs1); - tcg_gen_brcond_tl(TCG_COND_NE, load_res, src1, l1); - - gen_get_gpr(src2, rs2); - /* Note that the TCG atomic primitives are SC, - so we can ignore AQ/RL along this path. */ - tcg_gen_atomic_cmpxchg_tl(src1, load_res, load_val, src2, - ctx->mem_idx, mop); - tcg_gen_setcond_tl(TCG_COND_NE, dat, src1, load_val); - gen_set_gpr(rd, dat); - tcg_gen_br(l2); - - gen_set_label(l1); - /* Address comparion failure. However, we still need to - provide the memory barrier implied by AQ/RL. */ - tcg_gen_mb(TCG_MO_ALL + aq * TCG_BAR_LDAQ + rl * TCG_BAR_STRL); - tcg_gen_movi_tl(dat, 1); - gen_set_gpr(rd, dat); - - gen_set_label(l2); - tcg_temp_free(dat); - break; + if (ctx->mstatus_vs != EXT_STATUS_DIRTY) { + /* Remember the state change for the rest of the TB. */ + ctx->mstatus_vs = EXT_STATUS_DIRTY; - case OPC_RISC_AMOSWAP: - /* Note that the TCG atomic primitives are SC, - so we can ignore AQ/RL along this path. */ - gen_get_gpr(src1, rs1); - gen_get_gpr(src2, rs2); - tcg_gen_atomic_xchg_tl(src2, src1, src2, ctx->mem_idx, mop); - gen_set_gpr(rd, src2); - break; - case OPC_RISC_AMOADD: - gen_get_gpr(src1, rs1); - gen_get_gpr(src2, rs2); - tcg_gen_atomic_fetch_add_tl(src2, src1, src2, ctx->mem_idx, mop); - gen_set_gpr(rd, src2); - break; - case OPC_RISC_AMOXOR: - gen_get_gpr(src1, rs1); - gen_get_gpr(src2, rs2); - tcg_gen_atomic_fetch_xor_tl(src2, src1, src2, ctx->mem_idx, mop); - gen_set_gpr(rd, src2); - break; - case OPC_RISC_AMOAND: - gen_get_gpr(src1, rs1); - gen_get_gpr(src2, rs2); - tcg_gen_atomic_fetch_and_tl(src2, src1, src2, ctx->mem_idx, mop); - gen_set_gpr(rd, src2); - break; - case OPC_RISC_AMOOR: - gen_get_gpr(src1, rs1); - gen_get_gpr(src2, rs2); - tcg_gen_atomic_fetch_or_tl(src2, src1, src2, ctx->mem_idx, mop); - gen_set_gpr(rd, src2); - break; - case OPC_RISC_AMOMIN: - gen_get_gpr(src1, rs1); - gen_get_gpr(src2, rs2); - tcg_gen_atomic_fetch_smin_tl(src2, src1, src2, ctx->mem_idx, mop); - gen_set_gpr(rd, src2); - break; - case OPC_RISC_AMOMAX: - gen_get_gpr(src1, rs1); - gen_get_gpr(src2, rs2); - tcg_gen_atomic_fetch_smax_tl(src2, src1, src2, ctx->mem_idx, mop); - gen_set_gpr(rd, src2); - break; - case OPC_RISC_AMOMINU: - gen_get_gpr(src1, rs1); - gen_get_gpr(src2, rs2); - tcg_gen_atomic_fetch_umin_tl(src2, src1, src2, ctx->mem_idx, mop); - gen_set_gpr(rd, src2); - break; - case OPC_RISC_AMOMAXU: - gen_get_gpr(src1, rs1); - gen_get_gpr(src2, rs2); - tcg_gen_atomic_fetch_umax_tl(src2, src1, src2, ctx->mem_idx, mop); - gen_set_gpr(rd, src2); - break; + tmp = tcg_temp_new(); + tcg_gen_ld_tl(tmp, tcg_env, offsetof(CPURISCVState, mstatus)); + tcg_gen_ori_tl(tmp, tmp, MSTATUS_VS); + tcg_gen_st_tl(tmp, tcg_env, offsetof(CPURISCVState, mstatus)); - default: - gen_exception_illegal(ctx); - break; + if (ctx->virt_enabled) { + tcg_gen_ld_tl(tmp, tcg_env, offsetof(CPURISCVState, mstatus_hs)); + tcg_gen_ori_tl(tmp, tmp, MSTATUS_VS); + tcg_gen_st_tl(tmp, tcg_env, offsetof(CPURISCVState, mstatus_hs)); + } } +} +#else +static inline void mark_vs_dirty(DisasContext *ctx) { } +#endif - tcg_temp_free(src1); - tcg_temp_free(src2); +static void finalize_rvv_inst(DisasContext *ctx) +{ + mark_vs_dirty(ctx); + ctx->vstart_eq_zero = true; } static void gen_set_rm(DisasContext *ctx, int rm) { - TCGv_i32 t0; - if (ctx->frm == rm) { return; } ctx->frm = rm; - t0 = tcg_const_i32(rm); - gen_helper_set_rounding_mode(cpu_env, t0); - tcg_temp_free_i32(t0); + + if (rm == RISCV_FRM_DYN) { + /* The helper will return only if frm valid. */ + ctx->frm_valid = true; + } + + /* The helper may raise ILLEGAL_INSN -- record binv for unwind. */ + decode_save_opc(ctx); + gen_helper_set_rounding_mode(tcg_env, tcg_constant_i32(rm)); } -static void gen_fp_fmadd(DisasContext *ctx, uint32_t opc, int rd, - int rs1, int rs2, int rs3, int rm) +static void gen_set_rm_chkfrm(DisasContext *ctx, int rm) { - switch (opc) { - case OPC_RISC_FMADD_S: - gen_set_rm(ctx, rm); - gen_helper_fmadd_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], - cpu_fpr[rs2], cpu_fpr[rs3]); - break; - case OPC_RISC_FMADD_D: - gen_set_rm(ctx, rm); - gen_helper_fmadd_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], - cpu_fpr[rs2], cpu_fpr[rs3]); - break; - default: - gen_exception_illegal(ctx); - break; + if (ctx->frm == rm && ctx->frm_valid) { + return; } + ctx->frm = rm; + ctx->frm_valid = true; + + /* The helper may raise ILLEGAL_INSN -- record binv for unwind. */ + decode_save_opc(ctx); + gen_helper_set_rounding_mode_chkfrm(tcg_env, tcg_constant_i32(rm)); } -static void gen_fp_fmsub(DisasContext *ctx, uint32_t opc, int rd, - int rs1, int rs2, int rs3, int rm) +static int ex_plus_1(DisasContext *ctx, int nf) { - switch (opc) { - case OPC_RISC_FMSUB_S: - gen_set_rm(ctx, rm); - gen_helper_fmsub_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], - cpu_fpr[rs2], cpu_fpr[rs3]); - break; - case OPC_RISC_FMSUB_D: - gen_set_rm(ctx, rm); - gen_helper_fmsub_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], - cpu_fpr[rs2], cpu_fpr[rs3]); - break; - default: - gen_exception_illegal(ctx); - break; + return nf + 1; +} + +#define EX_SH(amount) \ + static int ex_shift_##amount(DisasContext *ctx, int imm) \ + { \ + return imm << amount; \ } +EX_SH(1) +EX_SH(2) +EX_SH(3) +EX_SH(4) +EX_SH(12) + +#define REQUIRE_EXT(ctx, ext) do { \ + if (!has_ext(ctx, ext)) { \ + return false; \ + } \ +} while (0) + +#define REQUIRE_32BIT(ctx) do { \ + if (get_xl(ctx) != MXL_RV32) { \ + return false; \ + } \ +} while (0) + +#define REQUIRE_64BIT(ctx) do { \ + if (get_xl(ctx) != MXL_RV64) { \ + return false; \ + } \ +} while (0) + +#define REQUIRE_128BIT(ctx) do { \ + if (get_xl(ctx) != MXL_RV128) { \ + return false; \ + } \ +} while (0) + +#define REQUIRE_64_OR_128BIT(ctx) do { \ + if (get_xl(ctx) == MXL_RV32) { \ + return false; \ + } \ +} while (0) + +#define REQUIRE_EITHER_EXT(ctx, A, B) do { \ + if (!ctx->cfg_ptr->ext_##A && \ + !ctx->cfg_ptr->ext_##B) { \ + return false; \ + } \ +} while (0) + +static int ex_rvc_register(DisasContext *ctx, int reg) +{ + return 8 + reg; } -static void gen_fp_fnmsub(DisasContext *ctx, uint32_t opc, int rd, - int rs1, int rs2, int rs3, int rm) +static int ex_sreg_register(DisasContext *ctx, int reg) { - switch (opc) { - case OPC_RISC_FNMSUB_S: - gen_set_rm(ctx, rm); - gen_helper_fnmsub_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], - cpu_fpr[rs2], cpu_fpr[rs3]); - break; - case OPC_RISC_FNMSUB_D: - gen_set_rm(ctx, rm); - gen_helper_fnmsub_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], - cpu_fpr[rs2], cpu_fpr[rs3]); - break; - default: - gen_exception_illegal(ctx); - break; + return reg < 2 ? reg + 8 : reg + 16; +} + +static int ex_rvc_shiftli(DisasContext *ctx, int imm) +{ + /* For RV128 a shamt of 0 means a shift by 64. */ + if (get_ol(ctx) == MXL_RV128) { + imm = imm ? imm : 64; } + return imm; } -static void gen_fp_fnmadd(DisasContext *ctx, uint32_t opc, int rd, - int rs1, int rs2, int rs3, int rm) +static int ex_rvc_shiftri(DisasContext *ctx, int imm) { - switch (opc) { - case OPC_RISC_FNMADD_S: - gen_set_rm(ctx, rm); - gen_helper_fnmadd_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], - cpu_fpr[rs2], cpu_fpr[rs3]); - break; - case OPC_RISC_FNMADD_D: - gen_set_rm(ctx, rm); - gen_helper_fnmadd_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], - cpu_fpr[rs2], cpu_fpr[rs3]); - break; - default: - gen_exception_illegal(ctx); - break; + /* + * For RV128 a shamt of 0 means a shift by 64, furthermore, for right + * shifts, the shamt is sign-extended. + */ + if (get_ol(ctx) == MXL_RV128) { + imm = imm | (imm & 32) << 1; + imm = imm ? imm : 64; } + return imm; } -static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd, - int rs1, int rs2, int rm) +/* Include the auto-generated decoder for 32 bit insn */ +#include "decode-insn32.c.inc" + +static bool gen_logic_imm_fn(DisasContext *ctx, arg_i *a, + void (*func)(TCGv, TCGv, target_long)) { - TCGv t0 = NULL; + TCGv dest = dest_gpr(ctx, a->rd); + TCGv src1 = get_gpr(ctx, a->rs1, EXT_NONE); - if (!(ctx->flags & TB_FLAGS_FP_ENABLE)) { - goto do_illegal; + func(dest, src1, a->imm); + + if (get_xl(ctx) == MXL_RV128) { + TCGv src1h = get_gprh(ctx, a->rs1); + TCGv desth = dest_gprh(ctx, a->rd); + + func(desth, src1h, -(a->imm < 0)); + gen_set_gpr128(ctx, a->rd, dest, desth); + } else { + gen_set_gpr(ctx, a->rd, dest); } - switch (opc) { - case OPC_RISC_FADD_S: - gen_set_rm(ctx, rm); - gen_helper_fadd_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); - break; - case OPC_RISC_FSUB_S: - gen_set_rm(ctx, rm); - gen_helper_fsub_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); - break; - case OPC_RISC_FMUL_S: - gen_set_rm(ctx, rm); - gen_helper_fmul_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); - break; - case OPC_RISC_FDIV_S: - gen_set_rm(ctx, rm); - gen_helper_fdiv_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); - break; - case OPC_RISC_FSQRT_S: - gen_set_rm(ctx, rm); - gen_helper_fsqrt_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1]); - break; - case OPC_RISC_FSGNJ_S: - gen_fsgnj(ctx, rd, rs1, rs2, rm, INT32_MIN); - break; + return true; +} - case OPC_RISC_FMIN_S: - /* also handles: OPC_RISC_FMAX_S */ - switch (rm) { - case 0x0: - gen_helper_fmin_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); - break; - case 0x1: - gen_helper_fmax_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); - break; - default: - goto do_illegal; - } - break; +static bool gen_logic(DisasContext *ctx, arg_r *a, + void (*func)(TCGv, TCGv, TCGv)) +{ + TCGv dest = dest_gpr(ctx, a->rd); + TCGv src1 = get_gpr(ctx, a->rs1, EXT_NONE); + TCGv src2 = get_gpr(ctx, a->rs2, EXT_NONE); - case OPC_RISC_FEQ_S: - /* also handles: OPC_RISC_FLT_S, OPC_RISC_FLE_S */ - t0 = tcg_temp_new(); - switch (rm) { - case 0x0: - gen_helper_fle_s(t0, cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); - break; - case 0x1: - gen_helper_flt_s(t0, cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); - break; - case 0x2: - gen_helper_feq_s(t0, cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); - break; - default: - goto do_illegal; - } - gen_set_gpr(rd, t0); - tcg_temp_free(t0); - break; + func(dest, src1, src2); - case OPC_RISC_FCVT_W_S: - /* also OPC_RISC_FCVT_WU_S, OPC_RISC_FCVT_L_S, OPC_RISC_FCVT_LU_S */ - t0 = tcg_temp_new(); - switch (rs2) { - case 0: /* FCVT_W_S */ - gen_set_rm(ctx, rm); - gen_helper_fcvt_w_s(t0, cpu_env, cpu_fpr[rs1]); - break; - case 1: /* FCVT_WU_S */ - gen_set_rm(ctx, rm); - gen_helper_fcvt_wu_s(t0, cpu_env, cpu_fpr[rs1]); - break; -#if defined(TARGET_RISCV64) - case 2: /* FCVT_L_S */ - gen_set_rm(ctx, rm); - gen_helper_fcvt_l_s(t0, cpu_env, cpu_fpr[rs1]); - break; - case 3: /* FCVT_LU_S */ - gen_set_rm(ctx, rm); - gen_helper_fcvt_lu_s(t0, cpu_env, cpu_fpr[rs1]); - break; -#endif - default: - goto do_illegal; - } - gen_set_gpr(rd, t0); - tcg_temp_free(t0); - break; + if (get_xl(ctx) == MXL_RV128) { + TCGv src1h = get_gprh(ctx, a->rs1); + TCGv src2h = get_gprh(ctx, a->rs2); + TCGv desth = dest_gprh(ctx, a->rd); - case OPC_RISC_FCVT_S_W: - /* also OPC_RISC_FCVT_S_WU, OPC_RISC_FCVT_S_L, OPC_RISC_FCVT_S_LU */ - t0 = tcg_temp_new(); - gen_get_gpr(t0, rs1); - switch (rs2) { - case 0: /* FCVT_S_W */ - gen_set_rm(ctx, rm); - gen_helper_fcvt_s_w(cpu_fpr[rd], cpu_env, t0); - break; - case 1: /* FCVT_S_WU */ - gen_set_rm(ctx, rm); - gen_helper_fcvt_s_wu(cpu_fpr[rd], cpu_env, t0); - break; -#if defined(TARGET_RISCV64) - case 2: /* FCVT_S_L */ - gen_set_rm(ctx, rm); - gen_helper_fcvt_s_l(cpu_fpr[rd], cpu_env, t0); - break; - case 3: /* FCVT_S_LU */ - gen_set_rm(ctx, rm); - gen_helper_fcvt_s_lu(cpu_fpr[rd], cpu_env, t0); - break; -#endif - default: - goto do_illegal; - } - tcg_temp_free(t0); - break; + func(desth, src1h, src2h); + gen_set_gpr128(ctx, a->rd, dest, desth); + } else { + gen_set_gpr(ctx, a->rd, dest); + } - case OPC_RISC_FMV_X_S: - /* also OPC_RISC_FCLASS_S */ - t0 = tcg_temp_new(); - switch (rm) { - case 0: /* FMV */ -#if defined(TARGET_RISCV64) - tcg_gen_ext32s_tl(t0, cpu_fpr[rs1]); -#else - tcg_gen_extrl_i64_i32(t0, cpu_fpr[rs1]); -#endif - break; - case 1: - gen_helper_fclass_s(t0, cpu_fpr[rs1]); - break; - default: - goto do_illegal; + return true; +} + +static bool gen_arith_imm_fn(DisasContext *ctx, arg_i *a, DisasExtend ext, + void (*func)(TCGv, TCGv, target_long), + void (*f128)(TCGv, TCGv, TCGv, TCGv, target_long)) +{ + TCGv dest = dest_gpr(ctx, a->rd); + TCGv src1 = get_gpr(ctx, a->rs1, ext); + + if (get_ol(ctx) < MXL_RV128) { + func(dest, src1, a->imm); + gen_set_gpr(ctx, a->rd, dest); + } else { + if (f128 == NULL) { + return false; } - gen_set_gpr(rd, t0); - tcg_temp_free(t0); - break; - case OPC_RISC_FMV_S_X: - t0 = tcg_temp_new(); - gen_get_gpr(t0, rs1); -#if defined(TARGET_RISCV64) - tcg_gen_mov_i64(cpu_fpr[rd], t0); -#else - tcg_gen_extu_i32_i64(cpu_fpr[rd], t0); -#endif - tcg_temp_free(t0); - break; + TCGv src1h = get_gprh(ctx, a->rs1); + TCGv desth = dest_gprh(ctx, a->rd); - /* double */ - case OPC_RISC_FADD_D: - gen_set_rm(ctx, rm); - gen_helper_fadd_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); - break; - case OPC_RISC_FSUB_D: - gen_set_rm(ctx, rm); - gen_helper_fsub_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); - break; - case OPC_RISC_FMUL_D: - gen_set_rm(ctx, rm); - gen_helper_fmul_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); - break; - case OPC_RISC_FDIV_D: - gen_set_rm(ctx, rm); - gen_helper_fdiv_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); - break; - case OPC_RISC_FSQRT_D: - gen_set_rm(ctx, rm); - gen_helper_fsqrt_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1]); - break; - case OPC_RISC_FSGNJ_D: - gen_fsgnj(ctx, rd, rs1, rs2, rm, INT64_MIN); - break; + f128(dest, desth, src1, src1h, a->imm); + gen_set_gpr128(ctx, a->rd, dest, desth); + } + return true; +} - case OPC_RISC_FMIN_D: - /* also OPC_RISC_FMAX_D */ - switch (rm) { - case 0: - gen_helper_fmin_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); - break; - case 1: - gen_helper_fmax_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); - break; - default: - goto do_illegal; - } - break; +static bool gen_arith_imm_tl(DisasContext *ctx, arg_i *a, DisasExtend ext, + void (*func)(TCGv, TCGv, TCGv), + void (*f128)(TCGv, TCGv, TCGv, TCGv, TCGv, TCGv)) +{ + TCGv dest = dest_gpr(ctx, a->rd); + TCGv src1 = get_gpr(ctx, a->rs1, ext); + TCGv src2 = tcg_constant_tl(a->imm); - case OPC_RISC_FCVT_S_D: - switch (rs2) { - case 1: - gen_set_rm(ctx, rm); - gen_helper_fcvt_s_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1]); - break; - default: - goto do_illegal; + if (get_ol(ctx) < MXL_RV128) { + func(dest, src1, src2); + gen_set_gpr(ctx, a->rd, dest); + } else { + if (f128 == NULL) { + return false; } - break; - case OPC_RISC_FCVT_D_S: - switch (rs2) { - case 0: - gen_set_rm(ctx, rm); - gen_helper_fcvt_d_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1]); - break; - default: - goto do_illegal; - } - break; + TCGv src1h = get_gprh(ctx, a->rs1); + TCGv src2h = tcg_constant_tl(-(a->imm < 0)); + TCGv desth = dest_gprh(ctx, a->rd); - case OPC_RISC_FEQ_D: - /* also OPC_RISC_FLT_D, OPC_RISC_FLE_D */ - t0 = tcg_temp_new(); - switch (rm) { - case 0: - gen_helper_fle_d(t0, cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); - break; - case 1: - gen_helper_flt_d(t0, cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); - break; - case 2: - gen_helper_feq_d(t0, cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); - break; - default: - goto do_illegal; - } - gen_set_gpr(rd, t0); - tcg_temp_free(t0); - break; + f128(dest, desth, src1, src1h, src2, src2h); + gen_set_gpr128(ctx, a->rd, dest, desth); + } + return true; +} - case OPC_RISC_FCVT_W_D: - /* also OPC_RISC_FCVT_WU_D, OPC_RISC_FCVT_L_D, OPC_RISC_FCVT_LU_D */ - t0 = tcg_temp_new(); - switch (rs2) { - case 0: - gen_set_rm(ctx, rm); - gen_helper_fcvt_w_d(t0, cpu_env, cpu_fpr[rs1]); - break; - case 1: - gen_set_rm(ctx, rm); - gen_helper_fcvt_wu_d(t0, cpu_env, cpu_fpr[rs1]); - break; -#if defined(TARGET_RISCV64) - case 2: - gen_set_rm(ctx, rm); - gen_helper_fcvt_l_d(t0, cpu_env, cpu_fpr[rs1]); - break; - case 3: - gen_set_rm(ctx, rm); - gen_helper_fcvt_lu_d(t0, cpu_env, cpu_fpr[rs1]); - break; -#endif - default: - goto do_illegal; - } - gen_set_gpr(rd, t0); - tcg_temp_free(t0); - break; +static bool gen_arith(DisasContext *ctx, arg_r *a, DisasExtend ext, + void (*func)(TCGv, TCGv, TCGv), + void (*f128)(TCGv, TCGv, TCGv, TCGv, TCGv, TCGv)) +{ + TCGv dest = dest_gpr(ctx, a->rd); + TCGv src1 = get_gpr(ctx, a->rs1, ext); + TCGv src2 = get_gpr(ctx, a->rs2, ext); - case OPC_RISC_FCVT_D_W: - /* also OPC_RISC_FCVT_D_WU, OPC_RISC_FCVT_D_L, OPC_RISC_FCVT_D_LU */ - t0 = tcg_temp_new(); - gen_get_gpr(t0, rs1); - switch (rs2) { - case 0: - gen_set_rm(ctx, rm); - gen_helper_fcvt_d_w(cpu_fpr[rd], cpu_env, t0); - break; - case 1: - gen_set_rm(ctx, rm); - gen_helper_fcvt_d_wu(cpu_fpr[rd], cpu_env, t0); - break; -#if defined(TARGET_RISCV64) - case 2: - gen_set_rm(ctx, rm); - gen_helper_fcvt_d_l(cpu_fpr[rd], cpu_env, t0); - break; - case 3: - gen_set_rm(ctx, rm); - gen_helper_fcvt_d_lu(cpu_fpr[rd], cpu_env, t0); - break; -#endif - default: - goto do_illegal; + if (get_ol(ctx) < MXL_RV128) { + func(dest, src1, src2); + gen_set_gpr(ctx, a->rd, dest); + } else { + if (f128 == NULL) { + return false; } - tcg_temp_free(t0); - break; -#if defined(TARGET_RISCV64) - case OPC_RISC_FMV_X_D: - /* also OPC_RISC_FCLASS_D */ - switch (rm) { - case 0: /* FMV */ - gen_set_gpr(rd, cpu_fpr[rs1]); - break; - case 1: - t0 = tcg_temp_new(); - gen_helper_fclass_d(t0, cpu_fpr[rs1]); - gen_set_gpr(rd, t0); - tcg_temp_free(t0); - break; - default: - goto do_illegal; - } - break; + TCGv src1h = get_gprh(ctx, a->rs1); + TCGv src2h = get_gprh(ctx, a->rs2); + TCGv desth = dest_gprh(ctx, a->rd); - case OPC_RISC_FMV_D_X: - t0 = tcg_temp_new(); - gen_get_gpr(t0, rs1); - tcg_gen_mov_tl(cpu_fpr[rd], t0); - tcg_temp_free(t0); - break; -#endif + f128(dest, desth, src1, src1h, src2, src2h); + gen_set_gpr128(ctx, a->rd, dest, desth); + } + return true; +} - default: - do_illegal: - if (t0) { - tcg_temp_free(t0); +static bool gen_arith_per_ol(DisasContext *ctx, arg_r *a, DisasExtend ext, + void (*f_tl)(TCGv, TCGv, TCGv), + void (*f_32)(TCGv, TCGv, TCGv), + void (*f_128)(TCGv, TCGv, TCGv, TCGv, TCGv, TCGv)) +{ + int olen = get_olen(ctx); + + if (olen != TARGET_LONG_BITS) { + if (olen == 32) { + f_tl = f_32; + } else if (olen != 128) { + g_assert_not_reached(); } - gen_exception_illegal(ctx); - break; } + return gen_arith(ctx, a, ext, f_tl, f_128); } -static void gen_system(CPURISCVState *env, DisasContext *ctx, uint32_t opc, - int rd, int rs1, int csr) +static bool gen_shift_imm_fn(DisasContext *ctx, arg_shift *a, DisasExtend ext, + void (*func)(TCGv, TCGv, target_long), + void (*f128)(TCGv, TCGv, TCGv, TCGv, target_long)) { - TCGv source1, csr_store, dest, rs1_pass, imm_rs1; - source1 = tcg_temp_new(); - csr_store = tcg_temp_new(); - dest = tcg_temp_new(); - rs1_pass = tcg_temp_new(); - imm_rs1 = tcg_temp_new(); - gen_get_gpr(source1, rs1); - tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next); - tcg_gen_movi_tl(rs1_pass, rs1); - tcg_gen_movi_tl(csr_store, csr); /* copy into temp reg to feed to helper */ + TCGv dest, src1; + int max_len = get_olen(ctx); -#ifndef CONFIG_USER_ONLY - /* Extract funct7 value and check whether it matches SFENCE.VMA */ - if ((opc == OPC_RISC_ECALL) && ((csr >> 5) == 9)) { - /* sfence.vma */ - /* TODO: handle ASID specific fences */ - gen_helper_tlb_flush(cpu_env); - return; + if (a->shamt >= max_len) { + return false; } -#endif - switch (opc) { - case OPC_RISC_ECALL: - switch (csr) { - case 0x0: /* ECALL */ - /* always generates U-level ECALL, fixed in do_interrupt handler */ - generate_exception(ctx, RISCV_EXCP_U_ECALL); - tcg_gen_exit_tb(NULL, 0); /* no chaining */ - ctx->base.is_jmp = DISAS_NORETURN; - break; - case 0x1: /* EBREAK */ - generate_exception(ctx, RISCV_EXCP_BREAKPOINT); - tcg_gen_exit_tb(NULL, 0); /* no chaining */ - ctx->base.is_jmp = DISAS_NORETURN; - break; -#ifndef CONFIG_USER_ONLY - case 0x002: /* URET */ - gen_exception_illegal(ctx); - break; - case 0x102: /* SRET */ - if (riscv_has_ext(env, RVS)) { - gen_helper_sret(cpu_pc, cpu_env, cpu_pc); - tcg_gen_exit_tb(NULL, 0); /* no chaining */ - ctx->base.is_jmp = DISAS_NORETURN; - } else { - gen_exception_illegal(ctx); - } - break; - case 0x202: /* HRET */ - gen_exception_illegal(ctx); - break; - case 0x302: /* MRET */ - gen_helper_mret(cpu_pc, cpu_env, cpu_pc); - tcg_gen_exit_tb(NULL, 0); /* no chaining */ - ctx->base.is_jmp = DISAS_NORETURN; - break; - case 0x7b2: /* DRET */ - gen_exception_illegal(ctx); - break; - case 0x105: /* WFI */ - tcg_gen_movi_tl(cpu_pc, ctx->pc_succ_insn); - gen_helper_wfi(cpu_env); - break; - case 0x104: /* SFENCE.VM */ - gen_helper_tlb_flush(cpu_env); - break; -#endif - default: - gen_exception_illegal(ctx); - break; - } - break; - default: - tcg_gen_movi_tl(imm_rs1, rs1); - gen_io_start(); - switch (opc) { - case OPC_RISC_CSRRW: - gen_helper_csrrw(dest, cpu_env, source1, csr_store); - break; - case OPC_RISC_CSRRS: - gen_helper_csrrs(dest, cpu_env, source1, csr_store, rs1_pass); - break; - case OPC_RISC_CSRRC: - gen_helper_csrrc(dest, cpu_env, source1, csr_store, rs1_pass); - break; - case OPC_RISC_CSRRWI: - gen_helper_csrrw(dest, cpu_env, imm_rs1, csr_store); - break; - case OPC_RISC_CSRRSI: - gen_helper_csrrs(dest, cpu_env, imm_rs1, csr_store, rs1_pass); - break; - case OPC_RISC_CSRRCI: - gen_helper_csrrc(dest, cpu_env, imm_rs1, csr_store, rs1_pass); - break; - default: - gen_exception_illegal(ctx); - return; + dest = dest_gpr(ctx, a->rd); + src1 = get_gpr(ctx, a->rs1, ext); + + if (max_len < 128) { + func(dest, src1, a->shamt); + gen_set_gpr(ctx, a->rd, dest); + } else { + TCGv src1h = get_gprh(ctx, a->rs1); + TCGv desth = dest_gprh(ctx, a->rd); + + if (f128 == NULL) { + return false; } - gen_io_end(); - gen_set_gpr(rd, dest); - /* end tb since we may be changing priv modes, to get mmu_index right */ - tcg_gen_movi_tl(cpu_pc, ctx->pc_succ_insn); - tcg_gen_exit_tb(NULL, 0); /* no chaining */ - ctx->base.is_jmp = DISAS_NORETURN; - break; + f128(dest, desth, src1, src1h, a->shamt); + gen_set_gpr128(ctx, a->rd, dest, desth); } - tcg_temp_free(source1); - tcg_temp_free(csr_store); - tcg_temp_free(dest); - tcg_temp_free(rs1_pass); - tcg_temp_free(imm_rs1); + return true; } -static void decode_RV32_64C0(DisasContext *ctx) +static bool gen_shift_imm_fn_per_ol(DisasContext *ctx, arg_shift *a, + DisasExtend ext, + void (*f_tl)(TCGv, TCGv, target_long), + void (*f_32)(TCGv, TCGv, target_long), + void (*f_128)(TCGv, TCGv, TCGv, TCGv, + target_long)) { - uint8_t funct3 = extract32(ctx->opcode, 13, 3); - uint8_t rd_rs2 = GET_C_RS2S(ctx->opcode); - uint8_t rs1s = GET_C_RS1S(ctx->opcode); - - switch (funct3) { - case 0: - /* illegal */ - if (ctx->opcode == 0) { - gen_exception_illegal(ctx); - } else { - /* C.ADDI4SPN -> addi rd', x2, zimm[9:2]*/ - gen_arith_imm(ctx, OPC_RISC_ADDI, rd_rs2, 2, - GET_C_ADDI4SPN_IMM(ctx->opcode)); + int olen = get_olen(ctx); + if (olen != TARGET_LONG_BITS) { + if (olen == 32) { + f_tl = f_32; + } else if (olen != 128) { + g_assert_not_reached(); } - break; - case 1: - /* C.FLD -> fld rd', offset[7:3](rs1')*/ - gen_fp_load(ctx, OPC_RISC_FLD, rd_rs2, rs1s, - GET_C_LD_IMM(ctx->opcode)); - /* C.LQ(RV128) */ - break; - case 2: - /* C.LW -> lw rd', offset[6:2](rs1') */ - gen_load(ctx, OPC_RISC_LW, rd_rs2, rs1s, - GET_C_LW_IMM(ctx->opcode)); - break; - case 3: -#if defined(TARGET_RISCV64) - /* C.LD(RV64/128) -> ld rd', offset[7:3](rs1')*/ - gen_load(ctx, OPC_RISC_LD, rd_rs2, rs1s, - GET_C_LD_IMM(ctx->opcode)); -#else - /* C.FLW (RV32) -> flw rd', offset[6:2](rs1')*/ - gen_fp_load(ctx, OPC_RISC_FLW, rd_rs2, rs1s, - GET_C_LW_IMM(ctx->opcode)); -#endif - break; - case 4: - /* reserved */ - gen_exception_illegal(ctx); - break; - case 5: - /* C.FSD(RV32/64) -> fsd rs2', offset[7:3](rs1') */ - gen_fp_store(ctx, OPC_RISC_FSD, rs1s, rd_rs2, - GET_C_LD_IMM(ctx->opcode)); - /* C.SQ (RV128) */ - break; - case 6: - /* C.SW -> sw rs2', offset[6:2](rs1')*/ - gen_store(ctx, OPC_RISC_SW, rs1s, rd_rs2, - GET_C_LW_IMM(ctx->opcode)); - break; - case 7: -#if defined(TARGET_RISCV64) - /* C.SD (RV64/128) -> sd rs2', offset[7:3](rs1')*/ - gen_store(ctx, OPC_RISC_SD, rs1s, rd_rs2, - GET_C_LD_IMM(ctx->opcode)); -#else - /* C.FSW (RV32) -> fsw rs2', offset[6:2](rs1')*/ - gen_fp_store(ctx, OPC_RISC_FSW, rs1s, rd_rs2, - GET_C_LW_IMM(ctx->opcode)); -#endif - break; } + return gen_shift_imm_fn(ctx, a, ext, f_tl, f_128); } -static void decode_RV32_64C1(CPURISCVState *env, DisasContext *ctx) +static bool gen_shift_imm_tl(DisasContext *ctx, arg_shift *a, DisasExtend ext, + void (*func)(TCGv, TCGv, TCGv)) { - uint8_t funct3 = extract32(ctx->opcode, 13, 3); - uint8_t rd_rs1 = GET_C_RS1(ctx->opcode); - uint8_t rs1s, rs2s; - uint8_t funct2; + TCGv dest, src1, src2; + int max_len = get_olen(ctx); - switch (funct3) { - case 0: - /* C.ADDI -> addi rd, rd, nzimm[5:0] */ - gen_arith_imm(ctx, OPC_RISC_ADDI, rd_rs1, rd_rs1, - GET_C_IMM(ctx->opcode)); - break; - case 1: -#if defined(TARGET_RISCV64) - /* C.ADDIW (RV64/128) -> addiw rd, rd, imm[5:0]*/ - gen_arith_imm(ctx, OPC_RISC_ADDIW, rd_rs1, rd_rs1, - GET_C_IMM(ctx->opcode)); -#else - /* C.JAL(RV32) -> jal x1, offset[11:1] */ - gen_jal(env, ctx, 1, GET_C_J_IMM(ctx->opcode)); -#endif - break; - case 2: - /* C.LI -> addi rd, x0, imm[5:0]*/ - gen_arith_imm(ctx, OPC_RISC_ADDI, rd_rs1, 0, GET_C_IMM(ctx->opcode)); - break; - case 3: - if (rd_rs1 == 2) { - /* C.ADDI16SP -> addi x2, x2, nzimm[9:4]*/ - gen_arith_imm(ctx, OPC_RISC_ADDI, 2, 2, - GET_C_ADDI16SP_IMM(ctx->opcode)); - } else if (rd_rs1 != 0) { - /* C.LUI (rs1/rd =/= {0,2}) -> lui rd, nzimm[17:12]*/ - tcg_gen_movi_tl(cpu_gpr[rd_rs1], - GET_C_IMM(ctx->opcode) << 12); - } - break; - case 4: - funct2 = extract32(ctx->opcode, 10, 2); - rs1s = GET_C_RS1S(ctx->opcode); - switch (funct2) { - case 0: /* C.SRLI(RV32) -> srli rd', rd', shamt[5:0] */ - gen_arith_imm(ctx, OPC_RISC_SHIFT_RIGHT_I, rs1s, rs1s, - GET_C_ZIMM(ctx->opcode)); - /* C.SRLI64(RV128) */ - break; - case 1: - /* C.SRAI -> srai rd', rd', shamt[5:0]*/ - gen_arith_imm(ctx, OPC_RISC_SHIFT_RIGHT_I, rs1s, rs1s, - GET_C_ZIMM(ctx->opcode) | 0x400); - /* C.SRAI64(RV128) */ - break; - case 2: - /* C.ANDI -> andi rd', rd', imm[5:0]*/ - gen_arith_imm(ctx, OPC_RISC_ANDI, rs1s, rs1s, - GET_C_IMM(ctx->opcode)); - break; - case 3: - funct2 = extract32(ctx->opcode, 5, 2); - rs2s = GET_C_RS2S(ctx->opcode); - switch (funct2) { - case 0: - /* C.SUB -> sub rd', rd', rs2' */ - if (extract32(ctx->opcode, 12, 1) == 0) { - gen_arith(ctx, OPC_RISC_SUB, rs1s, rs1s, rs2s); - } -#if defined(TARGET_RISCV64) - else { - gen_arith(ctx, OPC_RISC_SUBW, rs1s, rs1s, rs2s); - } -#endif - break; - case 1: - /* C.XOR -> xor rs1', rs1', rs2' */ - if (extract32(ctx->opcode, 12, 1) == 0) { - gen_arith(ctx, OPC_RISC_XOR, rs1s, rs1s, rs2s); - } -#if defined(TARGET_RISCV64) - else { - /* C.ADDW (RV64/128) */ - gen_arith(ctx, OPC_RISC_ADDW, rs1s, rs1s, rs2s); - } -#endif - break; - case 2: - /* C.OR -> or rs1', rs1', rs2' */ - gen_arith(ctx, OPC_RISC_OR, rs1s, rs1s, rs2s); - break; - case 3: - /* C.AND -> and rs1', rs1', rs2' */ - gen_arith(ctx, OPC_RISC_AND, rs1s, rs1s, rs2s); - break; - } - break; - } - break; - case 5: - /* C.J -> jal x0, offset[11:1]*/ - gen_jal(env, ctx, 0, GET_C_J_IMM(ctx->opcode)); - break; - case 6: - /* C.BEQZ -> beq rs1', x0, offset[8:1]*/ - rs1s = GET_C_RS1S(ctx->opcode); - gen_branch(env, ctx, OPC_RISC_BEQ, rs1s, 0, GET_C_B_IMM(ctx->opcode)); - break; - case 7: - /* C.BNEZ -> bne rs1', x0, offset[8:1]*/ - rs1s = GET_C_RS1S(ctx->opcode); - gen_branch(env, ctx, OPC_RISC_BNE, rs1s, 0, GET_C_B_IMM(ctx->opcode)); - break; + if (a->shamt >= max_len) { + return false; } + + dest = dest_gpr(ctx, a->rd); + src1 = get_gpr(ctx, a->rs1, ext); + src2 = tcg_constant_tl(a->shamt); + + func(dest, src1, src2); + + gen_set_gpr(ctx, a->rd, dest); + return true; } -static void decode_RV32_64C2(CPURISCVState *env, DisasContext *ctx) +static bool gen_shift(DisasContext *ctx, arg_r *a, DisasExtend ext, + void (*func)(TCGv, TCGv, TCGv), + void (*f128)(TCGv, TCGv, TCGv, TCGv, TCGv)) { - uint8_t rd, rs2; - uint8_t funct3 = extract32(ctx->opcode, 13, 3); + TCGv src2 = get_gpr(ctx, a->rs2, EXT_NONE); + TCGv ext2 = tcg_temp_new(); + int max_len = get_olen(ctx); + tcg_gen_andi_tl(ext2, src2, max_len - 1); - rd = GET_RD(ctx->opcode); + TCGv dest = dest_gpr(ctx, a->rd); + TCGv src1 = get_gpr(ctx, a->rs1, ext); - switch (funct3) { - case 0: /* C.SLLI -> slli rd, rd, shamt[5:0] - C.SLLI64 -> */ - gen_arith_imm(ctx, OPC_RISC_SLLI, rd, rd, GET_C_ZIMM(ctx->opcode)); - break; - case 1: /* C.FLDSP(RV32/64DC) -> fld rd, offset[8:3](x2) */ - gen_fp_load(ctx, OPC_RISC_FLD, rd, 2, GET_C_LDSP_IMM(ctx->opcode)); - break; - case 2: /* C.LWSP -> lw rd, offset[7:2](x2) */ - gen_load(ctx, OPC_RISC_LW, rd, 2, GET_C_LWSP_IMM(ctx->opcode)); - break; - case 3: -#if defined(TARGET_RISCV64) - /* C.LDSP(RVC64) -> ld rd, offset[8:3](x2) */ - gen_load(ctx, OPC_RISC_LD, rd, 2, GET_C_LDSP_IMM(ctx->opcode)); -#else - /* C.FLWSP(RV32FC) -> flw rd, offset[7:2](x2) */ - gen_fp_load(ctx, OPC_RISC_FLW, rd, 2, GET_C_LWSP_IMM(ctx->opcode)); -#endif - break; - case 4: - rs2 = GET_C_RS2(ctx->opcode); - - if (extract32(ctx->opcode, 12, 1) == 0) { - if (rs2 == 0) { - /* C.JR -> jalr x0, rs1, 0*/ - gen_jalr(env, ctx, OPC_RISC_JALR, 0, rd, 0); - } else { - /* C.MV -> add rd, x0, rs2 */ - gen_arith(ctx, OPC_RISC_ADD, rd, 0, rs2); - } - } else { - if (rd == 0) { - /* C.EBREAK -> ebreak*/ - gen_system(env, ctx, OPC_RISC_ECALL, 0, 0, 0x1); - } else { - if (rs2 == 0) { - /* C.JALR -> jalr x1, rs1, 0*/ - gen_jalr(env, ctx, OPC_RISC_JALR, 1, rd, 0); - } else { - /* C.ADD -> add rd, rd, rs2 */ - gen_arith(ctx, OPC_RISC_ADD, rd, rd, rs2); - } - } + if (max_len < 128) { + func(dest, src1, ext2); + gen_set_gpr(ctx, a->rd, dest); + } else { + TCGv src1h = get_gprh(ctx, a->rs1); + TCGv desth = dest_gprh(ctx, a->rd); + + if (f128 == NULL) { + return false; } - break; - case 5: - /* C.FSDSP -> fsd rs2, offset[8:3](x2)*/ - gen_fp_store(ctx, OPC_RISC_FSD, 2, GET_C_RS2(ctx->opcode), - GET_C_SDSP_IMM(ctx->opcode)); - /* C.SQSP */ - break; - case 6: /* C.SWSP -> sw rs2, offset[7:2](x2)*/ - gen_store(ctx, OPC_RISC_SW, 2, GET_C_RS2(ctx->opcode), - GET_C_SWSP_IMM(ctx->opcode)); - break; - case 7: -#if defined(TARGET_RISCV64) - /* C.SDSP(Rv64/128) -> sd rs2, offset[8:3](x2)*/ - gen_store(ctx, OPC_RISC_SD, 2, GET_C_RS2(ctx->opcode), - GET_C_SDSP_IMM(ctx->opcode)); -#else - /* C.FSWSP(RV32) -> fsw rs2, offset[7:2](x2) */ - gen_fp_store(ctx, OPC_RISC_FSW, 2, GET_C_RS2(ctx->opcode), - GET_C_SWSP_IMM(ctx->opcode)); -#endif - break; + f128(dest, desth, src1, src1h, ext2); + gen_set_gpr128(ctx, a->rd, dest, desth); } + return true; } -static void decode_RV32_64C(CPURISCVState *env, DisasContext *ctx) +static bool gen_shift_per_ol(DisasContext *ctx, arg_r *a, DisasExtend ext, + void (*f_tl)(TCGv, TCGv, TCGv), + void (*f_32)(TCGv, TCGv, TCGv), + void (*f_128)(TCGv, TCGv, TCGv, TCGv, TCGv)) { - uint8_t op = extract32(ctx->opcode, 0, 2); - - switch (op) { - case 0: - decode_RV32_64C0(ctx); - break; - case 1: - decode_RV32_64C1(env, ctx); - break; - case 2: - decode_RV32_64C2(env, ctx); - break; + int olen = get_olen(ctx); + if (olen != TARGET_LONG_BITS) { + if (olen == 32) { + f_tl = f_32; + } else if (olen != 128) { + g_assert_not_reached(); + } } + return gen_shift(ctx, a, ext, f_tl, f_128); } -static void decode_RV32_64G(CPURISCVState *env, DisasContext *ctx) +static bool gen_unary(DisasContext *ctx, arg_r2 *a, DisasExtend ext, + void (*func)(TCGv, TCGv)) { - int rs1; - int rs2; - int rd; - uint32_t op; - target_long imm; + TCGv dest = dest_gpr(ctx, a->rd); + TCGv src1 = get_gpr(ctx, a->rs1, ext); - /* We do not do misaligned address check here: the address should never be - * misaligned at this point. Instructions that set PC must do the check, - * since epc must be the address of the instruction that caused us to - * perform the misaligned instruction fetch */ + func(dest, src1); - op = MASK_OP_MAJOR(ctx->opcode); - rs1 = GET_RS1(ctx->opcode); - rs2 = GET_RS2(ctx->opcode); - rd = GET_RD(ctx->opcode); - imm = GET_IMM(ctx->opcode); + gen_set_gpr(ctx, a->rd, dest); + return true; +} - switch (op) { - case OPC_RISC_LUI: - if (rd == 0) { - break; /* NOP */ - } - tcg_gen_movi_tl(cpu_gpr[rd], sextract64(ctx->opcode, 12, 20) << 12); - break; - case OPC_RISC_AUIPC: - if (rd == 0) { - break; /* NOP */ - } - tcg_gen_movi_tl(cpu_gpr[rd], (sextract64(ctx->opcode, 12, 20) << 12) + - ctx->base.pc_next); - break; - case OPC_RISC_JAL: - imm = GET_JAL_IMM(ctx->opcode); - gen_jal(env, ctx, rd, imm); - break; - case OPC_RISC_JALR: - gen_jalr(env, ctx, MASK_OP_JALR(ctx->opcode), rd, rs1, imm); - break; - case OPC_RISC_BRANCH: - gen_branch(env, ctx, MASK_OP_BRANCH(ctx->opcode), rs1, rs2, - GET_B_IMM(ctx->opcode)); - break; - case OPC_RISC_LOAD: - gen_load(ctx, MASK_OP_LOAD(ctx->opcode), rd, rs1, imm); - break; - case OPC_RISC_STORE: - gen_store(ctx, MASK_OP_STORE(ctx->opcode), rs1, rs2, - GET_STORE_IMM(ctx->opcode)); - break; - case OPC_RISC_ARITH_IMM: -#if defined(TARGET_RISCV64) - case OPC_RISC_ARITH_IMM_W: -#endif - if (rd == 0) { - break; /* NOP */ - } - gen_arith_imm(ctx, MASK_OP_ARITH_IMM(ctx->opcode), rd, rs1, imm); - break; - case OPC_RISC_ARITH: -#if defined(TARGET_RISCV64) - case OPC_RISC_ARITH_W: -#endif - if (rd == 0) { - break; /* NOP */ - } - gen_arith(ctx, MASK_OP_ARITH(ctx->opcode), rd, rs1, rs2); - break; - case OPC_RISC_FP_LOAD: - gen_fp_load(ctx, MASK_OP_FP_LOAD(ctx->opcode), rd, rs1, imm); - break; - case OPC_RISC_FP_STORE: - gen_fp_store(ctx, MASK_OP_FP_STORE(ctx->opcode), rs1, rs2, - GET_STORE_IMM(ctx->opcode)); - break; - case OPC_RISC_ATOMIC: - gen_atomic(ctx, MASK_OP_ATOMIC(ctx->opcode), rd, rs1, rs2); - break; - case OPC_RISC_FMADD: - gen_fp_fmadd(ctx, MASK_OP_FP_FMADD(ctx->opcode), rd, rs1, rs2, - GET_RS3(ctx->opcode), GET_RM(ctx->opcode)); - break; - case OPC_RISC_FMSUB: - gen_fp_fmsub(ctx, MASK_OP_FP_FMSUB(ctx->opcode), rd, rs1, rs2, - GET_RS3(ctx->opcode), GET_RM(ctx->opcode)); - break; - case OPC_RISC_FNMSUB: - gen_fp_fnmsub(ctx, MASK_OP_FP_FNMSUB(ctx->opcode), rd, rs1, rs2, - GET_RS3(ctx->opcode), GET_RM(ctx->opcode)); - break; - case OPC_RISC_FNMADD: - gen_fp_fnmadd(ctx, MASK_OP_FP_FNMADD(ctx->opcode), rd, rs1, rs2, - GET_RS3(ctx->opcode), GET_RM(ctx->opcode)); - break; - case OPC_RISC_FP_ARITH: - gen_fp_arith(ctx, MASK_OP_FP_ARITH(ctx->opcode), rd, rs1, rs2, - GET_RM(ctx->opcode)); - break; - case OPC_RISC_FENCE: -#ifndef CONFIG_USER_ONLY - if (ctx->opcode & 0x1000) { - /* FENCE_I is a no-op in QEMU, - * however we need to end the translation block */ - tcg_gen_movi_tl(cpu_pc, ctx->pc_succ_insn); - tcg_gen_exit_tb(NULL, 0); - ctx->base.is_jmp = DISAS_NORETURN; +static bool gen_unary_per_ol(DisasContext *ctx, arg_r2 *a, DisasExtend ext, + void (*f_tl)(TCGv, TCGv), + void (*f_32)(TCGv, TCGv)) +{ + int olen = get_olen(ctx); + + if (olen != TARGET_LONG_BITS) { + if (olen == 32) { + f_tl = f_32; } else { - /* FENCE is a full memory barrier. */ - tcg_gen_mb(TCG_MO_ALL | TCG_BAR_SC); + g_assert_not_reached(); } -#endif - break; - case OPC_RISC_SYSTEM: - gen_system(env, ctx, MASK_OP_SYSTEM(ctx->opcode), rd, rs1, - (ctx->opcode & 0xFFF00000) >> 20); - break; - default: - gen_exception_illegal(ctx); - break; } + return gen_unary(ctx, a, ext, f_tl); } -static void decode_opc(CPURISCVState *env, DisasContext *ctx) +static uint32_t opcode_at(DisasContextBase *dcbase, target_ulong pc) { - /* check for compressed insn */ - if (extract32(ctx->opcode, 0, 2) != 3) { - if (!riscv_has_ext(env, RVC)) { - gen_exception_illegal(ctx); - } else { - ctx->pc_succ_insn = ctx->base.pc_next + 2; - decode_RV32_64C(env, ctx); + DisasContext *ctx = container_of(dcbase, DisasContext, base); + CPUState *cpu = ctx->cs; + CPURISCVState *env = cpu_env(cpu); + + return cpu_ldl_code(env, pc); +} + +/* Include insn module translation function */ +#include "insn_trans/trans_rvi.c.inc" +#include "insn_trans/trans_rvm.c.inc" +#include "insn_trans/trans_rva.c.inc" +#include "insn_trans/trans_rvf.c.inc" +#include "insn_trans/trans_rvd.c.inc" +#include "insn_trans/trans_rvh.c.inc" +#include "insn_trans/trans_rvv.c.inc" +#include "insn_trans/trans_rvb.c.inc" +#include "insn_trans/trans_rvzicond.c.inc" +#include "insn_trans/trans_rvzacas.c.inc" +#include "insn_trans/trans_rvzawrs.c.inc" +#include "insn_trans/trans_rvzicbo.c.inc" +#include "insn_trans/trans_rvzfa.c.inc" +#include "insn_trans/trans_rvzfh.c.inc" +#include "insn_trans/trans_rvk.c.inc" +#include "insn_trans/trans_rvvk.c.inc" +#include "insn_trans/trans_privileged.c.inc" +#include "insn_trans/trans_svinval.c.inc" +#include "insn_trans/trans_rvbf16.c.inc" +#include "decode-xthead.c.inc" +#include "insn_trans/trans_xthead.c.inc" +#include "insn_trans/trans_xventanacondops.c.inc" + +/* Include the auto-generated decoder for 16 bit insn */ +#include "decode-insn16.c.inc" +#include "insn_trans/trans_rvzce.c.inc" + +/* Include decoders for factored-out extensions */ +#include "decode-XVentanaCondOps.c.inc" + +/* The specification allows for longer insns, but not supported by qemu. */ +#define MAX_INSN_LEN 4 + +static inline int insn_len(uint16_t first_word) +{ + return (first_word & 3) == 3 ? 4 : 2; +} + +static void decode_opc(CPURISCVState *env, DisasContext *ctx, uint16_t opcode) +{ + /* + * A table with predicate (i.e., guard) functions and decoder functions + * that are tested in-order until a decoder matches onto the opcode. + */ + static const struct { + bool (*guard_func)(const RISCVCPUConfig *); + bool (*decode_func)(DisasContext *, uint32_t); + } decoders[] = { + { always_true_p, decode_insn32 }, + { has_xthead_p, decode_xthead }, + { has_XVentanaCondOps_p, decode_XVentanaCodeOps }, + }; + + ctx->virt_inst_excp = false; + ctx->cur_insn_len = insn_len(opcode); + /* Check for compressed insn */ + if (ctx->cur_insn_len == 2) { + ctx->opcode = opcode; + /* + * The Zca extension is added as way to refer to instructions in the C + * extension that do not include the floating-point loads and stores + */ + if ((has_ext(ctx, RVC) || ctx->cfg_ptr->ext_zca) && + decode_insn16(ctx, opcode)) { + return; } } else { - ctx->pc_succ_insn = ctx->base.pc_next + 4; - decode_RV32_64G(env, ctx); + uint32_t opcode32 = opcode; + opcode32 = deposit32(opcode32, 16, 16, + translator_lduw(env, &ctx->base, + ctx->base.pc_next + 2)); + ctx->opcode = opcode32; + + for (size_t i = 0; i < ARRAY_SIZE(decoders); ++i) { + if (decoders[i].guard_func(ctx->cfg_ptr) && + decoders[i].decode_func(ctx, opcode32)) { + return; + } + } } + + gen_exception_illegal(ctx); } static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) { DisasContext *ctx = container_of(dcbase, DisasContext, base); - - ctx->pc_succ_insn = ctx->base.pc_first; - ctx->flags = ctx->base.tb->flags; - ctx->mem_idx = ctx->base.tb->flags & TB_FLAGS_MMU_MASK; + CPURISCVState *env = cpu_env(cs); + RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(cs); + RISCVCPU *cpu = RISCV_CPU(cs); + uint32_t tb_flags = ctx->base.tb->flags; + + ctx->pc_save = ctx->base.pc_first; + ctx->priv = FIELD_EX32(tb_flags, TB_FLAGS, PRIV); + ctx->mem_idx = FIELD_EX32(tb_flags, TB_FLAGS, MEM_IDX); + ctx->mstatus_fs = FIELD_EX32(tb_flags, TB_FLAGS, FS); + ctx->mstatus_vs = FIELD_EX32(tb_flags, TB_FLAGS, VS); + ctx->priv_ver = env->priv_ver; + ctx->virt_enabled = FIELD_EX32(tb_flags, TB_FLAGS, VIRT_ENABLED); + ctx->misa_ext = env->misa_ext; ctx->frm = -1; /* unknown rounding mode */ + ctx->cfg_ptr = &(cpu->cfg); + ctx->vill = FIELD_EX32(tb_flags, TB_FLAGS, VILL); + ctx->sew = FIELD_EX32(tb_flags, TB_FLAGS, SEW); + ctx->lmul = sextract32(FIELD_EX32(tb_flags, TB_FLAGS, LMUL), 0, 3); + ctx->vta = FIELD_EX32(tb_flags, TB_FLAGS, VTA) && cpu->cfg.rvv_ta_all_1s; + ctx->vma = FIELD_EX32(tb_flags, TB_FLAGS, VMA) && cpu->cfg.rvv_ma_all_1s; + ctx->cfg_vta_all_1s = cpu->cfg.rvv_ta_all_1s; + ctx->vstart_eq_zero = FIELD_EX32(tb_flags, TB_FLAGS, VSTART_EQ_ZERO); + ctx->vl_eq_vlmax = FIELD_EX32(tb_flags, TB_FLAGS, VL_EQ_VLMAX); + ctx->misa_mxl_max = mcc->misa_mxl_max; + ctx->xl = FIELD_EX32(tb_flags, TB_FLAGS, XL); + ctx->address_xl = FIELD_EX32(tb_flags, TB_FLAGS, AXL); + ctx->cs = cs; + ctx->pm_mask_enabled = FIELD_EX32(tb_flags, TB_FLAGS, PM_MASK_ENABLED); + ctx->pm_base_enabled = FIELD_EX32(tb_flags, TB_FLAGS, PM_BASE_ENABLED); + ctx->ztso = cpu->cfg.ext_ztso; + ctx->itrigger = FIELD_EX32(tb_flags, TB_FLAGS, ITRIGGER); + ctx->zero = tcg_constant_tl(0); + ctx->virt_inst_excp = false; } static void riscv_tr_tb_start(DisasContextBase *db, CPUState *cpu) @@ -1822,42 +1216,41 @@ static void riscv_tr_tb_start(DisasContextBase *db, CPUState *cpu) static void riscv_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu) { DisasContext *ctx = container_of(dcbase, DisasContext, base); + target_ulong pc_next = ctx->base.pc_next; - tcg_gen_insn_start(ctx->base.pc_next); -} - -static bool riscv_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cpu, - const CPUBreakpoint *bp) -{ - DisasContext *ctx = container_of(dcbase, DisasContext, base); + if (tb_cflags(dcbase->tb) & CF_PCREL) { + pc_next &= ~TARGET_PAGE_MASK; + } - tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next); - ctx->base.is_jmp = DISAS_NORETURN; - gen_exception_debug(); - /* The address covered by the breakpoint must be included in - [tb->pc, tb->pc + tb->size) in order to for it to be - properly cleared -- thus we increment the PC here so that - the logic setting tb->size below does the right thing. */ - ctx->base.pc_next += 4; - return true; + tcg_gen_insn_start(pc_next, 0); + ctx->insn_start_updated = false; } - static void riscv_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) { DisasContext *ctx = container_of(dcbase, DisasContext, base); - CPURISCVState *env = cpu->env_ptr; + CPURISCVState *env = cpu_env(cpu); + uint16_t opcode16 = translator_lduw(env, &ctx->base, ctx->base.pc_next); - ctx->opcode = cpu_ldl_code(env, ctx->base.pc_next); - decode_opc(env, ctx); - ctx->base.pc_next = ctx->pc_succ_insn; + ctx->ol = ctx->xl; + decode_opc(env, ctx, opcode16); + ctx->base.pc_next += ctx->cur_insn_len; + /* Only the first insn within a TB is allowed to cross a page boundary. */ if (ctx->base.is_jmp == DISAS_NEXT) { - target_ulong page_start; - - page_start = ctx->base.pc_first & TARGET_PAGE_MASK; - if (ctx->base.pc_next - page_start >= TARGET_PAGE_SIZE) { + if (ctx->itrigger || !is_same_page(&ctx->base, ctx->base.pc_next)) { ctx->base.is_jmp = DISAS_TOO_MANY; + } else { + unsigned page_ofs = ctx->base.pc_next & ~TARGET_PAGE_MASK; + + if (page_ofs > TARGET_PAGE_SIZE - MAX_INSN_LEN) { + uint16_t next_insn = cpu_lduw_code(env, ctx->base.pc_next); + int len = insn_len(next_insn); + + if (!is_same_page(&ctx->base, ctx->base.pc_next + len - 1)) { + ctx->base.is_jmp = DISAS_TOO_MANY; + } + } } } } @@ -1868,7 +1261,7 @@ static void riscv_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) switch (ctx->base.is_jmp) { case DISAS_TOO_MANY: - gen_goto_tb(ctx, 0, ctx->base.pc_next); + gen_goto_tb(ctx, 0, 0); break; case DISAS_NORETURN: break; @@ -1877,51 +1270,74 @@ static void riscv_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) } } -static void riscv_tr_disas_log(const DisasContextBase *dcbase, CPUState *cpu) +static void riscv_tr_disas_log(const DisasContextBase *dcbase, + CPUState *cpu, FILE *logfile) { - qemu_log("IN: %s\n", lookup_symbol(dcbase->pc_first)); - log_target_disas(cpu, dcbase->pc_first, dcbase->tb->size); +#ifndef CONFIG_USER_ONLY + RISCVCPU *rvcpu = RISCV_CPU(cpu); + CPURISCVState *env = &rvcpu->env; +#endif + + fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first)); +#ifndef CONFIG_USER_ONLY + fprintf(logfile, "Priv: "TARGET_FMT_ld"; Virt: %d\n", + env->priv, env->virt_enabled); +#endif + target_disas(logfile, cpu, dcbase->pc_first, dcbase->tb->size); } static const TranslatorOps riscv_tr_ops = { .init_disas_context = riscv_tr_init_disas_context, .tb_start = riscv_tr_tb_start, .insn_start = riscv_tr_insn_start, - .breakpoint_check = riscv_tr_breakpoint_check, .translate_insn = riscv_tr_translate_insn, .tb_stop = riscv_tr_tb_stop, .disas_log = riscv_tr_disas_log, }; -void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) +void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, + vaddr pc, void *host_pc) { DisasContext ctx; - translator_loop(&riscv_tr_ops, &ctx.base, cs, tb); + translator_loop(cs, tb, max_insns, pc, host_pc, &riscv_tr_ops, &ctx.base); } void riscv_translate_init(void) { int i; - /* cpu_gpr[0] is a placeholder for the zero register. Do not use it. */ - /* Use the gen_set_gpr and gen_get_gpr helper functions when accessing */ - /* registers, unless you specifically block reads/writes to reg 0 */ + /* + * cpu_gpr[0] is a placeholder for the zero register. Do not use it. + * Use the gen_set_gpr and get_gpr helper functions when accessing regs, + * unless you specifically block reads/writes to reg 0. + */ cpu_gpr[0] = NULL; + cpu_gprh[0] = NULL; for (i = 1; i < 32; i++) { - cpu_gpr[i] = tcg_global_mem_new(cpu_env, + cpu_gpr[i] = tcg_global_mem_new(tcg_env, offsetof(CPURISCVState, gpr[i]), riscv_int_regnames[i]); + cpu_gprh[i] = tcg_global_mem_new(tcg_env, + offsetof(CPURISCVState, gprh[i]), riscv_int_regnamesh[i]); } for (i = 0; i < 32; i++) { - cpu_fpr[i] = tcg_global_mem_new_i64(cpu_env, + cpu_fpr[i] = tcg_global_mem_new_i64(tcg_env, offsetof(CPURISCVState, fpr[i]), riscv_fpr_regnames[i]); } - cpu_pc = tcg_global_mem_new(cpu_env, offsetof(CPURISCVState, pc), "pc"); - load_res = tcg_global_mem_new(cpu_env, offsetof(CPURISCVState, load_res), + cpu_pc = tcg_global_mem_new(tcg_env, offsetof(CPURISCVState, pc), "pc"); + cpu_vl = tcg_global_mem_new(tcg_env, offsetof(CPURISCVState, vl), "vl"); + cpu_vstart = tcg_global_mem_new(tcg_env, offsetof(CPURISCVState, vstart), + "vstart"); + load_res = tcg_global_mem_new(tcg_env, offsetof(CPURISCVState, load_res), "load_res"); - load_val = tcg_global_mem_new(cpu_env, offsetof(CPURISCVState, load_val), + load_val = tcg_global_mem_new(tcg_env, offsetof(CPURISCVState, load_val), "load_val"); + /* Assign PM CSRs to tcg globals */ + pm_mask = tcg_global_mem_new(tcg_env, offsetof(CPURISCVState, cur_pmmask), + "pmmask"); + pm_base = tcg_global_mem_new(tcg_env, offsetof(CPURISCVState, cur_pmbase), + "pmbase"); } |