1f8da88d7SSong Gao /* SPDX-License-Identifier: GPL-2.0-or-later */ 2f8da88d7SSong Gao /* 3f8da88d7SSong Gao * LoongArch emulation for QEMU - main translation routines. 4f8da88d7SSong Gao * 5f8da88d7SSong Gao * Copyright (c) 2021 Loongson Technology Corporation Limited 6f8da88d7SSong Gao */ 7f8da88d7SSong Gao 8f8da88d7SSong Gao #include "qemu/osdep.h" 9f8da88d7SSong Gao #include "cpu.h" 10f8da88d7SSong Gao #include "tcg/tcg-op.h" 1157b4f1acSSong Gao #include "tcg/tcg-op-gvec.h" 12d654e928SRichard Henderson #include "exec/translation-block.h" 13f8da88d7SSong Gao #include "exec/translator.h" 14f8da88d7SSong Gao #include "exec/helper-proto.h" 15f8da88d7SSong Gao #include "exec/helper-gen.h" 16f8da88d7SSong Gao #include "exec/log.h" 17f8da88d7SSong Gao #include "qemu/qemu-print.h" 18d578ca6cSSong Gao #include "fpu/softfloat.h" 19f8da88d7SSong Gao #include "translate.h" 20f8da88d7SSong Gao #include "internals.h" 21008a3b16SSong Gao #include "vec.h" 22f8da88d7SSong Gao 23f8da88d7SSong Gao /* Global register indices */ 24f8da88d7SSong Gao TCGv cpu_gpr[32], cpu_pc; 25f8da88d7SSong Gao static TCGv cpu_lladdr, cpu_llval; 26f8da88d7SSong Gao 27d53106c9SRichard Henderson #define HELPER_H "helper.h" 28d53106c9SRichard Henderson #include "exec/helper-info.c.inc" 29d53106c9SRichard Henderson #undef HELPER_H 30d53106c9SRichard Henderson 31f8da88d7SSong Gao #define DISAS_STOP DISAS_TARGET_0 325b1dedfeSXiaojuan Yang #define DISAS_EXIT DISAS_TARGET_1 335b1dedfeSXiaojuan Yang #define DISAS_EXIT_UPDATE DISAS_TARGET_2 34f8da88d7SSong Gao 3557b4f1acSSong Gao static inline int vec_full_offset(int regno) 3657b4f1acSSong Gao { 3757b4f1acSSong Gao return offsetof(CPULoongArchState, fpr[regno]); 3857b4f1acSSong Gao } 3957b4f1acSSong Gao 40f5ce2c8fSSong Gao static inline int vec_reg_offset(int regno, int index, MemOp mop) 41f5ce2c8fSSong Gao { 42f5ce2c8fSSong Gao const uint8_t size = 1 << mop; 43f5ce2c8fSSong Gao int offs = index * size; 44f5ce2c8fSSong Gao 45f5ce2c8fSSong Gao if (HOST_BIG_ENDIAN && size < 8 ) { 46f5ce2c8fSSong Gao offs ^= (8 - size); 47f5ce2c8fSSong Gao } 48f5ce2c8fSSong Gao 49f5ce2c8fSSong Gao return offs + vec_full_offset(regno); 50f5ce2c8fSSong Gao } 51f5ce2c8fSSong Gao 5257b4f1acSSong Gao static inline void get_vreg64(TCGv_i64 dest, int regno, int index) 5357b4f1acSSong Gao { 54ad75a51eSRichard Henderson tcg_gen_ld_i64(dest, tcg_env, 5557b4f1acSSong Gao offsetof(CPULoongArchState, fpr[regno].vreg.D(index))); 5657b4f1acSSong Gao } 5757b4f1acSSong Gao 5857b4f1acSSong Gao static inline void set_vreg64(TCGv_i64 src, int regno, int index) 5957b4f1acSSong Gao { 60ad75a51eSRichard Henderson tcg_gen_st_i64(src, tcg_env, 6157b4f1acSSong Gao offsetof(CPULoongArchState, fpr[regno].vreg.D(index))); 6257b4f1acSSong Gao } 6357b4f1acSSong Gao 64143d6785SSong Gao static inline int plus_1(DisasContext *ctx, int x) 65143d6785SSong Gao { 66143d6785SSong Gao return x + 1; 67143d6785SSong Gao } 68143d6785SSong Gao 69843b627aSSong Gao static inline int shl_1(DisasContext *ctx, int x) 70843b627aSSong Gao { 71843b627aSSong Gao return x << 1; 72843b627aSSong Gao } 73843b627aSSong Gao 74bb79174dSSong Gao static inline int shl_2(DisasContext *ctx, int x) 75bb79174dSSong Gao { 76bb79174dSSong Gao return x << 2; 77bb79174dSSong Gao } 78bb79174dSSong Gao 79843b627aSSong Gao static inline int shl_3(DisasContext *ctx, int x) 80843b627aSSong Gao { 81843b627aSSong Gao return x << 3; 82843b627aSSong Gao } 83843b627aSSong Gao 84d578ca6cSSong Gao /* 85d578ca6cSSong Gao * LoongArch the upper 32 bits are undefined ("can be any value"). 86d578ca6cSSong Gao * QEMU chooses to nanbox, because it is most likely to show guest bugs early. 87d578ca6cSSong Gao */ 88d578ca6cSSong Gao static void gen_nanbox_s(TCGv_i64 out, TCGv_i64 in) 89d578ca6cSSong Gao { 90d578ca6cSSong Gao tcg_gen_ori_i64(out, in, MAKE_64BIT_MASK(32, 32)); 91d578ca6cSSong Gao } 92d578ca6cSSong Gao 93f8da88d7SSong Gao void generate_exception(DisasContext *ctx, int excp) 94f8da88d7SSong Gao { 95f8da88d7SSong Gao tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next); 96ad75a51eSRichard Henderson gen_helper_raise_exception(tcg_env, tcg_constant_i32(excp)); 97f8da88d7SSong Gao ctx->base.is_jmp = DISAS_NORETURN; 98f8da88d7SSong Gao } 99f8da88d7SSong Gao 100f8da88d7SSong Gao static inline void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) 101f8da88d7SSong Gao { 1027033c0e6SJiajie Chen if (ctx->va32) { 1037033c0e6SJiajie Chen dest = (uint32_t) dest; 1047033c0e6SJiajie Chen } 1057033c0e6SJiajie Chen 106f8da88d7SSong Gao if (translator_use_goto_tb(&ctx->base, dest)) { 107f8da88d7SSong Gao tcg_gen_goto_tb(n); 108f8da88d7SSong Gao tcg_gen_movi_tl(cpu_pc, dest); 109f8da88d7SSong Gao tcg_gen_exit_tb(ctx->base.tb, n); 110f8da88d7SSong Gao } else { 111f8da88d7SSong Gao tcg_gen_movi_tl(cpu_pc, dest); 112f8da88d7SSong Gao tcg_gen_lookup_and_goto_ptr(); 113f8da88d7SSong Gao } 114f8da88d7SSong Gao } 115f8da88d7SSong Gao 116f8da88d7SSong Gao static void loongarch_tr_init_disas_context(DisasContextBase *dcbase, 117f8da88d7SSong Gao CPUState *cs) 118f8da88d7SSong Gao { 119f8da88d7SSong Gao int64_t bound; 120b77af26eSRichard Henderson CPULoongArchState *env = cpu_env(cs); 121f8da88d7SSong Gao DisasContext *ctx = container_of(dcbase, DisasContext, base); 122f8da88d7SSong Gao 123f8da88d7SSong Gao ctx->page_start = ctx->base.pc_first & TARGET_PAGE_MASK; 124c8885b88SRui Wang ctx->plv = ctx->base.tb->flags & HW_FLAGS_PLV_MASK; 125b4bda200SRui Wang if (ctx->base.tb->flags & HW_FLAGS_CRMD_PG) { 126c8885b88SRui Wang ctx->mem_idx = ctx->plv; 127b4bda200SRui Wang } else { 1283f262d25SRichard Henderson ctx->mem_idx = MMU_DA_IDX; 129b4bda200SRui Wang } 130f8da88d7SSong Gao 131f8da88d7SSong Gao /* Bound the number of insns to execute to those left on the page. */ 132f8da88d7SSong Gao bound = -(ctx->base.pc_first | TARGET_PAGE_MASK) / 4; 133f8da88d7SSong Gao ctx->base.max_insns = MIN(ctx->base.max_insns, bound); 134143d6785SSong Gao 13557b4f1acSSong Gao if (FIELD_EX64(env->cpucfg[2], CPUCFG2, LSX)) { 13657b4f1acSSong Gao ctx->vl = LSX_LEN; 13757b4f1acSSong Gao } 13857b4f1acSSong Gao 139269ca39aSSong Gao if (FIELD_EX64(env->cpucfg[2], CPUCFG2, LASX)) { 140269ca39aSSong Gao ctx->vl = LASX_LEN; 141269ca39aSSong Gao } 142269ca39aSSong Gao 14339665820SJiajie Chen ctx->la64 = is_la64(env); 14439665820SJiajie Chen ctx->va32 = (ctx->base.tb->flags & HW_FLAGS_VA32) != 0; 14539665820SJiajie Chen 146143d6785SSong Gao ctx->zero = tcg_constant_tl(0); 147c0c0461eSSong Gao 148c0c0461eSSong Gao ctx->cpucfg1 = env->cpucfg[1]; 14995e2ca24SSong Gao ctx->cpucfg2 = env->cpucfg[2]; 150f8da88d7SSong Gao } 151f8da88d7SSong Gao 152f8da88d7SSong Gao static void loongarch_tr_tb_start(DisasContextBase *dcbase, CPUState *cs) 153f8da88d7SSong Gao { 154f8da88d7SSong Gao } 155f8da88d7SSong Gao 156f8da88d7SSong Gao static void loongarch_tr_insn_start(DisasContextBase *dcbase, CPUState *cs) 157f8da88d7SSong Gao { 158f8da88d7SSong Gao DisasContext *ctx = container_of(dcbase, DisasContext, base); 159f8da88d7SSong Gao 160f8da88d7SSong Gao tcg_gen_insn_start(ctx->base.pc_next); 161f8da88d7SSong Gao } 162f8da88d7SSong Gao 163143d6785SSong Gao /* 164143d6785SSong Gao * Wrappers for getting reg values. 165143d6785SSong Gao * 166143d6785SSong Gao * The $zero register does not have cpu_gpr[0] allocated -- we supply the 167143d6785SSong Gao * constant zero as a source, and an uninitialized sink as destination. 168143d6785SSong Gao * 169143d6785SSong Gao * Further, we may provide an extension for word operations. 170143d6785SSong Gao */ 171143d6785SSong Gao static TCGv gpr_src(DisasContext *ctx, int reg_num, DisasExtend src_ext) 172143d6785SSong Gao { 173143d6785SSong Gao TCGv t; 174143d6785SSong Gao 175143d6785SSong Gao if (reg_num == 0) { 176143d6785SSong Gao return ctx->zero; 177143d6785SSong Gao } 178143d6785SSong Gao 179143d6785SSong Gao switch (src_ext) { 180143d6785SSong Gao case EXT_NONE: 181143d6785SSong Gao return cpu_gpr[reg_num]; 182143d6785SSong Gao case EXT_SIGN: 18360a7e25eSRichard Henderson t = tcg_temp_new(); 184143d6785SSong Gao tcg_gen_ext32s_tl(t, cpu_gpr[reg_num]); 185143d6785SSong Gao return t; 186143d6785SSong Gao case EXT_ZERO: 18760a7e25eSRichard Henderson t = tcg_temp_new(); 188143d6785SSong Gao tcg_gen_ext32u_tl(t, cpu_gpr[reg_num]); 189143d6785SSong Gao return t; 190143d6785SSong Gao } 191143d6785SSong Gao g_assert_not_reached(); 192143d6785SSong Gao } 193143d6785SSong Gao 194143d6785SSong Gao static TCGv gpr_dst(DisasContext *ctx, int reg_num, DisasExtend dst_ext) 195143d6785SSong Gao { 196143d6785SSong Gao if (reg_num == 0 || dst_ext) { 19760a7e25eSRichard Henderson return tcg_temp_new(); 198143d6785SSong Gao } 199143d6785SSong Gao return cpu_gpr[reg_num]; 200143d6785SSong Gao } 201143d6785SSong Gao 202143d6785SSong Gao static void gen_set_gpr(int reg_num, TCGv t, DisasExtend dst_ext) 203143d6785SSong Gao { 204143d6785SSong Gao if (reg_num != 0) { 205143d6785SSong Gao switch (dst_ext) { 206143d6785SSong Gao case EXT_NONE: 207143d6785SSong Gao tcg_gen_mov_tl(cpu_gpr[reg_num], t); 208143d6785SSong Gao break; 209143d6785SSong Gao case EXT_SIGN: 210143d6785SSong Gao tcg_gen_ext32s_tl(cpu_gpr[reg_num], t); 211143d6785SSong Gao break; 212143d6785SSong Gao case EXT_ZERO: 213143d6785SSong Gao tcg_gen_ext32u_tl(cpu_gpr[reg_num], t); 214143d6785SSong Gao break; 215143d6785SSong Gao default: 216143d6785SSong Gao g_assert_not_reached(); 217143d6785SSong Gao } 218143d6785SSong Gao } 219143d6785SSong Gao } 220143d6785SSong Gao 2214854bbbeSSong Gao static TCGv get_fpr(DisasContext *ctx, int reg_num) 2224854bbbeSSong Gao { 2234854bbbeSSong Gao TCGv t = tcg_temp_new(); 224ad75a51eSRichard Henderson tcg_gen_ld_i64(t, tcg_env, 2254854bbbeSSong Gao offsetof(CPULoongArchState, fpr[reg_num].vreg.D(0))); 2264854bbbeSSong Gao return t; 2274854bbbeSSong Gao } 2284854bbbeSSong Gao 2294854bbbeSSong Gao static void set_fpr(int reg_num, TCGv val) 2304854bbbeSSong Gao { 231ad75a51eSRichard Henderson tcg_gen_st_i64(val, tcg_env, 2324854bbbeSSong Gao offsetof(CPULoongArchState, fpr[reg_num].vreg.D(0))); 2334854bbbeSSong Gao } 2344854bbbeSSong Gao 23534423c01SJiajie Chen static TCGv make_address_x(DisasContext *ctx, TCGv base, TCGv addend) 23634423c01SJiajie Chen { 23734423c01SJiajie Chen TCGv temp = NULL; 23834423c01SJiajie Chen 2397033c0e6SJiajie Chen if (addend || ctx->va32) { 24034423c01SJiajie Chen temp = tcg_temp_new(); 2417033c0e6SJiajie Chen } 2427033c0e6SJiajie Chen if (addend) { 24334423c01SJiajie Chen tcg_gen_add_tl(temp, base, addend); 24434423c01SJiajie Chen base = temp; 24534423c01SJiajie Chen } 2467033c0e6SJiajie Chen if (ctx->va32) { 2477033c0e6SJiajie Chen tcg_gen_ext32u_tl(temp, base); 2487033c0e6SJiajie Chen base = temp; 2497033c0e6SJiajie Chen } 25034423c01SJiajie Chen return base; 25134423c01SJiajie Chen } 25234423c01SJiajie Chen 253c5af6628SJiajie Chen static TCGv make_address_i(DisasContext *ctx, TCGv base, target_long ofs) 254c5af6628SJiajie Chen { 255c5af6628SJiajie Chen TCGv addend = ofs ? tcg_constant_tl(ofs) : NULL; 256c5af6628SJiajie Chen return make_address_x(ctx, base, addend); 257c5af6628SJiajie Chen } 258c5af6628SJiajie Chen 2595a7ce25dSJiajie Chen static uint64_t make_address_pc(DisasContext *ctx, uint64_t addr) 2605a7ce25dSJiajie Chen { 2616496269dSJiajie Chen if (ctx->va32) { 2626496269dSJiajie Chen addr = (int32_t)addr; 2636496269dSJiajie Chen } 2645a7ce25dSJiajie Chen return addr; 2655a7ce25dSJiajie Chen } 2665a7ce25dSJiajie Chen 267143d6785SSong Gao #include "decode-insns.c.inc" 268143d6785SSong Gao #include "insn_trans/trans_arith.c.inc" 26963cfcd47SSong Gao #include "insn_trans/trans_shift.c.inc" 270ad08cb3fSSong Gao #include "insn_trans/trans_bit.c.inc" 271bb79174dSSong Gao #include "insn_trans/trans_memory.c.inc" 27294b02d57SSong Gao #include "insn_trans/trans_atomic.c.inc" 2738708a04aSSong Gao #include "insn_trans/trans_extra.c.inc" 274d578ca6cSSong Gao #include "insn_trans/trans_farith.c.inc" 2759b741076SSong Gao #include "insn_trans/trans_fcmp.c.inc" 2767c1f8870SSong Gao #include "insn_trans/trans_fcnv.c.inc" 277b7dabd56SSong Gao #include "insn_trans/trans_fmov.c.inc" 278e616bdfdSSong Gao #include "insn_trans/trans_fmemory.c.inc" 279ee86bd58SSong Gao #include "insn_trans/trans_branch.c.inc" 2805b1dedfeSXiaojuan Yang #include "insn_trans/trans_privileged.c.inc" 2811dc33f26SSong Gao #include "insn_trans/trans_vec.c.inc" 282143d6785SSong Gao 283f8da88d7SSong Gao static void loongarch_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) 284f8da88d7SSong Gao { 285f8da88d7SSong Gao DisasContext *ctx = container_of(dcbase, DisasContext, base); 286f8da88d7SSong Gao 28794956d7bSPhilippe Mathieu-Daudé ctx->opcode = translator_ldl(cpu_env(cs), &ctx->base, ctx->base.pc_next); 288f8da88d7SSong Gao 289f8da88d7SSong Gao if (!decode(ctx, ctx->opcode)) { 290f8da88d7SSong Gao qemu_log_mask(LOG_UNIMP, "Error: unknown opcode. " 291f8da88d7SSong Gao TARGET_FMT_lx ": 0x%x\n", 292f8da88d7SSong Gao ctx->base.pc_next, ctx->opcode); 293f8da88d7SSong Gao generate_exception(ctx, EXCCODE_INE); 294f8da88d7SSong Gao } 295f8da88d7SSong Gao 296f8da88d7SSong Gao ctx->base.pc_next += 4; 2977033c0e6SJiajie Chen 2987033c0e6SJiajie Chen if (ctx->va32) { 2997033c0e6SJiajie Chen ctx->base.pc_next = (uint32_t)ctx->base.pc_next; 3007033c0e6SJiajie Chen } 301f8da88d7SSong Gao } 302f8da88d7SSong Gao 303f8da88d7SSong Gao static void loongarch_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) 304f8da88d7SSong Gao { 305f8da88d7SSong Gao DisasContext *ctx = container_of(dcbase, DisasContext, base); 306f8da88d7SSong Gao 307f8da88d7SSong Gao switch (ctx->base.is_jmp) { 308f8da88d7SSong Gao case DISAS_STOP: 309f8da88d7SSong Gao tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next); 310f8da88d7SSong Gao tcg_gen_lookup_and_goto_ptr(); 311f8da88d7SSong Gao break; 312f8da88d7SSong Gao case DISAS_TOO_MANY: 313f8da88d7SSong Gao gen_goto_tb(ctx, 0, ctx->base.pc_next); 314f8da88d7SSong Gao break; 315f8da88d7SSong Gao case DISAS_NORETURN: 316f8da88d7SSong Gao break; 3175b1dedfeSXiaojuan Yang case DISAS_EXIT_UPDATE: 3185b1dedfeSXiaojuan Yang tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next); 3195b1dedfeSXiaojuan Yang QEMU_FALLTHROUGH; 3205b1dedfeSXiaojuan Yang case DISAS_EXIT: 3215b1dedfeSXiaojuan Yang tcg_gen_exit_tb(NULL, 0); 3225b1dedfeSXiaojuan Yang break; 323f8da88d7SSong Gao default: 324f8da88d7SSong Gao g_assert_not_reached(); 325f8da88d7SSong Gao } 326f8da88d7SSong Gao } 327f8da88d7SSong Gao 328f8da88d7SSong Gao static const TranslatorOps loongarch_tr_ops = { 329f8da88d7SSong Gao .init_disas_context = loongarch_tr_init_disas_context, 330f8da88d7SSong Gao .tb_start = loongarch_tr_tb_start, 331f8da88d7SSong Gao .insn_start = loongarch_tr_insn_start, 332f8da88d7SSong Gao .translate_insn = loongarch_tr_translate_insn, 333f8da88d7SSong Gao .tb_stop = loongarch_tr_tb_stop, 334f8da88d7SSong Gao }; 335f8da88d7SSong Gao 336*e4a8e093SRichard Henderson void loongarch_translate_code(CPUState *cs, TranslationBlock *tb, 337*e4a8e093SRichard Henderson int *max_insns, vaddr pc, void *host_pc) 338f8da88d7SSong Gao { 339f8da88d7SSong Gao DisasContext ctx; 340f8da88d7SSong Gao 341306c8721SRichard Henderson translator_loop(cs, tb, max_insns, pc, host_pc, 342306c8721SRichard Henderson &loongarch_tr_ops, &ctx.base); 343f8da88d7SSong Gao } 344f8da88d7SSong Gao 345f8da88d7SSong Gao void loongarch_translate_init(void) 346f8da88d7SSong Gao { 347f8da88d7SSong Gao int i; 348f8da88d7SSong Gao 349f8da88d7SSong Gao cpu_gpr[0] = NULL; 350f8da88d7SSong Gao for (i = 1; i < 32; i++) { 351ad75a51eSRichard Henderson cpu_gpr[i] = tcg_global_mem_new(tcg_env, 352f8da88d7SSong Gao offsetof(CPULoongArchState, gpr[i]), 353f8da88d7SSong Gao regnames[i]); 354f8da88d7SSong Gao } 355f8da88d7SSong Gao 356ad75a51eSRichard Henderson cpu_pc = tcg_global_mem_new(tcg_env, offsetof(CPULoongArchState, pc), "pc"); 357ad75a51eSRichard Henderson cpu_lladdr = tcg_global_mem_new(tcg_env, 358f8da88d7SSong Gao offsetof(CPULoongArchState, lladdr), "lladdr"); 359ad75a51eSRichard Henderson cpu_llval = tcg_global_mem_new(tcg_env, 360f8da88d7SSong Gao offsetof(CPULoongArchState, llval), "llval"); 361f8da88d7SSong Gao } 362