xref: /openbmc/qemu/target/mips/tcg/loong_translate.c (revision 7d87775f)
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 (rd == 0) {
35         /* Treat as NOP. */
36         return true;
37     }
38 
39     t0 = tcg_temp_new();
40     t1 = tcg_temp_new();
41     l1 = gen_new_label();
42     l2 = gen_new_label();
43     l3 = gen_new_label();
44 
45     gen_load_gpr(t0, rs);
46     gen_load_gpr(t1, rt);
47 
48     if (!is_double) {
49         tcg_gen_ext32s_tl(t0, t0);
50         tcg_gen_ext32s_tl(t1, t1);
51     }
52     tcg_gen_brcondi_tl(TCG_COND_NE, t1, 0, l1);
53     tcg_gen_movi_tl(cpu_gpr[rd], 0);
54     tcg_gen_br(l3);
55     gen_set_label(l1);
56 
57     tcg_gen_brcondi_tl(TCG_COND_NE, t0, is_double ? LLONG_MIN : INT_MIN, l2);
58     tcg_gen_brcondi_tl(TCG_COND_NE, t1, -1LL, l2);
59     tcg_gen_mov_tl(cpu_gpr[rd], t0);
60 
61     tcg_gen_br(l3);
62     gen_set_label(l2);
63     tcg_gen_div_tl(cpu_gpr[rd], t0, t1);
64     if (!is_double) {
65         tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]);
66     }
67     gen_set_label(l3);
68 
69     return true;
70 }
71 
72 static bool trans_DIV_G(DisasContext *s, arg_muldiv *a)
73 {
74     return gen_lext_DIV_G(s, a->rd, a->rs, a->rt, false);
75 }
76 
77 static bool trans_DDIV_G(DisasContext *s, arg_muldiv *a)
78 {
79     return gen_lext_DIV_G(s, a->rd, a->rs, a->rt, true);
80 }
81 
82 static bool gen_lext_DIVU_G(DisasContext *s, int rd, int rs, int rt,
83                             bool is_double)
84 {
85     TCGv t0, t1;
86     TCGLabel *l1, *l2;
87 
88     if (rd == 0) {
89         /* Treat as NOP. */
90         return true;
91     }
92 
93     t0 = tcg_temp_new();
94     t1 = tcg_temp_new();
95     l1 = gen_new_label();
96     l2 = gen_new_label();
97 
98     gen_load_gpr(t0, rs);
99     gen_load_gpr(t1, rt);
100 
101     if (!is_double) {
102         tcg_gen_ext32u_tl(t0, t0);
103         tcg_gen_ext32u_tl(t1, t1);
104     }
105     tcg_gen_brcondi_tl(TCG_COND_NE, t1, 0, l1);
106     tcg_gen_movi_tl(cpu_gpr[rd], 0);
107 
108     tcg_gen_br(l2);
109     gen_set_label(l1);
110     tcg_gen_divu_tl(cpu_gpr[rd], t0, t1);
111     if (!is_double) {
112         tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]);
113     }
114     gen_set_label(l2);
115 
116     return true;
117 }
118 
119 static bool trans_DIVU_G(DisasContext *s, arg_muldiv *a)
120 {
121     return gen_lext_DIVU_G(s, a->rd, a->rs, a->rt, false);
122 }
123 
124 static bool trans_DDIVU_G(DisasContext *s, arg_muldiv *a)
125 {
126     return gen_lext_DIVU_G(s, a->rd, a->rs, a->rt, true);
127 }
128 
129 static bool gen_lext_MOD_G(DisasContext *s, int rd, int rs, int rt,
130                            bool is_double)
131 {
132     TCGv t0, t1;
133     TCGLabel *l1, *l2, *l3;
134 
135     if (rd == 0) {
136         /* Treat as NOP. */
137         return true;
138     }
139 
140     t0 = tcg_temp_new();
141     t1 = tcg_temp_new();
142     l1 = gen_new_label();
143     l2 = gen_new_label();
144     l3 = gen_new_label();
145 
146     gen_load_gpr(t0, rs);
147     gen_load_gpr(t1, rt);
148 
149     if (!is_double) {
150         tcg_gen_ext32u_tl(t0, t0);
151         tcg_gen_ext32u_tl(t1, t1);
152     }
153     tcg_gen_brcondi_tl(TCG_COND_EQ, t1, 0, l1);
154     tcg_gen_brcondi_tl(TCG_COND_NE, t0, is_double ? LLONG_MIN : INT_MIN, l2);
155     tcg_gen_brcondi_tl(TCG_COND_NE, t1, -1LL, l2);
156     gen_set_label(l1);
157     tcg_gen_movi_tl(cpu_gpr[rd], 0);
158     tcg_gen_br(l3);
159     gen_set_label(l2);
160     tcg_gen_rem_tl(cpu_gpr[rd], t0, t1);
161     if (!is_double) {
162         tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]);
163     }
164     gen_set_label(l3);
165 
166     return true;
167 }
168 
169 static bool trans_MOD_G(DisasContext *s, arg_muldiv *a)
170 {
171     return gen_lext_MOD_G(s, a->rd, a->rs, a->rt, false);
172 }
173 
174 static bool trans_DMOD_G(DisasContext *s, arg_muldiv *a)
175 {
176     return gen_lext_MOD_G(s, a->rd, a->rs, a->rt, true);
177 }
178 
179 static bool gen_lext_MODU_G(DisasContext *s, int rd, int rs, int rt,
180                             bool is_double)
181 {
182     TCGv t0, t1;
183     TCGLabel *l1, *l2;
184 
185     if (rd == 0) {
186         /* Treat as NOP. */
187         return true;
188     }
189 
190     t0 = tcg_temp_new();
191     t1 = tcg_temp_new();
192     l1 = gen_new_label();
193     l2 = gen_new_label();
194 
195     gen_load_gpr(t0, rs);
196     gen_load_gpr(t1, rt);
197 
198     if (!is_double) {
199         tcg_gen_ext32u_tl(t0, t0);
200         tcg_gen_ext32u_tl(t1, t1);
201     }
202     tcg_gen_brcondi_tl(TCG_COND_NE, t1, 0, l1);
203     tcg_gen_movi_tl(cpu_gpr[rd], 0);
204     tcg_gen_br(l2);
205     gen_set_label(l1);
206     tcg_gen_remu_tl(cpu_gpr[rd], t0, t1);
207     if (!is_double) {
208         tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]);
209     }
210     gen_set_label(l2);
211 
212     return true;
213 }
214 
215 static bool trans_MODU_G(DisasContext *s, arg_muldiv *a)
216 {
217     return gen_lext_MODU_G(s, a->rd, a->rs, a->rt, false);
218 }
219 
220 static bool trans_DMODU_G(DisasContext *s, arg_muldiv *a)
221 {
222     return gen_lext_MODU_G(s, a->rd, a->rs, a->rt, true);
223 }
224 
225 static bool gen_lext_MULT_G(DisasContext *s, int rd, int rs, int rt,
226                             bool is_double)
227 {
228     TCGv t0, t1;
229 
230     if (rd == 0) {
231         /* Treat as NOP. */
232         return true;
233     }
234 
235     t0 = tcg_temp_new();
236     t1 = tcg_temp_new();
237 
238     gen_load_gpr(t0, rs);
239     gen_load_gpr(t1, rt);
240 
241     tcg_gen_mul_tl(cpu_gpr[rd], t0, t1);
242     if (!is_double) {
243         tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]);
244     }
245 
246     return true;
247 }
248 
249 static bool trans_MULTu_G(DisasContext *s, arg_muldiv *a)
250 {
251     return gen_lext_MULT_G(s, a->rd, a->rs, a->rt, false);
252 }
253 
254 static bool trans_DMULTu_G(DisasContext *s, arg_muldiv *a)
255 {
256     return gen_lext_MULT_G(s, a->rd, a->rs, a->rt, true);
257 }
258 
259 bool decode_ext_loongson(DisasContext *ctx, uint32_t insn)
260 {
261     if (!decode_64bit_enabled(ctx)) {
262         return false;
263     }
264     if ((ctx->insn_flags & INSN_LOONGSON2E) && decode_godson2(ctx, ctx->opcode)) {
265         return true;
266     }
267     if ((ctx->insn_flags & ASE_LEXT) && decode_loong_ext(ctx, ctx->opcode)) {
268         return true;
269     }
270     return false;
271 }
272