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