Message ID | 20220106094200.1801206-17-gaosong@loongson.cn |
---|---|
State | New |
Headers | show |
Series | Add LoongArch linux-user emulation support | expand |
On 1/6/22 17:41, Song Gao wrote: > This patch adds support for disassembling via option '-d in_asm'. > > Signed-off-by: Song Gao<gaosong@loongson.cn> > Signed-off-by: Xiaojuan Yang<yangxiaojuan@loongson.cn> > Reviewed-by: Richard Henderson<richard.henderson@linaro.org> > --- > include/disas/dis-asm.h | 2 + > meson.build | 1 + > target/loongarch/disas.c | 612 +++++++++++++++++++++++++++++++++++++++ > 3 files changed, 615 insertions(+) > create mode 100644 target/loongarch/disas.c > > diff --git a/include/disas/dis-asm.h b/include/disas/dis-asm.h > index 08e1beec85..aeab30f19c 100644 > --- a/include/disas/dis-asm.h > +++ b/include/disas/dis-asm.h > @@ -253,6 +253,7 @@ enum bfd_architecture > #define bfd_mach_rx 0x75 > #define bfd_mach_rx_v2 0x76 > #define bfd_mach_rx_v3 0x77 > + bfd_arch_loongarch, > bfd_arch_last > }; > #define bfd_mach_s390_31 31 > @@ -461,6 +462,7 @@ int print_insn_riscv32 (bfd_vma, disassemble_info*); > int print_insn_riscv64 (bfd_vma, disassemble_info*); > int print_insn_rx(bfd_vma, disassemble_info *); > int print_insn_hexagon(bfd_vma, disassemble_info *); > +int print_insn_loongarch(bfd_vma, disassemble_info *); > > #ifdef CONFIG_CAPSTONE > bool cap_disas_target(disassemble_info *info, uint64_t pc, size_t size); > diff --git a/meson.build b/meson.build > index 53065e96ec..fa4c6dd241 100644 > --- a/meson.build > +++ b/meson.build > @@ -1848,6 +1848,7 @@ disassemblers = { > 'sh4' : ['CONFIG_SH4_DIS'], > 'sparc' : ['CONFIG_SPARC_DIS'], > 'xtensa' : ['CONFIG_XTENSA_DIS'], > + 'loongarch' : ['CONFIG_LOONGARCH_DIS'], > } > if link_language == 'cpp' > disassemblers += { > diff --git a/target/loongarch/disas.c b/target/loongarch/disas.c > new file mode 100644 > index 0000000000..45be34de27 > --- /dev/null > +++ b/target/loongarch/disas.c > @@ -0,0 +1,612 @@ > +/* SPDX-License-Identifier: GPL-2.0-or-later */ > +/* > + * QEMU LoongArch Disassembler > + * > + * Copyright (c) 2021 Loongson Technology Corporation Limited. > + */ > + > +#include "qemu/osdep.h" > +#include "disas/dis-asm.h" > +#include "qemu/bitops.h" > + > +typedef struct { > + disassemble_info *info; > + uint64_t pc; > + uint32_t insn; > +} DisasContext; > + > +static inline int plus_1(DisasContext *ctx, int x) > +{ > + return x + 1; > +} > + > +static inline int shl_2(DisasContext *ctx, int x) > +{ > + return x * 4; Although "<< 2" has the same effect as "* 4" here, isn't "<< 2" better in matching the function name? > +} > + > +#define output(C, INSN, FMT, ...) \ > +{ \ > + (C)->info->fprintf_func((C)->info->stream, "%08x %-9s\t" FMT, \ > + (C)->insn, INSN, ##__VA_ARGS__); \ > +} > + > +#include "decode-insns.c.inc" > + > +int print_insn_loongarch(bfd_vma memaddr, struct disassemble_info *info) > +{ > + bfd_byte buffer[4]; > + uint32_t insn; > + int status; > + > + status = (*info->read_memory_func)(memaddr, buffer, 4, info); > + if (status != 0) { > + (*info->memory_error_func)(status, memaddr, info); > + return -1; > + } > + insn = bfd_getl32(buffer); > + DisasContext ctx = { > + .info = info, > + .pc = memaddr, > + .insn = insn > + }; > + > + if (!decode(&ctx, insn)) { > + output(&ctx, "illegal", ""); > + } > + return 4; > +} > + > +static void output_r_i(DisasContext *ctx, arg_r_i *a, const char *mnemonic) > +{ > + output(ctx, mnemonic, "r%d, %d", a->rd, a->imm); > +} > + > +static void output_rrr(DisasContext *ctx, arg_rrr *a, const char *mnemonic) > +{ > + output(ctx, mnemonic, "r%d, r%d, r%d", a->rd, a->rj, a->rk); > +} > + > +static void output_rr_i(DisasContext *ctx, arg_rr_i *a, const char *mnemonic) > +{ > + output(ctx, mnemonic, "r%d, r%d, %d", a->rd, a->rj, a->imm); > +} > + > +static void output_rrr_sa(DisasContext *ctx, arg_rrr_sa *a, > + const char *mnemonic) > +{ > + output(ctx, mnemonic, "r%d, r%d, r%d, %d", a->rd, a->rj, a->rk, a->sa); > +} > + > +static void output_rr(DisasContext *ctx, arg_rr *a, const char *mnemonic) > +{ > + output(ctx, mnemonic, "r%d, r%d", a->rd, a->rj); > +} > + > +static void output_rr_ms_ls(DisasContext *ctx, arg_rr_ms_ls *a, > + const char *mnemonic) > +{ > + output(ctx, mnemonic, "r%d, r%d, %d, %d", a->rd, a->rj, a->ms, a->ls); > +} > + > +static void output_hint_r_i(DisasContext *ctx, arg_hint_r_i *a, > + const char *mnemonic) > +{ > + output(ctx, mnemonic, "%d, r%d, %d", a->hint, a->rj, a->imm); > +} > + > +static void output_i(DisasContext *ctx, arg_i *a, const char *mnemonic) > +{ > + output(ctx, mnemonic, "%d", a->imm); > +} > + > +static void output_rr_jk(DisasContext *ctx, arg_rr_jk *a, > + const char *mnemonic) > +{ > + output(ctx, mnemonic, "r%d, r%d", a->rj, a->rk); > +} > + > +static void output_ff(DisasContext *ctx, arg_ff *a, const char *mnemonic) > +{ > + output(ctx, mnemonic, "f%d, f%d", a->fd, a->fj); > +} > + > +static void output_fff(DisasContext *ctx, arg_fff *a, const char *mnemonic) > +{ > + output(ctx, mnemonic, "f%d, f%d, f%d", a->fd, a->fj, a->fk); > +} > + > +static void output_ffff(DisasContext *ctx, arg_ffff *a, const char *mnemonic) > +{ > + output(ctx, mnemonic, "f%d, f%d, f%d, f%d", a->fd, a->fj, a->fk, a->fa); > +} > + > +static void output_fffc(DisasContext *ctx, arg_fffc *a, const char *mnemonic) > +{ > + output(ctx, mnemonic, "f%d, f%d, f%d, %d", a->fd, a->fj, a->fk, a->ca); > +} > + > +static void output_fr(DisasContext *ctx, arg_fr *a, const char *mnemonic) > +{ > + output(ctx, mnemonic, "f%d, r%d", a->fd, a->rj); > +} > + > +static void output_rf(DisasContext *ctx, arg_rf *a, const char *mnemonic) > +{ > + output(ctx, mnemonic, "r%d, f%d", a->rd, a->fj); > +} > + > +static void output_fcsrd_r(DisasContext *ctx, arg_fcsrd_r *a, > + const char *mnemonic) > +{ > + output(ctx, mnemonic, "fcsr%d, r%d", a->fcsrd, a->rj); > +} > + > +static void output_r_fcsrs(DisasContext *ctx, arg_r_fcsrs *a, > + const char *mnemonic) > +{ > + output(ctx, mnemonic, "r%d, fcsr%d", a->rd, a->fcsrs); > +} > + > +static void output_cf(DisasContext *ctx, arg_cf *a, const char *mnemonic) > +{ > + output(ctx, mnemonic, "fcc%d, f%d", a->cd, a->fj); > +} > + > +static void output_fc(DisasContext *ctx, arg_fc *a, const char *mnemonic) > +{ > + output(ctx, mnemonic, "f%d, fcc%d", a->fd, a->cj); > +} > + > +static void output_cr(DisasContext *ctx, arg_cr *a, const char *mnemonic) > +{ > + output(ctx, mnemonic, "fcc%d, r%d", a->cd, a->rj); > +} > + > +static void output_rc(DisasContext *ctx, arg_rc *a, const char *mnemonic) > +{ > + output(ctx, mnemonic, "r%d, fcc%d", a->rd, a->cj); > +} > + > +static void output_frr(DisasContext *ctx, arg_frr *a, const char *mnemonic) > +{ > + output(ctx, mnemonic, "f%d, r%d, r%d", a->fd, a->rj, a->rk); > +} > + > +static void output_fr_i(DisasContext *ctx, arg_fr_i *a, const char *mnemonic) > +{ > + output(ctx, mnemonic, "f%d, r%d, %d", a->fd, a->rj, a->imm); > +} > + > +static void output_r_offs(DisasContext *ctx, arg_r_offs *a, > + const char *mnemonic) > +{ > + output(ctx, mnemonic, "r%d, %d # 0x%" PRIx64, a->rj, a->offs, > + ctx->pc + a->offs); > +} > + > +static void output_c_offs(DisasContext *ctx, arg_c_offs *a, > + const char *mnemonic) > +{ > + output(ctx, mnemonic, "fcc%d, %d # 0x%" PRIx64, a->cj, a->offs, > + ctx->pc + a->offs); > +} > + > +static void output_offs(DisasContext *ctx, arg_offs *a, > + const char *mnemonic) > +{ > + output(ctx, mnemonic, "%d # 0x%" PRIx64, a->offs, ctx->pc + a->offs); > +} > + > +static void output_rr_offs(DisasContext *ctx, arg_rr_offs *a, > + const char *mnemonic) > +{ > + output(ctx, mnemonic, "r%d, r%d, %d", a->rj, a->rd, a->offs); Why no "# %d" and "ctx->pc + a->offs" here? Also the implicit reversal of rj and rd is ugly, but this wart comes straight from the manual so you may as well keep it for the time being. I'll rewrite all these code using the loongarch-opcodes tables and assembler syntax after everything is merged, though. > +} > + > +#define INSN(insn, type) \ > +static bool trans_##insn(DisasContext *ctx, arg_##type * a) \ > +{ \ > + output_##type(ctx, a, #insn); \ > + return true; \ > +} > + > +INSN(clo_w, rr) > +INSN(clz_w, rr) > +INSN(cto_w, rr) > +INSN(ctz_w, rr) > +INSN(clo_d, rr) > +INSN(clz_d, rr) > +INSN(cto_d, rr) > +INSN(ctz_d, rr) > +INSN(revb_2h, rr) > +INSN(revb_4h, rr) > +INSN(revb_2w, rr) > +INSN(revb_d, rr) > +INSN(revh_2w, rr) > +INSN(revh_d, rr) > +INSN(bitrev_4b, rr) > +INSN(bitrev_8b, rr) > +INSN(bitrev_w, rr) > +INSN(bitrev_d, rr) > +INSN(ext_w_h, rr) > +INSN(ext_w_b, rr) > +INSN(rdtime_d, rr) > +INSN(cpucfg, rr) > +INSN(asrtle_d, rr_jk) > +INSN(asrtgt_d, rr_jk) > +INSN(alsl_w, rrr_sa) > +INSN(alsl_wu, rrr_sa) > +INSN(bytepick_w, rrr_sa) > +INSN(bytepick_d, rrr_sa) > +INSN(add_w, rrr) > +INSN(add_d, rrr) > +INSN(sub_w, rrr) > +INSN(sub_d, rrr) > +INSN(slt, rrr) > +INSN(sltu, rrr) > +INSN(maskeqz, rrr) > +INSN(masknez, rrr) > +INSN(nor, rrr) > +INSN(and, rrr) > +INSN(or, rrr) > +INSN(xor, rrr) > +INSN(orn, rrr) > +INSN(andn, rrr) > +INSN(sll_w, rrr) > +INSN(srl_w, rrr) > +INSN(sra_w, rrr) > +INSN(sll_d, rrr) > +INSN(srl_d, rrr) > +INSN(sra_d, rrr) > +INSN(rotr_w, rrr) > +INSN(rotr_d, rrr) > +INSN(mul_w, rrr) > +INSN(mulh_w, rrr) > +INSN(mulh_wu, rrr) > +INSN(mul_d, rrr) > +INSN(mulh_d, rrr) > +INSN(mulh_du, rrr) > +INSN(mulw_d_w, rrr) > +INSN(mulw_d_wu, rrr) > +INSN(div_w, rrr) > +INSN(mod_w, rrr) > +INSN(div_wu, rrr) > +INSN(mod_wu, rrr) > +INSN(div_d, rrr) > +INSN(mod_d, rrr) > +INSN(div_du, rrr) > +INSN(mod_du, rrr) > +INSN(crc_w_b_w, rrr) > +INSN(crc_w_h_w, rrr) > +INSN(crc_w_w_w, rrr) > +INSN(crc_w_d_w, rrr) > +INSN(crcc_w_b_w, rrr) > +INSN(crcc_w_h_w, rrr) > +INSN(crcc_w_w_w, rrr) > +INSN(crcc_w_d_w, rrr) > +INSN(break, i) > +INSN(syscall, i) > +INSN(alsl_d, rrr_sa) > +INSN(slli_w, rr_i) > +INSN(slli_d, rr_i) > +INSN(srli_w, rr_i) > +INSN(srli_d, rr_i) > +INSN(srai_w, rr_i) > +INSN(srai_d, rr_i) > +INSN(rotri_w, rr_i) > +INSN(rotri_d, rr_i) > +INSN(bstrins_w, rr_ms_ls) > +INSN(bstrpick_w, rr_ms_ls) > +INSN(bstrins_d, rr_ms_ls) > +INSN(bstrpick_d, rr_ms_ls) > +INSN(fadd_s, fff) > +INSN(fadd_d, fff) > +INSN(fsub_s, fff) > +INSN(fsub_d, fff) > +INSN(fmul_s, fff) > +INSN(fmul_d, fff) > +INSN(fdiv_s, fff) > +INSN(fdiv_d, fff) > +INSN(fmax_s, fff) > +INSN(fmax_d, fff) > +INSN(fmin_s, fff) > +INSN(fmin_d, fff) > +INSN(fmaxa_s, fff) > +INSN(fmaxa_d, fff) > +INSN(fmina_s, fff) > +INSN(fmina_d, fff) > +INSN(fscaleb_s, fff) > +INSN(fscaleb_d, fff) > +INSN(fcopysign_s, fff) > +INSN(fcopysign_d, fff) > +INSN(fabs_s, ff) > +INSN(fabs_d, ff) > +INSN(fneg_s, ff) > +INSN(fneg_d, ff) > +INSN(flogb_s, ff) > +INSN(flogb_d, ff) > +INSN(fclass_s, ff) > +INSN(fclass_d, ff) > +INSN(fsqrt_s, ff) > +INSN(fsqrt_d, ff) > +INSN(frecip_s, ff) > +INSN(frecip_d, ff) > +INSN(frsqrt_s, ff) > +INSN(frsqrt_d, ff) > +INSN(fmov_s, ff) > +INSN(fmov_d, ff) > +INSN(movgr2fr_w, fr) > +INSN(movgr2fr_d, fr) > +INSN(movgr2frh_w, fr) > +INSN(movfr2gr_s, rf) > +INSN(movfr2gr_d, rf) > +INSN(movfrh2gr_s, rf) > +INSN(movgr2fcsr, fcsrd_r) > +INSN(movfcsr2gr, r_fcsrs) > +INSN(movfr2cf, cf) > +INSN(movcf2fr, fc) > +INSN(movgr2cf, cr) > +INSN(movcf2gr, rc) > +INSN(fcvt_s_d, ff) > +INSN(fcvt_d_s, ff) > +INSN(ftintrm_w_s, ff) > +INSN(ftintrm_w_d, ff) > +INSN(ftintrm_l_s, ff) > +INSN(ftintrm_l_d, ff) > +INSN(ftintrp_w_s, ff) > +INSN(ftintrp_w_d, ff) > +INSN(ftintrp_l_s, ff) > +INSN(ftintrp_l_d, ff) > +INSN(ftintrz_w_s, ff) > +INSN(ftintrz_w_d, ff) > +INSN(ftintrz_l_s, ff) > +INSN(ftintrz_l_d, ff) > +INSN(ftintrne_w_s, ff) > +INSN(ftintrne_w_d, ff) > +INSN(ftintrne_l_s, ff) > +INSN(ftintrne_l_d, ff) > +INSN(ftint_w_s, ff) > +INSN(ftint_w_d, ff) > +INSN(ftint_l_s, ff) > +INSN(ftint_l_d, ff) > +INSN(ffint_s_w, ff) > +INSN(ffint_s_l, ff) > +INSN(ffint_d_w, ff) > +INSN(ffint_d_l, ff) > +INSN(frint_s, ff) > +INSN(frint_d, ff) > +INSN(slti, rr_i) > +INSN(sltui, rr_i) > +INSN(addi_w, rr_i) > +INSN(addi_d, rr_i) > +INSN(lu52i_d, rr_i) > +INSN(andi, rr_i) > +INSN(ori, rr_i) > +INSN(xori, rr_i) > +INSN(rdtimel_w, rr) > +INSN(rdtimeh_w, rr) > +INSN(fmadd_s, ffff) > +INSN(fmadd_d, ffff) > +INSN(fmsub_s, ffff) > +INSN(fmsub_d, ffff) > +INSN(fnmadd_s, ffff) > +INSN(fnmadd_d, ffff) > +INSN(fnmsub_s, ffff) > +INSN(fnmsub_d, ffff) > +INSN(fsel, fffc) > +INSN(addu16i_d, rr_i) > +INSN(lu12i_w, r_i) > +INSN(lu32i_d, r_i) > +INSN(pcaddi, r_i) > +INSN(pcalau12i, r_i) > +INSN(pcaddu12i, r_i) > +INSN(pcaddu18i, r_i) > +INSN(ll_w, rr_i) > +INSN(sc_w, rr_i) > +INSN(ll_d, rr_i) > +INSN(sc_d, rr_i) > +INSN(ldptr_w, rr_i) > +INSN(stptr_w, rr_i) > +INSN(ldptr_d, rr_i) > +INSN(stptr_d, rr_i) > +INSN(ld_b, rr_i) > +INSN(ld_h, rr_i) > +INSN(ld_w, rr_i) > +INSN(ld_d, rr_i) > +INSN(st_b, rr_i) > +INSN(st_h, rr_i) > +INSN(st_w, rr_i) > +INSN(st_d, rr_i) > +INSN(ld_bu, rr_i) > +INSN(ld_hu, rr_i) > +INSN(ld_wu, rr_i) > +INSN(preld, hint_r_i) > +INSN(fld_s, fr_i) > +INSN(fst_s, fr_i) > +INSN(fld_d, fr_i) > +INSN(fst_d, fr_i) > +INSN(ldx_b, rrr) > +INSN(ldx_h, rrr) > +INSN(ldx_w, rrr) > +INSN(ldx_d, rrr) > +INSN(stx_b, rrr) > +INSN(stx_h, rrr) > +INSN(stx_w, rrr) > +INSN(stx_d, rrr) > +INSN(ldx_bu, rrr) > +INSN(ldx_hu, rrr) > +INSN(ldx_wu, rrr) > +INSN(fldx_s, frr) > +INSN(fldx_d, frr) > +INSN(fstx_s, frr) > +INSN(fstx_d, frr) > +INSN(amswap_w, rrr) > +INSN(amswap_d, rrr) > +INSN(amadd_w, rrr) > +INSN(amadd_d, rrr) > +INSN(amand_w, rrr) > +INSN(amand_d, rrr) > +INSN(amor_w, rrr) > +INSN(amor_d, rrr) > +INSN(amxor_w, rrr) > +INSN(amxor_d, rrr) > +INSN(ammax_w, rrr) > +INSN(ammax_d, rrr) > +INSN(ammin_w, rrr) > +INSN(ammin_d, rrr) > +INSN(ammax_wu, rrr) > +INSN(ammax_du, rrr) > +INSN(ammin_wu, rrr) > +INSN(ammin_du, rrr) > +INSN(amswap_db_w, rrr) > +INSN(amswap_db_d, rrr) > +INSN(amadd_db_w, rrr) > +INSN(amadd_db_d, rrr) > +INSN(amand_db_w, rrr) > +INSN(amand_db_d, rrr) > +INSN(amor_db_w, rrr) > +INSN(amor_db_d, rrr) > +INSN(amxor_db_w, rrr) > +INSN(amxor_db_d, rrr) > +INSN(ammax_db_w, rrr) > +INSN(ammax_db_d, rrr) > +INSN(ammin_db_w, rrr) > +INSN(ammin_db_d, rrr) > +INSN(ammax_db_wu, rrr) > +INSN(ammax_db_du, rrr) > +INSN(ammin_db_wu, rrr) > +INSN(ammin_db_du, rrr) > +INSN(dbar, i) > +INSN(ibar, i) > +INSN(fldgt_s, frr) > +INSN(fldgt_d, frr) > +INSN(fldle_s, frr) > +INSN(fldle_d, frr) > +INSN(fstgt_s, frr) > +INSN(fstgt_d, frr) > +INSN(fstle_s, frr) > +INSN(fstle_d, frr) > +INSN(ldgt_b, rrr) > +INSN(ldgt_h, rrr) > +INSN(ldgt_w, rrr) > +INSN(ldgt_d, rrr) > +INSN(ldle_b, rrr) > +INSN(ldle_h, rrr) > +INSN(ldle_w, rrr) > +INSN(ldle_d, rrr) > +INSN(stgt_b, rrr) > +INSN(stgt_h, rrr) > +INSN(stgt_w, rrr) > +INSN(stgt_d, rrr) > +INSN(stle_b, rrr) > +INSN(stle_h, rrr) > +INSN(stle_w, rrr) > +INSN(stle_d, rrr) > +INSN(beqz, r_offs) > +INSN(bnez, r_offs) > +INSN(bceqz, c_offs) > +INSN(bcnez, c_offs) > +INSN(jirl, rr_offs) > +INSN(b, offs) > +INSN(bl, offs) > +INSN(beq, rr_offs) > +INSN(bne, rr_offs) > +INSN(blt, rr_offs) > +INSN(bge, rr_offs) > +INSN(bltu, rr_offs) > +INSN(bgeu, rr_offs) > + > +#define output_fcmp(C, PREFIX, SUFFIX) \ > +{ \ > + (C)->info->fprintf_func((C)->info->stream, "%08x %s%s\tfcc%d, f%d, f%d", \ > + (C)->insn, PREFIX, SUFFIX, a->cd, \ > + a->fj, a->fk); \ > +} > + > +static bool output_cff_fcond(DisasContext *ctx, arg_cff_fcond * a, > + const char *suffix) > +{ > + bool ret = true; > + switch (a->fcond) { > + case 0x0: > + output_fcmp(ctx, "fcmp_caf_", suffix); > + break; > + case 0x1: > + output_fcmp(ctx, "fcmp_saf_", suffix); > + break; > + case 0x2: > + output_fcmp(ctx, "fcmp_clt_", suffix); > + break; > + case 0x3: > + output_fcmp(ctx, "fcmp_slt_", suffix); > + break; > + case 0x4: > + output_fcmp(ctx, "fcmp_ceq_", suffix); > + break; > + case 0x5: > + output_fcmp(ctx, "fcmp_seq_", suffix); > + break; > + case 0x6: > + output_fcmp(ctx, "fcmp_cle_", suffix); > + break; > + case 0x7: > + output_fcmp(ctx, "fcmp_sle_", suffix); > + break; > + case 0x8: > + output_fcmp(ctx, "fcmp_cun_", suffix); > + break; > + case 0x9: > + output_fcmp(ctx, "fcmp_sun_", suffix); > + break; > + case 0xA: > + output_fcmp(ctx, "fcmp_cult_", suffix); > + break; > + case 0xB: > + output_fcmp(ctx, "fcmp_sult_", suffix); > + break; > + case 0xC: > + output_fcmp(ctx, "fcmp_cueq_", suffix); > + break; > + case 0xD: > + output_fcmp(ctx, "fcmp_sueq_", suffix); > + break; > + case 0xE: > + output_fcmp(ctx, "fcmp_cule_", suffix); > + break; > + case 0xF: > + output_fcmp(ctx, "fcmp_sule_", suffix); > + break; > + case 0x10: > + output_fcmp(ctx, "fcmp_cne_", suffix); > + break; > + case 0x11: > + output_fcmp(ctx, "fcmp_sne_", suffix); > + break; > + case 0x14: > + output_fcmp(ctx, "fcmp_cor_", suffix); > + break; > + case 0x15: > + output_fcmp(ctx, "fcmp_sor_", suffix); > + break; > + case 0x18: > + output_fcmp(ctx, "fcmp_cune_", suffix); > + break; > + case 0x19: > + output_fcmp(ctx, "fcmp_sune_", suffix); > + break; > + default: > + ret = false; > + } > + return ret; > +} > + > +#define FCMP_INSN(suffix) \ > +static bool trans_fcmp_cond_##suffix(DisasContext *ctx, \ > + arg_cff_fcond * a) \ > +{ \ > + return output_cff_fcond(ctx, a, #suffix); \ > +} > + > +FCMP_INSN(s) > +FCMP_INSN(d)
On 1/9/22 1:25 AM, WANG Xuerui wrote: >> +static inline int shl_2(DisasContext *ctx, int x) >> +{ >> + return x * 4; > Although "<< 2" has the same effect as "* 4" here, isn't "<< 2" better in matching the > function name? Yes, good point. r~
diff --git a/include/disas/dis-asm.h b/include/disas/dis-asm.h index 08e1beec85..aeab30f19c 100644 --- a/include/disas/dis-asm.h +++ b/include/disas/dis-asm.h @@ -253,6 +253,7 @@ enum bfd_architecture #define bfd_mach_rx 0x75 #define bfd_mach_rx_v2 0x76 #define bfd_mach_rx_v3 0x77 + bfd_arch_loongarch, bfd_arch_last }; #define bfd_mach_s390_31 31 @@ -461,6 +462,7 @@ int print_insn_riscv32 (bfd_vma, disassemble_info*); int print_insn_riscv64 (bfd_vma, disassemble_info*); int print_insn_rx(bfd_vma, disassemble_info *); int print_insn_hexagon(bfd_vma, disassemble_info *); +int print_insn_loongarch(bfd_vma, disassemble_info *); #ifdef CONFIG_CAPSTONE bool cap_disas_target(disassemble_info *info, uint64_t pc, size_t size); diff --git a/meson.build b/meson.build index 53065e96ec..fa4c6dd241 100644 --- a/meson.build +++ b/meson.build @@ -1848,6 +1848,7 @@ disassemblers = { 'sh4' : ['CONFIG_SH4_DIS'], 'sparc' : ['CONFIG_SPARC_DIS'], 'xtensa' : ['CONFIG_XTENSA_DIS'], + 'loongarch' : ['CONFIG_LOONGARCH_DIS'], } if link_language == 'cpp' disassemblers += { diff --git a/target/loongarch/disas.c b/target/loongarch/disas.c new file mode 100644 index 0000000000..45be34de27 --- /dev/null +++ b/target/loongarch/disas.c @@ -0,0 +1,612 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * QEMU LoongArch Disassembler + * + * Copyright (c) 2021 Loongson Technology Corporation Limited. + */ + +#include "qemu/osdep.h" +#include "disas/dis-asm.h" +#include "qemu/bitops.h" + +typedef struct { + disassemble_info *info; + uint64_t pc; + uint32_t insn; +} DisasContext; + +static inline int plus_1(DisasContext *ctx, int x) +{ + return x + 1; +} + +static inline int shl_2(DisasContext *ctx, int x) +{ + return x * 4; +} + +#define output(C, INSN, FMT, ...) \ +{ \ + (C)->info->fprintf_func((C)->info->stream, "%08x %-9s\t" FMT, \ + (C)->insn, INSN, ##__VA_ARGS__); \ +} + +#include "decode-insns.c.inc" + +int print_insn_loongarch(bfd_vma memaddr, struct disassemble_info *info) +{ + bfd_byte buffer[4]; + uint32_t insn; + int status; + + status = (*info->read_memory_func)(memaddr, buffer, 4, info); + if (status != 0) { + (*info->memory_error_func)(status, memaddr, info); + return -1; + } + insn = bfd_getl32(buffer); + DisasContext ctx = { + .info = info, + .pc = memaddr, + .insn = insn + }; + + if (!decode(&ctx, insn)) { + output(&ctx, "illegal", ""); + } + return 4; +} + +static void output_r_i(DisasContext *ctx, arg_r_i *a, const char *mnemonic) +{ + output(ctx, mnemonic, "r%d, %d", a->rd, a->imm); +} + +static void output_rrr(DisasContext *ctx, arg_rrr *a, const char *mnemonic) +{ + output(ctx, mnemonic, "r%d, r%d, r%d", a->rd, a->rj, a->rk); +} + +static void output_rr_i(DisasContext *ctx, arg_rr_i *a, const char *mnemonic) +{ + output(ctx, mnemonic, "r%d, r%d, %d", a->rd, a->rj, a->imm); +} + +static void output_rrr_sa(DisasContext *ctx, arg_rrr_sa *a, + const char *mnemonic) +{ + output(ctx, mnemonic, "r%d, r%d, r%d, %d", a->rd, a->rj, a->rk, a->sa); +} + +static void output_rr(DisasContext *ctx, arg_rr *a, const char *mnemonic) +{ + output(ctx, mnemonic, "r%d, r%d", a->rd, a->rj); +} + +static void output_rr_ms_ls(DisasContext *ctx, arg_rr_ms_ls *a, + const char *mnemonic) +{ + output(ctx, mnemonic, "r%d, r%d, %d, %d", a->rd, a->rj, a->ms, a->ls); +} + +static void output_hint_r_i(DisasContext *ctx, arg_hint_r_i *a, + const char *mnemonic) +{ + output(ctx, mnemonic, "%d, r%d, %d", a->hint, a->rj, a->imm); +} + +static void output_i(DisasContext *ctx, arg_i *a, const char *mnemonic) +{ + output(ctx, mnemonic, "%d", a->imm); +} + +static void output_rr_jk(DisasContext *ctx, arg_rr_jk *a, + const char *mnemonic) +{ + output(ctx, mnemonic, "r%d, r%d", a->rj, a->rk); +} + +static void output_ff(DisasContext *ctx, arg_ff *a, const char *mnemonic) +{ + output(ctx, mnemonic, "f%d, f%d", a->fd, a->fj); +} + +static void output_fff(DisasContext *ctx, arg_fff *a, const char *mnemonic) +{ + output(ctx, mnemonic, "f%d, f%d, f%d", a->fd, a->fj, a->fk); +} + +static void output_ffff(DisasContext *ctx, arg_ffff *a, const char *mnemonic) +{ + output(ctx, mnemonic, "f%d, f%d, f%d, f%d", a->fd, a->fj, a->fk, a->fa); +} + +static void output_fffc(DisasContext *ctx, arg_fffc *a, const char *mnemonic) +{ + output(ctx, mnemonic, "f%d, f%d, f%d, %d", a->fd, a->fj, a->fk, a->ca); +} + +static void output_fr(DisasContext *ctx, arg_fr *a, const char *mnemonic) +{ + output(ctx, mnemonic, "f%d, r%d", a->fd, a->rj); +} + +static void output_rf(DisasContext *ctx, arg_rf *a, const char *mnemonic) +{ + output(ctx, mnemonic, "r%d, f%d", a->rd, a->fj); +} + +static void output_fcsrd_r(DisasContext *ctx, arg_fcsrd_r *a, + const char *mnemonic) +{ + output(ctx, mnemonic, "fcsr%d, r%d", a->fcsrd, a->rj); +} + +static void output_r_fcsrs(DisasContext *ctx, arg_r_fcsrs *a, + const char *mnemonic) +{ + output(ctx, mnemonic, "r%d, fcsr%d", a->rd, a->fcsrs); +} + +static void output_cf(DisasContext *ctx, arg_cf *a, const char *mnemonic) +{ + output(ctx, mnemonic, "fcc%d, f%d", a->cd, a->fj); +} + +static void output_fc(DisasContext *ctx, arg_fc *a, const char *mnemonic) +{ + output(ctx, mnemonic, "f%d, fcc%d", a->fd, a->cj); +} + +static void output_cr(DisasContext *ctx, arg_cr *a, const char *mnemonic) +{ + output(ctx, mnemonic, "fcc%d, r%d", a->cd, a->rj); +} + +static void output_rc(DisasContext *ctx, arg_rc *a, const char *mnemonic) +{ + output(ctx, mnemonic, "r%d, fcc%d", a->rd, a->cj); +} + +static void output_frr(DisasContext *ctx, arg_frr *a, const char *mnemonic) +{ + output(ctx, mnemonic, "f%d, r%d, r%d", a->fd, a->rj, a->rk); +} + +static void output_fr_i(DisasContext *ctx, arg_fr_i *a, const char *mnemonic) +{ + output(ctx, mnemonic, "f%d, r%d, %d", a->fd, a->rj, a->imm); +} + +static void output_r_offs(DisasContext *ctx, arg_r_offs *a, + const char *mnemonic) +{ + output(ctx, mnemonic, "r%d, %d # 0x%" PRIx64, a->rj, a->offs, + ctx->pc + a->offs); +} + +static void output_c_offs(DisasContext *ctx, arg_c_offs *a, + const char *mnemonic) +{ + output(ctx, mnemonic, "fcc%d, %d # 0x%" PRIx64, a->cj, a->offs, + ctx->pc + a->offs); +} + +static void output_offs(DisasContext *ctx, arg_offs *a, + const char *mnemonic) +{ + output(ctx, mnemonic, "%d # 0x%" PRIx64, a->offs, ctx->pc + a->offs); +} + +static void output_rr_offs(DisasContext *ctx, arg_rr_offs *a, + const char *mnemonic) +{ + output(ctx, mnemonic, "r%d, r%d, %d", a->rj, a->rd, a->offs); +} + +#define INSN(insn, type) \ +static bool trans_##insn(DisasContext *ctx, arg_##type * a) \ +{ \ + output_##type(ctx, a, #insn); \ + return true; \ +} + +INSN(clo_w, rr) +INSN(clz_w, rr) +INSN(cto_w, rr) +INSN(ctz_w, rr) +INSN(clo_d, rr) +INSN(clz_d, rr) +INSN(cto_d, rr) +INSN(ctz_d, rr) +INSN(revb_2h, rr) +INSN(revb_4h, rr) +INSN(revb_2w, rr) +INSN(revb_d, rr) +INSN(revh_2w, rr) +INSN(revh_d, rr) +INSN(bitrev_4b, rr) +INSN(bitrev_8b, rr) +INSN(bitrev_w, rr) +INSN(bitrev_d, rr) +INSN(ext_w_h, rr) +INSN(ext_w_b, rr) +INSN(rdtime_d, rr) +INSN(cpucfg, rr) +INSN(asrtle_d, rr_jk) +INSN(asrtgt_d, rr_jk) +INSN(alsl_w, rrr_sa) +INSN(alsl_wu, rrr_sa) +INSN(bytepick_w, rrr_sa) +INSN(bytepick_d, rrr_sa) +INSN(add_w, rrr) +INSN(add_d, rrr) +INSN(sub_w, rrr) +INSN(sub_d, rrr) +INSN(slt, rrr) +INSN(sltu, rrr) +INSN(maskeqz, rrr) +INSN(masknez, rrr) +INSN(nor, rrr) +INSN(and, rrr) +INSN(or, rrr) +INSN(xor, rrr) +INSN(orn, rrr) +INSN(andn, rrr) +INSN(sll_w, rrr) +INSN(srl_w, rrr) +INSN(sra_w, rrr) +INSN(sll_d, rrr) +INSN(srl_d, rrr) +INSN(sra_d, rrr) +INSN(rotr_w, rrr) +INSN(rotr_d, rrr) +INSN(mul_w, rrr) +INSN(mulh_w, rrr) +INSN(mulh_wu, rrr) +INSN(mul_d, rrr) +INSN(mulh_d, rrr) +INSN(mulh_du, rrr) +INSN(mulw_d_w, rrr) +INSN(mulw_d_wu, rrr) +INSN(div_w, rrr) +INSN(mod_w, rrr) +INSN(div_wu, rrr) +INSN(mod_wu, rrr) +INSN(div_d, rrr) +INSN(mod_d, rrr) +INSN(div_du, rrr) +INSN(mod_du, rrr) +INSN(crc_w_b_w, rrr) +INSN(crc_w_h_w, rrr) +INSN(crc_w_w_w, rrr) +INSN(crc_w_d_w, rrr) +INSN(crcc_w_b_w, rrr) +INSN(crcc_w_h_w, rrr) +INSN(crcc_w_w_w, rrr) +INSN(crcc_w_d_w, rrr) +INSN(break, i) +INSN(syscall, i) +INSN(alsl_d, rrr_sa) +INSN(slli_w, rr_i) +INSN(slli_d, rr_i) +INSN(srli_w, rr_i) +INSN(srli_d, rr_i) +INSN(srai_w, rr_i) +INSN(srai_d, rr_i) +INSN(rotri_w, rr_i) +INSN(rotri_d, rr_i) +INSN(bstrins_w, rr_ms_ls) +INSN(bstrpick_w, rr_ms_ls) +INSN(bstrins_d, rr_ms_ls) +INSN(bstrpick_d, rr_ms_ls) +INSN(fadd_s, fff) +INSN(fadd_d, fff) +INSN(fsub_s, fff) +INSN(fsub_d, fff) +INSN(fmul_s, fff) +INSN(fmul_d, fff) +INSN(fdiv_s, fff) +INSN(fdiv_d, fff) +INSN(fmax_s, fff) +INSN(fmax_d, fff) +INSN(fmin_s, fff) +INSN(fmin_d, fff) +INSN(fmaxa_s, fff) +INSN(fmaxa_d, fff) +INSN(fmina_s, fff) +INSN(fmina_d, fff) +INSN(fscaleb_s, fff) +INSN(fscaleb_d, fff) +INSN(fcopysign_s, fff) +INSN(fcopysign_d, fff) +INSN(fabs_s, ff) +INSN(fabs_d, ff) +INSN(fneg_s, ff) +INSN(fneg_d, ff) +INSN(flogb_s, ff) +INSN(flogb_d, ff) +INSN(fclass_s, ff) +INSN(fclass_d, ff) +INSN(fsqrt_s, ff) +INSN(fsqrt_d, ff) +INSN(frecip_s, ff) +INSN(frecip_d, ff) +INSN(frsqrt_s, ff) +INSN(frsqrt_d, ff) +INSN(fmov_s, ff) +INSN(fmov_d, ff) +INSN(movgr2fr_w, fr) +INSN(movgr2fr_d, fr) +INSN(movgr2frh_w, fr) +INSN(movfr2gr_s, rf) +INSN(movfr2gr_d, rf) +INSN(movfrh2gr_s, rf) +INSN(movgr2fcsr, fcsrd_r) +INSN(movfcsr2gr, r_fcsrs) +INSN(movfr2cf, cf) +INSN(movcf2fr, fc) +INSN(movgr2cf, cr) +INSN(movcf2gr, rc) +INSN(fcvt_s_d, ff) +INSN(fcvt_d_s, ff) +INSN(ftintrm_w_s, ff) +INSN(ftintrm_w_d, ff) +INSN(ftintrm_l_s, ff) +INSN(ftintrm_l_d, ff) +INSN(ftintrp_w_s, ff) +INSN(ftintrp_w_d, ff) +INSN(ftintrp_l_s, ff) +INSN(ftintrp_l_d, ff) +INSN(ftintrz_w_s, ff) +INSN(ftintrz_w_d, ff) +INSN(ftintrz_l_s, ff) +INSN(ftintrz_l_d, ff) +INSN(ftintrne_w_s, ff) +INSN(ftintrne_w_d, ff) +INSN(ftintrne_l_s, ff) +INSN(ftintrne_l_d, ff) +INSN(ftint_w_s, ff) +INSN(ftint_w_d, ff) +INSN(ftint_l_s, ff) +INSN(ftint_l_d, ff) +INSN(ffint_s_w, ff) +INSN(ffint_s_l, ff) +INSN(ffint_d_w, ff) +INSN(ffint_d_l, ff) +INSN(frint_s, ff) +INSN(frint_d, ff) +INSN(slti, rr_i) +INSN(sltui, rr_i) +INSN(addi_w, rr_i) +INSN(addi_d, rr_i) +INSN(lu52i_d, rr_i) +INSN(andi, rr_i) +INSN(ori, rr_i) +INSN(xori, rr_i) +INSN(rdtimel_w, rr) +INSN(rdtimeh_w, rr) +INSN(fmadd_s, ffff) +INSN(fmadd_d, ffff) +INSN(fmsub_s, ffff) +INSN(fmsub_d, ffff) +INSN(fnmadd_s, ffff) +INSN(fnmadd_d, ffff) +INSN(fnmsub_s, ffff) +INSN(fnmsub_d, ffff) +INSN(fsel, fffc) +INSN(addu16i_d, rr_i) +INSN(lu12i_w, r_i) +INSN(lu32i_d, r_i) +INSN(pcaddi, r_i) +INSN(pcalau12i, r_i) +INSN(pcaddu12i, r_i) +INSN(pcaddu18i, r_i) +INSN(ll_w, rr_i) +INSN(sc_w, rr_i) +INSN(ll_d, rr_i) +INSN(sc_d, rr_i) +INSN(ldptr_w, rr_i) +INSN(stptr_w, rr_i) +INSN(ldptr_d, rr_i) +INSN(stptr_d, rr_i) +INSN(ld_b, rr_i) +INSN(ld_h, rr_i) +INSN(ld_w, rr_i) +INSN(ld_d, rr_i) +INSN(st_b, rr_i) +INSN(st_h, rr_i) +INSN(st_w, rr_i) +INSN(st_d, rr_i) +INSN(ld_bu, rr_i) +INSN(ld_hu, rr_i) +INSN(ld_wu, rr_i) +INSN(preld, hint_r_i) +INSN(fld_s, fr_i) +INSN(fst_s, fr_i) +INSN(fld_d, fr_i) +INSN(fst_d, fr_i) +INSN(ldx_b, rrr) +INSN(ldx_h, rrr) +INSN(ldx_w, rrr) +INSN(ldx_d, rrr) +INSN(stx_b, rrr) +INSN(stx_h, rrr) +INSN(stx_w, rrr) +INSN(stx_d, rrr) +INSN(ldx_bu, rrr) +INSN(ldx_hu, rrr) +INSN(ldx_wu, rrr) +INSN(fldx_s, frr) +INSN(fldx_d, frr) +INSN(fstx_s, frr) +INSN(fstx_d, frr) +INSN(amswap_w, rrr) +INSN(amswap_d, rrr) +INSN(amadd_w, rrr) +INSN(amadd_d, rrr) +INSN(amand_w, rrr) +INSN(amand_d, rrr) +INSN(amor_w, rrr) +INSN(amor_d, rrr) +INSN(amxor_w, rrr) +INSN(amxor_d, rrr) +INSN(ammax_w, rrr) +INSN(ammax_d, rrr) +INSN(ammin_w, rrr) +INSN(ammin_d, rrr) +INSN(ammax_wu, rrr) +INSN(ammax_du, rrr) +INSN(ammin_wu, rrr) +INSN(ammin_du, rrr) +INSN(amswap_db_w, rrr) +INSN(amswap_db_d, rrr) +INSN(amadd_db_w, rrr) +INSN(amadd_db_d, rrr) +INSN(amand_db_w, rrr) +INSN(amand_db_d, rrr) +INSN(amor_db_w, rrr) +INSN(amor_db_d, rrr) +INSN(amxor_db_w, rrr) +INSN(amxor_db_d, rrr) +INSN(ammax_db_w, rrr) +INSN(ammax_db_d, rrr) +INSN(ammin_db_w, rrr) +INSN(ammin_db_d, rrr) +INSN(ammax_db_wu, rrr) +INSN(ammax_db_du, rrr) +INSN(ammin_db_wu, rrr) +INSN(ammin_db_du, rrr) +INSN(dbar, i) +INSN(ibar, i) +INSN(fldgt_s, frr) +INSN(fldgt_d, frr) +INSN(fldle_s, frr) +INSN(fldle_d, frr) +INSN(fstgt_s, frr) +INSN(fstgt_d, frr) +INSN(fstle_s, frr) +INSN(fstle_d, frr) +INSN(ldgt_b, rrr) +INSN(ldgt_h, rrr) +INSN(ldgt_w, rrr) +INSN(ldgt_d, rrr) +INSN(ldle_b, rrr) +INSN(ldle_h, rrr) +INSN(ldle_w, rrr) +INSN(ldle_d, rrr) +INSN(stgt_b, rrr) +INSN(stgt_h, rrr) +INSN(stgt_w, rrr) +INSN(stgt_d, rrr) +INSN(stle_b, rrr) +INSN(stle_h, rrr) +INSN(stle_w, rrr) +INSN(stle_d, rrr) +INSN(beqz, r_offs) +INSN(bnez, r_offs) +INSN(bceqz, c_offs) +INSN(bcnez, c_offs) +INSN(jirl, rr_offs) +INSN(b, offs) +INSN(bl, offs) +INSN(beq, rr_offs) +INSN(bne, rr_offs) +INSN(blt, rr_offs) +INSN(bge, rr_offs) +INSN(bltu, rr_offs) +INSN(bgeu, rr_offs) + +#define output_fcmp(C, PREFIX, SUFFIX) \ +{ \ + (C)->info->fprintf_func((C)->info->stream, "%08x %s%s\tfcc%d, f%d, f%d", \ + (C)->insn, PREFIX, SUFFIX, a->cd, \ + a->fj, a->fk); \ +} + +static bool output_cff_fcond(DisasContext *ctx, arg_cff_fcond * a, + const char *suffix) +{ + bool ret = true; + switch (a->fcond) { + case 0x0: + output_fcmp(ctx, "fcmp_caf_", suffix); + break; + case 0x1: + output_fcmp(ctx, "fcmp_saf_", suffix); + break; + case 0x2: + output_fcmp(ctx, "fcmp_clt_", suffix); + break; + case 0x3: + output_fcmp(ctx, "fcmp_slt_", suffix); + break; + case 0x4: + output_fcmp(ctx, "fcmp_ceq_", suffix); + break; + case 0x5: + output_fcmp(ctx, "fcmp_seq_", suffix); + break; + case 0x6: + output_fcmp(ctx, "fcmp_cle_", suffix); + break; + case 0x7: + output_fcmp(ctx, "fcmp_sle_", suffix); + break; + case 0x8: + output_fcmp(ctx, "fcmp_cun_", suffix); + break; + case 0x9: + output_fcmp(ctx, "fcmp_sun_", suffix); + break; + case 0xA: + output_fcmp(ctx, "fcmp_cult_", suffix); + break; + case 0xB: + output_fcmp(ctx, "fcmp_sult_", suffix); + break; + case 0xC: + output_fcmp(ctx, "fcmp_cueq_", suffix); + break; + case 0xD: + output_fcmp(ctx, "fcmp_sueq_", suffix); + break; + case 0xE: + output_fcmp(ctx, "fcmp_cule_", suffix); + break; + case 0xF: + output_fcmp(ctx, "fcmp_sule_", suffix); + break; + case 0x10: + output_fcmp(ctx, "fcmp_cne_", suffix); + break; + case 0x11: + output_fcmp(ctx, "fcmp_sne_", suffix); + break; + case 0x14: + output_fcmp(ctx, "fcmp_cor_", suffix); + break; + case 0x15: + output_fcmp(ctx, "fcmp_sor_", suffix); + break; + case 0x18: + output_fcmp(ctx, "fcmp_cune_", suffix); + break; + case 0x19: + output_fcmp(ctx, "fcmp_sune_", suffix); + break; + default: + ret = false; + } + return ret; +} + +#define FCMP_INSN(suffix) \ +static bool trans_fcmp_cond_##suffix(DisasContext *ctx, \ + arg_cff_fcond * a) \ +{ \ + return output_cff_fcond(ctx, a, #suffix); \ +} + +FCMP_INSN(s) +FCMP_INSN(d)