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
;
28 #define DISAS_STOP DISAS_TARGET_0
30 static inline int plus_1(DisasContext
*ctx
, int x
)
35 static inline int shl_2(DisasContext
*ctx
, int x
)
41 * LoongArch the upper 32 bits are undefined ("can be any value").
42 * QEMU chooses to nanbox, because it is most likely to show guest bugs early.
44 static void gen_nanbox_s(TCGv_i64 out
, TCGv_i64 in
)
46 tcg_gen_ori_i64(out
, in
, MAKE_64BIT_MASK(32, 32));
49 void generate_exception(DisasContext
*ctx
, int excp
)
51 tcg_gen_movi_tl(cpu_pc
, ctx
->base
.pc_next
);
52 gen_helper_raise_exception(cpu_env
, tcg_constant_i32(excp
));
53 ctx
->base
.is_jmp
= DISAS_NORETURN
;
56 static inline void gen_goto_tb(DisasContext
*ctx
, int n
, target_ulong dest
)
58 if (translator_use_goto_tb(&ctx
->base
, dest
)) {
60 tcg_gen_movi_tl(cpu_pc
, dest
);
61 tcg_gen_exit_tb(ctx
->base
.tb
, n
);
63 tcg_gen_movi_tl(cpu_pc
, dest
);
64 tcg_gen_lookup_and_goto_ptr();
68 static void loongarch_tr_init_disas_context(DisasContextBase
*dcbase
,
72 DisasContext
*ctx
= container_of(dcbase
, DisasContext
, base
);
74 ctx
->page_start
= ctx
->base
.pc_first
& TARGET_PAGE_MASK
;
75 ctx
->mem_idx
= ctx
->base
.tb
->flags
;
77 /* Bound the number of insns to execute to those left on the page. */
78 bound
= -(ctx
->base
.pc_first
| TARGET_PAGE_MASK
) / 4;
79 ctx
->base
.max_insns
= MIN(ctx
->base
.max_insns
, bound
);
82 memset(ctx
->temp
, 0, sizeof(ctx
->temp
));
84 ctx
->zero
= tcg_constant_tl(0);
87 static void loongarch_tr_tb_start(DisasContextBase
*dcbase
, CPUState
*cs
)
91 static void loongarch_tr_insn_start(DisasContextBase
*dcbase
, CPUState
*cs
)
93 DisasContext
*ctx
= container_of(dcbase
, DisasContext
, base
);
95 tcg_gen_insn_start(ctx
->base
.pc_next
);
99 * Wrappers for getting reg values.
101 * The $zero register does not have cpu_gpr[0] allocated -- we supply the
102 * constant zero as a source, and an uninitialized sink as destination.
104 * Further, we may provide an extension for word operations.
106 static TCGv
temp_new(DisasContext
*ctx
)
108 assert(ctx
->ntemp
< ARRAY_SIZE(ctx
->temp
));
109 return ctx
->temp
[ctx
->ntemp
++] = tcg_temp_new();
112 static TCGv
gpr_src(DisasContext
*ctx
, int reg_num
, DisasExtend src_ext
)
122 return cpu_gpr
[reg_num
];
125 tcg_gen_ext32s_tl(t
, cpu_gpr
[reg_num
]);
129 tcg_gen_ext32u_tl(t
, cpu_gpr
[reg_num
]);
132 g_assert_not_reached();
135 static TCGv
gpr_dst(DisasContext
*ctx
, int reg_num
, DisasExtend dst_ext
)
137 if (reg_num
== 0 || dst_ext
) {
138 return temp_new(ctx
);
140 return cpu_gpr
[reg_num
];
143 static void gen_set_gpr(int reg_num
, TCGv t
, DisasExtend dst_ext
)
148 tcg_gen_mov_tl(cpu_gpr
[reg_num
], t
);
151 tcg_gen_ext32s_tl(cpu_gpr
[reg_num
], t
);
154 tcg_gen_ext32u_tl(cpu_gpr
[reg_num
], t
);
157 g_assert_not_reached();
162 #include "decode-insns.c.inc"
163 #include "insn_trans/trans_arith.c.inc"
164 #include "insn_trans/trans_shift.c.inc"
165 #include "insn_trans/trans_bit.c.inc"
166 #include "insn_trans/trans_memory.c.inc"
167 #include "insn_trans/trans_atomic.c.inc"
168 #include "insn_trans/trans_extra.c.inc"
169 #include "insn_trans/trans_farith.c.inc"
170 #include "insn_trans/trans_fcmp.c.inc"
171 #include "insn_trans/trans_fcnv.c.inc"
172 #include "insn_trans/trans_fmov.c.inc"
173 #include "insn_trans/trans_fmemory.c.inc"
175 static void loongarch_tr_translate_insn(DisasContextBase
*dcbase
, CPUState
*cs
)
177 CPULoongArchState
*env
= cs
->env_ptr
;
178 DisasContext
*ctx
= container_of(dcbase
, DisasContext
, base
);
180 ctx
->opcode
= cpu_ldl_code(env
, ctx
->base
.pc_next
);
182 if (!decode(ctx
, ctx
->opcode
)) {
183 qemu_log_mask(LOG_UNIMP
, "Error: unknown opcode. "
184 TARGET_FMT_lx
": 0x%x\n",
185 ctx
->base
.pc_next
, ctx
->opcode
);
186 generate_exception(ctx
, EXCCODE_INE
);
189 for (int i
= ctx
->ntemp
- 1; i
>= 0; --i
) {
190 tcg_temp_free(ctx
->temp
[i
]);
195 ctx
->base
.pc_next
+= 4;
198 static void loongarch_tr_tb_stop(DisasContextBase
*dcbase
, CPUState
*cs
)
200 DisasContext
*ctx
= container_of(dcbase
, DisasContext
, base
);
202 switch (ctx
->base
.is_jmp
) {
204 tcg_gen_movi_tl(cpu_pc
, ctx
->base
.pc_next
);
205 tcg_gen_lookup_and_goto_ptr();
208 gen_goto_tb(ctx
, 0, ctx
->base
.pc_next
);
213 g_assert_not_reached();
217 static void loongarch_tr_disas_log(const DisasContextBase
*dcbase
,
218 CPUState
*cpu
, FILE *logfile
)
220 qemu_log("IN: %s\n", lookup_symbol(dcbase
->pc_first
));
221 target_disas(logfile
, cpu
, dcbase
->pc_first
, dcbase
->tb
->size
);
224 static const TranslatorOps loongarch_tr_ops
= {
225 .init_disas_context
= loongarch_tr_init_disas_context
,
226 .tb_start
= loongarch_tr_tb_start
,
227 .insn_start
= loongarch_tr_insn_start
,
228 .translate_insn
= loongarch_tr_translate_insn
,
229 .tb_stop
= loongarch_tr_tb_stop
,
230 .disas_log
= loongarch_tr_disas_log
,
233 void gen_intermediate_code(CPUState
*cs
, TranslationBlock
*tb
, int max_insns
)
237 translator_loop(&loongarch_tr_ops
, &ctx
.base
, cs
, tb
, max_insns
);
240 void loongarch_translate_init(void)
245 for (i
= 1; i
< 32; i
++) {
246 cpu_gpr
[i
] = tcg_global_mem_new(cpu_env
,
247 offsetof(CPULoongArchState
, gpr
[i
]),
251 for (i
= 0; i
< 32; i
++) {
252 int off
= offsetof(CPULoongArchState
, fpr
[i
]);
253 cpu_fpr
[i
] = tcg_global_mem_new_i64(cpu_env
, off
, fregnames
[i
]);
256 cpu_pc
= tcg_global_mem_new(cpu_env
, offsetof(CPULoongArchState
, pc
), "pc");
257 cpu_fcsr0
= tcg_global_mem_new_i32(cpu_env
,
258 offsetof(CPULoongArchState
, fcsr0
), "fcsr0");
259 cpu_lladdr
= tcg_global_mem_new(cpu_env
,
260 offsetof(CPULoongArchState
, lladdr
), "lladdr");
261 cpu_llval
= tcg_global_mem_new(cpu_env
,
262 offsetof(CPULoongArchState
, llval
), "llval");
265 void restore_state_to_opc(CPULoongArchState
*env
, TranslationBlock
*tb
,