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
gen_lext_DIV_G(DisasContext * s,int rd,int rs,int rt,bool is_double)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
trans_DIV_G(DisasContext * s,arg_muldiv * a)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
trans_DDIV_G(DisasContext * s,arg_muldiv * a)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
gen_lext_DIVU_G(DisasContext * s,int rd,int rs,int rt,bool is_double)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
trans_DIVU_G(DisasContext * s,arg_muldiv * a)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
trans_DDIVU_G(DisasContext * s,arg_muldiv * a)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
gen_lext_MOD_G(DisasContext * s,int rd,int rs,int rt,bool is_double)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
trans_MOD_G(DisasContext * s,arg_muldiv * a)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
trans_DMOD_G(DisasContext * s,arg_muldiv * a)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
gen_lext_MODU_G(DisasContext * s,int rd,int rs,int rt,bool is_double)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
trans_MODU_G(DisasContext * s,arg_muldiv * a)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
trans_DMODU_G(DisasContext * s,arg_muldiv * a)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
gen_lext_MULT_G(DisasContext * s,int rd,int rs,int rt,bool is_double)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
trans_MULTu_G(DisasContext * s,arg_muldiv * a)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
trans_DMULTu_G(DisasContext * s,arg_muldiv * a)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
decode_ext_loongson(DisasContext * ctx,uint32_t insn)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