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" 21f8da88d7SSong Gao 22f8da88d7SSong Gao /* Global register indices */ 23f8da88d7SSong Gao TCGv cpu_gpr[32], cpu_pc; 24f8da88d7SSong Gao static TCGv cpu_lladdr, cpu_llval; 25f8da88d7SSong Gao 26d53106c9SRichard Henderson #define HELPER_H "helper.h" 27d53106c9SRichard Henderson #include "exec/helper-info.c.inc" 28d53106c9SRichard Henderson #undef HELPER_H 29d53106c9SRichard Henderson 30f8da88d7SSong Gao #define DISAS_STOP DISAS_TARGET_0 315b1dedfeSXiaojuan Yang #define DISAS_EXIT DISAS_TARGET_1 325b1dedfeSXiaojuan Yang #define DISAS_EXIT_UPDATE DISAS_TARGET_2 33f8da88d7SSong Gao 3457b4f1acSSong Gao static inline int vec_full_offset(int regno) 3557b4f1acSSong Gao { 3657b4f1acSSong Gao return offsetof(CPULoongArchState, fpr[regno]); 3757b4f1acSSong Gao } 3857b4f1acSSong Gao 3957b4f1acSSong Gao static inline void get_vreg64(TCGv_i64 dest, int regno, int index) 4057b4f1acSSong Gao { 4157b4f1acSSong Gao tcg_gen_ld_i64(dest, cpu_env, 4257b4f1acSSong Gao offsetof(CPULoongArchState, fpr[regno].vreg.D(index))); 4357b4f1acSSong Gao } 4457b4f1acSSong Gao 4557b4f1acSSong Gao static inline void set_vreg64(TCGv_i64 src, int regno, int index) 4657b4f1acSSong Gao { 4757b4f1acSSong Gao tcg_gen_st_i64(src, cpu_env, 4857b4f1acSSong Gao offsetof(CPULoongArchState, fpr[regno].vreg.D(index))); 4957b4f1acSSong Gao } 5057b4f1acSSong Gao 51143d6785SSong Gao static inline int plus_1(DisasContext *ctx, int x) 52143d6785SSong Gao { 53143d6785SSong Gao return x + 1; 54143d6785SSong Gao } 55143d6785SSong Gao 56843b627aSSong Gao static inline int shl_1(DisasContext *ctx, int x) 57843b627aSSong Gao { 58843b627aSSong Gao return x << 1; 59843b627aSSong Gao } 60843b627aSSong Gao 61bb79174dSSong Gao static inline int shl_2(DisasContext *ctx, int x) 62bb79174dSSong Gao { 63bb79174dSSong Gao return x << 2; 64bb79174dSSong Gao } 65bb79174dSSong Gao 66843b627aSSong Gao static inline int shl_3(DisasContext *ctx, int x) 67843b627aSSong Gao { 68843b627aSSong Gao return x << 3; 69843b627aSSong Gao } 70843b627aSSong Gao 71d578ca6cSSong Gao /* 72d578ca6cSSong Gao * LoongArch the upper 32 bits are undefined ("can be any value"). 73d578ca6cSSong Gao * QEMU chooses to nanbox, because it is most likely to show guest bugs early. 74d578ca6cSSong Gao */ 75d578ca6cSSong Gao static void gen_nanbox_s(TCGv_i64 out, TCGv_i64 in) 76d578ca6cSSong Gao { 77d578ca6cSSong Gao tcg_gen_ori_i64(out, in, MAKE_64BIT_MASK(32, 32)); 78d578ca6cSSong Gao } 79d578ca6cSSong Gao 80f8da88d7SSong Gao void generate_exception(DisasContext *ctx, int excp) 81f8da88d7SSong Gao { 82f8da88d7SSong Gao tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next); 83f8da88d7SSong Gao gen_helper_raise_exception(cpu_env, tcg_constant_i32(excp)); 84f8da88d7SSong Gao ctx->base.is_jmp = DISAS_NORETURN; 85f8da88d7SSong Gao } 86f8da88d7SSong Gao 87f8da88d7SSong Gao static inline void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) 88f8da88d7SSong Gao { 897033c0e6SJiajie Chen if (ctx->va32) { 907033c0e6SJiajie Chen dest = (uint32_t) dest; 917033c0e6SJiajie Chen } 927033c0e6SJiajie Chen 93f8da88d7SSong Gao if (translator_use_goto_tb(&ctx->base, dest)) { 94f8da88d7SSong Gao tcg_gen_goto_tb(n); 95f8da88d7SSong Gao tcg_gen_movi_tl(cpu_pc, dest); 96f8da88d7SSong Gao tcg_gen_exit_tb(ctx->base.tb, n); 97f8da88d7SSong Gao } else { 98f8da88d7SSong Gao tcg_gen_movi_tl(cpu_pc, dest); 99f8da88d7SSong Gao tcg_gen_lookup_and_goto_ptr(); 100f8da88d7SSong Gao } 101f8da88d7SSong Gao } 102f8da88d7SSong Gao 103f8da88d7SSong Gao static void loongarch_tr_init_disas_context(DisasContextBase *dcbase, 104f8da88d7SSong Gao CPUState *cs) 105f8da88d7SSong Gao { 106f8da88d7SSong Gao int64_t bound; 10757b4f1acSSong Gao CPULoongArchState *env = cs->env_ptr; 108f8da88d7SSong Gao DisasContext *ctx = container_of(dcbase, DisasContext, base); 109f8da88d7SSong Gao 110f8da88d7SSong Gao ctx->page_start = ctx->base.pc_first & TARGET_PAGE_MASK; 111c8885b88SRui Wang ctx->plv = ctx->base.tb->flags & HW_FLAGS_PLV_MASK; 112b4bda200SRui Wang if (ctx->base.tb->flags & HW_FLAGS_CRMD_PG) { 113c8885b88SRui Wang ctx->mem_idx = ctx->plv; 114b4bda200SRui Wang } else { 115c8885b88SRui Wang ctx->mem_idx = MMU_IDX_DA; 116b4bda200SRui Wang } 117f8da88d7SSong Gao 118f8da88d7SSong Gao /* Bound the number of insns to execute to those left on the page. */ 119f8da88d7SSong Gao bound = -(ctx->base.pc_first | TARGET_PAGE_MASK) / 4; 120f8da88d7SSong Gao ctx->base.max_insns = MIN(ctx->base.max_insns, bound); 121143d6785SSong Gao 12257b4f1acSSong Gao if (FIELD_EX64(env->cpucfg[2], CPUCFG2, LSX)) { 12357b4f1acSSong Gao ctx->vl = LSX_LEN; 12457b4f1acSSong Gao } 12557b4f1acSSong Gao 12639665820SJiajie Chen ctx->la64 = is_la64(env); 12739665820SJiajie Chen ctx->va32 = (ctx->base.tb->flags & HW_FLAGS_VA32) != 0; 12839665820SJiajie Chen 129143d6785SSong Gao ctx->zero = tcg_constant_tl(0); 130c0c0461eSSong Gao 131c0c0461eSSong Gao ctx->cpucfg1 = env->cpucfg[1]; 13295e2ca24SSong Gao ctx->cpucfg2 = env->cpucfg[2]; 133f8da88d7SSong Gao } 134f8da88d7SSong Gao 135f8da88d7SSong Gao static void loongarch_tr_tb_start(DisasContextBase *dcbase, CPUState *cs) 136f8da88d7SSong Gao { 137f8da88d7SSong Gao } 138f8da88d7SSong Gao 139f8da88d7SSong Gao static void loongarch_tr_insn_start(DisasContextBase *dcbase, CPUState *cs) 140f8da88d7SSong Gao { 141f8da88d7SSong Gao DisasContext *ctx = container_of(dcbase, DisasContext, base); 142f8da88d7SSong Gao 143f8da88d7SSong Gao tcg_gen_insn_start(ctx->base.pc_next); 144f8da88d7SSong Gao } 145f8da88d7SSong Gao 146143d6785SSong Gao /* 147143d6785SSong Gao * Wrappers for getting reg values. 148143d6785SSong Gao * 149143d6785SSong Gao * The $zero register does not have cpu_gpr[0] allocated -- we supply the 150143d6785SSong Gao * constant zero as a source, and an uninitialized sink as destination. 151143d6785SSong Gao * 152143d6785SSong Gao * Further, we may provide an extension for word operations. 153143d6785SSong Gao */ 154143d6785SSong Gao static TCGv gpr_src(DisasContext *ctx, int reg_num, DisasExtend src_ext) 155143d6785SSong Gao { 156143d6785SSong Gao TCGv t; 157143d6785SSong Gao 158143d6785SSong Gao if (reg_num == 0) { 159143d6785SSong Gao return ctx->zero; 160143d6785SSong Gao } 161143d6785SSong Gao 162143d6785SSong Gao switch (src_ext) { 163143d6785SSong Gao case EXT_NONE: 164143d6785SSong Gao return cpu_gpr[reg_num]; 165143d6785SSong Gao case EXT_SIGN: 16660a7e25eSRichard Henderson t = tcg_temp_new(); 167143d6785SSong Gao tcg_gen_ext32s_tl(t, cpu_gpr[reg_num]); 168143d6785SSong Gao return t; 169143d6785SSong Gao case EXT_ZERO: 17060a7e25eSRichard Henderson t = tcg_temp_new(); 171143d6785SSong Gao tcg_gen_ext32u_tl(t, cpu_gpr[reg_num]); 172143d6785SSong Gao return t; 173143d6785SSong Gao } 174143d6785SSong Gao g_assert_not_reached(); 175143d6785SSong Gao } 176143d6785SSong Gao 177143d6785SSong Gao static TCGv gpr_dst(DisasContext *ctx, int reg_num, DisasExtend dst_ext) 178143d6785SSong Gao { 179143d6785SSong Gao if (reg_num == 0 || dst_ext) { 18060a7e25eSRichard Henderson return tcg_temp_new(); 181143d6785SSong Gao } 182143d6785SSong Gao return cpu_gpr[reg_num]; 183143d6785SSong Gao } 184143d6785SSong Gao 185143d6785SSong Gao static void gen_set_gpr(int reg_num, TCGv t, DisasExtend dst_ext) 186143d6785SSong Gao { 187143d6785SSong Gao if (reg_num != 0) { 188143d6785SSong Gao switch (dst_ext) { 189143d6785SSong Gao case EXT_NONE: 190143d6785SSong Gao tcg_gen_mov_tl(cpu_gpr[reg_num], t); 191143d6785SSong Gao break; 192143d6785SSong Gao case EXT_SIGN: 193143d6785SSong Gao tcg_gen_ext32s_tl(cpu_gpr[reg_num], t); 194143d6785SSong Gao break; 195143d6785SSong Gao case EXT_ZERO: 196143d6785SSong Gao tcg_gen_ext32u_tl(cpu_gpr[reg_num], t); 197143d6785SSong Gao break; 198143d6785SSong Gao default: 199143d6785SSong Gao g_assert_not_reached(); 200143d6785SSong Gao } 201143d6785SSong Gao } 202143d6785SSong Gao } 203143d6785SSong Gao 2044854bbbeSSong Gao static TCGv get_fpr(DisasContext *ctx, int reg_num) 2054854bbbeSSong Gao { 2064854bbbeSSong Gao TCGv t = tcg_temp_new(); 2074854bbbeSSong Gao tcg_gen_ld_i64(t, cpu_env, 2084854bbbeSSong Gao offsetof(CPULoongArchState, fpr[reg_num].vreg.D(0))); 2094854bbbeSSong Gao return t; 2104854bbbeSSong Gao } 2114854bbbeSSong Gao 2124854bbbeSSong Gao static void set_fpr(int reg_num, TCGv val) 2134854bbbeSSong Gao { 2144854bbbeSSong Gao tcg_gen_st_i64(val, cpu_env, 2154854bbbeSSong Gao offsetof(CPULoongArchState, fpr[reg_num].vreg.D(0))); 2164854bbbeSSong Gao } 2174854bbbeSSong Gao 21834423c01SJiajie Chen static TCGv make_address_x(DisasContext *ctx, TCGv base, TCGv addend) 21934423c01SJiajie Chen { 22034423c01SJiajie Chen TCGv temp = NULL; 22134423c01SJiajie Chen 2227033c0e6SJiajie Chen if (addend || ctx->va32) { 22334423c01SJiajie Chen temp = tcg_temp_new(); 2247033c0e6SJiajie Chen } 2257033c0e6SJiajie Chen if (addend) { 22634423c01SJiajie Chen tcg_gen_add_tl(temp, base, addend); 22734423c01SJiajie Chen base = temp; 22834423c01SJiajie Chen } 2297033c0e6SJiajie Chen if (ctx->va32) { 2307033c0e6SJiajie Chen tcg_gen_ext32u_tl(temp, base); 2317033c0e6SJiajie Chen base = temp; 2327033c0e6SJiajie Chen } 23334423c01SJiajie Chen return base; 23434423c01SJiajie Chen } 23534423c01SJiajie Chen 236c5af6628SJiajie Chen static TCGv make_address_i(DisasContext *ctx, TCGv base, target_long ofs) 237c5af6628SJiajie Chen { 238c5af6628SJiajie Chen TCGv addend = ofs ? tcg_constant_tl(ofs) : NULL; 239c5af6628SJiajie Chen return make_address_x(ctx, base, addend); 240c5af6628SJiajie Chen } 241c5af6628SJiajie Chen 2425a7ce25dSJiajie Chen static uint64_t make_address_pc(DisasContext *ctx, uint64_t addr) 2435a7ce25dSJiajie Chen { 2446496269dSJiajie Chen if (ctx->va32) { 2456496269dSJiajie Chen addr = (int32_t)addr; 2466496269dSJiajie Chen } 2475a7ce25dSJiajie Chen return addr; 2485a7ce25dSJiajie Chen } 2495a7ce25dSJiajie Chen 250143d6785SSong Gao #include "decode-insns.c.inc" 251143d6785SSong Gao #include "insn_trans/trans_arith.c.inc" 25263cfcd47SSong Gao #include "insn_trans/trans_shift.c.inc" 253ad08cb3fSSong Gao #include "insn_trans/trans_bit.c.inc" 254bb79174dSSong Gao #include "insn_trans/trans_memory.c.inc" 25594b02d57SSong Gao #include "insn_trans/trans_atomic.c.inc" 2568708a04aSSong Gao #include "insn_trans/trans_extra.c.inc" 257d578ca6cSSong Gao #include "insn_trans/trans_farith.c.inc" 2589b741076SSong Gao #include "insn_trans/trans_fcmp.c.inc" 2597c1f8870SSong Gao #include "insn_trans/trans_fcnv.c.inc" 260b7dabd56SSong Gao #include "insn_trans/trans_fmov.c.inc" 261e616bdfdSSong Gao #include "insn_trans/trans_fmemory.c.inc" 262ee86bd58SSong Gao #include "insn_trans/trans_branch.c.inc" 2635b1dedfeSXiaojuan Yang #include "insn_trans/trans_privileged.c.inc" 2641dc33f26SSong Gao #include "insn_trans/trans_vec.c.inc" 265143d6785SSong Gao 266f8da88d7SSong Gao static void loongarch_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) 267f8da88d7SSong Gao { 268f8da88d7SSong Gao CPULoongArchState *env = cs->env_ptr; 269f8da88d7SSong Gao DisasContext *ctx = container_of(dcbase, DisasContext, base); 270f8da88d7SSong Gao 271ec28dd6cStanhongze ctx->opcode = translator_ldl(env, &ctx->base, ctx->base.pc_next); 272f8da88d7SSong Gao 273f8da88d7SSong Gao if (!decode(ctx, ctx->opcode)) { 274f8da88d7SSong Gao qemu_log_mask(LOG_UNIMP, "Error: unknown opcode. " 275f8da88d7SSong Gao TARGET_FMT_lx ": 0x%x\n", 276f8da88d7SSong Gao ctx->base.pc_next, ctx->opcode); 277f8da88d7SSong Gao generate_exception(ctx, EXCCODE_INE); 278f8da88d7SSong Gao } 279f8da88d7SSong Gao 280f8da88d7SSong Gao ctx->base.pc_next += 4; 2817033c0e6SJiajie Chen 2827033c0e6SJiajie Chen if (ctx->va32) { 2837033c0e6SJiajie Chen ctx->base.pc_next = (uint32_t)ctx->base.pc_next; 2847033c0e6SJiajie Chen } 285f8da88d7SSong Gao } 286f8da88d7SSong Gao 287f8da88d7SSong Gao static void loongarch_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) 288f8da88d7SSong Gao { 289f8da88d7SSong Gao DisasContext *ctx = container_of(dcbase, DisasContext, base); 290f8da88d7SSong Gao 291f8da88d7SSong Gao switch (ctx->base.is_jmp) { 292f8da88d7SSong Gao case DISAS_STOP: 293f8da88d7SSong Gao tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next); 294f8da88d7SSong Gao tcg_gen_lookup_and_goto_ptr(); 295f8da88d7SSong Gao break; 296f8da88d7SSong Gao case DISAS_TOO_MANY: 297f8da88d7SSong Gao gen_goto_tb(ctx, 0, ctx->base.pc_next); 298f8da88d7SSong Gao break; 299f8da88d7SSong Gao case DISAS_NORETURN: 300f8da88d7SSong Gao break; 3015b1dedfeSXiaojuan Yang case DISAS_EXIT_UPDATE: 3025b1dedfeSXiaojuan Yang tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next); 3035b1dedfeSXiaojuan Yang QEMU_FALLTHROUGH; 3045b1dedfeSXiaojuan Yang case DISAS_EXIT: 3055b1dedfeSXiaojuan Yang tcg_gen_exit_tb(NULL, 0); 3065b1dedfeSXiaojuan Yang break; 307f8da88d7SSong Gao default: 308f8da88d7SSong Gao g_assert_not_reached(); 309f8da88d7SSong Gao } 310f8da88d7SSong Gao } 311f8da88d7SSong Gao 312f8da88d7SSong Gao static void loongarch_tr_disas_log(const DisasContextBase *dcbase, 313f8da88d7SSong Gao CPUState *cpu, FILE *logfile) 314f8da88d7SSong Gao { 315f8da88d7SSong Gao qemu_log("IN: %s\n", lookup_symbol(dcbase->pc_first)); 316f8da88d7SSong Gao target_disas(logfile, cpu, dcbase->pc_first, dcbase->tb->size); 317f8da88d7SSong Gao } 318f8da88d7SSong Gao 319f8da88d7SSong Gao static const TranslatorOps loongarch_tr_ops = { 320f8da88d7SSong Gao .init_disas_context = loongarch_tr_init_disas_context, 321f8da88d7SSong Gao .tb_start = loongarch_tr_tb_start, 322f8da88d7SSong Gao .insn_start = loongarch_tr_insn_start, 323f8da88d7SSong Gao .translate_insn = loongarch_tr_translate_insn, 324f8da88d7SSong Gao .tb_stop = loongarch_tr_tb_stop, 325f8da88d7SSong Gao .disas_log = loongarch_tr_disas_log, 326f8da88d7SSong Gao }; 327f8da88d7SSong Gao 328597f9b2dSRichard Henderson void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, 329306c8721SRichard Henderson target_ulong pc, void *host_pc) 330f8da88d7SSong Gao { 331f8da88d7SSong Gao DisasContext ctx; 332f8da88d7SSong Gao 333306c8721SRichard Henderson translator_loop(cs, tb, max_insns, pc, host_pc, 334306c8721SRichard Henderson &loongarch_tr_ops, &ctx.base); 335f8da88d7SSong Gao } 336f8da88d7SSong Gao 337f8da88d7SSong Gao void loongarch_translate_init(void) 338f8da88d7SSong Gao { 339f8da88d7SSong Gao int i; 340f8da88d7SSong Gao 341f8da88d7SSong Gao cpu_gpr[0] = NULL; 342f8da88d7SSong Gao for (i = 1; i < 32; i++) { 343f8da88d7SSong Gao cpu_gpr[i] = tcg_global_mem_new(cpu_env, 344f8da88d7SSong Gao offsetof(CPULoongArchState, gpr[i]), 345f8da88d7SSong Gao regnames[i]); 346f8da88d7SSong Gao } 347f8da88d7SSong Gao 348f8da88d7SSong Gao cpu_pc = tcg_global_mem_new(cpu_env, offsetof(CPULoongArchState, pc), "pc"); 349f8da88d7SSong Gao cpu_lladdr = tcg_global_mem_new(cpu_env, 350f8da88d7SSong Gao offsetof(CPULoongArchState, lladdr), "lladdr"); 351f8da88d7SSong Gao cpu_llval = tcg_global_mem_new(cpu_env, 352f8da88d7SSong Gao offsetof(CPULoongArchState, llval), "llval"); 353f8da88d7SSong Gao } 354