/ src / ARMeilleure / Instructions / InstEmitSimdCmp.cs
InstEmitSimdCmp.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.InstEmitHelper;
  7  using static ARMeilleure.Instructions.InstEmitSimdHelper;
  8  using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
  9  
 10  namespace ARMeilleure.Instructions
 11  {
 12      using Func2I = Func<Operand, Operand, Operand>;
 13  
 14      static partial class InstEmit
 15      {
 16          public static void Cmeq_S(ArmEmitterContext context)
 17          {
 18              EmitCmpOp(context, (op1, op2) => context.ICompareEqual(op1, op2), scalar: true);
 19          }
 20  
 21          public static void Cmeq_V(ArmEmitterContext context)
 22          {
 23              if (Optimizations.UseSse41)
 24              {
 25                  OpCodeSimd op = (OpCodeSimd)context.CurrOp;
 26  
 27                  Operand n = GetVec(op.Rn);
 28                  Operand m;
 29  
 30                  if (op is OpCodeSimdReg binOp)
 31                  {
 32                      m = GetVec(binOp.Rm);
 33                  }
 34                  else
 35                  {
 36                      m = context.VectorZero();
 37                  }
 38  
 39                  Intrinsic cmpInst = X86PcmpeqInstruction[op.Size];
 40  
 41                  Operand res = context.AddIntrinsic(cmpInst, n, m);
 42  
 43                  if (op.RegisterSize == RegisterSize.Simd64)
 44                  {
 45                      res = context.VectorZeroUpper64(res);
 46                  }
 47  
 48                  context.Copy(GetVec(op.Rd), res);
 49              }
 50              else
 51              {
 52                  EmitCmpOp(context, (op1, op2) => context.ICompareEqual(op1, op2), scalar: false);
 53              }
 54          }
 55  
 56          public static void Cmge_S(ArmEmitterContext context)
 57          {
 58              EmitCmpOp(context, (op1, op2) => context.ICompareGreaterOrEqual(op1, op2), scalar: true);
 59          }
 60  
 61          public static void Cmge_V(ArmEmitterContext context)
 62          {
 63              if (Optimizations.UseSse42)
 64              {
 65                  OpCodeSimd op = (OpCodeSimd)context.CurrOp;
 66  
 67                  Operand n = GetVec(op.Rn);
 68                  Operand m;
 69  
 70                  if (op is OpCodeSimdReg binOp)
 71                  {
 72                      m = GetVec(binOp.Rm);
 73                  }
 74                  else
 75                  {
 76                      m = context.VectorZero();
 77                  }
 78  
 79                  Intrinsic cmpInst = X86PcmpgtInstruction[op.Size];
 80  
 81                  Operand res = context.AddIntrinsic(cmpInst, m, n);
 82  
 83                  Operand mask = X86GetAllElements(context, -1L);
 84  
 85                  res = context.AddIntrinsic(Intrinsic.X86Pandn, res, mask);
 86  
 87                  if (op.RegisterSize == RegisterSize.Simd64)
 88                  {
 89                      res = context.VectorZeroUpper64(res);
 90                  }
 91  
 92                  context.Copy(GetVec(op.Rd), res);
 93              }
 94              else
 95              {
 96                  EmitCmpOp(context, (op1, op2) => context.ICompareGreaterOrEqual(op1, op2), scalar: false);
 97              }
 98          }
 99  
100          public static void Cmgt_S(ArmEmitterContext context)
101          {
102              EmitCmpOp(context, (op1, op2) => context.ICompareGreater(op1, op2), scalar: true);
103          }
104  
105          public static void Cmgt_V(ArmEmitterContext context)
106          {
107              if (Optimizations.UseSse42)
108              {
109                  OpCodeSimd op = (OpCodeSimd)context.CurrOp;
110  
111                  Operand n = GetVec(op.Rn);
112                  Operand m;
113  
114                  if (op is OpCodeSimdReg binOp)
115                  {
116                      m = GetVec(binOp.Rm);
117                  }
118                  else
119                  {
120                      m = context.VectorZero();
121                  }
122  
123                  Intrinsic cmpInst = X86PcmpgtInstruction[op.Size];
124  
125                  Operand res = context.AddIntrinsic(cmpInst, n, m);
126  
127                  if (op.RegisterSize == RegisterSize.Simd64)
128                  {
129                      res = context.VectorZeroUpper64(res);
130                  }
131  
132                  context.Copy(GetVec(op.Rd), res);
133              }
134              else
135              {
136                  EmitCmpOp(context, (op1, op2) => context.ICompareGreater(op1, op2), scalar: false);
137              }
138          }
139  
140          public static void Cmhi_S(ArmEmitterContext context)
141          {
142              EmitCmpOp(context, (op1, op2) => context.ICompareGreaterUI(op1, op2), scalar: true);
143          }
144  
145          public static void Cmhi_V(ArmEmitterContext context)
146          {
147              OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
148  
149              if (Optimizations.UseSse41 && op.Size < 3)
150              {
151                  Operand n = GetVec(op.Rn);
152                  Operand m = GetVec(op.Rm);
153  
154                  Intrinsic maxInst = X86PmaxuInstruction[op.Size];
155  
156                  Operand res = context.AddIntrinsic(maxInst, m, n);
157  
158                  Intrinsic cmpInst = X86PcmpeqInstruction[op.Size];
159  
160                  res = context.AddIntrinsic(cmpInst, res, m);
161  
162                  Operand mask = X86GetAllElements(context, -1L);
163  
164                  res = context.AddIntrinsic(Intrinsic.X86Pandn, res, mask);
165  
166                  if (op.RegisterSize == RegisterSize.Simd64)
167                  {
168                      res = context.VectorZeroUpper64(res);
169                  }
170  
171                  context.Copy(GetVec(op.Rd), res);
172              }
173              else
174              {
175                  EmitCmpOp(context, (op1, op2) => context.ICompareGreaterUI(op1, op2), scalar: false);
176              }
177          }
178  
179          public static void Cmhs_S(ArmEmitterContext context)
180          {
181              EmitCmpOp(context, (op1, op2) => context.ICompareGreaterOrEqualUI(op1, op2), scalar: true);
182          }
183  
184          public static void Cmhs_V(ArmEmitterContext context)
185          {
186              OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
187  
188              if (Optimizations.UseSse41 && op.Size < 3)
189              {
190                  Operand n = GetVec(op.Rn);
191                  Operand m = GetVec(op.Rm);
192  
193                  Intrinsic maxInst = X86PmaxuInstruction[op.Size];
194  
195                  Operand res = context.AddIntrinsic(maxInst, n, m);
196  
197                  Intrinsic cmpInst = X86PcmpeqInstruction[op.Size];
198  
199                  res = context.AddIntrinsic(cmpInst, res, n);
200  
201                  if (op.RegisterSize == RegisterSize.Simd64)
202                  {
203                      res = context.VectorZeroUpper64(res);
204                  }
205  
206                  context.Copy(GetVec(op.Rd), res);
207              }
208              else
209              {
210                  EmitCmpOp(context, (op1, op2) => context.ICompareGreaterOrEqualUI(op1, op2), scalar: false);
211              }
212          }
213  
214          public static void Cmle_S(ArmEmitterContext context)
215          {
216              EmitCmpOp(context, (op1, op2) => context.ICompareLessOrEqual(op1, op2), scalar: true);
217          }
218  
219          public static void Cmle_V(ArmEmitterContext context)
220          {
221              if (Optimizations.UseSse42)
222              {
223                  OpCodeSimd op = (OpCodeSimd)context.CurrOp;
224  
225                  Operand n = GetVec(op.Rn);
226  
227                  Intrinsic cmpInst = X86PcmpgtInstruction[op.Size];
228  
229                  Operand res = context.AddIntrinsic(cmpInst, n, context.VectorZero());
230  
231                  Operand mask = X86GetAllElements(context, -1L);
232  
233                  res = context.AddIntrinsic(Intrinsic.X86Pandn, res, mask);
234  
235                  if (op.RegisterSize == RegisterSize.Simd64)
236                  {
237                      res = context.VectorZeroUpper64(res);
238                  }
239  
240                  context.Copy(GetVec(op.Rd), res);
241              }
242              else
243              {
244                  EmitCmpOp(context, (op1, op2) => context.ICompareLessOrEqual(op1, op2), scalar: false);
245              }
246          }
247  
248          public static void Cmlt_S(ArmEmitterContext context)
249          {
250              EmitCmpOp(context, (op1, op2) => context.ICompareLess(op1, op2), scalar: true);
251          }
252  
253          public static void Cmlt_V(ArmEmitterContext context)
254          {
255              if (Optimizations.UseSse42)
256              {
257                  OpCodeSimd op = (OpCodeSimd)context.CurrOp;
258  
259                  Operand n = GetVec(op.Rn);
260  
261                  Intrinsic cmpInst = X86PcmpgtInstruction[op.Size];
262  
263                  Operand res = context.AddIntrinsic(cmpInst, context.VectorZero(), n);
264  
265                  if (op.RegisterSize == RegisterSize.Simd64)
266                  {
267                      res = context.VectorZeroUpper64(res);
268                  }
269  
270                  context.Copy(GetVec(op.Rd), res);
271              }
272              else
273              {
274                  EmitCmpOp(context, (op1, op2) => context.ICompareLess(op1, op2), scalar: false);
275              }
276          }
277  
278          public static void Cmtst_S(ArmEmitterContext context)
279          {
280              EmitCmtstOp(context, scalar: true);
281          }
282  
283          public static void Cmtst_V(ArmEmitterContext context)
284          {
285              EmitCmtstOp(context, scalar: false);
286          }
287  
288          public static void Facge_S(ArmEmitterContext context)
289          {
290              if (Optimizations.FastFP && Optimizations.UseAvx)
291              {
292                  EmitSse2OrAvxCmpOpF(context, CmpCondition.GreaterThanOrEqual, scalar: true, absolute: true);
293              }
294              else
295              {
296                  EmitCmpOpF(context, nameof(SoftFloat32.FPCompareGE), scalar: true, absolute: true);
297              }
298          }
299  
300          public static void Facge_V(ArmEmitterContext context)
301          {
302              if (Optimizations.FastFP && Optimizations.UseAvx)
303              {
304                  EmitSse2OrAvxCmpOpF(context, CmpCondition.GreaterThanOrEqual, scalar: false, absolute: true);
305              }
306              else
307              {
308                  EmitCmpOpF(context, nameof(SoftFloat32.FPCompareGE), scalar: false, absolute: true);
309              }
310          }
311  
312          public static void Facgt_S(ArmEmitterContext context)
313          {
314              if (Optimizations.FastFP && Optimizations.UseAvx)
315              {
316                  EmitSse2OrAvxCmpOpF(context, CmpCondition.GreaterThan, scalar: true, absolute: true);
317              }
318              else
319              {
320                  EmitCmpOpF(context, nameof(SoftFloat32.FPCompareGT), scalar: true, absolute: true);
321              }
322          }
323  
324          public static void Facgt_V(ArmEmitterContext context)
325          {
326              if (Optimizations.FastFP && Optimizations.UseAvx)
327              {
328                  EmitSse2OrAvxCmpOpF(context, CmpCondition.GreaterThan, scalar: false, absolute: true);
329              }
330              else
331              {
332                  EmitCmpOpF(context, nameof(SoftFloat32.FPCompareGT), scalar: false, absolute: true);
333              }
334          }
335  
336          public static void Fccmp_S(ArmEmitterContext context)
337          {
338              EmitFccmpOrFccmpe(context, signalNaNs: false);
339          }
340  
341          public static void Fccmpe_S(ArmEmitterContext context)
342          {
343              EmitFccmpOrFccmpe(context, signalNaNs: true);
344          }
345  
346          public static void Fcmeq_S(ArmEmitterContext context)
347          {
348              if (Optimizations.FastFP && Optimizations.UseSse2)
349              {
350                  EmitSse2OrAvxCmpOpF(context, CmpCondition.Equal, scalar: true);
351              }
352              else
353              {
354                  EmitCmpOpF(context, nameof(SoftFloat32.FPCompareEQ), scalar: true);
355              }
356          }
357  
358          public static void Fcmeq_V(ArmEmitterContext context)
359          {
360              if (Optimizations.FastFP && Optimizations.UseSse2)
361              {
362                  EmitSse2OrAvxCmpOpF(context, CmpCondition.Equal, scalar: false);
363              }
364              else
365              {
366                  EmitCmpOpF(context, nameof(SoftFloat32.FPCompareEQ), scalar: false);
367              }
368          }
369  
370          public static void Fcmge_S(ArmEmitterContext context)
371          {
372              if (Optimizations.FastFP && Optimizations.UseAvx)
373              {
374                  EmitSse2OrAvxCmpOpF(context, CmpCondition.GreaterThanOrEqual, scalar: true);
375              }
376              else
377              {
378                  EmitCmpOpF(context, nameof(SoftFloat32.FPCompareGE), scalar: true);
379              }
380          }
381  
382          public static void Fcmge_V(ArmEmitterContext context)
383          {
384              if (Optimizations.FastFP && Optimizations.UseAvx)
385              {
386                  EmitSse2OrAvxCmpOpF(context, CmpCondition.GreaterThanOrEqual, scalar: false);
387              }
388              else
389              {
390                  EmitCmpOpF(context, nameof(SoftFloat32.FPCompareGE), scalar: false);
391              }
392          }
393  
394          public static void Fcmgt_S(ArmEmitterContext context)
395          {
396              if (Optimizations.FastFP && Optimizations.UseAvx)
397              {
398                  EmitSse2OrAvxCmpOpF(context, CmpCondition.GreaterThan, scalar: true);
399              }
400              else
401              {
402                  EmitCmpOpF(context, nameof(SoftFloat32.FPCompareGT), scalar: true);
403              }
404          }
405  
406          public static void Fcmgt_V(ArmEmitterContext context)
407          {
408              if (Optimizations.FastFP && Optimizations.UseAvx)
409              {
410                  EmitSse2OrAvxCmpOpF(context, CmpCondition.GreaterThan, scalar: false);
411              }
412              else
413              {
414                  EmitCmpOpF(context, nameof(SoftFloat32.FPCompareGT), scalar: false);
415              }
416          }
417  
418          public static void Fcmle_S(ArmEmitterContext context)
419          {
420              if (Optimizations.FastFP && Optimizations.UseSse2)
421              {
422                  EmitSse2OrAvxCmpOpF(context, CmpCondition.LessThanOrEqual, scalar: true);
423              }
424              else
425              {
426                  EmitCmpOpF(context, nameof(SoftFloat32.FPCompareLE), scalar: true);
427              }
428          }
429  
430          public static void Fcmle_V(ArmEmitterContext context)
431          {
432              if (Optimizations.FastFP && Optimizations.UseSse2)
433              {
434                  EmitSse2OrAvxCmpOpF(context, CmpCondition.LessThanOrEqual, scalar: false);
435              }
436              else
437              {
438                  EmitCmpOpF(context, nameof(SoftFloat32.FPCompareLE), scalar: false);
439              }
440          }
441  
442          public static void Fcmlt_S(ArmEmitterContext context)
443          {
444              if (Optimizations.FastFP && Optimizations.UseSse2)
445              {
446                  EmitSse2OrAvxCmpOpF(context, CmpCondition.LessThan, scalar: true);
447              }
448              else
449              {
450                  EmitCmpOpF(context, nameof(SoftFloat32.FPCompareLT), scalar: true);
451              }
452          }
453  
454          public static void Fcmlt_V(ArmEmitterContext context)
455          {
456              if (Optimizations.FastFP && Optimizations.UseSse2)
457              {
458                  EmitSse2OrAvxCmpOpF(context, CmpCondition.LessThan, scalar: false);
459              }
460              else
461              {
462                  EmitCmpOpF(context, nameof(SoftFloat32.FPCompareLT), scalar: false);
463              }
464          }
465  
466          public static void Fcmp_S(ArmEmitterContext context)
467          {
468              if (Optimizations.UseAdvSimd)
469              {
470                  InstEmitSimdHelperArm64.EmitFcmpOrFcmpe(context, signalNaNs: false);
471              }
472              else
473              {
474                  EmitFcmpOrFcmpe(context, signalNaNs: false);
475              }
476          }
477  
478          public static void Fcmpe_S(ArmEmitterContext context)
479          {
480              if (Optimizations.UseAdvSimd)
481              {
482                  InstEmitSimdHelperArm64.EmitFcmpOrFcmpe(context, signalNaNs: true);
483              }
484              else
485              {
486                  EmitFcmpOrFcmpe(context, signalNaNs: true);
487              }
488          }
489  
490          private static void EmitFccmpOrFccmpe(ArmEmitterContext context, bool signalNaNs)
491          {
492              OpCodeSimdFcond op = (OpCodeSimdFcond)context.CurrOp;
493  
494              Operand lblTrue = Label();
495              Operand lblEnd = Label();
496  
497              context.BranchIfTrue(lblTrue, InstEmitFlowHelper.GetCondTrue(context, op.Cond));
498  
499              EmitSetNzcv(context, op.Nzcv);
500  
501              context.Branch(lblEnd);
502  
503              context.MarkLabel(lblTrue);
504  
505              EmitFcmpOrFcmpe(context, signalNaNs);
506  
507              context.MarkLabel(lblEnd);
508          }
509  
510          private static void EmitSetNzcv(ArmEmitterContext context, int nzcv)
511          {
512              static Operand Extract(int value, int bit)
513              {
514                  if (bit != 0)
515                  {
516                      value >>= bit;
517                  }
518  
519                  value &= 1;
520  
521                  return Const(value);
522              }
523  
524              SetFlag(context, PState.VFlag, Extract(nzcv, 0));
525              SetFlag(context, PState.CFlag, Extract(nzcv, 1));
526              SetFlag(context, PState.ZFlag, Extract(nzcv, 2));
527              SetFlag(context, PState.NFlag, Extract(nzcv, 3));
528          }
529  
530          private static void EmitFcmpOrFcmpe(ArmEmitterContext context, bool signalNaNs)
531          {
532              OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
533  
534              bool cmpWithZero = op is not OpCodeSimdFcond && op.Bit3;
535  
536              if (Optimizations.FastFP && (signalNaNs ? Optimizations.UseAvx : Optimizations.UseSse2))
537              {
538                  Operand n = GetVec(op.Rn);
539                  Operand m = cmpWithZero ? context.VectorZero() : GetVec(op.Rm);
540  
541                  CmpCondition cmpOrdered = signalNaNs ? CmpCondition.OrderedS : CmpCondition.OrderedQ;
542  
543                  Operand lblNaN = Label();
544                  Operand lblEnd = Label();
545  
546                  if (op.Size == 0)
547                  {
548                      Operand ordMask = context.AddIntrinsic(Intrinsic.X86Cmpss, n, m, Const((int)cmpOrdered));
549  
550                      Operand isOrdered = context.AddIntrinsicInt(Intrinsic.X86Cvtsi2si, ordMask);
551  
552                      context.BranchIfFalse(lblNaN, isOrdered);
553  
554                      Operand nCopy = context.Copy(n);
555                      Operand mCopy = cmpWithZero ? context.VectorZero() : context.Copy(m);
556  
557                      Operand cf = context.AddIntrinsicInt(Intrinsic.X86Comissge, nCopy, mCopy);
558                      Operand zf = context.AddIntrinsicInt(Intrinsic.X86Comisseq, nCopy, mCopy);
559                      Operand nf = context.AddIntrinsicInt(Intrinsic.X86Comisslt, nCopy, mCopy);
560  
561                      SetFlag(context, PState.VFlag, Const(0));
562                      SetFlag(context, PState.CFlag, cf);
563                      SetFlag(context, PState.ZFlag, zf);
564                      SetFlag(context, PState.NFlag, nf);
565                  }
566                  else /* if (op.Size == 1) */
567                  {
568                      Operand ordMask = context.AddIntrinsic(Intrinsic.X86Cmpsd, n, m, Const((int)cmpOrdered));
569  
570                      Operand isOrdered = context.AddIntrinsicLong(Intrinsic.X86Cvtsi2si, ordMask);
571  
572                      context.BranchIfFalse(lblNaN, isOrdered);
573  
574                      Operand nCopy = context.Copy(n);
575                      Operand mCopy = cmpWithZero ? context.VectorZero() : context.Copy(m);
576  
577                      Operand cf = context.AddIntrinsicInt(Intrinsic.X86Comisdge, nCopy, mCopy);
578                      Operand zf = context.AddIntrinsicInt(Intrinsic.X86Comisdeq, nCopy, mCopy);
579                      Operand nf = context.AddIntrinsicInt(Intrinsic.X86Comisdlt, nCopy, mCopy);
580  
581                      SetFlag(context, PState.VFlag, Const(0));
582                      SetFlag(context, PState.CFlag, cf);
583                      SetFlag(context, PState.ZFlag, zf);
584                      SetFlag(context, PState.NFlag, nf);
585                  }
586  
587                  context.Branch(lblEnd);
588  
589                  context.MarkLabel(lblNaN);
590  
591                  SetFlag(context, PState.VFlag, Const(1));
592                  SetFlag(context, PState.CFlag, Const(1));
593                  SetFlag(context, PState.ZFlag, Const(0));
594                  SetFlag(context, PState.NFlag, Const(0));
595  
596                  context.MarkLabel(lblEnd);
597              }
598              else
599              {
600                  OperandType type = op.Size != 0 ? OperandType.FP64 : OperandType.FP32;
601  
602                  Operand ne = context.VectorExtract(type, GetVec(op.Rn), 0);
603                  Operand me;
604  
605                  if (cmpWithZero)
606                  {
607                      me = op.Size == 0 ? ConstF(0f) : ConstF(0d);
608                  }
609                  else
610                  {
611                      me = context.VectorExtract(type, GetVec(op.Rm), 0);
612                  }
613  
614                  Operand nzcv = EmitSoftFloatCall(context, nameof(SoftFloat32.FPCompare), ne, me, Const(signalNaNs));
615  
616                  EmitSetNzcv(context, nzcv);
617              }
618          }
619  
620          private static void EmitSetNzcv(ArmEmitterContext context, Operand nzcv)
621          {
622              Operand Extract(Operand value, int bit)
623              {
624                  if (bit != 0)
625                  {
626                      value = context.ShiftRightUI(value, Const(bit));
627                  }
628  
629                  value = context.BitwiseAnd(value, Const(1));
630  
631                  return value;
632              }
633  
634              SetFlag(context, PState.VFlag, Extract(nzcv, 0));
635              SetFlag(context, PState.CFlag, Extract(nzcv, 1));
636              SetFlag(context, PState.ZFlag, Extract(nzcv, 2));
637              SetFlag(context, PState.NFlag, Extract(nzcv, 3));
638          }
639  
640          private static void EmitCmpOp(ArmEmitterContext context, Func2I emitCmp, bool scalar)
641          {
642              OpCodeSimd op = (OpCodeSimd)context.CurrOp;
643  
644              Operand res = context.VectorZero();
645  
646              int elems = !scalar ? op.GetBytesCount() >> op.Size : 1;
647  
648              ulong szMask = ulong.MaxValue >> (64 - (8 << op.Size));
649  
650              for (int index = 0; index < elems; index++)
651              {
652                  Operand ne = EmitVectorExtractSx(context, op.Rn, index, op.Size);
653                  Operand me;
654  
655                  if (op is OpCodeSimdReg binOp)
656                  {
657                      me = EmitVectorExtractSx(context, binOp.Rm, index, op.Size);
658                  }
659                  else
660                  {
661                      me = Const(0L);
662                  }
663  
664                  Operand isTrue = emitCmp(ne, me);
665  
666                  Operand mask = context.ConditionalSelect(isTrue, Const(szMask), Const(0L));
667  
668                  res = EmitVectorInsert(context, res, mask, index, op.Size);
669              }
670  
671              context.Copy(GetVec(op.Rd), res);
672          }
673  
674          private static void EmitCmtstOp(ArmEmitterContext context, bool scalar)
675          {
676              OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
677  
678              Operand res = context.VectorZero();
679  
680              int elems = !scalar ? op.GetBytesCount() >> op.Size : 1;
681  
682              ulong szMask = ulong.MaxValue >> (64 - (8 << op.Size));
683  
684              for (int index = 0; index < elems; index++)
685              {
686                  Operand ne = EmitVectorExtractZx(context, op.Rn, index, op.Size);
687                  Operand me = EmitVectorExtractZx(context, op.Rm, index, op.Size);
688  
689                  Operand test = context.BitwiseAnd(ne, me);
690  
691                  Operand isTrue = context.ICompareNotEqual(test, Const(0L));
692  
693                  Operand mask = context.ConditionalSelect(isTrue, Const(szMask), Const(0L));
694  
695                  res = EmitVectorInsert(context, res, mask, index, op.Size);
696              }
697  
698              context.Copy(GetVec(op.Rd), res);
699          }
700  
701          private static void EmitCmpOpF(ArmEmitterContext context, string name, bool scalar, bool absolute = false)
702          {
703              OpCodeSimd op = (OpCodeSimd)context.CurrOp;
704  
705              Operand res = context.VectorZero();
706  
707              int sizeF = op.Size & 1;
708  
709              OperandType type = sizeF != 0 ? OperandType.FP64 : OperandType.FP32;
710  
711              int elems = !scalar ? op.GetBytesCount() >> sizeF + 2 : 1;
712  
713              for (int index = 0; index < elems; index++)
714              {
715                  Operand ne = context.VectorExtract(type, GetVec(op.Rn), index);
716                  Operand me;
717  
718                  if (op is OpCodeSimdReg binOp)
719                  {
720                      me = context.VectorExtract(type, GetVec(binOp.Rm), index);
721                  }
722                  else
723                  {
724                      me = sizeF == 0 ? ConstF(0f) : ConstF(0d);
725                  }
726  
727                  if (absolute)
728                  {
729                      ne = EmitUnaryMathCall(context, nameof(Math.Abs), ne);
730                      me = EmitUnaryMathCall(context, nameof(Math.Abs), me);
731                  }
732  
733                  Operand e = EmitSoftFloatCall(context, name, ne, me);
734  
735                  res = context.VectorInsert(res, e, index);
736              }
737  
738              context.Copy(GetVec(op.Rd), res);
739          }
740  
741          private static void EmitSse2OrAvxCmpOpF(ArmEmitterContext context, CmpCondition cond, bool scalar, bool absolute = false)
742          {
743              OpCodeSimd op = (OpCodeSimd)context.CurrOp;
744  
745              Operand n = GetVec(op.Rn);
746              Operand m = op is OpCodeSimdReg binOp ? GetVec(binOp.Rm) : context.VectorZero();
747  
748              int sizeF = op.Size & 1;
749  
750              if (sizeF == 0)
751              {
752                  if (absolute)
753                  {
754                      Operand mask = scalar ? X86GetScalar(context, int.MaxValue) : X86GetAllElements(context, int.MaxValue);
755  
756                      n = context.AddIntrinsic(Intrinsic.X86Andps, n, mask);
757                      m = context.AddIntrinsic(Intrinsic.X86Andps, m, mask);
758                  }
759  
760                  Intrinsic inst = scalar ? Intrinsic.X86Cmpss : Intrinsic.X86Cmpps;
761  
762                  Operand res = context.AddIntrinsic(inst, n, m, Const((int)cond));
763  
764                  if (scalar)
765                  {
766                      res = context.VectorZeroUpper96(res);
767                  }
768                  else if (op.RegisterSize == RegisterSize.Simd64)
769                  {
770                      res = context.VectorZeroUpper64(res);
771                  }
772  
773                  context.Copy(GetVec(op.Rd), res);
774              }
775              else /* if (sizeF == 1) */
776              {
777                  if (absolute)
778                  {
779                      Operand mask = scalar ? X86GetScalar(context, long.MaxValue) : X86GetAllElements(context, long.MaxValue);
780  
781                      n = context.AddIntrinsic(Intrinsic.X86Andpd, n, mask);
782                      m = context.AddIntrinsic(Intrinsic.X86Andpd, m, mask);
783                  }
784  
785                  Intrinsic inst = scalar ? Intrinsic.X86Cmpsd : Intrinsic.X86Cmppd;
786  
787                  Operand res = context.AddIntrinsic(inst, n, m, Const((int)cond));
788  
789                  if (scalar)
790                  {
791                      res = context.VectorZeroUpper64(res);
792                  }
793  
794                  context.Copy(GetVec(op.Rd), res);
795              }
796          }
797      }
798  }