| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274 |
- using ARMeilleure.Decoders;
- using ARMeilleure.IntermediateRepresentation;
- using ARMeilleure.Translation;
- using System;
- using System.Diagnostics;
- using static ARMeilleure.Instructions.InstEmitSimdHelper;
- using static ARMeilleure.Instructions.InstEmitSimdHelper32;
- using static ARMeilleure.IntermediateRepresentation.OperandHelper;
- namespace ARMeilleure.Instructions
- {
- static partial class InstEmit32
- {
- private static int FlipVdBits(int vd, bool lowBit)
- {
- if (lowBit)
- {
- // Move the low bit to the top.
- return ((vd & 0x1) << 4) | (vd >> 1);
- }
- else
- {
- // Move the high bit to the bottom.
- return ((vd & 0xf) << 1) | (vd >> 4);
- }
- }
- private static Operand EmitSaturateFloatToInt(ArmEmitterContext context, Operand op1, bool unsigned)
- {
- if (op1.Type == OperandType.FP64)
- {
- if (unsigned)
- {
- return context.Call(new _U32_F64(SoftFallback.SatF64ToU32), op1);
- }
- else
- {
- return context.Call(new _S32_F64(SoftFallback.SatF64ToS32), op1);
- }
- }
- else
- {
- if (unsigned)
- {
- return context.Call(new _U32_F32(SoftFallback.SatF32ToU32), op1);
- }
- else
- {
- return context.Call(new _S32_F32(SoftFallback.SatF32ToS32), op1);
- }
- }
- }
- public static void Vcvt_V(ArmEmitterContext context)
- {
- OpCode32Simd op = (OpCode32Simd)context.CurrOp;
- bool unsigned = (op.Opc & 1) != 0;
- bool toInteger = (op.Opc & 2) != 0;
- OperandType floatSize = (op.Size == 2) ? OperandType.FP32 : OperandType.FP64;
- if (toInteger)
- {
- EmitVectorUnaryOpF32(context, (op1) =>
- {
- return EmitSaturateFloatToInt(context, op1, unsigned);
- });
- }
- else
- {
- if (unsigned)
- {
- EmitVectorUnaryOpZx32(context, (op1) => EmitFPConvert(context, op1, floatSize, false));
- }
- else
- {
- EmitVectorUnaryOpSx32(context, (op1) => EmitFPConvert(context, op1, floatSize, true));
- }
- }
-
- }
- public static void Vcvt_FD(ArmEmitterContext context)
- {
- OpCode32SimdS op = (OpCode32SimdS)context.CurrOp;
- int vm = op.Vm;
- int vd;
- if (op.Size == 3)
- {
- vd = FlipVdBits(op.Vd, false);
- // Double to single.
- Operand fp = ExtractScalar(context, OperandType.FP64, vm);
- Operand res = context.ConvertToFP(OperandType.FP32, fp);
- InsertScalar(context, vd, res);
- }
- else
- {
- vd = FlipVdBits(op.Vd, true);
- // Single to double.
- Operand fp = ExtractScalar(context, OperandType.FP32, vm);
- Operand res = context.ConvertToFP(OperandType.FP64, fp);
- InsertScalar(context, vd, res);
- }
- }
- 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);
- }
- }
- public static Operand EmitRoundMathCall(ArmEmitterContext context, MidpointRounding roundMode, Operand n)
- {
- IOpCode32Simd op = (IOpCode32Simd)context.CurrOp;
- Delegate dlg;
- if ((op.Size & 1) == 0)
- {
- dlg = new _F32_F32_MidpointRounding(MathF.Round);
- }
- else /* if ((op.Size & 1) == 1) */
- {
- dlg = new _F64_F64_MidpointRounding(Math.Round);
- }
- return context.Call(dlg, n, Const((int)roundMode));
- }
- public static void Vcvt_R(ArmEmitterContext context)
- {
- OpCode32SimdCvtFI op = (OpCode32SimdCvtFI)context.CurrOp;
- OperandType floatSize = op.RegisterSize == RegisterSize.Int64 ? OperandType.FP64 : OperandType.FP32;
- bool unsigned = (op.Opc & 1) == 0;
- 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;
- }
- Operand asInteger;
- asInteger = EmitSaturateFloatToInt(context, toConvert, unsigned);
- InsertScalar(context, op.Vd, asInteger);
- }
- 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_Z(ArmEmitterContext context)
- {
- EmitScalarUnaryOpF32(context, (op1) => EmitUnaryMathCall(context, MathF.Truncate, Math.Truncate, op1));
- }
- private static Operand EmitFPConvert(ArmEmitterContext context, Operand value, OperandType type, bool signed)
- {
- Debug.Assert(value.Type == OperandType.I32 || value.Type == OperandType.I64);
- if (signed)
- {
- return context.ConvertToFP(type, value);
- }
- else
- {
- return context.ConvertToFPUI(type, value);
- }
- }
- }
- }
|