1 /* This file is part of the dynarmic project.
2 * Copyright (c) 2016 MerryMage
3 * SPDX-License-Identifier: 0BSD
4 */
5
6 #include "frontend/A32/translate/impl/translate_arm.h"
7
8 namespace Dynarmic::A32 {
9
Pack2x16To1x32(A32::IREmitter & ir,IR::U32 lo,IR::U32 hi)10 static IR::U32 Pack2x16To1x32(A32::IREmitter& ir, IR::U32 lo, IR::U32 hi) {
11 return ir.Or(ir.And(lo, ir.Imm32(0xFFFF)), ir.LogicalShiftLeft(hi, ir.Imm8(16), ir.Imm1(0)).result);
12 }
13
MostSignificantHalf(A32::IREmitter & ir,IR::U32 value)14 static IR::U16 MostSignificantHalf(A32::IREmitter& ir, IR::U32 value) {
15 return ir.LeastSignificantHalf(ir.LogicalShiftRight(value, ir.Imm8(16), ir.Imm1(0)).result);
16 }
17
18 // Saturation instructions
19
20 // SSAT<c> <Rd>, #<imm>, <Rn>{, <shift>}
arm_SSAT(Cond cond,Imm<5> sat_imm,Reg d,Imm<5> imm5,bool sh,Reg n)21 bool ArmTranslatorVisitor::arm_SSAT(Cond cond, Imm<5> sat_imm, Reg d, Imm<5> imm5, bool sh, Reg n) {
22 if (d == Reg::PC || n == Reg::PC) {
23 return UnpredictableInstruction();
24 }
25
26 if (!ConditionPassed(cond)) {
27 return true;
28 }
29
30 const auto saturate_to = static_cast<size_t>(sat_imm.ZeroExtend()) + 1;
31 const auto shift = !sh ? ShiftType::LSL : ShiftType::ASR;
32 const auto operand = EmitImmShift(ir.GetRegister(n), shift, imm5, ir.GetCFlag());
33 const auto result = ir.SignedSaturation(operand.result, saturate_to);
34
35 ir.SetRegister(d, result.result);
36 ir.OrQFlag(result.overflow);
37 return true;
38 }
39
40 // SSAT16<c> <Rd>, #<imm>, <Rn>
arm_SSAT16(Cond cond,Imm<4> sat_imm,Reg d,Reg n)41 bool ArmTranslatorVisitor::arm_SSAT16(Cond cond, Imm<4> sat_imm, Reg d, Reg n) {
42 if (d == Reg::PC || n == Reg::PC) {
43 return UnpredictableInstruction();
44 }
45
46 if (!ConditionPassed(cond)) {
47 return true;
48 }
49
50 const auto saturate_to = static_cast<size_t>(sat_imm.ZeroExtend()) + 1;
51 const auto lo_operand = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(ir.GetRegister(n)));
52 const auto hi_operand = ir.SignExtendHalfToWord(MostSignificantHalf(ir, ir.GetRegister(n)));
53 const auto lo_result = ir.SignedSaturation(lo_operand, saturate_to);
54 const auto hi_result = ir.SignedSaturation(hi_operand, saturate_to);
55
56 ir.SetRegister(d, Pack2x16To1x32(ir, lo_result.result, hi_result.result));
57 ir.OrQFlag(lo_result.overflow);
58 ir.OrQFlag(hi_result.overflow);
59 return true;
60 }
61
62 // USAT<c> <Rd>, #<imm5>, <Rn>{, <shift>}
arm_USAT(Cond cond,Imm<5> sat_imm,Reg d,Imm<5> imm5,bool sh,Reg n)63 bool ArmTranslatorVisitor::arm_USAT(Cond cond, Imm<5> sat_imm, Reg d, Imm<5> imm5, bool sh, Reg n) {
64 if (d == Reg::PC || n == Reg::PC) {
65 return UnpredictableInstruction();
66 }
67
68 if (!ConditionPassed(cond)) {
69 return true;
70 }
71
72 const auto saturate_to = static_cast<size_t>(sat_imm.ZeroExtend());
73 const auto shift = !sh ? ShiftType::LSL : ShiftType::ASR;
74 const auto operand = EmitImmShift(ir.GetRegister(n), shift, imm5, ir.GetCFlag());
75 const auto result = ir.UnsignedSaturation(operand.result, saturate_to);
76
77 ir.SetRegister(d, result.result);
78 ir.OrQFlag(result.overflow);
79 return true;
80 }
81
82 // USAT16<c> <Rd>, #<imm4>, <Rn>
arm_USAT16(Cond cond,Imm<4> sat_imm,Reg d,Reg n)83 bool ArmTranslatorVisitor::arm_USAT16(Cond cond, Imm<4> sat_imm, Reg d, Reg n) {
84 if (d == Reg::PC || n == Reg::PC) {
85 return UnpredictableInstruction();
86 }
87
88 if (!ConditionPassed(cond)) {
89 return true;
90 }
91
92 // UnsignedSaturation takes a *signed* value as input, hence sign extension is required.
93 const auto saturate_to = static_cast<size_t>(sat_imm.ZeroExtend());
94 const auto lo_operand = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(ir.GetRegister(n)));
95 const auto hi_operand = ir.SignExtendHalfToWord(MostSignificantHalf(ir, ir.GetRegister(n)));
96 const auto lo_result = ir.UnsignedSaturation(lo_operand, saturate_to);
97 const auto hi_result = ir.UnsignedSaturation(hi_operand, saturate_to);
98
99 ir.SetRegister(d, Pack2x16To1x32(ir, lo_result.result, hi_result.result));
100 ir.OrQFlag(lo_result.overflow);
101 ir.OrQFlag(hi_result.overflow);
102 return true;
103 }
104
105 // Saturated Add/Subtract instructions
106
107 // QADD<c> <Rd>, <Rm>, <Rn>
arm_QADD(Cond cond,Reg n,Reg d,Reg m)108 bool ArmTranslatorVisitor::arm_QADD(Cond cond, Reg n, Reg d, Reg m) {
109 if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
110 return UnpredictableInstruction();
111 }
112
113 if (!ConditionPassed(cond)) {
114 return true;
115 }
116
117 const auto a = ir.GetRegister(m);
118 const auto b = ir.GetRegister(n);
119 const auto result = ir.SignedSaturatedAdd(a, b);
120
121 ir.SetRegister(d, result.result);
122 ir.OrQFlag(result.overflow);
123 return true;
124 }
125
126 // QSUB<c> <Rd>, <Rm>, <Rn>
arm_QSUB(Cond cond,Reg n,Reg d,Reg m)127 bool ArmTranslatorVisitor::arm_QSUB(Cond cond, Reg n, Reg d, Reg m) {
128 if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
129 return UnpredictableInstruction();
130 }
131
132 if (!ConditionPassed(cond)) {
133 return true;
134 }
135
136 const auto a = ir.GetRegister(m);
137 const auto b = ir.GetRegister(n);
138 const auto result = ir.SignedSaturatedSub(a, b);
139
140 ir.SetRegister(d, result.result);
141 ir.OrQFlag(result.overflow);
142 return true;
143 }
144
145 // QDADD<c> <Rd>, <Rm>, <Rn>
arm_QDADD(Cond cond,Reg n,Reg d,Reg m)146 bool ArmTranslatorVisitor::arm_QDADD(Cond cond, Reg n, Reg d, Reg m) {
147 if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
148 return UnpredictableInstruction();
149 }
150
151 if (!ConditionPassed(cond)) {
152 return true;
153 }
154
155 const auto a = ir.GetRegister(m);
156 const auto b = ir.GetRegister(n);
157 const auto doubled = ir.SignedSaturatedAdd(b, b);
158 ir.OrQFlag(doubled.overflow);
159
160 const auto result = ir.SignedSaturatedAdd(a, doubled.result);
161 ir.SetRegister(d, result.result);
162 ir.OrQFlag(result.overflow);
163 return true;
164 }
165
166 // QDSUB<c> <Rd>, <Rm>, <Rn>
arm_QDSUB(Cond cond,Reg n,Reg d,Reg m)167 bool ArmTranslatorVisitor::arm_QDSUB(Cond cond, Reg n, Reg d, Reg m) {
168 if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
169 return UnpredictableInstruction();
170 }
171
172 if (!ConditionPassed(cond)) {
173 return true;
174 }
175
176 const auto a = ir.GetRegister(m);
177 const auto b = ir.GetRegister(n);
178 const auto doubled = ir.SignedSaturatedAdd(b, b);
179 ir.OrQFlag(doubled.overflow);
180
181 const auto result = ir.SignedSaturatedSub(a, doubled.result);
182 ir.SetRegister(d, result.result);
183 ir.OrQFlag(result.overflow);
184 return true;
185 }
186
187 // Parallel saturated instructions
188
189 // QASX<c> <Rd>, <Rn>, <Rm>
arm_QASX(Cond cond,Reg n,Reg d,Reg m)190 bool ArmTranslatorVisitor::arm_QASX(Cond cond, Reg n, Reg d, Reg m) {
191 if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
192 return UnpredictableInstruction();
193 }
194
195 if (!ConditionPassed(cond)) {
196 return true;
197 }
198
199 const auto Rn = ir.GetRegister(n);
200 const auto Rm = ir.GetRegister(m);
201 const auto Rn_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(Rn));
202 const auto Rn_hi = ir.SignExtendHalfToWord(MostSignificantHalf(ir, Rn));
203 const auto Rm_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(Rm));
204 const auto Rm_hi = ir.SignExtendHalfToWord(MostSignificantHalf(ir, Rm));
205 const auto diff = ir.SignedSaturation(ir.Sub(Rn_lo, Rm_hi), 16).result;
206 const auto sum = ir.SignedSaturation(ir.Add(Rn_hi, Rm_lo), 16).result;
207 const auto result = Pack2x16To1x32(ir, diff, sum);
208
209 ir.SetRegister(d, result);
210 return true;
211 }
212
213 // QSAX<c> <Rd>, <Rn>, <Rm>
arm_QSAX(Cond cond,Reg n,Reg d,Reg m)214 bool ArmTranslatorVisitor::arm_QSAX(Cond cond, Reg n, Reg d, Reg m) {
215 if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
216 return UnpredictableInstruction();
217 }
218
219 if (!ConditionPassed(cond)) {
220 return true;
221 }
222
223 const auto Rn = ir.GetRegister(n);
224 const auto Rm = ir.GetRegister(m);
225 const auto Rn_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(Rn));
226 const auto Rn_hi = ir.SignExtendHalfToWord(MostSignificantHalf(ir, Rn));
227 const auto Rm_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(Rm));
228 const auto Rm_hi = ir.SignExtendHalfToWord(MostSignificantHalf(ir, Rm));
229 const auto sum = ir.SignedSaturation(ir.Add(Rn_lo, Rm_hi), 16).result;
230 const auto diff = ir.SignedSaturation(ir.Sub(Rn_hi, Rm_lo), 16).result;
231 const auto result = Pack2x16To1x32(ir, sum, diff);
232
233 ir.SetRegister(d, result);
234 return true;
235 }
236
237 // UQASX<c> <Rd>, <Rn>, <Rm>
arm_UQASX(Cond cond,Reg n,Reg d,Reg m)238 bool ArmTranslatorVisitor::arm_UQASX(Cond cond, Reg n, Reg d, Reg m) {
239 if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
240 return UnpredictableInstruction();
241 }
242
243 if (!ConditionPassed(cond)) {
244 return true;
245 }
246
247 const auto Rn = ir.GetRegister(n);
248 const auto Rm = ir.GetRegister(m);
249 const auto Rn_lo = ir.ZeroExtendHalfToWord(ir.LeastSignificantHalf(Rn));
250 const auto Rn_hi = ir.ZeroExtendHalfToWord(MostSignificantHalf(ir, Rn));
251 const auto Rm_lo = ir.ZeroExtendHalfToWord(ir.LeastSignificantHalf(Rm));
252 const auto Rm_hi = ir.ZeroExtendHalfToWord(MostSignificantHalf(ir, Rm));
253 const auto diff = ir.UnsignedSaturation(ir.Sub(Rn_lo, Rm_hi), 16).result;
254 const auto sum = ir.UnsignedSaturation(ir.Add(Rn_hi, Rm_lo), 16).result;
255 const auto result = Pack2x16To1x32(ir, diff, sum);
256
257 ir.SetRegister(d, result);
258 return true;
259 }
260
261 // UQSAX<c> <Rd>, <Rn>, <Rm>
arm_UQSAX(Cond cond,Reg n,Reg d,Reg m)262 bool ArmTranslatorVisitor::arm_UQSAX(Cond cond, Reg n, Reg d, Reg m) {
263 if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
264 return UnpredictableInstruction();
265 }
266
267 if (!ConditionPassed(cond)) {
268 return true;
269 }
270
271 const auto Rn = ir.GetRegister(n);
272 const auto Rm = ir.GetRegister(m);
273 const auto Rn_lo = ir.ZeroExtendHalfToWord(ir.LeastSignificantHalf(Rn));
274 const auto Rn_hi = ir.ZeroExtendHalfToWord(MostSignificantHalf(ir, Rn));
275 const auto Rm_lo = ir.ZeroExtendHalfToWord(ir.LeastSignificantHalf(Rm));
276 const auto Rm_hi = ir.ZeroExtendHalfToWord(MostSignificantHalf(ir, Rm));
277 const auto sum = ir.UnsignedSaturation(ir.Add(Rn_lo, Rm_hi), 16).result;
278 const auto diff = ir.UnsignedSaturation(ir.Sub(Rn_hi, Rm_lo), 16).result;
279 const auto result = Pack2x16To1x32(ir, sum, diff);
280
281 ir.SetRegister(d, result);
282 return true;
283 }
284
285 } // namespace Dynarmic::A32
286