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 { 89f8da88d7SSong Gao if (translator_use_goto_tb(&ctx->base, dest)) { 90f8da88d7SSong Gao tcg_gen_goto_tb(n); 91f8da88d7SSong Gao tcg_gen_movi_tl(cpu_pc, dest); 92f8da88d7SSong Gao tcg_gen_exit_tb(ctx->base.tb, n); 93f8da88d7SSong Gao } else { 94f8da88d7SSong Gao tcg_gen_movi_tl(cpu_pc, dest); 95f8da88d7SSong Gao tcg_gen_lookup_and_goto_ptr(); 96f8da88d7SSong Gao } 97f8da88d7SSong Gao } 98f8da88d7SSong Gao 99f8da88d7SSong Gao static void loongarch_tr_init_disas_context(DisasContextBase *dcbase, 100f8da88d7SSong Gao CPUState *cs) 101f8da88d7SSong Gao { 102f8da88d7SSong Gao int64_t bound; 10357b4f1acSSong Gao CPULoongArchState *env = cs->env_ptr; 104f8da88d7SSong Gao DisasContext *ctx = container_of(dcbase, DisasContext, base); 105f8da88d7SSong Gao 106f8da88d7SSong Gao ctx->page_start = ctx->base.pc_first & TARGET_PAGE_MASK; 107c8885b88SRui Wang ctx->plv = ctx->base.tb->flags & HW_FLAGS_PLV_MASK; 108b4bda200SRui Wang if (ctx->base.tb->flags & HW_FLAGS_CRMD_PG) { 109c8885b88SRui Wang ctx->mem_idx = ctx->plv; 110b4bda200SRui Wang } else { 111c8885b88SRui Wang ctx->mem_idx = MMU_IDX_DA; 112b4bda200SRui Wang } 113f8da88d7SSong Gao 114f8da88d7SSong Gao /* Bound the number of insns to execute to those left on the page. */ 115f8da88d7SSong Gao bound = -(ctx->base.pc_first | TARGET_PAGE_MASK) / 4; 116f8da88d7SSong Gao ctx->base.max_insns = MIN(ctx->base.max_insns, bound); 117143d6785SSong Gao 11857b4f1acSSong Gao if (FIELD_EX64(env->cpucfg[2], CPUCFG2, LSX)) { 11957b4f1acSSong Gao ctx->vl = LSX_LEN; 12057b4f1acSSong Gao } 12157b4f1acSSong Gao 122143d6785SSong Gao ctx->zero = tcg_constant_tl(0); 123f8da88d7SSong Gao } 124f8da88d7SSong Gao 125f8da88d7SSong Gao static void loongarch_tr_tb_start(DisasContextBase *dcbase, CPUState *cs) 126f8da88d7SSong Gao { 127f8da88d7SSong Gao } 128f8da88d7SSong Gao 129f8da88d7SSong Gao static void loongarch_tr_insn_start(DisasContextBase *dcbase, CPUState *cs) 130f8da88d7SSong Gao { 131f8da88d7SSong Gao DisasContext *ctx = container_of(dcbase, DisasContext, base); 132f8da88d7SSong Gao 133f8da88d7SSong Gao tcg_gen_insn_start(ctx->base.pc_next); 134f8da88d7SSong Gao } 135f8da88d7SSong Gao 136143d6785SSong Gao /* 137143d6785SSong Gao * Wrappers for getting reg values. 138143d6785SSong Gao * 139143d6785SSong Gao * The $zero register does not have cpu_gpr[0] allocated -- we supply the 140143d6785SSong Gao * constant zero as a source, and an uninitialized sink as destination. 141143d6785SSong Gao * 142143d6785SSong Gao * Further, we may provide an extension for word operations. 143143d6785SSong Gao */ 144143d6785SSong Gao static TCGv gpr_src(DisasContext *ctx, int reg_num, DisasExtend src_ext) 145143d6785SSong Gao { 146143d6785SSong Gao TCGv t; 147143d6785SSong Gao 148143d6785SSong Gao if (reg_num == 0) { 149143d6785SSong Gao return ctx->zero; 150143d6785SSong Gao } 151143d6785SSong Gao 152143d6785SSong Gao switch (src_ext) { 153143d6785SSong Gao case EXT_NONE: 154143d6785SSong Gao return cpu_gpr[reg_num]; 155143d6785SSong Gao case EXT_SIGN: 15660a7e25eSRichard Henderson t = tcg_temp_new(); 157143d6785SSong Gao tcg_gen_ext32s_tl(t, cpu_gpr[reg_num]); 158143d6785SSong Gao return t; 159143d6785SSong Gao case EXT_ZERO: 16060a7e25eSRichard Henderson t = tcg_temp_new(); 161143d6785SSong Gao tcg_gen_ext32u_tl(t, cpu_gpr[reg_num]); 162143d6785SSong Gao return t; 163143d6785SSong Gao } 164143d6785SSong Gao g_assert_not_reached(); 165143d6785SSong Gao } 166143d6785SSong Gao 167143d6785SSong Gao static TCGv gpr_dst(DisasContext *ctx, int reg_num, DisasExtend dst_ext) 168143d6785SSong Gao { 169143d6785SSong Gao if (reg_num == 0 || dst_ext) { 17060a7e25eSRichard Henderson return tcg_temp_new(); 171143d6785SSong Gao } 172143d6785SSong Gao return cpu_gpr[reg_num]; 173143d6785SSong Gao } 174143d6785SSong Gao 175143d6785SSong Gao static void gen_set_gpr(int reg_num, TCGv t, DisasExtend dst_ext) 176143d6785SSong Gao { 177143d6785SSong Gao if (reg_num != 0) { 178143d6785SSong Gao switch (dst_ext) { 179143d6785SSong Gao case EXT_NONE: 180143d6785SSong Gao tcg_gen_mov_tl(cpu_gpr[reg_num], t); 181143d6785SSong Gao break; 182143d6785SSong Gao case EXT_SIGN: 183143d6785SSong Gao tcg_gen_ext32s_tl(cpu_gpr[reg_num], t); 184143d6785SSong Gao break; 185143d6785SSong Gao case EXT_ZERO: 186143d6785SSong Gao tcg_gen_ext32u_tl(cpu_gpr[reg_num], t); 187143d6785SSong Gao break; 188143d6785SSong Gao default: 189143d6785SSong Gao g_assert_not_reached(); 190143d6785SSong Gao } 191143d6785SSong Gao } 192143d6785SSong Gao } 193143d6785SSong Gao 1944854bbbeSSong Gao static TCGv get_fpr(DisasContext *ctx, int reg_num) 1954854bbbeSSong Gao { 1964854bbbeSSong Gao TCGv t = tcg_temp_new(); 1974854bbbeSSong Gao tcg_gen_ld_i64(t, cpu_env, 1984854bbbeSSong Gao offsetof(CPULoongArchState, fpr[reg_num].vreg.D(0))); 1994854bbbeSSong Gao return t; 2004854bbbeSSong Gao } 2014854bbbeSSong Gao 2024854bbbeSSong Gao static void set_fpr(int reg_num, TCGv val) 2034854bbbeSSong Gao { 2044854bbbeSSong Gao tcg_gen_st_i64(val, cpu_env, 2054854bbbeSSong Gao offsetof(CPULoongArchState, fpr[reg_num].vreg.D(0))); 2064854bbbeSSong Gao } 2074854bbbeSSong Gao 208143d6785SSong Gao #include "decode-insns.c.inc" 209143d6785SSong Gao #include "insn_trans/trans_arith.c.inc" 21063cfcd47SSong Gao #include "insn_trans/trans_shift.c.inc" 211ad08cb3fSSong Gao #include "insn_trans/trans_bit.c.inc" 212bb79174dSSong Gao #include "insn_trans/trans_memory.c.inc" 21394b02d57SSong Gao #include "insn_trans/trans_atomic.c.inc" 2148708a04aSSong Gao #include "insn_trans/trans_extra.c.inc" 215d578ca6cSSong Gao #include "insn_trans/trans_farith.c.inc" 2169b741076SSong Gao #include "insn_trans/trans_fcmp.c.inc" 2177c1f8870SSong Gao #include "insn_trans/trans_fcnv.c.inc" 218b7dabd56SSong Gao #include "insn_trans/trans_fmov.c.inc" 219e616bdfdSSong Gao #include "insn_trans/trans_fmemory.c.inc" 220ee86bd58SSong Gao #include "insn_trans/trans_branch.c.inc" 2215b1dedfeSXiaojuan Yang #include "insn_trans/trans_privileged.c.inc" 222a0c9400aSSong Gao #include "insn_trans/trans_lsx.c.inc" 223143d6785SSong Gao 224f8da88d7SSong Gao static void loongarch_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) 225f8da88d7SSong Gao { 226f8da88d7SSong Gao CPULoongArchState *env = cs->env_ptr; 227f8da88d7SSong Gao DisasContext *ctx = container_of(dcbase, DisasContext, base); 228f8da88d7SSong Gao 229ec28dd6cStanhongze ctx->opcode = translator_ldl(env, &ctx->base, ctx->base.pc_next); 230f8da88d7SSong Gao 231f8da88d7SSong Gao if (!decode(ctx, ctx->opcode)) { 232f8da88d7SSong Gao qemu_log_mask(LOG_UNIMP, "Error: unknown opcode. " 233f8da88d7SSong Gao TARGET_FMT_lx ": 0x%x\n", 234f8da88d7SSong Gao ctx->base.pc_next, ctx->opcode); 235f8da88d7SSong Gao generate_exception(ctx, EXCCODE_INE); 236f8da88d7SSong Gao } 237f8da88d7SSong Gao 238f8da88d7SSong Gao ctx->base.pc_next += 4; 239f8da88d7SSong Gao } 240f8da88d7SSong Gao 241f8da88d7SSong Gao static void loongarch_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) 242f8da88d7SSong Gao { 243f8da88d7SSong Gao DisasContext *ctx = container_of(dcbase, DisasContext, base); 244f8da88d7SSong Gao 245f8da88d7SSong Gao switch (ctx->base.is_jmp) { 246f8da88d7SSong Gao case DISAS_STOP: 247f8da88d7SSong Gao tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next); 248f8da88d7SSong Gao tcg_gen_lookup_and_goto_ptr(); 249f8da88d7SSong Gao break; 250f8da88d7SSong Gao case DISAS_TOO_MANY: 251f8da88d7SSong Gao gen_goto_tb(ctx, 0, ctx->base.pc_next); 252f8da88d7SSong Gao break; 253f8da88d7SSong Gao case DISAS_NORETURN: 254f8da88d7SSong Gao break; 2555b1dedfeSXiaojuan Yang case DISAS_EXIT_UPDATE: 2565b1dedfeSXiaojuan Yang tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next); 2575b1dedfeSXiaojuan Yang QEMU_FALLTHROUGH; 2585b1dedfeSXiaojuan Yang case DISAS_EXIT: 2595b1dedfeSXiaojuan Yang tcg_gen_exit_tb(NULL, 0); 2605b1dedfeSXiaojuan Yang break; 261f8da88d7SSong Gao default: 262f8da88d7SSong Gao g_assert_not_reached(); 263f8da88d7SSong Gao } 264f8da88d7SSong Gao } 265f8da88d7SSong Gao 266f8da88d7SSong Gao static void loongarch_tr_disas_log(const DisasContextBase *dcbase, 267f8da88d7SSong Gao CPUState *cpu, FILE *logfile) 268f8da88d7SSong Gao { 269f8da88d7SSong Gao qemu_log("IN: %s\n", lookup_symbol(dcbase->pc_first)); 270f8da88d7SSong Gao target_disas(logfile, cpu, dcbase->pc_first, dcbase->tb->size); 271f8da88d7SSong Gao } 272f8da88d7SSong Gao 273f8da88d7SSong Gao static const TranslatorOps loongarch_tr_ops = { 274f8da88d7SSong Gao .init_disas_context = loongarch_tr_init_disas_context, 275f8da88d7SSong Gao .tb_start = loongarch_tr_tb_start, 276f8da88d7SSong Gao .insn_start = loongarch_tr_insn_start, 277f8da88d7SSong Gao .translate_insn = loongarch_tr_translate_insn, 278f8da88d7SSong Gao .tb_stop = loongarch_tr_tb_stop, 279f8da88d7SSong Gao .disas_log = loongarch_tr_disas_log, 280f8da88d7SSong Gao }; 281f8da88d7SSong Gao 282597f9b2dSRichard Henderson void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, 283306c8721SRichard Henderson target_ulong pc, void *host_pc) 284f8da88d7SSong Gao { 285f8da88d7SSong Gao DisasContext ctx; 286f8da88d7SSong Gao 287306c8721SRichard Henderson translator_loop(cs, tb, max_insns, pc, host_pc, 288306c8721SRichard Henderson &loongarch_tr_ops, &ctx.base); 289f8da88d7SSong Gao } 290f8da88d7SSong Gao 291f8da88d7SSong Gao void loongarch_translate_init(void) 292f8da88d7SSong Gao { 293f8da88d7SSong Gao int i; 294f8da88d7SSong Gao 295f8da88d7SSong Gao cpu_gpr[0] = NULL; 296f8da88d7SSong Gao for (i = 1; i < 32; i++) { 297f8da88d7SSong Gao cpu_gpr[i] = tcg_global_mem_new(cpu_env, 298f8da88d7SSong Gao offsetof(CPULoongArchState, gpr[i]), 299f8da88d7SSong Gao regnames[i]); 300f8da88d7SSong Gao } 301f8da88d7SSong Gao 302f8da88d7SSong Gao cpu_pc = tcg_global_mem_new(cpu_env, offsetof(CPULoongArchState, pc), "pc"); 303f8da88d7SSong Gao cpu_lladdr = tcg_global_mem_new(cpu_env, 304f8da88d7SSong Gao offsetof(CPULoongArchState, lladdr), "lladdr"); 305f8da88d7SSong Gao cpu_llval = tcg_global_mem_new(cpu_env, 306f8da88d7SSong Gao offsetof(CPULoongArchState, llval), "llval"); 307f8da88d7SSong Gao } 308