| /* |
| * RISC-V translation routines for the Zc[b,mp,mt] Standard Extensions. |
| * |
| * Copyright (c) 2021-2022 PLCT Lab |
| * |
| * This program is free software; you can redistribute it and/or modify it |
| * under the terms and conditions of the GNU General Public License, |
| * version 2 or later, as published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| * more details. |
| * |
| * You should have received a copy of the GNU General Public License along with |
| * this program. If not, see <http://www.gnu.org/licenses/>. |
| */ |
| |
| #define REQUIRE_ZCB(ctx) do { \ |
| if (!ctx->cfg_ptr->ext_zcb) \ |
| return false; \ |
| } while (0) |
| |
| #define REQUIRE_ZCMP(ctx) do { \ |
| if (!ctx->cfg_ptr->ext_zcmp) \ |
| return false; \ |
| } while (0) |
| |
| #define REQUIRE_ZCMT(ctx) do { \ |
| if (!ctx->cfg_ptr->ext_zcmt) \ |
| return false; \ |
| } while (0) |
| |
| static bool trans_c_zext_b(DisasContext *ctx, arg_c_zext_b *a) |
| { |
| REQUIRE_ZCB(ctx); |
| return gen_unary(ctx, a, EXT_NONE, tcg_gen_ext8u_tl); |
| } |
| |
| static bool trans_c_zext_h(DisasContext *ctx, arg_c_zext_h *a) |
| { |
| REQUIRE_ZCB(ctx); |
| REQUIRE_ZBB(ctx); |
| return gen_unary(ctx, a, EXT_NONE, tcg_gen_ext16u_tl); |
| } |
| |
| static bool trans_c_sext_b(DisasContext *ctx, arg_c_sext_b *a) |
| { |
| REQUIRE_ZCB(ctx); |
| REQUIRE_ZBB(ctx); |
| return gen_unary(ctx, a, EXT_NONE, tcg_gen_ext8s_tl); |
| } |
| |
| static bool trans_c_sext_h(DisasContext *ctx, arg_c_sext_h *a) |
| { |
| REQUIRE_ZCB(ctx); |
| REQUIRE_ZBB(ctx); |
| return gen_unary(ctx, a, EXT_NONE, tcg_gen_ext16s_tl); |
| } |
| |
| static bool trans_c_zext_w(DisasContext *ctx, arg_c_zext_w *a) |
| { |
| REQUIRE_64BIT(ctx); |
| REQUIRE_ZCB(ctx); |
| REQUIRE_ZBA(ctx); |
| return gen_unary(ctx, a, EXT_NONE, tcg_gen_ext32u_tl); |
| } |
| |
| static bool trans_c_not(DisasContext *ctx, arg_c_not *a) |
| { |
| REQUIRE_ZCB(ctx); |
| return gen_unary(ctx, a, EXT_NONE, tcg_gen_not_tl); |
| } |
| |
| static bool trans_c_mul(DisasContext *ctx, arg_c_mul *a) |
| { |
| REQUIRE_ZCB(ctx); |
| REQUIRE_M_OR_ZMMUL(ctx); |
| return gen_arith(ctx, a, EXT_NONE, tcg_gen_mul_tl, NULL); |
| } |
| |
| static bool trans_c_lbu(DisasContext *ctx, arg_c_lbu *a) |
| { |
| REQUIRE_ZCB(ctx); |
| return gen_load(ctx, a, MO_UB); |
| } |
| |
| static bool trans_c_lhu(DisasContext *ctx, arg_c_lhu *a) |
| { |
| REQUIRE_ZCB(ctx); |
| return gen_load(ctx, a, MO_UW); |
| } |
| |
| static bool trans_c_lh(DisasContext *ctx, arg_c_lh *a) |
| { |
| REQUIRE_ZCB(ctx); |
| return gen_load(ctx, a, MO_SW); |
| } |
| |
| static bool trans_c_sb(DisasContext *ctx, arg_c_sb *a) |
| { |
| REQUIRE_ZCB(ctx); |
| return gen_store(ctx, a, MO_UB); |
| } |
| |
| static bool trans_c_sh(DisasContext *ctx, arg_c_sh *a) |
| { |
| REQUIRE_ZCB(ctx); |
| return gen_store(ctx, a, MO_UW); |
| } |
| |
| #define X_S0 8 |
| #define X_S1 9 |
| #define X_Sn 16 |
| |
| static uint32_t decode_push_pop_list(DisasContext *ctx, target_ulong rlist) |
| { |
| uint32_t reg_bitmap = 0; |
| |
| if (has_ext(ctx, RVE) && rlist > 6) { |
| return 0; |
| } |
| |
| switch (rlist) { |
| case 15: |
| reg_bitmap |= 1 << (X_Sn + 11) ; |
| reg_bitmap |= 1 << (X_Sn + 10) ; |
| /* FALL THROUGH */ |
| case 14: |
| reg_bitmap |= 1 << (X_Sn + 9) ; |
| /* FALL THROUGH */ |
| case 13: |
| reg_bitmap |= 1 << (X_Sn + 8) ; |
| /* FALL THROUGH */ |
| case 12: |
| reg_bitmap |= 1 << (X_Sn + 7) ; |
| /* FALL THROUGH */ |
| case 11: |
| reg_bitmap |= 1 << (X_Sn + 6) ; |
| /* FALL THROUGH */ |
| case 10: |
| reg_bitmap |= 1 << (X_Sn + 5) ; |
| /* FALL THROUGH */ |
| case 9: |
| reg_bitmap |= 1 << (X_Sn + 4) ; |
| /* FALL THROUGH */ |
| case 8: |
| reg_bitmap |= 1 << (X_Sn + 3) ; |
| /* FALL THROUGH */ |
| case 7: |
| reg_bitmap |= 1 << (X_Sn + 2) ; |
| /* FALL THROUGH */ |
| case 6: |
| reg_bitmap |= 1 << X_S1 ; |
| /* FALL THROUGH */ |
| case 5: |
| reg_bitmap |= 1 << X_S0; |
| /* FALL THROUGH */ |
| case 4: |
| reg_bitmap |= 1 << xRA; |
| break; |
| default: |
| break; |
| } |
| |
| return reg_bitmap; |
| } |
| |
| static bool gen_pop(DisasContext *ctx, arg_cmpp *a, bool ret, bool ret_val) |
| { |
| REQUIRE_ZCMP(ctx); |
| |
| uint32_t reg_bitmap = decode_push_pop_list(ctx, a->urlist); |
| if (reg_bitmap == 0) { |
| return false; |
| } |
| |
| MemOp memop = get_ol(ctx) == MXL_RV32 ? MO_TEUL : MO_TEUQ; |
| int reg_size = memop_size(memop); |
| target_ulong stack_adj = ROUND_UP(ctpop32(reg_bitmap) * reg_size, 16) + |
| a->spimm; |
| TCGv sp = dest_gpr(ctx, xSP); |
| TCGv addr = tcg_temp_new(); |
| int i; |
| |
| tcg_gen_addi_tl(addr, sp, stack_adj - reg_size); |
| |
| for (i = X_Sn + 11; i >= 0; i--) { |
| if (reg_bitmap & (1 << i)) { |
| TCGv dest = dest_gpr(ctx, i); |
| tcg_gen_qemu_ld_tl(dest, addr, ctx->mem_idx, memop); |
| gen_set_gpr(ctx, i, dest); |
| tcg_gen_subi_tl(addr, addr, reg_size); |
| } |
| } |
| |
| tcg_gen_addi_tl(sp, sp, stack_adj); |
| gen_set_gpr(ctx, xSP, sp); |
| |
| if (ret_val) { |
| gen_set_gpr(ctx, xA0, ctx->zero); |
| } |
| |
| if (ret) { |
| TCGv ret_addr = get_gpr(ctx, xRA, EXT_SIGN); |
| tcg_gen_mov_tl(cpu_pc, ret_addr); |
| tcg_gen_lookup_and_goto_ptr(); |
| ctx->base.is_jmp = DISAS_NORETURN; |
| } |
| |
| return true; |
| } |
| |
| static bool trans_cm_push(DisasContext *ctx, arg_cm_push *a) |
| { |
| REQUIRE_ZCMP(ctx); |
| |
| uint32_t reg_bitmap = decode_push_pop_list(ctx, a->urlist); |
| if (reg_bitmap == 0) { |
| return false; |
| } |
| |
| MemOp memop = get_ol(ctx) == MXL_RV32 ? MO_TEUL : MO_TEUQ; |
| int reg_size = memop_size(memop); |
| target_ulong stack_adj = ROUND_UP(ctpop32(reg_bitmap) * reg_size, 16) + |
| a->spimm; |
| TCGv sp = dest_gpr(ctx, xSP); |
| TCGv addr = tcg_temp_new(); |
| int i; |
| |
| tcg_gen_subi_tl(addr, sp, reg_size); |
| |
| for (i = X_Sn + 11; i >= 0; i--) { |
| if (reg_bitmap & (1 << i)) { |
| TCGv val = get_gpr(ctx, i, EXT_NONE); |
| tcg_gen_qemu_st_tl(val, addr, ctx->mem_idx, memop); |
| tcg_gen_subi_tl(addr, addr, reg_size); |
| } |
| } |
| |
| tcg_gen_subi_tl(sp, sp, stack_adj); |
| gen_set_gpr(ctx, xSP, sp); |
| |
| return true; |
| } |
| |
| static bool trans_cm_pop(DisasContext *ctx, arg_cm_pop *a) |
| { |
| return gen_pop(ctx, a, false, false); |
| } |
| |
| static bool trans_cm_popret(DisasContext *ctx, arg_cm_popret *a) |
| { |
| return gen_pop(ctx, a, true, false); |
| } |
| |
| static bool trans_cm_popretz(DisasContext *ctx, arg_cm_popret *a) |
| { |
| return gen_pop(ctx, a, true, true); |
| } |
| |
| static bool trans_cm_mva01s(DisasContext *ctx, arg_cm_mva01s *a) |
| { |
| REQUIRE_ZCMP(ctx); |
| |
| TCGv src1 = get_gpr(ctx, a->rs1, EXT_NONE); |
| TCGv src2 = get_gpr(ctx, a->rs2, EXT_NONE); |
| |
| gen_set_gpr(ctx, xA0, src1); |
| gen_set_gpr(ctx, xA1, src2); |
| |
| return true; |
| } |
| |
| static bool trans_cm_mvsa01(DisasContext *ctx, arg_cm_mvsa01 *a) |
| { |
| REQUIRE_ZCMP(ctx); |
| |
| if (a->rs1 == a->rs2) { |
| return false; |
| } |
| |
| TCGv a0 = get_gpr(ctx, xA0, EXT_NONE); |
| TCGv a1 = get_gpr(ctx, xA1, EXT_NONE); |
| |
| gen_set_gpr(ctx, a->rs1, a0); |
| gen_set_gpr(ctx, a->rs2, a1); |
| |
| return true; |
| } |
| |
| static bool trans_cm_jalt(DisasContext *ctx, arg_cm_jalt *a) |
| { |
| REQUIRE_ZCMT(ctx); |
| |
| TCGv addr = tcg_temp_new(); |
| |
| /* |
| * Update pc to current for the non-unwinding exception |
| * that might come from cpu_ld*_code() in the helper. |
| */ |
| gen_update_pc(ctx, 0); |
| gen_helper_cm_jalt(addr, tcg_env, tcg_constant_i32(a->index)); |
| |
| /* c.jt vs c.jalt depends on the index. */ |
| if (a->index >= 32) { |
| TCGv succ_pc = dest_gpr(ctx, xRA); |
| gen_pc_plus_diff(succ_pc, ctx, ctx->cur_insn_len); |
| gen_set_gpr(ctx, xRA, succ_pc); |
| } |
| |
| tcg_gen_mov_tl(cpu_pc, addr); |
| |
| tcg_gen_lookup_and_goto_ptr(); |
| ctx->base.is_jmp = DISAS_NORETURN; |
| return true; |
| } |