mirror of
https://github.com/Ryujinx/Ryujinx.git
synced 2024-11-20 01:46:34 +00:00
CPU refactoring - move SIMD (scalar and vector) instructions to separate files by category, remove AILConv and use only the methods inside SIMD helper to extract/insert vector elements
This commit is contained in:
parent
b3e47b5712
commit
161193e113
24 changed files with 2551 additions and 2610 deletions
|
@ -151,14 +151,14 @@ namespace ChocolArm64
|
|||
Set("00011110xx1xxxxx001000xxxxx1x000", AInstEmit.Fcmpe_S, typeof(AOpCodeSimdReg));
|
||||
Set("00011110xx1xxxxxxxxx11xxxxxxxxxx", AInstEmit.Fcsel_S, typeof(AOpCodeSimdFcond));
|
||||
Set("00011110xx10001xx10000xxxxxxxxxx", AInstEmit.Fcvt_S, typeof(AOpCodeSimd));
|
||||
Set("x0011110xx110000000000xxxxxxxxxx", AInstEmit.Fcvtms_S, typeof(AOpCodeSimdCvt));
|
||||
Set("x0011110xx101000000000xxxxxxxxxx", AInstEmit.Fcvtps_S, typeof(AOpCodeSimdCvt));
|
||||
Set("x0011110xx111000000000xxxxxxxxxx", AInstEmit.Fcvtzs_S, typeof(AOpCodeSimdCvt));
|
||||
Set("x0011110xx011000xxxxxxxxxxxxxxxx", AInstEmit.Fcvtzs_Fix, typeof(AOpCodeSimdCvt));
|
||||
Set("x0011110xx110000000000xxxxxxxxxx", AInstEmit.Fcvtms_Gp, typeof(AOpCodeSimdCvt));
|
||||
Set("x0011110xx101000000000xxxxxxxxxx", AInstEmit.Fcvtps_Gp, typeof(AOpCodeSimdCvt));
|
||||
Set("x0011110xx111000000000xxxxxxxxxx", AInstEmit.Fcvtzs_Gp, typeof(AOpCodeSimdCvt));
|
||||
Set("x0011110xx011000xxxxxxxxxxxxxxxx", AInstEmit.Fcvtzs_Gp_Fix, typeof(AOpCodeSimdCvt));
|
||||
Set("0x0011101x100001101110xxxxxxxxxx", AInstEmit.Fcvtzs_V, typeof(AOpCodeSimd));
|
||||
Set("0x0011110>>xxxxx111111xxxxxxxxxx", AInstEmit.Fcvtzs_V, typeof(AOpCodeSimdShImm));
|
||||
Set("x0011110xx111001000000xxxxxxxxxx", AInstEmit.Fcvtzu_S, typeof(AOpCodeSimdCvt));
|
||||
Set("x0011110xx011001xxxxxxxxxxxxxxxx", AInstEmit.Fcvtzu_Fix, typeof(AOpCodeSimdCvt));
|
||||
Set("x0011110xx111001000000xxxxxxxxxx", AInstEmit.Fcvtzu_Gp, typeof(AOpCodeSimdCvt));
|
||||
Set("x0011110xx011001xxxxxxxxxxxxxxxx", AInstEmit.Fcvtzu_Gp_Fix, typeof(AOpCodeSimdCvt));
|
||||
Set("0x1011101x100001101110xxxxxxxxxx", AInstEmit.Fcvtzu_V, typeof(AOpCodeSimd));
|
||||
Set("0x1011110>>xxxxx111111xxxxxxxxxx", AInstEmit.Fcvtzu_V, typeof(AOpCodeSimdShImm));
|
||||
Set("00011110xx1xxxxx000110xxxxxxxxxx", AInstEmit.Fdiv_S, typeof(AOpCodeSimdReg));
|
||||
|
|
|
@ -37,6 +37,8 @@ namespace ChocolArm64.Decoder
|
|||
return;
|
||||
}
|
||||
|
||||
Extend64 = false;
|
||||
|
||||
RegisterSize = Q
|
||||
? ARegisterSize.SIMD128
|
||||
: ARegisterSize.SIMD64;
|
||||
|
|
|
@ -87,6 +87,8 @@ namespace ChocolArm64.Decoder
|
|||
this.SElems = SElems;
|
||||
this.Size = Scale;
|
||||
|
||||
Extend64 = false;
|
||||
|
||||
WBack = ((OpCode >> 23) & 0x1) != 0;
|
||||
|
||||
RegisterSize = Q != 0
|
||||
|
|
|
@ -20,7 +20,7 @@ namespace ChocolArm64.Instruction
|
|||
{
|
||||
AOpCodeAdr Op = (AOpCodeAdr)Context.CurrOp;
|
||||
|
||||
Context.EmitLdc_I((Op.Position & ~0xfff) + (Op.Imm << 12));
|
||||
Context.EmitLdc_I((Op.Position & ~0xfffL) + (Op.Imm << 12));
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
using ChocolArm64.Decoder;
|
||||
using ChocolArm64.Memory;
|
||||
using ChocolArm64.Translation;
|
||||
using System;
|
||||
|
@ -31,24 +32,41 @@ namespace ChocolArm64.Instruction
|
|||
|
||||
private static void EmitReadCall(AILEmitterCtx Context, Extension Ext, int Size)
|
||||
{
|
||||
if (Size < 0 || Size > 4)
|
||||
bool IsSimd = GetIsSimd(Context);
|
||||
|
||||
string Name = null;
|
||||
|
||||
if (Size < 0 || Size > (IsSimd ? 4 : 3))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(Size));
|
||||
}
|
||||
|
||||
string Name = null;
|
||||
|
||||
if (IsSimd)
|
||||
{
|
||||
switch (Size)
|
||||
{
|
||||
case 0: Name = nameof(AMemory.ReadVector8); break;
|
||||
case 1: Name = nameof(AMemory.ReadVector16); break;
|
||||
case 2: Name = nameof(AMemory.ReadVector32); break;
|
||||
case 3: Name = nameof(AMemory.ReadVector64); break;
|
||||
case 4: Name = nameof(AMemory.ReadVector128); break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (Size)
|
||||
{
|
||||
case 0: Name = nameof(AMemory.ReadByte); break;
|
||||
case 1: Name = nameof(AMemory.ReadUInt16); break;
|
||||
case 2: Name = nameof(AMemory.ReadUInt32); break;
|
||||
case 3: Name = nameof(AMemory.ReadUInt64); break;
|
||||
case 4: Name = nameof(AMemory.ReadVector128); break;
|
||||
}
|
||||
}
|
||||
|
||||
Context.EmitCall(typeof(AMemory), Name);
|
||||
|
||||
if (!IsSimd)
|
||||
{
|
||||
if (Ext == Extension.Sx32 ||
|
||||
Ext == Extension.Sx64)
|
||||
{
|
||||
|
@ -67,31 +85,54 @@ namespace ChocolArm64.Instruction
|
|||
: OpCodes.Conv_U8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void EmitWriteCall(AILEmitterCtx Context, int Size)
|
||||
{
|
||||
if (Size < 0 || Size > 4)
|
||||
bool IsSimd = GetIsSimd(Context);
|
||||
|
||||
string Name = null;
|
||||
|
||||
if (Size < 0 || Size > (IsSimd ? 4 : 3))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(Size));
|
||||
}
|
||||
|
||||
if (Size < 3)
|
||||
if (Size < 3 && !IsSimd)
|
||||
{
|
||||
Context.Emit(OpCodes.Conv_I4);
|
||||
}
|
||||
|
||||
string Name = null;
|
||||
|
||||
if (IsSimd)
|
||||
{
|
||||
switch (Size)
|
||||
{
|
||||
case 0: Name = nameof(AMemory.WriteVector8); break;
|
||||
case 1: Name = nameof(AMemory.WriteVector16); break;
|
||||
case 2: Name = nameof(AMemory.WriteVector32); break;
|
||||
case 3: Name = nameof(AMemory.WriteVector64); break;
|
||||
case 4: Name = nameof(AMemory.WriteVector128); break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (Size)
|
||||
{
|
||||
case 0: Name = nameof(AMemory.WriteByte); break;
|
||||
case 1: Name = nameof(AMemory.WriteUInt16); break;
|
||||
case 2: Name = nameof(AMemory.WriteUInt32); break;
|
||||
case 3: Name = nameof(AMemory.WriteUInt64); break;
|
||||
case 4: Name = nameof(AMemory.WriteVector128); break;
|
||||
}
|
||||
}
|
||||
|
||||
Context.EmitCall(typeof(AMemory), Name);
|
||||
}
|
||||
|
||||
private static bool GetIsSimd(AILEmitterCtx Context)
|
||||
{
|
||||
return Context.CurrOp is IAOpCodeSimd &&
|
||||
!(Context.CurrOp is AOpCodeSimdMemMs ||
|
||||
Context.CurrOp is AOpCodeSimdMemSs);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,745 +0,0 @@
|
|||
using ChocolArm64.Decoder;
|
||||
using ChocolArm64.State;
|
||||
using ChocolArm64.Translation;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace ChocolArm64.Instruction
|
||||
{
|
||||
static partial class AInstEmit
|
||||
{
|
||||
public static void Addp_S(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
Context.EmitLdc_I4(Op.Size);
|
||||
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.Addp_S));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Dup_S(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
Context.EmitLdc_I4(Op.DstIndex);
|
||||
Context.EmitLdc_I4(Op.Size);
|
||||
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.Dup_S));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Fabs_S(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvecsf(Op.Rn);
|
||||
|
||||
MethodInfo MthdInfo;
|
||||
|
||||
if (Op.Size == 0)
|
||||
{
|
||||
MthdInfo = typeof(MathF).GetMethod(nameof(MathF.Abs), new Type[] { typeof(float) });
|
||||
}
|
||||
else if (Op.Size == 1)
|
||||
{
|
||||
MthdInfo = typeof(Math).GetMethod(nameof(Math.Abs), new Type[] { typeof(double) });
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
Context.EmitCall(MthdInfo);
|
||||
|
||||
Context.EmitStvecsf(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Fadd_S(AILEmitterCtx Context) => EmitScalarOp(Context, OpCodes.Add);
|
||||
|
||||
public static void Fccmp_S(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdFcond Op = (AOpCodeSimdFcond)Context.CurrOp;
|
||||
|
||||
AILLabel LblTrue = new AILLabel();
|
||||
AILLabel LblEnd = new AILLabel();
|
||||
|
||||
Context.EmitCondBranch(LblTrue, Op.Cond);
|
||||
|
||||
//TODO: Share this logic with Ccmp.
|
||||
Context.EmitLdc_I4((Op.NZCV >> 0) & 1);
|
||||
|
||||
Context.EmitStflg((int)APState.VBit);
|
||||
|
||||
Context.EmitLdc_I4((Op.NZCV >> 1) & 1);
|
||||
|
||||
Context.EmitStflg((int)APState.CBit);
|
||||
|
||||
Context.EmitLdc_I4((Op.NZCV >> 2) & 1);
|
||||
|
||||
Context.EmitStflg((int)APState.ZBit);
|
||||
|
||||
Context.EmitLdc_I4((Op.NZCV >> 3) & 1);
|
||||
|
||||
Context.EmitStflg((int)APState.NBit);
|
||||
|
||||
Context.Emit(OpCodes.Br_S, LblEnd);
|
||||
|
||||
Context.MarkLabel(LblTrue);
|
||||
|
||||
Fcmp_S(Context);
|
||||
|
||||
Context.MarkLabel(LblEnd);
|
||||
}
|
||||
|
||||
public static void Fcmp_S(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
||||
|
||||
bool CmpWithZero = !(Op is AOpCodeSimdFcond) ? Op.Bit3 : false;
|
||||
|
||||
//todo
|
||||
//Context.TryMarkCondWithoutCmp();
|
||||
|
||||
void EmitLoadOpers()
|
||||
{
|
||||
Context.EmitLdvecsf(Op.Rn);
|
||||
|
||||
if (CmpWithZero)
|
||||
{
|
||||
EmitLdcImmF(Context, 0, Op.Size);
|
||||
}
|
||||
else
|
||||
{
|
||||
Context.EmitLdvecsf(Op.Rm);
|
||||
}
|
||||
}
|
||||
|
||||
//Z = Rn == Rm
|
||||
EmitLoadOpers();
|
||||
|
||||
Context.Emit(OpCodes.Ceq);
|
||||
Context.Emit(OpCodes.Dup);
|
||||
|
||||
Context.EmitStflg((int)APState.ZBit);
|
||||
|
||||
//C = Rn >= Rm
|
||||
EmitLoadOpers();
|
||||
|
||||
Context.Emit(OpCodes.Cgt);
|
||||
Context.Emit(OpCodes.Or);
|
||||
|
||||
Context.EmitStflg((int)APState.CBit);
|
||||
|
||||
//N = Rn < Rm
|
||||
EmitLoadOpers();
|
||||
|
||||
Context.Emit(OpCodes.Clt);
|
||||
|
||||
Context.EmitStflg((int)APState.NBit);
|
||||
|
||||
//Handle NaN case. If any number is NaN, then NZCV = 0011.
|
||||
AILLabel LblNotNaN = new AILLabel();
|
||||
|
||||
if (CmpWithZero)
|
||||
{
|
||||
EmitNaNCheck(Context, Op.Rn);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitNaNCheck(Context, Op.Rn);
|
||||
EmitNaNCheck(Context, Op.Rm);
|
||||
|
||||
Context.Emit(OpCodes.Or);
|
||||
}
|
||||
|
||||
Context.Emit(OpCodes.Brfalse_S, LblNotNaN);
|
||||
|
||||
Context.EmitLdc_I4(1);
|
||||
Context.EmitLdc_I4(1);
|
||||
|
||||
Context.EmitStflg((int)APState.CBit);
|
||||
Context.EmitStflg((int)APState.VBit);
|
||||
|
||||
Context.MarkLabel(LblNotNaN);
|
||||
}
|
||||
|
||||
public static void Fcmpe_S(AILEmitterCtx Context)
|
||||
{
|
||||
//TODO: Raise exception if value is NaN, how to handle exceptions?
|
||||
Fcmp_S(Context);
|
||||
}
|
||||
|
||||
public static void Fcsel_S(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdFcond Op = (AOpCodeSimdFcond)Context.CurrOp;
|
||||
|
||||
AILLabel LblTrue = new AILLabel();
|
||||
AILLabel LblEnd = new AILLabel();
|
||||
|
||||
Context.EmitCondBranch(LblTrue, Op.Cond);
|
||||
Context.EmitLdvecsf(Op.Rm);
|
||||
Context.EmitStvecsf(Op.Rd);
|
||||
|
||||
Context.Emit(OpCodes.Br_S, LblEnd);
|
||||
|
||||
Context.MarkLabel(LblTrue);
|
||||
|
||||
Context.EmitLdvecsf(Op.Rn);
|
||||
Context.EmitStvecsf(Op.Rd);
|
||||
|
||||
Context.MarkLabel(LblEnd);
|
||||
}
|
||||
|
||||
public static void Fcvt_S(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvecsf(Op.Rn);
|
||||
|
||||
EmitFloatCast(Context, Op.Opc);
|
||||
|
||||
Context.EmitStvecsf(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Fcvtms_S(AILEmitterCtx Context) => EmitMathOpCvtToInt(Context, nameof(Math.Floor));
|
||||
public static void Fcvtps_S(AILEmitterCtx Context) => EmitMathOpCvtToInt(Context, nameof(Math.Ceiling));
|
||||
|
||||
public static void Fcvtzs_S(AILEmitterCtx Context) => EmitFcvtz_(Context, true);
|
||||
public static void Fcvtzu_S(AILEmitterCtx Context) => EmitFcvtz_(Context, false);
|
||||
|
||||
private static void EmitFcvtz_(AILEmitterCtx Context, bool Signed)
|
||||
{
|
||||
AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvecsf(Op.Rn);
|
||||
|
||||
if (Signed)
|
||||
{
|
||||
EmitCvtToInt(Context, Op.Size);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitCvtToUInt(Context, Op.Size);
|
||||
}
|
||||
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Fcvtzs_Fix(AILEmitterCtx Context) => EmitFcvtz__Fix(Context, true);
|
||||
public static void Fcvtzu_Fix(AILEmitterCtx Context) => EmitFcvtz__Fix(Context, false);
|
||||
|
||||
private static void EmitFcvtz__Fix(AILEmitterCtx Context, bool Signed)
|
||||
{
|
||||
AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvecsf(Op.Rn);
|
||||
|
||||
EmitLdcImmF(Context, 1L << Op.FBits, Op.Size);
|
||||
|
||||
Context.Emit(OpCodes.Mul);
|
||||
|
||||
if (Signed)
|
||||
{
|
||||
EmitCvtToInt(Context, Op.Size);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitCvtToUInt(Context, Op.Size);
|
||||
}
|
||||
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Fdiv_S(AILEmitterCtx Context) => EmitScalarOp(Context, OpCodes.Div);
|
||||
|
||||
public static void Fmadd_S(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvecsf(Op.Ra);
|
||||
Context.EmitLdvecsf(Op.Rn);
|
||||
Context.EmitLdvecsf(Op.Rm);
|
||||
|
||||
Context.Emit(OpCodes.Mul);
|
||||
Context.Emit(OpCodes.Add);
|
||||
|
||||
Context.EmitStvecsf(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Fmax_S(AILEmitterCtx Context) => EmitMathOp3(Context, nameof(Math.Max));
|
||||
public static void Fmin_S(AILEmitterCtx Context) => EmitMathOp3(Context, nameof(Math.Min));
|
||||
|
||||
public static void Fmaxnm_S(AILEmitterCtx Context) => EmitMathOp3(Context, nameof(Math.Max));
|
||||
public static void Fminnm_S(AILEmitterCtx Context) => EmitMathOp3(Context, nameof(Math.Min));
|
||||
|
||||
public static void Fmov_S(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvecsf(Op.Rn);
|
||||
Context.EmitStvecsf(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Fmov_Si(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdFmov Op = (AOpCodeSimdFmov)Context.CurrOp;
|
||||
|
||||
Context.EmitLdc_I8(Op.Imm);
|
||||
Context.EmitLdc_I4(0);
|
||||
Context.EmitLdc_I4(Op.Size + 2);
|
||||
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.Fmov_S));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Fmov_Ftoi(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvecsi(Op.Rn);
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Fmov_Itof(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp;
|
||||
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
Context.EmitStvecsi(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Fmov_Ftoi1(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
Context.EmitLdc_I4(1);
|
||||
Context.EmitLdc_I4(3);
|
||||
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.ExtractVec));
|
||||
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Fmov_Itof1(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp;
|
||||
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
Context.EmitLdc_I4(1);
|
||||
Context.EmitLdc_I4(3);
|
||||
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.Fmov_S));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Fmsub_S(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvecsf(Op.Ra);
|
||||
Context.EmitLdvecsf(Op.Rn);
|
||||
|
||||
Context.Emit(OpCodes.Neg);
|
||||
|
||||
Context.EmitLdvecsf(Op.Rm);
|
||||
|
||||
Context.Emit(OpCodes.Mul);
|
||||
Context.Emit(OpCodes.Sub);
|
||||
|
||||
Context.EmitStvecsf(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Fmul_S(AILEmitterCtx Context) => EmitScalarOp(Context, OpCodes.Mul);
|
||||
|
||||
public static void Fneg_S(AILEmitterCtx Context) => EmitScalarOp(Context, OpCodes.Neg);
|
||||
|
||||
public static void Fnmul_S(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvecsf(Op.Rn);
|
||||
Context.EmitLdvecsf(Op.Rm);
|
||||
|
||||
Context.Emit(OpCodes.Mul);
|
||||
Context.Emit(OpCodes.Neg);
|
||||
|
||||
Context.EmitStvecsf(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Frinta_S(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvecsf(Op.Rn);
|
||||
Context.EmitLdc_I4((int)MidpointRounding.AwayFromZero);
|
||||
|
||||
MethodInfo MthdInfo;
|
||||
|
||||
if (Op.Size == 0)
|
||||
{
|
||||
Type[] Types = new Type[] { typeof(float), typeof(MidpointRounding) };
|
||||
|
||||
MthdInfo = typeof(MathF).GetMethod(nameof(MathF.Round), Types);
|
||||
}
|
||||
else if (Op.Size == 1)
|
||||
{
|
||||
Type[] Types = new Type[] { typeof(double), typeof(MidpointRounding) };
|
||||
|
||||
MthdInfo = typeof(Math).GetMethod(nameof(Math.Round), Types);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
Context.EmitCall(MthdInfo);
|
||||
|
||||
Context.EmitStvecsf(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Frintm_S(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvecsf(Op.Rn);
|
||||
|
||||
MethodInfo MthdInfo;
|
||||
|
||||
if (Op.Size == 0)
|
||||
{
|
||||
MthdInfo = typeof(MathF).GetMethod(nameof(MathF.Floor), new Type[] { typeof(float) });
|
||||
}
|
||||
else if (Op.Size == 1)
|
||||
{
|
||||
MthdInfo = typeof(Math).GetMethod(nameof(Math.Floor), new Type[] { typeof(double) });
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
Context.EmitCall(MthdInfo);
|
||||
|
||||
Context.EmitStvecsf(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Fsqrt_S(AILEmitterCtx Context) => EmitMathOp2(Context, nameof(Math.Sqrt));
|
||||
|
||||
public static void Fsub_S(AILEmitterCtx Context) => EmitScalarOp(Context, OpCodes.Sub);
|
||||
|
||||
public static void Scvtf_Gp(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp;
|
||||
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
|
||||
EmitFloatCast(Context, Op.Size);
|
||||
|
||||
Context.EmitStvecsf(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Scvtf_S(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvecsi(Op.Rn);
|
||||
|
||||
EmitFloatCast(Context, Op.Size);
|
||||
|
||||
Context.EmitStvecsf(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Shl_S(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvecsi(Op.Rn);
|
||||
Context.EmitLdc_I4(Op.Imm - (8 << Op.Size));
|
||||
|
||||
Context.Emit(OpCodes.Shl);
|
||||
|
||||
Context.EmitStvecsi(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Sshr_S(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvecsi(Op.Rn);
|
||||
Context.EmitLdc_I4((8 << (Op.Size + 1)) - Op.Imm);
|
||||
|
||||
Context.Emit(OpCodes.Shr);
|
||||
|
||||
Context.EmitStvecsi(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Sub_S(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvecsi(Op.Rn);
|
||||
Context.EmitLdvecsi(Op.Rm);
|
||||
|
||||
Context.Emit(OpCodes.Sub);
|
||||
|
||||
Context.EmitStvecsi(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Ucvtf_Gp(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp;
|
||||
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
|
||||
Context.Emit(OpCodes.Conv_R_Un);
|
||||
|
||||
EmitFloatCast(Context, Op.Size);
|
||||
|
||||
Context.EmitStvecsf(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Ucvtf_S(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvecsi(Op.Rn);
|
||||
|
||||
Context.Emit(OpCodes.Conv_R_Un);
|
||||
|
||||
EmitFloatCast(Context, Op.Size);
|
||||
|
||||
Context.EmitStvecsf(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Umov_S(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
Context.EmitLdc_I4(Op.DstIndex);
|
||||
Context.EmitLdc_I4(Op.Size);
|
||||
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.ExtractVec));
|
||||
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
|
||||
private static void EmitScalarOp(AILEmitterCtx Context, OpCode ILOp)
|
||||
{
|
||||
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvecsf(Op.Rn);
|
||||
|
||||
//Negate and Not are the only unary operations supported on IL.
|
||||
//"Not" doesn't work with floats, so we don't need to compare it.
|
||||
if (ILOp != OpCodes.Neg)
|
||||
{
|
||||
Context.EmitLdvecsf(Op.Rm);
|
||||
}
|
||||
|
||||
Context.Emit(ILOp);
|
||||
|
||||
Context.EmitStvecsf(Op.Rd);
|
||||
}
|
||||
|
||||
private static void EmitMathOp2(AILEmitterCtx Context, string Name)
|
||||
{
|
||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvecsf(Op.Rn);
|
||||
|
||||
EmitMathOpCall(Context, Name);
|
||||
|
||||
Context.EmitStvecsf(Op.Rd);
|
||||
}
|
||||
|
||||
private static void EmitMathOp3(AILEmitterCtx Context, string Name)
|
||||
{
|
||||
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvecsf(Op.Rn);
|
||||
Context.EmitLdvecsf(Op.Rm);
|
||||
|
||||
MethodInfo MthdInfo;
|
||||
|
||||
if (Op.Size == 0)
|
||||
{
|
||||
MthdInfo = typeof(MathF).GetMethod(Name, new Type[] { typeof(float), typeof(float) });
|
||||
}
|
||||
else if (Op.Size == 1)
|
||||
{
|
||||
MthdInfo = typeof(Math).GetMethod(Name, new Type[] { typeof(double), typeof(double) });
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
Context.EmitCall(MthdInfo);
|
||||
|
||||
Context.EmitStvecsf(Op.Rd);
|
||||
}
|
||||
|
||||
public static void EmitMathOpCvtToInt(AILEmitterCtx Context, string Name)
|
||||
{
|
||||
AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvecsf(Op.Rn);
|
||||
|
||||
EmitMathOpCall(Context, Name);
|
||||
|
||||
EmitCvtToInt(Context, Op.Size);
|
||||
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
|
||||
private static void EmitMathOpCall(AILEmitterCtx Context, string Name)
|
||||
{
|
||||
IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp;
|
||||
|
||||
MethodInfo MthdInfo;
|
||||
|
||||
if (Op.Size == 0)
|
||||
{
|
||||
MthdInfo = typeof(MathF).GetMethod(Name);
|
||||
}
|
||||
else if (Op.Size == 1)
|
||||
{
|
||||
MthdInfo = typeof(Math).GetMethod(Name);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
Context.EmitCall(MthdInfo);
|
||||
}
|
||||
|
||||
private static void EmitCvtToInt(AILEmitterCtx Context, int Size)
|
||||
{
|
||||
if (Size < 0 || Size > 1)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(Size));
|
||||
}
|
||||
|
||||
Context.EmitLdc_I4(0);
|
||||
|
||||
if (Context.CurrOp.RegisterSize == ARegisterSize.Int32)
|
||||
{
|
||||
if (Size == 0)
|
||||
{
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatSingleToInt32));
|
||||
}
|
||||
else /* if (Size == 1) */
|
||||
{
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatDoubleToInt32));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Size == 0)
|
||||
{
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatSingleToInt64));
|
||||
}
|
||||
else /* if (Size == 1) */
|
||||
{
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatDoubleToInt64));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitCvtToUInt(AILEmitterCtx Context, int Size)
|
||||
{
|
||||
if (Size < 0 || Size > 1)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(Size));
|
||||
}
|
||||
|
||||
Context.EmitLdc_I4(0);
|
||||
|
||||
if (Context.CurrOp.RegisterSize == ARegisterSize.Int32)
|
||||
{
|
||||
if (Size == 0)
|
||||
{
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatSingleToUInt32));
|
||||
}
|
||||
else /* if (Size == 1) */
|
||||
{
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatDoubleToUInt32));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Size == 0)
|
||||
{
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatSingleToUInt64));
|
||||
}
|
||||
else /* if (Size == 1) */
|
||||
{
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatDoubleToUInt64));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitFloatCast(AILEmitterCtx Context, int Size)
|
||||
{
|
||||
if (Size == 0)
|
||||
{
|
||||
Context.Emit(OpCodes.Conv_R4);
|
||||
}
|
||||
else if (Size == 1)
|
||||
{
|
||||
Context.Emit(OpCodes.Conv_R8);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(Size));
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitLdcImmF(AILEmitterCtx Context, double ImmF, int Size)
|
||||
{
|
||||
if (Size == 0)
|
||||
{
|
||||
Context.EmitLdc_R4((float)ImmF);
|
||||
}
|
||||
else if (Size == 1)
|
||||
{
|
||||
Context.EmitLdc_R8(ImmF);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(Size));
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitNaNCheck(AILEmitterCtx Context, int Index)
|
||||
{
|
||||
IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvecsf(Index);
|
||||
|
||||
if (Op.Size == 0)
|
||||
{
|
||||
Context.EmitCall(typeof(float), nameof(float.IsNaN));
|
||||
}
|
||||
else if (Op.Size == 1)
|
||||
{
|
||||
Context.EmitCall(typeof(double), nameof(double.IsNaN));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
354
Ryujinx/Cpu/Instruction/AInstEmitSimdArithmetic.cs
Normal file
354
Ryujinx/Cpu/Instruction/AInstEmitSimdArithmetic.cs
Normal file
|
@ -0,0 +1,354 @@
|
|||
using ChocolArm64.Decoder;
|
||||
using ChocolArm64.State;
|
||||
using ChocolArm64.Translation;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
using static ChocolArm64.Instruction.AInstEmitSimdHelper;
|
||||
|
||||
namespace ChocolArm64.Instruction
|
||||
{
|
||||
static partial class AInstEmit
|
||||
{
|
||||
public static void Add_V(AILEmitterCtx Context)
|
||||
{
|
||||
EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.Add));
|
||||
}
|
||||
|
||||
public static void Addp_S(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||
|
||||
EmitVectorExtractZx(Context, Op.Rn, 0, Op.Size);
|
||||
EmitVectorExtractZx(Context, Op.Rn, 1, Op.Size);
|
||||
|
||||
Context.Emit(OpCodes.Add);
|
||||
|
||||
EmitScalarSet(Context, Op.Rd, Op.Size);
|
||||
}
|
||||
|
||||
public static void Addp_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
||||
|
||||
int Bytes = Context.CurrOp.GetBitsCount() >> 3;
|
||||
|
||||
int Elems = Bytes >> Op.Size;
|
||||
int Half = Elems >> 1;
|
||||
|
||||
for (int Index = 0; Index < Elems; Index++)
|
||||
{
|
||||
int Elem = (Index & (Half - 1)) << 1;
|
||||
|
||||
EmitVectorExtractZx(Context, Index < Half ? Op.Rn : Op.Rm, Elem + 0, Op.Size);
|
||||
EmitVectorExtractZx(Context, Index < Half ? Op.Rn : Op.Rm, Elem + 1, Op.Size);
|
||||
|
||||
Context.Emit(OpCodes.Add);
|
||||
|
||||
EmitVectorInsertTmp(Context, Index, Op.Size);
|
||||
}
|
||||
|
||||
Context.EmitLdvectmp();
|
||||
Context.EmitStvec(Op.Rd);
|
||||
|
||||
if (Op.RegisterSize == ARegisterSize.SIMD64)
|
||||
{
|
||||
EmitVectorZeroUpper(Context, Op.Rd);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Addv_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||
|
||||
int Bytes = Context.CurrOp.GetBitsCount() >> 3;
|
||||
|
||||
EmitVectorExtractZx(Context, Op.Rn, 0, Op.Size);
|
||||
|
||||
for (int Index = 1; Index < (Bytes >> Op.Size); Index++)
|
||||
{
|
||||
EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size);
|
||||
|
||||
Context.Emit(OpCodes.Add);
|
||||
}
|
||||
|
||||
EmitScalarSet(Context, Op.Rd, Op.Size);
|
||||
}
|
||||
|
||||
public static void Cnt_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||
|
||||
int Elems = Op.RegisterSize == ARegisterSize.SIMD128 ? 16 : 8;
|
||||
|
||||
for (int Index = 0; Index < Elems; Index++)
|
||||
{
|
||||
EmitVectorExtractZx(Context, Op.Rn, Index, 0);
|
||||
|
||||
Context.Emit(OpCodes.Conv_U1);
|
||||
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.CountSetBits8));
|
||||
|
||||
Context.Emit(OpCodes.Conv_U8);
|
||||
|
||||
EmitVectorInsert(Context, Op.Rd, Index, 0);
|
||||
}
|
||||
|
||||
if (Op.RegisterSize == ARegisterSize.SIMD64)
|
||||
{
|
||||
EmitVectorZeroUpper(Context, Op.Rd);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Fabs_S(AILEmitterCtx Context)
|
||||
{
|
||||
EmitScalarUnaryOpF(Context, () =>
|
||||
{
|
||||
EmitUnaryMathCall(Context, nameof(Math.Abs));
|
||||
});
|
||||
}
|
||||
|
||||
public static void Fadd_S(AILEmitterCtx Context)
|
||||
{
|
||||
EmitScalarBinaryOpF(Context, () => Context.Emit(OpCodes.Add));
|
||||
}
|
||||
|
||||
public static void Fadd_V(AILEmitterCtx Context)
|
||||
{
|
||||
EmitVectorBinaryOpF(Context, () => Context.Emit(OpCodes.Add));
|
||||
}
|
||||
|
||||
public static void Fdiv_S(AILEmitterCtx Context)
|
||||
{
|
||||
EmitScalarBinaryOpF(Context, () => Context.Emit(OpCodes.Div));
|
||||
}
|
||||
|
||||
public static void Fmadd_S(AILEmitterCtx Context)
|
||||
{
|
||||
EmitScalarTernaryRaOpF(Context, () =>
|
||||
{
|
||||
Context.Emit(OpCodes.Mul);
|
||||
Context.Emit(OpCodes.Add);
|
||||
});
|
||||
}
|
||||
|
||||
public static void Fmax_S(AILEmitterCtx Context)
|
||||
{
|
||||
EmitScalarBinaryOpF(Context, () =>
|
||||
{
|
||||
EmitBinaryMathCall(Context, nameof(Math.Max));
|
||||
});
|
||||
}
|
||||
|
||||
public static void Fmin_S(AILEmitterCtx Context)
|
||||
{
|
||||
EmitScalarBinaryOpF(Context, () =>
|
||||
{
|
||||
EmitBinaryMathCall(Context, nameof(Math.Min));
|
||||
});
|
||||
}
|
||||
|
||||
public static void Fmaxnm_S(AILEmitterCtx Context)
|
||||
{
|
||||
Fmax_S(Context);
|
||||
}
|
||||
|
||||
public static void Fminnm_S(AILEmitterCtx Context)
|
||||
{
|
||||
Fmin_S(Context);
|
||||
}
|
||||
|
||||
public static void Fmla_V(AILEmitterCtx Context)
|
||||
{
|
||||
EmitVectorTernaryOpF(Context, () =>
|
||||
{
|
||||
Context.Emit(OpCodes.Mul);
|
||||
Context.Emit(OpCodes.Add);
|
||||
});
|
||||
}
|
||||
|
||||
public static void Fmla_Ve(AILEmitterCtx Context)
|
||||
{
|
||||
EmitVectorTernaryOpByElemF(Context, () =>
|
||||
{
|
||||
Context.Emit(OpCodes.Mul);
|
||||
Context.Emit(OpCodes.Add);
|
||||
});
|
||||
}
|
||||
|
||||
public static void Fmsub_S(AILEmitterCtx Context)
|
||||
{
|
||||
EmitScalarTernaryRaOpF(Context, () =>
|
||||
{
|
||||
Context.Emit(OpCodes.Mul);
|
||||
Context.Emit(OpCodes.Neg);
|
||||
Context.Emit(OpCodes.Add);
|
||||
});
|
||||
}
|
||||
|
||||
public static void Fmul_S(AILEmitterCtx Context)
|
||||
{
|
||||
EmitScalarBinaryOpF(Context, () => Context.Emit(OpCodes.Mul));
|
||||
}
|
||||
|
||||
public static void Fmul_V(AILEmitterCtx Context)
|
||||
{
|
||||
EmitVectorBinaryOpF(Context, () => Context.Emit(OpCodes.Mul));
|
||||
}
|
||||
|
||||
public static void Fmul_Ve(AILEmitterCtx Context)
|
||||
{
|
||||
EmitVectorBinaryOpByElemF(Context, () => Context.Emit(OpCodes.Mul));
|
||||
}
|
||||
|
||||
public static void Fneg_S(AILEmitterCtx Context)
|
||||
{
|
||||
EmitScalarUnaryOpF(Context, () => Context.Emit(OpCodes.Neg));
|
||||
}
|
||||
|
||||
public static void Fnmul_S(AILEmitterCtx Context)
|
||||
{
|
||||
EmitScalarBinaryOpF(Context, () =>
|
||||
{
|
||||
Context.Emit(OpCodes.Mul);
|
||||
Context.Emit(OpCodes.Neg);
|
||||
});
|
||||
}
|
||||
|
||||
public static void Frinta_S(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||
|
||||
EmitVectorExtractF(Context, Op.Rn, 0, Op.Size);
|
||||
|
||||
Context.EmitLdc_I4((int)MidpointRounding.AwayFromZero);
|
||||
|
||||
MethodInfo MthdInfo;
|
||||
|
||||
Type[] Types = new Type[] { null, typeof(MidpointRounding) };
|
||||
|
||||
Types[0] = Op.Size == 0
|
||||
? typeof(float)
|
||||
: typeof(double);
|
||||
|
||||
if (Op.Size == 0)
|
||||
{
|
||||
MthdInfo = typeof(MathF).GetMethod(nameof(MathF.Round), Types);
|
||||
}
|
||||
else if (Op.Size == 1)
|
||||
{
|
||||
MthdInfo = typeof(Math).GetMethod(nameof(Math.Round), Types);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
Context.EmitCall(MthdInfo);
|
||||
|
||||
EmitScalarSetF(Context, Op.Rd, Op.Size);
|
||||
}
|
||||
|
||||
public static void Frintm_S(AILEmitterCtx Context)
|
||||
{
|
||||
EmitScalarUnaryOpF(Context, () =>
|
||||
{
|
||||
EmitUnaryMathCall(Context, nameof(Math.Floor));
|
||||
});
|
||||
}
|
||||
|
||||
public static void Fsqrt_S(AILEmitterCtx Context)
|
||||
{
|
||||
EmitScalarUnaryOpF(Context, () =>
|
||||
{
|
||||
EmitUnaryMathCall(Context, nameof(Math.Sqrt));
|
||||
});
|
||||
}
|
||||
|
||||
public static void Fsub_S(AILEmitterCtx Context)
|
||||
{
|
||||
EmitScalarBinaryOpF(Context, () => Context.Emit(OpCodes.Sub));
|
||||
}
|
||||
|
||||
public static void Fsub_V(AILEmitterCtx Context)
|
||||
{
|
||||
EmitVectorBinaryOpF(Context, () => Context.Emit(OpCodes.Sub));
|
||||
}
|
||||
|
||||
public static void Mla_V(AILEmitterCtx Context)
|
||||
{
|
||||
EmitVectorTernaryOpZx(Context, () =>
|
||||
{
|
||||
Context.Emit(OpCodes.Mul);
|
||||
Context.Emit(OpCodes.Add);
|
||||
});
|
||||
}
|
||||
|
||||
public static void Mul_V(AILEmitterCtx Context)
|
||||
{
|
||||
EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.Mul));
|
||||
}
|
||||
|
||||
public static void Neg_V(AILEmitterCtx Context)
|
||||
{
|
||||
EmitVectorUnaryOpSx(Context, () => Context.Emit(OpCodes.Neg));
|
||||
}
|
||||
|
||||
public static void Saddw_V(AILEmitterCtx Context)
|
||||
{
|
||||
EmitVectorWidenBinaryOpSx(Context, () => Context.Emit(OpCodes.Add));
|
||||
}
|
||||
|
||||
public static void Smax_V(AILEmitterCtx Context)
|
||||
{
|
||||
Type[] Types = new Type[] { typeof(long), typeof(long) };
|
||||
|
||||
MethodInfo MthdInfo = typeof(Math).GetMethod(nameof(Math.Max), Types);
|
||||
|
||||
EmitVectorBinaryOpSx(Context, () => Context.EmitCall(MthdInfo));
|
||||
}
|
||||
|
||||
public static void Smin_V(AILEmitterCtx Context)
|
||||
{
|
||||
Type[] Types = new Type[] { typeof(long), typeof(long) };
|
||||
|
||||
MethodInfo MthdInfo = typeof(Math).GetMethod(nameof(Math.Min), Types);
|
||||
|
||||
EmitVectorBinaryOpSx(Context, () => Context.EmitCall(MthdInfo));
|
||||
}
|
||||
|
||||
public static void Sub_S(AILEmitterCtx Context)
|
||||
{
|
||||
EmitScalarBinaryOpZx(Context, () => Context.Emit(OpCodes.Sub));
|
||||
}
|
||||
|
||||
public static void Sub_V(AILEmitterCtx Context)
|
||||
{
|
||||
EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.Sub));
|
||||
}
|
||||
|
||||
public static void Uaddlv_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||
|
||||
int Bytes = Context.CurrOp.GetBitsCount() >> 3;
|
||||
|
||||
EmitVectorExtractZx(Context, Op.Rn, 0, Op.Size);
|
||||
|
||||
for (int Index = 1; Index < (Bytes >> Op.Size); Index++)
|
||||
{
|
||||
EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size);
|
||||
|
||||
Context.Emit(OpCodes.Add);
|
||||
}
|
||||
|
||||
EmitScalarSet(Context, Op.Rd, Op.Size + 1);
|
||||
}
|
||||
|
||||
public static void Uaddw_V(AILEmitterCtx Context)
|
||||
{
|
||||
EmitVectorWidenBinaryOpZx(Context, () => Context.Emit(OpCodes.Add));
|
||||
}
|
||||
}
|
||||
}
|
236
Ryujinx/Cpu/Instruction/AInstEmitSimdCmp.cs
Normal file
236
Ryujinx/Cpu/Instruction/AInstEmitSimdCmp.cs
Normal file
|
@ -0,0 +1,236 @@
|
|||
using ChocolArm64.Decoder;
|
||||
using ChocolArm64.State;
|
||||
using ChocolArm64.Translation;
|
||||
using System;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
using static ChocolArm64.Instruction.AInstEmitSimdHelper;
|
||||
|
||||
namespace ChocolArm64.Instruction
|
||||
{
|
||||
static partial class AInstEmit
|
||||
{
|
||||
public static void Cmeq_V(AILEmitterCtx Context)
|
||||
{
|
||||
EmitVectorCmp(Context, OpCodes.Beq_S);
|
||||
}
|
||||
|
||||
public static void Cmge_V(AILEmitterCtx Context)
|
||||
{
|
||||
EmitVectorCmp(Context, OpCodes.Bge_S);
|
||||
}
|
||||
|
||||
public static void Cmgt_V(AILEmitterCtx Context)
|
||||
{
|
||||
EmitVectorCmp(Context, OpCodes.Bgt_S);
|
||||
}
|
||||
|
||||
public static void Cmhi_V(AILEmitterCtx Context)
|
||||
{
|
||||
EmitVectorCmp(Context, OpCodes.Bgt_Un_S);
|
||||
}
|
||||
|
||||
public static void Cmhs_V(AILEmitterCtx Context)
|
||||
{
|
||||
EmitVectorCmp(Context, OpCodes.Bge_Un_S);
|
||||
}
|
||||
|
||||
public static void Cmle_V(AILEmitterCtx Context)
|
||||
{
|
||||
EmitVectorCmp(Context, OpCodes.Ble_S);
|
||||
}
|
||||
|
||||
public static void Cmlt_V(AILEmitterCtx Context)
|
||||
{
|
||||
EmitVectorCmp(Context, OpCodes.Blt_S);
|
||||
}
|
||||
|
||||
public static void Fccmp_S(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdFcond Op = (AOpCodeSimdFcond)Context.CurrOp;
|
||||
|
||||
AILLabel LblTrue = new AILLabel();
|
||||
AILLabel LblEnd = new AILLabel();
|
||||
|
||||
Context.EmitCondBranch(LblTrue, Op.Cond);
|
||||
|
||||
//TODO: Share this logic with Ccmp.
|
||||
Context.EmitLdc_I4((Op.NZCV >> 0) & 1);
|
||||
|
||||
Context.EmitStflg((int)APState.VBit);
|
||||
|
||||
Context.EmitLdc_I4((Op.NZCV >> 1) & 1);
|
||||
|
||||
Context.EmitStflg((int)APState.CBit);
|
||||
|
||||
Context.EmitLdc_I4((Op.NZCV >> 2) & 1);
|
||||
|
||||
Context.EmitStflg((int)APState.ZBit);
|
||||
|
||||
Context.EmitLdc_I4((Op.NZCV >> 3) & 1);
|
||||
|
||||
Context.EmitStflg((int)APState.NBit);
|
||||
|
||||
Context.Emit(OpCodes.Br_S, LblEnd);
|
||||
|
||||
Context.MarkLabel(LblTrue);
|
||||
|
||||
Fcmp_S(Context);
|
||||
|
||||
Context.MarkLabel(LblEnd);
|
||||
}
|
||||
|
||||
public static void Fcmp_S(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
||||
|
||||
bool CmpWithZero = !(Op is AOpCodeSimdFcond) ? Op.Bit3 : false;
|
||||
|
||||
void EmitLoadOpers()
|
||||
{
|
||||
EmitVectorExtractF(Context, Op.Rn, 0, Op.Size);
|
||||
|
||||
if (CmpWithZero)
|
||||
{
|
||||
EmitLdcImmF(Context, 0, Op.Size);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitVectorExtractF(Context, Op.Rm, 0, Op.Size);
|
||||
}
|
||||
}
|
||||
|
||||
//Z = Rn == Rm
|
||||
EmitLoadOpers();
|
||||
|
||||
Context.Emit(OpCodes.Ceq);
|
||||
Context.Emit(OpCodes.Dup);
|
||||
|
||||
Context.EmitStflg((int)APState.ZBit);
|
||||
|
||||
//C = Rn >= Rm
|
||||
EmitLoadOpers();
|
||||
|
||||
Context.Emit(OpCodes.Cgt);
|
||||
Context.Emit(OpCodes.Or);
|
||||
|
||||
Context.EmitStflg((int)APState.CBit);
|
||||
|
||||
//N = Rn < Rm
|
||||
EmitLoadOpers();
|
||||
|
||||
Context.Emit(OpCodes.Clt);
|
||||
|
||||
Context.EmitStflg((int)APState.NBit);
|
||||
|
||||
//Handle NaN case. If any number is NaN, then NZCV = 0011.
|
||||
AILLabel LblNotNaN = new AILLabel();
|
||||
|
||||
if (CmpWithZero)
|
||||
{
|
||||
EmitNaNCheck(Context, Op.Rn);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitNaNCheck(Context, Op.Rn);
|
||||
EmitNaNCheck(Context, Op.Rm);
|
||||
|
||||
Context.Emit(OpCodes.Or);
|
||||
}
|
||||
|
||||
Context.Emit(OpCodes.Brfalse_S, LblNotNaN);
|
||||
|
||||
Context.EmitLdc_I4(1);
|
||||
Context.EmitLdc_I4(1);
|
||||
|
||||
Context.EmitStflg((int)APState.CBit);
|
||||
Context.EmitStflg((int)APState.VBit);
|
||||
|
||||
Context.MarkLabel(LblNotNaN);
|
||||
}
|
||||
|
||||
public static void Fcmpe_S(AILEmitterCtx Context)
|
||||
{
|
||||
Fcmp_S(Context);
|
||||
}
|
||||
|
||||
private static void EmitLdcImmF(AILEmitterCtx Context, double ImmF, int Size)
|
||||
{
|
||||
if (Size == 0)
|
||||
{
|
||||
Context.EmitLdc_R4((float)ImmF);
|
||||
}
|
||||
else if (Size == 1)
|
||||
{
|
||||
Context.EmitLdc_R8(ImmF);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(Size));
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitNaNCheck(AILEmitterCtx Context, int Reg)
|
||||
{
|
||||
IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp;
|
||||
|
||||
EmitVectorExtractF(Context, Reg, 0, Op.Size);
|
||||
|
||||
if (Op.Size == 0)
|
||||
{
|
||||
Context.EmitCall(typeof(float), nameof(float.IsNaN));
|
||||
}
|
||||
else if (Op.Size == 1)
|
||||
{
|
||||
Context.EmitCall(typeof(double), nameof(double.IsNaN));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitVectorCmp(AILEmitterCtx Context, OpCode ILOp)
|
||||
{
|
||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||
|
||||
int Bytes = Context.CurrOp.GetBitsCount() >> 3;
|
||||
|
||||
ulong SzMask = ulong.MaxValue >> (64 - (8 << Op.Size));
|
||||
|
||||
for (int Index = 0; Index < (Bytes >> Op.Size); Index++)
|
||||
{
|
||||
EmitVectorExtractSx(Context, Op.Rn, Index, Op.Size);
|
||||
|
||||
if (Op is AOpCodeSimdReg BinOp)
|
||||
{
|
||||
EmitVectorExtractSx(Context, BinOp.Rm, Index, Op.Size);
|
||||
}
|
||||
else
|
||||
{
|
||||
Context.EmitLdc_I8(0);
|
||||
}
|
||||
|
||||
AILLabel LblTrue = new AILLabel();
|
||||
AILLabel LblEnd = new AILLabel();
|
||||
|
||||
Context.Emit(ILOp, LblTrue);
|
||||
|
||||
EmitVectorInsert(Context, Op.Rd, Index, Op.Size, 0);
|
||||
|
||||
Context.Emit(OpCodes.Br_S, LblEnd);
|
||||
|
||||
Context.MarkLabel(LblTrue);
|
||||
|
||||
EmitVectorInsert(Context, Op.Rd, Index, Op.Size, (long)SzMask);
|
||||
|
||||
Context.MarkLabel(LblEnd);
|
||||
}
|
||||
|
||||
if (Op.RegisterSize == ARegisterSize.SIMD64)
|
||||
{
|
||||
EmitVectorZeroUpper(Context, Op.Rd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
399
Ryujinx/Cpu/Instruction/AInstEmitSimdCvt.cs
Normal file
399
Ryujinx/Cpu/Instruction/AInstEmitSimdCvt.cs
Normal file
|
@ -0,0 +1,399 @@
|
|||
using ChocolArm64.Decoder;
|
||||
using ChocolArm64.State;
|
||||
using ChocolArm64.Translation;
|
||||
using System;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
using static ChocolArm64.Instruction.AInstEmitSimdHelper;
|
||||
|
||||
namespace ChocolArm64.Instruction
|
||||
{
|
||||
static partial class AInstEmit
|
||||
{
|
||||
public static void Fcvt_S(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||
|
||||
EmitVectorExtractF(Context, Op.Rn, 0, Op.Size);
|
||||
|
||||
EmitFloatCast(Context, Op.Opc);
|
||||
|
||||
EmitScalarSetF(Context, Op.Rd, Op.Opc);
|
||||
}
|
||||
|
||||
public static void Fcvtms_Gp(AILEmitterCtx Context)
|
||||
{
|
||||
EmitFcvt_s_Gp(Context, nameof(Math.Floor));
|
||||
}
|
||||
|
||||
public static void Fcvtps_Gp(AILEmitterCtx Context)
|
||||
{
|
||||
EmitFcvt_s_Gp(Context, nameof(Math.Ceiling));
|
||||
}
|
||||
|
||||
public static void Fcvtzs_Gp(AILEmitterCtx Context)
|
||||
{
|
||||
EmitFcvtz__Gp(Context, Signed: true);
|
||||
}
|
||||
|
||||
public static void Fcvtzs_Gp_Fix(AILEmitterCtx Context)
|
||||
{
|
||||
EmitFcvtz__Gp_Fix(Context, Signed: true);
|
||||
}
|
||||
|
||||
public static void Fcvtzs_V(AILEmitterCtx Context)
|
||||
{
|
||||
EmitVectorFcvt(Context, Signed: true);
|
||||
}
|
||||
|
||||
public static void Fcvtzu_Gp(AILEmitterCtx Context)
|
||||
{
|
||||
EmitFcvtz__Gp(Context, Signed: false);
|
||||
}
|
||||
|
||||
public static void Fcvtzu_Gp_Fix(AILEmitterCtx Context)
|
||||
{
|
||||
EmitFcvtz__Gp_Fix(Context, Signed: false);
|
||||
}
|
||||
|
||||
public static void Fcvtzu_V(AILEmitterCtx Context)
|
||||
{
|
||||
EmitVectorFcvt(Context, Signed: false);
|
||||
}
|
||||
|
||||
public static void Scvtf_Gp(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp;
|
||||
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
|
||||
if (Context.CurrOp.RegisterSize == ARegisterSize.Int32)
|
||||
{
|
||||
Context.Emit(OpCodes.Conv_U4);
|
||||
}
|
||||
|
||||
EmitFloatCast(Context, Op.Size);
|
||||
|
||||
EmitScalarSetF(Context, Op.Rd, Op.Size);
|
||||
}
|
||||
|
||||
public static void Scvtf_S(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||
|
||||
EmitVectorExtractSx(Context, Op.Rd, 0, Op.Size + 2);
|
||||
|
||||
EmitFloatCast(Context, Op.Size);
|
||||
|
||||
EmitScalarSetF(Context, Op.Rd, Op.Size);
|
||||
}
|
||||
|
||||
public static void Scvtf_V(AILEmitterCtx Context)
|
||||
{
|
||||
EmitVectorCvtf(Context, Signed: true);
|
||||
}
|
||||
|
||||
public static void Ucvtf_Gp(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp;
|
||||
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
|
||||
if (Context.CurrOp.RegisterSize == ARegisterSize.Int32)
|
||||
{
|
||||
Context.Emit(OpCodes.Conv_U4);
|
||||
}
|
||||
|
||||
Context.Emit(OpCodes.Conv_R_Un);
|
||||
|
||||
EmitFloatCast(Context, Op.Size);
|
||||
|
||||
EmitScalarSetF(Context, Op.Rd, Op.Size);
|
||||
}
|
||||
|
||||
public static void Ucvtf_S(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||
|
||||
EmitVectorExtractZx(Context, Op.Rn, 0, Op.Size + 2);
|
||||
|
||||
Context.Emit(OpCodes.Conv_R_Un);
|
||||
|
||||
EmitFloatCast(Context, Op.Size);
|
||||
|
||||
EmitScalarSetF(Context, Op.Rd, Op.Size);
|
||||
}
|
||||
|
||||
public static void Ucvtf_V(AILEmitterCtx Context)
|
||||
{
|
||||
EmitVectorCvtf(Context, Signed: false);
|
||||
}
|
||||
|
||||
private static int GetFBits(AILEmitterCtx Context)
|
||||
{
|
||||
if (Context.CurrOp is AOpCodeSimdShImm Op)
|
||||
{
|
||||
return GetImmShr(Op);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static void EmitFloatCast(AILEmitterCtx Context, int Size)
|
||||
{
|
||||
if (Size == 0)
|
||||
{
|
||||
Context.Emit(OpCodes.Conv_R4);
|
||||
}
|
||||
else if (Size == 1)
|
||||
{
|
||||
Context.Emit(OpCodes.Conv_R8);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(Size));
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitFcvt_s_Gp(AILEmitterCtx Context, string Name)
|
||||
{
|
||||
AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp;
|
||||
|
||||
EmitVectorExtractF(Context, Op.Rn, 0, Op.Size);
|
||||
|
||||
EmitUnaryMathCall(Context, Name);
|
||||
|
||||
EmitScalarFcvts(Context, Op.Size, 0);
|
||||
|
||||
if (Context.CurrOp.RegisterSize == ARegisterSize.Int32)
|
||||
{
|
||||
Context.Emit(OpCodes.Conv_U8);
|
||||
}
|
||||
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
|
||||
private static void EmitFcvtz__Gp(AILEmitterCtx Context, bool Signed)
|
||||
{
|
||||
AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp;
|
||||
|
||||
EmitVectorExtractF(Context, Op.Rn, 0, Op.Size);
|
||||
|
||||
if (Signed)
|
||||
{
|
||||
EmitScalarFcvts(Context, Op.Size, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitScalarFcvtu(Context, Op.Size, 0);
|
||||
}
|
||||
|
||||
if (Context.CurrOp.RegisterSize == ARegisterSize.Int32)
|
||||
{
|
||||
Context.Emit(OpCodes.Conv_U8);
|
||||
}
|
||||
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
|
||||
private static void EmitFcvtz__Gp_Fix(AILEmitterCtx Context, bool Signed)
|
||||
{
|
||||
AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp;
|
||||
|
||||
EmitVectorExtractF(Context, Op.Rn, 0, Op.Size);
|
||||
|
||||
if (Signed)
|
||||
{
|
||||
EmitScalarFcvts(Context, Op.Size, Op.FBits);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitScalarFcvtu(Context, Op.Size, Op.FBits);
|
||||
}
|
||||
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
|
||||
private static void EmitVectorCvtf(AILEmitterCtx Context, bool Signed)
|
||||
{
|
||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||
|
||||
int SizeF = Op.Size & 1;
|
||||
int SizeI = SizeF + 2;
|
||||
|
||||
int FBits = GetFBits(Context);
|
||||
|
||||
int Bytes = Context.CurrOp.GetBitsCount() >> 3;
|
||||
|
||||
for (int Index = 0; Index < (Bytes >> SizeI); Index++)
|
||||
{
|
||||
EmitVectorExtract(Context, Op.Rn, Index, SizeI, Signed);
|
||||
|
||||
if (!Signed)
|
||||
{
|
||||
Context.Emit(OpCodes.Conv_R_Un);
|
||||
}
|
||||
|
||||
Context.Emit(SizeF == 0
|
||||
? OpCodes.Conv_R4
|
||||
: OpCodes.Conv_R8);
|
||||
|
||||
EmitI2fFBitsMul(Context, SizeF, FBits);
|
||||
|
||||
EmitVectorInsertF(Context, Op.Rd, Index, SizeF);
|
||||
}
|
||||
|
||||
if (Op.RegisterSize == ARegisterSize.SIMD64)
|
||||
{
|
||||
EmitVectorZeroUpper(Context, Op.Rd);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitVectorFcvt(AILEmitterCtx Context, bool Signed)
|
||||
{
|
||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||
|
||||
int SizeF = Op.Size & 1;
|
||||
int SizeI = SizeF + 2;
|
||||
|
||||
int FBits = GetFBits(Context);
|
||||
|
||||
int Bytes = Context.CurrOp.GetBitsCount() >> 3;
|
||||
|
||||
for (int Index = 0; Index < (Bytes >> SizeI); Index++)
|
||||
{
|
||||
EmitVectorExtractF(Context, Op.Rn, Index, SizeF);
|
||||
|
||||
EmitF2iFBitsMul(Context, SizeF, FBits);
|
||||
|
||||
if (SizeF == 0)
|
||||
{
|
||||
ASoftFallback.EmitCall(Context, Signed
|
||||
? nameof(ASoftFallback.SatF32ToS32)
|
||||
: nameof(ASoftFallback.SatF32ToU32));
|
||||
}
|
||||
else /* if (SizeF == 1) */
|
||||
{
|
||||
ASoftFallback.EmitCall(Context, Signed
|
||||
? nameof(ASoftFallback.SatF64ToS64)
|
||||
: nameof(ASoftFallback.SatF64ToU64));
|
||||
}
|
||||
|
||||
EmitVectorInsert(Context, Op.Rd, Index, SizeI);
|
||||
}
|
||||
|
||||
if (Op.RegisterSize == ARegisterSize.SIMD64)
|
||||
{
|
||||
EmitVectorZeroUpper(Context, Op.Rd);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitScalarFcvts(AILEmitterCtx Context, int Size, int FBits)
|
||||
{
|
||||
if (Size < 0 || Size > 1)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(Size));
|
||||
}
|
||||
|
||||
EmitF2iFBitsMul(Context, Size, FBits);
|
||||
|
||||
if (Context.CurrOp.RegisterSize == ARegisterSize.Int32)
|
||||
{
|
||||
if (Size == 0)
|
||||
{
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatF32ToS32));
|
||||
}
|
||||
else /* if (Size == 1) */
|
||||
{
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatF64ToS32));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Size == 0)
|
||||
{
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatF32ToS64));
|
||||
}
|
||||
else /* if (Size == 1) */
|
||||
{
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatF64ToS64));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitScalarFcvtu(AILEmitterCtx Context, int Size, int FBits)
|
||||
{
|
||||
if (Size < 0 || Size > 1)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(Size));
|
||||
}
|
||||
|
||||
EmitF2iFBitsMul(Context, Size, FBits);
|
||||
|
||||
if (Context.CurrOp.RegisterSize == ARegisterSize.Int32)
|
||||
{
|
||||
if (Size == 0)
|
||||
{
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatF32ToU32));
|
||||
}
|
||||
else /* if (Size == 1) */
|
||||
{
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatF64ToU32));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Size == 0)
|
||||
{
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatF32ToU64));
|
||||
}
|
||||
else /* if (Size == 1) */
|
||||
{
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatF64ToU64));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitF2iFBitsMul(AILEmitterCtx Context, int Size, int FBits)
|
||||
{
|
||||
if (FBits != 0)
|
||||
{
|
||||
if (Size == 0)
|
||||
{
|
||||
Context.EmitLdc_R4(MathF.Pow(2, FBits));
|
||||
}
|
||||
else if (Size == 1)
|
||||
{
|
||||
Context.EmitLdc_R8(Math.Pow(2, FBits));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(Size));
|
||||
}
|
||||
|
||||
Context.Emit(OpCodes.Mul);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitI2fFBitsMul(AILEmitterCtx Context, int Size, int FBits)
|
||||
{
|
||||
if (FBits != 0)
|
||||
{
|
||||
if (Size == 0)
|
||||
{
|
||||
Context.EmitLdc_R4(1f / MathF.Pow(2, FBits));
|
||||
}
|
||||
else if (Size == 1)
|
||||
{
|
||||
Context.EmitLdc_R8(1 / Math.Pow(2, FBits));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(Size));
|
||||
}
|
||||
|
||||
Context.Emit(OpCodes.Mul);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
508
Ryujinx/Cpu/Instruction/AInstEmitSimdHelper.cs
Normal file
508
Ryujinx/Cpu/Instruction/AInstEmitSimdHelper.cs
Normal file
|
@ -0,0 +1,508 @@
|
|||
using ChocolArm64.Decoder;
|
||||
using ChocolArm64.State;
|
||||
using ChocolArm64.Translation;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
namespace ChocolArm64.Instruction
|
||||
{
|
||||
static class AInstEmitSimdHelper
|
||||
{
|
||||
[Flags]
|
||||
public enum OperFlags
|
||||
{
|
||||
Rd = 1 << 0,
|
||||
Rn = 1 << 1,
|
||||
Rm = 1 << 2,
|
||||
Ra = 1 << 3,
|
||||
|
||||
RnRm = Rn | Rm,
|
||||
RdRn = Rd | Rn,
|
||||
RaRnRm = Ra | Rn | Rm,
|
||||
RdRnRm = Rd | Rn | Rm
|
||||
}
|
||||
|
||||
public static int GetImmShl(AOpCodeSimdShImm Op)
|
||||
{
|
||||
return Op.Imm - (8 << Op.Size);
|
||||
}
|
||||
|
||||
public static int GetImmShr(AOpCodeSimdShImm Op)
|
||||
{
|
||||
return (8 << (Op.Size + 1)) - Op.Imm;
|
||||
}
|
||||
|
||||
public static void EmitUnaryMathCall(AILEmitterCtx Context, string Name)
|
||||
{
|
||||
IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp;
|
||||
|
||||
MethodInfo MthdInfo;
|
||||
|
||||
if (Op.Size == 0)
|
||||
{
|
||||
MthdInfo = typeof(MathF).GetMethod(Name, new Type[] { typeof(float) });
|
||||
}
|
||||
else if (Op.Size == 1)
|
||||
{
|
||||
MthdInfo = typeof(Math).GetMethod(Name, new Type[] { typeof(double) });
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
Context.EmitCall(MthdInfo);
|
||||
}
|
||||
|
||||
public static void EmitBinaryMathCall(AILEmitterCtx Context, string Name)
|
||||
{
|
||||
IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp;
|
||||
|
||||
MethodInfo MthdInfo;
|
||||
|
||||
if (Op.Size == 0)
|
||||
{
|
||||
MthdInfo = typeof(MathF).GetMethod(Name, new Type[] { typeof(float), typeof(float) });
|
||||
}
|
||||
else if (Op.Size == 1)
|
||||
{
|
||||
MthdInfo = typeof(Math).GetMethod(Name, new Type[] { typeof(double), typeof(double) });
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
Context.EmitCall(MthdInfo);
|
||||
}
|
||||
|
||||
public static void EmitScalarUnaryOpSx(AILEmitterCtx Context, Action Emit)
|
||||
{
|
||||
EmitScalarOp(Context, Emit, OperFlags.Rn, true);
|
||||
}
|
||||
|
||||
public static void EmitScalarBinaryOpSx(AILEmitterCtx Context, Action Emit)
|
||||
{
|
||||
EmitScalarOp(Context, Emit, OperFlags.RnRm, true);
|
||||
}
|
||||
|
||||
public static void EmitScalarUnaryOpZx(AILEmitterCtx Context, Action Emit)
|
||||
{
|
||||
EmitScalarOp(Context, Emit, OperFlags.Rn, false);
|
||||
}
|
||||
|
||||
public static void EmitScalarBinaryOpZx(AILEmitterCtx Context, Action Emit)
|
||||
{
|
||||
EmitScalarOp(Context, Emit, OperFlags.RnRm, false);
|
||||
}
|
||||
|
||||
public static void EmitScalarTernaryOpZx(AILEmitterCtx Context, Action Emit)
|
||||
{
|
||||
EmitScalarOp(Context, Emit, OperFlags.RdRnRm, false);
|
||||
}
|
||||
|
||||
public static void EmitScalarOp(AILEmitterCtx Context, Action Emit, OperFlags Opers, bool Signed)
|
||||
{
|
||||
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
||||
|
||||
if (Opers.HasFlag(OperFlags.Rd))
|
||||
{
|
||||
EmitVectorExtract(Context, Op.Rd, 0, Op.Size, Signed);
|
||||
}
|
||||
|
||||
if (Opers.HasFlag(OperFlags.Rn))
|
||||
{
|
||||
EmitVectorExtract(Context, Op.Rn, 0, Op.Size, Signed);
|
||||
}
|
||||
|
||||
if (Opers.HasFlag(OperFlags.Rm))
|
||||
{
|
||||
EmitVectorExtract(Context, Op.Rm, 0, Op.Size, Signed);
|
||||
}
|
||||
|
||||
Emit();
|
||||
|
||||
EmitScalarSet(Context, Op.Rd, Op.Size);
|
||||
}
|
||||
|
||||
public static void EmitScalarUnaryOpF(AILEmitterCtx Context, Action Emit)
|
||||
{
|
||||
EmitScalarOpF(Context, Emit, OperFlags.Rn);
|
||||
}
|
||||
|
||||
public static void EmitScalarBinaryOpF(AILEmitterCtx Context, Action Emit)
|
||||
{
|
||||
EmitScalarOpF(Context, Emit, OperFlags.RnRm);
|
||||
}
|
||||
|
||||
public static void EmitScalarTernaryRaOpF(AILEmitterCtx Context, Action Emit)
|
||||
{
|
||||
EmitScalarOpF(Context, Emit, OperFlags.RaRnRm);
|
||||
}
|
||||
|
||||
public static void EmitScalarOpF(AILEmitterCtx Context, Action Emit, OperFlags Opers)
|
||||
{
|
||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||
|
||||
int SizeF = Op.Size & 1;
|
||||
|
||||
if (Opers.HasFlag(OperFlags.Ra))
|
||||
{
|
||||
EmitVectorExtractF(Context, ((AOpCodeSimdReg)Op).Ra, 0, SizeF);
|
||||
}
|
||||
|
||||
if (Opers.HasFlag(OperFlags.Rn))
|
||||
{
|
||||
EmitVectorExtractF(Context, Op.Rn, 0, SizeF);
|
||||
}
|
||||
|
||||
if (Opers.HasFlag(OperFlags.Rm))
|
||||
{
|
||||
EmitVectorExtractF(Context, ((AOpCodeSimdReg)Op).Rm, 0, SizeF);
|
||||
}
|
||||
|
||||
Emit();
|
||||
|
||||
EmitScalarSetF(Context, Op.Rd, SizeF);
|
||||
}
|
||||
|
||||
public static void EmitVectorBinaryOpF(AILEmitterCtx Context, Action Emit)
|
||||
{
|
||||
EmitVectorOpF(Context, Emit, OperFlags.RnRm);
|
||||
}
|
||||
|
||||
public static void EmitVectorTernaryOpF(AILEmitterCtx Context, Action Emit)
|
||||
{
|
||||
EmitVectorOpF(Context, Emit, OperFlags.RdRnRm);
|
||||
}
|
||||
|
||||
public static void EmitVectorBinaryOpByElemF(AILEmitterCtx Context, Action Emit)
|
||||
{
|
||||
AOpCodeSimdRegElem Op = (AOpCodeSimdRegElem)Context.CurrOp;
|
||||
|
||||
EmitVectorOpF(Context, Emit, OperFlags.RnRm, Op.Index);
|
||||
}
|
||||
|
||||
public static void EmitVectorTernaryOpByElemF(AILEmitterCtx Context, Action Emit)
|
||||
{
|
||||
AOpCodeSimdRegElem Op = (AOpCodeSimdRegElem)Context.CurrOp;
|
||||
|
||||
EmitVectorOpF(Context, Emit, OperFlags.RdRnRm, Op.Index);
|
||||
}
|
||||
|
||||
public static void EmitVectorOpF(AILEmitterCtx Context, Action Emit, OperFlags Opers, int Elem = -1)
|
||||
{
|
||||
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
||||
|
||||
int SizeF = Op.Size & 1;
|
||||
|
||||
int Bytes = Context.CurrOp.GetBitsCount() >> 3;
|
||||
|
||||
for (int Index = 0; Index < (Bytes >> SizeF + 2); Index++)
|
||||
{
|
||||
if (Opers.HasFlag(OperFlags.Rd))
|
||||
{
|
||||
EmitVectorExtractF(Context, Op.Rd, Index, SizeF);
|
||||
}
|
||||
|
||||
if (Opers.HasFlag(OperFlags.Rn))
|
||||
{
|
||||
EmitVectorExtractF(Context, Op.Rn, Index, SizeF);
|
||||
}
|
||||
|
||||
if (Opers.HasFlag(OperFlags.Rm))
|
||||
{
|
||||
if (Elem != -1)
|
||||
{
|
||||
EmitVectorExtractF(Context, Op.Rm, Elem, SizeF);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitVectorExtractF(Context, Op.Rm, Index, SizeF);
|
||||
}
|
||||
}
|
||||
|
||||
Emit();
|
||||
|
||||
EmitVectorInsertF(Context, Op.Rd, Index, SizeF);
|
||||
}
|
||||
|
||||
if (Op.RegisterSize == ARegisterSize.SIMD64)
|
||||
{
|
||||
EmitVectorZeroUpper(Context, Op.Rd);
|
||||
}
|
||||
}
|
||||
|
||||
public static void EmitVectorUnaryOpSx(AILEmitterCtx Context, Action Emit)
|
||||
{
|
||||
EmitVectorOp(Context, Emit, OperFlags.Rn, true);
|
||||
}
|
||||
|
||||
public static void EmitVectorBinaryOpSx(AILEmitterCtx Context, Action Emit)
|
||||
{
|
||||
EmitVectorOp(Context, Emit, OperFlags.RnRm, true);
|
||||
}
|
||||
|
||||
public static void EmitVectorUnaryOpZx(AILEmitterCtx Context, Action Emit)
|
||||
{
|
||||
EmitVectorOp(Context, Emit, OperFlags.Rn, false);
|
||||
}
|
||||
|
||||
public static void EmitVectorBinaryOpZx(AILEmitterCtx Context, Action Emit)
|
||||
{
|
||||
EmitVectorOp(Context, Emit, OperFlags.RnRm, false);
|
||||
}
|
||||
|
||||
public static void EmitVectorTernaryOpZx(AILEmitterCtx Context, Action Emit)
|
||||
{
|
||||
EmitVectorOp(Context, Emit, OperFlags.RdRnRm, false);
|
||||
}
|
||||
|
||||
public static void EmitVectorOp(AILEmitterCtx Context, Action Emit, OperFlags Opers, bool Signed)
|
||||
{
|
||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||
|
||||
int Bytes = Context.CurrOp.GetBitsCount() >> 3;
|
||||
|
||||
for (int Index = 0; Index < (Bytes >> Op.Size); Index++)
|
||||
{
|
||||
if (Opers.HasFlag(OperFlags.Rd))
|
||||
{
|
||||
EmitVectorExtract(Context, Op.Rd, Index, Op.Size, Signed);
|
||||
}
|
||||
|
||||
if (Opers.HasFlag(OperFlags.Rn))
|
||||
{
|
||||
EmitVectorExtract(Context, Op.Rn, Index, Op.Size, Signed);
|
||||
}
|
||||
|
||||
if (Opers.HasFlag(OperFlags.Rm))
|
||||
{
|
||||
EmitVectorExtract(Context, ((AOpCodeSimdReg)Op).Rm, Index, Op.Size, Signed);
|
||||
}
|
||||
|
||||
Emit();
|
||||
|
||||
EmitVectorInsert(Context, Op.Rd, Index, Op.Size);
|
||||
}
|
||||
|
||||
if (Op.RegisterSize == ARegisterSize.SIMD64)
|
||||
{
|
||||
EmitVectorZeroUpper(Context, Op.Rd);
|
||||
}
|
||||
}
|
||||
|
||||
public static void EmitVectorImmUnaryOp(AILEmitterCtx Context, Action Emit)
|
||||
{
|
||||
EmitVectorImmOp(Context, Emit, false);
|
||||
}
|
||||
|
||||
public static void EmitVectorImmBinaryOp(AILEmitterCtx Context, Action Emit)
|
||||
{
|
||||
EmitVectorImmOp(Context, Emit, true);
|
||||
}
|
||||
|
||||
public static void EmitVectorImmOp(AILEmitterCtx Context, Action Emit, bool Binary)
|
||||
{
|
||||
AOpCodeSimdImm Op = (AOpCodeSimdImm)Context.CurrOp;
|
||||
|
||||
int Bytes = Context.CurrOp.GetBitsCount() >> 3;
|
||||
|
||||
for (int Index = 0; Index < (Bytes >> Op.Size); Index++)
|
||||
{
|
||||
if (Binary)
|
||||
{
|
||||
EmitVectorExtractZx(Context, Op.Rd, Index, Op.Size);
|
||||
}
|
||||
|
||||
Context.EmitLdc_I8(Op.Imm);
|
||||
|
||||
Emit();
|
||||
|
||||
EmitVectorInsert(Context, Op.Rd, Index, Op.Size);
|
||||
}
|
||||
|
||||
if (Op.RegisterSize == ARegisterSize.SIMD64)
|
||||
{
|
||||
EmitVectorZeroUpper(Context, Op.Rd);
|
||||
}
|
||||
}
|
||||
|
||||
public static void EmitVectorWidenBinaryOpSx(AILEmitterCtx Context, Action Emit)
|
||||
{
|
||||
EmitVectorWidenBinaryOp(Context, Emit, true);
|
||||
}
|
||||
|
||||
public static void EmitVectorWidenBinaryOpZx(AILEmitterCtx Context, Action Emit)
|
||||
{
|
||||
EmitVectorWidenBinaryOp(Context, Emit, false);
|
||||
}
|
||||
|
||||
public static void EmitVectorWidenBinaryOp(AILEmitterCtx Context, Action Emit, bool Signed)
|
||||
{
|
||||
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
||||
|
||||
int Elems = 8 >> Op.Size;
|
||||
|
||||
int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0;
|
||||
|
||||
for (int Index = 0; Index < Elems; Index++)
|
||||
{
|
||||
EmitVectorExtract(Context, Op.Rn, Index, Op.Size + 1, Signed);
|
||||
EmitVectorExtract(Context, Op.Rm, Part + Index, Op.Size, Signed);
|
||||
|
||||
Emit();
|
||||
|
||||
EmitVectorInsertTmp(Context, Index, Op.Size + 1);
|
||||
}
|
||||
|
||||
Context.EmitLdvectmp();
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void EmitScalarSet(AILEmitterCtx Context, int Reg, int Size)
|
||||
{
|
||||
EmitVectorZeroAll(Context, Reg);
|
||||
EmitVectorInsert(Context, Reg, 0, Size);
|
||||
}
|
||||
|
||||
public static void EmitScalarSetF(AILEmitterCtx Context, int Reg, int Size)
|
||||
{
|
||||
EmitVectorZeroAll(Context, Reg);
|
||||
EmitVectorInsertF(Context, Reg, 0, Size);
|
||||
}
|
||||
|
||||
public static void EmitVectorExtractSx(AILEmitterCtx Context, int Reg, int Index, int Size)
|
||||
{
|
||||
EmitVectorExtract(Context, Reg, Index, Size, true);
|
||||
}
|
||||
|
||||
public static void EmitVectorExtractZx(AILEmitterCtx Context, int Reg, int Index, int Size)
|
||||
{
|
||||
EmitVectorExtract(Context, Reg, Index, Size, false);
|
||||
}
|
||||
|
||||
public static void EmitVectorExtract(AILEmitterCtx Context, int Reg, int Index, int Size, bool Signed)
|
||||
{
|
||||
if (Size < 0 || Size > 3)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(Size));
|
||||
}
|
||||
|
||||
IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Reg);
|
||||
Context.EmitLdc_I4(Index);
|
||||
Context.EmitLdc_I4(Size);
|
||||
|
||||
ASoftFallback.EmitCall(Context, Signed
|
||||
? nameof(ASoftFallback.VectorExtractIntSx)
|
||||
: nameof(ASoftFallback.VectorExtractIntZx));
|
||||
}
|
||||
|
||||
public static void EmitVectorExtractF(AILEmitterCtx Context, int Reg, int Index, int Size)
|
||||
{
|
||||
Context.EmitLdvec(Reg);
|
||||
Context.EmitLdc_I4(Index);
|
||||
|
||||
if (Size == 0)
|
||||
{
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.VectorExtractSingle));
|
||||
}
|
||||
else if (Size == 1)
|
||||
{
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.VectorExtractDouble));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(Size));
|
||||
}
|
||||
}
|
||||
|
||||
public static void EmitVectorZeroAll(AILEmitterCtx Context, int Rd)
|
||||
{
|
||||
EmitVectorZeroLower(Context, Rd);
|
||||
EmitVectorZeroUpper(Context, Rd);
|
||||
}
|
||||
|
||||
public static void EmitVectorZeroLower(AILEmitterCtx Context, int Rd)
|
||||
{
|
||||
EmitVectorInsert(Context, Rd, 0, 3, 0);
|
||||
}
|
||||
|
||||
public static void EmitVectorZeroUpper(AILEmitterCtx Context, int Rd)
|
||||
{
|
||||
EmitVectorInsert(Context, Rd, 1, 3, 0);
|
||||
}
|
||||
|
||||
public static void EmitVectorInsert(AILEmitterCtx Context, int Reg, int Index, int Size)
|
||||
{
|
||||
if (Size < 0 || Size > 3)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(Size));
|
||||
}
|
||||
|
||||
Context.EmitLdvec(Reg);
|
||||
Context.EmitLdc_I4(Index);
|
||||
Context.EmitLdc_I4(Size);
|
||||
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.VectorInsertInt));
|
||||
|
||||
Context.EmitStvec(Reg);
|
||||
}
|
||||
|
||||
public static void EmitVectorInsertTmp(AILEmitterCtx Context, int Index, int Size)
|
||||
{
|
||||
if (Size < 0 || Size > 3)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(Size));
|
||||
}
|
||||
|
||||
Context.EmitLdvectmp();
|
||||
Context.EmitLdc_I4(Index);
|
||||
Context.EmitLdc_I4(Size);
|
||||
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.VectorInsertInt));
|
||||
|
||||
Context.EmitStvectmp();
|
||||
}
|
||||
|
||||
public static void EmitVectorInsert(AILEmitterCtx Context, int Reg, int Index, int Size, long Value)
|
||||
{
|
||||
if (Size < 0 || Size > 3)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(Size));
|
||||
}
|
||||
|
||||
Context.EmitLdc_I8(Value);
|
||||
Context.EmitLdvec(Reg);
|
||||
Context.EmitLdc_I4(Index);
|
||||
Context.EmitLdc_I4(Size);
|
||||
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.VectorInsertInt));
|
||||
|
||||
Context.EmitStvec(Reg);
|
||||
}
|
||||
|
||||
public static void EmitVectorInsertF(AILEmitterCtx Context, int Reg, int Index, int Size)
|
||||
{
|
||||
Context.EmitLdvec(Reg);
|
||||
Context.EmitLdc_I4(Index);
|
||||
|
||||
if (Size == 0)
|
||||
{
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.VectorInsertSingle));
|
||||
}
|
||||
else if (Size == 1)
|
||||
{
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.VectorInsertDouble));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(Size));
|
||||
}
|
||||
|
||||
Context.EmitStvec(Reg);
|
||||
}
|
||||
}
|
||||
}
|
69
Ryujinx/Cpu/Instruction/AInstEmitSimdLogical.cs
Normal file
69
Ryujinx/Cpu/Instruction/AInstEmitSimdLogical.cs
Normal file
|
@ -0,0 +1,69 @@
|
|||
using ChocolArm64.Translation;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
using static ChocolArm64.Instruction.AInstEmitSimdHelper;
|
||||
|
||||
namespace ChocolArm64.Instruction
|
||||
{
|
||||
static partial class AInstEmit
|
||||
{
|
||||
public static void And_V(AILEmitterCtx Context)
|
||||
{
|
||||
EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.And));
|
||||
}
|
||||
|
||||
public static void Bic_V(AILEmitterCtx Context)
|
||||
{
|
||||
EmitVectorBinaryOpZx(Context, () =>
|
||||
{
|
||||
Context.Emit(OpCodes.Not);
|
||||
Context.Emit(OpCodes.And);
|
||||
});
|
||||
}
|
||||
|
||||
public static void Bic_Vi(AILEmitterCtx Context)
|
||||
{
|
||||
EmitVectorImmBinaryOp(Context, () =>
|
||||
{
|
||||
Context.Emit(OpCodes.Not);
|
||||
Context.Emit(OpCodes.And);
|
||||
});
|
||||
}
|
||||
|
||||
public static void Bsl_V(AILEmitterCtx Context)
|
||||
{
|
||||
EmitVectorTernaryOpZx(Context, () =>
|
||||
{
|
||||
Context.EmitSttmp();
|
||||
Context.EmitLdtmp();
|
||||
|
||||
Context.Emit(OpCodes.Xor);
|
||||
Context.Emit(OpCodes.And);
|
||||
|
||||
Context.EmitLdtmp();
|
||||
|
||||
Context.Emit(OpCodes.Xor);
|
||||
});
|
||||
}
|
||||
|
||||
public static void Eor_V(AILEmitterCtx Context)
|
||||
{
|
||||
EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.Xor));
|
||||
}
|
||||
|
||||
public static void Not_V(AILEmitterCtx Context)
|
||||
{
|
||||
EmitVectorUnaryOpZx(Context, () => Context.Emit(OpCodes.Not));
|
||||
}
|
||||
|
||||
public static void Orr_V(AILEmitterCtx Context)
|
||||
{
|
||||
EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.Or));
|
||||
}
|
||||
|
||||
public static void Orr_Vi(AILEmitterCtx Context)
|
||||
{
|
||||
EmitVectorImmBinaryOp(Context, () => Context.Emit(OpCodes.Or));
|
||||
}
|
||||
}
|
||||
}
|
155
Ryujinx/Cpu/Instruction/AInstEmitSimdMemory.cs
Normal file
155
Ryujinx/Cpu/Instruction/AInstEmitSimdMemory.cs
Normal file
|
@ -0,0 +1,155 @@
|
|||
using ChocolArm64.Decoder;
|
||||
using ChocolArm64.State;
|
||||
using ChocolArm64.Translation;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
using static ChocolArm64.Instruction.AInstEmitMemoryHelper;
|
||||
using static ChocolArm64.Instruction.AInstEmitSimdHelper;
|
||||
|
||||
namespace ChocolArm64.Instruction
|
||||
{
|
||||
static partial class AInstEmit
|
||||
{
|
||||
public static void Ld__Vms(AILEmitterCtx Context)
|
||||
{
|
||||
EmitSimdMemMs(Context, IsLoad: true);
|
||||
}
|
||||
|
||||
public static void Ld__Vss(AILEmitterCtx Context)
|
||||
{
|
||||
EmitSimdMemSs(Context, IsLoad: true);
|
||||
}
|
||||
|
||||
public static void St__Vms(AILEmitterCtx Context)
|
||||
{
|
||||
EmitSimdMemMs(Context, IsLoad: false);
|
||||
}
|
||||
|
||||
public static void St__Vss(AILEmitterCtx Context)
|
||||
{
|
||||
EmitSimdMemSs(Context, IsLoad: false);
|
||||
}
|
||||
|
||||
private static void EmitSimdMemMs(AILEmitterCtx Context, bool IsLoad)
|
||||
{
|
||||
AOpCodeSimdMemMs Op = (AOpCodeSimdMemMs)Context.CurrOp;
|
||||
|
||||
int Offset = 0;
|
||||
|
||||
for (int Rep = 0; Rep < Op.Reps; Rep++)
|
||||
for (int Elem = 0; Elem < Op.Elems; Elem++)
|
||||
for (int SElem = 0; SElem < Op.SElems; SElem++)
|
||||
{
|
||||
int Rtt = (Op.Rt + Rep + SElem) & 0x1f;
|
||||
|
||||
if (IsLoad)
|
||||
{
|
||||
Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
|
||||
Context.EmitLdint(Op.Rn);
|
||||
Context.EmitLdc_I8(Offset);
|
||||
|
||||
Context.Emit(OpCodes.Add);
|
||||
|
||||
EmitReadZxCall(Context, Op.Size);
|
||||
|
||||
EmitVectorInsert(Context, Rtt, Elem, Op.Size);
|
||||
|
||||
if (Op.RegisterSize == ARegisterSize.SIMD64 && Elem == Op.Elems - 1)
|
||||
{
|
||||
EmitVectorZeroUpper(Context, Rtt);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
|
||||
Context.EmitLdint(Op.Rn);
|
||||
Context.EmitLdc_I8(Offset);
|
||||
|
||||
Context.Emit(OpCodes.Add);
|
||||
|
||||
EmitVectorExtractZx(Context, Rtt, Elem, Op.Size);
|
||||
|
||||
EmitWriteCall(Context, Op.Size);
|
||||
}
|
||||
|
||||
Offset += 1 << Op.Size;
|
||||
}
|
||||
|
||||
if (Op.WBack)
|
||||
{
|
||||
EmitSimdMemWBack(Context, Offset);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitSimdMemSs(AILEmitterCtx Context, bool IsLoad)
|
||||
{
|
||||
AOpCodeSimdMemSs Op = (AOpCodeSimdMemSs)Context.CurrOp;
|
||||
|
||||
//TODO: Replicate mode.
|
||||
|
||||
int Offset = 0;
|
||||
|
||||
for (int SElem = 0; SElem < Op.SElems; SElem++)
|
||||
{
|
||||
int Rt = (Op.Rt + SElem) & 0x1f;
|
||||
|
||||
if (IsLoad)
|
||||
{
|
||||
Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
|
||||
Context.EmitLdint(Op.Rn);
|
||||
Context.EmitLdc_I8(Offset);
|
||||
|
||||
Context.Emit(OpCodes.Add);
|
||||
|
||||
EmitReadZxCall(Context, Op.Size);
|
||||
|
||||
EmitVectorInsert(Context, Rt, Op.Index, Op.Size);
|
||||
|
||||
if (Op.RegisterSize == ARegisterSize.SIMD64)
|
||||
{
|
||||
EmitVectorZeroUpper(Context, Rt);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
|
||||
Context.EmitLdint(Op.Rn);
|
||||
Context.EmitLdc_I8(Offset);
|
||||
|
||||
Context.Emit(OpCodes.Add);
|
||||
|
||||
EmitVectorExtractZx(Context, Rt, Op.Index, Op.Size);
|
||||
|
||||
EmitWriteCall(Context, Op.Size);
|
||||
}
|
||||
|
||||
Offset += 1 << Op.Size;
|
||||
}
|
||||
|
||||
if (Op.WBack)
|
||||
{
|
||||
EmitSimdMemWBack(Context, Offset);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitSimdMemWBack(AILEmitterCtx Context, int Offset)
|
||||
{
|
||||
AOpCodeMemReg Op = (AOpCodeMemReg)Context.CurrOp;
|
||||
|
||||
Context.EmitLdint(Op.Rn);
|
||||
|
||||
if (Op.Rm != ARegisters.ZRIndex)
|
||||
{
|
||||
Context.EmitLdint(Op.Rm);
|
||||
}
|
||||
else
|
||||
{
|
||||
Context.EmitLdc_I8(Offset);
|
||||
}
|
||||
|
||||
Context.Emit(OpCodes.Add);
|
||||
|
||||
Context.EmitStint(Op.Rn);
|
||||
}
|
||||
}
|
||||
}
|
275
Ryujinx/Cpu/Instruction/AInstEmitSimdMove.cs
Normal file
275
Ryujinx/Cpu/Instruction/AInstEmitSimdMove.cs
Normal file
|
@ -0,0 +1,275 @@
|
|||
using ChocolArm64.Decoder;
|
||||
using ChocolArm64.State;
|
||||
using ChocolArm64.Translation;
|
||||
using System;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
using static ChocolArm64.Instruction.AInstEmitSimdHelper;
|
||||
|
||||
namespace ChocolArm64.Instruction
|
||||
{
|
||||
static partial class AInstEmit
|
||||
{
|
||||
public static void Dup_Gp(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp;
|
||||
|
||||
int Bytes = Context.CurrOp.GetBitsCount() >> 3;
|
||||
|
||||
for (int Index = 0; Index < (Bytes >> Op.Size); Index++)
|
||||
{
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
|
||||
EmitVectorInsert(Context, Op.Rd, Index, Op.Size);
|
||||
}
|
||||
|
||||
if (Op.RegisterSize == ARegisterSize.SIMD64)
|
||||
{
|
||||
EmitVectorZeroUpper(Context, Op.Rd);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Dup_S(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp;
|
||||
|
||||
EmitVectorExtractZx(Context, Op.Rn, Op.DstIndex, Op.Size);
|
||||
|
||||
EmitScalarSet(Context, Op.Rd, Op.Size);
|
||||
}
|
||||
|
||||
public static void Dup_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp;
|
||||
|
||||
int Bytes = Context.CurrOp.GetBitsCount() >> 3;
|
||||
|
||||
for (int Index = 0; Index < (Bytes >> Op.Size); Index++)
|
||||
{
|
||||
EmitVectorExtractZx(Context, Op.Rn, Op.DstIndex, Op.Size);
|
||||
|
||||
EmitVectorInsert(Context, Op.Rd, Index, Op.Size);
|
||||
}
|
||||
|
||||
if (Op.RegisterSize == ARegisterSize.SIMD64)
|
||||
{
|
||||
EmitVectorZeroUpper(Context, Op.Rd);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Fcsel_S(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdFcond Op = (AOpCodeSimdFcond)Context.CurrOp;
|
||||
|
||||
AILLabel LblTrue = new AILLabel();
|
||||
AILLabel LblEnd = new AILLabel();
|
||||
|
||||
Context.EmitCondBranch(LblTrue, Op.Cond);
|
||||
|
||||
EmitVectorExtractF(Context, Op.Rm, 0, Op.Size);
|
||||
|
||||
Context.Emit(OpCodes.Br_S, LblEnd);
|
||||
|
||||
Context.MarkLabel(LblTrue);
|
||||
|
||||
EmitVectorExtractF(Context, Op.Rn, 0, Op.Size);
|
||||
|
||||
Context.MarkLabel(LblEnd);
|
||||
|
||||
EmitScalarSetF(Context, Op.Rd, Op.Size);
|
||||
}
|
||||
|
||||
public static void Fmov_Ftoi(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp;
|
||||
|
||||
EmitVectorExtractZx(Context, Op.Rn, 0, 3);
|
||||
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Fmov_Ftoi1(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp;
|
||||
|
||||
EmitVectorExtractZx(Context, Op.Rn, 1, 3);
|
||||
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Fmov_Itof(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp;
|
||||
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
|
||||
EmitScalarSet(Context, Op.Rd, 3);
|
||||
}
|
||||
|
||||
public static void Fmov_Itof1(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp;
|
||||
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
|
||||
EmitVectorInsert(Context, Op.Rd, 1, 3);
|
||||
}
|
||||
|
||||
public static void Fmov_S(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||
|
||||
EmitVectorExtractF(Context, Op.Rn, 0, Op.Size);
|
||||
|
||||
EmitScalarSetF(Context, Op.Rd, Op.Size);
|
||||
}
|
||||
|
||||
public static void Fmov_Si(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdFmov Op = (AOpCodeSimdFmov)Context.CurrOp;
|
||||
|
||||
Context.EmitLdc_I8(Op.Imm);
|
||||
|
||||
EmitScalarSet(Context, Op.Rd, Op.Size + 2);
|
||||
}
|
||||
|
||||
public static void Fmov_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdImm Op = (AOpCodeSimdImm)Context.CurrOp;
|
||||
|
||||
for (int Index = 0; Index < (4 >> Op.Size); Index++)
|
||||
{
|
||||
Context.EmitLdc_I8(Op.Imm);
|
||||
|
||||
EmitVectorInsert(Context, Op.Rd, Index, Op.Size + 2);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Ins_Gp(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp;
|
||||
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
|
||||
EmitVectorInsert(Context, Op.Rd, Op.DstIndex, Op.Size);
|
||||
}
|
||||
|
||||
public static void Ins_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp;
|
||||
|
||||
EmitVectorExtractZx(Context, Op.Rn, Op.SrcIndex, Op.Size);
|
||||
|
||||
EmitVectorInsert(Context, Op.Rd, Op.DstIndex, Op.Size);
|
||||
}
|
||||
|
||||
public static void Movi_V(AILEmitterCtx Context)
|
||||
{
|
||||
EmitVectorImmUnaryOp(Context, () => { });
|
||||
}
|
||||
|
||||
public static void Mvni_V(AILEmitterCtx Context)
|
||||
{
|
||||
EmitVectorImmUnaryOp(Context, () => Context.Emit(OpCodes.Not));
|
||||
}
|
||||
|
||||
public static void Tbl_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdTbl Op = (AOpCodeSimdTbl)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rm);
|
||||
|
||||
for (int Index = 0; Index < Op.Size; Index++)
|
||||
{
|
||||
Context.EmitLdvec((Op.Rn + Index) & 0x1f);
|
||||
}
|
||||
|
||||
switch (Op.Size)
|
||||
{
|
||||
case 1: ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Tbl1_V64),
|
||||
nameof(ASoftFallback.Tbl1_V128)); break;
|
||||
|
||||
case 2: ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Tbl2_V64),
|
||||
nameof(ASoftFallback.Tbl2_V128)); break;
|
||||
|
||||
case 3: ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Tbl3_V64),
|
||||
nameof(ASoftFallback.Tbl3_V128)); break;
|
||||
|
||||
case 4: ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Tbl4_V64),
|
||||
nameof(ASoftFallback.Tbl4_V128)); break;
|
||||
|
||||
default: throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Umov_S(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp;
|
||||
|
||||
EmitVectorExtractZx(Context, Op.Rn, Op.DstIndex, Op.Size);
|
||||
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Uzp1_V(AILEmitterCtx Context)
|
||||
{
|
||||
EmitVectorUnzip(Context, Part: 0);
|
||||
}
|
||||
|
||||
public static void Uzp2_V(AILEmitterCtx Context)
|
||||
{
|
||||
EmitVectorUnzip(Context, Part: 1);
|
||||
}
|
||||
|
||||
public static void Xtn_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||
|
||||
int Elems = 8 >> Op.Size;
|
||||
|
||||
int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0;
|
||||
|
||||
for (int Index = 0; Index < Elems; Index++)
|
||||
{
|
||||
EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size + 1);
|
||||
|
||||
EmitVectorInsert(Context, Op.Rd, Part + Index, Op.Size);
|
||||
}
|
||||
|
||||
if (Part == 0)
|
||||
{
|
||||
EmitVectorZeroUpper(Context, Op.Rd);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitVectorUnzip(AILEmitterCtx Context, int Part)
|
||||
{
|
||||
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
||||
|
||||
int Bytes = Context.CurrOp.GetBitsCount() >> 3;
|
||||
|
||||
int Elems = Bytes >> Op.Size;
|
||||
int Half = Elems >> 1;
|
||||
|
||||
for (int Index = 0; Index < Elems; Index++)
|
||||
{
|
||||
int Elem = Part + ((Index & (Half - 1)) << 1);
|
||||
|
||||
EmitVectorExtractZx(Context, Index < Half ? Op.Rn : Op.Rm, Elem, Op.Size);
|
||||
|
||||
EmitVectorInsert(Context, Op.Rd, Index, Op.Size);
|
||||
}
|
||||
|
||||
if (Op.RegisterSize == ARegisterSize.SIMD64)
|
||||
{
|
||||
EmitVectorZeroUpper(Context, Op.Rd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
306
Ryujinx/Cpu/Instruction/AInstEmitSimdShift.cs
Normal file
306
Ryujinx/Cpu/Instruction/AInstEmitSimdShift.cs
Normal file
|
@ -0,0 +1,306 @@
|
|||
using ChocolArm64.Decoder;
|
||||
using ChocolArm64.State;
|
||||
using ChocolArm64.Translation;
|
||||
using System;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
using static ChocolArm64.Instruction.AInstEmitSimdHelper;
|
||||
|
||||
namespace ChocolArm64.Instruction
|
||||
{
|
||||
static partial class AInstEmit
|
||||
{
|
||||
[Flags]
|
||||
private enum ShrFlags
|
||||
{
|
||||
None = 0,
|
||||
Signed = 1 << 0,
|
||||
Rounding = 1 << 1,
|
||||
Accumulate = 1 << 2
|
||||
}
|
||||
|
||||
public static void Shl_S(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
|
||||
|
||||
EmitVectorExtractZx(Context, Op.Rn, 0, Op.Size);
|
||||
|
||||
Context.EmitLdc_I4(GetImmShl(Op));
|
||||
|
||||
Context.Emit(OpCodes.Shl);
|
||||
|
||||
EmitScalarSet(Context, Op.Rd, Op.Size);
|
||||
}
|
||||
|
||||
public static void Shl_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
|
||||
|
||||
int Shift = Op.Imm - (8 << Op.Size);
|
||||
|
||||
EmitVectorShImmBinaryZx(Context, () => Context.Emit(OpCodes.Shl), Shift);
|
||||
}
|
||||
|
||||
public static void Shrn_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
|
||||
|
||||
int Shift = (8 << (Op.Size + 1)) - Op.Imm;
|
||||
|
||||
EmitVectorShImmNarrowBinaryZx(Context, () => Context.Emit(OpCodes.Shr_Un), Shift);
|
||||
}
|
||||
|
||||
public static void Sshl_V(AILEmitterCtx Context)
|
||||
{
|
||||
EmitVectorShl(Context, Signed: true);
|
||||
}
|
||||
|
||||
public static void Sshll_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
|
||||
|
||||
int Shift = Op.Imm - (8 << Op.Size);
|
||||
|
||||
EmitVectorShImmWidenBinarySx(Context, () => Context.Emit(OpCodes.Shl), Shift);
|
||||
}
|
||||
|
||||
public static void Sshr_S(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
|
||||
|
||||
EmitVectorExtractSx(Context, Op.Rn, 0, Op.Size);
|
||||
|
||||
Context.EmitLdc_I4(GetImmShr(Op));
|
||||
|
||||
Context.Emit(OpCodes.Shr);
|
||||
|
||||
EmitScalarSet(Context, Op.Rd, Op.Size);
|
||||
}
|
||||
|
||||
public static void Sshr_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
|
||||
|
||||
int Shift = (8 << (Op.Size + 1)) - Op.Imm;
|
||||
|
||||
EmitVectorShImmBinarySx(Context, () => Context.Emit(OpCodes.Shr), Shift);
|
||||
}
|
||||
|
||||
public static void Ushl_V(AILEmitterCtx Context)
|
||||
{
|
||||
EmitVectorShl(Context, Signed: false);
|
||||
}
|
||||
|
||||
public static void Ushll_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
|
||||
|
||||
int Shift = Op.Imm - (8 << Op.Size);
|
||||
|
||||
EmitVectorShImmWidenBinaryZx(Context, () => Context.Emit(OpCodes.Shl), Shift);
|
||||
}
|
||||
|
||||
public static void Ushr_V(AILEmitterCtx Context)
|
||||
{
|
||||
EmitVectorShr(Context, ShrFlags.None);
|
||||
}
|
||||
|
||||
public static void Usra_V(AILEmitterCtx Context)
|
||||
{
|
||||
EmitVectorShr(Context, ShrFlags.Accumulate);
|
||||
}
|
||||
|
||||
private static void EmitVectorShl(AILEmitterCtx Context, bool Signed)
|
||||
{
|
||||
//This instruction shifts the value on vector A by the number of bits
|
||||
//specified on the signed, lower 8 bits of vector B. If the shift value
|
||||
//is greater or equal to the data size of each lane, then the result is zero.
|
||||
//Additionally, negative shifts produces right shifts by the negated shift value.
|
||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||
|
||||
int MaxShift = 8 << Op.Size;
|
||||
|
||||
Action Emit = () =>
|
||||
{
|
||||
AILLabel LblShl = new AILLabel();
|
||||
AILLabel LblZero = new AILLabel();
|
||||
AILLabel LblEnd = new AILLabel();
|
||||
|
||||
void EmitShift(OpCode ILOp)
|
||||
{
|
||||
Context.Emit(OpCodes.Dup);
|
||||
|
||||
Context.EmitLdc_I4(MaxShift);
|
||||
|
||||
Context.Emit(OpCodes.Bge_S, LblZero);
|
||||
Context.Emit(ILOp);
|
||||
Context.Emit(OpCodes.Br_S, LblEnd);
|
||||
}
|
||||
|
||||
Context.Emit(OpCodes.Conv_I1);
|
||||
Context.Emit(OpCodes.Dup);
|
||||
|
||||
Context.EmitLdc_I4(0);
|
||||
|
||||
Context.Emit(OpCodes.Bge_S, LblShl);
|
||||
Context.Emit(OpCodes.Neg);
|
||||
|
||||
EmitShift(Signed
|
||||
? OpCodes.Shr
|
||||
: OpCodes.Shr_Un);
|
||||
|
||||
Context.MarkLabel(LblShl);
|
||||
|
||||
EmitShift(OpCodes.Shl);
|
||||
|
||||
Context.MarkLabel(LblZero);
|
||||
|
||||
Context.Emit(OpCodes.Pop);
|
||||
Context.Emit(OpCodes.Pop);
|
||||
|
||||
Context.EmitLdc_I8(0);
|
||||
|
||||
Context.MarkLabel(LblEnd);
|
||||
};
|
||||
|
||||
if (Signed)
|
||||
{
|
||||
EmitVectorBinaryOpSx(Context, Emit);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitVectorBinaryOpZx(Context, Emit);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitVectorShr(AILEmitterCtx Context, ShrFlags Flags)
|
||||
{
|
||||
AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
|
||||
|
||||
int Shift = (8 << (Op.Size + 1)) - Op.Imm;
|
||||
|
||||
if (Flags.HasFlag(ShrFlags.Accumulate))
|
||||
{
|
||||
Action Emit = () =>
|
||||
{
|
||||
Context.EmitLdc_I4(Shift);
|
||||
|
||||
Context.Emit(OpCodes.Shr_Un);
|
||||
Context.Emit(OpCodes.Add);
|
||||
};
|
||||
|
||||
EmitVectorOp(Context, Emit, OperFlags.RdRn, Signed: false);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitVectorUnaryOpZx(Context, () =>
|
||||
{
|
||||
Context.EmitLdc_I4(Shift);
|
||||
|
||||
Context.Emit(OpCodes.Shr_Un);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitVectorShImmBinarySx(AILEmitterCtx Context, Action Emit, int Imm)
|
||||
{
|
||||
EmitVectorShImmBinaryOp(Context, Emit, Imm, true);
|
||||
}
|
||||
|
||||
private static void EmitVectorShImmBinaryZx(AILEmitterCtx Context, Action Emit, int Imm)
|
||||
{
|
||||
EmitVectorShImmBinaryOp(Context, Emit, Imm, false);
|
||||
}
|
||||
|
||||
private static void EmitVectorShImmBinaryOp(AILEmitterCtx Context, Action Emit, int Imm, bool Signed)
|
||||
{
|
||||
AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
|
||||
|
||||
int Bytes = Context.CurrOp.GetBitsCount() >> 3;
|
||||
|
||||
for (int Index = 0; Index < (Bytes >> Op.Size); Index++)
|
||||
{
|
||||
EmitVectorExtract(Context, Op.Rn, Index, Op.Size, Signed);
|
||||
|
||||
Context.EmitLdc_I4(Imm);
|
||||
|
||||
Emit();
|
||||
|
||||
EmitVectorInsert(Context, Op.Rd, Index, Op.Size);
|
||||
}
|
||||
|
||||
if (Op.RegisterSize == ARegisterSize.SIMD64)
|
||||
{
|
||||
EmitVectorZeroUpper(Context, Op.Rd);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitVectorShImmNarrowBinarySx(AILEmitterCtx Context, Action Emit, int Imm)
|
||||
{
|
||||
EmitVectorShImmNarrowBinaryOp(Context, Emit, Imm, true);
|
||||
}
|
||||
|
||||
private static void EmitVectorShImmNarrowBinaryZx(AILEmitterCtx Context, Action Emit, int Imm)
|
||||
{
|
||||
EmitVectorShImmNarrowBinaryOp(Context, Emit, Imm, false);
|
||||
}
|
||||
|
||||
private static void EmitVectorShImmNarrowBinaryOp(AILEmitterCtx Context, Action Emit, int Imm, bool Signed)
|
||||
{
|
||||
AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
|
||||
|
||||
int Elems = 8 >> Op.Size;
|
||||
|
||||
int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0;
|
||||
|
||||
for (int Index = 0; Index < Elems; Index++)
|
||||
{
|
||||
EmitVectorExtract(Context, Op.Rn, Index, Op.Size + 1, Signed);
|
||||
|
||||
Context.EmitLdc_I4(Imm);
|
||||
|
||||
Emit();
|
||||
|
||||
EmitVectorInsert(Context, Op.Rd, Part + Index, Op.Size);
|
||||
}
|
||||
|
||||
if (Part == 0)
|
||||
{
|
||||
EmitVectorZeroUpper(Context, Op.Rd);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitVectorShImmWidenBinarySx(AILEmitterCtx Context, Action Emit, int Imm)
|
||||
{
|
||||
EmitVectorShImmWidenBinaryOp(Context, Emit, Imm, true);
|
||||
}
|
||||
|
||||
private static void EmitVectorShImmWidenBinaryZx(AILEmitterCtx Context, Action Emit, int Imm)
|
||||
{
|
||||
EmitVectorShImmWidenBinaryOp(Context, Emit, Imm, false);
|
||||
}
|
||||
|
||||
private static void EmitVectorShImmWidenBinaryOp(AILEmitterCtx Context, Action Emit, int Imm, bool Signed)
|
||||
{
|
||||
AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
|
||||
|
||||
int Elems = 8 >> Op.Size;
|
||||
|
||||
int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0;
|
||||
|
||||
for (int Index = 0; Index < Elems; Index++)
|
||||
{
|
||||
EmitVectorExtract(Context, Op.Rn, Part + Index, Op.Size, Signed);
|
||||
|
||||
Context.EmitLdc_I4(Imm);
|
||||
|
||||
Emit();
|
||||
|
||||
EmitVectorInsertTmp(Context, Index, Op.Size + 1);
|
||||
}
|
||||
|
||||
Context.EmitLdvectmp();
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
using ChocolArm64.State;
|
||||
using ChocolArm64.Translation;
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace ChocolArm64.Instruction
|
||||
{
|
||||
|
@ -97,142 +98,62 @@ namespace ChocolArm64.Instruction
|
|||
throw new ArgumentException(nameof(Size));
|
||||
}
|
||||
|
||||
public static int SatSingleToInt32(float Value, int FBits)
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int SatF32ToS32(float Value)
|
||||
{
|
||||
if (FBits != 0) Value *= MathF.Pow(2, FBits);
|
||||
|
||||
return Value > int.MaxValue ? int.MaxValue :
|
||||
Value < int.MinValue ? int.MinValue : (int)Value;
|
||||
}
|
||||
|
||||
public static long SatSingleToInt64(float Value, int FBits)
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static long SatF32ToS64(float Value)
|
||||
{
|
||||
if (FBits != 0) Value *= MathF.Pow(2, FBits);
|
||||
|
||||
return Value > long.MaxValue ? long.MaxValue :
|
||||
Value < long.MinValue ? long.MinValue : (long)Value;
|
||||
}
|
||||
|
||||
public static uint SatSingleToUInt32(float Value, int FBits)
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static uint SatF32ToU32(float Value)
|
||||
{
|
||||
if (FBits != 0) Value *= MathF.Pow(2, FBits);
|
||||
|
||||
return Value > uint.MaxValue ? uint.MaxValue :
|
||||
Value < uint.MinValue ? uint.MinValue : (uint)Value;
|
||||
}
|
||||
|
||||
public static ulong SatSingleToUInt64(float Value, int FBits)
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ulong SatF32ToU64(float Value)
|
||||
{
|
||||
if (FBits != 0) Value *= MathF.Pow(2, FBits);
|
||||
|
||||
return Value > ulong.MaxValue ? ulong.MaxValue :
|
||||
Value < ulong.MinValue ? ulong.MinValue : (ulong)Value;
|
||||
}
|
||||
|
||||
public static int SatDoubleToInt32(double Value, int FBits)
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int SatF64ToS32(double Value)
|
||||
{
|
||||
if (FBits != 0) Value *= Math.Pow(2, FBits);
|
||||
|
||||
return Value > int.MaxValue ? int.MaxValue :
|
||||
Value < int.MinValue ? int.MinValue : (int)Value;
|
||||
}
|
||||
|
||||
public static long SatDoubleToInt64(double Value, int FBits)
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static long SatF64ToS64(double Value)
|
||||
{
|
||||
if (FBits != 0) Value *= Math.Pow(2, FBits);
|
||||
|
||||
return Value > long.MaxValue ? long.MaxValue :
|
||||
Value < long.MinValue ? long.MinValue : (long)Value;
|
||||
}
|
||||
|
||||
public static uint SatDoubleToUInt32(double Value, int FBits)
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static uint SatF64ToU32(double Value)
|
||||
{
|
||||
if (FBits != 0) Value *= Math.Pow(2, FBits);
|
||||
|
||||
return Value > uint.MaxValue ? uint.MaxValue :
|
||||
Value < uint.MinValue ? uint.MinValue : (uint)Value;
|
||||
}
|
||||
|
||||
public static ulong SatDoubleToUInt64(double Value, int FBits)
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ulong SatF64ToU64(double Value)
|
||||
{
|
||||
if (FBits != 0) Value *= Math.Pow(2, FBits);
|
||||
|
||||
return Value > ulong.MaxValue ? ulong.MaxValue :
|
||||
Value < ulong.MinValue ? ulong.MinValue : (ulong)Value;
|
||||
}
|
||||
|
||||
public static float Int32ToSingle(int Value, int FBits)
|
||||
{
|
||||
float ValueF = Value;
|
||||
|
||||
if (FBits != 0) ValueF *= 1 / MathF.Pow(2, FBits);
|
||||
|
||||
return ValueF;
|
||||
}
|
||||
|
||||
public static float Int64ToSingle(long Value, int FBits)
|
||||
{
|
||||
float ValueF = Value;
|
||||
|
||||
if (FBits != 0) ValueF *= 1 / MathF.Pow(2, FBits);
|
||||
|
||||
return ValueF;
|
||||
}
|
||||
|
||||
public static float UInt32ToSingle(uint Value, int FBits)
|
||||
{
|
||||
float ValueF = Value;
|
||||
|
||||
if (FBits != 0) ValueF *= 1 / MathF.Pow(2, FBits);
|
||||
|
||||
return ValueF;
|
||||
}
|
||||
|
||||
public static float UInt64ToSingle(ulong Value, int FBits)
|
||||
{
|
||||
float ValueF = Value;
|
||||
|
||||
if (FBits != 0) ValueF *= 1 / MathF.Pow(2, FBits);
|
||||
|
||||
return ValueF;
|
||||
}
|
||||
|
||||
public static double Int32ToDouble(int Value, int FBits)
|
||||
{
|
||||
double ValueF = Value;
|
||||
|
||||
if (FBits != 0) ValueF *= 1 / Math.Pow(2, FBits);
|
||||
|
||||
return ValueF;
|
||||
}
|
||||
|
||||
public static double Int64ToDouble(long Value, int FBits)
|
||||
{
|
||||
double ValueF = Value;
|
||||
|
||||
if (FBits != 0) ValueF *= 1 / Math.Pow(2, FBits);
|
||||
|
||||
return ValueF;
|
||||
}
|
||||
|
||||
public static double UInt32ToDouble(uint Value, int FBits)
|
||||
{
|
||||
double ValueF = Value;
|
||||
|
||||
if (FBits != 0) ValueF *= 1 / Math.Pow(2, FBits);
|
||||
|
||||
return ValueF;
|
||||
}
|
||||
|
||||
public static double UInt64ToDouble(ulong Value, int FBits)
|
||||
{
|
||||
double ValueF = Value;
|
||||
|
||||
if (FBits != 0) ValueF *= 1 / Math.Pow(2, FBits);
|
||||
|
||||
return ValueF;
|
||||
}
|
||||
|
||||
public static ulong SMulHi128(ulong LHS, ulong RHS)
|
||||
{
|
||||
long LLo = (uint)(LHS >> 0);
|
||||
|
@ -271,14 +192,6 @@ namespace ChocolArm64.Instruction
|
|||
return ResHi;
|
||||
}
|
||||
|
||||
public static AVec Addp_S(AVec Vector, int Size)
|
||||
{
|
||||
ulong Low = ExtractVec(Vector, 0, Size);
|
||||
ulong High = ExtractVec(Vector, 1, Size);
|
||||
|
||||
return InsertVec(new AVec(), 0, Size, Low + High);
|
||||
}
|
||||
|
||||
public static int CountSetBits8(byte Value)
|
||||
{
|
||||
return (Value >> 0) & 1 + (Value >> 1) & 1 +
|
||||
|
@ -287,38 +200,6 @@ namespace ChocolArm64.Instruction
|
|||
(Value >> 6) & 1 + (Value >> 7);
|
||||
}
|
||||
|
||||
public static AVec Dup_Gp64(ulong Value, int Size)
|
||||
{
|
||||
return Dup_Gp(Value, Size, 8);
|
||||
}
|
||||
|
||||
public static AVec Dup_Gp128(ulong Value, int Size)
|
||||
{
|
||||
return Dup_Gp(Value, Size, 16);
|
||||
}
|
||||
|
||||
private static AVec Dup_Gp(ulong Value, int Size, int Bytes)
|
||||
{
|
||||
AVec Res = new AVec();
|
||||
|
||||
for (int Index = 0; Index < (Bytes >> Size); Index++)
|
||||
{
|
||||
Res = InsertVec(Res, Index, Size, Value);
|
||||
}
|
||||
|
||||
return Res;
|
||||
}
|
||||
|
||||
public static AVec Dup_S(AVec Vector, int Elem, int Size)
|
||||
{
|
||||
return InsertVec(new AVec(), 0, Size, ExtractVec(Vector, Elem, Size));
|
||||
}
|
||||
|
||||
public static AVec Fmov_S(ulong Value, int Elem, int Size)
|
||||
{
|
||||
return InsertVec(new AVec(), Elem, Size, Value);
|
||||
}
|
||||
|
||||
public static AVec Tbl1_V64(AVec Vector, AVec Tb0)
|
||||
{
|
||||
return Tbl(Vector, 8, Tb0);
|
||||
|
@ -368,23 +249,23 @@ namespace ChocolArm64.Instruction
|
|||
for (int Index = 0; Index < Tb.Length; Index++)
|
||||
for (int Index2 = 0; Index2 < 16; Index2++)
|
||||
{
|
||||
Table[Index * 16 + Index2] = (byte)ExtractVec(Tb[Index], Index2, 0);
|
||||
Table[Index * 16 + Index2] = (byte)VectorExtractIntZx(Tb[Index], Index2, 0);
|
||||
}
|
||||
|
||||
for (int Index = 0; Index < Bytes; Index++)
|
||||
{
|
||||
byte TblIdx = (byte)ExtractVec(Vector, Index, 0);
|
||||
byte TblIdx = (byte)VectorExtractIntZx(Vector, Index, 0);
|
||||
|
||||
if (TblIdx < Table.Length)
|
||||
{
|
||||
Res = InsertVec(Res, Index, 0, Table[TblIdx]);
|
||||
Res = VectorInsertInt(Table[TblIdx], Res, Index, 0);
|
||||
}
|
||||
}
|
||||
|
||||
return Res;
|
||||
}
|
||||
|
||||
public static ulong ExtractVec(AVec Vector, int Index, int Size)
|
||||
public static ulong VectorExtractIntZx(AVec Vector, int Index, int Size)
|
||||
{
|
||||
switch (Size)
|
||||
{
|
||||
|
@ -397,7 +278,7 @@ namespace ChocolArm64.Instruction
|
|||
throw new ArgumentOutOfRangeException(nameof(Size));
|
||||
}
|
||||
|
||||
public static long ExtractSVec(AVec Vector, int Index, int Size)
|
||||
public static long VectorExtractIntSx(AVec Vector, int Index, int Size)
|
||||
{
|
||||
switch (Size)
|
||||
{
|
||||
|
@ -442,31 +323,5 @@ namespace ChocolArm64.Instruction
|
|||
|
||||
throw new ArgumentOutOfRangeException(nameof(Size));
|
||||
}
|
||||
|
||||
public static AVec InsertVec(AVec Vector, int Index, int Size, ulong Value)
|
||||
{
|
||||
switch (Size)
|
||||
{
|
||||
case 0: return AVec.InsertByte(Vector, Index, (byte)Value);
|
||||
case 1: return AVec.InsertUInt16(Vector, Index, (ushort)Value);
|
||||
case 2: return AVec.InsertUInt32(Vector, Index, (uint)Value);
|
||||
case 3: return AVec.InsertUInt64(Vector, Index, (ulong)Value);
|
||||
}
|
||||
|
||||
throw new ArgumentOutOfRangeException(nameof(Size));
|
||||
}
|
||||
|
||||
public static AVec InsertSVec(AVec Vector, int Index, int Size, long Value)
|
||||
{
|
||||
switch (Size)
|
||||
{
|
||||
case 0: return AVec.InsertByte(Vector, Index, (byte)Value);
|
||||
case 1: return AVec.InsertUInt16(Vector, Index, (ushort)Value);
|
||||
case 2: return AVec.InsertUInt32(Vector, Index, (uint)Value);
|
||||
case 3: return AVec.InsertUInt64(Vector, Index, (ulong)Value);
|
||||
}
|
||||
|
||||
throw new ArgumentOutOfRangeException(nameof(Size));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -174,6 +174,42 @@ namespace ChocolArm64.Memory
|
|||
return *((ulong*)(RamPtr + (uint)Position));
|
||||
}
|
||||
|
||||
public AVec ReadVector8(long Position)
|
||||
{
|
||||
#if DEBUG
|
||||
EnsureAccessIsValid(Position, AMemoryPerm.Read);
|
||||
#endif
|
||||
|
||||
return new AVec() { B0 = ReadByte(Position) };
|
||||
}
|
||||
|
||||
public AVec ReadVector16(long Position)
|
||||
{
|
||||
#if DEBUG
|
||||
EnsureAccessIsValid(Position, AMemoryPerm.Read);
|
||||
#endif
|
||||
|
||||
return new AVec() { H0 = ReadUInt16(Position) };
|
||||
}
|
||||
|
||||
public AVec ReadVector32(long Position)
|
||||
{
|
||||
#if DEBUG
|
||||
EnsureAccessIsValid(Position, AMemoryPerm.Read);
|
||||
#endif
|
||||
|
||||
return new AVec() { W0 = ReadUInt32(Position) };
|
||||
}
|
||||
|
||||
public AVec ReadVector64(long Position)
|
||||
{
|
||||
#if DEBUG
|
||||
EnsureAccessIsValid(Position, AMemoryPerm.Read);
|
||||
#endif
|
||||
|
||||
return new AVec() { X0 = ReadUInt64(Position) };
|
||||
}
|
||||
|
||||
public AVec ReadVector128(long Position)
|
||||
{
|
||||
#if DEBUG
|
||||
|
@ -228,6 +264,42 @@ namespace ChocolArm64.Memory
|
|||
*((ulong*)(RamPtr + (uint)Position)) = Value;
|
||||
}
|
||||
|
||||
public void WriteVector8(long Position, AVec Value)
|
||||
{
|
||||
#if DEBUG
|
||||
EnsureAccessIsValid(Position, AMemoryPerm.Write);
|
||||
#endif
|
||||
|
||||
WriteByte(Position, Value.B0);
|
||||
}
|
||||
|
||||
public void WriteVector16(long Position, AVec Value)
|
||||
{
|
||||
#if DEBUG
|
||||
EnsureAccessIsValid(Position, AMemoryPerm.Write);
|
||||
#endif
|
||||
|
||||
WriteUInt16(Position, Value.H0);
|
||||
}
|
||||
|
||||
public void WriteVector32(long Position, AVec Value)
|
||||
{
|
||||
#if DEBUG
|
||||
EnsureAccessIsValid(Position, AMemoryPerm.Write);
|
||||
#endif
|
||||
|
||||
WriteUInt32(Position, Value.W0);
|
||||
}
|
||||
|
||||
public void WriteVector64(long Position, AVec Value)
|
||||
{
|
||||
#if DEBUG
|
||||
EnsureAccessIsValid(Position, AMemoryPerm.Write);
|
||||
#endif
|
||||
|
||||
WriteUInt64(Position, Value.X0);
|
||||
}
|
||||
|
||||
public void WriteVector128(long Position, AVec Value)
|
||||
{
|
||||
#if DEBUG
|
||||
|
|
|
@ -1,113 +0,0 @@
|
|||
using ChocolArm64.State;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace ChocolArm64.Translation
|
||||
{
|
||||
static class AILConv
|
||||
{
|
||||
public static void EmitConv(AILEmitter Context, Type SrcType, Type TgtType)
|
||||
{
|
||||
if (SrcType == TgtType)
|
||||
{
|
||||
//If both types are equal we don't need to cast anything.
|
||||
return;
|
||||
}
|
||||
|
||||
if (SrcType.IsPrimitive)
|
||||
{
|
||||
if (TgtType == typeof(byte))
|
||||
{
|
||||
Context.Generator.Emit(OpCodes.Conv_U1);
|
||||
}
|
||||
else if (TgtType == typeof(ushort))
|
||||
{
|
||||
Context.Generator.Emit(OpCodes.Conv_U2);
|
||||
}
|
||||
else if (TgtType == typeof(uint))
|
||||
{
|
||||
Context.Generator.Emit(OpCodes.Conv_U4);
|
||||
}
|
||||
else if (TgtType == typeof(ulong))
|
||||
{
|
||||
Context.Generator.Emit(OpCodes.Conv_U8);
|
||||
}
|
||||
else if (TgtType == typeof(float))
|
||||
{
|
||||
Context.Generator.Emit(OpCodes.Conv_R4);
|
||||
}
|
||||
else if (TgtType == typeof(double))
|
||||
{
|
||||
Context.Generator.Emit(OpCodes.Conv_R8);
|
||||
}
|
||||
else if (TgtType == typeof(AVec))
|
||||
{
|
||||
EmitMakeVec(Context, SrcType);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException(nameof(TgtType));
|
||||
}
|
||||
}
|
||||
else if (SrcType == typeof(AVec))
|
||||
{
|
||||
if (TgtType == typeof(float))
|
||||
{
|
||||
EmitScalarLdfld(Context, nameof(AVec.S0));
|
||||
}
|
||||
else if (TgtType == typeof(double))
|
||||
{
|
||||
EmitScalarLdfld(Context, nameof(AVec.D0));
|
||||
}
|
||||
else if (TgtType == typeof(byte))
|
||||
{
|
||||
EmitScalarLdfld(Context, nameof(AVec.B0));
|
||||
}
|
||||
else if (TgtType == typeof(ushort))
|
||||
{
|
||||
EmitScalarLdfld(Context, nameof(AVec.H0));
|
||||
}
|
||||
else if (TgtType == typeof(uint))
|
||||
{
|
||||
EmitScalarLdfld(Context, nameof(AVec.W0));
|
||||
}
|
||||
else if (TgtType == typeof(ulong))
|
||||
{
|
||||
EmitScalarLdfld(Context, nameof(AVec.X0));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException(nameof(TgtType));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException(nameof(SrcType));
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitScalarLdfld(AILEmitter Context,string FldName)
|
||||
{
|
||||
Context.Generator.Emit(OpCodes.Ldfld, typeof(AVec).GetField(FldName));
|
||||
}
|
||||
|
||||
private static void EmitMakeVec(AILEmitter Context, Type SrcType)
|
||||
{
|
||||
string MthdName = nameof(MakeScalar);
|
||||
|
||||
Type[] MthdTypes = new Type[] { SrcType };
|
||||
|
||||
MethodInfo MthdInfo = typeof(AILConv).GetMethod(MthdName, MthdTypes);
|
||||
|
||||
Context.Generator.Emit(OpCodes.Call, MthdInfo);
|
||||
}
|
||||
|
||||
public static AVec MakeScalar(byte Value) => new AVec { B0 = Value };
|
||||
public static AVec MakeScalar(ushort Value) => new AVec { H0 = Value };
|
||||
public static AVec MakeScalar(uint Value) => new AVec { W0 = Value };
|
||||
public static AVec MakeScalar(float Value) => new AVec { S0 = Value };
|
||||
public static AVec MakeScalar(ulong Value) => new AVec { X0 = Value };
|
||||
public static AVec MakeScalar(double Value) => new AVec { D0 = Value };
|
||||
}
|
||||
}
|
|
@ -86,9 +86,6 @@ namespace ChocolArm64.Translation
|
|||
ARegister Reg = Subroutine.Params[Index];
|
||||
|
||||
Generator.EmitLdarg(Index + ParamsStart);
|
||||
|
||||
AILConv.EmitConv(this, GetFieldType(Reg.Type), GetLocalType(Reg));
|
||||
|
||||
Generator.EmitStloc(GetLocalIndex(Reg));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -150,8 +150,8 @@ namespace ChocolArm64.Translation
|
|||
|
||||
if (LastCmpOp != null && LastFlagOp == LastCmpOp && BranchOps.ContainsKey(Cond))
|
||||
{
|
||||
Ldloc(Tmp3Index, AIoType.Int, GetIntType(LastCmpOp));
|
||||
Ldloc(Tmp4Index, AIoType.Int, GetIntType(LastCmpOp));
|
||||
Ldloc(Tmp3Index, AIoType.Int, LastCmpOp.RegisterSize);
|
||||
Ldloc(Tmp4Index, AIoType.Int, LastCmpOp.RegisterSize);
|
||||
|
||||
if (LastCmpOp.Emitter == AInstEmit.Adds)
|
||||
{
|
||||
|
@ -353,12 +353,6 @@ namespace ChocolArm64.Translation
|
|||
public void EmitLdvec(int Index) => Ldloc(Index, AIoType.Vector);
|
||||
public void EmitStvec(int Index) => Stloc(Index, AIoType.Vector);
|
||||
|
||||
public void EmitLdvecsi(int Index) => Ldloc(Index, AIoType.VectorI);
|
||||
public void EmitStvecsi(int Index) => Stloc(Index, AIoType.VectorI);
|
||||
|
||||
public void EmitLdvecsf(int Index) => Ldloc(Index, AIoType.VectorF);
|
||||
public void EmitStvecsf(int Index) => Stloc(Index, AIoType.VectorF);
|
||||
|
||||
public void EmitLdflg(int Index) => Ldloc(Index, AIoType.Flag);
|
||||
public void EmitStflg(int Index)
|
||||
{
|
||||
|
@ -369,107 +363,17 @@ namespace ChocolArm64.Translation
|
|||
|
||||
private void Ldloc(int Index, AIoType IoType)
|
||||
{
|
||||
ILBlock.Add(new AILOpCodeLoad(Index, IoType, GetOperType(IoType)));
|
||||
ILBlock.Add(new AILOpCodeLoad(Index, IoType, CurrOp.RegisterSize));
|
||||
}
|
||||
|
||||
private void Ldloc(int Index, AIoType IoType, Type Type)
|
||||
private void Ldloc(int Index, AIoType IoType, ARegisterSize RegisterSize)
|
||||
{
|
||||
ILBlock.Add(new AILOpCodeLoad(Index, IoType, Type));
|
||||
ILBlock.Add(new AILOpCodeLoad(Index, IoType, RegisterSize));
|
||||
}
|
||||
|
||||
private void Stloc(int Index, AIoType IoType)
|
||||
{
|
||||
ILBlock.Add(new AILOpCodeStore(Index, IoType, GetOutOperType(IoType)));
|
||||
}
|
||||
|
||||
private Type GetOutOperType(AIoType IoType)
|
||||
{
|
||||
//This instruction is used to convert between floating point
|
||||
//types, so the input and output types are different.
|
||||
if (CurrOp.Emitter == AInstEmit.Fcvt_S)
|
||||
{
|
||||
return GetFloatType(((AOpCodeSimd)CurrOp).Opc);
|
||||
}
|
||||
else
|
||||
{
|
||||
return GetOperType(IoType);
|
||||
}
|
||||
}
|
||||
|
||||
private Type GetOperType(AIoType IoType)
|
||||
{
|
||||
switch (IoType & AIoType.Mask)
|
||||
{
|
||||
case AIoType.Flag: return typeof(bool);
|
||||
case AIoType.Int: return GetIntType(CurrOp);
|
||||
case AIoType.Vector: return GetVecType(CurrOp, IoType);
|
||||
}
|
||||
|
||||
throw new ArgumentException(nameof(IoType));
|
||||
}
|
||||
|
||||
private Type GetIntType(AOpCode OpCode)
|
||||
{
|
||||
//Always default to 64-bits.
|
||||
return OpCode.RegisterSize == ARegisterSize.Int32
|
||||
? typeof(uint)
|
||||
: typeof(ulong);
|
||||
}
|
||||
|
||||
private Type GetVecType(AOpCode OpCode, AIoType IoType)
|
||||
{
|
||||
if (!(OpCode is IAOpCodeSimd Op))
|
||||
{
|
||||
return typeof(AVec);
|
||||
}
|
||||
|
||||
int Size = Op.Size;
|
||||
|
||||
if (Op.Emitter == AInstEmit.Fmov_Ftoi ||
|
||||
Op.Emitter == AInstEmit.Fmov_Itof)
|
||||
{
|
||||
Size |= 2;
|
||||
}
|
||||
|
||||
if ((Op is AOpCodeMem || Op is IAOpCodeLit) &&
|
||||
!(Op is AOpCodeSimdMemMs || Op is AOpCodeSimdMemSs))
|
||||
{
|
||||
return Size < 4 ? typeof(ulong) : typeof(AVec);
|
||||
}
|
||||
else if (IoType == AIoType.VectorI)
|
||||
{
|
||||
return GetIntType(Size);
|
||||
}
|
||||
else if (IoType == AIoType.VectorF)
|
||||
{
|
||||
return GetFloatType(Size);
|
||||
}
|
||||
|
||||
return typeof(AVec);
|
||||
}
|
||||
|
||||
private static Type GetIntType(int Size)
|
||||
{
|
||||
switch (Size)
|
||||
{
|
||||
case 0: return typeof(byte);
|
||||
case 1: return typeof(ushort);
|
||||
case 2: return typeof(uint);
|
||||
case 3: return typeof(ulong);
|
||||
}
|
||||
|
||||
throw new ArgumentOutOfRangeException(nameof(Size));
|
||||
}
|
||||
|
||||
private static Type GetFloatType(int Size)
|
||||
{
|
||||
switch (Size)
|
||||
{
|
||||
case 0: return typeof(float);
|
||||
case 1: return typeof(double);
|
||||
}
|
||||
|
||||
throw new ArgumentOutOfRangeException(nameof(Size));
|
||||
ILBlock.Add(new AILOpCodeStore(Index, IoType, CurrOp.RegisterSize));
|
||||
}
|
||||
|
||||
public void EmitCallPropGet(Type ObjType, string PropName)
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using ChocolArm64.State;
|
||||
using System;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace ChocolArm64.Translation
|
||||
|
@ -10,41 +9,38 @@ namespace ChocolArm64.Translation
|
|||
|
||||
public AIoType IoType { get; private set; }
|
||||
|
||||
public Type OperType { get; private set; }
|
||||
public ARegisterSize RegisterSize { get; private set; }
|
||||
|
||||
public AILOpCodeLoad(int Index, AIoType IoType) : this(Index, IoType, null) { }
|
||||
public AILOpCodeLoad(int Index, AIoType IoType) : this(Index, IoType, ARegisterSize.Int64) { }
|
||||
|
||||
public AILOpCodeLoad(int Index, AIoType IoType, Type OperType)
|
||||
public AILOpCodeLoad(int Index, AIoType IoType, ARegisterSize RegisterSize)
|
||||
{
|
||||
this.IoType = IoType;
|
||||
this.Index = Index;
|
||||
this.OperType = OperType;
|
||||
this.RegisterSize = RegisterSize;
|
||||
}
|
||||
|
||||
public void Emit(AILEmitter Context)
|
||||
{
|
||||
switch (IoType & AIoType.Mask)
|
||||
{
|
||||
case AIoType.Arg: EmitLdarg(Context, Index); break;
|
||||
case AIoType.Fields: EmitLdfld(Context, Index); break;
|
||||
case AIoType.Flag: EmitLdloc(Context, Index, ARegisterType.Flag); break;
|
||||
case AIoType.Int: EmitLdloc(Context, Index, ARegisterType.Int); break;
|
||||
case AIoType.Vector: EmitLdloc(Context, Index, ARegisterType.Vector); break;
|
||||
}
|
||||
}
|
||||
case AIoType.Arg: Context.Generator.EmitLdarg(Index); break;
|
||||
|
||||
private void EmitLdarg(AILEmitter Context, int Index)
|
||||
{
|
||||
Context.Generator.EmitLdarg(Index);
|
||||
}
|
||||
|
||||
private void EmitLdfld(AILEmitter Context, int Index)
|
||||
case AIoType.Fields:
|
||||
{
|
||||
long IntInputs = Context.LocalAlloc.GetIntInputs(Context.GetILBlock(Index));
|
||||
long VecInputs = Context.LocalAlloc.GetVecInputs(Context.GetILBlock(Index));
|
||||
|
||||
LoadLocals(Context, IntInputs, ARegisterType.Int);
|
||||
LoadLocals(Context, VecInputs, ARegisterType.Vector);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case AIoType.Flag: EmitLdloc(Context, Index, ARegisterType.Flag); break;
|
||||
case AIoType.Int: EmitLdloc(Context, Index, ARegisterType.Int); break;
|
||||
case AIoType.Vector: EmitLdloc(Context, Index, ARegisterType.Vector); break;
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadLocals(AILEmitter Context, long Inputs, ARegisterType BaseType)
|
||||
|
@ -60,23 +56,22 @@ namespace ChocolArm64.Translation
|
|||
Context.Generator.EmitLdarg(ATranslatedSub.RegistersArgIdx);
|
||||
Context.Generator.Emit(OpCodes.Ldfld, Reg.GetField());
|
||||
|
||||
AILConv.EmitConv(
|
||||
Context,
|
||||
Context.GetFieldType(Reg.Type),
|
||||
Context.GetLocalType(Reg));
|
||||
|
||||
Context.Generator.EmitStloc(Context.GetLocalIndex(Reg));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void EmitLdloc(AILEmitter Context, int Index, ARegisterType Type)
|
||||
private void EmitLdloc(AILEmitter Context, int Index, ARegisterType RegisterType)
|
||||
{
|
||||
ARegister Reg = new ARegister(Index, Type);
|
||||
ARegister Reg = new ARegister(Index, RegisterType);
|
||||
|
||||
Context.Generator.EmitLdloc(Context.GetLocalIndex(Reg));
|
||||
|
||||
AILConv.EmitConv(Context, Context.GetLocalType(Reg), OperType);
|
||||
if (RegisterType == ARegisterType.Int &&
|
||||
RegisterSize == ARegisterSize.Int32)
|
||||
{
|
||||
Context.Generator.Emit(OpCodes.Conv_U4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,50 +1,46 @@
|
|||
using ChocolArm64.State;
|
||||
using System;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace ChocolArm64.Translation
|
||||
{
|
||||
struct AILOpCodeStore : IAILEmit
|
||||
{
|
||||
public AIoType IoType { get; private set; }
|
||||
|
||||
public Type OperType { get; private set; }
|
||||
|
||||
public int Index { get; private set; }
|
||||
|
||||
public AILOpCodeStore(int Index, AIoType IoType) : this(Index, IoType, null) { }
|
||||
public AIoType IoType { get; private set; }
|
||||
|
||||
public AILOpCodeStore(int Index, AIoType IoType, Type OperType)
|
||||
public ARegisterSize RegisterSize { get; private set; }
|
||||
|
||||
public AILOpCodeStore(int Index, AIoType IoType) : this(Index, IoType, ARegisterSize.Int64) { }
|
||||
|
||||
public AILOpCodeStore(int Index, AIoType IoType, ARegisterSize RegisterSize)
|
||||
{
|
||||
this.IoType = IoType;
|
||||
this.Index = Index;
|
||||
this.OperType = OperType;
|
||||
this.RegisterSize = RegisterSize;
|
||||
}
|
||||
|
||||
public void Emit(AILEmitter Context)
|
||||
{
|
||||
switch (IoType & AIoType.Mask)
|
||||
{
|
||||
case AIoType.Arg: EmitStarg(Context, Index); break;
|
||||
case AIoType.Fields: EmitStfld(Context, Index); break;
|
||||
case AIoType.Flag: EmitStloc(Context, Index, ARegisterType.Flag); break;
|
||||
case AIoType.Int: EmitStloc(Context, Index, ARegisterType.Int); break;
|
||||
case AIoType.Vector: EmitStloc(Context, Index, ARegisterType.Vector); break;
|
||||
}
|
||||
}
|
||||
case AIoType.Arg: Context.Generator.EmitStarg(Index); break;
|
||||
|
||||
private void EmitStarg(AILEmitter Context, int Index)
|
||||
{
|
||||
Context.Generator.EmitStarg(Index);
|
||||
}
|
||||
|
||||
private void EmitStfld(AILEmitter Context, int Index)
|
||||
case AIoType.Fields:
|
||||
{
|
||||
long IntOutputs = Context.LocalAlloc.GetIntOutputs(Context.GetILBlock(Index));
|
||||
long VecOutputs = Context.LocalAlloc.GetVecOutputs(Context.GetILBlock(Index));
|
||||
|
||||
StoreLocals(Context, IntOutputs, ARegisterType.Int);
|
||||
StoreLocals(Context, VecOutputs, ARegisterType.Vector);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case AIoType.Flag: EmitStloc(Context, Index, ARegisterType.Flag); break;
|
||||
case AIoType.Int: EmitStloc(Context, Index, ARegisterType.Int); break;
|
||||
case AIoType.Vector: EmitStloc(Context, Index, ARegisterType.Vector); break;
|
||||
}
|
||||
}
|
||||
|
||||
private void StoreLocals(AILEmitter Context, long Outputs, ARegisterType BaseType)
|
||||
|
@ -60,21 +56,20 @@ namespace ChocolArm64.Translation
|
|||
Context.Generator.EmitLdarg(ATranslatedSub.RegistersArgIdx);
|
||||
Context.Generator.EmitLdloc(Context.GetLocalIndex(Reg));
|
||||
|
||||
AILConv.EmitConv(
|
||||
Context,
|
||||
Context.GetLocalType(Reg),
|
||||
Context.GetFieldType(Reg.Type));
|
||||
|
||||
Context.Generator.Emit(OpCodes.Stfld, Reg.GetField());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void EmitStloc(AILEmitter Context, int Index, ARegisterType Type)
|
||||
private void EmitStloc(AILEmitter Context, int Index, ARegisterType RegisterType)
|
||||
{
|
||||
ARegister Reg = new ARegister(Index, Type);
|
||||
ARegister Reg = new ARegister(Index, RegisterType);
|
||||
|
||||
AILConv.EmitConv(Context, OperType, Context.GetLocalType(Reg));
|
||||
if (RegisterType == ARegisterType.Int &&
|
||||
RegisterSize == ARegisterSize.Int32)
|
||||
{
|
||||
Context.Generator.Emit(OpCodes.Conv_U8);
|
||||
}
|
||||
|
||||
Context.Generator.EmitStloc(Context.GetLocalIndex(Reg));
|
||||
}
|
||||
|
|
|
@ -80,6 +80,8 @@ namespace Ryujinx.OsHle
|
|||
continue;
|
||||
}
|
||||
|
||||
Logging.Info($"Loding {Path.GetFileNameWithoutExtension(File)}...");
|
||||
|
||||
using (FileStream Input = new FileStream(File, FileMode.Open))
|
||||
{
|
||||
Nso Program = new Nso(Input);
|
||||
|
|
|
@ -70,6 +70,8 @@ namespace Ryujinx.OsHle
|
|||
|
||||
public void LoadProgram(IExecutable Program)
|
||||
{
|
||||
Logging.Info($"Image base at 0x{ImageBase:x16}.");
|
||||
|
||||
Executable Executable = new Executable(Program, Memory, ImageBase);
|
||||
|
||||
Executables.Add(Executable);
|
||||
|
|
Loading…
Reference in a new issue