mirror of
https://git.ryujinx.app/ryubing/ryujinx.git
synced 2025-08-02 17:57:15 +02:00
aloha
This commit is contained in:
commit
b7e1d9930d
230 changed files with 17548 additions and 0 deletions
18
Ryujinx/Cpu/Instruction/AInst.cs
Normal file
18
Ryujinx/Cpu/Instruction/AInst.cs
Normal file
|
@ -0,0 +1,18 @@
|
|||
using System;
|
||||
|
||||
namespace ChocolArm64.Instruction
|
||||
{
|
||||
struct AInst
|
||||
{
|
||||
public AInstEmitter Emitter { get; private set; }
|
||||
public Type Type { get; private set; }
|
||||
|
||||
public static AInst Undefined => new AInst(AInstEmit.Und, null);
|
||||
|
||||
public AInst(AInstEmitter Emitter, Type Type)
|
||||
{
|
||||
this.Emitter = Emitter;
|
||||
this.Type = Type;
|
||||
}
|
||||
}
|
||||
}
|
296
Ryujinx/Cpu/Instruction/AInstEmitAlu.cs
Normal file
296
Ryujinx/Cpu/Instruction/AInstEmitAlu.cs
Normal file
|
@ -0,0 +1,296 @@
|
|||
using ChocolArm64.Decoder;
|
||||
using ChocolArm64.State;
|
||||
using ChocolArm64.Translation;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
using static ChocolArm64.Instruction.AInstEmitAluHelper;
|
||||
|
||||
namespace ChocolArm64.Instruction
|
||||
{
|
||||
static partial class AInstEmit
|
||||
{
|
||||
public static void Add(AILEmitterCtx Context) => EmitDataOp(Context, OpCodes.Add);
|
||||
|
||||
public static void Adds(AILEmitterCtx Context)
|
||||
{
|
||||
Context.TryOptMarkCondWithoutCmp();
|
||||
|
||||
EmitDataLoadOpers(Context);
|
||||
|
||||
Context.Emit(OpCodes.Add);
|
||||
|
||||
Context.EmitZNFlagCheck();
|
||||
|
||||
EmitAddsCCheck(Context);
|
||||
EmitAddsVCheck(Context);
|
||||
EmitDataStoreS(Context);
|
||||
}
|
||||
|
||||
public static void And(AILEmitterCtx Context) => EmitDataOp(Context, OpCodes.And);
|
||||
|
||||
public static void Ands(AILEmitterCtx Context)
|
||||
{
|
||||
EmitDataLoadOpers(Context);
|
||||
|
||||
Context.Emit(OpCodes.And);
|
||||
|
||||
Context.EmitZNFlagCheck();
|
||||
|
||||
EmitDataStoreS(Context);
|
||||
}
|
||||
|
||||
public static void Asrv(AILEmitterCtx Context) => EmitDataOpShift(Context, OpCodes.Shr);
|
||||
|
||||
public static void Bic(AILEmitterCtx Context) => EmitBic(Context, false);
|
||||
public static void Bics(AILEmitterCtx Context) => EmitBic(Context, true);
|
||||
|
||||
private static void EmitBic(AILEmitterCtx Context, bool SetFlags)
|
||||
{
|
||||
EmitDataLoadOpers(Context);
|
||||
|
||||
Context.Emit(OpCodes.Not);
|
||||
Context.Emit(OpCodes.And);
|
||||
|
||||
if (SetFlags)
|
||||
{
|
||||
Context.EmitZNFlagCheck();
|
||||
}
|
||||
|
||||
EmitDataStore(Context, SetFlags);
|
||||
}
|
||||
|
||||
public static void Clz(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeAlu Op = (AOpCodeAlu)Context.CurrOp;
|
||||
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
|
||||
if (Op.RegisterSize == ARegisterSize.Int32)
|
||||
{
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.CountLeadingZeros32));
|
||||
}
|
||||
else
|
||||
{
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.CountLeadingZeros64));
|
||||
}
|
||||
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Eor(AILEmitterCtx Context) => EmitDataOp(Context, OpCodes.Xor);
|
||||
|
||||
public static void Extr(AILEmitterCtx Context)
|
||||
{
|
||||
//TODO: Ensure that the Shift is valid for the Is64Bits.
|
||||
AOpCodeAluRs Op = (AOpCodeAluRs)Context.CurrOp;
|
||||
|
||||
Context.EmitLdintzr(Op.Rm);
|
||||
|
||||
if (Op.Shift > 0)
|
||||
{
|
||||
Context.EmitLdc_I4(Op.Shift);
|
||||
|
||||
Context.Emit(OpCodes.Shr_Un);
|
||||
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
Context.EmitLdc_I4(Op.GetBitsCount() - Op.Shift);
|
||||
|
||||
Context.Emit(OpCodes.Shl);
|
||||
Context.Emit(OpCodes.Or);
|
||||
}
|
||||
|
||||
EmitDataStore(Context);
|
||||
}
|
||||
|
||||
public static void Lslv(AILEmitterCtx Context) => EmitDataOpShift(Context, OpCodes.Shl);
|
||||
public static void Lsrv(AILEmitterCtx Context) => EmitDataOpShift(Context, OpCodes.Shr_Un);
|
||||
|
||||
public static void Sub(AILEmitterCtx Context) => EmitDataOp(Context, OpCodes.Sub);
|
||||
|
||||
public static void Subs(AILEmitterCtx Context)
|
||||
{
|
||||
Context.TryOptMarkCondWithoutCmp();
|
||||
|
||||
EmitDataLoadOpers(Context);
|
||||
|
||||
Context.Emit(OpCodes.Sub);
|
||||
|
||||
Context.EmitZNFlagCheck();
|
||||
|
||||
EmitSubsCCheck(Context);
|
||||
EmitSubsVCheck(Context);
|
||||
EmitDataStoreS(Context);
|
||||
}
|
||||
|
||||
public static void Orn(AILEmitterCtx Context)
|
||||
{
|
||||
EmitDataLoadOpers(Context);
|
||||
|
||||
Context.Emit(OpCodes.Not);
|
||||
Context.Emit(OpCodes.Or);
|
||||
|
||||
EmitDataStore(Context);
|
||||
}
|
||||
|
||||
public static void Orr(AILEmitterCtx Context) => EmitDataOp(Context, OpCodes.Or);
|
||||
|
||||
public static void Rbit(AILEmitterCtx Context) => EmitFallback32_64(Context,
|
||||
nameof(ASoftFallback.ReverseBits32),
|
||||
nameof(ASoftFallback.ReverseBits64));
|
||||
|
||||
public static void Rev16(AILEmitterCtx Context) => EmitFallback32_64(Context,
|
||||
nameof(ASoftFallback.ReverseBytes16_32),
|
||||
nameof(ASoftFallback.ReverseBytes16_64));
|
||||
|
||||
public static void Rev32(AILEmitterCtx Context) => EmitFallback32_64(Context,
|
||||
nameof(ASoftFallback.ReverseBytes32_32),
|
||||
nameof(ASoftFallback.ReverseBytes32_64));
|
||||
|
||||
public static void EmitFallback32_64(AILEmitterCtx Context, string Name32, string Name64)
|
||||
{
|
||||
AOpCodeAlu Op = (AOpCodeAlu)Context.CurrOp;
|
||||
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
|
||||
if (Op.RegisterSize == ARegisterSize.Int32)
|
||||
{
|
||||
ASoftFallback.EmitCall(Context, Name32);
|
||||
}
|
||||
else
|
||||
{
|
||||
ASoftFallback.EmitCall(Context, Name64);
|
||||
}
|
||||
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Rev64(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeAlu Op = (AOpCodeAlu)Context.CurrOp;
|
||||
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.ReverseBytes64));
|
||||
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
|
||||
private static void EmitRev(AILEmitterCtx Context, string Name)
|
||||
{
|
||||
AOpCodeAlu Op = (AOpCodeAlu)Context.CurrOp;
|
||||
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
|
||||
ASoftFallback.EmitCall(Context, Name);
|
||||
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Rorv(AILEmitterCtx Context)
|
||||
{
|
||||
EmitDataLoadRn(Context);
|
||||
EmitDataLoadShift(Context);
|
||||
|
||||
Context.Emit(OpCodes.Shr_Un);
|
||||
|
||||
EmitDataLoadRn(Context);
|
||||
|
||||
Context.EmitLdc_I4(Context.CurrOp.GetBitsCount());
|
||||
|
||||
EmitDataLoadShift(Context);
|
||||
|
||||
Context.Emit(OpCodes.Sub);
|
||||
Context.Emit(OpCodes.Shl);
|
||||
Context.Emit(OpCodes.Or);
|
||||
|
||||
EmitDataStore(Context);
|
||||
}
|
||||
|
||||
public static void Sdiv(AILEmitterCtx Context) => EmitDiv(Context, OpCodes.Div);
|
||||
public static void Udiv(AILEmitterCtx Context) => EmitDiv(Context, OpCodes.Div_Un);
|
||||
|
||||
private static void EmitDiv(AILEmitterCtx Context, OpCode ILOp)
|
||||
{
|
||||
//If Rm == 0, Rd = 0 (division by zero).
|
||||
Context.EmitLdc_I(0);
|
||||
|
||||
EmitDataLoadRm(Context);
|
||||
|
||||
Context.EmitLdc_I(0);
|
||||
|
||||
AILLabel BadDiv = new AILLabel();
|
||||
|
||||
Context.Emit(OpCodes.Beq_S, BadDiv);
|
||||
Context.Emit(OpCodes.Pop);
|
||||
|
||||
if (ILOp == OpCodes.Div)
|
||||
{
|
||||
//If Rn == INT_MIN && Rm == -1, Rd = INT_MIN (overflow).
|
||||
long IntMin = 1L << (Context.CurrOp.GetBitsCount() - 1);
|
||||
|
||||
Context.EmitLdc_I(IntMin);
|
||||
|
||||
EmitDataLoadRn(Context);
|
||||
|
||||
Context.EmitLdc_I(IntMin);
|
||||
|
||||
Context.Emit(OpCodes.Ceq);
|
||||
|
||||
EmitDataLoadRm(Context);
|
||||
|
||||
Context.EmitLdc_I(-1);
|
||||
|
||||
Context.Emit(OpCodes.Ceq);
|
||||
Context.Emit(OpCodes.And);
|
||||
Context.Emit(OpCodes.Brtrue_S, BadDiv);
|
||||
Context.Emit(OpCodes.Pop);
|
||||
}
|
||||
|
||||
EmitDataLoadRn(Context);
|
||||
EmitDataLoadRm(Context);
|
||||
|
||||
Context.Emit(ILOp);
|
||||
|
||||
Context.MarkLabel(BadDiv);
|
||||
|
||||
EmitDataStore(Context);
|
||||
}
|
||||
|
||||
private static void EmitDataOp(AILEmitterCtx Context, OpCode ILOp)
|
||||
{
|
||||
EmitDataLoadOpers(Context);
|
||||
|
||||
Context.Emit(ILOp);
|
||||
|
||||
EmitDataStore(Context);
|
||||
}
|
||||
|
||||
private static void EmitDataOpShift(AILEmitterCtx Context, OpCode ILOp)
|
||||
{
|
||||
EmitDataLoadRn(Context);
|
||||
EmitDataLoadShift(Context);
|
||||
|
||||
Context.Emit(ILOp);
|
||||
|
||||
EmitDataStore(Context);
|
||||
}
|
||||
|
||||
private static void EmitDataLoadShift(AILEmitterCtx Context)
|
||||
{
|
||||
EmitDataLoadRm(Context);
|
||||
|
||||
Context.EmitLdc_I(Context.CurrOp.GetBitsCount() - 1);
|
||||
|
||||
Context.Emit(OpCodes.And);
|
||||
|
||||
//Note: Only 32-bits shift values are valid, so when the value is 64-bits
|
||||
//we need to cast it to a 32-bits integer. This is fine because we
|
||||
//AND the value and only keep the lower 5 or 6 bits anyway -- it
|
||||
//could very well fit on a byte.
|
||||
if (Context.CurrOp.RegisterSize != ARegisterSize.Int32)
|
||||
{
|
||||
Context.Emit(OpCodes.Conv_I4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
159
Ryujinx/Cpu/Instruction/AInstEmitAluHelper.cs
Normal file
159
Ryujinx/Cpu/Instruction/AInstEmitAluHelper.cs
Normal file
|
@ -0,0 +1,159 @@
|
|||
using ChocolArm64.Decoder;
|
||||
using ChocolArm64.State;
|
||||
using ChocolArm64.Translation;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace ChocolArm64.Instruction
|
||||
{
|
||||
static class AInstEmitAluHelper
|
||||
{
|
||||
public static void EmitAddsCCheck(AILEmitterCtx Context)
|
||||
{
|
||||
//C = Rd < Rn
|
||||
Context.Emit(OpCodes.Dup);
|
||||
|
||||
EmitDataLoadRn(Context);
|
||||
|
||||
Context.Emit(OpCodes.Clt_Un);
|
||||
|
||||
Context.EmitStflg((int)APState.CBit);
|
||||
}
|
||||
|
||||
public static void EmitAddsVCheck(AILEmitterCtx Context)
|
||||
{
|
||||
//V = (Rd ^ Rn) & (Rd ^ Rm) & ~(Rn ^ Rm) < 0
|
||||
Context.EmitSttmp();
|
||||
Context.EmitLdtmp();
|
||||
Context.EmitLdtmp();
|
||||
|
||||
EmitDataLoadRn(Context);
|
||||
|
||||
Context.Emit(OpCodes.Xor);
|
||||
|
||||
Context.EmitLdtmp();
|
||||
|
||||
EmitDataLoadOper2(Context);
|
||||
|
||||
Context.Emit(OpCodes.Xor);
|
||||
Context.Emit(OpCodes.And);
|
||||
|
||||
EmitDataLoadOpers(Context);
|
||||
|
||||
Context.Emit(OpCodes.Xor);
|
||||
Context.Emit(OpCodes.Not);
|
||||
Context.Emit(OpCodes.And);
|
||||
|
||||
Context.EmitLdc_I(0);
|
||||
|
||||
Context.Emit(OpCodes.Clt);
|
||||
|
||||
Context.EmitStflg((int)APState.VBit);
|
||||
}
|
||||
|
||||
public static void EmitSubsCCheck(AILEmitterCtx Context)
|
||||
{
|
||||
//C = Rn == Rm || Rn > Rm
|
||||
EmitDataLoadOpers(Context);
|
||||
|
||||
Context.Emit(OpCodes.Ceq);
|
||||
|
||||
EmitDataLoadOpers(Context);
|
||||
|
||||
Context.Emit(OpCodes.Cgt_Un);
|
||||
Context.Emit(OpCodes.Or);
|
||||
|
||||
Context.EmitStflg((int)APState.CBit);
|
||||
}
|
||||
|
||||
public static void EmitSubsVCheck(AILEmitterCtx Context)
|
||||
{
|
||||
//V = (Rd ^ Rn) & (Rn ^ Rm) < 0
|
||||
Context.Emit(OpCodes.Dup);
|
||||
|
||||
EmitDataLoadRn(Context);
|
||||
|
||||
Context.Emit(OpCodes.Xor);
|
||||
|
||||
EmitDataLoadOpers(Context);
|
||||
|
||||
Context.Emit(OpCodes.Xor);
|
||||
Context.Emit(OpCodes.And);
|
||||
|
||||
Context.EmitLdc_I(0);
|
||||
|
||||
Context.Emit(OpCodes.Clt);
|
||||
|
||||
Context.EmitStflg((int)APState.VBit);
|
||||
}
|
||||
|
||||
public static void EmitDataLoadRm(AILEmitterCtx Context)
|
||||
{
|
||||
Context.EmitLdintzr(((IAOpCodeAluRs)Context.CurrOp).Rm);
|
||||
}
|
||||
|
||||
public static void EmitDataLoadOpers(AILEmitterCtx Context)
|
||||
{
|
||||
EmitDataLoadRn(Context);
|
||||
EmitDataLoadOper2(Context);
|
||||
}
|
||||
|
||||
public static void EmitDataLoadRn(AILEmitterCtx Context)
|
||||
{
|
||||
IAOpCodeAlu Op = (IAOpCodeAlu)Context.CurrOp;
|
||||
|
||||
if (Op.DataOp == ADataOp.Logical || Op is IAOpCodeAluRs)
|
||||
{
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
}
|
||||
else
|
||||
{
|
||||
Context.EmitLdint(Op.Rn);
|
||||
}
|
||||
}
|
||||
|
||||
public static void EmitDataLoadOper2(AILEmitterCtx Context)
|
||||
{
|
||||
switch (Context.CurrOp)
|
||||
{
|
||||
case IAOpCodeAluImm Op:
|
||||
Context.EmitLdc_I(Op.Imm);
|
||||
break;
|
||||
|
||||
case IAOpCodeAluRs Op:
|
||||
Context.EmitLdintzr(Op.Rm);
|
||||
|
||||
switch (Op.ShiftType)
|
||||
{
|
||||
case AShiftType.Lsl: Context.EmitLsl(Op.Shift); break;
|
||||
case AShiftType.Lsr: Context.EmitLsr(Op.Shift); break;
|
||||
case AShiftType.Asr: Context.EmitAsr(Op.Shift); break;
|
||||
case AShiftType.Ror: Context.EmitRor(Op.Shift); break;
|
||||
}
|
||||
break;
|
||||
|
||||
case IAOpCodeAluRx Op:
|
||||
Context.EmitLdintzr(Op.Rm);
|
||||
Context.EmitCast(Op.IntType);
|
||||
Context.EmitLsl(Op.Shift);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void EmitDataStore(AILEmitterCtx Context) => EmitDataStore(Context, false);
|
||||
public static void EmitDataStoreS(AILEmitterCtx Context) => EmitDataStore(Context, true);
|
||||
|
||||
public static void EmitDataStore(AILEmitterCtx Context, bool SetFlags)
|
||||
{
|
||||
IAOpCodeAlu Op = (IAOpCodeAlu)Context.CurrOp;
|
||||
|
||||
if (SetFlags || Op is IAOpCodeAluRs)
|
||||
{
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
else
|
||||
{
|
||||
Context.EmitStint(Op.Rd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
208
Ryujinx/Cpu/Instruction/AInstEmitBfm.cs
Normal file
208
Ryujinx/Cpu/Instruction/AInstEmitBfm.cs
Normal file
|
@ -0,0 +1,208 @@
|
|||
using ChocolArm64.Decoder;
|
||||
using ChocolArm64.State;
|
||||
using ChocolArm64.Translation;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace ChocolArm64.Instruction
|
||||
{
|
||||
static partial class AInstEmit
|
||||
{
|
||||
public static void Bfm(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeBfm Op = (AOpCodeBfm)Context.CurrOp;
|
||||
|
||||
EmitBfmLoadRn(Context);
|
||||
|
||||
Context.EmitLdintzr(Op.Rd);
|
||||
Context.EmitLdc_I(~Op.WMask & Op.TMask);
|
||||
|
||||
Context.Emit(OpCodes.And);
|
||||
Context.Emit(OpCodes.Or);
|
||||
|
||||
Context.EmitLdintzr(Op.Rd);
|
||||
Context.EmitLdc_I(~Op.TMask);
|
||||
|
||||
Context.Emit(OpCodes.And);
|
||||
Context.Emit(OpCodes.Or);
|
||||
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Sbfm(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeBfm Op = (AOpCodeBfm)Context.CurrOp;
|
||||
|
||||
int BitsCount = Op.GetBitsCount();
|
||||
|
||||
if (Op.Pos + 1 == BitsCount)
|
||||
{
|
||||
EmitBfmShift(Context, OpCodes.Shr);
|
||||
}
|
||||
else if (Op.Pos < Op.Shift)
|
||||
{
|
||||
EmitSbfiz(Context);
|
||||
}
|
||||
else if (Op.Pos == 7 && Op.Shift == 0)
|
||||
{
|
||||
EmitSbfmCast(Context, OpCodes.Conv_I1);
|
||||
}
|
||||
else if (Op.Pos == 15 && Op.Shift == 0)
|
||||
{
|
||||
EmitSbfmCast(Context, OpCodes.Conv_I2);
|
||||
}
|
||||
else if (Op.Pos == 31 && Op.Shift == 0)
|
||||
{
|
||||
EmitSbfmCast(Context, OpCodes.Conv_I4);
|
||||
}
|
||||
else if (Op.Shift == 0)
|
||||
{
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
|
||||
Context.EmitLsl(BitsCount - 1 - Op.Pos);
|
||||
Context.EmitAsr(BitsCount - 1);
|
||||
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitBfmLoadRn(Context);
|
||||
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
|
||||
Context.EmitLsl(BitsCount - 1 - Op.Pos);
|
||||
Context.EmitAsr(BitsCount - 1);
|
||||
|
||||
Context.EmitLdc_I(~Op.TMask);
|
||||
|
||||
Context.Emit(OpCodes.And);
|
||||
Context.Emit(OpCodes.Or);
|
||||
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Ubfm(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeBfm Op = (AOpCodeBfm)Context.CurrOp;
|
||||
|
||||
if (Op.Pos + 1 == Op.GetBitsCount())
|
||||
{
|
||||
EmitBfmShift(Context, OpCodes.Shr_Un);
|
||||
}
|
||||
else if (Op.Pos < Op.Shift)
|
||||
{
|
||||
EmitUbfiz(Context);
|
||||
}
|
||||
else if (Op.Pos + 1 == Op.Shift)
|
||||
{
|
||||
EmitBfmLsl(Context);
|
||||
}
|
||||
else if (Op.Pos == 7 && Op.Shift == 0)
|
||||
{
|
||||
EmitUbfmCast(Context, OpCodes.Conv_U1);
|
||||
}
|
||||
else if (Op.Pos == 15 && Op.Shift == 0)
|
||||
{
|
||||
EmitUbfmCast(Context, OpCodes.Conv_U2);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitBfmLoadRn(Context);
|
||||
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitSbfiz(AILEmitterCtx Context) => EmitBfiz(Context, true);
|
||||
private static void EmitUbfiz(AILEmitterCtx Context) => EmitBfiz(Context, false);
|
||||
|
||||
private static void EmitBfiz(AILEmitterCtx Context, bool Signed)
|
||||
{
|
||||
AOpCodeBfm Op = (AOpCodeBfm)Context.CurrOp;
|
||||
|
||||
int Width = Op.Pos + 1;
|
||||
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
|
||||
Context.EmitLsl(Op.GetBitsCount() - Width);
|
||||
|
||||
if (Signed)
|
||||
{
|
||||
Context.EmitAsr(Op.Shift - Width);
|
||||
}
|
||||
else
|
||||
{
|
||||
Context.EmitLsr(Op.Shift - Width);
|
||||
}
|
||||
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
|
||||
private static void EmitSbfmCast(AILEmitterCtx Context, OpCode ILOp)
|
||||
{
|
||||
EmitBfmCast(Context, ILOp, true);
|
||||
}
|
||||
|
||||
private static void EmitUbfmCast(AILEmitterCtx Context, OpCode ILOp)
|
||||
{
|
||||
EmitBfmCast(Context, ILOp, false);
|
||||
}
|
||||
|
||||
private static void EmitBfmCast(AILEmitterCtx Context, OpCode ILOp, bool Signed)
|
||||
{
|
||||
AOpCodeBfm Op = (AOpCodeBfm)Context.CurrOp;
|
||||
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
|
||||
Context.Emit(ILOp);
|
||||
|
||||
if (Op.RegisterSize != ARegisterSize.Int32)
|
||||
{
|
||||
Context.Emit(Signed
|
||||
? OpCodes.Conv_I8
|
||||
: OpCodes.Conv_U8);
|
||||
}
|
||||
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
|
||||
private static void EmitBfmShift(AILEmitterCtx Context, OpCode ILOp)
|
||||
{
|
||||
AOpCodeBfm Op = (AOpCodeBfm)Context.CurrOp;
|
||||
|
||||
if (Op.Shift > 0)
|
||||
{
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
Context.EmitLdc_I4(Op.Shift);
|
||||
|
||||
Context.Emit(ILOp);
|
||||
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitBfmLsl(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeBfm Op = (AOpCodeBfm)Context.CurrOp;
|
||||
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
|
||||
Context.EmitLsl(Op.GetBitsCount() - Op.Shift);
|
||||
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
|
||||
private static void EmitBfmLoadRn(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeBfm Op = (AOpCodeBfm)Context.CurrOp;
|
||||
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
|
||||
Context.EmitRor(Op.Shift);
|
||||
|
||||
Context.EmitLdc_I(Op.WMask & Op.TMask);
|
||||
|
||||
Context.Emit(OpCodes.And);
|
||||
}
|
||||
}
|
||||
}
|
81
Ryujinx/Cpu/Instruction/AInstEmitCcmp.cs
Normal file
81
Ryujinx/Cpu/Instruction/AInstEmitCcmp.cs
Normal file
|
@ -0,0 +1,81 @@
|
|||
using ChocolArm64.Decoder;
|
||||
using ChocolArm64.State;
|
||||
using ChocolArm64.Translation;
|
||||
using System;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
using static ChocolArm64.Instruction.AInstEmitAluHelper;
|
||||
|
||||
namespace ChocolArm64.Instruction
|
||||
{
|
||||
static partial class AInstEmit
|
||||
{
|
||||
private enum CcmpOp
|
||||
{
|
||||
Cmp,
|
||||
Cmn
|
||||
}
|
||||
|
||||
public static void Ccmn(AILEmitterCtx Context) => EmitCcmp(Context, CcmpOp.Cmn);
|
||||
public static void Ccmp(AILEmitterCtx Context) => EmitCcmp(Context, CcmpOp.Cmp);
|
||||
|
||||
private static void EmitCcmp(AILEmitterCtx Context, CcmpOp CmpOp)
|
||||
{
|
||||
AOpCodeCcmp Op = (AOpCodeCcmp)Context.CurrOp;
|
||||
|
||||
AILLabel LblTrue = new AILLabel();
|
||||
AILLabel LblEnd = new AILLabel();
|
||||
|
||||
Context.EmitCondBranch(LblTrue, Op.Cond);
|
||||
|
||||
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);
|
||||
|
||||
EmitDataLoadOpers(Context);
|
||||
|
||||
if (CmpOp == CcmpOp.Cmp)
|
||||
{
|
||||
Context.Emit(OpCodes.Sub);
|
||||
|
||||
Context.EmitZNFlagCheck();
|
||||
|
||||
EmitSubsCCheck(Context);
|
||||
EmitSubsVCheck(Context);
|
||||
}
|
||||
else if (CmpOp == CcmpOp.Cmn)
|
||||
{
|
||||
Context.Emit(OpCodes.Add);
|
||||
|
||||
Context.EmitZNFlagCheck();
|
||||
|
||||
EmitAddsCCheck(Context);
|
||||
EmitAddsVCheck(Context);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException(nameof(CmpOp));
|
||||
}
|
||||
|
||||
Context.Emit(OpCodes.Pop);
|
||||
|
||||
Context.MarkLabel(LblEnd);
|
||||
}
|
||||
}
|
||||
}
|
59
Ryujinx/Cpu/Instruction/AInstEmitCsel.cs
Normal file
59
Ryujinx/Cpu/Instruction/AInstEmitCsel.cs
Normal file
|
@ -0,0 +1,59 @@
|
|||
using ChocolArm64.Decoder;
|
||||
using ChocolArm64.Translation;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace ChocolArm64.Instruction
|
||||
{
|
||||
static partial class AInstEmit
|
||||
{
|
||||
private enum CselOperation
|
||||
{
|
||||
None,
|
||||
Increment,
|
||||
Invert,
|
||||
Negate
|
||||
}
|
||||
|
||||
public static void Csel(AILEmitterCtx Context) => EmitCsel(Context, CselOperation.None);
|
||||
public static void Csinc(AILEmitterCtx Context) => EmitCsel(Context, CselOperation.Increment);
|
||||
public static void Csinv(AILEmitterCtx Context) => EmitCsel(Context, CselOperation.Invert);
|
||||
public static void Csneg(AILEmitterCtx Context) => EmitCsel(Context, CselOperation.Negate);
|
||||
|
||||
private static void EmitCsel(AILEmitterCtx Context, CselOperation CselOp)
|
||||
{
|
||||
AOpCodeCsel Op = (AOpCodeCsel)Context.CurrOp;
|
||||
|
||||
AILLabel LblTrue = new AILLabel();
|
||||
AILLabel LblEnd = new AILLabel();
|
||||
|
||||
Context.EmitCondBranch(LblTrue, Op.Cond);
|
||||
Context.EmitLdintzr(Op.Rm);
|
||||
|
||||
if (CselOp == CselOperation.Increment)
|
||||
{
|
||||
Context.EmitLdc_I(1);
|
||||
|
||||
Context.Emit(OpCodes.Add);
|
||||
}
|
||||
else if (CselOp == CselOperation.Invert)
|
||||
{
|
||||
Context.Emit(OpCodes.Not);
|
||||
}
|
||||
else if (CselOp == CselOperation.Negate)
|
||||
{
|
||||
Context.Emit(OpCodes.Neg);
|
||||
}
|
||||
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
|
||||
Context.Emit(OpCodes.Br_S, LblEnd);
|
||||
|
||||
Context.MarkLabel(LblTrue);
|
||||
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
|
||||
Context.MarkLabel(LblEnd);
|
||||
}
|
||||
}
|
||||
}
|
33
Ryujinx/Cpu/Instruction/AInstEmitException.cs
Normal file
33
Ryujinx/Cpu/Instruction/AInstEmitException.cs
Normal file
|
@ -0,0 +1,33 @@
|
|||
using ChocolArm64.Decoder;
|
||||
using ChocolArm64.State;
|
||||
using ChocolArm64.Translation;
|
||||
using System;
|
||||
|
||||
namespace ChocolArm64.Instruction
|
||||
{
|
||||
static partial class AInstEmit
|
||||
{
|
||||
public static void Svc(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeException Op = (AOpCodeException)Context.CurrOp;
|
||||
|
||||
Context.EmitStoreState();
|
||||
|
||||
Context.EmitLdarg(ATranslatedSub.RegistersArgIdx);
|
||||
|
||||
Context.EmitLdc_I4(Op.Id);
|
||||
|
||||
Context.EmitCall(typeof(ARegisters), nameof(ARegisters.OnSvcCall));
|
||||
|
||||
if (Context.CurrBlock.Next != null)
|
||||
{
|
||||
Context.EmitLoadState(Context.CurrBlock.Next);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Und(AILEmitterCtx Context)
|
||||
{
|
||||
throw new Exception("und inst! " + Context.CurrOp.Position.ToString("x8"));
|
||||
}
|
||||
}
|
||||
}
|
124
Ryujinx/Cpu/Instruction/AInstEmitFlow.cs
Normal file
124
Ryujinx/Cpu/Instruction/AInstEmitFlow.cs
Normal file
|
@ -0,0 +1,124 @@
|
|||
using ChocolArm64.Decoder;
|
||||
using ChocolArm64.State;
|
||||
using ChocolArm64.Translation;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace ChocolArm64.Instruction
|
||||
{
|
||||
static partial class AInstEmit
|
||||
{
|
||||
public static void B(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeBImmAl Op = (AOpCodeBImmAl)Context.CurrOp;
|
||||
|
||||
Context.Emit(OpCodes.Br, Context.GetLabel(Op.Imm));
|
||||
}
|
||||
|
||||
public static void B_Cond(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeBImmCond Op = (AOpCodeBImmCond)Context.CurrOp;
|
||||
|
||||
Context.EmitCondBranch(Context.GetLabel(Op.Imm), Op.Cond);
|
||||
}
|
||||
|
||||
public static void Bl(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeBImmAl Op = (AOpCodeBImmAl)Context.CurrOp;
|
||||
|
||||
Context.EmitLdc_I(Op.Position + 4);
|
||||
Context.EmitStint(ARegisters.LRIndex);
|
||||
Context.EmitStoreState();
|
||||
|
||||
if (Context.TryOptEmitSubroutineCall())
|
||||
{
|
||||
//Note: the return value of the called method will be placed
|
||||
//at the Stack, the return value is always a Int64 with the
|
||||
//return address of the function. We check if the address is
|
||||
//correct, if it isn't we keep returning until we reach the dispatcher.
|
||||
Context.Emit(OpCodes.Dup);
|
||||
|
||||
Context.EmitLdc_I8(Op.Position + 4);
|
||||
|
||||
AILLabel LblContinue = new AILLabel();
|
||||
|
||||
Context.Emit(OpCodes.Beq_S, LblContinue);
|
||||
Context.Emit(OpCodes.Ret);
|
||||
|
||||
Context.MarkLabel(LblContinue);
|
||||
|
||||
Context.Emit(OpCodes.Pop);
|
||||
|
||||
if (Context.CurrBlock.Next != null)
|
||||
{
|
||||
Context.EmitLoadState(Context.CurrBlock.Next);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Context.EmitLdc_I8(Op.Imm);
|
||||
|
||||
Context.Emit(OpCodes.Ret);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Blr(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeBReg Op = (AOpCodeBReg)Context.CurrOp;
|
||||
|
||||
Context.EmitLdc_I(Op.Position + 4);
|
||||
Context.EmitStint(ARegisters.LRIndex);
|
||||
Context.EmitStoreState();
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
|
||||
Context.Emit(OpCodes.Ret);
|
||||
}
|
||||
|
||||
public static void Br(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeBReg Op = (AOpCodeBReg)Context.CurrOp;
|
||||
|
||||
Context.EmitStoreState();
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
|
||||
Context.Emit(OpCodes.Ret);
|
||||
}
|
||||
|
||||
public static void Cbnz(AILEmitterCtx Context) => EmitCb(Context, OpCodes.Bne_Un);
|
||||
public static void Cbz(AILEmitterCtx Context) => EmitCb(Context, OpCodes.Beq);
|
||||
|
||||
private static void EmitCb(AILEmitterCtx Context, OpCode ILOp)
|
||||
{
|
||||
AOpCodeBImmCmp Op = (AOpCodeBImmCmp)Context.CurrOp;
|
||||
|
||||
Context.EmitLdintzr(Op.Rt);
|
||||
Context.EmitLdc_I(0);
|
||||
|
||||
Context.Emit(ILOp, Context.GetLabel(Op.Imm));
|
||||
}
|
||||
|
||||
public static void Ret(AILEmitterCtx Context)
|
||||
{
|
||||
Context.EmitStoreState();
|
||||
Context.EmitLdint(ARegisters.LRIndex);
|
||||
|
||||
Context.Emit(OpCodes.Ret);
|
||||
}
|
||||
|
||||
public static void Tbnz(AILEmitterCtx Context) => EmitTb(Context, OpCodes.Bne_Un);
|
||||
public static void Tbz(AILEmitterCtx Context) => EmitTb(Context, OpCodes.Beq);
|
||||
|
||||
private static void EmitTb(AILEmitterCtx Context, OpCode ILOp)
|
||||
{
|
||||
AOpCodeBImmTest Op = (AOpCodeBImmTest)Context.CurrOp;
|
||||
|
||||
Context.EmitLdintzr(Op.Rt);
|
||||
Context.EmitLdc_I(1L << Op.Pos);
|
||||
|
||||
Context.Emit(OpCodes.And);
|
||||
|
||||
Context.EmitLdc_I(0);
|
||||
|
||||
Context.Emit(ILOp, Context.GetLabel(Op.Imm));
|
||||
}
|
||||
}
|
||||
}
|
252
Ryujinx/Cpu/Instruction/AInstEmitMemory.cs
Normal file
252
Ryujinx/Cpu/Instruction/AInstEmitMemory.cs
Normal file
|
@ -0,0 +1,252 @@
|
|||
using ChocolArm64.Decoder;
|
||||
using ChocolArm64.Translation;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
using static ChocolArm64.Instruction.AInstEmitMemoryHelper;
|
||||
|
||||
namespace ChocolArm64.Instruction
|
||||
{
|
||||
static partial class AInstEmit
|
||||
{
|
||||
public static void Adr(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeAdr Op = (AOpCodeAdr)Context.CurrOp;
|
||||
|
||||
Context.EmitLdc_I(Op.Position + Op.Imm);
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Adrp(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeAdr Op = (AOpCodeAdr)Context.CurrOp;
|
||||
|
||||
Context.EmitLdc_I((Op.Position & ~0xfff) + (Op.Imm << 12));
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Ldr(AILEmitterCtx Context) => EmitLdr(Context, false);
|
||||
public static void Ldrs(AILEmitterCtx Context) => EmitLdr(Context, true);
|
||||
|
||||
public static void EmitLdr(AILEmitterCtx Context, bool Signed)
|
||||
{
|
||||
AOpCodeMem Op = (AOpCodeMem)Context.CurrOp;
|
||||
|
||||
Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
|
||||
|
||||
EmitLoadAddress(Context);
|
||||
|
||||
if (Signed && Op.Extend64)
|
||||
{
|
||||
EmitReadSx64Call(Context, Op.Size);
|
||||
}
|
||||
else if (Signed)
|
||||
{
|
||||
EmitReadSx32Call(Context, Op.Size);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitReadZxCall(Context, Op.Size);
|
||||
}
|
||||
|
||||
if (Op is IAOpCodeSimd)
|
||||
{
|
||||
Context.EmitStvec(Op.Rt);
|
||||
}
|
||||
else
|
||||
{
|
||||
Context.EmitStintzr(Op.Rt);
|
||||
}
|
||||
|
||||
EmitWBackIfNeeded(Context);
|
||||
}
|
||||
|
||||
public static void LdrLit(AILEmitterCtx Context)
|
||||
{
|
||||
IAOpCodeLit Op = (IAOpCodeLit)Context.CurrOp;
|
||||
|
||||
if (Op.Prefetch)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
|
||||
Context.EmitLdc_I8(Op.Imm);
|
||||
|
||||
if (Op.Signed)
|
||||
{
|
||||
EmitReadSx64Call(Context, Op.Size);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitReadZxCall(Context, Op.Size);
|
||||
}
|
||||
|
||||
if (Op is IAOpCodeSimd)
|
||||
{
|
||||
Context.EmitStvec(Op.Rt);
|
||||
}
|
||||
else
|
||||
{
|
||||
Context.EmitStint(Op.Rt);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Ldp(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeMemPair Op = (AOpCodeMemPair)Context.CurrOp;
|
||||
|
||||
void EmitReadAndStore(int Rt)
|
||||
{
|
||||
if (Op.Extend64)
|
||||
{
|
||||
EmitReadSx64Call(Context, Op.Size);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitReadZxCall(Context, Op.Size);
|
||||
}
|
||||
|
||||
if (Op is IAOpCodeSimd)
|
||||
{
|
||||
Context.EmitStvec(Rt);
|
||||
}
|
||||
else
|
||||
{
|
||||
Context.EmitStintzr(Rt);
|
||||
}
|
||||
}
|
||||
|
||||
Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
|
||||
|
||||
EmitLoadAddress(Context);
|
||||
|
||||
EmitReadAndStore(Op.Rt);
|
||||
|
||||
Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
|
||||
Context.EmitLdtmp();
|
||||
Context.EmitLdc_I8(1 << Op.Size);
|
||||
|
||||
Context.Emit(OpCodes.Add);
|
||||
|
||||
EmitReadAndStore(Op.Rt2);
|
||||
|
||||
EmitWBackIfNeeded(Context);
|
||||
}
|
||||
|
||||
public static void Str(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeMem Op = (AOpCodeMem)Context.CurrOp;
|
||||
|
||||
Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
|
||||
|
||||
EmitLoadAddress(Context);
|
||||
|
||||
if (Op is IAOpCodeSimd)
|
||||
{
|
||||
Context.EmitLdvec(Op.Rt);
|
||||
}
|
||||
else
|
||||
{
|
||||
Context.EmitLdintzr(Op.Rt);
|
||||
}
|
||||
|
||||
EmitWriteCall(Context, Op.Size);
|
||||
|
||||
EmitWBackIfNeeded(Context);
|
||||
}
|
||||
|
||||
public static void Stp(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeMemPair Op = (AOpCodeMemPair)Context.CurrOp;
|
||||
|
||||
Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
|
||||
|
||||
EmitLoadAddress(Context);
|
||||
|
||||
if (Op is IAOpCodeSimd)
|
||||
{
|
||||
Context.EmitLdvec(Op.Rt);
|
||||
}
|
||||
else
|
||||
{
|
||||
Context.EmitLdintzr(Op.Rt);
|
||||
}
|
||||
|
||||
EmitWriteCall(Context, Op.Size);
|
||||
|
||||
Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
|
||||
Context.EmitLdtmp();
|
||||
Context.EmitLdc_I8(1 << Op.Size);
|
||||
|
||||
Context.Emit(OpCodes.Add);
|
||||
|
||||
if (Op is IAOpCodeSimd)
|
||||
{
|
||||
Context.EmitLdvec(Op.Rt2);
|
||||
}
|
||||
else
|
||||
{
|
||||
Context.EmitLdintzr(Op.Rt2);
|
||||
}
|
||||
|
||||
EmitWriteCall(Context, Op.Size);
|
||||
|
||||
EmitWBackIfNeeded(Context);
|
||||
}
|
||||
|
||||
private static void EmitLoadAddress(AILEmitterCtx Context)
|
||||
{
|
||||
switch (Context.CurrOp)
|
||||
{
|
||||
case AOpCodeMemImm Op:
|
||||
Context.EmitLdint(Op.Rn);
|
||||
|
||||
if (!Op.PostIdx)
|
||||
{
|
||||
//Pre-indexing.
|
||||
Context.EmitLdc_I(Op.Imm);
|
||||
|
||||
Context.Emit(OpCodes.Add);
|
||||
}
|
||||
break;
|
||||
|
||||
case AOpCodeMemReg Op:
|
||||
Context.EmitLdint(Op.Rn);
|
||||
Context.EmitLdintzr(Op.Rm);
|
||||
Context.EmitCast(Op.IntType);
|
||||
|
||||
if (Op.Shift)
|
||||
{
|
||||
Context.EmitLsl(Op.Size);
|
||||
}
|
||||
|
||||
Context.Emit(OpCodes.Add);
|
||||
break;
|
||||
}
|
||||
|
||||
//Save address to Scratch var since the register value may change.
|
||||
Context.Emit(OpCodes.Dup);
|
||||
|
||||
Context.EmitSttmp();
|
||||
}
|
||||
|
||||
private static void EmitWBackIfNeeded(AILEmitterCtx Context)
|
||||
{
|
||||
//Check whenever the current OpCode has post-indexed write back, if so write it.
|
||||
//Note: AOpCodeMemPair inherits from AOpCodeMemImm, so this works for both.
|
||||
if (Context.CurrOp is AOpCodeMemImm Op && Op.WBack)
|
||||
{
|
||||
Context.EmitLdtmp();
|
||||
|
||||
if (Op.PostIdx)
|
||||
{
|
||||
Context.EmitLdc_I(Op.Imm);
|
||||
|
||||
Context.Emit(OpCodes.Add);
|
||||
}
|
||||
|
||||
Context.EmitStint(Op.Rn);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
180
Ryujinx/Cpu/Instruction/AInstEmitMemoryEx.cs
Normal file
180
Ryujinx/Cpu/Instruction/AInstEmitMemoryEx.cs
Normal file
|
@ -0,0 +1,180 @@
|
|||
using ChocolArm64.Decoder;
|
||||
using ChocolArm64.Memory;
|
||||
using ChocolArm64.Translation;
|
||||
using System;
|
||||
using System.Reflection.Emit;
|
||||
using System.Threading;
|
||||
|
||||
using static ChocolArm64.Instruction.AInstEmitMemoryHelper;
|
||||
|
||||
namespace ChocolArm64.Instruction
|
||||
{
|
||||
static partial class AInstEmit
|
||||
{
|
||||
[Flags]
|
||||
private enum AccessType
|
||||
{
|
||||
None = 0,
|
||||
Ordered = 1,
|
||||
Exclusive = 2,
|
||||
OrderedEx = Ordered | Exclusive
|
||||
}
|
||||
|
||||
public static void Clrex(AILEmitterCtx Context)
|
||||
{
|
||||
EmitMemoryCall(Context, nameof(AMemory.ClearExclusive));
|
||||
}
|
||||
|
||||
public static void Dmb(AILEmitterCtx Context) => EmitBarrier(Context);
|
||||
public static void Dsb(AILEmitterCtx Context) => EmitBarrier(Context);
|
||||
|
||||
public static void Ldar(AILEmitterCtx Context) => EmitLdr(Context, AccessType.Ordered);
|
||||
public static void Ldaxr(AILEmitterCtx Context) => EmitLdr(Context, AccessType.OrderedEx);
|
||||
public static void Ldxr(AILEmitterCtx Context) => EmitLdr(Context, AccessType.Exclusive);
|
||||
public static void Ldxp(AILEmitterCtx Context) => EmitLdp(Context, AccessType.Exclusive);
|
||||
public static void Ldaxp(AILEmitterCtx Context) => EmitLdp(Context, AccessType.OrderedEx);
|
||||
|
||||
private static void EmitLdr(AILEmitterCtx Context, AccessType AccType)
|
||||
{
|
||||
EmitLoad(Context, AccType, false);
|
||||
}
|
||||
|
||||
private static void EmitLdp(AILEmitterCtx Context, AccessType AccType)
|
||||
{
|
||||
EmitLoad(Context, AccType, true);
|
||||
}
|
||||
|
||||
private static void EmitLoad(AILEmitterCtx Context, AccessType AccType, bool Pair)
|
||||
{
|
||||
AOpCodeMemEx Op = (AOpCodeMemEx)Context.CurrOp;
|
||||
|
||||
Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
|
||||
Context.EmitLdint(Op.Rn);
|
||||
|
||||
EmitReadZxCall(Context, Op.Size);
|
||||
|
||||
Context.EmitStintzr(Op.Rt);
|
||||
|
||||
if (Pair)
|
||||
{
|
||||
Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
|
||||
Context.EmitLdint(Op.Rn);
|
||||
Context.EmitLdc_I(8 << Op.Size);
|
||||
|
||||
Context.Emit(OpCodes.Add);
|
||||
|
||||
EmitReadZxCall(Context, Op.Size);
|
||||
|
||||
Context.EmitStintzr(Op.Rt2);
|
||||
}
|
||||
|
||||
if (AccType.HasFlag(AccessType.Exclusive))
|
||||
{
|
||||
EmitMemoryCall(Context, nameof(AMemory.SetExclusive), Op.Rn);
|
||||
}
|
||||
|
||||
if (AccType.HasFlag(AccessType.Ordered))
|
||||
{
|
||||
EmitBarrier(Context);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Pfrm(AILEmitterCtx Context)
|
||||
{
|
||||
//Memory Prefetch, execute as no-op.
|
||||
}
|
||||
|
||||
public static void Stlr(AILEmitterCtx Context) => EmitStr(Context, AccessType.Ordered);
|
||||
public static void Stlxr(AILEmitterCtx Context) => EmitStr(Context, AccessType.OrderedEx);
|
||||
public static void Stxr(AILEmitterCtx Context) => EmitStr(Context, AccessType.Exclusive);
|
||||
public static void Stxp(AILEmitterCtx Context) => EmitStp(Context, AccessType.Exclusive);
|
||||
public static void Stlxp(AILEmitterCtx Context) => EmitStp(Context, AccessType.OrderedEx);
|
||||
|
||||
private static void EmitStr(AILEmitterCtx Context, AccessType AccType)
|
||||
{
|
||||
EmitStore(Context, AccType, false);
|
||||
}
|
||||
|
||||
private static void EmitStp(AILEmitterCtx Context, AccessType AccType)
|
||||
{
|
||||
EmitStore(Context, AccType, true);
|
||||
}
|
||||
|
||||
private static void EmitStore(AILEmitterCtx Context, AccessType AccType, bool Pair)
|
||||
{
|
||||
AOpCodeMemEx Op = (AOpCodeMemEx)Context.CurrOp;
|
||||
|
||||
if (AccType.HasFlag(AccessType.Ordered))
|
||||
{
|
||||
EmitBarrier(Context);
|
||||
}
|
||||
|
||||
AILLabel LblEx = new AILLabel();
|
||||
AILLabel LblEnd = new AILLabel();
|
||||
|
||||
if (AccType.HasFlag(AccessType.Exclusive))
|
||||
{
|
||||
EmitMemoryCall(Context, nameof(AMemory.TestExclusive), Op.Rn);
|
||||
|
||||
Context.Emit(OpCodes.Brtrue_S, LblEx);
|
||||
|
||||
Context.EmitLdc_I8(1);
|
||||
Context.EmitStintzr(Op.Rs);
|
||||
|
||||
Context.Emit(OpCodes.Br_S, LblEnd);
|
||||
}
|
||||
|
||||
Context.MarkLabel(LblEx);
|
||||
|
||||
Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
|
||||
Context.EmitLdint(Op.Rn);
|
||||
Context.EmitLdintzr(Op.Rt);
|
||||
|
||||
EmitWriteCall(Context, Op.Size);
|
||||
|
||||
if (Pair)
|
||||
{
|
||||
Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
|
||||
Context.EmitLdint(Op.Rn);
|
||||
Context.EmitLdc_I(8 << Op.Size);
|
||||
|
||||
Context.Emit(OpCodes.Add);
|
||||
|
||||
Context.EmitLdintzr(Op.Rt2);
|
||||
|
||||
EmitWriteCall(Context, Op.Size);
|
||||
}
|
||||
|
||||
if (AccType.HasFlag(AccessType.Exclusive))
|
||||
{
|
||||
Context.EmitLdc_I8(0);
|
||||
Context.EmitStintzr(Op.Rs);
|
||||
|
||||
Clrex(Context);
|
||||
}
|
||||
|
||||
Context.MarkLabel(LblEnd);
|
||||
}
|
||||
|
||||
private static void EmitMemoryCall(AILEmitterCtx Context, string Name, int Rn = -1)
|
||||
{
|
||||
Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
|
||||
Context.EmitLdarg(ATranslatedSub.RegistersArgIdx);
|
||||
|
||||
if (Rn != -1)
|
||||
{
|
||||
Context.EmitLdint(Rn);
|
||||
}
|
||||
|
||||
Context.EmitCall(typeof(AMemory), Name);
|
||||
}
|
||||
|
||||
private static void EmitBarrier(AILEmitterCtx Context)
|
||||
{
|
||||
//Note: This barrier is most likely not necessary, and probably
|
||||
//doesn't make any difference since we need to do a ton of stuff
|
||||
//(software MMU emulation) to read or write anything anyway.
|
||||
Context.EmitCall(typeof(Thread), nameof(Thread.MemoryBarrier));
|
||||
}
|
||||
}
|
||||
}
|
97
Ryujinx/Cpu/Instruction/AInstEmitMemoryHelper.cs
Normal file
97
Ryujinx/Cpu/Instruction/AInstEmitMemoryHelper.cs
Normal file
|
@ -0,0 +1,97 @@
|
|||
using ChocolArm64.Memory;
|
||||
using ChocolArm64.Translation;
|
||||
using System;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace ChocolArm64.Instruction
|
||||
{
|
||||
static class AInstEmitMemoryHelper
|
||||
{
|
||||
private enum Extension
|
||||
{
|
||||
Zx,
|
||||
Sx32,
|
||||
Sx64
|
||||
}
|
||||
|
||||
public static void EmitReadZxCall(AILEmitterCtx Context, int Size)
|
||||
{
|
||||
EmitReadCall(Context, Extension.Zx, Size);
|
||||
}
|
||||
|
||||
public static void EmitReadSx32Call(AILEmitterCtx Context, int Size)
|
||||
{
|
||||
EmitReadCall(Context, Extension.Sx32, Size);
|
||||
}
|
||||
|
||||
public static void EmitReadSx64Call(AILEmitterCtx Context, int Size)
|
||||
{
|
||||
EmitReadCall(Context, Extension.Sx64, Size);
|
||||
}
|
||||
|
||||
private static void EmitReadCall(AILEmitterCtx Context, Extension Ext, int Size)
|
||||
{
|
||||
if (Size < 0 || Size > 4)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(Size));
|
||||
}
|
||||
|
||||
string Name = null;
|
||||
|
||||
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 (Ext == Extension.Sx32 ||
|
||||
Ext == Extension.Sx64)
|
||||
{
|
||||
switch (Size)
|
||||
{
|
||||
case 0: Context.Emit(OpCodes.Conv_I1); break;
|
||||
case 1: Context.Emit(OpCodes.Conv_I2); break;
|
||||
case 2: Context.Emit(OpCodes.Conv_I4); break;
|
||||
}
|
||||
}
|
||||
|
||||
if (Size < 3)
|
||||
{
|
||||
Context.Emit(Ext == Extension.Sx64
|
||||
? OpCodes.Conv_I8
|
||||
: OpCodes.Conv_U8);
|
||||
}
|
||||
}
|
||||
|
||||
public static void EmitWriteCall(AILEmitterCtx Context, int Size)
|
||||
{
|
||||
if (Size < 0 || Size > 4)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(Size));
|
||||
}
|
||||
|
||||
if (Size < 3)
|
||||
{
|
||||
Context.Emit(OpCodes.Conv_I4);
|
||||
}
|
||||
|
||||
string Name = null;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
41
Ryujinx/Cpu/Instruction/AInstEmitMove.cs
Normal file
41
Ryujinx/Cpu/Instruction/AInstEmitMove.cs
Normal file
|
@ -0,0 +1,41 @@
|
|||
using ChocolArm64.Decoder;
|
||||
using ChocolArm64.Translation;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace ChocolArm64.Instruction
|
||||
{
|
||||
static partial class AInstEmit
|
||||
{
|
||||
public static void Movk(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeMov Op = (AOpCodeMov)Context.CurrOp;
|
||||
|
||||
Context.EmitLdintzr(Op.Rd);
|
||||
Context.EmitLdc_I(~(0xffffL << Op.Pos));
|
||||
|
||||
Context.Emit(OpCodes.And);
|
||||
|
||||
Context.EmitLdc_I(Op.Imm);
|
||||
|
||||
Context.Emit(OpCodes.Or);
|
||||
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Movn(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeMov Op = (AOpCodeMov)Context.CurrOp;
|
||||
|
||||
Context.EmitLdc_I(~Op.Imm);
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Movz(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeMov Op = (AOpCodeMov)Context.CurrOp;
|
||||
|
||||
Context.EmitLdc_I(Op.Imm);
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
}
|
||||
}
|
80
Ryujinx/Cpu/Instruction/AInstEmitMul.cs
Normal file
80
Ryujinx/Cpu/Instruction/AInstEmitMul.cs
Normal file
|
@ -0,0 +1,80 @@
|
|||
using ChocolArm64.Decoder;
|
||||
using ChocolArm64.Translation;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace ChocolArm64.Instruction
|
||||
{
|
||||
static partial class AInstEmit
|
||||
{
|
||||
public static void Madd(AILEmitterCtx Context) => EmitMul(Context, OpCodes.Add);
|
||||
public static void Msub(AILEmitterCtx Context) => EmitMul(Context, OpCodes.Sub);
|
||||
|
||||
private static void EmitMul(AILEmitterCtx Context, OpCode ILOp)
|
||||
{
|
||||
AOpCodeMul Op = (AOpCodeMul)Context.CurrOp;
|
||||
|
||||
Context.EmitLdintzr(Op.Ra);
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
Context.EmitLdintzr(Op.Rm);
|
||||
|
||||
Context.Emit(OpCodes.Mul);
|
||||
Context.Emit(ILOp);
|
||||
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Smaddl(AILEmitterCtx Context) => EmitMull(Context, OpCodes.Add, true);
|
||||
public static void Smsubl(AILEmitterCtx Context) => EmitMull(Context, OpCodes.Sub, true);
|
||||
public static void Umaddl(AILEmitterCtx Context) => EmitMull(Context, OpCodes.Add, false);
|
||||
public static void Umsubl(AILEmitterCtx Context) => EmitMull(Context, OpCodes.Sub, false);
|
||||
|
||||
private static void EmitMull(AILEmitterCtx Context, OpCode AddSubOp, bool Signed)
|
||||
{
|
||||
AOpCodeMul Op = (AOpCodeMul)Context.CurrOp;
|
||||
|
||||
OpCode CastOp = Signed
|
||||
? OpCodes.Conv_I8
|
||||
: OpCodes.Conv_U8;
|
||||
|
||||
Context.EmitLdintzr(Op.Ra);
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
|
||||
Context.Emit(OpCodes.Conv_I4);
|
||||
Context.Emit(CastOp);
|
||||
|
||||
Context.EmitLdintzr(Op.Rm);
|
||||
|
||||
Context.Emit(OpCodes.Conv_I4);
|
||||
Context.Emit(CastOp);
|
||||
Context.Emit(OpCodes.Mul);
|
||||
|
||||
Context.Emit(AddSubOp);
|
||||
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Smulh(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeMul Op = (AOpCodeMul)Context.CurrOp;
|
||||
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
Context.EmitLdintzr(Op.Rm);
|
||||
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SMulHi128));
|
||||
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Umulh(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeMul Op = (AOpCodeMul)Context.CurrOp;
|
||||
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
Context.EmitLdintzr(Op.Rm);
|
||||
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.UMulHi128));
|
||||
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
}
|
||||
}
|
659
Ryujinx/Cpu/Instruction/AInstEmitScalar.cs
Normal file
659
Ryujinx/Cpu/Instruction/AInstEmitScalar.cs
Normal file
|
@ -0,0 +1,659 @@
|
|||
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 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 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)
|
||||
{
|
||||
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 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);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
EmitMathOpCall(Context, Name);
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
965
Ryujinx/Cpu/Instruction/AInstEmitSimd.cs
Normal file
965
Ryujinx/Cpu/Instruction/AInstEmitSimd.cs
Normal file
|
@ -0,0 +1,965 @@
|
|||
using ChocolArm64.Decoder;
|
||||
using ChocolArm64.State;
|
||||
using ChocolArm64.Translation;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
using static ChocolArm64.Instruction.AInstEmitMemoryHelper;
|
||||
|
||||
namespace ChocolArm64.Instruction
|
||||
{
|
||||
static partial class AInstEmit
|
||||
{
|
||||
public static void Add_V(AILEmitterCtx Context) => EmitVectorBinaryZx(Context, OpCodes.Add);
|
||||
|
||||
public static void Addp_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
Context.EmitLdvec(Op.Rm);
|
||||
Context.EmitLdc_I4(Op.Size);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Addp64),
|
||||
nameof(ASoftFallback.Addp128));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Addv_V(AILEmitterCtx Context) => EmitVectorAddv(Context);
|
||||
|
||||
public static void And_V(AILEmitterCtx Context) => EmitVectorBinaryZx(Context, OpCodes.And);
|
||||
|
||||
public static void Bic_V(AILEmitterCtx Context) => EmitVectorBic(Context);
|
||||
public static void Bic_Vi(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdImm Op = (AOpCodeSimdImm)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rd);
|
||||
Context.EmitLdc_I8(Op.Imm);
|
||||
Context.EmitLdc_I4(Op.Size);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Bic_Vi64),
|
||||
nameof(ASoftFallback.Bic_Vi128));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Bsl_V(AILEmitterCtx Context) => EmitVectorBsl(Context);
|
||||
|
||||
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 Cnt_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Cnt64),
|
||||
nameof(ASoftFallback.Cnt128));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Dup_Gp(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp;
|
||||
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
Context.EmitLdc_I4(Op.Size);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Dup_Gp64),
|
||||
nameof(ASoftFallback.Dup_Gp128));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Dup_V(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_V64),
|
||||
nameof(ASoftFallback.Dup_V128));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Eor_V(AILEmitterCtx Context) => EmitVectorBinaryZx(Context, OpCodes.Xor);
|
||||
|
||||
public static void Fadd_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
Context.EmitLdvec(Op.Rm);
|
||||
Context.EmitLdc_I4(Op.SizeF);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Fadd64),
|
||||
nameof(ASoftFallback.Fadd128));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Fcvtzs_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
Context.EmitLdc_I4(Op.SizeF);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Fcvtzs_V64),
|
||||
nameof(ASoftFallback.Fcvtzs_V128));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Fcvtzu_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
Context.EmitLdc_I4(0);
|
||||
Context.EmitLdc_I4(Op.SizeF);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Fcvtzu_V_64),
|
||||
nameof(ASoftFallback.Fcvtzu_V_128));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Fcvtzu_V_Fix(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
Context.EmitLdc_I4((8 << (Op.Size + 1)) - Op.Imm);
|
||||
Context.EmitLdc_I4(Op.Size - 2);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Fcvtzu_V_64),
|
||||
nameof(ASoftFallback.Fcvtzu_V_128));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Fmla_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rd);
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
Context.EmitLdvec(Op.Rm);
|
||||
Context.EmitLdc_I4(Op.SizeF);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Fmla64),
|
||||
nameof(ASoftFallback.Fmla128));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Fmla_Vs(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdRegElem Op = (AOpCodeSimdRegElem)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rd);
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
Context.EmitLdvec(Op.Rm);
|
||||
Context.EmitLdc_I4(Op.Index);
|
||||
Context.EmitLdc_I4(Op.SizeF);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Fmla_Ve64),
|
||||
nameof(ASoftFallback.Fmla_Ve128));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Fmov_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdImm Op = (AOpCodeSimdImm)Context.CurrOp;
|
||||
|
||||
Context.EmitLdc_I8(Op.Imm);
|
||||
Context.EmitLdc_I4(Op.Size + 2);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Dup_Gp64),
|
||||
nameof(ASoftFallback.Dup_Gp128));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Fmul_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
Context.EmitLdvec(Op.Rm);
|
||||
Context.EmitLdc_I4(Op.SizeF);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Fmul64),
|
||||
nameof(ASoftFallback.Fmul128));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Fmul_Vs(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdRegElem Op = (AOpCodeSimdRegElem)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
Context.EmitLdvec(Op.Rm);
|
||||
Context.EmitLdc_I4(Op.Index);
|
||||
Context.EmitLdc_I4(Op.SizeF);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Fmul_Ve64),
|
||||
nameof(ASoftFallback.Fmul_Ve128));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Fsub_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
Context.EmitLdvec(Op.Rm);
|
||||
Context.EmitLdc_I4(Op.SizeF);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Fsub64),
|
||||
nameof(ASoftFallback.Fsub128));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Ins_Gp(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rd);
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
Context.EmitLdc_I4(Op.DstIndex);
|
||||
Context.EmitLdc_I4(Op.Size);
|
||||
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.Ins_Gp));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Ins_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rd);
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
Context.EmitLdc_I4(Op.SrcIndex);
|
||||
Context.EmitLdc_I4(Op.DstIndex);
|
||||
Context.EmitLdc_I4(Op.Size);
|
||||
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.Ins_V));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Ld__V(AILEmitterCtx Context) => EmitSimdMultLdSt(Context, IsLoad: true);
|
||||
|
||||
public static void Mla_V(AILEmitterCtx Context) => EmitVectorMla(Context);
|
||||
|
||||
public static void Movi_V(AILEmitterCtx Context) => EmitMovi_V(Context, false);
|
||||
|
||||
public static void Mul_V(AILEmitterCtx Context) => EmitVectorBinaryZx(Context, OpCodes.Mul);
|
||||
|
||||
public static void Mvni_V(AILEmitterCtx Context) => EmitMovi_V(Context, true);
|
||||
|
||||
private static void EmitMovi_V(AILEmitterCtx Context, bool Not)
|
||||
{
|
||||
AOpCodeSimdImm Op = (AOpCodeSimdImm)Context.CurrOp;
|
||||
|
||||
Context.EmitLdc_I8(Not ? ~Op.Imm : Op.Imm);
|
||||
Context.EmitLdc_I4(Op.Size);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Dup_Gp64),
|
||||
nameof(ASoftFallback.Dup_Gp128));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Neg_V(AILEmitterCtx Context) => EmitVectorUnarySx(Context, OpCodes.Neg);
|
||||
|
||||
public static void Not_V(AILEmitterCtx Context) => EmitVectorUnaryZx(Context, OpCodes.Not);
|
||||
|
||||
public static void Orr_V(AILEmitterCtx Context) => EmitVectorBinaryZx(Context, OpCodes.Or);
|
||||
|
||||
public static void Orr_Vi(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdImm Op = (AOpCodeSimdImm)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rd);
|
||||
Context.EmitLdc_I8(Op.Imm);
|
||||
Context.EmitLdc_I4(Op.Size);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Orr_Vi64),
|
||||
nameof(ASoftFallback.Orr_Vi128));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Saddw_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
Context.EmitLdvec(Op.Rm);
|
||||
Context.EmitLdc_I4(Op.Size);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Saddw),
|
||||
nameof(ASoftFallback.Saddw2));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Scvtf_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
Context.EmitLdc_I4(Op.SizeF);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Scvtf_V64),
|
||||
nameof(ASoftFallback.Scvtf_V128));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Shl_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
Context.EmitLdc_I4(Op.Imm - (8 << Op.Size));
|
||||
Context.EmitLdc_I4(Op.Size);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Shl64),
|
||||
nameof(ASoftFallback.Shl128));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Smax_V(AILEmitterCtx Context) => EmitVectorSmax(Context);
|
||||
public static void Smin_V(AILEmitterCtx Context) => EmitVectorSmin(Context);
|
||||
|
||||
public static void Sshll_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
Context.EmitLdc_I4(Op.Imm - (8 << Op.Size));
|
||||
Context.EmitLdc_I4(Op.Size);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Sshll),
|
||||
nameof(ASoftFallback.Sshll2));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Sshr_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
Context.EmitLdc_I4((8 << (Op.Size + 1)) - Op.Imm);
|
||||
Context.EmitLdc_I4(Op.Size);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Sshr64),
|
||||
nameof(ASoftFallback.Sshr128));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void St__V(AILEmitterCtx Context) => EmitSimdMultLdSt(Context, IsLoad: false);
|
||||
|
||||
public static void Sub_V(AILEmitterCtx Context) => EmitVectorBinaryZx(Context, OpCodes.Sub);
|
||||
|
||||
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 Uaddlv_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
Context.EmitLdc_I4(Op.Size);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Uaddlv64),
|
||||
nameof(ASoftFallback.Uaddlv128));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Uaddw_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
Context.EmitLdvec(Op.Rm);
|
||||
Context.EmitLdc_I4(Op.Size);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Uaddw),
|
||||
nameof(ASoftFallback.Uaddw2));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Ucvtf_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
|
||||
if (Op.Size == 0)
|
||||
{
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.Ucvtf_V_F));
|
||||
}
|
||||
else if (Op.Size == 1)
|
||||
{
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.Ucvtf_V_D));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
Context.EmitStvec(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);
|
||||
}
|
||||
|
||||
public static void Ushl_V(AILEmitterCtx Context) => EmitVectorUshl(Context);
|
||||
|
||||
public static void Ushll_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
Context.EmitLdc_I4(Op.Imm - (8 << Op.Size));
|
||||
Context.EmitLdc_I4(Op.Size);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Ushll),
|
||||
nameof(ASoftFallback.Ushll2));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Ushr_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
Context.EmitLdc_I4((8 << (Op.Size + 1)) - Op.Imm);
|
||||
Context.EmitLdc_I4(Op.Size);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Ushr64),
|
||||
nameof(ASoftFallback.Ushr128));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Usra_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rd);
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
Context.EmitLdc_I4((8 << (Op.Size + 1)) - Op.Imm);
|
||||
Context.EmitLdc_I4(Op.Size);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Usra64),
|
||||
nameof(ASoftFallback.Usra128));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Uzp1_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
Context.EmitLdvec(Op.Rm);
|
||||
Context.EmitLdc_I4(Op.Size);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Uzp1_V64),
|
||||
nameof(ASoftFallback.Uzp1_V128));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Xtn_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
Context.EmitLdc_I4(Op.Size);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Xtn),
|
||||
nameof(ASoftFallback.Xtn2));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
private static void EmitSimdMultLdSt(AILEmitterCtx Context, bool IsLoad)
|
||||
{
|
||||
AOpCodeSimdMemMult Op = (AOpCodeSimdMemMult)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.EmitLdvec(Rtt);
|
||||
Context.EmitLdc_I4(Elem);
|
||||
Context.EmitLdc_I4(Op.Size);
|
||||
Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
|
||||
Context.EmitLdint(Op.Rn);
|
||||
Context.EmitLdc_I8(Offset);
|
||||
|
||||
Context.Emit(OpCodes.Add);
|
||||
|
||||
EmitReadZxCall(Context, Op.Size);
|
||||
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.InsertVec));
|
||||
|
||||
Context.EmitStvec(Rtt);
|
||||
|
||||
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);
|
||||
|
||||
Context.EmitLdvec(Rtt);
|
||||
Context.EmitLdc_I4(Elem);
|
||||
Context.EmitLdc_I4(Op.Size);
|
||||
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.ExtractVec));
|
||||
|
||||
EmitWriteCall(Context, Op.Size);
|
||||
}
|
||||
|
||||
Offset += 1 << Op.Size;
|
||||
}
|
||||
|
||||
if (Op.WBack)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitVectorAddv(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||
|
||||
int Bytes = Context.CurrOp.GetBitsCount() >> 3;
|
||||
|
||||
EmitVectorZeroLower(Context, Op.Rd);
|
||||
EmitVectorZeroUpper(Context, Op.Rd);
|
||||
|
||||
Context.EmitLdvec(Op.Rd);
|
||||
Context.EmitLdc_I4(0);
|
||||
Context.EmitLdc_I4(Op.Size);
|
||||
|
||||
EmitVectorExtractZx(Context, Op.Rn, 0);
|
||||
|
||||
for (int Index = 1; Index < (Bytes >> Op.Size); Index++)
|
||||
{
|
||||
EmitVectorExtractZx(Context, Op.Rn, Index);
|
||||
|
||||
Context.Emit(OpCodes.Add);
|
||||
}
|
||||
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.InsertVec));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
private static void EmitVectorBic(AILEmitterCtx Context)
|
||||
{
|
||||
EmitVectorBinaryZx(Context, () =>
|
||||
{
|
||||
Context.Emit(OpCodes.Not);
|
||||
Context.Emit(OpCodes.And);
|
||||
});
|
||||
}
|
||||
|
||||
private static void EmitVectorBsl(AILEmitterCtx Context)
|
||||
{
|
||||
EmitVectorTernaryZx(Context, () =>
|
||||
{
|
||||
Context.EmitSttmp();
|
||||
Context.EmitLdtmp();
|
||||
|
||||
Context.Emit(OpCodes.Xor);
|
||||
Context.Emit(OpCodes.And);
|
||||
|
||||
Context.EmitLdtmp();
|
||||
|
||||
Context.Emit(OpCodes.Xor);
|
||||
});
|
||||
}
|
||||
|
||||
private static void EmitVectorMla(AILEmitterCtx Context)
|
||||
{
|
||||
EmitVectorTernaryZx(Context, () =>
|
||||
{
|
||||
Context.Emit(OpCodes.Mul);
|
||||
Context.Emit(OpCodes.Add);
|
||||
});
|
||||
}
|
||||
|
||||
private static void EmitVectorSmax(AILEmitterCtx Context)
|
||||
{
|
||||
Type[] Types = new Type[] { typeof(long), typeof(long) };
|
||||
|
||||
MethodInfo MthdInfo = typeof(Math).GetMethod(nameof(Math.Max), Types);
|
||||
|
||||
EmitVectorBinarySx(Context, () => Context.EmitCall(MthdInfo));
|
||||
}
|
||||
|
||||
private static void EmitVectorSmin(AILEmitterCtx Context)
|
||||
{
|
||||
Type[] Types = new Type[] { typeof(long), typeof(long) };
|
||||
|
||||
MethodInfo MthdInfo = typeof(Math).GetMethod(nameof(Math.Min), Types);
|
||||
|
||||
EmitVectorBinarySx(Context, () => Context.EmitCall(MthdInfo));
|
||||
}
|
||||
|
||||
private static void EmitVectorUshl(AILEmitterCtx Context)
|
||||
{
|
||||
//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;
|
||||
|
||||
EmitVectorBinaryZx(Context, () =>
|
||||
{
|
||||
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(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);
|
||||
});
|
||||
}
|
||||
|
||||
private static void EmitVectorUnarySx(AILEmitterCtx Context, OpCode ILOp)
|
||||
{
|
||||
EmitVectorUnarySx(Context, () => Context.Emit(ILOp));
|
||||
}
|
||||
|
||||
private static void EmitVectorUnaryZx(AILEmitterCtx Context, OpCode ILOp)
|
||||
{
|
||||
EmitVectorUnaryZx(Context, () => Context.Emit(ILOp));
|
||||
}
|
||||
|
||||
private static void EmitVectorBinaryZx(AILEmitterCtx Context, OpCode ILOp)
|
||||
{
|
||||
EmitVectorBinaryZx(Context, () => Context.Emit(ILOp));
|
||||
}
|
||||
|
||||
private static void EmitVectorUnarySx(AILEmitterCtx Context, Action Emit)
|
||||
{
|
||||
EmitVectorOp(Context, Emit, 1, true);
|
||||
}
|
||||
|
||||
private static void EmitVectorBinarySx(AILEmitterCtx Context, Action Emit)
|
||||
{
|
||||
EmitVectorOp(Context, Emit, 2, true);
|
||||
}
|
||||
|
||||
private static void EmitVectorUnaryZx(AILEmitterCtx Context, Action Emit)
|
||||
{
|
||||
EmitVectorOp(Context, Emit, 1, false);
|
||||
}
|
||||
|
||||
private static void EmitVectorBinaryZx(AILEmitterCtx Context, Action Emit)
|
||||
{
|
||||
EmitVectorOp(Context, Emit, 2, false);
|
||||
}
|
||||
|
||||
private static void EmitVectorTernaryZx(AILEmitterCtx Context, Action Emit)
|
||||
{
|
||||
EmitVectorOp(Context, Emit, 3, false);
|
||||
}
|
||||
|
||||
private static void EmitVectorOp(AILEmitterCtx Context, Action Emit, int Opers, bool Signed)
|
||||
{
|
||||
if (Opers < 1 || Opers > 3)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(Opers));
|
||||
}
|
||||
|
||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||
|
||||
int Bytes = Context.CurrOp.GetBitsCount() >> 3;
|
||||
|
||||
for (int Index = 0; Index < (Bytes >> Op.Size); Index++)
|
||||
{
|
||||
Context.EmitLdvec(Op.Rd);
|
||||
Context.EmitLdc_I4(Index);
|
||||
Context.EmitLdc_I4(Op.Size);
|
||||
|
||||
if (Opers == 3)
|
||||
{
|
||||
EmitVectorExtract(Context, Op.Rd, Index, Signed);
|
||||
}
|
||||
|
||||
if (Opers >= 1)
|
||||
{
|
||||
EmitVectorExtract(Context, Op.Rn, Index, Signed);
|
||||
}
|
||||
|
||||
if (Opers >= 2)
|
||||
{
|
||||
EmitVectorExtract(Context, ((AOpCodeSimdReg)Op).Rm, Index, Signed);
|
||||
}
|
||||
|
||||
Emit();
|
||||
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.InsertVec));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
if (Op.RegisterSize == ARegisterSize.SIMD64)
|
||||
{
|
||||
EmitVectorZeroUpper(Context, Op.Rd);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (Op is AOpCodeSimdReg BinOp)
|
||||
{
|
||||
EmitVectorExtractSx(Context, BinOp.Rm, Index);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitVectorExtractSx(AILEmitterCtx Context, int Reg, int Index)
|
||||
{
|
||||
EmitVectorExtract(Context, Reg, Index, true);
|
||||
}
|
||||
|
||||
private static void EmitVectorExtractZx(AILEmitterCtx Context, int Reg, int Index)
|
||||
{
|
||||
EmitVectorExtract(Context, Reg, Index, false);
|
||||
}
|
||||
|
||||
private static void EmitVectorExtract(AILEmitterCtx Context, int Reg, int Index, bool Signed)
|
||||
{
|
||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Reg);
|
||||
Context.EmitLdc_I4(Index);
|
||||
Context.EmitLdc_I4(Op.Size);
|
||||
|
||||
ASoftFallback.EmitCall(Context, Signed
|
||||
? nameof(ASoftFallback.ExtractSVec)
|
||||
: nameof(ASoftFallback.ExtractVec));
|
||||
}
|
||||
|
||||
private static void EmitVectorZeroLower(AILEmitterCtx Context, int Rd)
|
||||
{
|
||||
EmitVectorInsert(Context, Rd, 0, 3, 0);
|
||||
}
|
||||
|
||||
private static void EmitVectorZeroUpper(AILEmitterCtx Context, int Rd)
|
||||
{
|
||||
EmitVectorInsert(Context, Rd, 1, 3, 0);
|
||||
}
|
||||
|
||||
private static void EmitVectorInsert(AILEmitterCtx Context, int Reg, int Index, int Size, long Value)
|
||||
{
|
||||
Context.EmitLdvec(Reg);
|
||||
Context.EmitLdc_I4(Index);
|
||||
Context.EmitLdc_I4(Size);
|
||||
Context.EmitLdc_I8(Value);
|
||||
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.InsertVec));
|
||||
|
||||
Context.EmitStvec(Reg);
|
||||
}
|
||||
}
|
||||
}
|
84
Ryujinx/Cpu/Instruction/AInstEmitSystem.cs
Normal file
84
Ryujinx/Cpu/Instruction/AInstEmitSystem.cs
Normal file
|
@ -0,0 +1,84 @@
|
|||
using ChocolArm64.Decoder;
|
||||
using ChocolArm64.State;
|
||||
using ChocolArm64.Translation;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace ChocolArm64.Instruction
|
||||
{
|
||||
static partial class AInstEmit
|
||||
{
|
||||
public static void Mrs(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSystem Op = (AOpCodeSystem)Context.CurrOp;
|
||||
|
||||
Context.EmitLdarg(ATranslatedSub.RegistersArgIdx);
|
||||
|
||||
Context.EmitLdc_I4(Op.Op0);
|
||||
Context.EmitLdc_I4(Op.Op1);
|
||||
Context.EmitLdc_I4(Op.CRn);
|
||||
Context.EmitLdc_I4(Op.CRm);
|
||||
Context.EmitLdc_I4(Op.Op2);
|
||||
|
||||
Context.EmitCall(typeof(ARegisters), nameof(ARegisters.GetSystemReg));
|
||||
|
||||
Context.EmitStintzr(Op.Rt);
|
||||
}
|
||||
|
||||
public static void Msr(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSystem Op = (AOpCodeSystem)Context.CurrOp;
|
||||
|
||||
Context.EmitLdarg(ATranslatedSub.RegistersArgIdx);
|
||||
|
||||
Context.EmitLdc_I4(Op.Op0);
|
||||
Context.EmitLdc_I4(Op.Op1);
|
||||
Context.EmitLdc_I4(Op.CRn);
|
||||
Context.EmitLdc_I4(Op.CRm);
|
||||
Context.EmitLdc_I4(Op.Op2);
|
||||
Context.EmitLdintzr(Op.Rt);
|
||||
|
||||
Context.EmitCall(typeof(ARegisters), nameof(ARegisters.SetSystemReg));
|
||||
}
|
||||
|
||||
public static void Nop(AILEmitterCtx Context)
|
||||
{
|
||||
//Do nothing.
|
||||
}
|
||||
|
||||
public static void Sys(AILEmitterCtx Context)
|
||||
{
|
||||
//This instruction is used to do some operations on the CPU like cache invalidation,
|
||||
//address translation and the like.
|
||||
//We treat it as no-op here since we don't have any cache being emulated anyway.
|
||||
AOpCodeSystem Op = (AOpCodeSystem)Context.CurrOp;
|
||||
|
||||
int Id;
|
||||
|
||||
Id = Op.Op2 << 0;
|
||||
Id |= Op.CRm << 3;
|
||||
Id |= Op.CRn << 7;
|
||||
Id |= Op.Op1 << 11;
|
||||
|
||||
switch (Id)
|
||||
{
|
||||
case 0b011_0111_0100_001:
|
||||
{
|
||||
//DC ZVA
|
||||
for (int Offs = 0; Offs < 64; Offs += 8)
|
||||
{
|
||||
Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
|
||||
Context.EmitLdint(Op.Rt);
|
||||
Context.EmitLdc_I(Offs);
|
||||
|
||||
Context.Emit(OpCodes.Add);
|
||||
|
||||
Context.EmitLdc_I8(0);
|
||||
|
||||
AInstEmitMemoryHelper.EmitWriteCall(Context, 3);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
6
Ryujinx/Cpu/Instruction/AInstEmitter.cs
Normal file
6
Ryujinx/Cpu/Instruction/AInstEmitter.cs
Normal file
|
@ -0,0 +1,6 @@
|
|||
using ChocolArm64.Translation;
|
||||
|
||||
namespace ChocolArm64.Instruction
|
||||
{
|
||||
delegate void AInstEmitter(AILEmitterCtx Context);
|
||||
}
|
1207
Ryujinx/Cpu/Instruction/ASoftFallback.cs
Normal file
1207
Ryujinx/Cpu/Instruction/ASoftFallback.cs
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue