mirror of
https://github.com/ryujinx-mirror/ryujinx.git
synced 2024-10-02 16:50:20 -07:00

* Implement a new JIT for Arm devices * Auto-format * Make a lot of Assembler members read-only * More read-only * Fix more warnings * ObjectDisposedException.ThrowIf * New JIT cache for platforms that enforce W^X, currently unused * Remove unused using * Fix assert * Pass memory manager type around * Safe memory manager mode support + other improvements * Actual safe memory manager mode masking support * PR feedback
306 lines
12 KiB
C#
306 lines
12 KiB
C#
using System;
|
|
using System.Diagnostics;
|
|
|
|
namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
|
{
|
|
static class InstEmitVfpConvert
|
|
{
|
|
public static void Vcvta(CodeGenContext context, uint rd, uint rm, bool op, uint size)
|
|
{
|
|
if (size == 3)
|
|
{
|
|
// F64 -> S32/U32 conversion on SIMD is not supported, so we convert it to a GPR, then insert it back into the SIMD register.
|
|
|
|
if (op)
|
|
{
|
|
InstEmitNeonCommon.EmitScalarUnaryToGprTempF(context, rd, rm, size, 0, context.Arm64Assembler.FcvtasFloat);
|
|
}
|
|
else
|
|
{
|
|
InstEmitNeonCommon.EmitScalarUnaryToGprTempF(context, rd, rm, size, 0, context.Arm64Assembler.FcvtauFloat);
|
|
}
|
|
}
|
|
else if (op)
|
|
{
|
|
InstEmitNeonCommon.EmitScalarUnaryF(context, rd, rm, size, context.Arm64Assembler.FcvtasS, context.Arm64Assembler.FcvtasSH);
|
|
}
|
|
else
|
|
{
|
|
InstEmitNeonCommon.EmitScalarUnaryF(context, rd, rm, size, context.Arm64Assembler.FcvtauS, context.Arm64Assembler.FcvtauSH);
|
|
}
|
|
}
|
|
|
|
public static void Vcvtb(CodeGenContext context, uint rd, uint rm, uint sz, uint op)
|
|
{
|
|
EmitVcvtbVcvtt(context, rd, rm, sz, op, top: false);
|
|
}
|
|
|
|
public static void Vcvtm(CodeGenContext context, uint rd, uint rm, bool op, uint size)
|
|
{
|
|
if (size == 3)
|
|
{
|
|
// F64 -> S32/U32 conversion on SIMD is not supported, so we convert it to a GPR, then insert it back into the SIMD register.
|
|
|
|
if (op)
|
|
{
|
|
InstEmitNeonCommon.EmitScalarUnaryToGprTempF(context, rd, rm, size, 0, context.Arm64Assembler.FcvtmsFloat);
|
|
}
|
|
else
|
|
{
|
|
InstEmitNeonCommon.EmitScalarUnaryToGprTempF(context, rd, rm, size, 0, context.Arm64Assembler.FcvtmuFloat);
|
|
}
|
|
}
|
|
else if (op)
|
|
{
|
|
InstEmitNeonCommon.EmitScalarUnaryF(context, rd, rm, size, context.Arm64Assembler.FcvtmsS, context.Arm64Assembler.FcvtmsSH);
|
|
}
|
|
else
|
|
{
|
|
InstEmitNeonCommon.EmitScalarUnaryF(context, rd, rm, size, context.Arm64Assembler.FcvtmuS, context.Arm64Assembler.FcvtmuSH);
|
|
}
|
|
}
|
|
|
|
public static void Vcvtn(CodeGenContext context, uint rd, uint rm, bool op, uint size)
|
|
{
|
|
if (size == 3)
|
|
{
|
|
// F64 -> S32/U32 conversion on SIMD is not supported, so we convert it to a GPR, then insert it back into the SIMD register.
|
|
|
|
if (op)
|
|
{
|
|
InstEmitNeonCommon.EmitScalarUnaryToGprTempF(context, rd, rm, size, 0, context.Arm64Assembler.FcvtnsFloat);
|
|
}
|
|
else
|
|
{
|
|
InstEmitNeonCommon.EmitScalarUnaryToGprTempF(context, rd, rm, size, 0, context.Arm64Assembler.FcvtnuFloat);
|
|
}
|
|
}
|
|
else if (op)
|
|
{
|
|
InstEmitNeonCommon.EmitScalarUnaryF(context, rd, rm, size, context.Arm64Assembler.FcvtnsS, context.Arm64Assembler.FcvtnsSH);
|
|
}
|
|
else
|
|
{
|
|
InstEmitNeonCommon.EmitScalarUnaryF(context, rd, rm, size, context.Arm64Assembler.FcvtnuS, context.Arm64Assembler.FcvtnuSH);
|
|
}
|
|
}
|
|
|
|
public static void Vcvtp(CodeGenContext context, uint rd, uint rm, bool op, uint size)
|
|
{
|
|
if (size == 3)
|
|
{
|
|
// F64 -> S32/U32 conversion on SIMD is not supported, so we convert it to a GPR, then insert it back into the SIMD register.
|
|
|
|
if (op)
|
|
{
|
|
InstEmitNeonCommon.EmitScalarUnaryToGprTempF(context, rd, rm, size, 0, context.Arm64Assembler.FcvtpsFloat);
|
|
}
|
|
else
|
|
{
|
|
InstEmitNeonCommon.EmitScalarUnaryToGprTempF(context, rd, rm, size, 0, context.Arm64Assembler.FcvtpuFloat);
|
|
}
|
|
}
|
|
else if (op)
|
|
{
|
|
InstEmitNeonCommon.EmitScalarUnaryF(context, rd, rm, size, context.Arm64Assembler.FcvtpsS, context.Arm64Assembler.FcvtpsSH);
|
|
}
|
|
else
|
|
{
|
|
InstEmitNeonCommon.EmitScalarUnaryF(context, rd, rm, size, context.Arm64Assembler.FcvtpuS, context.Arm64Assembler.FcvtpuSH);
|
|
}
|
|
}
|
|
|
|
public static void VcvtDs(CodeGenContext context, uint rd, uint rm, uint size)
|
|
{
|
|
bool doubleToSingle = size == 3;
|
|
|
|
using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempSimdRegisterScoped();
|
|
|
|
if (doubleToSingle)
|
|
{
|
|
// Double to single.
|
|
|
|
using ScopedRegister rmReg = InstEmitNeonCommon.MoveScalarToSide(context, rm, false);
|
|
|
|
context.Arm64Assembler.FcvtFloat(tempRegister.Operand, rmReg.Operand, 0, 1);
|
|
|
|
InstEmitNeonCommon.InsertResult(context, tempRegister.Operand, rd, true);
|
|
}
|
|
else
|
|
{
|
|
// Single to double.
|
|
|
|
using ScopedRegister rmReg = InstEmitNeonCommon.MoveScalarToSide(context, rm, true);
|
|
|
|
context.Arm64Assembler.FcvtFloat(tempRegister.Operand, rmReg.Operand, 1, 0);
|
|
|
|
InstEmitNeonCommon.InsertResult(context, tempRegister.Operand, rd, false);
|
|
}
|
|
}
|
|
|
|
public static void VcvtIv(CodeGenContext context, uint rd, uint rm, bool unsigned, uint size)
|
|
{
|
|
if (size == 3)
|
|
{
|
|
// F64 -> S32/U32 conversion on SIMD is not supported, so we convert it to a GPR, then insert it back into the SIMD register.
|
|
|
|
if (unsigned)
|
|
{
|
|
InstEmitNeonCommon.EmitScalarUnaryToGprTempF(context, rd, rm, size, 0, context.Arm64Assembler.FcvtzuFloatInt);
|
|
}
|
|
else
|
|
{
|
|
InstEmitNeonCommon.EmitScalarUnaryToGprTempF(context, rd, rm, size, 0, context.Arm64Assembler.FcvtzsFloatInt);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (unsigned)
|
|
{
|
|
InstEmitNeonCommon.EmitScalarUnaryF(context, rd, rm, size, context.Arm64Assembler.FcvtzuIntS, context.Arm64Assembler.FcvtzuIntSH);
|
|
}
|
|
else
|
|
{
|
|
InstEmitNeonCommon.EmitScalarUnaryF(context, rd, rm, size, context.Arm64Assembler.FcvtzsIntS, context.Arm64Assembler.FcvtzsIntSH);
|
|
}
|
|
}
|
|
}
|
|
|
|
public static void VcvtVi(CodeGenContext context, uint rd, uint rm, bool unsigned, uint size)
|
|
{
|
|
if (size == 3)
|
|
{
|
|
// S32/U32 -> F64 conversion on SIMD is not supported, so we convert it to a GPR, then insert it back into the SIMD register.
|
|
|
|
if (unsigned)
|
|
{
|
|
InstEmitNeonCommon.EmitScalarUnaryFromGprTempF(context, rd, rm, size, 0, context.Arm64Assembler.UcvtfFloatInt);
|
|
}
|
|
else
|
|
{
|
|
InstEmitNeonCommon.EmitScalarUnaryFromGprTempF(context, rd, rm, size, 0, context.Arm64Assembler.ScvtfFloatInt);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (unsigned)
|
|
{
|
|
InstEmitNeonCommon.EmitScalarUnaryF(context, rd, rm, size, context.Arm64Assembler.UcvtfIntS, context.Arm64Assembler.UcvtfIntSH);
|
|
}
|
|
else
|
|
{
|
|
InstEmitNeonCommon.EmitScalarUnaryF(context, rd, rm, size, context.Arm64Assembler.ScvtfIntS, context.Arm64Assembler.ScvtfIntSH);
|
|
}
|
|
}
|
|
}
|
|
|
|
public static void VcvtXv(CodeGenContext context, uint rd, uint imm5, bool sx, uint sf, uint op, bool u)
|
|
{
|
|
Debug.Assert(op >> 1 == 0);
|
|
|
|
bool unsigned = u;
|
|
bool toFixed = op == 1;
|
|
uint size = sf;
|
|
uint fbits = Math.Clamp((sx ? 32u : 16u) - imm5, 1, 8u << (int)size);
|
|
|
|
if (toFixed)
|
|
{
|
|
if (unsigned)
|
|
{
|
|
InstEmitNeonCommon.EmitScalarUnaryFixedF(context, rd, rd, fbits, size, is16Bit: false, context.Arm64Assembler.FcvtzuFixS);
|
|
}
|
|
else
|
|
{
|
|
InstEmitNeonCommon.EmitScalarUnaryFixedF(context, rd, rd, fbits, size, is16Bit: false, context.Arm64Assembler.FcvtzsFixS);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (unsigned)
|
|
{
|
|
InstEmitNeonCommon.EmitScalarUnaryFixedF(context, rd, rd, fbits, size, is16Bit: !sx, context.Arm64Assembler.UcvtfFixS);
|
|
}
|
|
else
|
|
{
|
|
InstEmitNeonCommon.EmitScalarUnaryFixedF(context, rd, rd, fbits, size, is16Bit: !sx, context.Arm64Assembler.ScvtfFixS);
|
|
}
|
|
}
|
|
}
|
|
|
|
public static void VcvtrIv(CodeGenContext context, uint rd, uint rm, uint op, uint size)
|
|
{
|
|
bool unsigned = (op & 1) == 0;
|
|
|
|
Debug.Assert(size == 1 || size == 2 || size == 3);
|
|
|
|
bool singleRegs = size != 3;
|
|
|
|
using ScopedRegister rmReg = InstEmitNeonCommon.MoveScalarToSide(context, rm, singleRegs);
|
|
|
|
using ScopedRegister tempRegister = InstEmitNeonCommon.PickSimdRegister(context.RegisterAllocator, rmReg);
|
|
|
|
// Round using the FPCR rounding mode first, since the FCVTZ instructions will use the round to zero mode.
|
|
context.Arm64Assembler.FrintiFloat(tempRegister.Operand, rmReg.Operand, size ^ 2u);
|
|
|
|
if (unsigned)
|
|
{
|
|
if (size == 1)
|
|
{
|
|
context.Arm64Assembler.FcvtzuIntSH(tempRegister.Operand, tempRegister.Operand);
|
|
}
|
|
else
|
|
{
|
|
context.Arm64Assembler.FcvtzuIntS(tempRegister.Operand, tempRegister.Operand, size & 1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (size == 1)
|
|
{
|
|
context.Arm64Assembler.FcvtzsIntSH(tempRegister.Operand, tempRegister.Operand);
|
|
}
|
|
else
|
|
{
|
|
context.Arm64Assembler.FcvtzsIntS(tempRegister.Operand, tempRegister.Operand, size & 1);
|
|
}
|
|
}
|
|
|
|
InstEmitNeonCommon.InsertResult(context, tempRegister.Operand, rd, singleRegs);
|
|
}
|
|
|
|
public static void Vcvtt(CodeGenContext context, uint rd, uint rm, uint sz, uint op)
|
|
{
|
|
EmitVcvtbVcvtt(context, rd, rm, sz, op, top: true);
|
|
}
|
|
|
|
public static void EmitVcvtbVcvtt(CodeGenContext context, uint rd, uint rm, uint sz, uint op, bool top)
|
|
{
|
|
bool usesDouble = sz == 1;
|
|
bool convertFromHalf = op == 0;
|
|
|
|
using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempSimdRegisterScoped();
|
|
|
|
if (convertFromHalf)
|
|
{
|
|
// Half to single/double.
|
|
|
|
using ScopedRegister rmReg = InstEmitNeonCommon.Move16BitScalarToSide(context, rm, top);
|
|
|
|
context.Arm64Assembler.FcvtFloat(tempRegister.Operand, rmReg.Operand, usesDouble ? 1u : 0u, 3u);
|
|
|
|
InstEmitNeonCommon.InsertResult(context, tempRegister.Operand, rd, !usesDouble);
|
|
}
|
|
else
|
|
{
|
|
// Single/double to half.
|
|
|
|
using ScopedRegister rmReg = InstEmitNeonCommon.MoveScalarToSide(context, rm, !usesDouble);
|
|
|
|
context.Arm64Assembler.FcvtFloat(tempRegister.Operand, rmReg.Operand, 3u, usesDouble ? 1u : 0u);
|
|
|
|
InstEmitNeonCommon.Insert16BitResult(context, tempRegister.Operand, rd, top);
|
|
}
|
|
}
|
|
}
|
|
}
|