/ src / ARMeilleure / Instructions / InstEmitMul32.cs
InstEmitMul32.cs
  1  using ARMeilleure.Decoders;
  2  using ARMeilleure.IntermediateRepresentation;
  3  using ARMeilleure.State;
  4  using ARMeilleure.Translation;
  5  using System;
  6  using static ARMeilleure.Instructions.InstEmitAluHelper;
  7  using static ARMeilleure.Instructions.InstEmitHelper;
  8  using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
  9  
 10  namespace ARMeilleure.Instructions
 11  {
 12      static partial class InstEmit32
 13      {
 14          [Flags]
 15          private enum MullFlags
 16          {
 17              Subtract = 1,
 18              Add = 1 << 1,
 19              Signed = 1 << 2,
 20  
 21              SignedAdd = Signed | Add,
 22              SignedSubtract = Signed | Subtract,
 23          }
 24  
 25          public static void Mla(ArmEmitterContext context)
 26          {
 27              IOpCode32AluMla op = (IOpCode32AluMla)context.CurrOp;
 28  
 29              Operand n = GetAluN(context);
 30              Operand m = GetAluM(context);
 31              Operand a = GetIntA32(context, op.Ra);
 32  
 33              Operand res = context.Add(a, context.Multiply(n, m));
 34  
 35              if (ShouldSetFlags(context))
 36              {
 37                  EmitNZFlagsCheck(context, res);
 38              }
 39  
 40              EmitAluStore(context, res);
 41          }
 42  
 43          public static void Mls(ArmEmitterContext context)
 44          {
 45              IOpCode32AluMla op = (IOpCode32AluMla)context.CurrOp;
 46  
 47              Operand n = GetAluN(context);
 48              Operand m = GetAluM(context);
 49              Operand a = GetIntA32(context, op.Ra);
 50  
 51              Operand res = context.Subtract(a, context.Multiply(n, m));
 52  
 53              EmitAluStore(context, res);
 54          }
 55  
 56          public static void Smmla(ArmEmitterContext context)
 57          {
 58              EmitSmmul(context, MullFlags.SignedAdd);
 59          }
 60  
 61          public static void Smmls(ArmEmitterContext context)
 62          {
 63              EmitSmmul(context, MullFlags.SignedSubtract);
 64          }
 65  
 66          public static void Smmul(ArmEmitterContext context)
 67          {
 68              EmitSmmul(context, MullFlags.Signed);
 69          }
 70  
 71          private static void EmitSmmul(ArmEmitterContext context, MullFlags flags)
 72          {
 73              IOpCode32AluMla op = (IOpCode32AluMla)context.CurrOp;
 74  
 75              Operand n = context.SignExtend32(OperandType.I64, GetIntA32(context, op.Rn));
 76              Operand m = context.SignExtend32(OperandType.I64, GetIntA32(context, op.Rm));
 77  
 78              Operand res = context.Multiply(n, m);
 79  
 80              if (flags.HasFlag(MullFlags.Add) && op.Ra != 0xf)
 81              {
 82                  res = context.Add(context.ShiftLeft(context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Ra)), Const(32)), res);
 83              }
 84              else if (flags.HasFlag(MullFlags.Subtract))
 85              {
 86                  res = context.Subtract(context.ShiftLeft(context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Ra)), Const(32)), res);
 87              }
 88  
 89              if (op.R)
 90              {
 91                  res = context.Add(res, Const(0x80000000L));
 92              }
 93  
 94              Operand hi = context.ConvertI64ToI32(context.ShiftRightSI(res, Const(32)));
 95  
 96              EmitGenericAluStoreA32(context, op.Rd, false, hi);
 97          }
 98  
 99          public static void Smla__(ArmEmitterContext context)
100          {
101              IOpCode32AluMla op = (IOpCode32AluMla)context.CurrOp;
102  
103              Operand n = GetIntA32(context, op.Rn);
104              Operand m = GetIntA32(context, op.Rm);
105              Operand a = GetIntA32(context, op.Ra);
106  
107              if (op.NHigh)
108              {
109                  n = context.SignExtend16(OperandType.I64, context.ShiftRightUI(n, Const(16)));
110              }
111              else
112              {
113                  n = context.SignExtend16(OperandType.I64, n);
114              }
115  
116              if (op.MHigh)
117              {
118                  m = context.SignExtend16(OperandType.I64, context.ShiftRightUI(m, Const(16)));
119              }
120              else
121              {
122                  m = context.SignExtend16(OperandType.I64, m);
123              }
124  
125              Operand res = context.Multiply(n, m);
126  
127              Operand toAdd = context.SignExtend32(OperandType.I64, a);
128              res = context.Add(res, toAdd);
129              Operand q = context.ICompareNotEqual(res, context.SignExtend32(OperandType.I64, res));
130              res = context.ConvertI64ToI32(res);
131  
132              UpdateQFlag(context, q);
133  
134              EmitGenericAluStoreA32(context, op.Rd, false, res);
135          }
136  
137          public static void Smlal(ArmEmitterContext context)
138          {
139              EmitMlal(context, true);
140          }
141  
142          public static void Smlal__(ArmEmitterContext context)
143          {
144              IOpCode32AluUmull op = (IOpCode32AluUmull)context.CurrOp;
145  
146              Operand n = GetIntA32(context, op.Rn);
147              Operand m = GetIntA32(context, op.Rm);
148  
149              if (op.NHigh)
150              {
151                  n = context.SignExtend16(OperandType.I64, context.ShiftRightUI(n, Const(16)));
152              }
153              else
154              {
155                  n = context.SignExtend16(OperandType.I64, n);
156              }
157  
158              if (op.MHigh)
159              {
160                  m = context.SignExtend16(OperandType.I64, context.ShiftRightUI(m, Const(16)));
161              }
162              else
163              {
164                  m = context.SignExtend16(OperandType.I64, m);
165              }
166  
167              Operand res = context.Multiply(n, m);
168  
169              Operand toAdd = context.ShiftLeft(context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.RdHi)), Const(32));
170              toAdd = context.BitwiseOr(toAdd, context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.RdLo)));
171              res = context.Add(res, toAdd);
172  
173              Operand hi = context.ConvertI64ToI32(context.ShiftRightUI(res, Const(32)));
174              Operand lo = context.ConvertI64ToI32(res);
175  
176              EmitGenericAluStoreA32(context, op.RdHi, false, hi);
177              EmitGenericAluStoreA32(context, op.RdLo, false, lo);
178          }
179  
180          public static void Smlaw_(ArmEmitterContext context)
181          {
182              IOpCode32AluMla op = (IOpCode32AluMla)context.CurrOp;
183  
184              Operand n = GetIntA32(context, op.Rn);
185              Operand m = GetIntA32(context, op.Rm);
186              Operand a = GetIntA32(context, op.Ra);
187  
188              if (op.MHigh)
189              {
190                  m = context.SignExtend16(OperandType.I64, context.ShiftRightUI(m, Const(16)));
191              }
192              else
193              {
194                  m = context.SignExtend16(OperandType.I64, m);
195              }
196  
197              Operand res = context.Multiply(context.SignExtend32(OperandType.I64, n), m);
198  
199              Operand toAdd = context.ShiftLeft(context.SignExtend32(OperandType.I64, a), Const(16));
200              res = context.Add(res, toAdd);
201              res = context.ShiftRightSI(res, Const(16));
202              Operand q = context.ICompareNotEqual(res, context.SignExtend32(OperandType.I64, res));
203              res = context.ConvertI64ToI32(res);
204  
205              UpdateQFlag(context, q);
206  
207              EmitGenericAluStoreA32(context, op.Rd, false, res);
208          }
209  
210          public static void Smul__(ArmEmitterContext context)
211          {
212              IOpCode32AluMla op = (IOpCode32AluMla)context.CurrOp;
213  
214              Operand n = GetIntA32(context, op.Rn);
215              Operand m = GetIntA32(context, op.Rm);
216  
217              if (op.NHigh)
218              {
219                  n = context.ShiftRightSI(n, Const(16));
220              }
221              else
222              {
223                  n = context.SignExtend16(OperandType.I32, n);
224              }
225  
226              if (op.MHigh)
227              {
228                  m = context.ShiftRightSI(m, Const(16));
229              }
230              else
231              {
232                  m = context.SignExtend16(OperandType.I32, m);
233              }
234  
235              Operand res = context.Multiply(n, m);
236  
237              EmitGenericAluStoreA32(context, op.Rd, false, res);
238          }
239  
240          public static void Smull(ArmEmitterContext context)
241          {
242              IOpCode32AluUmull op = (IOpCode32AluUmull)context.CurrOp;
243  
244              Operand n = context.SignExtend32(OperandType.I64, GetIntA32(context, op.Rn));
245              Operand m = context.SignExtend32(OperandType.I64, GetIntA32(context, op.Rm));
246  
247              Operand res = context.Multiply(n, m);
248  
249              Operand hi = context.ConvertI64ToI32(context.ShiftRightUI(res, Const(32)));
250              Operand lo = context.ConvertI64ToI32(res);
251  
252              if (ShouldSetFlags(context))
253              {
254                  EmitNZFlagsCheck(context, res);
255              }
256  
257              EmitGenericAluStoreA32(context, op.RdHi, ShouldSetFlags(context), hi);
258              EmitGenericAluStoreA32(context, op.RdLo, ShouldSetFlags(context), lo);
259          }
260  
261          public static void Smulw_(ArmEmitterContext context)
262          {
263              IOpCode32AluMla op = (IOpCode32AluMla)context.CurrOp;
264  
265              Operand n = GetIntA32(context, op.Rn);
266              Operand m = GetIntA32(context, op.Rm);
267  
268              if (op.MHigh)
269              {
270                  m = context.SignExtend16(OperandType.I64, context.ShiftRightUI(m, Const(16)));
271              }
272              else
273              {
274                  m = context.SignExtend16(OperandType.I64, m);
275              }
276  
277              Operand res = context.Multiply(context.SignExtend32(OperandType.I64, n), m);
278  
279              res = context.ShiftRightUI(res, Const(16));
280              res = context.ConvertI64ToI32(res);
281  
282              EmitGenericAluStoreA32(context, op.Rd, false, res);
283          }
284  
285          public static void Umaal(ArmEmitterContext context)
286          {
287              IOpCode32AluUmull op = (IOpCode32AluUmull)context.CurrOp;
288  
289              Operand n = context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Rn));
290              Operand m = context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Rm));
291              Operand dHi = context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.RdHi));
292              Operand dLo = context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.RdLo));
293  
294              Operand res = context.Multiply(n, m);
295              res = context.Add(res, dHi);
296              res = context.Add(res, dLo);
297  
298              Operand hi = context.ConvertI64ToI32(context.ShiftRightUI(res, Const(32)));
299              Operand lo = context.ConvertI64ToI32(res);
300  
301              EmitGenericAluStoreA32(context, op.RdHi, false, hi);
302              EmitGenericAluStoreA32(context, op.RdLo, false, lo);
303          }
304  
305          public static void Umlal(ArmEmitterContext context)
306          {
307              EmitMlal(context, false);
308          }
309  
310          public static void Umull(ArmEmitterContext context)
311          {
312              IOpCode32AluUmull op = (IOpCode32AluUmull)context.CurrOp;
313  
314              Operand n = context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Rn));
315              Operand m = context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Rm));
316  
317              Operand res = context.Multiply(n, m);
318  
319              Operand hi = context.ConvertI64ToI32(context.ShiftRightUI(res, Const(32)));
320              Operand lo = context.ConvertI64ToI32(res);
321  
322              if (ShouldSetFlags(context))
323              {
324                  EmitNZFlagsCheck(context, res);
325              }
326  
327              EmitGenericAluStoreA32(context, op.RdHi, ShouldSetFlags(context), hi);
328              EmitGenericAluStoreA32(context, op.RdLo, ShouldSetFlags(context), lo);
329          }
330  
331          private static void EmitMlal(ArmEmitterContext context, bool signed)
332          {
333              IOpCode32AluUmull op = (IOpCode32AluUmull)context.CurrOp;
334  
335              Operand n = GetIntA32(context, op.Rn);
336              Operand m = GetIntA32(context, op.Rm);
337  
338              if (signed)
339              {
340                  n = context.SignExtend32(OperandType.I64, n);
341                  m = context.SignExtend32(OperandType.I64, m);
342              }
343              else
344              {
345                  n = context.ZeroExtend32(OperandType.I64, n);
346                  m = context.ZeroExtend32(OperandType.I64, m);
347              }
348  
349              Operand res = context.Multiply(n, m);
350  
351              Operand toAdd = context.ShiftLeft(context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.RdHi)), Const(32));
352              toAdd = context.BitwiseOr(toAdd, context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.RdLo)));
353              res = context.Add(res, toAdd);
354  
355              Operand hi = context.ConvertI64ToI32(context.ShiftRightUI(res, Const(32)));
356              Operand lo = context.ConvertI64ToI32(res);
357  
358              if (ShouldSetFlags(context))
359              {
360                  EmitNZFlagsCheck(context, res);
361              }
362  
363              EmitGenericAluStoreA32(context, op.RdHi, ShouldSetFlags(context), hi);
364              EmitGenericAluStoreA32(context, op.RdLo, ShouldSetFlags(context), lo);
365          }
366  
367          private static void UpdateQFlag(ArmEmitterContext context, Operand q)
368          {
369              Operand lblSkipSetQ = Label();
370  
371              context.BranchIfFalse(lblSkipSetQ, q);
372  
373              SetFlag(context, PState.QFlag, Const(1));
374  
375              context.MarkLabel(lblSkipSetQ);
376          }
377      }
378  }