Cpu: Implement VCVT (between floating-point and fixed-point) instruction (#5343)

* cpu: Implement VCVT (between floating-point and fixed-point) instruction

Rebase, fix and superseed of https://github.com/Ryujinx/Ryujinx/pull/2915

(Since I only have little CPU knowledge, I hope I have done everything good)

* Update Ptc.cs

* Fix wrong cast

* Rename tests

* Addresses feedback

Co-Authored-By: gdkchan <5624669+gdkchan@users.noreply.github.com>

* Remove extra empty line

---------

Co-authored-by: gdkchan <5624669+gdkchan@users.noreply.github.com>
This commit is contained in:
Ac_K 2023-06-28 17:36:30 +02:00 committed by GitHub
parent 2cdc82cb91
commit 9288ffd26d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 309 additions and 176 deletions

View file

@ -0,0 +1,23 @@
namespace ARMeilleure.Decoders
{
class OpCode32SimdCvtFFixed : OpCode32Simd
{
public int Fbits { get; protected set; }
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdCvtFFixed(inst, address, opCode, false);
public new static OpCode CreateT32(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdCvtFFixed(inst, address, opCode, true);
public OpCode32SimdCvtFFixed(InstDescriptor inst, ulong address, int opCode, bool isThumb) : base(inst, address, opCode, isThumb)
{
Opc = (opCode >> 8) & 0x1;
Size = Opc == 1 ? 0 : 2;
Fbits = 64 - ((opCode >> 16) & 0x3f);
if (DecoderHelper.VectorArgumentsInvalid(Q, Vd, Vm))
{
Instruction = InstDescriptor.Undefined;
}
}
}
}

View file

@ -918,6 +918,7 @@ namespace ARMeilleure.Decoders
SetAsimd("111100111x11xx01xxxx0x100xx0xxxx", InstName.Vclt, InstEmit32.Vclt_Z, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32);
SetAsimd("111100111x110000xxxx01010xx0xxxx", InstName.Vcnt, InstEmit32.Vcnt, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32);
SetAsimd("111100111x111011xxxx011xxxx0xxxx", InstName.Vcvt, InstEmit32.Vcvt_V, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32); // FP and integer, vector.
SetAsimd("1111001x1x1xxxxxxxxx111x0xx1xxxx", InstName.Vcvt, InstEmit32.Vcvt_V_Fixed, OpCode32SimdCvtFFixed.Create, OpCode32SimdCvtFFixed.CreateT32); // Between floating point and fixed point, vector.
SetAsimd("111100111x11xxxxxxxx11000xx0xxxx", InstName.Vdup, InstEmit32.Vdup_1, OpCode32SimdDupElem.Create, OpCode32SimdDupElem.CreateT32);
SetAsimd("111100110x00xxxxxxxx0001xxx1xxxx", InstName.Veor, InstEmit32.Veor_I, OpCode32SimdBinary.Create, OpCode32SimdBinary.CreateT32);
SetAsimd("111100101x11xxxxxxxxxxxxxxx0xxxx", InstName.Vext, InstEmit32.Vext, OpCode32SimdExt.Create, OpCode32SimdExt.CreateT32);

View file

@ -114,6 +114,35 @@ namespace ARMeilleure.Instructions
}
}
public static void Vcvt_V_Fixed(ArmEmitterContext context)
{
OpCode32SimdCvtFFixed op = (OpCode32SimdCvtFFixed)context.CurrOp;
var toFixed = op.Opc == 1;
int fracBits = op.Fbits;
var unsigned = op.U;
if (toFixed) // F32 to S32 or U32 (fixed)
{
EmitVectorUnaryOpF32(context, (op1) =>
{
var scaledValue = context.Multiply(op1, ConstF(MathF.Pow(2f, fracBits)));
MethodInfo info = unsigned ? typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToU32)) : typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToS32));
return context.Call(info, scaledValue);
});
}
else // S32 or U32 (fixed) to F32
{
EmitVectorUnaryOpI32(context, (op1) =>
{
var floatValue = unsigned ? context.ConvertToFPUI(OperandType.FP32, op1) : context.ConvertToFP(OperandType.FP32, op1);
return context.Multiply(floatValue, ConstF(1f / MathF.Pow(2f, fracBits)));
}, !unsigned);
}
}
public static void Vcvt_FD(ArmEmitterContext context)
{
OpCode32SimdS op = (OpCode32SimdS)context.CurrOp;

View file

@ -29,7 +29,7 @@ namespace ARMeilleure.Translation.PTC
private const string OuterHeaderMagicString = "PTCohd\0\0";
private const string InnerHeaderMagicString = "PTCihd\0\0";
private const uint InternalVersion = 5292; //! To be incremented manually for each change to the ARMeilleure project.
private const uint InternalVersion = 5343; //! To be incremented manually for each change to the ARMeilleure project.
private const string ActualDir = "0";
private const string BackupDir = "1";

View file

@ -426,6 +426,86 @@ namespace Ryujinx.Tests.Cpu
CompareAgainstUnicorn();
}
[Test, Pairwise, Description("VCVT.I32.F32 <Vd>, <Vm>, #<fbits>")]
public void Vcvt_V_Fixed_F32_I32([Values(0u, 1u, 2u, 3u)] uint vd,
[Values(0u, 1u, 2u, 3u)] uint vm,
[ValueSource(nameof(_1S_F_))][Random(RndCnt)] ulong s0,
[ValueSource(nameof(_1S_F_))][Random(RndCnt)] ulong s1,
[ValueSource(nameof(_1S_F_))][Random(RndCnt)] ulong s2,
[ValueSource(nameof(_1S_F_))][Random(RndCnt)] ulong s3,
[Random(32u, 63u, 1)] uint fixImm,
[Values] bool unsigned,
[Values] bool q)
{
uint opcode = 0xF2800F10u; // VCVT.U32.F32 D0, D0, #0
if (q)
{
opcode |= 1 << 6;
vm <<= 1;
vd <<= 1;
}
if (unsigned)
{
opcode |= 1 << 24;
}
opcode |= ((vm & 0x10) << 1);
opcode |= ((vm & 0xf) << 0);
opcode |= ((vd & 0x10) << 18);
opcode |= ((vd & 0xf) << 12);
opcode |= (fixImm & 0x3f) << 16;
var v0 = new V128((uint)s0, (uint)s1, (uint)s2, (uint)s3);
SingleOpcode(opcode, v0: v0);
CompareAgainstUnicorn();
}
[Test, Pairwise, Description("VCVT.F32.I32 <Vd>, <Vm>, #<fbits>")]
public void Vcvt_V_Fixed_I32_F32([Values(0u, 1u, 2u, 3u)] uint vd,
[Values(0u, 1u, 2u, 3u)] uint vm,
[ValueSource(nameof(_1S_))][Random(RndCnt)] uint s0,
[ValueSource(nameof(_1S_))][Random(RndCnt)] uint s1,
[ValueSource(nameof(_1S_))][Random(RndCnt)] uint s2,
[ValueSource(nameof(_1S_))][Random(RndCnt)] uint s3,
[Range(32u, 63u, 1)] uint fixImm,
[Values] bool unsigned,
[Values] bool q)
{
uint opcode = 0xF2800E10u; // VCVT.F32.U32 D0, D0, #0
if (q)
{
opcode |= 1 << 6;
vm <<= 1;
vd <<= 1;
}
if (unsigned)
{
opcode |= 1 << 24;
}
opcode |= ((vm & 0x10) << 1);
opcode |= ((vm & 0xf) << 0);
opcode |= ((vd & 0x10) << 18);
opcode |= ((vd & 0xf) << 12);
opcode |= (fixImm & 0x3f) << 16;
var v0 = new V128(s0, s1, s2, s3);
SingleOpcode(opcode, v0: v0);
CompareAgainstUnicorn();
}
#endif
}
}