]>
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 | ||
f8da88d7 SG |
34 | void generate_exception(DisasContext *ctx, int excp) |
35 | { | |
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; | |
39 | } | |
40 | ||
41 | static inline void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) | |
42 | { | |
43 | if (translator_use_goto_tb(&ctx->base, dest)) { | |
44 | tcg_gen_goto_tb(n); | |
45 | tcg_gen_movi_tl(cpu_pc, dest); | |
46 | tcg_gen_exit_tb(ctx->base.tb, n); | |
47 | } else { | |
48 | tcg_gen_movi_tl(cpu_pc, dest); | |
49 | tcg_gen_lookup_and_goto_ptr(); | |
50 | } | |
51 | } | |
52 | ||
53 | static void loongarch_tr_init_disas_context(DisasContextBase *dcbase, | |
54 | CPUState *cs) | |
55 | { | |
56 | int64_t bound; | |
57 | DisasContext *ctx = container_of(dcbase, DisasContext, base); | |
58 | ||
59 | ctx->page_start = ctx->base.pc_first & TARGET_PAGE_MASK; | |
60 | ctx->mem_idx = ctx->base.tb->flags; | |
61 | ||
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); | |
143d6785 SG |
65 | |
66 | ctx->ntemp = 0; | |
67 | memset(ctx->temp, 0, sizeof(ctx->temp)); | |
68 | ||
69 | ctx->zero = tcg_constant_tl(0); | |
f8da88d7 SG |
70 | } |
71 | ||
72 | static void loongarch_tr_tb_start(DisasContextBase *dcbase, CPUState *cs) | |
73 | { | |
74 | } | |
75 | ||
76 | static void loongarch_tr_insn_start(DisasContextBase *dcbase, CPUState *cs) | |
77 | { | |
78 | DisasContext *ctx = container_of(dcbase, DisasContext, base); | |
79 | ||
80 | tcg_gen_insn_start(ctx->base.pc_next); | |
81 | } | |
82 | ||
143d6785 SG |
83 | /* |
84 | * Wrappers for getting reg values. | |
85 | * | |
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. | |
88 | * | |
89 | * Further, we may provide an extension for word operations. | |
90 | */ | |
91 | static TCGv temp_new(DisasContext *ctx) | |
92 | { | |
93 | assert(ctx->ntemp < ARRAY_SIZE(ctx->temp)); | |
94 | return ctx->temp[ctx->ntemp++] = tcg_temp_new(); | |
95 | } | |
96 | ||
97 | static TCGv gpr_src(DisasContext *ctx, int reg_num, DisasExtend src_ext) | |
98 | { | |
99 | TCGv t; | |
100 | ||
101 | if (reg_num == 0) { | |
102 | return ctx->zero; | |
103 | } | |
104 | ||
105 | switch (src_ext) { | |
106 | case EXT_NONE: | |
107 | return cpu_gpr[reg_num]; | |
108 | case EXT_SIGN: | |
109 | t = temp_new(ctx); | |
110 | tcg_gen_ext32s_tl(t, cpu_gpr[reg_num]); | |
111 | return t; | |
112 | case EXT_ZERO: | |
113 | t = temp_new(ctx); | |
114 | tcg_gen_ext32u_tl(t, cpu_gpr[reg_num]); | |
115 | return t; | |
116 | } | |
117 | g_assert_not_reached(); | |
118 | } | |
119 | ||
120 | static TCGv gpr_dst(DisasContext *ctx, int reg_num, DisasExtend dst_ext) | |
121 | { | |
122 | if (reg_num == 0 || dst_ext) { | |
123 | return temp_new(ctx); | |
124 | } | |
125 | return cpu_gpr[reg_num]; | |
126 | } | |
127 | ||
128 | static void gen_set_gpr(int reg_num, TCGv t, DisasExtend dst_ext) | |
129 | { | |
130 | if (reg_num != 0) { | |
131 | switch (dst_ext) { | |
132 | case EXT_NONE: | |
133 | tcg_gen_mov_tl(cpu_gpr[reg_num], t); | |
134 | break; | |
135 | case EXT_SIGN: | |
136 | tcg_gen_ext32s_tl(cpu_gpr[reg_num], t); | |
137 | break; | |
138 | case EXT_ZERO: | |
139 | tcg_gen_ext32u_tl(cpu_gpr[reg_num], t); | |
140 | break; | |
141 | default: | |
142 | g_assert_not_reached(); | |
143 | } | |
144 | } | |
145 | } | |
146 | ||
147 | #include "decode-insns.c.inc" | |
148 | #include "insn_trans/trans_arith.c.inc" | |
63cfcd47 | 149 | #include "insn_trans/trans_shift.c.inc" |
143d6785 | 150 | |
f8da88d7 SG |
151 | static void loongarch_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) |
152 | { | |
153 | CPULoongArchState *env = cs->env_ptr; | |
154 | DisasContext *ctx = container_of(dcbase, DisasContext, base); | |
155 | ||
156 | ctx->opcode = cpu_ldl_code(env, ctx->base.pc_next); | |
157 | ||
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); | |
163 | } | |
164 | ||
143d6785 SG |
165 | for (int i = ctx->ntemp - 1; i >= 0; --i) { |
166 | tcg_temp_free(ctx->temp[i]); | |
167 | ctx->temp[i] = NULL; | |
168 | } | |
169 | ctx->ntemp = 0; | |
170 | ||
f8da88d7 SG |
171 | ctx->base.pc_next += 4; |
172 | } | |
173 | ||
174 | static void loongarch_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) | |
175 | { | |
176 | DisasContext *ctx = container_of(dcbase, DisasContext, base); | |
177 | ||
178 | switch (ctx->base.is_jmp) { | |
179 | case DISAS_STOP: | |
180 | tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next); | |
181 | tcg_gen_lookup_and_goto_ptr(); | |
182 | break; | |
183 | case DISAS_TOO_MANY: | |
184 | gen_goto_tb(ctx, 0, ctx->base.pc_next); | |
185 | break; | |
186 | case DISAS_NORETURN: | |
187 | break; | |
188 | default: | |
189 | g_assert_not_reached(); | |
190 | } | |
191 | } | |
192 | ||
193 | static void loongarch_tr_disas_log(const DisasContextBase *dcbase, | |
194 | CPUState *cpu, FILE *logfile) | |
195 | { | |
196 | qemu_log("IN: %s\n", lookup_symbol(dcbase->pc_first)); | |
197 | target_disas(logfile, cpu, dcbase->pc_first, dcbase->tb->size); | |
198 | } | |
199 | ||
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, | |
207 | }; | |
208 | ||
209 | void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns) | |
210 | { | |
211 | DisasContext ctx; | |
212 | ||
213 | translator_loop(&loongarch_tr_ops, &ctx.base, cs, tb, max_insns); | |
214 | } | |
215 | ||
216 | void loongarch_translate_init(void) | |
217 | { | |
218 | int i; | |
219 | ||
220 | cpu_gpr[0] = NULL; | |
221 | for (i = 1; i < 32; i++) { | |
222 | cpu_gpr[i] = tcg_global_mem_new(cpu_env, | |
223 | offsetof(CPULoongArchState, gpr[i]), | |
224 | regnames[i]); | |
225 | } | |
226 | ||
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]); | |
230 | } | |
231 | ||
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"); | |
239 | } | |
240 | ||
241 | void restore_state_to_opc(CPULoongArchState *env, TranslationBlock *tb, | |
242 | target_ulong *data) | |
243 | { | |
244 | env->pc = data[0]; | |
245 | } |