/ src / ARMeilleure / Instructions / InstEmitSimdCmp32.cs
InstEmitSimdCmp32.cs
  1  using ARMeilleure.Decoders;
  2  using ARMeilleure.IntermediateRepresentation;
  3  using ARMeilleure.State;
  4  using ARMeilleure.Translation;
  5  using System;
  6  
  7  using static ARMeilleure.Instructions.InstEmitHelper;
  8  using static ARMeilleure.Instructions.InstEmitSimdHelper;
  9  using static ARMeilleure.Instructions.InstEmitSimdHelper32;
 10  using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
 11  
 12  namespace ARMeilleure.Instructions
 13  {
 14      using Func2I = Func<Operand, Operand, Operand>;
 15  
 16      static partial class InstEmit32
 17      {
 18          public static void Vceq_V(ArmEmitterContext context)
 19          {
 20              if (Optimizations.FastFP && Optimizations.UseAdvSimd)
 21              {
 22                  InstEmitSimdHelper32Arm64.EmitCmpOpF32(context, CmpCondition.Equal, false);
 23              }
 24              else if (Optimizations.FastFP && Optimizations.UseSse2)
 25              {
 26                  EmitSse2OrAvxCmpOpF32(context, CmpCondition.Equal, false);
 27              }
 28              else
 29              {
 30                  EmitCmpOpF32(context, nameof(SoftFloat32.FPCompareEQFpscr), false);
 31              }
 32          }
 33  
 34          public static void Vceq_I(ArmEmitterContext context)
 35          {
 36              EmitCmpOpI32(context, context.ICompareEqual, context.ICompareEqual, false, false);
 37          }
 38  
 39          public static void Vceq_Z(ArmEmitterContext context)
 40          {
 41              OpCode32Simd op = (OpCode32Simd)context.CurrOp;
 42  
 43              if (op.F)
 44              {
 45                  if (Optimizations.FastFP && Optimizations.UseAdvSimd)
 46                  {
 47                      InstEmitSimdHelper32Arm64.EmitCmpOpF32(context, CmpCondition.Equal, true);
 48                  }
 49                  else if (Optimizations.FastFP && Optimizations.UseSse2)
 50                  {
 51                      EmitSse2OrAvxCmpOpF32(context, CmpCondition.Equal, true);
 52                  }
 53                  else
 54                  {
 55                      EmitCmpOpF32(context, nameof(SoftFloat32.FPCompareEQFpscr), true);
 56                  }
 57              }
 58              else
 59              {
 60                  EmitCmpOpI32(context, context.ICompareEqual, context.ICompareEqual, true, false);
 61              }
 62          }
 63  
 64          public static void Vcge_V(ArmEmitterContext context)
 65          {
 66              if (Optimizations.FastFP && Optimizations.UseAdvSimd)
 67              {
 68                  InstEmitSimdHelper32Arm64.EmitCmpOpF32(context, CmpCondition.GreaterThanOrEqual, false);
 69              }
 70              else if (Optimizations.FastFP && Optimizations.UseAvx)
 71              {
 72                  EmitSse2OrAvxCmpOpF32(context, CmpCondition.GreaterThanOrEqual, false);
 73              }
 74              else
 75              {
 76                  EmitCmpOpF32(context, nameof(SoftFloat32.FPCompareGEFpscr), false);
 77              }
 78          }
 79  
 80          public static void Vcge_I(ArmEmitterContext context)
 81          {
 82              OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
 83  
 84              EmitCmpOpI32(context, context.ICompareGreaterOrEqual, context.ICompareGreaterOrEqualUI, false, !op.U);
 85          }
 86  
 87          public static void Vcge_Z(ArmEmitterContext context)
 88          {
 89              OpCode32Simd op = (OpCode32Simd)context.CurrOp;
 90  
 91              if (op.F)
 92              {
 93                  if (Optimizations.FastFP && Optimizations.UseAdvSimd)
 94                  {
 95                      InstEmitSimdHelper32Arm64.EmitCmpOpF32(context, CmpCondition.GreaterThanOrEqual, true);
 96                  }
 97                  else if (Optimizations.FastFP && Optimizations.UseAvx)
 98                  {
 99                      EmitSse2OrAvxCmpOpF32(context, CmpCondition.GreaterThanOrEqual, true);
100                  }
101                  else
102                  {
103                      EmitCmpOpF32(context, nameof(SoftFloat32.FPCompareGEFpscr), true);
104                  }
105              }
106              else
107              {
108                  EmitCmpOpI32(context, context.ICompareGreaterOrEqual, context.ICompareGreaterOrEqualUI, true, true);
109              }
110          }
111  
112          public static void Vcgt_V(ArmEmitterContext context)
113          {
114              if (Optimizations.FastFP && Optimizations.UseAdvSimd)
115              {
116                  InstEmitSimdHelper32Arm64.EmitCmpOpF32(context, CmpCondition.GreaterThan, false);
117              }
118              else if (Optimizations.FastFP && Optimizations.UseAvx)
119              {
120                  EmitSse2OrAvxCmpOpF32(context, CmpCondition.GreaterThan, false);
121              }
122              else
123              {
124                  EmitCmpOpF32(context, nameof(SoftFloat32.FPCompareGTFpscr), false);
125              }
126          }
127  
128          public static void Vcgt_I(ArmEmitterContext context)
129          {
130              OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
131  
132              EmitCmpOpI32(context, context.ICompareGreater, context.ICompareGreaterUI, false, !op.U);
133          }
134  
135          public static void Vcgt_Z(ArmEmitterContext context)
136          {
137              OpCode32Simd op = (OpCode32Simd)context.CurrOp;
138  
139              if (op.F)
140              {
141                  if (Optimizations.FastFP && Optimizations.UseAdvSimd)
142                  {
143                      InstEmitSimdHelper32Arm64.EmitCmpOpF32(context, CmpCondition.GreaterThan, true);
144                  }
145                  else if (Optimizations.FastFP && Optimizations.UseAvx)
146                  {
147                      EmitSse2OrAvxCmpOpF32(context, CmpCondition.GreaterThan, true);
148                  }
149                  else
150                  {
151                      EmitCmpOpF32(context, nameof(SoftFloat32.FPCompareGTFpscr), true);
152                  }
153              }
154              else
155              {
156                  EmitCmpOpI32(context, context.ICompareGreater, context.ICompareGreaterUI, true, true);
157              }
158          }
159  
160          public static void Vcle_Z(ArmEmitterContext context)
161          {
162              OpCode32Simd op = (OpCode32Simd)context.CurrOp;
163  
164              if (op.F)
165              {
166                  if (Optimizations.FastFP && Optimizations.UseAdvSimd)
167                  {
168                      InstEmitSimdHelper32Arm64.EmitCmpOpF32(context, CmpCondition.LessThanOrEqual, true);
169                  }
170                  else if (Optimizations.FastFP && Optimizations.UseSse2)
171                  {
172                      EmitSse2OrAvxCmpOpF32(context, CmpCondition.LessThanOrEqual, true);
173                  }
174                  else
175                  {
176                      EmitCmpOpF32(context, nameof(SoftFloat32.FPCompareLEFpscr), true);
177                  }
178              }
179              else
180              {
181                  EmitCmpOpI32(context, context.ICompareLessOrEqual, context.ICompareLessOrEqualUI, true, true);
182              }
183          }
184  
185          public static void Vclt_Z(ArmEmitterContext context)
186          {
187              OpCode32Simd op = (OpCode32Simd)context.CurrOp;
188  
189              if (op.F)
190              {
191                  if (Optimizations.FastFP && Optimizations.UseAdvSimd)
192                  {
193                      InstEmitSimdHelper32Arm64.EmitCmpOpF32(context, CmpCondition.LessThan, true);
194                  }
195                  else if (Optimizations.FastFP && Optimizations.UseSse2)
196                  {
197                      EmitSse2OrAvxCmpOpF32(context, CmpCondition.LessThan, true);
198                  }
199                  else
200                  {
201                      EmitCmpOpF32(context, nameof(SoftFloat32.FPCompareLTFpscr), true);
202                  }
203              }
204              else
205              {
206                  EmitCmpOpI32(context, context.ICompareLess, context.ICompareLessUI, true, true);
207              }
208          }
209  
210          private static void EmitCmpOpF32(ArmEmitterContext context, string name, bool zero)
211          {
212              if (zero)
213              {
214                  EmitVectorUnaryOpF32(context, (m) =>
215                  {
216                      Operand zeroOp = m.Type == OperandType.FP64 ? ConstF(0.0d) : ConstF(0.0f);
217  
218                      return EmitSoftFloatCallDefaultFpscr(context, name, m, zeroOp);
219                  });
220              }
221              else
222              {
223                  EmitVectorBinaryOpF32(context, (n, m) =>
224                  {
225                      return EmitSoftFloatCallDefaultFpscr(context, name, n, m);
226                  });
227              }
228          }
229  
230          private static Operand ZerosOrOnes(ArmEmitterContext context, Operand fromBool, OperandType baseType)
231          {
232              var ones = (baseType == OperandType.I64) ? Const(-1L) : Const(-1);
233  
234              return context.ConditionalSelect(fromBool, ones, Const(baseType, 0L));
235          }
236  
237          private static void EmitCmpOpI32(
238              ArmEmitterContext context,
239              Func2I signedOp,
240              Func2I unsignedOp,
241              bool zero,
242              bool signed)
243          {
244              if (zero)
245              {
246                  if (signed)
247                  {
248                      EmitVectorUnaryOpSx32(context, (m) =>
249                      {
250                          OperandType type = m.Type;
251                          Operand zeroV = (type == OperandType.I64) ? Const(0L) : Const(0);
252  
253                          return ZerosOrOnes(context, signedOp(m, zeroV), type);
254                      });
255                  }
256                  else
257                  {
258                      EmitVectorUnaryOpZx32(context, (m) =>
259                      {
260                          OperandType type = m.Type;
261                          Operand zeroV = (type == OperandType.I64) ? Const(0L) : Const(0);
262  
263                          return ZerosOrOnes(context, unsignedOp(m, zeroV), type);
264                      });
265                  }
266              }
267              else
268              {
269                  if (signed)
270                  {
271                      EmitVectorBinaryOpSx32(context, (n, m) => ZerosOrOnes(context, signedOp(n, m), n.Type));
272                  }
273                  else
274                  {
275                      EmitVectorBinaryOpZx32(context, (n, m) => ZerosOrOnes(context, unsignedOp(n, m), n.Type));
276                  }
277              }
278          }
279  
280          public static void Vcmp(ArmEmitterContext context)
281          {
282              if (Optimizations.UseAdvSimd)
283              {
284                  InstEmitSimdHelper32Arm64.EmitVcmpOrVcmpe(context, false);
285              }
286              else
287              {
288                  EmitVcmpOrVcmpe(context, false);
289              }
290          }
291  
292          public static void Vcmpe(ArmEmitterContext context)
293          {
294              if (Optimizations.UseAdvSimd)
295              {
296                  InstEmitSimdHelper32Arm64.EmitVcmpOrVcmpe(context, true);
297              }
298              else
299              {
300                  EmitVcmpOrVcmpe(context, true);
301              }
302          }
303  
304          private static void EmitVcmpOrVcmpe(ArmEmitterContext context, bool signalNaNs)
305          {
306              OpCode32SimdS op = (OpCode32SimdS)context.CurrOp;
307  
308              bool cmpWithZero = (op.Opc & 2) != 0;
309              int sizeF = op.Size & 1;
310  
311              if (Optimizations.FastFP && (signalNaNs ? Optimizations.UseAvx : Optimizations.UseSse2))
312              {
313                  CmpCondition cmpOrdered = signalNaNs ? CmpCondition.OrderedS : CmpCondition.OrderedQ;
314  
315                  bool doubleSize = sizeF != 0;
316                  int shift = doubleSize ? 1 : 2;
317                  Operand m = GetVecA32(op.Vm >> shift);
318                  Operand n = GetVecA32(op.Vd >> shift);
319  
320                  n = EmitSwapScalar(context, n, op.Vd, doubleSize);
321                  m = cmpWithZero ? context.VectorZero() : EmitSwapScalar(context, m, op.Vm, doubleSize);
322  
323                  Operand lblNaN = Label();
324                  Operand lblEnd = Label();
325  
326                  if (!doubleSize)
327                  {
328                      Operand ordMask = context.AddIntrinsic(Intrinsic.X86Cmpss, n, m, Const((int)cmpOrdered));
329  
330                      Operand isOrdered = context.AddIntrinsicInt(Intrinsic.X86Cvtsi2si, ordMask);
331  
332                      context.BranchIfFalse(lblNaN, isOrdered);
333  
334                      Operand cf = context.AddIntrinsicInt(Intrinsic.X86Comissge, n, m);
335                      Operand zf = context.AddIntrinsicInt(Intrinsic.X86Comisseq, n, m);
336                      Operand nf = context.AddIntrinsicInt(Intrinsic.X86Comisslt, n, m);
337  
338                      SetFpFlag(context, FPState.VFlag, Const(0));
339                      SetFpFlag(context, FPState.CFlag, cf);
340                      SetFpFlag(context, FPState.ZFlag, zf);
341                      SetFpFlag(context, FPState.NFlag, nf);
342                  }
343                  else
344                  {
345                      Operand ordMask = context.AddIntrinsic(Intrinsic.X86Cmpsd, n, m, Const((int)cmpOrdered));
346  
347                      Operand isOrdered = context.AddIntrinsicLong(Intrinsic.X86Cvtsi2si, ordMask);
348  
349                      context.BranchIfFalse(lblNaN, isOrdered);
350  
351                      Operand cf = context.AddIntrinsicInt(Intrinsic.X86Comisdge, n, m);
352                      Operand zf = context.AddIntrinsicInt(Intrinsic.X86Comisdeq, n, m);
353                      Operand nf = context.AddIntrinsicInt(Intrinsic.X86Comisdlt, n, m);
354  
355                      SetFpFlag(context, FPState.VFlag, Const(0));
356                      SetFpFlag(context, FPState.CFlag, cf);
357                      SetFpFlag(context, FPState.ZFlag, zf);
358                      SetFpFlag(context, FPState.NFlag, nf);
359                  }
360  
361                  context.Branch(lblEnd);
362  
363                  context.MarkLabel(lblNaN);
364  
365                  SetFpFlag(context, FPState.VFlag, Const(1));
366                  SetFpFlag(context, FPState.CFlag, Const(1));
367                  SetFpFlag(context, FPState.ZFlag, Const(0));
368                  SetFpFlag(context, FPState.NFlag, Const(0));
369  
370                  context.MarkLabel(lblEnd);
371              }
372              else
373              {
374                  OperandType type = sizeF != 0 ? OperandType.FP64 : OperandType.FP32;
375  
376                  Operand ne = ExtractScalar(context, type, op.Vd);
377                  Operand me;
378  
379                  if (cmpWithZero)
380                  {
381                      me = sizeF == 0 ? ConstF(0f) : ConstF(0d);
382                  }
383                  else
384                  {
385                      me = ExtractScalar(context, type, op.Vm);
386                  }
387  
388                  Operand nzcv = EmitSoftFloatCall(context, nameof(SoftFloat32.FPCompare), ne, me, Const(signalNaNs));
389  
390                  EmitSetFpscrNzcv(context, nzcv);
391              }
392          }
393  
394          private static void EmitSetFpscrNzcv(ArmEmitterContext context, Operand nzcv)
395          {
396              Operand Extract(Operand value, int bit)
397              {
398                  if (bit != 0)
399                  {
400                      value = context.ShiftRightUI(value, Const(bit));
401                  }
402  
403                  value = context.BitwiseAnd(value, Const(1));
404  
405                  return value;
406              }
407  
408              SetFpFlag(context, FPState.VFlag, Extract(nzcv, 0));
409              SetFpFlag(context, FPState.CFlag, Extract(nzcv, 1));
410              SetFpFlag(context, FPState.ZFlag, Extract(nzcv, 2));
411              SetFpFlag(context, FPState.NFlag, Extract(nzcv, 3));
412          }
413  
414          private static void EmitSse2OrAvxCmpOpF32(ArmEmitterContext context, CmpCondition cond, bool zero)
415          {
416              OpCode32Simd op = (OpCode32Simd)context.CurrOp;
417  
418              int sizeF = op.Size & 1;
419              Intrinsic inst = (sizeF == 0) ? Intrinsic.X86Cmpps : Intrinsic.X86Cmppd;
420  
421              if (zero)
422              {
423                  EmitVectorUnaryOpSimd32(context, (m) =>
424                  {
425                      return context.AddIntrinsic(inst, m, context.VectorZero(), Const((int)cond));
426                  });
427              }
428              else
429              {
430                  EmitVectorBinaryOpSimd32(context, (n, m) =>
431                  {
432                      return context.AddIntrinsic(inst, n, m, Const((int)cond));
433                  });
434              }
435          }
436      }
437  }