forked from Mirror/Ryujinx
275 lines
9.2 KiB
C#
275 lines
9.2 KiB
C#
|
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);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|