xref: /openbmc/qemu/target/loongarch/translate.c (revision ab930e80)
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 /*
3  * LoongArch emulation for QEMU - main translation routines.
4  *
5  * Copyright (c) 2021 Loongson Technology Corporation Limited
6  */
7 
8 #include "qemu/osdep.h"
9 #include "cpu.h"
10 #include "tcg/tcg-op.h"
11 #include "tcg/tcg-op-gvec.h"
12 
13 #include "exec/translator.h"
14 #include "exec/helper-proto.h"
15 #include "exec/helper-gen.h"
16 
17 #include "exec/log.h"
18 #include "qemu/qemu-print.h"
19 #include "fpu/softfloat.h"
20 #include "translate.h"
21 #include "internals.h"
22 
23 /* Global register indices */
24 TCGv cpu_gpr[32], cpu_pc;
25 static TCGv cpu_lladdr, cpu_llval;
26 
27 #include "exec/gen-icount.h"
28 
29 #define DISAS_STOP        DISAS_TARGET_0
30 #define DISAS_EXIT        DISAS_TARGET_1
31 #define DISAS_EXIT_UPDATE DISAS_TARGET_2
32 
33 static inline int vec_full_offset(int regno)
34 {
35     return  offsetof(CPULoongArchState, fpr[regno]);
36 }
37 
38 static inline void get_vreg64(TCGv_i64 dest, int regno, int index)
39 {
40     tcg_gen_ld_i64(dest, cpu_env,
41                    offsetof(CPULoongArchState, fpr[regno].vreg.D(index)));
42 }
43 
44 static inline void set_vreg64(TCGv_i64 src, int regno, int index)
45 {
46     tcg_gen_st_i64(src, cpu_env,
47                    offsetof(CPULoongArchState, fpr[regno].vreg.D(index)));
48 }
49 
50 static inline int plus_1(DisasContext *ctx, int x)
51 {
52     return x + 1;
53 }
54 
55 static inline int shl_1(DisasContext *ctx, int x)
56 {
57     return x << 1;
58 }
59 
60 static inline int shl_2(DisasContext *ctx, int x)
61 {
62     return x << 2;
63 }
64 
65 static inline int shl_3(DisasContext *ctx, int x)
66 {
67     return x << 3;
68 }
69 
70 /*
71  * LoongArch the upper 32 bits are undefined ("can be any value").
72  * QEMU chooses to nanbox, because it is most likely to show guest bugs early.
73  */
74 static void gen_nanbox_s(TCGv_i64 out, TCGv_i64 in)
75 {
76     tcg_gen_ori_i64(out, in, MAKE_64BIT_MASK(32, 32));
77 }
78 
79 void generate_exception(DisasContext *ctx, int excp)
80 {
81     tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next);
82     gen_helper_raise_exception(cpu_env, tcg_constant_i32(excp));
83     ctx->base.is_jmp = DISAS_NORETURN;
84 }
85 
86 static inline void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest)
87 {
88     if (translator_use_goto_tb(&ctx->base, dest)) {
89         tcg_gen_goto_tb(n);
90         tcg_gen_movi_tl(cpu_pc, dest);
91         tcg_gen_exit_tb(ctx->base.tb, n);
92     } else {
93         tcg_gen_movi_tl(cpu_pc, dest);
94         tcg_gen_lookup_and_goto_ptr();
95     }
96 }
97 
98 static void loongarch_tr_init_disas_context(DisasContextBase *dcbase,
99                                             CPUState *cs)
100 {
101     int64_t bound;
102     CPULoongArchState *env = cs->env_ptr;
103     DisasContext *ctx = container_of(dcbase, DisasContext, base);
104 
105     ctx->page_start = ctx->base.pc_first & TARGET_PAGE_MASK;
106     ctx->plv = ctx->base.tb->flags & HW_FLAGS_PLV_MASK;
107     if (ctx->base.tb->flags & HW_FLAGS_CRMD_PG) {
108         ctx->mem_idx = ctx->plv;
109     } else {
110         ctx->mem_idx = MMU_IDX_DA;
111     }
112 
113     /* Bound the number of insns to execute to those left on the page.  */
114     bound = -(ctx->base.pc_first | TARGET_PAGE_MASK) / 4;
115     ctx->base.max_insns = MIN(ctx->base.max_insns, bound);
116 
117     if (FIELD_EX64(env->cpucfg[2], CPUCFG2, LSX)) {
118         ctx->vl = LSX_LEN;
119     }
120 
121     ctx->zero = tcg_constant_tl(0);
122 }
123 
124 static void loongarch_tr_tb_start(DisasContextBase *dcbase, CPUState *cs)
125 {
126 }
127 
128 static void loongarch_tr_insn_start(DisasContextBase *dcbase, CPUState *cs)
129 {
130     DisasContext *ctx = container_of(dcbase, DisasContext, base);
131 
132     tcg_gen_insn_start(ctx->base.pc_next);
133 }
134 
135 /*
136  * Wrappers for getting reg values.
137  *
138  * The $zero register does not have cpu_gpr[0] allocated -- we supply the
139  * constant zero as a source, and an uninitialized sink as destination.
140  *
141  * Further, we may provide an extension for word operations.
142  */
143 static TCGv gpr_src(DisasContext *ctx, int reg_num, DisasExtend src_ext)
144 {
145     TCGv t;
146 
147     if (reg_num == 0) {
148         return ctx->zero;
149     }
150 
151     switch (src_ext) {
152     case EXT_NONE:
153         return cpu_gpr[reg_num];
154     case EXT_SIGN:
155         t = tcg_temp_new();
156         tcg_gen_ext32s_tl(t, cpu_gpr[reg_num]);
157         return t;
158     case EXT_ZERO:
159         t = tcg_temp_new();
160         tcg_gen_ext32u_tl(t, cpu_gpr[reg_num]);
161         return t;
162     }
163     g_assert_not_reached();
164 }
165 
166 static TCGv gpr_dst(DisasContext *ctx, int reg_num, DisasExtend dst_ext)
167 {
168     if (reg_num == 0 || dst_ext) {
169         return tcg_temp_new();
170     }
171     return cpu_gpr[reg_num];
172 }
173 
174 static void gen_set_gpr(int reg_num, TCGv t, DisasExtend dst_ext)
175 {
176     if (reg_num != 0) {
177         switch (dst_ext) {
178         case EXT_NONE:
179             tcg_gen_mov_tl(cpu_gpr[reg_num], t);
180             break;
181         case EXT_SIGN:
182             tcg_gen_ext32s_tl(cpu_gpr[reg_num], t);
183             break;
184         case EXT_ZERO:
185             tcg_gen_ext32u_tl(cpu_gpr[reg_num], t);
186             break;
187         default:
188             g_assert_not_reached();
189         }
190     }
191 }
192 
193 static TCGv get_fpr(DisasContext *ctx, int reg_num)
194 {
195     TCGv t = tcg_temp_new();
196     tcg_gen_ld_i64(t, cpu_env,
197                    offsetof(CPULoongArchState, fpr[reg_num].vreg.D(0)));
198     return  t;
199 }
200 
201 static void set_fpr(int reg_num, TCGv val)
202 {
203     tcg_gen_st_i64(val, cpu_env,
204                    offsetof(CPULoongArchState, fpr[reg_num].vreg.D(0)));
205 }
206 
207 #include "decode-insns.c.inc"
208 #include "insn_trans/trans_arith.c.inc"
209 #include "insn_trans/trans_shift.c.inc"
210 #include "insn_trans/trans_bit.c.inc"
211 #include "insn_trans/trans_memory.c.inc"
212 #include "insn_trans/trans_atomic.c.inc"
213 #include "insn_trans/trans_extra.c.inc"
214 #include "insn_trans/trans_farith.c.inc"
215 #include "insn_trans/trans_fcmp.c.inc"
216 #include "insn_trans/trans_fcnv.c.inc"
217 #include "insn_trans/trans_fmov.c.inc"
218 #include "insn_trans/trans_fmemory.c.inc"
219 #include "insn_trans/trans_branch.c.inc"
220 #include "insn_trans/trans_privileged.c.inc"
221 #include "insn_trans/trans_lsx.c.inc"
222 
223 static void loongarch_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs)
224 {
225     CPULoongArchState *env = cs->env_ptr;
226     DisasContext *ctx = container_of(dcbase, DisasContext, base);
227 
228     ctx->opcode = translator_ldl(env, &ctx->base, ctx->base.pc_next);
229 
230     if (!decode(ctx, ctx->opcode)) {
231         qemu_log_mask(LOG_UNIMP, "Error: unknown opcode. "
232                       TARGET_FMT_lx ": 0x%x\n",
233                       ctx->base.pc_next, ctx->opcode);
234         generate_exception(ctx, EXCCODE_INE);
235     }
236 
237     ctx->base.pc_next += 4;
238 }
239 
240 static void loongarch_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs)
241 {
242     DisasContext *ctx = container_of(dcbase, DisasContext, base);
243 
244     switch (ctx->base.is_jmp) {
245     case DISAS_STOP:
246         tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next);
247         tcg_gen_lookup_and_goto_ptr();
248         break;
249     case DISAS_TOO_MANY:
250         gen_goto_tb(ctx, 0, ctx->base.pc_next);
251         break;
252     case DISAS_NORETURN:
253         break;
254     case DISAS_EXIT_UPDATE:
255         tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next);
256         QEMU_FALLTHROUGH;
257     case DISAS_EXIT:
258         tcg_gen_exit_tb(NULL, 0);
259         break;
260     default:
261         g_assert_not_reached();
262     }
263 }
264 
265 static void loongarch_tr_disas_log(const DisasContextBase *dcbase,
266                                    CPUState *cpu, FILE *logfile)
267 {
268     qemu_log("IN: %s\n", lookup_symbol(dcbase->pc_first));
269     target_disas(logfile, cpu, dcbase->pc_first, dcbase->tb->size);
270 }
271 
272 static const TranslatorOps loongarch_tr_ops = {
273     .init_disas_context = loongarch_tr_init_disas_context,
274     .tb_start           = loongarch_tr_tb_start,
275     .insn_start         = loongarch_tr_insn_start,
276     .translate_insn     = loongarch_tr_translate_insn,
277     .tb_stop            = loongarch_tr_tb_stop,
278     .disas_log          = loongarch_tr_disas_log,
279 };
280 
281 void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns,
282                            target_ulong pc, void *host_pc)
283 {
284     DisasContext ctx;
285 
286     translator_loop(cs, tb, max_insns, pc, host_pc,
287                     &loongarch_tr_ops, &ctx.base);
288 }
289 
290 void loongarch_translate_init(void)
291 {
292     int i;
293 
294     cpu_gpr[0] = NULL;
295     for (i = 1; i < 32; i++) {
296         cpu_gpr[i] = tcg_global_mem_new(cpu_env,
297                                         offsetof(CPULoongArchState, gpr[i]),
298                                         regnames[i]);
299     }
300 
301     cpu_pc = tcg_global_mem_new(cpu_env, offsetof(CPULoongArchState, pc), "pc");
302     cpu_lladdr = tcg_global_mem_new(cpu_env,
303                     offsetof(CPULoongArchState, lladdr), "lladdr");
304     cpu_llval = tcg_global_mem_new(cpu_env,
305                     offsetof(CPULoongArchState, llval), "llval");
306 }
307