1 /* SPDX-License-Identifier: GPL-2.0-or-later */
3 * LoongArch emulation for QEMU - main translation routines.
5 * Copyright (c) 2021 Loongson Technology Corporation Limited
8 #include "qemu/osdep.h"
10 #include "tcg/tcg-op.h"
11 #include "exec/translator.h"
12 #include "exec/helper-proto.h"
13 #include "exec/helper-gen.h"
15 #include "exec/translator.h"
17 #include "qemu/qemu-print.h"
18 #include "fpu/softfloat.h"
19 #include "translate.h"
20 #include "internals.h"
22 /* Global register indices */
23 TCGv cpu_gpr
[32], cpu_pc
;
24 static TCGv cpu_lladdr
, cpu_llval
;
27 #include "exec/gen-icount.h"
29 #define DISAS_STOP DISAS_TARGET_0
30 #define DISAS_EXIT DISAS_TARGET_1
31 #define DISAS_EXIT_UPDATE DISAS_TARGET_2
33 static inline int plus_1(DisasContext
*ctx
, int x
)
38 static inline int shl_2(DisasContext
*ctx
, int x
)
44 * LoongArch the upper 32 bits are undefined ("can be any value").
45 * QEMU chooses to nanbox, because it is most likely to show guest bugs early.
47 static void gen_nanbox_s(TCGv_i64 out
, TCGv_i64 in
)
49 tcg_gen_ori_i64(out
, in
, MAKE_64BIT_MASK(32, 32));
52 void generate_exception(DisasContext
*ctx
, int excp
)
54 tcg_gen_movi_tl(cpu_pc
, ctx
->base
.pc_next
);
55 gen_helper_raise_exception(cpu_env
, tcg_constant_i32(excp
));
56 ctx
->base
.is_jmp
= DISAS_NORETURN
;
59 static inline void gen_goto_tb(DisasContext
*ctx
, int n
, target_ulong dest
)
61 if (translator_use_goto_tb(&ctx
->base
, dest
)) {
63 tcg_gen_movi_tl(cpu_pc
, dest
);
64 tcg_gen_exit_tb(ctx
->base
.tb
, n
);
66 tcg_gen_movi_tl(cpu_pc
, dest
);
67 tcg_gen_lookup_and_goto_ptr();
71 static void loongarch_tr_init_disas_context(DisasContextBase
*dcbase
,
75 DisasContext
*ctx
= container_of(dcbase
, DisasContext
, base
);
77 ctx
->page_start
= ctx
->base
.pc_first
& TARGET_PAGE_MASK
;
78 ctx
->plv
= ctx
->base
.tb
->flags
& HW_FLAGS_PLV_MASK
;
79 if (ctx
->base
.tb
->flags
& HW_FLAGS_CRMD_PG
) {
80 ctx
->mem_idx
= ctx
->plv
;
82 ctx
->mem_idx
= MMU_IDX_DA
;
85 /* Bound the number of insns to execute to those left on the page. */
86 bound
= -(ctx
->base
.pc_first
| TARGET_PAGE_MASK
) / 4;
87 ctx
->base
.max_insns
= MIN(ctx
->base
.max_insns
, bound
);
90 memset(ctx
->temp
, 0, sizeof(ctx
->temp
));
92 ctx
->zero
= tcg_constant_tl(0);
95 static void loongarch_tr_tb_start(DisasContextBase
*dcbase
, CPUState
*cs
)
99 static void loongarch_tr_insn_start(DisasContextBase
*dcbase
, CPUState
*cs
)
101 DisasContext
*ctx
= container_of(dcbase
, DisasContext
, base
);
103 tcg_gen_insn_start(ctx
->base
.pc_next
);
107 * Wrappers for getting reg values.
109 * The $zero register does not have cpu_gpr[0] allocated -- we supply the
110 * constant zero as a source, and an uninitialized sink as destination.
112 * Further, we may provide an extension for word operations.
114 static TCGv
temp_new(DisasContext
*ctx
)
116 assert(ctx
->ntemp
< ARRAY_SIZE(ctx
->temp
));
117 return ctx
->temp
[ctx
->ntemp
++] = tcg_temp_new();
120 static TCGv
gpr_src(DisasContext
*ctx
, int reg_num
, DisasExtend src_ext
)
130 return cpu_gpr
[reg_num
];
133 tcg_gen_ext32s_tl(t
, cpu_gpr
[reg_num
]);
137 tcg_gen_ext32u_tl(t
, cpu_gpr
[reg_num
]);
140 g_assert_not_reached();
143 static TCGv
gpr_dst(DisasContext
*ctx
, int reg_num
, DisasExtend dst_ext
)
145 if (reg_num
== 0 || dst_ext
) {
146 return temp_new(ctx
);
148 return cpu_gpr
[reg_num
];
151 static void gen_set_gpr(int reg_num
, TCGv t
, DisasExtend dst_ext
)
156 tcg_gen_mov_tl(cpu_gpr
[reg_num
], t
);
159 tcg_gen_ext32s_tl(cpu_gpr
[reg_num
], t
);
162 tcg_gen_ext32u_tl(cpu_gpr
[reg_num
], t
);
165 g_assert_not_reached();
170 #include "decode-insns.c.inc"
171 #include "insn_trans/trans_arith.c.inc"
172 #include "insn_trans/trans_shift.c.inc"
173 #include "insn_trans/trans_bit.c.inc"
174 #include "insn_trans/trans_memory.c.inc"
175 #include "insn_trans/trans_atomic.c.inc"
176 #include "insn_trans/trans_extra.c.inc"
177 #include "insn_trans/trans_farith.c.inc"
178 #include "insn_trans/trans_fcmp.c.inc"
179 #include "insn_trans/trans_fcnv.c.inc"
180 #include "insn_trans/trans_fmov.c.inc"
181 #include "insn_trans/trans_fmemory.c.inc"
182 #include "insn_trans/trans_branch.c.inc"
183 #include "insn_trans/trans_privileged.c.inc"
185 static void loongarch_tr_translate_insn(DisasContextBase
*dcbase
, CPUState
*cs
)
187 CPULoongArchState
*env
= cs
->env_ptr
;
188 DisasContext
*ctx
= container_of(dcbase
, DisasContext
, base
);
190 ctx
->opcode
= cpu_ldl_code(env
, ctx
->base
.pc_next
);
192 if (!decode(ctx
, ctx
->opcode
)) {
193 qemu_log_mask(LOG_UNIMP
, "Error: unknown opcode. "
194 TARGET_FMT_lx
": 0x%x\n",
195 ctx
->base
.pc_next
, ctx
->opcode
);
196 generate_exception(ctx
, EXCCODE_INE
);
199 for (int i
= ctx
->ntemp
- 1; i
>= 0; --i
) {
200 tcg_temp_free(ctx
->temp
[i
]);
205 ctx
->base
.pc_next
+= 4;
208 static void loongarch_tr_tb_stop(DisasContextBase
*dcbase
, CPUState
*cs
)
210 DisasContext
*ctx
= container_of(dcbase
, DisasContext
, base
);
212 switch (ctx
->base
.is_jmp
) {
214 tcg_gen_movi_tl(cpu_pc
, ctx
->base
.pc_next
);
215 tcg_gen_lookup_and_goto_ptr();
218 gen_goto_tb(ctx
, 0, ctx
->base
.pc_next
);
222 case DISAS_EXIT_UPDATE
:
223 tcg_gen_movi_tl(cpu_pc
, ctx
->base
.pc_next
);
226 tcg_gen_exit_tb(NULL
, 0);
229 g_assert_not_reached();
233 static void loongarch_tr_disas_log(const DisasContextBase
*dcbase
,
234 CPUState
*cpu
, FILE *logfile
)
236 qemu_log("IN: %s\n", lookup_symbol(dcbase
->pc_first
));
237 target_disas(logfile
, cpu
, dcbase
->pc_first
, dcbase
->tb
->size
);
240 static const TranslatorOps loongarch_tr_ops
= {
241 .init_disas_context
= loongarch_tr_init_disas_context
,
242 .tb_start
= loongarch_tr_tb_start
,
243 .insn_start
= loongarch_tr_insn_start
,
244 .translate_insn
= loongarch_tr_translate_insn
,
245 .tb_stop
= loongarch_tr_tb_stop
,
246 .disas_log
= loongarch_tr_disas_log
,
249 void gen_intermediate_code(CPUState
*cs
, TranslationBlock
*tb
, int max_insns
,
250 target_ulong pc
, void *host_pc
)
254 translator_loop(cs
, tb
, max_insns
, pc
, host_pc
,
255 &loongarch_tr_ops
, &ctx
.base
);
258 void loongarch_translate_init(void)
263 for (i
= 1; i
< 32; i
++) {
264 cpu_gpr
[i
] = tcg_global_mem_new(cpu_env
,
265 offsetof(CPULoongArchState
, gpr
[i
]),
269 for (i
= 0; i
< 32; i
++) {
270 int off
= offsetof(CPULoongArchState
, fpr
[i
]);
271 cpu_fpr
[i
] = tcg_global_mem_new_i64(cpu_env
, off
, fregnames
[i
]);
274 cpu_pc
= tcg_global_mem_new(cpu_env
, offsetof(CPULoongArchState
, pc
), "pc");
275 cpu_lladdr
= tcg_global_mem_new(cpu_env
,
276 offsetof(CPULoongArchState
, lladdr
), "lladdr");
277 cpu_llval
= tcg_global_mem_new(cpu_env
,
278 offsetof(CPULoongArchState
, llval
), "llval");