1 /* This file is part of the dynarmic project.
2  * Copyright (c) 2018 MerryMage
3  * SPDX-License-Identifier: 0BSD
4  */
5 
6 #include "common/bit_util.h"
7 #include "common/fp/rounding_mode.h"
8 #include "frontend/A64/translate/impl/impl.h"
9 
10 namespace Dynarmic::A64 {
11 namespace {
12 enum class Rounding {
13     None,
14     Round
15 };
16 
17 enum class Accumulating {
18     None,
19     Accumulate
20 };
21 
22 enum class Signedness {
23     Signed,
24     Unsigned
25 };
26 
27 enum class Narrowing {
28     Truncation,
29     SaturateToUnsigned,
30     SaturateToSigned,
31 };
32 
33 enum class SaturatingShiftLeftType {
34     Signed,
35     Unsigned,
36     SignedWithUnsignedSaturation,
37 };
38 
39 enum class FloatConversionDirection {
40     FixedToFloat,
41     FloatToFixed,
42 };
43 
PerformRoundingCorrection(TranslatorVisitor & v,size_t esize,u64 round_value,IR::U128 original,IR::U128 shifted)44 IR::U128 PerformRoundingCorrection(TranslatorVisitor& v, size_t esize, u64 round_value, IR::U128 original, IR::U128 shifted) {
45     const IR::U128 round_const = v.ir.VectorBroadcast(esize, v.I(esize, round_value));
46     const IR::U128 round_correction = v.ir.VectorEqual(esize, v.ir.VectorAnd(original, round_const), round_const);
47     return v.ir.VectorSub(esize, shifted, round_correction);
48 }
49 
ShiftRight(TranslatorVisitor & v,bool Q,Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd,Rounding rounding,Accumulating accumulating,Signedness signedness)50 bool ShiftRight(TranslatorVisitor& v, bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd,
51                 Rounding rounding, Accumulating accumulating, Signedness signedness) {
52     if (immh == 0b0000) {
53         return v.DecodeError();
54     }
55 
56     if (immh.Bit<3>() && !Q) {
57         return v.ReservedValue();
58     }
59 
60     const size_t esize = 8 << Common::HighestSetBit(immh.ZeroExtend());
61     const size_t datasize = Q ? 128 : 64;
62 
63     const u8 shift_amount = static_cast<u8>(2 * esize) - concatenate(immh, immb).ZeroExtend<u8>();
64 
65     const IR::U128 operand = v.V(datasize, Vn);
66 
67     IR::U128 result = [&] {
68         if (signedness == Signedness::Signed) {
69             return v.ir.VectorArithmeticShiftRight(esize, operand, shift_amount);
70         }
71         return v.ir.VectorLogicalShiftRight(esize, operand, shift_amount);
72     }();
73 
74     if (rounding == Rounding::Round) {
75         const u64 round_value = 1ULL << (shift_amount - 1);
76         result = PerformRoundingCorrection(v, esize, round_value, operand, result);
77     }
78 
79     if (accumulating == Accumulating::Accumulate) {
80         const IR::U128 accumulator = v.V(datasize, Vd);
81         result = v.ir.VectorAdd(esize, result, accumulator);
82     }
83 
84     v.V(datasize, Vd, result);
85     return true;
86 }
87 
ShiftRightNarrowing(TranslatorVisitor & v,bool Q,Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd,Rounding rounding,Narrowing narrowing,Signedness signedness)88 bool ShiftRightNarrowing(TranslatorVisitor& v, bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd,
89                          Rounding rounding, Narrowing narrowing, Signedness signedness) {
90     if (immh == 0b0000) {
91         return v.DecodeError();
92     }
93 
94     if (immh.Bit<3>()) {
95         return v.ReservedValue();
96     }
97 
98     const size_t esize = 8 << Common::HighestSetBit(immh.ZeroExtend());
99     const size_t source_esize = 2 * esize;
100     const size_t part = Q ? 1 : 0;
101 
102     const u8 shift_amount = static_cast<u8>(source_esize - concatenate(immh, immb).ZeroExtend());
103 
104     const IR::U128 operand = v.V(128, Vn);
105 
106     IR::U128 wide_result = [&] {
107         if (signedness == Signedness::Signed) {
108             return v.ir.VectorArithmeticShiftRight(source_esize, operand, shift_amount);
109         }
110         return v.ir.VectorLogicalShiftRight(source_esize, operand, shift_amount);
111     }();
112 
113     if (rounding == Rounding::Round) {
114         const u64 round_value = 1ULL << (shift_amount - 1);
115         wide_result = PerformRoundingCorrection(v, source_esize, round_value, operand, wide_result);
116     }
117 
118     const IR::U128 result = [&] {
119         switch (narrowing) {
120         case Narrowing::Truncation:
121             return v.ir.VectorNarrow(source_esize, wide_result);
122         case Narrowing::SaturateToUnsigned:
123             if (signedness == Signedness::Signed) {
124                 return v.ir.VectorSignedSaturatedNarrowToUnsigned(source_esize, wide_result);
125             }
126             return v.ir.VectorUnsignedSaturatedNarrow(source_esize, wide_result);
127         case Narrowing::SaturateToSigned:
128             ASSERT(signedness == Signedness::Signed);
129             return v.ir.VectorSignedSaturatedNarrowToSigned(source_esize, wide_result);
130         }
131         UNREACHABLE();
132     }();
133 
134     v.Vpart(64, Vd, part, result);
135     return true;
136 }
137 
ShiftLeftLong(TranslatorVisitor & v,bool Q,Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd,Signedness signedness)138 bool ShiftLeftLong(TranslatorVisitor& v, bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd,
139                    Signedness signedness) {
140     if (immh == 0b0000) {
141         return v.DecodeError();
142     }
143 
144     if (immh.Bit<3>()) {
145         return v.ReservedValue();
146     }
147 
148     const size_t esize = 8 << Common::HighestSetBit(immh.ZeroExtend());
149     const size_t datasize = 64;
150     const size_t part = Q ? 1 : 0;
151 
152     const u8 shift_amount = concatenate(immh, immb).ZeroExtend<u8>() - static_cast<u8>(esize);
153 
154     const IR::U128 operand = v.Vpart(datasize, Vn, part);
155     const IR::U128 expanded_operand = [&] {
156         if (signedness == Signedness::Signed) {
157             return v.ir.VectorSignExtend(esize, operand);
158         }
159         return v.ir.VectorZeroExtend(esize, operand);
160     }();
161     const IR::U128 result = v.ir.VectorLogicalShiftLeft(2 * esize, expanded_operand, shift_amount);
162 
163     v.V(2 * datasize, Vd, result);
164     return true;
165 }
166 
SaturatingShiftLeft(TranslatorVisitor & v,bool Q,Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd,SaturatingShiftLeftType type)167 bool SaturatingShiftLeft(TranslatorVisitor& v, bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd, SaturatingShiftLeftType type) {
168     if (!Q && immh.Bit<3>()) {
169         return v.ReservedValue();
170     }
171 
172     const size_t esize = 8 << Common::HighestSetBit(immh.ZeroExtend());
173     const size_t datasize = Q ? 128 : 64;
174     const size_t shift = concatenate(immh, immb).ZeroExtend() - esize;
175 
176     const IR::U128 operand = v.V(datasize, Vn);
177     const IR::U128 shift_vec = v.ir.VectorBroadcast(esize, v.I(esize, shift));
178     const IR::U128 result = [&] {
179         if (type == SaturatingShiftLeftType::Signed) {
180             return v.ir.VectorSignedSaturatedShiftLeft(esize, operand, shift_vec);
181         }
182 
183         if (type == SaturatingShiftLeftType::Unsigned) {
184             return v.ir.VectorUnsignedSaturatedShiftLeft(esize, operand, shift_vec);
185         }
186 
187         return v.ir.VectorSignedSaturatedShiftLeftUnsigned(esize, operand, shift_vec);
188     }();
189 
190     v.V(datasize, Vd, result);
191     return true;
192 }
193 
ConvertFloat(TranslatorVisitor & v,bool Q,Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd,Signedness signedness,FloatConversionDirection direction,FP::RoundingMode rounding_mode)194 bool ConvertFloat(TranslatorVisitor& v, bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd, Signedness signedness, FloatConversionDirection direction, FP::RoundingMode rounding_mode) {
195     if (immh == 0b0000) {
196         return v.DecodeError();
197     }
198 
199     if (immh == 0b0001 || immh == 0b0010 || immh == 0b0011) {
200         return v.ReservedValue();
201     }
202 
203     if (immh.Bit<3>() && !Q) {
204         return v.ReservedValue();
205     }
206 
207     const size_t esize = 8 << Common::HighestSetBit(immh.ZeroExtend());
208     const size_t datasize = Q ? 128 : 64;
209 
210     const u8 fbits = static_cast<u8>(esize * 2) - concatenate(immh, immb).ZeroExtend<u8>();
211 
212     const IR::U128 operand = v.V(datasize, Vn);
213     const IR::U128 result = [&] {
214         switch (direction) {
215         case FloatConversionDirection::FixedToFloat:
216             return signedness == Signedness::Signed
217                  ? v.ir.FPVectorFromSignedFixed(esize, operand, fbits, rounding_mode)
218                  : v.ir.FPVectorFromUnsignedFixed(esize, operand, fbits, rounding_mode);
219         case FloatConversionDirection::FloatToFixed:
220             return signedness == Signedness::Signed
221                  ? v.ir.FPVectorToSignedFixed(esize, operand, fbits, rounding_mode)
222                  : v.ir.FPVectorToUnsignedFixed(esize, operand, fbits, rounding_mode);
223         }
224         UNREACHABLE();
225     }();
226 
227     v.V(datasize, Vd, result);
228     return true;
229 }
230 
231 } // Anonymous namespace
232 
SSHR_2(bool Q,Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd)233 bool TranslatorVisitor::SSHR_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
234     return ShiftRight(*this, Q, immh, immb, Vn, Vd, Rounding::None, Accumulating::None, Signedness::Signed);
235 }
236 
SRSHR_2(bool Q,Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd)237 bool TranslatorVisitor::SRSHR_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
238     return ShiftRight(*this, Q, immh, immb, Vn, Vd, Rounding::Round, Accumulating::None, Signedness::Signed);
239 }
240 
SRSRA_2(bool Q,Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd)241 bool TranslatorVisitor::SRSRA_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
242     return ShiftRight(*this, Q, immh, immb, Vn, Vd, Rounding::Round, Accumulating::Accumulate, Signedness::Signed);
243 }
244 
SSRA_2(bool Q,Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd)245 bool TranslatorVisitor::SSRA_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
246     return ShiftRight(*this, Q, immh, immb, Vn, Vd, Rounding::None, Accumulating::Accumulate, Signedness::Signed);
247 }
248 
SHL_2(bool Q,Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd)249 bool TranslatorVisitor::SHL_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
250     if (immh == 0b0000) {
251         return DecodeError();
252     }
253     if (immh.Bit<3>() && !Q) {
254         return ReservedValue();
255     }
256     const size_t esize = 8 << Common::HighestSetBit(immh.ZeroExtend());
257     const size_t datasize = Q ? 128 : 64;
258 
259     const u8 shift_amount = concatenate(immh, immb).ZeroExtend<u8>() - static_cast<u8>(esize);
260 
261     const IR::U128 operand = V(datasize, Vn);
262     const IR::U128 result = ir.VectorLogicalShiftLeft(esize, operand, shift_amount);
263 
264     V(datasize, Vd, result);
265     return true;
266 }
267 
SHRN(bool Q,Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd)268 bool TranslatorVisitor::SHRN(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
269     return ShiftRightNarrowing(*this, Q, immh, immb, Vn, Vd, Rounding::None, Narrowing::Truncation, Signedness::Unsigned);
270 }
271 
RSHRN(bool Q,Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd)272 bool TranslatorVisitor::RSHRN(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
273     return ShiftRightNarrowing(*this, Q, immh, immb, Vn, Vd, Rounding::Round, Narrowing::Truncation, Signedness::Unsigned);
274 }
275 
SQSHL_imm_2(bool Q,Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd)276 bool TranslatorVisitor::SQSHL_imm_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
277     return SaturatingShiftLeft(*this, Q, immh, immb, Vn, Vd, SaturatingShiftLeftType::Signed);
278 }
279 
SQSHLU_2(bool Q,Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd)280 bool TranslatorVisitor::SQSHLU_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
281     return SaturatingShiftLeft(*this, Q, immh, immb, Vn, Vd, SaturatingShiftLeftType::SignedWithUnsignedSaturation);
282 }
283 
SQSHRN_2(bool Q,Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd)284 bool TranslatorVisitor::SQSHRN_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
285     return ShiftRightNarrowing(*this, Q, immh, immb, Vn, Vd, Rounding::None, Narrowing::SaturateToSigned, Signedness::Signed);
286 }
287 
SQRSHRN_2(bool Q,Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd)288 bool TranslatorVisitor::SQRSHRN_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
289     return ShiftRightNarrowing(*this, Q, immh, immb, Vn, Vd, Rounding::Round, Narrowing::SaturateToSigned, Signedness::Signed);
290 }
291 
SQSHRUN_2(bool Q,Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd)292 bool TranslatorVisitor::SQSHRUN_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
293     return ShiftRightNarrowing(*this, Q, immh, immb, Vn, Vd, Rounding::None, Narrowing::SaturateToUnsigned, Signedness::Signed);
294 }
295 
SQRSHRUN_2(bool Q,Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd)296 bool TranslatorVisitor::SQRSHRUN_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
297     return ShiftRightNarrowing(*this, Q, immh, immb, Vn, Vd, Rounding::Round, Narrowing::SaturateToUnsigned, Signedness::Signed);
298 }
299 
UQSHL_imm_2(bool Q,Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd)300 bool TranslatorVisitor::UQSHL_imm_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
301     return SaturatingShiftLeft(*this, Q, immh, immb, Vn, Vd, SaturatingShiftLeftType::Unsigned);
302 }
303 
UQSHRN_2(bool Q,Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd)304 bool TranslatorVisitor::UQSHRN_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
305     return ShiftRightNarrowing(*this, Q, immh, immb, Vn, Vd, Rounding::None, Narrowing::SaturateToUnsigned, Signedness::Unsigned);
306 }
307 
UQRSHRN_2(bool Q,Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd)308 bool TranslatorVisitor::UQRSHRN_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
309     return ShiftRightNarrowing(*this, Q, immh, immb, Vn, Vd, Rounding::Round, Narrowing::SaturateToUnsigned, Signedness::Unsigned);
310 }
311 
SSHLL(bool Q,Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd)312 bool TranslatorVisitor::SSHLL(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
313     return ShiftLeftLong(*this, Q, immh, immb, Vn, Vd, Signedness::Signed);
314 }
315 
URSHR_2(bool Q,Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd)316 bool TranslatorVisitor::URSHR_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
317     return ShiftRight(*this, Q, immh, immb, Vn, Vd, Rounding::Round, Accumulating::None, Signedness::Unsigned);
318 }
319 
URSRA_2(bool Q,Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd)320 bool TranslatorVisitor::URSRA_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
321     return ShiftRight(*this, Q, immh, immb, Vn, Vd, Rounding::Round, Accumulating::Accumulate, Signedness::Unsigned);
322 }
323 
USHR_2(bool Q,Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd)324 bool TranslatorVisitor::USHR_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
325     return ShiftRight(*this, Q, immh, immb, Vn, Vd, Rounding::None, Accumulating::None, Signedness::Unsigned);
326 }
327 
USRA_2(bool Q,Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd)328 bool TranslatorVisitor::USRA_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
329     return ShiftRight(*this, Q, immh, immb, Vn, Vd, Rounding::None, Accumulating::Accumulate, Signedness::Unsigned);
330 }
331 
USHLL(bool Q,Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd)332 bool TranslatorVisitor::USHLL(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
333     return ShiftLeftLong(*this, Q, immh, immb, Vn, Vd, Signedness::Unsigned);
334 }
335 
SRI_2(bool Q,Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd)336 bool TranslatorVisitor::SRI_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
337     if (immh == 0b0000) {
338         return DecodeError();
339     }
340 
341     if (!Q && immh.Bit<3>()) {
342         return ReservedValue();
343     }
344 
345     const size_t esize = 8 << Common::HighestSetBit(immh.ZeroExtend());
346     const size_t datasize = Q ? 128 : 64;
347 
348     const u8 shift_amount = static_cast<u8>((esize * 2) - concatenate(immh, immb).ZeroExtend<u8>());
349     const u64 mask = shift_amount == esize ? 0 : Common::Ones<u64>(esize) >> shift_amount;
350 
351     const IR::U128 operand1 = V(datasize, Vn);
352     const IR::U128 operand2 = V(datasize, Vd);
353 
354     const IR::U128 shifted = ir.VectorLogicalShiftRight(esize, operand1, shift_amount);
355     const IR::U128 mask_vec = ir.VectorBroadcast(esize, I(esize, mask));
356     const IR::U128 result = ir.VectorOr(ir.VectorAnd(operand2, ir.VectorNot(mask_vec)), shifted);
357 
358     V(datasize, Vd, result);
359     return true;
360 }
361 
SLI_2(bool Q,Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd)362 bool TranslatorVisitor::SLI_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
363     if (immh == 0b0000) {
364         return DecodeError();
365     }
366 
367     if (!Q && immh.Bit<3>()) {
368         return ReservedValue();
369     }
370 
371     const size_t esize = 8 << Common::HighestSetBit(immh.ZeroExtend());
372     const size_t datasize = Q ? 128 : 64;
373 
374     const u8 shift_amount = concatenate(immh, immb).ZeroExtend<u8>() - static_cast<u8>(esize);
375     const u64 mask = Common::Ones<u64>(esize) << shift_amount;
376 
377     const IR::U128 operand1 = V(datasize, Vn);
378     const IR::U128 operand2 = V(datasize, Vd);
379 
380     const IR::U128 shifted = ir.VectorLogicalShiftLeft(esize, operand1, shift_amount);
381     const IR::U128 mask_vec = ir.VectorBroadcast(esize, I(esize, mask));
382     const IR::U128 result = ir.VectorOr(ir.VectorAnd(operand2, ir.VectorNot(mask_vec)), shifted);
383 
384     V(datasize, Vd, result);
385     return true;
386 }
387 
SCVTF_fix_2(bool Q,Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd)388 bool TranslatorVisitor::SCVTF_fix_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
389     return ConvertFloat(*this, Q, immh, immb, Vn, Vd, Signedness::Signed, FloatConversionDirection::FixedToFloat, ir.current_location->FPCR().RMode());
390 }
391 
UCVTF_fix_2(bool Q,Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd)392 bool TranslatorVisitor::UCVTF_fix_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
393     return ConvertFloat(*this, Q, immh, immb, Vn, Vd, Signedness::Unsigned, FloatConversionDirection::FixedToFloat, ir.current_location->FPCR().RMode());
394 }
395 
FCVTZS_fix_2(bool Q,Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd)396 bool TranslatorVisitor::FCVTZS_fix_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
397     return ConvertFloat(*this, Q, immh, immb, Vn, Vd, Signedness::Signed, FloatConversionDirection::FloatToFixed, FP::RoundingMode::TowardsZero);
398 }
399 
FCVTZU_fix_2(bool Q,Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd)400 bool TranslatorVisitor::FCVTZU_fix_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
401     return ConvertFloat(*this, Q, immh, immb, Vn, Vd, Signedness::Unsigned, FloatConversionDirection::FloatToFixed, FP::RoundingMode::TowardsZero);
402 }
403 
404 } // namespace Dynarmic::A64
405