xref: /openbmc/qemu/target/loongarch/translate.c (revision 75ac231c)
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 "exec/translator.h"
12 #include "exec/helper-proto.h"
13 #include "exec/helper-gen.h"
14 
15 #include "exec/translator.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 TCGv_i64 cpu_fpr[32];
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 plus_1(DisasContext *ctx, int x)
34 {
35     return x + 1;
36 }
37 
38 static inline int shl_2(DisasContext *ctx, int x)
39 {
40     return x << 2;
41 }
42 
43 /*
44  * LoongArch the upper 32 bits are undefined ("can be any value").
45  * QEMU chooses to nanbox, because it is most likely to show guest bugs early.
46  */
47 static void gen_nanbox_s(TCGv_i64 out, TCGv_i64 in)
48 {
49     tcg_gen_ori_i64(out, in, MAKE_64BIT_MASK(32, 32));
50 }
51 
52 void generate_exception(DisasContext *ctx, int excp)
53 {
54     tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next);
55     gen_helper_raise_exception(cpu_env, tcg_constant_i32(excp));
56     ctx->base.is_jmp = DISAS_NORETURN;
57 }
58 
59 static inline void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest)
60 {
61     if (translator_use_goto_tb(&ctx->base, dest)) {
62         tcg_gen_goto_tb(n);
63         tcg_gen_movi_tl(cpu_pc, dest);
64         tcg_gen_exit_tb(ctx->base.tb, n);
65     } else {
66         tcg_gen_movi_tl(cpu_pc, dest);
67         tcg_gen_lookup_and_goto_ptr();
68     }
69 }
70 
71 static void loongarch_tr_init_disas_context(DisasContextBase *dcbase,
72                                             CPUState *cs)
73 {
74     int64_t bound;
75     DisasContext *ctx = container_of(dcbase, DisasContext, base);
76 
77     ctx->page_start = ctx->base.pc_first & TARGET_PAGE_MASK;
78     if (ctx->base.tb->flags & HW_FLAGS_CRMD_PG) {
79         ctx->mem_idx = ctx->base.tb->flags & HW_FLAGS_PLV_MASK;
80     } else {
81         ctx->mem_idx = MMU_DA_IDX;
82     }
83 
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);
87 
88     ctx->ntemp = 0;
89     memset(ctx->temp, 0, sizeof(ctx->temp));
90 
91     ctx->zero = tcg_constant_tl(0);
92 }
93 
94 static void loongarch_tr_tb_start(DisasContextBase *dcbase, CPUState *cs)
95 {
96 }
97 
98 static void loongarch_tr_insn_start(DisasContextBase *dcbase, CPUState *cs)
99 {
100     DisasContext *ctx = container_of(dcbase, DisasContext, base);
101 
102     tcg_gen_insn_start(ctx->base.pc_next);
103 }
104 
105 /*
106  * Wrappers for getting reg values.
107  *
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.
110  *
111  * Further, we may provide an extension for word operations.
112  */
113 static TCGv temp_new(DisasContext *ctx)
114 {
115     assert(ctx->ntemp < ARRAY_SIZE(ctx->temp));
116     return ctx->temp[ctx->ntemp++] = tcg_temp_new();
117 }
118 
119 static TCGv gpr_src(DisasContext *ctx, int reg_num, DisasExtend src_ext)
120 {
121     TCGv t;
122 
123     if (reg_num == 0) {
124         return ctx->zero;
125     }
126 
127     switch (src_ext) {
128     case EXT_NONE:
129         return cpu_gpr[reg_num];
130     case EXT_SIGN:
131         t = temp_new(ctx);
132         tcg_gen_ext32s_tl(t, cpu_gpr[reg_num]);
133         return t;
134     case EXT_ZERO:
135         t = temp_new(ctx);
136         tcg_gen_ext32u_tl(t, cpu_gpr[reg_num]);
137         return t;
138     }
139     g_assert_not_reached();
140 }
141 
142 static TCGv gpr_dst(DisasContext *ctx, int reg_num, DisasExtend dst_ext)
143 {
144     if (reg_num == 0 || dst_ext) {
145         return temp_new(ctx);
146     }
147     return cpu_gpr[reg_num];
148 }
149 
150 static void gen_set_gpr(int reg_num, TCGv t, DisasExtend dst_ext)
151 {
152     if (reg_num != 0) {
153         switch (dst_ext) {
154         case EXT_NONE:
155             tcg_gen_mov_tl(cpu_gpr[reg_num], t);
156             break;
157         case EXT_SIGN:
158             tcg_gen_ext32s_tl(cpu_gpr[reg_num], t);
159             break;
160         case EXT_ZERO:
161             tcg_gen_ext32u_tl(cpu_gpr[reg_num], t);
162             break;
163         default:
164             g_assert_not_reached();
165         }
166     }
167 }
168 
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"
183 
184 static void loongarch_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs)
185 {
186     CPULoongArchState *env = cs->env_ptr;
187     DisasContext *ctx = container_of(dcbase, DisasContext, base);
188 
189     ctx->opcode = cpu_ldl_code(env, ctx->base.pc_next);
190 
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);
196     }
197 
198     for (int i = ctx->ntemp - 1; i >= 0; --i) {
199         tcg_temp_free(ctx->temp[i]);
200         ctx->temp[i] = NULL;
201     }
202     ctx->ntemp = 0;
203 
204     ctx->base.pc_next += 4;
205 }
206 
207 static void loongarch_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs)
208 {
209     DisasContext *ctx = container_of(dcbase, DisasContext, base);
210 
211     switch (ctx->base.is_jmp) {
212     case DISAS_STOP:
213         tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next);
214         tcg_gen_lookup_and_goto_ptr();
215         break;
216     case DISAS_TOO_MANY:
217         gen_goto_tb(ctx, 0, ctx->base.pc_next);
218         break;
219     case DISAS_NORETURN:
220         break;
221     case DISAS_EXIT_UPDATE:
222         tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next);
223         QEMU_FALLTHROUGH;
224     case DISAS_EXIT:
225         tcg_gen_exit_tb(NULL, 0);
226         break;
227     default:
228         g_assert_not_reached();
229     }
230 }
231 
232 static void loongarch_tr_disas_log(const DisasContextBase *dcbase,
233                                    CPUState *cpu, FILE *logfile)
234 {
235     qemu_log("IN: %s\n", lookup_symbol(dcbase->pc_first));
236     target_disas(logfile, cpu, dcbase->pc_first, dcbase->tb->size);
237 }
238 
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,
246 };
247 
248 void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns,
249                            target_ulong pc, void *host_pc)
250 {
251     DisasContext ctx;
252 
253     translator_loop(cs, tb, max_insns, pc, host_pc,
254                     &loongarch_tr_ops, &ctx.base);
255 }
256 
257 void loongarch_translate_init(void)
258 {
259     int i;
260 
261     cpu_gpr[0] = NULL;
262     for (i = 1; i < 32; i++) {
263         cpu_gpr[i] = tcg_global_mem_new(cpu_env,
264                                         offsetof(CPULoongArchState, gpr[i]),
265                                         regnames[i]);
266     }
267 
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]);
271     }
272 
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");
278 }
279