]>
Commit | Line | Data |
---|---|---|
f8da88d7 SG |
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 "translate.h" | |
19 | #include "internals.h" | |
20 | ||
21 | /* Global register indices */ | |
22 | TCGv cpu_gpr[32], cpu_pc; | |
23 | static TCGv cpu_lladdr, cpu_llval; | |
24 | TCGv_i32 cpu_fcsr0; | |
25 | TCGv_i64 cpu_fpr[32]; | |
26 | ||
27 | #define DISAS_STOP DISAS_TARGET_0 | |
28 | ||
143d6785 SG |
29 | static inline int plus_1(DisasContext *ctx, int x) |
30 | { | |
31 | return x + 1; | |
32 | } | |
33 | ||
bb79174d SG |
34 | static inline int shl_2(DisasContext *ctx, int x) |
35 | { | |
36 | return x << 2; | |
37 | } | |
38 | ||
f8da88d7 SG |
39 | void generate_exception(DisasContext *ctx, int excp) |
40 | { | |
41 | tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next); | |
42 | gen_helper_raise_exception(cpu_env, tcg_constant_i32(excp)); | |
43 | ctx->base.is_jmp = DISAS_NORETURN; | |
44 | } | |
45 | ||
46 | static inline void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) | |
47 | { | |
48 | if (translator_use_goto_tb(&ctx->base, dest)) { | |
49 | tcg_gen_goto_tb(n); | |
50 | tcg_gen_movi_tl(cpu_pc, dest); | |
51 | tcg_gen_exit_tb(ctx->base.tb, n); | |
52 | } else { | |
53 | tcg_gen_movi_tl(cpu_pc, dest); | |
54 | tcg_gen_lookup_and_goto_ptr(); | |
55 | } | |
56 | } | |
57 | ||
58 | static void loongarch_tr_init_disas_context(DisasContextBase *dcbase, | |
59 | CPUState *cs) | |
60 | { | |
61 | int64_t bound; | |
62 | DisasContext *ctx = container_of(dcbase, DisasContext, base); | |
63 | ||
64 | ctx->page_start = ctx->base.pc_first & TARGET_PAGE_MASK; | |
65 | ctx->mem_idx = ctx->base.tb->flags; | |
66 | ||
67 | /* Bound the number of insns to execute to those left on the page. */ | |
68 | bound = -(ctx->base.pc_first | TARGET_PAGE_MASK) / 4; | |
69 | ctx->base.max_insns = MIN(ctx->base.max_insns, bound); | |
143d6785 SG |
70 | |
71 | ctx->ntemp = 0; | |
72 | memset(ctx->temp, 0, sizeof(ctx->temp)); | |
73 | ||
74 | ctx->zero = tcg_constant_tl(0); | |
f8da88d7 SG |
75 | } |
76 | ||
77 | static void loongarch_tr_tb_start(DisasContextBase *dcbase, CPUState *cs) | |
78 | { | |
79 | } | |
80 | ||
81 | static void loongarch_tr_insn_start(DisasContextBase *dcbase, CPUState *cs) | |
82 | { | |
83 | DisasContext *ctx = container_of(dcbase, DisasContext, base); | |
84 | ||
85 | tcg_gen_insn_start(ctx->base.pc_next); | |
86 | } | |
87 | ||
143d6785 SG |
88 | /* |
89 | * Wrappers for getting reg values. | |
90 | * | |
91 | * The $zero register does not have cpu_gpr[0] allocated -- we supply the | |
92 | * constant zero as a source, and an uninitialized sink as destination. | |
93 | * | |
94 | * Further, we may provide an extension for word operations. | |
95 | */ | |
96 | static TCGv temp_new(DisasContext *ctx) | |
97 | { | |
98 | assert(ctx->ntemp < ARRAY_SIZE(ctx->temp)); | |
99 | return ctx->temp[ctx->ntemp++] = tcg_temp_new(); | |
100 | } | |
101 | ||
102 | static TCGv gpr_src(DisasContext *ctx, int reg_num, DisasExtend src_ext) | |
103 | { | |
104 | TCGv t; | |
105 | ||
106 | if (reg_num == 0) { | |
107 | return ctx->zero; | |
108 | } | |
109 | ||
110 | switch (src_ext) { | |
111 | case EXT_NONE: | |
112 | return cpu_gpr[reg_num]; | |
113 | case EXT_SIGN: | |
114 | t = temp_new(ctx); | |
115 | tcg_gen_ext32s_tl(t, cpu_gpr[reg_num]); | |
116 | return t; | |
117 | case EXT_ZERO: | |
118 | t = temp_new(ctx); | |
119 | tcg_gen_ext32u_tl(t, cpu_gpr[reg_num]); | |
120 | return t; | |
121 | } | |
122 | g_assert_not_reached(); | |
123 | } | |
124 | ||
125 | static TCGv gpr_dst(DisasContext *ctx, int reg_num, DisasExtend dst_ext) | |
126 | { | |
127 | if (reg_num == 0 || dst_ext) { | |
128 | return temp_new(ctx); | |
129 | } | |
130 | return cpu_gpr[reg_num]; | |
131 | } | |
132 | ||
133 | static void gen_set_gpr(int reg_num, TCGv t, DisasExtend dst_ext) | |
134 | { | |
135 | if (reg_num != 0) { | |
136 | switch (dst_ext) { | |
137 | case EXT_NONE: | |
138 | tcg_gen_mov_tl(cpu_gpr[reg_num], t); | |
139 | break; | |
140 | case EXT_SIGN: | |
141 | tcg_gen_ext32s_tl(cpu_gpr[reg_num], t); | |
142 | break; | |
143 | case EXT_ZERO: | |
144 | tcg_gen_ext32u_tl(cpu_gpr[reg_num], t); | |
145 | break; | |
146 | default: | |
147 | g_assert_not_reached(); | |
148 | } | |
149 | } | |
150 | } | |
151 | ||
152 | #include "decode-insns.c.inc" | |
153 | #include "insn_trans/trans_arith.c.inc" | |
63cfcd47 | 154 | #include "insn_trans/trans_shift.c.inc" |
ad08cb3f | 155 | #include "insn_trans/trans_bit.c.inc" |
bb79174d | 156 | #include "insn_trans/trans_memory.c.inc" |
94b02d57 | 157 | #include "insn_trans/trans_atomic.c.inc" |
143d6785 | 158 | |
f8da88d7 SG |
159 | static void loongarch_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) |
160 | { | |
161 | CPULoongArchState *env = cs->env_ptr; | |
162 | DisasContext *ctx = container_of(dcbase, DisasContext, base); | |
163 | ||
164 | ctx->opcode = cpu_ldl_code(env, ctx->base.pc_next); | |
165 | ||
166 | if (!decode(ctx, ctx->opcode)) { | |
167 | qemu_log_mask(LOG_UNIMP, "Error: unknown opcode. " | |
168 | TARGET_FMT_lx ": 0x%x\n", | |
169 | ctx->base.pc_next, ctx->opcode); | |
170 | generate_exception(ctx, EXCCODE_INE); | |
171 | } | |
172 | ||
143d6785 SG |
173 | for (int i = ctx->ntemp - 1; i >= 0; --i) { |
174 | tcg_temp_free(ctx->temp[i]); | |
175 | ctx->temp[i] = NULL; | |
176 | } | |
177 | ctx->ntemp = 0; | |
178 | ||
f8da88d7 SG |
179 | ctx->base.pc_next += 4; |
180 | } | |
181 | ||
182 | static void loongarch_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) | |
183 | { | |
184 | DisasContext *ctx = container_of(dcbase, DisasContext, base); | |
185 | ||
186 | switch (ctx->base.is_jmp) { | |
187 | case DISAS_STOP: | |
188 | tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next); | |
189 | tcg_gen_lookup_and_goto_ptr(); | |
190 | break; | |
191 | case DISAS_TOO_MANY: | |
192 | gen_goto_tb(ctx, 0, ctx->base.pc_next); | |
193 | break; | |
194 | case DISAS_NORETURN: | |
195 | break; | |
196 | default: | |
197 | g_assert_not_reached(); | |
198 | } | |
199 | } | |
200 | ||
201 | static void loongarch_tr_disas_log(const DisasContextBase *dcbase, | |
202 | CPUState *cpu, FILE *logfile) | |
203 | { | |
204 | qemu_log("IN: %s\n", lookup_symbol(dcbase->pc_first)); | |
205 | target_disas(logfile, cpu, dcbase->pc_first, dcbase->tb->size); | |
206 | } | |
207 | ||
208 | static const TranslatorOps loongarch_tr_ops = { | |
209 | .init_disas_context = loongarch_tr_init_disas_context, | |
210 | .tb_start = loongarch_tr_tb_start, | |
211 | .insn_start = loongarch_tr_insn_start, | |
212 | .translate_insn = loongarch_tr_translate_insn, | |
213 | .tb_stop = loongarch_tr_tb_stop, | |
214 | .disas_log = loongarch_tr_disas_log, | |
215 | }; | |
216 | ||
217 | void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns) | |
218 | { | |
219 | DisasContext ctx; | |
220 | ||
221 | translator_loop(&loongarch_tr_ops, &ctx.base, cs, tb, max_insns); | |
222 | } | |
223 | ||
224 | void loongarch_translate_init(void) | |
225 | { | |
226 | int i; | |
227 | ||
228 | cpu_gpr[0] = NULL; | |
229 | for (i = 1; i < 32; i++) { | |
230 | cpu_gpr[i] = tcg_global_mem_new(cpu_env, | |
231 | offsetof(CPULoongArchState, gpr[i]), | |
232 | regnames[i]); | |
233 | } | |
234 | ||
235 | for (i = 0; i < 32; i++) { | |
236 | int off = offsetof(CPULoongArchState, fpr[i]); | |
237 | cpu_fpr[i] = tcg_global_mem_new_i64(cpu_env, off, fregnames[i]); | |
238 | } | |
239 | ||
240 | cpu_pc = tcg_global_mem_new(cpu_env, offsetof(CPULoongArchState, pc), "pc"); | |
241 | cpu_fcsr0 = tcg_global_mem_new_i32(cpu_env, | |
242 | offsetof(CPULoongArchState, fcsr0), "fcsr0"); | |
243 | cpu_lladdr = tcg_global_mem_new(cpu_env, | |
244 | offsetof(CPULoongArchState, lladdr), "lladdr"); | |
245 | cpu_llval = tcg_global_mem_new(cpu_env, | |
246 | offsetof(CPULoongArchState, llval), "llval"); | |
247 | } | |
248 | ||
249 | void restore_state_to_opc(CPULoongArchState *env, TranslationBlock *tb, | |
250 | target_ulong *data) | |
251 | { | |
252 | env->pc = data[0]; | |
253 | } |