15c23704eSSong Gao /* SPDX-License-Identifier: GPL-2.0-or-later */
25c23704eSSong Gao /*
35c23704eSSong Gao * LoongArch emulation for QEMU - main translation routines.
45c23704eSSong Gao *
55c23704eSSong Gao * Copyright (c) 2021 Loongson Technology Corporation Limited
65c23704eSSong Gao */
75c23704eSSong Gao
85c23704eSSong Gao #include "qemu/osdep.h"
95c23704eSSong Gao #include "cpu.h"
105c23704eSSong Gao #include "tcg/tcg-op.h"
115c23704eSSong Gao #include "tcg/tcg-op-gvec.h"
125c23704eSSong Gao #include "exec/translation-block.h"
135c23704eSSong Gao #include "exec/translator.h"
145c23704eSSong Gao #include "exec/helper-proto.h"
155c23704eSSong Gao #include "exec/helper-gen.h"
165c23704eSSong Gao #include "exec/log.h"
175c23704eSSong Gao #include "qemu/qemu-print.h"
185c23704eSSong Gao #include "fpu/softfloat.h"
195c23704eSSong Gao #include "translate.h"
205c23704eSSong Gao #include "internals.h"
215c23704eSSong Gao #include "vec.h"
225c23704eSSong Gao
235c23704eSSong Gao /* Global register indices */
245c23704eSSong Gao TCGv cpu_gpr[32], cpu_pc;
255c23704eSSong Gao static TCGv cpu_lladdr, cpu_llval;
265c23704eSSong Gao
275c23704eSSong Gao #define HELPER_H "helper.h"
285c23704eSSong Gao #include "exec/helper-info.c.inc"
295c23704eSSong Gao #undef HELPER_H
305c23704eSSong Gao
315c23704eSSong Gao #define DISAS_STOP DISAS_TARGET_0
325c23704eSSong Gao #define DISAS_EXIT DISAS_TARGET_1
335c23704eSSong Gao #define DISAS_EXIT_UPDATE DISAS_TARGET_2
345c23704eSSong Gao
vec_full_offset(int regno)355c23704eSSong Gao static inline int vec_full_offset(int regno)
365c23704eSSong Gao {
375c23704eSSong Gao return offsetof(CPULoongArchState, fpr[regno]);
385c23704eSSong Gao }
395c23704eSSong Gao
vec_reg_offset(int regno,int index,MemOp mop)405c23704eSSong Gao static inline int vec_reg_offset(int regno, int index, MemOp mop)
415c23704eSSong Gao {
425c23704eSSong Gao const uint8_t size = 1 << mop;
435c23704eSSong Gao int offs = index * size;
445c23704eSSong Gao
455c23704eSSong Gao if (HOST_BIG_ENDIAN && size < 8 ) {
465c23704eSSong Gao offs ^= (8 - size);
475c23704eSSong Gao }
485c23704eSSong Gao
495c23704eSSong Gao return offs + vec_full_offset(regno);
505c23704eSSong Gao }
515c23704eSSong Gao
get_vreg64(TCGv_i64 dest,int regno,int index)525c23704eSSong Gao static inline void get_vreg64(TCGv_i64 dest, int regno, int index)
535c23704eSSong Gao {
545c23704eSSong Gao tcg_gen_ld_i64(dest, tcg_env,
555c23704eSSong Gao offsetof(CPULoongArchState, fpr[regno].vreg.D(index)));
565c23704eSSong Gao }
575c23704eSSong Gao
set_vreg64(TCGv_i64 src,int regno,int index)585c23704eSSong Gao static inline void set_vreg64(TCGv_i64 src, int regno, int index)
595c23704eSSong Gao {
605c23704eSSong Gao tcg_gen_st_i64(src, tcg_env,
615c23704eSSong Gao offsetof(CPULoongArchState, fpr[regno].vreg.D(index)));
625c23704eSSong Gao }
635c23704eSSong Gao
plus_1(DisasContext * ctx,int x)645c23704eSSong Gao static inline int plus_1(DisasContext *ctx, int x)
655c23704eSSong Gao {
665c23704eSSong Gao return x + 1;
675c23704eSSong Gao }
685c23704eSSong Gao
shl_1(DisasContext * ctx,int x)695c23704eSSong Gao static inline int shl_1(DisasContext *ctx, int x)
705c23704eSSong Gao {
715c23704eSSong Gao return x << 1;
725c23704eSSong Gao }
735c23704eSSong Gao
shl_2(DisasContext * ctx,int x)745c23704eSSong Gao static inline int shl_2(DisasContext *ctx, int x)
755c23704eSSong Gao {
765c23704eSSong Gao return x << 2;
775c23704eSSong Gao }
785c23704eSSong Gao
shl_3(DisasContext * ctx,int x)795c23704eSSong Gao static inline int shl_3(DisasContext *ctx, int x)
805c23704eSSong Gao {
815c23704eSSong Gao return x << 3;
825c23704eSSong Gao }
835c23704eSSong Gao
845c23704eSSong Gao /*
855c23704eSSong Gao * LoongArch the upper 32 bits are undefined ("can be any value").
865c23704eSSong Gao * QEMU chooses to nanbox, because it is most likely to show guest bugs early.
875c23704eSSong Gao */
gen_nanbox_s(TCGv_i64 out,TCGv_i64 in)885c23704eSSong Gao static void gen_nanbox_s(TCGv_i64 out, TCGv_i64 in)
895c23704eSSong Gao {
905c23704eSSong Gao tcg_gen_ori_i64(out, in, MAKE_64BIT_MASK(32, 32));
915c23704eSSong Gao }
925c23704eSSong Gao
generate_exception(DisasContext * ctx,int excp)935c23704eSSong Gao void generate_exception(DisasContext *ctx, int excp)
945c23704eSSong Gao {
955c23704eSSong Gao tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next);
965c23704eSSong Gao gen_helper_raise_exception(tcg_env, tcg_constant_i32(excp));
975c23704eSSong Gao ctx->base.is_jmp = DISAS_NORETURN;
985c23704eSSong Gao }
995c23704eSSong Gao
gen_goto_tb(DisasContext * ctx,int n,target_ulong dest)1005c23704eSSong Gao static inline void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest)
1015c23704eSSong Gao {
1025c23704eSSong Gao if (ctx->va32) {
1035c23704eSSong Gao dest = (uint32_t) dest;
1045c23704eSSong Gao }
1055c23704eSSong Gao
1065c23704eSSong Gao if (translator_use_goto_tb(&ctx->base, dest)) {
1075c23704eSSong Gao tcg_gen_goto_tb(n);
1085c23704eSSong Gao tcg_gen_movi_tl(cpu_pc, dest);
1095c23704eSSong Gao tcg_gen_exit_tb(ctx->base.tb, n);
1105c23704eSSong Gao } else {
1115c23704eSSong Gao tcg_gen_movi_tl(cpu_pc, dest);
1125c23704eSSong Gao tcg_gen_lookup_and_goto_ptr();
1135c23704eSSong Gao }
1145c23704eSSong Gao }
1155c23704eSSong Gao
loongarch_tr_init_disas_context(DisasContextBase * dcbase,CPUState * cs)1165c23704eSSong Gao static void loongarch_tr_init_disas_context(DisasContextBase *dcbase,
1175c23704eSSong Gao CPUState *cs)
1185c23704eSSong Gao {
1195c23704eSSong Gao int64_t bound;
1205c23704eSSong Gao CPULoongArchState *env = cpu_env(cs);
1215c23704eSSong Gao DisasContext *ctx = container_of(dcbase, DisasContext, base);
1225c23704eSSong Gao
1235c23704eSSong Gao ctx->page_start = ctx->base.pc_first & TARGET_PAGE_MASK;
1245c23704eSSong Gao ctx->plv = ctx->base.tb->flags & HW_FLAGS_PLV_MASK;
1255c23704eSSong Gao if (ctx->base.tb->flags & HW_FLAGS_CRMD_PG) {
1265c23704eSSong Gao ctx->mem_idx = ctx->plv;
1275c23704eSSong Gao } else {
1283f262d25SRichard Henderson ctx->mem_idx = MMU_DA_IDX;
1295c23704eSSong Gao }
1305c23704eSSong Gao
1315c23704eSSong Gao /* Bound the number of insns to execute to those left on the page. */
1325c23704eSSong Gao bound = -(ctx->base.pc_first | TARGET_PAGE_MASK) / 4;
1335c23704eSSong Gao ctx->base.max_insns = MIN(ctx->base.max_insns, bound);
1345c23704eSSong Gao
1355c23704eSSong Gao if (FIELD_EX64(env->cpucfg[2], CPUCFG2, LSX)) {
1365c23704eSSong Gao ctx->vl = LSX_LEN;
1375c23704eSSong Gao }
1385c23704eSSong Gao
1395c23704eSSong Gao if (FIELD_EX64(env->cpucfg[2], CPUCFG2, LASX)) {
1405c23704eSSong Gao ctx->vl = LASX_LEN;
1415c23704eSSong Gao }
1425c23704eSSong Gao
1435c23704eSSong Gao ctx->la64 = is_la64(env);
1445c23704eSSong Gao ctx->va32 = (ctx->base.tb->flags & HW_FLAGS_VA32) != 0;
1455c23704eSSong Gao
1465c23704eSSong Gao ctx->zero = tcg_constant_tl(0);
1475c23704eSSong Gao
1485c23704eSSong Gao ctx->cpucfg1 = env->cpucfg[1];
1495c23704eSSong Gao ctx->cpucfg2 = env->cpucfg[2];
1505c23704eSSong Gao }
1515c23704eSSong Gao
loongarch_tr_tb_start(DisasContextBase * dcbase,CPUState * cs)1525c23704eSSong Gao static void loongarch_tr_tb_start(DisasContextBase *dcbase, CPUState *cs)
1535c23704eSSong Gao {
1545c23704eSSong Gao }
1555c23704eSSong Gao
loongarch_tr_insn_start(DisasContextBase * dcbase,CPUState * cs)1565c23704eSSong Gao static void loongarch_tr_insn_start(DisasContextBase *dcbase, CPUState *cs)
1575c23704eSSong Gao {
1585c23704eSSong Gao DisasContext *ctx = container_of(dcbase, DisasContext, base);
1595c23704eSSong Gao
1605c23704eSSong Gao tcg_gen_insn_start(ctx->base.pc_next);
1615c23704eSSong Gao }
1625c23704eSSong Gao
1635c23704eSSong Gao /*
1645c23704eSSong Gao * Wrappers for getting reg values.
1655c23704eSSong Gao *
1665c23704eSSong Gao * The $zero register does not have cpu_gpr[0] allocated -- we supply the
1675c23704eSSong Gao * constant zero as a source, and an uninitialized sink as destination.
1685c23704eSSong Gao *
1695c23704eSSong Gao * Further, we may provide an extension for word operations.
1705c23704eSSong Gao */
gpr_src(DisasContext * ctx,int reg_num,DisasExtend src_ext)1715c23704eSSong Gao static TCGv gpr_src(DisasContext *ctx, int reg_num, DisasExtend src_ext)
1725c23704eSSong Gao {
1735c23704eSSong Gao TCGv t;
1745c23704eSSong Gao
1755c23704eSSong Gao if (reg_num == 0) {
1765c23704eSSong Gao return ctx->zero;
1775c23704eSSong Gao }
1785c23704eSSong Gao
1795c23704eSSong Gao switch (src_ext) {
1805c23704eSSong Gao case EXT_NONE:
1815c23704eSSong Gao return cpu_gpr[reg_num];
1825c23704eSSong Gao case EXT_SIGN:
1835c23704eSSong Gao t = tcg_temp_new();
1845c23704eSSong Gao tcg_gen_ext32s_tl(t, cpu_gpr[reg_num]);
1855c23704eSSong Gao return t;
1865c23704eSSong Gao case EXT_ZERO:
1875c23704eSSong Gao t = tcg_temp_new();
1885c23704eSSong Gao tcg_gen_ext32u_tl(t, cpu_gpr[reg_num]);
1895c23704eSSong Gao return t;
1905c23704eSSong Gao }
1915c23704eSSong Gao g_assert_not_reached();
1925c23704eSSong Gao }
1935c23704eSSong Gao
gpr_dst(DisasContext * ctx,int reg_num,DisasExtend dst_ext)1945c23704eSSong Gao static TCGv gpr_dst(DisasContext *ctx, int reg_num, DisasExtend dst_ext)
1955c23704eSSong Gao {
1965c23704eSSong Gao if (reg_num == 0 || dst_ext) {
1975c23704eSSong Gao return tcg_temp_new();
1985c23704eSSong Gao }
1995c23704eSSong Gao return cpu_gpr[reg_num];
2005c23704eSSong Gao }
2015c23704eSSong Gao
gen_set_gpr(int reg_num,TCGv t,DisasExtend dst_ext)2025c23704eSSong Gao static void gen_set_gpr(int reg_num, TCGv t, DisasExtend dst_ext)
2035c23704eSSong Gao {
2045c23704eSSong Gao if (reg_num != 0) {
2055c23704eSSong Gao switch (dst_ext) {
2065c23704eSSong Gao case EXT_NONE:
2075c23704eSSong Gao tcg_gen_mov_tl(cpu_gpr[reg_num], t);
2085c23704eSSong Gao break;
2095c23704eSSong Gao case EXT_SIGN:
2105c23704eSSong Gao tcg_gen_ext32s_tl(cpu_gpr[reg_num], t);
2115c23704eSSong Gao break;
2125c23704eSSong Gao case EXT_ZERO:
2135c23704eSSong Gao tcg_gen_ext32u_tl(cpu_gpr[reg_num], t);
2145c23704eSSong Gao break;
2155c23704eSSong Gao default:
2165c23704eSSong Gao g_assert_not_reached();
2175c23704eSSong Gao }
2185c23704eSSong Gao }
2195c23704eSSong Gao }
2205c23704eSSong Gao
get_fpr(DisasContext * ctx,int reg_num)2215c23704eSSong Gao static TCGv get_fpr(DisasContext *ctx, int reg_num)
2225c23704eSSong Gao {
2235c23704eSSong Gao TCGv t = tcg_temp_new();
2245c23704eSSong Gao tcg_gen_ld_i64(t, tcg_env,
2255c23704eSSong Gao offsetof(CPULoongArchState, fpr[reg_num].vreg.D(0)));
2265c23704eSSong Gao return t;
2275c23704eSSong Gao }
2285c23704eSSong Gao
set_fpr(int reg_num,TCGv val)2295c23704eSSong Gao static void set_fpr(int reg_num, TCGv val)
2305c23704eSSong Gao {
2315c23704eSSong Gao tcg_gen_st_i64(val, tcg_env,
2325c23704eSSong Gao offsetof(CPULoongArchState, fpr[reg_num].vreg.D(0)));
2335c23704eSSong Gao }
2345c23704eSSong Gao
make_address_x(DisasContext * ctx,TCGv base,TCGv addend)2355c23704eSSong Gao static TCGv make_address_x(DisasContext *ctx, TCGv base, TCGv addend)
2365c23704eSSong Gao {
2375c23704eSSong Gao TCGv temp = NULL;
2385c23704eSSong Gao
2395c23704eSSong Gao if (addend || ctx->va32) {
2405c23704eSSong Gao temp = tcg_temp_new();
2415c23704eSSong Gao }
2425c23704eSSong Gao if (addend) {
2435c23704eSSong Gao tcg_gen_add_tl(temp, base, addend);
2445c23704eSSong Gao base = temp;
2455c23704eSSong Gao }
2465c23704eSSong Gao if (ctx->va32) {
2475c23704eSSong Gao tcg_gen_ext32u_tl(temp, base);
2485c23704eSSong Gao base = temp;
2495c23704eSSong Gao }
2505c23704eSSong Gao return base;
2515c23704eSSong Gao }
2525c23704eSSong Gao
make_address_i(DisasContext * ctx,TCGv base,target_long ofs)2535c23704eSSong Gao static TCGv make_address_i(DisasContext *ctx, TCGv base, target_long ofs)
2545c23704eSSong Gao {
2555c23704eSSong Gao TCGv addend = ofs ? tcg_constant_tl(ofs) : NULL;
2565c23704eSSong Gao return make_address_x(ctx, base, addend);
2575c23704eSSong Gao }
2585c23704eSSong Gao
make_address_pc(DisasContext * ctx,uint64_t addr)2595c23704eSSong Gao static uint64_t make_address_pc(DisasContext *ctx, uint64_t addr)
2605c23704eSSong Gao {
2615c23704eSSong Gao if (ctx->va32) {
2625c23704eSSong Gao addr = (int32_t)addr;
2635c23704eSSong Gao }
2645c23704eSSong Gao return addr;
2655c23704eSSong Gao }
2665c23704eSSong Gao
2675c23704eSSong Gao #include "decode-insns.c.inc"
2685c23704eSSong Gao #include "insn_trans/trans_arith.c.inc"
2695c23704eSSong Gao #include "insn_trans/trans_shift.c.inc"
2705c23704eSSong Gao #include "insn_trans/trans_bit.c.inc"
2715c23704eSSong Gao #include "insn_trans/trans_memory.c.inc"
2725c23704eSSong Gao #include "insn_trans/trans_atomic.c.inc"
2735c23704eSSong Gao #include "insn_trans/trans_extra.c.inc"
2745c23704eSSong Gao #include "insn_trans/trans_farith.c.inc"
2755c23704eSSong Gao #include "insn_trans/trans_fcmp.c.inc"
2765c23704eSSong Gao #include "insn_trans/trans_fcnv.c.inc"
2775c23704eSSong Gao #include "insn_trans/trans_fmov.c.inc"
2785c23704eSSong Gao #include "insn_trans/trans_fmemory.c.inc"
2795c23704eSSong Gao #include "insn_trans/trans_branch.c.inc"
2805c23704eSSong Gao #include "insn_trans/trans_privileged.c.inc"
2815c23704eSSong Gao #include "insn_trans/trans_vec.c.inc"
2825c23704eSSong Gao
loongarch_tr_translate_insn(DisasContextBase * dcbase,CPUState * cs)2835c23704eSSong Gao static void loongarch_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs)
2845c23704eSSong Gao {
2855c23704eSSong Gao DisasContext *ctx = container_of(dcbase, DisasContext, base);
2865c23704eSSong Gao
287*94956d7bSPhilippe Mathieu-Daudé ctx->opcode = translator_ldl(cpu_env(cs), &ctx->base, ctx->base.pc_next);
2885c23704eSSong Gao
2895c23704eSSong Gao if (!decode(ctx, ctx->opcode)) {
2905c23704eSSong Gao qemu_log_mask(LOG_UNIMP, "Error: unknown opcode. "
2915c23704eSSong Gao TARGET_FMT_lx ": 0x%x\n",
2925c23704eSSong Gao ctx->base.pc_next, ctx->opcode);
2935c23704eSSong Gao generate_exception(ctx, EXCCODE_INE);
2945c23704eSSong Gao }
2955c23704eSSong Gao
2965c23704eSSong Gao ctx->base.pc_next += 4;
2975c23704eSSong Gao
2985c23704eSSong Gao if (ctx->va32) {
2995c23704eSSong Gao ctx->base.pc_next = (uint32_t)ctx->base.pc_next;
3005c23704eSSong Gao }
3015c23704eSSong Gao }
3025c23704eSSong Gao
loongarch_tr_tb_stop(DisasContextBase * dcbase,CPUState * cs)3035c23704eSSong Gao static void loongarch_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs)
3045c23704eSSong Gao {
3055c23704eSSong Gao DisasContext *ctx = container_of(dcbase, DisasContext, base);
3065c23704eSSong Gao
3075c23704eSSong Gao switch (ctx->base.is_jmp) {
3085c23704eSSong Gao case DISAS_STOP:
3095c23704eSSong Gao tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next);
3105c23704eSSong Gao tcg_gen_lookup_and_goto_ptr();
3115c23704eSSong Gao break;
3125c23704eSSong Gao case DISAS_TOO_MANY:
3135c23704eSSong Gao gen_goto_tb(ctx, 0, ctx->base.pc_next);
3145c23704eSSong Gao break;
3155c23704eSSong Gao case DISAS_NORETURN:
3165c23704eSSong Gao break;
3175c23704eSSong Gao case DISAS_EXIT_UPDATE:
3185c23704eSSong Gao tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next);
3195c23704eSSong Gao QEMU_FALLTHROUGH;
3205c23704eSSong Gao case DISAS_EXIT:
3215c23704eSSong Gao tcg_gen_exit_tb(NULL, 0);
3225c23704eSSong Gao break;
3235c23704eSSong Gao default:
3245c23704eSSong Gao g_assert_not_reached();
3255c23704eSSong Gao }
3265c23704eSSong Gao }
3275c23704eSSong Gao
3285c23704eSSong Gao static const TranslatorOps loongarch_tr_ops = {
3295c23704eSSong Gao .init_disas_context = loongarch_tr_init_disas_context,
3305c23704eSSong Gao .tb_start = loongarch_tr_tb_start,
3315c23704eSSong Gao .insn_start = loongarch_tr_insn_start,
3325c23704eSSong Gao .translate_insn = loongarch_tr_translate_insn,
3335c23704eSSong Gao .tb_stop = loongarch_tr_tb_stop,
3345c23704eSSong Gao };
3355c23704eSSong Gao
gen_intermediate_code(CPUState * cs,TranslationBlock * tb,int * max_insns,vaddr pc,void * host_pc)3365c23704eSSong Gao void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns,
33732f0c394SAnton Johansson vaddr pc, void *host_pc)
3385c23704eSSong Gao {
3395c23704eSSong Gao DisasContext ctx;
3405c23704eSSong Gao
3415c23704eSSong Gao translator_loop(cs, tb, max_insns, pc, host_pc,
3425c23704eSSong Gao &loongarch_tr_ops, &ctx.base);
3435c23704eSSong Gao }
3445c23704eSSong Gao
loongarch_translate_init(void)3455c23704eSSong Gao void loongarch_translate_init(void)
3465c23704eSSong Gao {
3475c23704eSSong Gao int i;
3485c23704eSSong Gao
3495c23704eSSong Gao cpu_gpr[0] = NULL;
3505c23704eSSong Gao for (i = 1; i < 32; i++) {
3515c23704eSSong Gao cpu_gpr[i] = tcg_global_mem_new(tcg_env,
3525c23704eSSong Gao offsetof(CPULoongArchState, gpr[i]),
3535c23704eSSong Gao regnames[i]);
3545c23704eSSong Gao }
3555c23704eSSong Gao
3565c23704eSSong Gao cpu_pc = tcg_global_mem_new(tcg_env, offsetof(CPULoongArchState, pc), "pc");
3575c23704eSSong Gao cpu_lladdr = tcg_global_mem_new(tcg_env,
3585c23704eSSong Gao offsetof(CPULoongArchState, lladdr), "lladdr");
3595c23704eSSong Gao cpu_llval = tcg_global_mem_new(tcg_env,
3605c23704eSSong Gao offsetof(CPULoongArchState, llval), "llval");
3615c23704eSSong Gao }
362