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