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