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 "translate.h"
19 #include "internals.h"
21 /* Global register indices */
22 TCGv cpu_gpr
[32], cpu_pc
;
23 static TCGv cpu_lladdr
, cpu_llval
;
27 #define DISAS_STOP DISAS_TARGET_0
29 static inline int plus_1(DisasContext
*ctx
, int x
)
34 void generate_exception(DisasContext
*ctx
, int excp
)
36 tcg_gen_movi_tl(cpu_pc
, ctx
->base
.pc_next
);
37 gen_helper_raise_exception(cpu_env
, tcg_constant_i32(excp
));
38 ctx
->base
.is_jmp
= DISAS_NORETURN
;
41 static inline void gen_goto_tb(DisasContext
*ctx
, int n
, target_ulong dest
)
43 if (translator_use_goto_tb(&ctx
->base
, dest
)) {
45 tcg_gen_movi_tl(cpu_pc
, dest
);
46 tcg_gen_exit_tb(ctx
->base
.tb
, n
);
48 tcg_gen_movi_tl(cpu_pc
, dest
);
49 tcg_gen_lookup_and_goto_ptr();
53 static void loongarch_tr_init_disas_context(DisasContextBase
*dcbase
,
57 DisasContext
*ctx
= container_of(dcbase
, DisasContext
, base
);
59 ctx
->page_start
= ctx
->base
.pc_first
& TARGET_PAGE_MASK
;
60 ctx
->mem_idx
= ctx
->base
.tb
->flags
;
62 /* Bound the number of insns to execute to those left on the page. */
63 bound
= -(ctx
->base
.pc_first
| TARGET_PAGE_MASK
) / 4;
64 ctx
->base
.max_insns
= MIN(ctx
->base
.max_insns
, bound
);
67 memset(ctx
->temp
, 0, sizeof(ctx
->temp
));
69 ctx
->zero
= tcg_constant_tl(0);
72 static void loongarch_tr_tb_start(DisasContextBase
*dcbase
, CPUState
*cs
)
76 static void loongarch_tr_insn_start(DisasContextBase
*dcbase
, CPUState
*cs
)
78 DisasContext
*ctx
= container_of(dcbase
, DisasContext
, base
);
80 tcg_gen_insn_start(ctx
->base
.pc_next
);
84 * Wrappers for getting reg values.
86 * The $zero register does not have cpu_gpr[0] allocated -- we supply the
87 * constant zero as a source, and an uninitialized sink as destination.
89 * Further, we may provide an extension for word operations.
91 static TCGv
temp_new(DisasContext
*ctx
)
93 assert(ctx
->ntemp
< ARRAY_SIZE(ctx
->temp
));
94 return ctx
->temp
[ctx
->ntemp
++] = tcg_temp_new();
97 static TCGv
gpr_src(DisasContext
*ctx
, int reg_num
, DisasExtend src_ext
)
107 return cpu_gpr
[reg_num
];
110 tcg_gen_ext32s_tl(t
, cpu_gpr
[reg_num
]);
114 tcg_gen_ext32u_tl(t
, cpu_gpr
[reg_num
]);
117 g_assert_not_reached();
120 static TCGv
gpr_dst(DisasContext
*ctx
, int reg_num
, DisasExtend dst_ext
)
122 if (reg_num
== 0 || dst_ext
) {
123 return temp_new(ctx
);
125 return cpu_gpr
[reg_num
];
128 static void gen_set_gpr(int reg_num
, TCGv t
, DisasExtend dst_ext
)
133 tcg_gen_mov_tl(cpu_gpr
[reg_num
], t
);
136 tcg_gen_ext32s_tl(cpu_gpr
[reg_num
], t
);
139 tcg_gen_ext32u_tl(cpu_gpr
[reg_num
], t
);
142 g_assert_not_reached();
147 #include "decode-insns.c.inc"
148 #include "insn_trans/trans_arith.c.inc"
149 #include "insn_trans/trans_shift.c.inc"
151 static void loongarch_tr_translate_insn(DisasContextBase
*dcbase
, CPUState
*cs
)
153 CPULoongArchState
*env
= cs
->env_ptr
;
154 DisasContext
*ctx
= container_of(dcbase
, DisasContext
, base
);
156 ctx
->opcode
= cpu_ldl_code(env
, ctx
->base
.pc_next
);
158 if (!decode(ctx
, ctx
->opcode
)) {
159 qemu_log_mask(LOG_UNIMP
, "Error: unknown opcode. "
160 TARGET_FMT_lx
": 0x%x\n",
161 ctx
->base
.pc_next
, ctx
->opcode
);
162 generate_exception(ctx
, EXCCODE_INE
);
165 for (int i
= ctx
->ntemp
- 1; i
>= 0; --i
) {
166 tcg_temp_free(ctx
->temp
[i
]);
171 ctx
->base
.pc_next
+= 4;
174 static void loongarch_tr_tb_stop(DisasContextBase
*dcbase
, CPUState
*cs
)
176 DisasContext
*ctx
= container_of(dcbase
, DisasContext
, base
);
178 switch (ctx
->base
.is_jmp
) {
180 tcg_gen_movi_tl(cpu_pc
, ctx
->base
.pc_next
);
181 tcg_gen_lookup_and_goto_ptr();
184 gen_goto_tb(ctx
, 0, ctx
->base
.pc_next
);
189 g_assert_not_reached();
193 static void loongarch_tr_disas_log(const DisasContextBase
*dcbase
,
194 CPUState
*cpu
, FILE *logfile
)
196 qemu_log("IN: %s\n", lookup_symbol(dcbase
->pc_first
));
197 target_disas(logfile
, cpu
, dcbase
->pc_first
, dcbase
->tb
->size
);
200 static const TranslatorOps loongarch_tr_ops
= {
201 .init_disas_context
= loongarch_tr_init_disas_context
,
202 .tb_start
= loongarch_tr_tb_start
,
203 .insn_start
= loongarch_tr_insn_start
,
204 .translate_insn
= loongarch_tr_translate_insn
,
205 .tb_stop
= loongarch_tr_tb_stop
,
206 .disas_log
= loongarch_tr_disas_log
,
209 void gen_intermediate_code(CPUState
*cs
, TranslationBlock
*tb
, int max_insns
)
213 translator_loop(&loongarch_tr_ops
, &ctx
.base
, cs
, tb
, max_insns
);
216 void loongarch_translate_init(void)
221 for (i
= 1; i
< 32; i
++) {
222 cpu_gpr
[i
] = tcg_global_mem_new(cpu_env
,
223 offsetof(CPULoongArchState
, gpr
[i
]),
227 for (i
= 0; i
< 32; i
++) {
228 int off
= offsetof(CPULoongArchState
, fpr
[i
]);
229 cpu_fpr
[i
] = tcg_global_mem_new_i64(cpu_env
, off
, fregnames
[i
]);
232 cpu_pc
= tcg_global_mem_new(cpu_env
, offsetof(CPULoongArchState
, pc
), "pc");
233 cpu_fcsr0
= tcg_global_mem_new_i32(cpu_env
,
234 offsetof(CPULoongArchState
, fcsr0
), "fcsr0");
235 cpu_lladdr
= tcg_global_mem_new(cpu_env
,
236 offsetof(CPULoongArchState
, lladdr
), "lladdr");
237 cpu_llval
= tcg_global_mem_new(cpu_env
,
238 offsetof(CPULoongArchState
, llval
), "llval");
241 void restore_state_to_opc(CPULoongArchState
*env
, TranslationBlock
*tb
,