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 }