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