xref: /openbmc/qemu/target/mips/tcg/loong_translate.c (revision ad6e1f19)
1 /*
2  * MIPS Loongson 64-bit translation routines
3  *
4  *  Copyright (c) 2004-2005 Jocelyn Mayer
5  *  Copyright (c) 2006 Marius Groeger (FPU operations)
6  *  Copyright (c) 2006 Thiemo Seufer (MIPS32R2 support)
7  *  Copyright (c) 2011 Richard Henderson <rth@twiddle.net>
8  *  Copyright (c) 2021 Philippe Mathieu-Daudé
9  *
10  * This code is licensed under the GNU GPLv2 and later.
11  */
12 
13 #include "qemu/osdep.h"
14 #include "translate.h"
15 
16 /* Include the auto-generated decoder.  */
17 #include "decode-godson2.c.inc"
18 #include "decode-loong-ext.c.inc"
19 
20 /*
21  * Word or double-word Fixed-point instructions.
22  * ---------------------------------------------
23  *
24  * Fixed-point multiplies and divisions write only
25  * one result into general-purpose registers.
26  */
27 
28 static bool gen_lext_DIV_G(DisasContext *s, int rd, int rs, int rt,
29                            bool is_double)
30 {
31     TCGv t0, t1;
32     TCGLabel *l1, *l2, *l3;
33 
34     if (is_double) {
35         if (TARGET_LONG_BITS != 64) {
36             return false;
37         }
38         check_mips_64(s);
39     }
40 
41     if (rd == 0) {
42         /* Treat as NOP. */
43         return true;
44     }
45 
46     t0 = tcg_temp_new();
47     t1 = tcg_temp_new();
48     l1 = gen_new_label();
49     l2 = gen_new_label();
50     l3 = gen_new_label();
51 
52     gen_load_gpr(t0, rs);
53     gen_load_gpr(t1, rt);
54 
55     if (!is_double) {
56         tcg_gen_ext32s_tl(t0, t0);
57         tcg_gen_ext32s_tl(t1, t1);
58     }
59     tcg_gen_brcondi_tl(TCG_COND_NE, t1, 0, l1);
60     tcg_gen_movi_tl(cpu_gpr[rd], 0);
61     tcg_gen_br(l3);
62     gen_set_label(l1);
63 
64     tcg_gen_brcondi_tl(TCG_COND_NE, t0, is_double && TARGET_LONG_BITS == 64
65                                         ? LLONG_MIN : INT_MIN, l2);
66     tcg_gen_brcondi_tl(TCG_COND_NE, t1, -1LL, l2);
67     tcg_gen_mov_tl(cpu_gpr[rd], t0);
68 
69     tcg_gen_br(l3);
70     gen_set_label(l2);
71     tcg_gen_div_tl(cpu_gpr[rd], t0, t1);
72     if (!is_double) {
73         tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]);
74     }
75     gen_set_label(l3);
76 
77     return true;
78 }
79 
80 static bool trans_DIV_G(DisasContext *s, arg_muldiv *a)
81 {
82     return gen_lext_DIV_G(s, a->rd, a->rs, a->rt, false);
83 }
84 
85 static bool trans_DDIV_G(DisasContext *s, arg_muldiv *a)
86 {
87     return gen_lext_DIV_G(s, a->rd, a->rs, a->rt, true);
88 }
89 
90 static bool gen_lext_DIVU_G(DisasContext *s, int rd, int rs, int rt,
91                             bool is_double)
92 {
93     TCGv t0, t1;
94     TCGLabel *l1, *l2;
95 
96     if (is_double) {
97         if (TARGET_LONG_BITS != 64) {
98             return false;
99         }
100         check_mips_64(s);
101     }
102 
103     if (rd == 0) {
104         /* Treat as NOP. */
105         return true;
106     }
107 
108     t0 = tcg_temp_new();
109     t1 = tcg_temp_new();
110     l1 = gen_new_label();
111     l2 = gen_new_label();
112 
113     gen_load_gpr(t0, rs);
114     gen_load_gpr(t1, rt);
115 
116     if (!is_double) {
117         tcg_gen_ext32u_tl(t0, t0);
118         tcg_gen_ext32u_tl(t1, t1);
119     }
120     tcg_gen_brcondi_tl(TCG_COND_NE, t1, 0, l1);
121     tcg_gen_movi_tl(cpu_gpr[rd], 0);
122 
123     tcg_gen_br(l2);
124     gen_set_label(l1);
125     tcg_gen_divu_tl(cpu_gpr[rd], t0, t1);
126     if (!is_double) {
127         tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]);
128     }
129     gen_set_label(l2);
130 
131     return true;
132 }
133 
134 static bool trans_DIVU_G(DisasContext *s, arg_muldiv *a)
135 {
136     return gen_lext_DIVU_G(s, a->rd, a->rs, a->rt, false);
137 }
138 
139 static bool trans_DDIVU_G(DisasContext *s, arg_muldiv *a)
140 {
141     return gen_lext_DIVU_G(s, a->rd, a->rs, a->rt, true);
142 }
143 
144 static bool gen_lext_MOD_G(DisasContext *s, int rd, int rs, int rt,
145                            bool is_double)
146 {
147     TCGv t0, t1;
148     TCGLabel *l1, *l2, *l3;
149 
150     if (is_double) {
151         if (TARGET_LONG_BITS != 64) {
152             return false;
153         }
154         check_mips_64(s);
155     }
156 
157     if (rd == 0) {
158         /* Treat as NOP. */
159         return true;
160     }
161 
162     t0 = tcg_temp_new();
163     t1 = tcg_temp_new();
164     l1 = gen_new_label();
165     l2 = gen_new_label();
166     l3 = gen_new_label();
167 
168     gen_load_gpr(t0, rs);
169     gen_load_gpr(t1, rt);
170 
171     if (!is_double) {
172         tcg_gen_ext32u_tl(t0, t0);
173         tcg_gen_ext32u_tl(t1, t1);
174     }
175     tcg_gen_brcondi_tl(TCG_COND_EQ, t1, 0, l1);
176     tcg_gen_brcondi_tl(TCG_COND_NE, t0, is_double && TARGET_LONG_BITS == 64
177                                         ? LLONG_MIN : INT_MIN, l2);
178     tcg_gen_brcondi_tl(TCG_COND_NE, t1, -1LL, l2);
179     gen_set_label(l1);
180     tcg_gen_movi_tl(cpu_gpr[rd], 0);
181     tcg_gen_br(l3);
182     gen_set_label(l2);
183     tcg_gen_rem_tl(cpu_gpr[rd], t0, t1);
184     if (!is_double) {
185         tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]);
186     }
187     gen_set_label(l3);
188 
189     return true;
190 }
191 
192 static bool trans_MOD_G(DisasContext *s, arg_muldiv *a)
193 {
194     return gen_lext_MOD_G(s, a->rd, a->rs, a->rt, false);
195 }
196 
197 static bool trans_DMOD_G(DisasContext *s, arg_muldiv *a)
198 {
199     return gen_lext_MOD_G(s, a->rd, a->rs, a->rt, true);
200 }
201 
202 static bool gen_lext_MODU_G(DisasContext *s, int rd, int rs, int rt,
203                             bool is_double)
204 {
205     TCGv t0, t1;
206     TCGLabel *l1, *l2;
207 
208     if (is_double) {
209         if (TARGET_LONG_BITS != 64) {
210             return false;
211         }
212         check_mips_64(s);
213     }
214 
215     if (rd == 0) {
216         /* Treat as NOP. */
217         return true;
218     }
219 
220     t0 = tcg_temp_new();
221     t1 = tcg_temp_new();
222     l1 = gen_new_label();
223     l2 = gen_new_label();
224 
225     gen_load_gpr(t0, rs);
226     gen_load_gpr(t1, rt);
227 
228     if (!is_double) {
229         tcg_gen_ext32u_tl(t0, t0);
230         tcg_gen_ext32u_tl(t1, t1);
231     }
232     tcg_gen_brcondi_tl(TCG_COND_NE, t1, 0, l1);
233     tcg_gen_movi_tl(cpu_gpr[rd], 0);
234     tcg_gen_br(l2);
235     gen_set_label(l1);
236     tcg_gen_remu_tl(cpu_gpr[rd], t0, t1);
237     if (!is_double) {
238         tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]);
239     }
240     gen_set_label(l2);
241 
242     return true;
243 }
244 
245 static bool trans_MODU_G(DisasContext *s, arg_muldiv *a)
246 {
247     return gen_lext_MODU_G(s, a->rd, a->rs, a->rt, false);
248 }
249 
250 static bool trans_DMODU_G(DisasContext *s, arg_muldiv *a)
251 {
252     return gen_lext_MODU_G(s, a->rd, a->rs, a->rt, true);
253 }
254 
255 static bool gen_lext_MULT_G(DisasContext *s, int rd, int rs, int rt,
256                             bool is_double)
257 {
258     TCGv t0, t1;
259 
260     if (is_double) {
261         if (TARGET_LONG_BITS != 64) {
262             return false;
263         }
264         check_mips_64(s);
265     }
266 
267     if (rd == 0) {
268         /* Treat as NOP. */
269         return true;
270     }
271 
272     t0 = tcg_temp_new();
273     t1 = tcg_temp_new();
274 
275     gen_load_gpr(t0, rs);
276     gen_load_gpr(t1, rt);
277 
278     tcg_gen_mul_tl(cpu_gpr[rd], t0, t1);
279     if (!is_double) {
280         tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]);
281     }
282 
283     return true;
284 }
285 
286 static bool trans_MULTu_G(DisasContext *s, arg_muldiv *a)
287 {
288     return gen_lext_MULT_G(s, a->rd, a->rs, a->rt, false);
289 }
290 
291 static bool trans_DMULTu_G(DisasContext *s, arg_muldiv *a)
292 {
293     return gen_lext_MULT_G(s, a->rd, a->rs, a->rt, true);
294 }
295 
296 bool decode_ext_loongson(DisasContext *ctx, uint32_t insn)
297 {
298     if (!decode_64bit_enabled(ctx)) {
299         return false;
300     }
301     if ((ctx->insn_flags & INSN_LOONGSON2E) && decode_godson2(ctx, ctx->opcode)) {
302         return true;
303     }
304     if ((ctx->insn_flags & ASE_LEXT) && decode_loong_ext(ctx, ctx->opcode)) {
305         return true;
306     }
307     return false;
308 }
309