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"
16 #include "qemu/qemu-print.h"
17 #include "fpu/softfloat.h"
18 #include "translate.h"
19 #include "internals.h"
21 /* Global register indices */
22 TCGv cpu_gpr
[32], cpu_pc
;
23 static TCGv cpu_lladdr
, cpu_llval
;
26 #include "exec/gen-icount.h"
28 #define DISAS_STOP DISAS_TARGET_0
29 #define DISAS_EXIT DISAS_TARGET_1
30 #define DISAS_EXIT_UPDATE DISAS_TARGET_2
32 static inline int plus_1(DisasContext
*ctx
, int x
)
37 static inline int shl_2(DisasContext
*ctx
, int x
)
43 * LoongArch the upper 32 bits are undefined ("can be any value").
44 * QEMU chooses to nanbox, because it is most likely to show guest bugs early.
46 static void gen_nanbox_s(TCGv_i64 out
, TCGv_i64 in
)
48 tcg_gen_ori_i64(out
, in
, MAKE_64BIT_MASK(32, 32));
51 void generate_exception(DisasContext
*ctx
, int excp
)
53 tcg_gen_movi_tl(cpu_pc
, ctx
->base
.pc_next
);
54 gen_helper_raise_exception(cpu_env
, tcg_constant_i32(excp
));
55 ctx
->base
.is_jmp
= DISAS_NORETURN
;
58 static inline void gen_goto_tb(DisasContext
*ctx
, int n
, target_ulong dest
)
60 if (translator_use_goto_tb(&ctx
->base
, dest
)) {
62 tcg_gen_movi_tl(cpu_pc
, dest
);
63 tcg_gen_exit_tb(ctx
->base
.tb
, n
);
65 tcg_gen_movi_tl(cpu_pc
, dest
);
66 tcg_gen_lookup_and_goto_ptr();
70 static void loongarch_tr_init_disas_context(DisasContextBase
*dcbase
,
74 DisasContext
*ctx
= container_of(dcbase
, DisasContext
, base
);
76 ctx
->page_start
= ctx
->base
.pc_first
& TARGET_PAGE_MASK
;
77 ctx
->plv
= ctx
->base
.tb
->flags
& HW_FLAGS_PLV_MASK
;
78 if (ctx
->base
.tb
->flags
& HW_FLAGS_CRMD_PG
) {
79 ctx
->mem_idx
= ctx
->plv
;
81 ctx
->mem_idx
= MMU_IDX_DA
;
84 /* Bound the number of insns to execute to those left on the page. */
85 bound
= -(ctx
->base
.pc_first
| TARGET_PAGE_MASK
) / 4;
86 ctx
->base
.max_insns
= MIN(ctx
->base
.max_insns
, bound
);
89 memset(ctx
->temp
, 0, sizeof(ctx
->temp
));
91 ctx
->zero
= tcg_constant_tl(0);
94 static void loongarch_tr_tb_start(DisasContextBase
*dcbase
, CPUState
*cs
)
98 static void loongarch_tr_insn_start(DisasContextBase
*dcbase
, CPUState
*cs
)
100 DisasContext
*ctx
= container_of(dcbase
, DisasContext
, base
);
102 tcg_gen_insn_start(ctx
->base
.pc_next
);
106 * Wrappers for getting reg values.
108 * The $zero register does not have cpu_gpr[0] allocated -- we supply the
109 * constant zero as a source, and an uninitialized sink as destination.
111 * Further, we may provide an extension for word operations.
113 static TCGv
temp_new(DisasContext
*ctx
)
115 assert(ctx
->ntemp
< ARRAY_SIZE(ctx
->temp
));
116 return ctx
->temp
[ctx
->ntemp
++] = tcg_temp_new();
119 static TCGv
gpr_src(DisasContext
*ctx
, int reg_num
, DisasExtend src_ext
)
129 return cpu_gpr
[reg_num
];
132 tcg_gen_ext32s_tl(t
, cpu_gpr
[reg_num
]);
136 tcg_gen_ext32u_tl(t
, cpu_gpr
[reg_num
]);
139 g_assert_not_reached();
142 static TCGv
gpr_dst(DisasContext
*ctx
, int reg_num
, DisasExtend dst_ext
)
144 if (reg_num
== 0 || dst_ext
) {
145 return temp_new(ctx
);
147 return cpu_gpr
[reg_num
];
150 static void gen_set_gpr(int reg_num
, TCGv t
, DisasExtend dst_ext
)
155 tcg_gen_mov_tl(cpu_gpr
[reg_num
], t
);
158 tcg_gen_ext32s_tl(cpu_gpr
[reg_num
], t
);
161 tcg_gen_ext32u_tl(cpu_gpr
[reg_num
], t
);
164 g_assert_not_reached();
169 #include "decode-insns.c.inc"
170 #include "insn_trans/trans_arith.c.inc"
171 #include "insn_trans/trans_shift.c.inc"
172 #include "insn_trans/trans_bit.c.inc"
173 #include "insn_trans/trans_memory.c.inc"
174 #include "insn_trans/trans_atomic.c.inc"
175 #include "insn_trans/trans_extra.c.inc"
176 #include "insn_trans/trans_farith.c.inc"
177 #include "insn_trans/trans_fcmp.c.inc"
178 #include "insn_trans/trans_fcnv.c.inc"
179 #include "insn_trans/trans_fmov.c.inc"
180 #include "insn_trans/trans_fmemory.c.inc"
181 #include "insn_trans/trans_branch.c.inc"
182 #include "insn_trans/trans_privileged.c.inc"
184 static void loongarch_tr_translate_insn(DisasContextBase
*dcbase
, CPUState
*cs
)
186 CPULoongArchState
*env
= cs
->env_ptr
;
187 DisasContext
*ctx
= container_of(dcbase
, DisasContext
, base
);
189 ctx
->opcode
= cpu_ldl_code(env
, ctx
->base
.pc_next
);
191 if (!decode(ctx
, ctx
->opcode
)) {
192 qemu_log_mask(LOG_UNIMP
, "Error: unknown opcode. "
193 TARGET_FMT_lx
": 0x%x\n",
194 ctx
->base
.pc_next
, ctx
->opcode
);
195 generate_exception(ctx
, EXCCODE_INE
);
198 for (int i
= ctx
->ntemp
- 1; i
>= 0; --i
) {
199 tcg_temp_free(ctx
->temp
[i
]);
204 ctx
->base
.pc_next
+= 4;
207 static void loongarch_tr_tb_stop(DisasContextBase
*dcbase
, CPUState
*cs
)
209 DisasContext
*ctx
= container_of(dcbase
, DisasContext
, base
);
211 switch (ctx
->base
.is_jmp
) {
213 tcg_gen_movi_tl(cpu_pc
, ctx
->base
.pc_next
);
214 tcg_gen_lookup_and_goto_ptr();
217 gen_goto_tb(ctx
, 0, ctx
->base
.pc_next
);
221 case DISAS_EXIT_UPDATE
:
222 tcg_gen_movi_tl(cpu_pc
, ctx
->base
.pc_next
);
225 tcg_gen_exit_tb(NULL
, 0);
228 g_assert_not_reached();
232 static void loongarch_tr_disas_log(const DisasContextBase
*dcbase
,
233 CPUState
*cpu
, FILE *logfile
)
235 qemu_log("IN: %s\n", lookup_symbol(dcbase
->pc_first
));
236 target_disas(logfile
, cpu
, dcbase
->pc_first
, dcbase
->tb
->size
);
239 static const TranslatorOps loongarch_tr_ops
= {
240 .init_disas_context
= loongarch_tr_init_disas_context
,
241 .tb_start
= loongarch_tr_tb_start
,
242 .insn_start
= loongarch_tr_insn_start
,
243 .translate_insn
= loongarch_tr_translate_insn
,
244 .tb_stop
= loongarch_tr_tb_stop
,
245 .disas_log
= loongarch_tr_disas_log
,
248 void gen_intermediate_code(CPUState
*cs
, TranslationBlock
*tb
, int *max_insns
,
249 target_ulong pc
, void *host_pc
)
253 translator_loop(cs
, tb
, max_insns
, pc
, host_pc
,
254 &loongarch_tr_ops
, &ctx
.base
);
257 void loongarch_translate_init(void)
262 for (i
= 1; i
< 32; i
++) {
263 cpu_gpr
[i
] = tcg_global_mem_new(cpu_env
,
264 offsetof(CPULoongArchState
, gpr
[i
]),
268 for (i
= 0; i
< 32; i
++) {
269 int off
= offsetof(CPULoongArchState
, fpr
[i
]);
270 cpu_fpr
[i
] = tcg_global_mem_new_i64(cpu_env
, off
, fregnames
[i
]);
273 cpu_pc
= tcg_global_mem_new(cpu_env
, offsetof(CPULoongArchState
, pc
), "pc");
274 cpu_lladdr
= tcg_global_mem_new(cpu_env
,
275 offsetof(CPULoongArchState
, lladdr
), "lladdr");
276 cpu_llval
= tcg_global_mem_new(cpu_env
,
277 offsetof(CPULoongArchState
, llval
), "llval");