public static void Vrint_RM(ArmEmitterContext context) { OpCode32SimdCvtFI op = (OpCode32SimdCvtFI)context.CurrOp; OperandType floatSize = op.RegisterSize == RegisterSize.Int64 ? OperandType.FP64 : OperandType.FP32; Operand toConvert = ExtractScalar(context, floatSize, op.Vm); switch (op.Opc2) { case 0b00: // Away toConvert = EmitRoundMathCall(context, MidpointRounding.AwayFromZero, toConvert); break; case 0b01: // Nearest toConvert = EmitRoundMathCall(context, MidpointRounding.ToEven, toConvert); break; case 0b10: // Towards positive infinity toConvert = EmitUnaryMathCall(context, MathF.Ceiling, Math.Ceiling, toConvert); break; case 0b11: // Towards negative infinity toConvert = EmitUnaryMathCall(context, MathF.Floor, Math.Floor, toConvert); break; } InsertScalar(context, op.Vd, toConvert); }
public static void Vrint_RM(ArmEmitterContext context) { OpCode32SimdCvtFI op = (OpCode32SimdCvtFI)context.CurrOp; OperandType floatSize = op.RegisterSize == RegisterSize.Int64 ? OperandType.FP64 : OperandType.FP32; int rm = op.Opc2 & 3; if (Optimizations.UseSse2 && rm != 0b00) { EmitScalarUnaryOpSimd32(context, (m) => { Intrinsic inst = (op.Size & 1) == 0 ? Intrinsic.X86Roundss : Intrinsic.X86Roundsd; FPRoundingMode roundMode = RMToRoundMode(rm); return(context.AddIntrinsic(inst, m, Const(X86GetRoundControl(roundMode)))); }); } else { Operand toConvert = ExtractScalar(context, floatSize, op.Vm); switch (rm) { case 0b00: // Away toConvert = EmitRoundMathCall(context, MidpointRounding.AwayFromZero, toConvert); break; case 0b01: // Nearest toConvert = EmitRoundMathCall(context, MidpointRounding.ToEven, toConvert); break; case 0b10: // Towards positive infinity toConvert = EmitUnaryMathCall(context, MathF.Ceiling, Math.Ceiling, toConvert); break; case 0b11: // Towards negative infinity toConvert = EmitUnaryMathCall(context, MathF.Floor, Math.Floor, toConvert); break; } InsertScalar(context, op.Vd, toConvert); } }
// VCVTA/M/N/P (floating-point). public static void Vcvt_RM(ArmEmitterContext context) { OpCode32SimdCvtFI op = (OpCode32SimdCvtFI)context.CurrOp; // toInteger == true (opCode<18> == 1 => Opc2<2> == 1). OperandType floatSize = op.RegisterSize == RegisterSize.Int64 ? OperandType.FP64 : OperandType.FP32; bool unsigned = op.Opc == 0; int rm = op.Opc2 & 3; if (Optimizations.UseSse41 && rm != 0b00) { EmitSse41ConvertInt32(context, RMToRoundMode(rm), !unsigned); } else { Operand toConvert = ExtractScalar(context, floatSize, op.Vm); switch (rm) { case 0b00: // Away toConvert = EmitRoundMathCall(context, MidpointRounding.AwayFromZero, toConvert); break; case 0b01: // Nearest toConvert = EmitRoundMathCall(context, MidpointRounding.ToEven, toConvert); break; case 0b10: // Towards positive infinity toConvert = EmitUnaryMathCall(context, nameof(Math.Ceiling), toConvert); break; case 0b11: // Towards negative infinity toConvert = EmitUnaryMathCall(context, nameof(Math.Floor), toConvert); break; } Operand asInteger; asInteger = EmitSaturateFloatToInt(context, toConvert, unsigned); InsertScalar(context, op.Vd, asInteger); } }
private static void EmitSse41ConvertInt32(ArmEmitterContext context, FPRoundingMode roundMode, bool signed) { // A port of the similar round function in InstEmitSimdCvt. OpCode32SimdCvtFI op = (OpCode32SimdCvtFI)context.CurrOp; bool doubleSize = (op.Size & 1) != 0; int shift = doubleSize ? 1 : 2; Operand n = GetVecA32(op.Vm >> shift); n = EmitSwapScalar(context, n, op.Vm, doubleSize); if (!doubleSize) { Operand nRes = context.AddIntrinsic(Intrinsic.X86Cmpss, n, n, Const((int)CmpCondition.OrderedQ)); nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, n); nRes = context.AddIntrinsic(Intrinsic.X86Roundss, nRes, Const(X86GetRoundControl(roundMode))); Operand zero = context.VectorZero(); Operand nCmp; Operand nIntOrLong2 = default; if (!signed) { nCmp = context.AddIntrinsic(Intrinsic.X86Cmpss, nRes, zero, Const((int)CmpCondition.NotLessThanOrEqual)); nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, nCmp); } int fpMaxVal = 0x4F000000; // 2.14748365E9f (2147483648) Operand fpMaxValMask = X86GetScalar(context, fpMaxVal); Operand nIntOrLong = context.AddIntrinsicInt(Intrinsic.X86Cvtss2si, nRes); if (!signed) { nRes = context.AddIntrinsic(Intrinsic.X86Subss, nRes, fpMaxValMask); nCmp = context.AddIntrinsic(Intrinsic.X86Cmpss, nRes, zero, Const((int)CmpCondition.NotLessThanOrEqual)); nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, nCmp); nIntOrLong2 = context.AddIntrinsicInt(Intrinsic.X86Cvtss2si, nRes); } nRes = context.AddIntrinsic(Intrinsic.X86Cmpss, nRes, fpMaxValMask, Const((int)CmpCondition.NotLessThan)); Operand nInt = context.AddIntrinsicInt(Intrinsic.X86Cvtsi2si, nRes); Operand dRes; if (signed) { dRes = context.BitwiseExclusiveOr(nIntOrLong, nInt); } else { dRes = context.BitwiseExclusiveOr(nIntOrLong2, nInt); dRes = context.Add(dRes, nIntOrLong); } InsertScalar(context, op.Vd, dRes); } else { Operand nRes = context.AddIntrinsic(Intrinsic.X86Cmpsd, n, n, Const((int)CmpCondition.OrderedQ)); nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, n); nRes = context.AddIntrinsic(Intrinsic.X86Roundsd, nRes, Const(X86GetRoundControl(roundMode))); Operand zero = context.VectorZero(); Operand nCmp; Operand nIntOrLong2 = default; if (!signed) { nCmp = context.AddIntrinsic(Intrinsic.X86Cmpsd, nRes, zero, Const((int)CmpCondition.NotLessThanOrEqual)); nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, nCmp); } long fpMaxVal = 0x41E0000000000000L; // 2147483648.0000000d (2147483648) Operand fpMaxValMask = X86GetScalar(context, fpMaxVal); Operand nIntOrLong = context.AddIntrinsicInt(Intrinsic.X86Cvtsd2si, nRes); if (!signed) { nRes = context.AddIntrinsic(Intrinsic.X86Subsd, nRes, fpMaxValMask); nCmp = context.AddIntrinsic(Intrinsic.X86Cmpsd, nRes, zero, Const((int)CmpCondition.NotLessThanOrEqual)); nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, nCmp); nIntOrLong2 = context.AddIntrinsicInt(Intrinsic.X86Cvtsd2si, nRes); } nRes = context.AddIntrinsic(Intrinsic.X86Cmpsd, nRes, fpMaxValMask, Const((int)CmpCondition.NotLessThan)); Operand nLong = context.AddIntrinsicLong(Intrinsic.X86Cvtsi2si, nRes); nLong = context.ConvertI64ToI32(nLong); Operand dRes; if (signed) { dRes = context.BitwiseExclusiveOr(nIntOrLong, nLong); } else { dRes = context.BitwiseExclusiveOr(nIntOrLong2, nLong); dRes = context.Add(dRes, nIntOrLong); } InsertScalar(context, op.Vd, dRes); } }
// VCVT (floating-point to integer, floating-point) | VCVT (integer to floating-point, floating-point). public static void Vcvt_FI(ArmEmitterContext context) { OpCode32SimdCvtFI op = (OpCode32SimdCvtFI)context.CurrOp; bool toInteger = (op.Opc2 & 0b100) != 0; OperandType floatSize = op.RegisterSize == RegisterSize.Int64 ? OperandType.FP64 : OperandType.FP32; if (toInteger) { bool unsigned = (op.Opc2 & 1) == 0; bool roundWithFpscr = op.Opc != 1; if (!roundWithFpscr && Optimizations.UseSse41) { EmitSse41ConvertInt32(context, FPRoundingMode.TowardsZero, !unsigned); } else { Operand toConvert = ExtractScalar(context, floatSize, op.Vm); Operand asInteger; // TODO: Fast Path. if (roundWithFpscr) { MethodInfo info; if (floatSize == OperandType.FP64) { info = unsigned ? typeof(SoftFallback).GetMethod(nameof(SoftFallback.DoubleToUInt32)) : typeof(SoftFallback).GetMethod(nameof(SoftFallback.DoubleToInt32)); } else { info = unsigned ? typeof(SoftFallback).GetMethod(nameof(SoftFallback.FloatToUInt32)) : typeof(SoftFallback).GetMethod(nameof(SoftFallback.FloatToInt32)); } asInteger = context.Call(info, toConvert); } else { // Round towards zero. asInteger = EmitSaturateFloatToInt(context, toConvert, unsigned); } InsertScalar(context, op.Vd, asInteger); } } else { bool unsigned = op.Opc == 0; Operand toConvert = ExtractScalar(context, OperandType.I32, op.Vm); Operand asFloat = EmitFPConvert(context, toConvert, floatSize, !unsigned); InsertScalar(context, op.Vd, asFloat); } }
public static void Vcvt_FI(ArmEmitterContext context) { OpCode32SimdCvtFI op = (OpCode32SimdCvtFI)context.CurrOp; bool toInteger = (op.Opc2 & 0b100) != 0; OperandType floatSize = op.RegisterSize == RegisterSize.Int64 ? OperandType.FP64 : OperandType.FP32; if (toInteger) { bool unsigned = (op.Opc2 & 1) == 0; bool roundWithFpscr = op.Opc != 1; Operand toConvert = ExtractScalar(context, floatSize, op.Vm); Operand asInteger; // TODO: Fast Path. if (roundWithFpscr) { // These need to get the FPSCR value, so it's worth noting we'd need to do a c# call at some point. if (floatSize == OperandType.FP64) { if (unsigned) { asInteger = context.Call(new _U32_F64(SoftFallback.DoubleToUInt32), toConvert); } else { asInteger = context.Call(new _S32_F64(SoftFallback.DoubleToInt32), toConvert); } } else { if (unsigned) { asInteger = context.Call(new _U32_F32(SoftFallback.FloatToUInt32), toConvert); } else { asInteger = context.Call(new _S32_F32(SoftFallback.FloatToInt32), toConvert); } } } else { // Round towards zero. asInteger = EmitSaturateFloatToInt(context, toConvert, unsigned); } InsertScalar(context, op.Vd, asInteger); } else { bool unsigned = op.Opc == 0; Operand toConvert = ExtractScalar(context, OperandType.I32, op.Vm); Operand asFloat = EmitFPConvert(context, toConvert, floatSize, !unsigned); InsertScalar(context, op.Vd, asFloat); } }