1 /* This file is part of the dynarmic project.
2 * Copyright (c) 2018 MerryMage
3 * SPDX-License-Identifier: 0BSD
4 */
5
6 #include <optional>
7
8 #include "common/bit_util.h"
9 #include "frontend/A64/translate/impl/impl.h"
10
11 namespace Dynarmic::A64 {
12 namespace {
13 enum class ComparisonType {
14 EQ,
15 GE,
16 GT,
17 HI,
18 HS,
19 LE,
20 LT,
21 };
22
23 enum class ComparisonVariant {
24 Register,
25 Zero,
26 };
27
28 enum class Signedness {
29 Signed,
30 Unsigned,
31 };
32
RoundingShiftLeft(TranslatorVisitor & v,Imm<2> size,Vec Vm,Vec Vn,Vec Vd,Signedness sign)33 bool RoundingShiftLeft(TranslatorVisitor& v, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, Signedness sign) {
34 if (size != 0b11) {
35 return v.ReservedValue();
36 }
37
38 const IR::U128 operand1 = v.V(64, Vn);
39 const IR::U128 operand2 = v.V(64, Vm);
40 const IR::U128 result = [&] {
41 if (sign == Signedness::Signed) {
42 return v.ir.VectorRoundingShiftLeftSigned(64, operand1, operand2);
43 }
44
45 return v.ir.VectorRoundingShiftLeftUnsigned(64, operand1, operand2);
46 }();
47
48 v.V(64, Vd, result);
49 return true;
50 }
51
ScalarCompare(TranslatorVisitor & v,Imm<2> size,std::optional<Vec> Vm,Vec Vn,Vec Vd,ComparisonType type,ComparisonVariant variant)52 bool ScalarCompare(TranslatorVisitor& v, Imm<2> size, std::optional<Vec> Vm, Vec Vn, Vec Vd,
53 ComparisonType type, ComparisonVariant variant) {
54 if (size != 0b11) {
55 return v.ReservedValue();
56 }
57
58 const size_t esize = 64;
59 const size_t datasize = 64;
60
61 const IR::U128 operand1 = v.V(datasize, Vn);
62 const IR::U128 operand2 = variant == ComparisonVariant::Register ? v.V(datasize, *Vm) : v.ir.ZeroVector();
63
64 const IR::U128 result = [&] {
65 switch (type) {
66 case ComparisonType::EQ:
67 return v.ir.VectorEqual(esize, operand1, operand2);
68 case ComparisonType::GE:
69 return v.ir.VectorGreaterEqualSigned(esize, operand1, operand2);
70 case ComparisonType::GT:
71 return v.ir.VectorGreaterSigned(esize, operand1, operand2);
72 case ComparisonType::HI:
73 return v.ir.VectorGreaterUnsigned(esize, operand1, operand2);
74 case ComparisonType::HS:
75 return v.ir.VectorGreaterEqualUnsigned(esize, operand1, operand2);
76 case ComparisonType::LE:
77 return v.ir.VectorLessEqualSigned(esize, operand1, operand2);
78 case ComparisonType::LT:
79 default:
80 return v.ir.VectorLessSigned(esize, operand1, operand2);
81 }
82 }();
83
84 v.V_scalar(datasize, Vd, v.ir.VectorGetElement(esize, result, 0));
85 return true;
86 }
87
88 enum class FPComparisonType {
89 EQ,
90 GE,
91 AbsoluteGE,
92 GT,
93 AbsoluteGT
94 };
95
ScalarFPCompareRegister(TranslatorVisitor & v,bool sz,Vec Vm,Vec Vn,Vec Vd,FPComparisonType type)96 bool ScalarFPCompareRegister(TranslatorVisitor& v, bool sz, Vec Vm, Vec Vn, Vec Vd, FPComparisonType type) {
97 const size_t esize = sz ? 64 : 32;
98 const size_t datasize = esize;
99
100 const IR::U128 operand1 = v.V(datasize, Vn);
101 const IR::U128 operand2 = v.V(datasize, Vm);
102 const IR::U128 result = [&] {
103 switch (type) {
104 case FPComparisonType::EQ:
105 return v.ir.FPVectorEqual(esize, operand1, operand2);
106 case FPComparisonType::GE:
107 return v.ir.FPVectorGreaterEqual(esize, operand1, operand2);
108 case FPComparisonType::AbsoluteGE:
109 return v.ir.FPVectorGreaterEqual(esize,
110 v.ir.FPVectorAbs(esize, operand1),
111 v.ir.FPVectorAbs(esize, operand2));
112 case FPComparisonType::GT:
113 return v.ir.FPVectorGreater(esize, operand1, operand2);
114 case FPComparisonType::AbsoluteGT:
115 return v.ir.FPVectorGreater(esize,
116 v.ir.FPVectorAbs(esize, operand1),
117 v.ir.FPVectorAbs(esize, operand2));
118 }
119
120 UNREACHABLE();
121 }();
122
123 v.V_scalar(datasize, Vd, v.ir.VectorGetElement(esize, result, 0));
124 return true;
125 }
126 } // Anonymous namespace
127
SQADD_1(Imm<2> size,Vec Vm,Vec Vn,Vec Vd)128 bool TranslatorVisitor::SQADD_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
129 const size_t esize = 8 << size.ZeroExtend<size_t>();
130
131 const IR::UAny operand1 = V_scalar(esize, Vn);
132 const IR::UAny operand2 = V_scalar(esize, Vm);
133 const auto result = ir.SignedSaturatedAdd(operand1, operand2);
134 ir.OrQC(result.overflow);
135 V_scalar(esize, Vd, result.result);
136 return true;
137 }
138
SQDMULH_vec_1(Imm<2> size,Vec Vm,Vec Vn,Vec Vd)139 bool TranslatorVisitor::SQDMULH_vec_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
140 if (size == 0b00 || size == 0b11) {
141 return ReservedValue();
142 }
143
144 const size_t esize = 8 << size.ZeroExtend();
145
146 const IR::UAny operand1 = V_scalar(esize, Vn);
147 const IR::UAny operand2 = V_scalar(esize, Vm);
148 const auto result = ir.SignedSaturatedDoublingMultiplyReturnHigh(operand1, operand2);
149
150 ir.OrQC(result.overflow);
151
152 V_scalar(esize, Vd, result.result);
153 return true;
154 }
155
SQRDMULH_vec_1(Imm<2> size,Vec Vm,Vec Vn,Vec Vd)156 bool TranslatorVisitor::SQRDMULH_vec_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
157 if (size == 0b00 || size == 0b11) {
158 return ReservedValue();
159 }
160
161 const size_t esize = 8 << size.ZeroExtend();
162
163 const IR::U128 operand1 = ir.ZeroExtendToQuad(ir.VectorGetElement(esize, V(128, Vn), 0));
164 const IR::U128 operand2 = ir.ZeroExtendToQuad(ir.VectorGetElement(esize, V(128, Vm), 0));
165 const IR::UpperAndLower multiply = ir.VectorSignedSaturatedDoublingMultiply(esize, operand1, operand2);
166 const IR::U128 result = ir.VectorAdd(esize, multiply.upper, ir.VectorLogicalShiftRight(esize, multiply.lower, static_cast<u8>(esize - 1)));
167
168 V_scalar(esize, Vd, ir.VectorGetElement(esize, result, 0));
169 return true;
170 }
171
SQSUB_1(Imm<2> size,Vec Vm,Vec Vn,Vec Vd)172 bool TranslatorVisitor::SQSUB_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
173 const size_t esize = 8 << size.ZeroExtend<size_t>();
174
175 const IR::UAny operand1 = V_scalar(esize, Vn);
176 const IR::UAny operand2 = V_scalar(esize, Vm);
177 const auto result = ir.SignedSaturatedSub(operand1, operand2);
178 ir.OrQC(result.overflow);
179 V_scalar(esize, Vd, result.result);
180 return true;
181 }
182
UQADD_1(Imm<2> size,Vec Vm,Vec Vn,Vec Vd)183 bool TranslatorVisitor::UQADD_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
184 const size_t esize = 8 << size.ZeroExtend();
185
186 const IR::UAny operand1 = V_scalar(esize, Vn);
187 const IR::UAny operand2 = V_scalar(esize, Vm);
188 const auto result = ir.UnsignedSaturatedAdd(operand1, operand2);
189 ir.OrQC(result.overflow);
190 V_scalar(esize, Vd, result.result);
191 return true;
192 }
193
UQSUB_1(Imm<2> size,Vec Vm,Vec Vn,Vec Vd)194 bool TranslatorVisitor::UQSUB_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
195 const size_t esize = 8 << size.ZeroExtend();
196
197 const IR::UAny operand1 = V_scalar(esize, Vn);
198 const IR::UAny operand2 = V_scalar(esize, Vm);
199 const auto result = ir.UnsignedSaturatedSub(operand1, operand2);
200 ir.OrQC(result.overflow);
201 V_scalar(esize, Vd, result.result);
202 return true;
203 }
204
ADD_1(Imm<2> size,Vec Vm,Vec Vn,Vec Vd)205 bool TranslatorVisitor::ADD_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
206 if (size != 0b11) {
207 return ReservedValue();
208 }
209 const size_t esize = 8 << size.ZeroExtend<size_t>();
210 const size_t datasize = esize;
211
212 const IR::U64 operand1 = V_scalar(datasize, Vn);
213 const IR::U64 operand2 = V_scalar(datasize, Vm);
214 const IR::U64 result = ir.Add(operand1, operand2);
215 V_scalar(datasize, Vd, result);
216 return true;
217 }
218
CMEQ_reg_1(Imm<2> size,Vec Vm,Vec Vn,Vec Vd)219 bool TranslatorVisitor::CMEQ_reg_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
220 return ScalarCompare(*this, size, Vm, Vn, Vd, ComparisonType::EQ, ComparisonVariant::Register);
221 }
222
CMEQ_zero_1(Imm<2> size,Vec Vn,Vec Vd)223 bool TranslatorVisitor::CMEQ_zero_1(Imm<2> size, Vec Vn, Vec Vd) {
224 return ScalarCompare(*this, size, {}, Vn, Vd, ComparisonType::EQ, ComparisonVariant::Zero);
225 }
226
CMGE_reg_1(Imm<2> size,Vec Vm,Vec Vn,Vec Vd)227 bool TranslatorVisitor::CMGE_reg_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
228 return ScalarCompare(*this, size, Vm, Vn, Vd, ComparisonType::GE, ComparisonVariant::Register);
229 }
230
CMGE_zero_1(Imm<2> size,Vec Vn,Vec Vd)231 bool TranslatorVisitor::CMGE_zero_1(Imm<2> size, Vec Vn, Vec Vd) {
232 return ScalarCompare(*this, size, {}, Vn, Vd, ComparisonType::GE, ComparisonVariant::Zero);
233 }
234
CMGT_reg_1(Imm<2> size,Vec Vm,Vec Vn,Vec Vd)235 bool TranslatorVisitor::CMGT_reg_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
236 return ScalarCompare(*this, size, Vm, Vn, Vd, ComparisonType::GT, ComparisonVariant::Register);
237 }
238
CMGT_zero_1(Imm<2> size,Vec Vn,Vec Vd)239 bool TranslatorVisitor::CMGT_zero_1(Imm<2> size, Vec Vn, Vec Vd) {
240 return ScalarCompare(*this, size, {}, Vn, Vd, ComparisonType::GT, ComparisonVariant::Zero);
241 }
242
CMLE_1(Imm<2> size,Vec Vn,Vec Vd)243 bool TranslatorVisitor::CMLE_1(Imm<2> size, Vec Vn, Vec Vd) {
244 return ScalarCompare(*this, size, {}, Vn, Vd, ComparisonType::LE, ComparisonVariant::Zero);
245 }
246
CMLT_1(Imm<2> size,Vec Vn,Vec Vd)247 bool TranslatorVisitor::CMLT_1(Imm<2> size, Vec Vn, Vec Vd) {
248 return ScalarCompare(*this, size, {}, Vn, Vd, ComparisonType::LT, ComparisonVariant::Zero);
249 }
250
CMHI_1(Imm<2> size,Vec Vm,Vec Vn,Vec Vd)251 bool TranslatorVisitor::CMHI_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
252 return ScalarCompare(*this, size, Vm, Vn, Vd, ComparisonType::HI, ComparisonVariant::Register);
253 }
254
CMHS_1(Imm<2> size,Vec Vm,Vec Vn,Vec Vd)255 bool TranslatorVisitor::CMHS_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
256 return ScalarCompare(*this, size, Vm, Vn, Vd, ComparisonType::HS, ComparisonVariant::Register);
257 }
258
CMTST_1(Imm<2> size,Vec Vm,Vec Vn,Vec Vd)259 bool TranslatorVisitor::CMTST_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
260 if (size != 0b11) {
261 return ReservedValue();
262 }
263
264 const IR::U128 operand1 = V(64, Vn);
265 const IR::U128 operand2 = V(64, Vm);
266 const IR::U128 anded = ir.VectorAnd(operand1, operand2);
267 const IR::U128 result = ir.VectorNot(ir.VectorEqual(64, anded, ir.ZeroVector()));
268
269 V(64, Vd, result);
270 return true;
271 }
272
FABD_2(bool sz,Vec Vm,Vec Vn,Vec Vd)273 bool TranslatorVisitor::FABD_2(bool sz, Vec Vm, Vec Vn, Vec Vd) {
274 const size_t esize = sz ? 64 : 32;
275
276 const IR::U32U64 operand1 = V_scalar(esize, Vn);
277 const IR::U32U64 operand2 = V_scalar(esize, Vm);
278 const IR::U32U64 result = ir.FPAbs(ir.FPSub(operand1, operand2));
279
280 V_scalar(esize, Vd, result);
281 return true;
282 }
283
FMULX_vec_2(bool sz,Vec Vm,Vec Vn,Vec Vd)284 bool TranslatorVisitor::FMULX_vec_2(bool sz, Vec Vm, Vec Vn, Vec Vd) {
285 const size_t esize = sz ? 64 : 32;
286
287 const IR::U32U64 operand1 = V_scalar(esize, Vn);
288 const IR::U32U64 operand2 = V_scalar(esize, Vm);
289 const IR::U32U64 result = ir.FPMulX(operand1, operand2);
290
291 V_scalar(esize, Vd, result);
292 return true;
293 }
294
FRECPS_1(Vec Vm,Vec Vn,Vec Vd)295 bool TranslatorVisitor::FRECPS_1(Vec Vm, Vec Vn, Vec Vd) {
296 const size_t esize = 16;
297
298 const IR::U16 operand1 = V_scalar(esize, Vn);
299 const IR::U16 operand2 = V_scalar(esize, Vm);
300 const IR::U16 result = ir.FPRecipStepFused(operand1, operand2);
301
302 V_scalar(esize, Vd, result);
303 return true;
304 }
305
FRECPS_2(bool sz,Vec Vm,Vec Vn,Vec Vd)306 bool TranslatorVisitor::FRECPS_2(bool sz, Vec Vm, Vec Vn, Vec Vd) {
307 const size_t esize = sz ? 64 : 32;
308
309 const IR::U32U64 operand1 = V_scalar(esize, Vn);
310 const IR::U32U64 operand2 = V_scalar(esize, Vm);
311 const IR::U32U64 result = ir.FPRecipStepFused(operand1, operand2);
312
313 V_scalar(esize, Vd, result);
314 return true;
315 }
316
FRSQRTS_1(Vec Vm,Vec Vn,Vec Vd)317 bool TranslatorVisitor::FRSQRTS_1(Vec Vm, Vec Vn, Vec Vd) {
318 const size_t esize = 16;
319
320 const IR::U16 operand1 = V_scalar(esize, Vn);
321 const IR::U16 operand2 = V_scalar(esize, Vm);
322 const IR::U16 result = ir.FPRSqrtStepFused(operand1, operand2);
323
324 V_scalar(esize, Vd, result);
325 return true;
326 }
327
FRSQRTS_2(bool sz,Vec Vm,Vec Vn,Vec Vd)328 bool TranslatorVisitor::FRSQRTS_2(bool sz, Vec Vm, Vec Vn, Vec Vd) {
329 const size_t esize = sz ? 64 : 32;
330
331 const IR::U32U64 operand1 = V_scalar(esize, Vn);
332 const IR::U32U64 operand2 = V_scalar(esize, Vm);
333 const IR::U32U64 result = ir.FPRSqrtStepFused(operand1, operand2);
334
335 V_scalar(esize, Vd, result);
336 return true;
337 }
338
FACGE_2(bool sz,Vec Vm,Vec Vn,Vec Vd)339 bool TranslatorVisitor::FACGE_2(bool sz, Vec Vm, Vec Vn, Vec Vd) {
340 return ScalarFPCompareRegister(*this, sz, Vm, Vn, Vd, FPComparisonType::AbsoluteGE);
341 }
342
FACGT_2(bool sz,Vec Vm,Vec Vn,Vec Vd)343 bool TranslatorVisitor::FACGT_2(bool sz, Vec Vm, Vec Vn, Vec Vd) {
344 return ScalarFPCompareRegister(*this, sz, Vm, Vn, Vd, FPComparisonType::AbsoluteGT);
345 }
346
FCMEQ_reg_1(Vec Vm,Vec Vn,Vec Vd)347 bool TranslatorVisitor::FCMEQ_reg_1(Vec Vm, Vec Vn, Vec Vd) {
348 const IR::U128 lhs = V(128, Vn);
349 const IR::U128 rhs = V(128, Vm);
350 const IR::U128 result = ir.FPVectorEqual(16, lhs, rhs);
351
352 V_scalar(16, Vd, ir.VectorGetElement(16, result, 0));
353 return true;
354 }
355
FCMEQ_reg_2(bool sz,Vec Vm,Vec Vn,Vec Vd)356 bool TranslatorVisitor::FCMEQ_reg_2(bool sz, Vec Vm, Vec Vn, Vec Vd) {
357 return ScalarFPCompareRegister(*this, sz, Vm, Vn, Vd, FPComparisonType::EQ);
358 }
359
FCMGE_reg_2(bool sz,Vec Vm,Vec Vn,Vec Vd)360 bool TranslatorVisitor::FCMGE_reg_2(bool sz, Vec Vm, Vec Vn, Vec Vd) {
361 return ScalarFPCompareRegister(*this, sz, Vm, Vn, Vd, FPComparisonType::GE);
362 }
363
FCMGT_reg_2(bool sz,Vec Vm,Vec Vn,Vec Vd)364 bool TranslatorVisitor::FCMGT_reg_2(bool sz, Vec Vm, Vec Vn, Vec Vd) {
365 return ScalarFPCompareRegister(*this, sz, Vm, Vn, Vd, FPComparisonType::GT);
366 }
367
SQSHL_reg_1(Imm<2> size,Vec Vm,Vec Vn,Vec Vd)368 bool TranslatorVisitor::SQSHL_reg_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
369 const size_t esize = 8U << size.ZeroExtend();
370
371 const IR::U128 operand1 = ir.ZeroExtendToQuad(ir.VectorGetElement(esize, V(128, Vn), 0));
372 const IR::U128 operand2 = ir.ZeroExtendToQuad(ir.VectorGetElement(esize, V(128, Vm), 0));
373 const IR::U128 result = ir.VectorSignedSaturatedShiftLeft(esize, operand1, operand2);
374
375 ir.SetQ(Vd, result);
376 return true;
377 }
378
SRSHL_1(Imm<2> size,Vec Vm,Vec Vn,Vec Vd)379 bool TranslatorVisitor::SRSHL_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
380 return RoundingShiftLeft(*this, size, Vm, Vn, Vd, Signedness::Signed);
381 }
382
SSHL_1(Imm<2> size,Vec Vm,Vec Vn,Vec Vd)383 bool TranslatorVisitor::SSHL_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
384 if (size != 0b11) {
385 return ReservedValue();
386 }
387
388 const IR::U128 operand1 = V(64, Vn);
389 const IR::U128 operand2 = V(64, Vm);
390 const IR::U128 result = ir.VectorArithmeticVShift(64, operand1, operand2);
391
392 V(64, Vd, result);
393 return true;
394 }
395
SUB_1(Imm<2> size,Vec Vm,Vec Vn,Vec Vd)396 bool TranslatorVisitor::SUB_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
397 if (size != 0b11) {
398 return ReservedValue();
399 }
400 const size_t esize = 8 << size.ZeroExtend<size_t>();
401 const size_t datasize = esize;
402
403 const IR::U64 operand1 = V_scalar(datasize, Vn);
404 const IR::U64 operand2 = V_scalar(datasize, Vm);
405 const IR::U64 result = ir.Sub(operand1, operand2);
406 V_scalar(datasize, Vd, result);
407 return true;
408 }
409
UQSHL_reg_1(Imm<2> size,Vec Vm,Vec Vn,Vec Vd)410 bool TranslatorVisitor::UQSHL_reg_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
411 const size_t esize = 8U << size.ZeroExtend();
412
413 const IR::U128 operand1 = ir.ZeroExtendToQuad(ir.VectorGetElement(esize, V(128, Vn), 0));
414 const IR::U128 operand2 = ir.ZeroExtendToQuad(ir.VectorGetElement(esize, V(128, Vm), 0));
415 const IR::U128 result = ir.VectorUnsignedSaturatedShiftLeft(esize, operand1, operand2);
416
417 ir.SetQ(Vd, result);
418 return true;
419 }
420
URSHL_1(Imm<2> size,Vec Vm,Vec Vn,Vec Vd)421 bool TranslatorVisitor::URSHL_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
422 return RoundingShiftLeft(*this, size, Vm, Vn, Vd, Signedness::Unsigned);
423 }
424
USHL_1(Imm<2> size,Vec Vm,Vec Vn,Vec Vd)425 bool TranslatorVisitor::USHL_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
426 if (size != 0b11) {
427 return ReservedValue();
428 }
429
430 const IR::U128 operand1 = V(64, Vn);
431 const IR::U128 operand2 = V(64, Vm);
432 const IR::U128 result = ir.VectorLogicalVShift(64, operand1, operand2);
433
434 V(64, Vd, result);
435 return true;
436 }
437
438 } // namespace Dynarmic::A64
439