1 /* This file is part of the dynarmic project.
2  * Copyright (c) 2018 MerryMage
3  * SPDX-License-Identifier: 0BSD
4  */
5 
6 #include "common/fp/rounding_mode.h"
7 #include "frontend/A64/translate/impl/impl.h"
8 
9 namespace Dynarmic::A64 {
10 namespace {
11 enum class Narrowing {
12     Truncation,
13     SaturateToUnsigned,
14     SaturateToSigned,
15 };
16 
17 enum class SaturatingShiftLeftType {
18     Signed,
19     Unsigned,
20     SignedWithUnsignedSaturation,
21 };
22 
23 enum class ShiftExtraBehavior {
24     None,
25     Accumulate,
26 };
27 
28 enum class Signedness {
29     Signed,
30     Unsigned,
31 };
32 
33 enum class FloatConversionDirection {
34     FixedToFloat,
35     FloatToFixed,
36 };
37 
SaturatingShiftLeft(TranslatorVisitor & v,Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd,SaturatingShiftLeftType type)38 bool SaturatingShiftLeft(TranslatorVisitor& v, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd, SaturatingShiftLeftType type) {
39     if (immh == 0b0000) {
40         return v.ReservedValue();
41     }
42 
43     const size_t esize = 8U << Common::HighestSetBit(immh.ZeroExtend());
44     const size_t shift_amount = concatenate(immh, immb).ZeroExtend() - esize;
45 
46     const IR::U128 operand = v.ir.ZeroExtendToQuad(v.V_scalar(esize, Vn));
47     const IR::U128 shift = v.ir.ZeroExtendToQuad(v.I(esize, shift_amount));
48     const IR::U128 result = [&v, esize, operand, shift, type] {
49         if (type == SaturatingShiftLeftType::Signed) {
50             return v.ir.VectorSignedSaturatedShiftLeft(esize, operand, shift);
51         }
52 
53         if (type == SaturatingShiftLeftType::Unsigned) {
54             return v.ir.VectorUnsignedSaturatedShiftLeft(esize, operand, shift);
55         }
56 
57         return v.ir.VectorSignedSaturatedShiftLeftUnsigned(esize, operand, shift);
58     }();
59 
60     v.ir.SetQ(Vd, result);
61     return true;
62 }
63 
ShiftRight(TranslatorVisitor & v,Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd,ShiftExtraBehavior behavior,Signedness signedness)64 bool ShiftRight(TranslatorVisitor& v, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd,
65                 ShiftExtraBehavior behavior, Signedness signedness) {
66     if (!immh.Bit<3>()) {
67         return v.ReservedValue();
68     }
69 
70     const size_t esize = 64;
71     const u8 shift_amount = static_cast<u8>((esize * 2) - concatenate(immh, immb).ZeroExtend());
72 
73     const IR::U64 operand = v.V_scalar(esize, Vn);
74     IR::U64 result = [&]() -> IR::U64 {
75         if (signedness == Signedness::Signed) {
76             return v.ir.ArithmeticShiftRight(operand, v.ir.Imm8(shift_amount));
77         }
78         return v.ir.LogicalShiftRight(operand, v.ir.Imm8(shift_amount));
79     }();
80 
81     if (behavior == ShiftExtraBehavior::Accumulate) {
82         const IR::U64 addend = v.V_scalar(esize, Vd);
83         result = v.ir.Add(result, addend);
84     }
85 
86     v.V_scalar(esize, Vd, result);
87     return true;
88 }
89 
RoundingShiftRight(TranslatorVisitor & v,Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd,ShiftExtraBehavior behavior,Signedness signedness)90 bool RoundingShiftRight(TranslatorVisitor& v, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd,
91                         ShiftExtraBehavior behavior, Signedness signedness) {
92     if (!immh.Bit<3>()) {
93         return v.ReservedValue();
94     }
95 
96     const size_t esize = 64;
97     const u8 shift_amount = static_cast<u8>((esize * 2) - concatenate(immh, immb).ZeroExtend());
98 
99     const IR::U64 operand = v.V_scalar(esize, Vn);
100     const IR::U64 round_bit = v.ir.LogicalShiftRight(v.ir.LogicalShiftLeft(operand, v.ir.Imm8(64 - shift_amount)), v.ir.Imm8(63));
101     const IR::U64 result = [&] {
102         const IR::U64 shifted = [&]() -> IR::U64 {
103             if (signedness == Signedness::Signed) {
104                 return v.ir.ArithmeticShiftRight(operand, v.ir.Imm8(shift_amount));
105             }
106             return v.ir.LogicalShiftRight(operand, v.ir.Imm8(shift_amount));
107         }();
108 
109         IR::U64 tmp = v.ir.Add(shifted, round_bit);
110 
111         if (behavior == ShiftExtraBehavior::Accumulate) {
112             tmp = v.ir.Add(tmp, v.V_scalar(esize, Vd));
113         }
114 
115         return tmp;
116     }();
117 
118     v.V_scalar(esize, Vd, result);
119     return true;
120 }
121 
122 enum class ShiftDirection {
123     Left,
124     Right,
125 };
126 
ShiftAndInsert(TranslatorVisitor & v,Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd,ShiftDirection direction)127 bool ShiftAndInsert(TranslatorVisitor& v, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd,
128                     ShiftDirection direction) {
129     if (!immh.Bit<3>()) {
130         return v.ReservedValue();
131     }
132 
133     const size_t esize = 64;
134 
135     const u8 shift_amount = [&] {
136         if (direction == ShiftDirection::Right) {
137             return static_cast<u8>((esize * 2) - concatenate(immh, immb).ZeroExtend());
138         }
139 
140         return static_cast<u8>(concatenate(immh, immb).ZeroExtend() - esize);
141     }();
142 
143     const u64 mask = [&] {
144         if (direction == ShiftDirection::Right) {
145              return shift_amount == esize ? 0 : Common::Ones<u64>(esize) >> shift_amount;
146         }
147 
148         return Common::Ones<u64>(esize) << shift_amount;
149     }();
150 
151     const IR::U64 operand1 = v.V_scalar(esize, Vn);
152     const IR::U64 operand2 = v.V_scalar(esize, Vd);
153 
154     const IR::U64 shifted = [&] {
155         if (direction == ShiftDirection::Right) {
156             return v.ir.LogicalShiftRight(operand1, v.ir.Imm8(shift_amount));
157         }
158 
159         return v.ir.LogicalShiftLeft(operand1, v.ir.Imm8(shift_amount));
160     }();
161 
162     const IR::U64 result = v.ir.Or(v.ir.And(operand2, v.ir.Not(v.ir.Imm64(mask))), shifted);
163     v.V_scalar(esize, Vd, result);
164     return true;
165 }
166 
ShiftRightNarrowing(TranslatorVisitor & v,Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd,Narrowing narrowing,Signedness signedness)167 bool ShiftRightNarrowing(TranslatorVisitor& v, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd,
168                          Narrowing narrowing, Signedness signedness) {
169     if (immh == 0b0000) {
170         return v.ReservedValue();
171     }
172 
173     if (immh.Bit<3>()) {
174         return v.ReservedValue();
175     }
176 
177     const size_t esize = 8 << Common::HighestSetBit(immh.ZeroExtend());
178     const size_t source_esize = 2 * esize;
179     const u8 shift_amount = static_cast<u8>(source_esize - concatenate(immh, immb).ZeroExtend());
180 
181     const IR::U128 operand = v.ir.ZeroExtendToQuad(v.ir.VectorGetElement(source_esize, v.V(128, Vn), 0));
182 
183     IR::U128 wide_result = [&] {
184         if (signedness == Signedness::Signed) {
185             return v.ir.VectorArithmeticShiftRight(source_esize, operand, shift_amount);
186         }
187         return v.ir.VectorLogicalShiftRight(source_esize, operand, shift_amount);
188     }();
189 
190     const IR::U128 result = [&] {
191         switch (narrowing) {
192         case Narrowing::Truncation:
193             return v.ir.VectorNarrow(source_esize, wide_result);
194         case Narrowing::SaturateToUnsigned:
195             if (signedness == Signedness::Signed) {
196                 return v.ir.VectorSignedSaturatedNarrowToUnsigned(source_esize, wide_result);
197             }
198             return v.ir.VectorUnsignedSaturatedNarrow(source_esize, wide_result);
199         case Narrowing::SaturateToSigned:
200             ASSERT(signedness == Signedness::Signed);
201             return v.ir.VectorSignedSaturatedNarrowToSigned(source_esize, wide_result);
202         }
203         UNREACHABLE();
204     }();
205 
206     const IR::UAny segment = v.ir.VectorGetElement(esize, result, 0);
207     v.V_scalar(esize, Vd, segment);
208     return true;
209 }
210 
ScalarFPConvertWithRound(TranslatorVisitor & v,Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd,Signedness sign,FloatConversionDirection direction,FP::RoundingMode rounding_mode)211 bool ScalarFPConvertWithRound(TranslatorVisitor& v, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd, Signedness sign, FloatConversionDirection direction, FP::RoundingMode rounding_mode) {
212     const u32 immh_value = immh.ZeroExtend();
213 
214     if ((immh_value & 0b1110) == 0b0000) {
215         return v.ReservedValue();
216     }
217 
218     // TODO: We currently don't handle FP16, so bail like the ARM reference manual allows.
219     if ((immh_value & 0b1110) == 0b0010) {
220         return v.ReservedValue();
221     }
222 
223     const size_t esize = (immh_value & 0b1000) != 0 ? 64 : 32;
224     const size_t concat = concatenate(immh, immb).ZeroExtend();
225     const size_t fbits = (esize * 2) - concat;
226 
227     const IR::U32U64 operand = v.V_scalar(esize, Vn);
228     const IR::U32U64 result = [&]() -> IR::U32U64 {
229         switch (direction) {
230         case FloatConversionDirection::FloatToFixed:
231             if (esize == 64) {
232                 return sign == Signedness::Signed
233                        ? v.ir.FPToFixedS64(operand, fbits, rounding_mode)
234                        : v.ir.FPToFixedU64(operand, fbits, rounding_mode);
235             }
236 
237             return sign == Signedness::Signed
238                    ? v.ir.FPToFixedS32(operand, fbits, rounding_mode)
239                    : v.ir.FPToFixedU32(operand, fbits, rounding_mode);
240 
241         case FloatConversionDirection::FixedToFloat:
242             if (esize == 64) {
243                 return sign == Signedness::Signed
244                        ? v.ir.FPSignedFixedToDouble(operand, fbits, rounding_mode)
245                        : v.ir.FPUnsignedFixedToDouble(operand, fbits, rounding_mode);
246             }
247 
248             return sign == Signedness::Signed
249                    ? v.ir.FPSignedFixedToSingle(operand, fbits, rounding_mode)
250                    : v.ir.FPUnsignedFixedToSingle(operand, fbits, rounding_mode);
251         }
252 
253         UNREACHABLE();
254     }();
255 
256     v.V_scalar(esize, Vd, result);
257     return true;
258 }
259 } // Anonymous namespace
260 
FCVTZS_fix_1(Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd)261 bool TranslatorVisitor::FCVTZS_fix_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
262     return ScalarFPConvertWithRound(*this, immh, immb, Vn, Vd, Signedness::Signed, FloatConversionDirection::FloatToFixed, FP::RoundingMode::TowardsZero);
263 }
264 
FCVTZU_fix_1(Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd)265 bool TranslatorVisitor::FCVTZU_fix_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
266     return ScalarFPConvertWithRound(*this, immh, immb, Vn, Vd, Signedness::Unsigned, FloatConversionDirection::FloatToFixed, FP::RoundingMode::TowardsZero);
267 }
268 
SCVTF_fix_1(Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd)269 bool TranslatorVisitor::SCVTF_fix_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
270     return ScalarFPConvertWithRound(*this, immh, immb, Vn, Vd, Signedness::Signed, FloatConversionDirection::FixedToFloat, ir.current_location->FPCR().RMode());
271 }
272 
UCVTF_fix_1(Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd)273 bool TranslatorVisitor::UCVTF_fix_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
274     return ScalarFPConvertWithRound(*this, immh, immb, Vn, Vd, Signedness::Unsigned, FloatConversionDirection::FixedToFloat, ir.current_location->FPCR().RMode());
275 }
276 
SLI_1(Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd)277 bool TranslatorVisitor::SLI_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
278     return ShiftAndInsert(*this, immh, immb, Vn, Vd, ShiftDirection::Left);
279 }
280 
SRI_1(Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd)281 bool TranslatorVisitor::SRI_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
282     return ShiftAndInsert(*this, immh, immb, Vn, Vd, ShiftDirection::Right);
283 }
284 
SQSHL_imm_1(Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd)285 bool TranslatorVisitor::SQSHL_imm_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
286     return SaturatingShiftLeft(*this, immh, immb, Vn, Vd, SaturatingShiftLeftType::Signed);
287 }
288 
SQSHLU_1(Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd)289 bool TranslatorVisitor::SQSHLU_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
290     return SaturatingShiftLeft(*this, immh, immb, Vn, Vd, SaturatingShiftLeftType::SignedWithUnsignedSaturation);
291 }
292 
SQSHRN_1(Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd)293 bool TranslatorVisitor::SQSHRN_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
294     return ShiftRightNarrowing(*this, immh, immb, Vn, Vd, Narrowing::SaturateToSigned, Signedness::Signed);
295 }
296 
SQSHRUN_1(Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd)297 bool TranslatorVisitor::SQSHRUN_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
298     return ShiftRightNarrowing(*this, immh, immb, Vn, Vd, Narrowing::SaturateToUnsigned, Signedness::Signed);
299 }
300 
SRSHR_1(Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd)301 bool TranslatorVisitor::SRSHR_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
302     return RoundingShiftRight(*this, immh, immb, Vn, Vd, ShiftExtraBehavior::None, Signedness::Signed);
303 }
304 
SRSRA_1(Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd)305 bool TranslatorVisitor::SRSRA_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
306     return RoundingShiftRight(*this, immh, immb, Vn, Vd, ShiftExtraBehavior::Accumulate, Signedness::Signed);
307 }
308 
SSHR_1(Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd)309 bool TranslatorVisitor::SSHR_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
310     return ShiftRight(*this, immh, immb, Vn, Vd, ShiftExtraBehavior::None, Signedness::Signed);
311 }
312 
SSRA_1(Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd)313 bool TranslatorVisitor::SSRA_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
314     return ShiftRight(*this, immh, immb, Vn, Vd, ShiftExtraBehavior::Accumulate, Signedness::Signed);
315 }
316 
SHL_1(Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd)317 bool TranslatorVisitor::SHL_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
318     if (!immh.Bit<3>()) {
319         return ReservedValue();
320     }
321 
322     const size_t esize = 64;
323     const u8 shift_amount = static_cast<u8>(concatenate(immh, immb).ZeroExtend() - esize);
324 
325     const IR::U64 operand = V_scalar(esize, Vn);
326     const IR::U64 result = ir.LogicalShiftLeft(operand, ir.Imm8(shift_amount));
327 
328     V_scalar(esize, Vd, result);
329     return true;
330 }
331 
UQSHL_imm_1(Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd)332 bool TranslatorVisitor::UQSHL_imm_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
333     return SaturatingShiftLeft(*this, immh, immb, Vn, Vd, SaturatingShiftLeftType::Unsigned);
334 }
335 
UQSHRN_1(Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd)336 bool TranslatorVisitor::UQSHRN_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
337     return ShiftRightNarrowing(*this, immh, immb, Vn, Vd, Narrowing::SaturateToUnsigned, Signedness::Unsigned);
338 }
339 
URSHR_1(Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd)340 bool TranslatorVisitor::URSHR_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
341     return RoundingShiftRight(*this, immh, immb, Vn, Vd, ShiftExtraBehavior::None, Signedness::Unsigned);
342 }
343 
URSRA_1(Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd)344 bool TranslatorVisitor::URSRA_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
345     return RoundingShiftRight(*this, immh, immb, Vn, Vd, ShiftExtraBehavior::Accumulate, Signedness::Unsigned);
346 }
347 
USHR_1(Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd)348 bool TranslatorVisitor::USHR_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
349     return ShiftRight(*this, immh, immb, Vn, Vd, ShiftExtraBehavior::None, Signedness::Unsigned);
350 }
351 
USRA_1(Imm<4> immh,Imm<3> immb,Vec Vn,Vec Vd)352 bool TranslatorVisitor::USRA_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
353     return ShiftRight(*this, immh, immb, Vn, Vd, ShiftExtraBehavior::Accumulate, Signedness::Unsigned);
354 }
355 
356 } // namespace Dynarmic::A64
357