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, true));
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