aloha
This commit is contained in:
commit
b7e1d9930d
230 changed files with 17548 additions and 0 deletions
65
Ryujinx/Cpu/Translation/AILBlock.cs
Normal file
65
Ryujinx/Cpu/Translation/AILBlock.cs
Normal file
|
@ -0,0 +1,65 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace ChocolArm64.Translation
|
||||
{
|
||||
class AILBlock : IAILEmit
|
||||
{
|
||||
public long IntInputs { get; private set; }
|
||||
public long IntOutputs { get; private set; }
|
||||
|
||||
public long VecInputs { get; private set; }
|
||||
public long VecOutputs { get; private set; }
|
||||
|
||||
public bool HasStateStore { get; private set; }
|
||||
|
||||
public List<IAILEmit> ILEmitters { get; private set; }
|
||||
|
||||
public AILBlock Next { get; set; }
|
||||
public AILBlock Branch { get; set; }
|
||||
|
||||
public AILBlock()
|
||||
{
|
||||
ILEmitters = new List<IAILEmit>();
|
||||
}
|
||||
|
||||
public void Add(IAILEmit ILEmitter)
|
||||
{
|
||||
if (ILEmitter is AILOpCodeLoad Ld && AILEmitter.IsRegIndex(Ld.Index))
|
||||
{
|
||||
switch (Ld.IoType & AIoType.Mask)
|
||||
{
|
||||
case AIoType.Flag: IntInputs |= ((1L << Ld.Index) << 32) & ~IntOutputs; break;
|
||||
case AIoType.Int: IntInputs |= (1L << Ld.Index) & ~IntOutputs; break;
|
||||
case AIoType.Vector: VecInputs |= (1L << Ld.Index) & ~VecOutputs; break;
|
||||
}
|
||||
}
|
||||
else if (ILEmitter is AILOpCodeStore St)
|
||||
{
|
||||
if (AILEmitter.IsRegIndex(St.Index))
|
||||
{
|
||||
switch (St.IoType & AIoType.Mask)
|
||||
{
|
||||
case AIoType.Flag: IntOutputs |= (1L << St.Index) << 32; break;
|
||||
case AIoType.Int: IntOutputs |= 1L << St.Index; break;
|
||||
case AIoType.Vector: VecOutputs |= 1L << St.Index; break;
|
||||
}
|
||||
}
|
||||
|
||||
if (St.IoType == AIoType.Fields)
|
||||
{
|
||||
HasStateStore = true;
|
||||
}
|
||||
}
|
||||
|
||||
ILEmitters.Add(ILEmitter);
|
||||
}
|
||||
|
||||
public void Emit(AILEmitter Context)
|
||||
{
|
||||
foreach (IAILEmit ILEmitter in ILEmitters)
|
||||
{
|
||||
ILEmitter.Emit(Context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
113
Ryujinx/Cpu/Translation/AILConv.cs
Normal file
113
Ryujinx/Cpu/Translation/AILConv.cs
Normal file
|
@ -0,0 +1,113 @@
|
|||
using ChocolArm64.State;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace ChocolArm64.Translation
|
||||
{
|
||||
static class AILConv
|
||||
{
|
||||
public static void EmitConv(AILEmitter Context, Type SrcType, Type TgtType)
|
||||
{
|
||||
if (SrcType == TgtType)
|
||||
{
|
||||
//If both types are equal we don't need to cast anything.
|
||||
return;
|
||||
}
|
||||
|
||||
if (SrcType.IsPrimitive)
|
||||
{
|
||||
if (TgtType == typeof(byte))
|
||||
{
|
||||
Context.Generator.Emit(OpCodes.Conv_U1);
|
||||
}
|
||||
else if (TgtType == typeof(ushort))
|
||||
{
|
||||
Context.Generator.Emit(OpCodes.Conv_U2);
|
||||
}
|
||||
else if (TgtType == typeof(uint))
|
||||
{
|
||||
Context.Generator.Emit(OpCodes.Conv_U4);
|
||||
}
|
||||
else if (TgtType == typeof(ulong))
|
||||
{
|
||||
Context.Generator.Emit(OpCodes.Conv_U8);
|
||||
}
|
||||
else if (TgtType == typeof(float))
|
||||
{
|
||||
Context.Generator.Emit(OpCodes.Conv_R4);
|
||||
}
|
||||
else if (TgtType == typeof(double))
|
||||
{
|
||||
Context.Generator.Emit(OpCodes.Conv_R8);
|
||||
}
|
||||
else if (TgtType == typeof(AVec))
|
||||
{
|
||||
EmitMakeVec(Context, SrcType);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException(nameof(TgtType));
|
||||
}
|
||||
}
|
||||
else if (SrcType == typeof(AVec))
|
||||
{
|
||||
if (TgtType == typeof(float))
|
||||
{
|
||||
EmitScalarLdfld(Context, nameof(AVec.S0));
|
||||
}
|
||||
else if (TgtType == typeof(double))
|
||||
{
|
||||
EmitScalarLdfld(Context, nameof(AVec.D0));
|
||||
}
|
||||
else if (TgtType == typeof(byte))
|
||||
{
|
||||
EmitScalarLdfld(Context, nameof(AVec.B0));
|
||||
}
|
||||
else if (TgtType == typeof(ushort))
|
||||
{
|
||||
EmitScalarLdfld(Context, nameof(AVec.H0));
|
||||
}
|
||||
else if (TgtType == typeof(uint))
|
||||
{
|
||||
EmitScalarLdfld(Context, nameof(AVec.W0));
|
||||
}
|
||||
else if (TgtType == typeof(ulong))
|
||||
{
|
||||
EmitScalarLdfld(Context, nameof(AVec.X0));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException(nameof(TgtType));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException(nameof(SrcType));
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitScalarLdfld(AILEmitter Context,string FldName)
|
||||
{
|
||||
Context.Generator.Emit(OpCodes.Ldfld, typeof(AVec).GetField(FldName));
|
||||
}
|
||||
|
||||
private static void EmitMakeVec(AILEmitter Context, Type SrcType)
|
||||
{
|
||||
string MthdName = nameof(MakeScalar);
|
||||
|
||||
Type[] MthdTypes = new Type[] { SrcType };
|
||||
|
||||
MethodInfo MthdInfo = typeof(AILConv).GetMethod(MthdName, MthdTypes);
|
||||
|
||||
Context.Generator.Emit(OpCodes.Call, MthdInfo);
|
||||
}
|
||||
|
||||
public static AVec MakeScalar(byte Value) => new AVec { B0 = Value };
|
||||
public static AVec MakeScalar(ushort Value) => new AVec { H0 = Value };
|
||||
public static AVec MakeScalar(uint Value) => new AVec { W0 = Value };
|
||||
public static AVec MakeScalar(float Value) => new AVec { S0 = Value };
|
||||
public static AVec MakeScalar(ulong Value) => new AVec { X0 = Value };
|
||||
public static AVec MakeScalar(double Value) => new AVec { D0 = Value };
|
||||
}
|
||||
}
|
190
Ryujinx/Cpu/Translation/AILEmitter.cs
Normal file
190
Ryujinx/Cpu/Translation/AILEmitter.cs
Normal file
|
@ -0,0 +1,190 @@
|
|||
using ChocolArm64.Decoder;
|
||||
using ChocolArm64.State;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace ChocolArm64.Translation
|
||||
{
|
||||
class AILEmitter
|
||||
{
|
||||
public ALocalAlloc LocalAlloc { get; private set; }
|
||||
|
||||
public ILGenerator Generator { get; private set; }
|
||||
|
||||
private Dictionary<ARegister, int> Locals;
|
||||
|
||||
private AILBlock[] ILBlocks;
|
||||
|
||||
private AILBlock Root;
|
||||
|
||||
private ATranslatedSub Subroutine;
|
||||
|
||||
private string SubName;
|
||||
|
||||
private int LocalsCount;
|
||||
|
||||
public AILEmitter(ABlock[] Graph, ABlock Root, string SubName)
|
||||
{
|
||||
this.SubName = SubName;
|
||||
|
||||
Locals = new Dictionary<ARegister, int>();
|
||||
|
||||
ILBlocks = new AILBlock[Graph.Length];
|
||||
|
||||
AILBlock GetBlock(int Index)
|
||||
{
|
||||
if (Index < 0 || Index >= ILBlocks.Length)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (ILBlocks[Index] == null)
|
||||
{
|
||||
ILBlocks[Index] = new AILBlock();
|
||||
}
|
||||
|
||||
return ILBlocks[Index];
|
||||
}
|
||||
|
||||
for (int Index = 0; Index < ILBlocks.Length; Index++)
|
||||
{
|
||||
AILBlock Block = GetBlock(Index);
|
||||
|
||||
Block.Next = GetBlock(Array.IndexOf(Graph, Graph[Index].Next));
|
||||
Block.Branch = GetBlock(Array.IndexOf(Graph, Graph[Index].Branch));
|
||||
}
|
||||
|
||||
this.Root = ILBlocks[Array.IndexOf(Graph, Root)];
|
||||
}
|
||||
|
||||
public ATranslatedSub GetSubroutine()
|
||||
{
|
||||
LocalAlloc = new ALocalAlloc(ILBlocks, Root);
|
||||
|
||||
InitSubroutine();
|
||||
InitLocals();
|
||||
|
||||
foreach (AILBlock ILBlock in ILBlocks)
|
||||
{
|
||||
ILBlock.Emit(this);
|
||||
}
|
||||
|
||||
return Subroutine;
|
||||
}
|
||||
|
||||
public AILBlock GetILBlock(int Index) => ILBlocks[Index];
|
||||
|
||||
private void InitLocals()
|
||||
{
|
||||
int ParamsStart = ATranslatedSub.FixedArgTypes.Length;
|
||||
|
||||
Locals = new Dictionary<ARegister, int>();
|
||||
|
||||
for (int Index = 0; Index < Subroutine.Params.Count; Index++)
|
||||
{
|
||||
ARegister Reg = Subroutine.Params[Index];
|
||||
|
||||
Generator.EmitLdarg(Index + ParamsStart);
|
||||
|
||||
AILConv.EmitConv(this, GetFieldType(Reg.Type), GetLocalType(Reg));
|
||||
|
||||
Generator.EmitStloc(GetLocalIndex(Reg));
|
||||
}
|
||||
}
|
||||
|
||||
private void InitSubroutine()
|
||||
{
|
||||
List<ARegister> Params = new List<ARegister>();
|
||||
|
||||
void SetParams(long Inputs, ARegisterType BaseType)
|
||||
{
|
||||
for (int Bit = 0; Bit < 64; Bit++)
|
||||
{
|
||||
long Mask = 1L << Bit;
|
||||
|
||||
if ((Inputs & Mask) != 0)
|
||||
{
|
||||
Params.Add(GetRegFromBit(Bit, BaseType));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SetParams(LocalAlloc.GetIntInputs(Root), ARegisterType.Int);
|
||||
SetParams(LocalAlloc.GetVecInputs(Root), ARegisterType.Vector);
|
||||
|
||||
DynamicMethod Mthd = new DynamicMethod(SubName, typeof(long), GetParamTypes(Params));
|
||||
|
||||
Generator = Mthd.GetILGenerator();
|
||||
|
||||
Subroutine = new ATranslatedSub(Mthd, Params);
|
||||
}
|
||||
|
||||
private Type[] GetParamTypes(IList<ARegister> Params)
|
||||
{
|
||||
Type[] FixedArgs = ATranslatedSub.FixedArgTypes;
|
||||
|
||||
Type[] Output = new Type[Params.Count + FixedArgs.Length];
|
||||
|
||||
FixedArgs.CopyTo(Output, 0);
|
||||
|
||||
int TypeIdx = FixedArgs.Length;
|
||||
|
||||
for (int Index = 0; Index < Params.Count; Index++)
|
||||
{
|
||||
Output[TypeIdx++] = GetFieldType(Params[Index].Type);
|
||||
}
|
||||
|
||||
return Output;
|
||||
}
|
||||
|
||||
public int GetLocalIndex(ARegister Reg)
|
||||
{
|
||||
if (!Locals.TryGetValue(Reg, out int Index))
|
||||
{
|
||||
Generator.DeclareLocal(GetLocalType(Reg));
|
||||
|
||||
Index = LocalsCount++;
|
||||
|
||||
Locals.Add(Reg, Index);
|
||||
}
|
||||
|
||||
return Index;
|
||||
}
|
||||
|
||||
public Type GetLocalType(ARegister Reg) => GetFieldType(Reg.Type);
|
||||
|
||||
public Type GetFieldType(ARegisterType RegType)
|
||||
{
|
||||
switch (RegType)
|
||||
{
|
||||
case ARegisterType.Flag: return typeof(bool);
|
||||
case ARegisterType.Int: return typeof(ulong);
|
||||
case ARegisterType.Vector: return typeof(AVec);
|
||||
}
|
||||
|
||||
throw new ArgumentException(nameof(RegType));
|
||||
}
|
||||
|
||||
public static ARegister GetRegFromBit(int Bit, ARegisterType BaseType)
|
||||
{
|
||||
if (Bit < 32)
|
||||
{
|
||||
return new ARegister(Bit, BaseType);
|
||||
}
|
||||
else if (BaseType == ARegisterType.Int)
|
||||
{
|
||||
return new ARegister(Bit & 0x1f, ARegisterType.Flag);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(Bit));
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsRegIndex(int Index)
|
||||
{
|
||||
return Index >= 0 && Index < 32;
|
||||
}
|
||||
}
|
||||
}
|
548
Ryujinx/Cpu/Translation/AILEmitterCtx.cs
Normal file
548
Ryujinx/Cpu/Translation/AILEmitterCtx.cs
Normal file
|
@ -0,0 +1,548 @@
|
|||
using ChocolArm64.Decoder;
|
||||
using ChocolArm64.Instruction;
|
||||
using ChocolArm64.State;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace ChocolArm64.Translation
|
||||
{
|
||||
class AILEmitterCtx
|
||||
{
|
||||
private ATranslator Translator;
|
||||
|
||||
private Dictionary<long, AILLabel> Labels;
|
||||
|
||||
private AILEmitter Emitter;
|
||||
|
||||
private AILBlock ILBlock;
|
||||
|
||||
private AOpCode LastCmpOp;
|
||||
private AOpCode LastFlagOp;
|
||||
|
||||
private int BlkIndex;
|
||||
private int OpcIndex;
|
||||
|
||||
private ABlock[] Graph;
|
||||
private ABlock Root;
|
||||
public ABlock CurrBlock => Graph[BlkIndex];
|
||||
public AOpCode CurrOp => Graph[BlkIndex].OpCodes[OpcIndex];
|
||||
|
||||
//This is the index of the temporary register, used to store temporary
|
||||
//values needed by some functions, since IL doesn't have a swap instruction.
|
||||
//You can use any value here as long it doesn't conflict with the indices
|
||||
//for the other registers. Any value >= 64 or < 0 will do.
|
||||
private const int Tmp1Index = -1;
|
||||
private const int Tmp2Index = -2;
|
||||
private const int Tmp3Index = -3;
|
||||
private const int Tmp4Index = -4;
|
||||
|
||||
public AILEmitterCtx(ATranslator Translator, ABlock[] Graph, ABlock Root)
|
||||
{
|
||||
this.Translator = Translator;
|
||||
this.Graph = Graph;
|
||||
this.Root = Root;
|
||||
|
||||
string SubName = $"Sub{Root.Position:X16}";
|
||||
|
||||
Labels = new Dictionary<long, AILLabel>();
|
||||
|
||||
Emitter = new AILEmitter(Graph, Root, SubName);
|
||||
|
||||
ILBlock = Emitter.GetILBlock(0);
|
||||
|
||||
OpcIndex = -1;
|
||||
|
||||
if (!AdvanceOpCode())
|
||||
{
|
||||
throw new ArgumentException(nameof(Graph));
|
||||
}
|
||||
}
|
||||
|
||||
public ATranslatedSub GetSubroutine() => Emitter.GetSubroutine();
|
||||
|
||||
public bool AdvanceOpCode()
|
||||
{
|
||||
while (++OpcIndex >= (CurrBlock?.OpCodes.Count ?? 0))
|
||||
{
|
||||
if (BlkIndex + 1 >= Graph.Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
BlkIndex++;
|
||||
OpcIndex = -1;
|
||||
|
||||
ILBlock = Emitter.GetILBlock(BlkIndex);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void EmitOpCode()
|
||||
{
|
||||
if (OpcIndex == 0)
|
||||
{
|
||||
MarkLabel(GetLabel(CurrBlock.Position));
|
||||
}
|
||||
|
||||
CurrOp.Emitter(this);
|
||||
}
|
||||
|
||||
public bool TryOptEmitSubroutineCall()
|
||||
{
|
||||
if (!Translator.TryGetCachedSub(CurrOp, out ATranslatedSub Sub))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int Index = 0; Index < ATranslatedSub.FixedArgTypes.Length; Index++)
|
||||
{
|
||||
EmitLdarg(Index);
|
||||
}
|
||||
|
||||
foreach (ARegister Reg in Sub.Params)
|
||||
{
|
||||
switch (Reg.Type)
|
||||
{
|
||||
case ARegisterType.Flag: Ldloc(Reg.Index, AIoType.Flag); break;
|
||||
case ARegisterType.Int: Ldloc(Reg.Index, AIoType.Int); break;
|
||||
case ARegisterType.Vector: Ldloc(Reg.Index, AIoType.Vector); break;
|
||||
}
|
||||
}
|
||||
|
||||
EmitCall(Sub.Method);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void TryOptMarkCondWithoutCmp()
|
||||
{
|
||||
LastCmpOp = CurrOp;
|
||||
|
||||
AInstEmitAluHelper.EmitDataLoadOpers(this);
|
||||
|
||||
Stloc(Tmp4Index, AIoType.Int);
|
||||
Stloc(Tmp3Index, AIoType.Int);
|
||||
}
|
||||
|
||||
private Dictionary<ACond, OpCode> BranchOps = new Dictionary<ACond, OpCode>()
|
||||
{
|
||||
{ ACond.Eq, OpCodes.Beq },
|
||||
{ ACond.Ne, OpCodes.Bne_Un },
|
||||
{ ACond.Ge_Un, OpCodes.Bge_Un },
|
||||
{ ACond.Lt_Un, OpCodes.Blt_Un },
|
||||
{ ACond.Gt_Un, OpCodes.Bgt_Un },
|
||||
{ ACond.Le_Un, OpCodes.Ble_Un },
|
||||
{ ACond.Ge, OpCodes.Bge },
|
||||
{ ACond.Lt, OpCodes.Blt },
|
||||
{ ACond.Gt, OpCodes.Bgt },
|
||||
{ ACond.Le, OpCodes.Ble }
|
||||
};
|
||||
|
||||
public void EmitCondBranch(AILLabel Target, ACond Cond)
|
||||
{
|
||||
OpCode ILOp;
|
||||
|
||||
int IntCond = (int)Cond;
|
||||
|
||||
if (LastFlagOp == LastCmpOp && BranchOps.ContainsKey(Cond))
|
||||
{
|
||||
Ldloc(Tmp3Index, AIoType.Int, GetIntType(LastCmpOp));
|
||||
Ldloc(Tmp4Index, AIoType.Int, GetIntType(LastCmpOp));
|
||||
|
||||
if (LastCmpOp.Emitter == AInstEmit.Adds)
|
||||
{
|
||||
Emit(OpCodes.Neg);
|
||||
}
|
||||
|
||||
ILOp = BranchOps[Cond];
|
||||
}
|
||||
else if (IntCond < 14)
|
||||
{
|
||||
int CondTrue = IntCond >> 1;
|
||||
|
||||
switch (CondTrue)
|
||||
{
|
||||
case 0: EmitLdflg((int)APState.ZBit); break;
|
||||
case 1: EmitLdflg((int)APState.CBit); break;
|
||||
case 2: EmitLdflg((int)APState.NBit); break;
|
||||
case 3: EmitLdflg((int)APState.VBit); break;
|
||||
|
||||
case 4:
|
||||
EmitLdflg((int)APState.CBit);
|
||||
EmitLdflg((int)APState.ZBit);
|
||||
|
||||
Emit(OpCodes.Not);
|
||||
Emit(OpCodes.And);
|
||||
break;
|
||||
|
||||
case 5:
|
||||
case 6:
|
||||
EmitLdflg((int)APState.NBit);
|
||||
EmitLdflg((int)APState.VBit);
|
||||
|
||||
Emit(OpCodes.Ceq);
|
||||
|
||||
if (CondTrue == 6)
|
||||
{
|
||||
EmitLdflg((int)APState.ZBit);
|
||||
|
||||
Emit(OpCodes.Not);
|
||||
Emit(OpCodes.And);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
ILOp = (IntCond & 1) != 0
|
||||
? OpCodes.Brfalse
|
||||
: OpCodes.Brtrue;
|
||||
}
|
||||
else
|
||||
{
|
||||
ILOp = OpCodes.Br;
|
||||
}
|
||||
|
||||
Emit(ILOp, Target);
|
||||
}
|
||||
|
||||
public void EmitCast(AIntType IntType)
|
||||
{
|
||||
switch (IntType)
|
||||
{
|
||||
case AIntType.UInt8: Emit(OpCodes.Conv_U1); break;
|
||||
case AIntType.UInt16: Emit(OpCodes.Conv_U2); break;
|
||||
case AIntType.UInt32: Emit(OpCodes.Conv_U4); break;
|
||||
case AIntType.UInt64: Emit(OpCodes.Conv_U8); break;
|
||||
case AIntType.Int8: Emit(OpCodes.Conv_I1); break;
|
||||
case AIntType.Int16: Emit(OpCodes.Conv_I2); break;
|
||||
case AIntType.Int32: Emit(OpCodes.Conv_I4); break;
|
||||
case AIntType.Int64: Emit(OpCodes.Conv_I8); break;
|
||||
}
|
||||
|
||||
if (IntType == AIntType.UInt64 ||
|
||||
IntType == AIntType.Int64)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (CurrOp.RegisterSize != ARegisterSize.Int32)
|
||||
{
|
||||
Emit(IntType >= AIntType.Int8
|
||||
? OpCodes.Conv_I8
|
||||
: OpCodes.Conv_U8);
|
||||
}
|
||||
}
|
||||
|
||||
public void EmitLsl(int Amount) => EmitILShift(Amount, OpCodes.Shl);
|
||||
public void EmitLsr(int Amount) => EmitILShift(Amount, OpCodes.Shr_Un);
|
||||
public void EmitAsr(int Amount) => EmitILShift(Amount, OpCodes.Shr);
|
||||
|
||||
private void EmitILShift(int Amount, OpCode ILOp)
|
||||
{
|
||||
if (Amount > 0)
|
||||
{
|
||||
EmitLdc_I4(Amount);
|
||||
|
||||
Emit(ILOp);
|
||||
}
|
||||
}
|
||||
|
||||
public void EmitRor(int Amount)
|
||||
{
|
||||
if (Amount > 0)
|
||||
{
|
||||
Stloc(Tmp2Index, AIoType.Int);
|
||||
Ldloc(Tmp2Index, AIoType.Int);
|
||||
|
||||
EmitLdc_I4(Amount);
|
||||
|
||||
Emit(OpCodes.Shr_Un);
|
||||
|
||||
Ldloc(Tmp2Index, AIoType.Int);
|
||||
|
||||
EmitLdc_I4(CurrOp.GetBitsCount() - Amount);
|
||||
|
||||
Emit(OpCodes.Shl);
|
||||
Emit(OpCodes.Or);
|
||||
}
|
||||
}
|
||||
|
||||
public AILLabel GetLabel(long Position)
|
||||
{
|
||||
if (!Labels.TryGetValue(Position, out AILLabel Output))
|
||||
{
|
||||
Output = new AILLabel();
|
||||
|
||||
Labels.Add(Position, Output);
|
||||
}
|
||||
|
||||
return Output;
|
||||
}
|
||||
|
||||
public void MarkLabel(AILLabel Label)
|
||||
{
|
||||
ILBlock.Add(Label);
|
||||
}
|
||||
|
||||
public void Emit(OpCode ILOp)
|
||||
{
|
||||
ILBlock.Add(new AILOpCode(ILOp));
|
||||
}
|
||||
|
||||
public void Emit(OpCode ILOp, AILLabel Label)
|
||||
{
|
||||
ILBlock.Add(new AILOpCodeBranch(ILOp, Label));
|
||||
}
|
||||
|
||||
public void Emit(string Text)
|
||||
{
|
||||
ILBlock.Add(new AILOpCodeLog(Text));
|
||||
}
|
||||
|
||||
public void EmitLdarg(int Index)
|
||||
{
|
||||
ILBlock.Add(new AILOpCodeLoad(Index, AIoType.Arg));
|
||||
}
|
||||
|
||||
public void EmitLdintzr(int Index)
|
||||
{
|
||||
if (Index != ARegisters.ZRIndex)
|
||||
{
|
||||
EmitLdint(Index);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitLdc_I(0);
|
||||
}
|
||||
}
|
||||
|
||||
public void EmitStintzr(int Index)
|
||||
{
|
||||
if (Index != ARegisters.ZRIndex)
|
||||
{
|
||||
EmitStint(Index);
|
||||
}
|
||||
else
|
||||
{
|
||||
Emit(OpCodes.Pop);
|
||||
}
|
||||
}
|
||||
|
||||
public void EmitLoadState(ABlock RetBlk)
|
||||
{
|
||||
ILBlock.Add(new AILOpCodeLoad(Array.IndexOf(Graph, RetBlk), AIoType.Fields));
|
||||
}
|
||||
|
||||
public void EmitStoreState()
|
||||
{
|
||||
ILBlock.Add(new AILOpCodeStore(Array.IndexOf(Graph, CurrBlock), AIoType.Fields));
|
||||
}
|
||||
|
||||
public void EmitLdtmp() => EmitLdint(Tmp1Index);
|
||||
public void EmitSttmp() => EmitStint(Tmp1Index);
|
||||
|
||||
public void EmitLdint(int Index) => Ldloc(Index, AIoType.Int);
|
||||
public void EmitStint(int Index) => Stloc(Index, AIoType.Int);
|
||||
|
||||
public void EmitLdvec(int Index) => Ldloc(Index, AIoType.Vector);
|
||||
public void EmitStvec(int Index) => Stloc(Index, AIoType.Vector);
|
||||
|
||||
public void EmitLdvecsi(int Index) => Ldloc(Index, AIoType.VectorI);
|
||||
public void EmitStvecsi(int Index) => Stloc(Index, AIoType.VectorI);
|
||||
|
||||
public void EmitLdvecsf(int Index) => Ldloc(Index, AIoType.VectorF);
|
||||
public void EmitStvecsf(int Index) => Stloc(Index, AIoType.VectorF);
|
||||
|
||||
public void EmitLdflg(int Index) => Ldloc(Index, AIoType.Flag);
|
||||
public void EmitStflg(int Index)
|
||||
{
|
||||
LastFlagOp = CurrOp;
|
||||
|
||||
Stloc(Index, AIoType.Flag);
|
||||
}
|
||||
|
||||
private void Ldloc(int Index, AIoType IoType)
|
||||
{
|
||||
ILBlock.Add(new AILOpCodeLoad(Index, IoType, GetOperType(IoType)));
|
||||
}
|
||||
|
||||
private void Ldloc(int Index, AIoType IoType, Type Type)
|
||||
{
|
||||
ILBlock.Add(new AILOpCodeLoad(Index, IoType, Type));
|
||||
}
|
||||
|
||||
private void Stloc(int Index, AIoType IoType)
|
||||
{
|
||||
ILBlock.Add(new AILOpCodeStore(Index, IoType, GetOutOperType(IoType)));
|
||||
}
|
||||
|
||||
private Type GetOutOperType(AIoType IoType)
|
||||
{
|
||||
//This instruction is used to convert between floating point
|
||||
//types, so the input and output types are different.
|
||||
if (CurrOp.Emitter == AInstEmit.Fcvt_S)
|
||||
{
|
||||
return GetFloatType(((AOpCodeSimd)CurrOp).Opc);
|
||||
}
|
||||
else
|
||||
{
|
||||
return GetOperType(IoType);
|
||||
}
|
||||
}
|
||||
|
||||
private Type GetOperType(AIoType IoType)
|
||||
{
|
||||
switch (IoType & AIoType.Mask)
|
||||
{
|
||||
case AIoType.Flag: return typeof(bool);
|
||||
case AIoType.Int: return GetIntType(CurrOp);
|
||||
case AIoType.Vector: return GetVecType(CurrOp, IoType);
|
||||
}
|
||||
|
||||
throw new ArgumentException(nameof(IoType));
|
||||
}
|
||||
|
||||
private Type GetIntType(AOpCode OpCode)
|
||||
{
|
||||
//Always default to 64-bits.
|
||||
return OpCode.RegisterSize == ARegisterSize.Int32
|
||||
? typeof(uint)
|
||||
: typeof(ulong);
|
||||
}
|
||||
|
||||
private Type GetVecType(AOpCode OpCode, AIoType IoType)
|
||||
{
|
||||
if (!(OpCode is IAOpCodeSimd Op))
|
||||
{
|
||||
return typeof(AVec);
|
||||
}
|
||||
|
||||
int Size = Op.Size;
|
||||
|
||||
if (Op.Emitter == AInstEmit.Fmov_Ftoi ||
|
||||
Op.Emitter == AInstEmit.Fmov_Itof)
|
||||
{
|
||||
Size |= 2;
|
||||
}
|
||||
|
||||
if (Op is AOpCodeMem || Op is IAOpCodeLit)
|
||||
{
|
||||
return Size < 4 ? typeof(ulong) : typeof(AVec);
|
||||
}
|
||||
else if (IoType == AIoType.VectorI)
|
||||
{
|
||||
return GetIntType(Size);
|
||||
}
|
||||
else if (IoType == AIoType.VectorF)
|
||||
{
|
||||
return GetFloatType(Size);
|
||||
}
|
||||
|
||||
return typeof(AVec);
|
||||
}
|
||||
|
||||
private static Type GetIntType(int Size)
|
||||
{
|
||||
switch (Size)
|
||||
{
|
||||
case 0: return typeof(byte);
|
||||
case 1: return typeof(ushort);
|
||||
case 2: return typeof(uint);
|
||||
case 3: return typeof(ulong);
|
||||
}
|
||||
|
||||
throw new ArgumentOutOfRangeException(nameof(Size));
|
||||
}
|
||||
|
||||
private static Type GetFloatType(int Size)
|
||||
{
|
||||
switch (Size)
|
||||
{
|
||||
case 0: return typeof(float);
|
||||
case 1: return typeof(double);
|
||||
}
|
||||
|
||||
throw new ArgumentOutOfRangeException(nameof(Size));
|
||||
}
|
||||
|
||||
public void EmitCall(Type MthdType, string MthdName)
|
||||
{
|
||||
if (MthdType == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(MthdType));
|
||||
}
|
||||
|
||||
if (MthdName == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(MthdName));
|
||||
}
|
||||
|
||||
EmitCall(MthdType.GetMethod(MthdName));
|
||||
}
|
||||
|
||||
public void EmitCall(MethodInfo MthdInfo)
|
||||
{
|
||||
if (MthdInfo == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(MthdInfo));
|
||||
}
|
||||
|
||||
ILBlock.Add(new AILOpCodeCall(MthdInfo));
|
||||
}
|
||||
|
||||
public void EmitLdc_I(long Value)
|
||||
{
|
||||
if (CurrOp.RegisterSize == ARegisterSize.Int32)
|
||||
{
|
||||
EmitLdc_I4((int)Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitLdc_I8(Value);
|
||||
}
|
||||
}
|
||||
|
||||
public void EmitLdc_I4(int Value)
|
||||
{
|
||||
ILBlock.Add(new AILOpCodeConst(Value));
|
||||
}
|
||||
|
||||
public void EmitLdc_I8(long Value)
|
||||
{
|
||||
ILBlock.Add(new AILOpCodeConst(Value));
|
||||
}
|
||||
|
||||
public void EmitLdc_R4(float Value)
|
||||
{
|
||||
ILBlock.Add(new AILOpCodeConst(Value));
|
||||
}
|
||||
|
||||
public void EmitLdc_R8(double Value)
|
||||
{
|
||||
ILBlock.Add(new AILOpCodeConst(Value));
|
||||
}
|
||||
|
||||
public void EmitZNFlagCheck()
|
||||
{
|
||||
EmitZNCheck(OpCodes.Ceq, (int)APState.ZBit);
|
||||
EmitZNCheck(OpCodes.Clt, (int)APState.NBit);
|
||||
}
|
||||
|
||||
private void EmitZNCheck(OpCode ILCmpOp, int Flag)
|
||||
{
|
||||
Emit(OpCodes.Dup);
|
||||
Emit(OpCodes.Ldc_I4_0);
|
||||
|
||||
if (CurrOp.RegisterSize != ARegisterSize.Int32)
|
||||
{
|
||||
Emit(OpCodes.Conv_I8);
|
||||
}
|
||||
|
||||
Emit(ILCmpOp);
|
||||
|
||||
EmitStflg(Flag);
|
||||
}
|
||||
}
|
||||
}
|
28
Ryujinx/Cpu/Translation/AILLabel.cs
Normal file
28
Ryujinx/Cpu/Translation/AILLabel.cs
Normal file
|
@ -0,0 +1,28 @@
|
|||
using System.Reflection.Emit;
|
||||
|
||||
namespace ChocolArm64.Translation
|
||||
{
|
||||
class AILLabel : IAILEmit
|
||||
{
|
||||
private bool HasLabel;
|
||||
|
||||
private Label Lbl;
|
||||
|
||||
public void Emit(AILEmitter Context)
|
||||
{
|
||||
Context.Generator.MarkLabel(GetLabel(Context));
|
||||
}
|
||||
|
||||
public Label GetLabel(AILEmitter Context)
|
||||
{
|
||||
if (!HasLabel)
|
||||
{
|
||||
Lbl = Context.Generator.DefineLabel();
|
||||
|
||||
HasLabel = true;
|
||||
}
|
||||
|
||||
return Lbl;
|
||||
}
|
||||
}
|
||||
}
|
19
Ryujinx/Cpu/Translation/AILOpCode.cs
Normal file
19
Ryujinx/Cpu/Translation/AILOpCode.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
using System.Reflection.Emit;
|
||||
|
||||
namespace ChocolArm64.Translation
|
||||
{
|
||||
struct AILOpCode : IAILEmit
|
||||
{
|
||||
private OpCode ILOp;
|
||||
|
||||
public AILOpCode(OpCode ILOp)
|
||||
{
|
||||
this.ILOp = ILOp;
|
||||
}
|
||||
|
||||
public void Emit(AILEmitter Context)
|
||||
{
|
||||
Context.Generator.Emit(ILOp);
|
||||
}
|
||||
}
|
||||
}
|
21
Ryujinx/Cpu/Translation/AILOpCodeBranch.cs
Normal file
21
Ryujinx/Cpu/Translation/AILOpCodeBranch.cs
Normal file
|
@ -0,0 +1,21 @@
|
|||
using System.Reflection.Emit;
|
||||
|
||||
namespace ChocolArm64.Translation
|
||||
{
|
||||
struct AILOpCodeBranch : IAILEmit
|
||||
{
|
||||
private OpCode ILOp;
|
||||
private AILLabel Label;
|
||||
|
||||
public AILOpCodeBranch(OpCode ILOp, AILLabel Label)
|
||||
{
|
||||
this.ILOp = ILOp;
|
||||
this.Label = Label;
|
||||
}
|
||||
|
||||
public void Emit(AILEmitter Context)
|
||||
{
|
||||
Context.Generator.Emit(ILOp, Label.GetLabel(Context));
|
||||
}
|
||||
}
|
||||
}
|
20
Ryujinx/Cpu/Translation/AILOpCodeCall.cs
Normal file
20
Ryujinx/Cpu/Translation/AILOpCodeCall.cs
Normal file
|
@ -0,0 +1,20 @@
|
|||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace ChocolArm64.Translation
|
||||
{
|
||||
struct AILOpCodeCall : IAILEmit
|
||||
{
|
||||
private MethodInfo MthdInfo;
|
||||
|
||||
public AILOpCodeCall(MethodInfo MthdInfo)
|
||||
{
|
||||
this.MthdInfo = MthdInfo;
|
||||
}
|
||||
|
||||
public void Emit(AILEmitter Context)
|
||||
{
|
||||
Context.Generator.Emit(OpCodes.Call, MthdInfo);
|
||||
}
|
||||
}
|
||||
}
|
81
Ryujinx/Cpu/Translation/AILOpCodeConst.cs
Normal file
81
Ryujinx/Cpu/Translation/AILOpCodeConst.cs
Normal file
|
@ -0,0 +1,81 @@
|
|||
using System.Reflection.Emit;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace ChocolArm64.Translation
|
||||
{
|
||||
class AILOpCodeConst : IAILEmit
|
||||
{
|
||||
[StructLayout(LayoutKind.Explicit, Size = 8)]
|
||||
private struct ImmVal
|
||||
{
|
||||
[FieldOffset(0)] public int I4;
|
||||
[FieldOffset(0)] public long I8;
|
||||
[FieldOffset(0)] public float R4;
|
||||
[FieldOffset(0)] public double R8;
|
||||
}
|
||||
|
||||
private ImmVal Value;
|
||||
|
||||
private enum ConstType
|
||||
{
|
||||
Int32,
|
||||
Int64,
|
||||
Single,
|
||||
Double
|
||||
}
|
||||
|
||||
private ConstType Type;
|
||||
|
||||
private AILOpCodeConst(ConstType Type)
|
||||
{
|
||||
this.Type = Type;
|
||||
}
|
||||
|
||||
public AILOpCodeConst(int Value) : this(ConstType.Int32)
|
||||
{
|
||||
this.Value = new ImmVal { I4 = Value };
|
||||
}
|
||||
|
||||
public AILOpCodeConst(long Value) : this(ConstType.Int64)
|
||||
{
|
||||
this.Value = new ImmVal { I8 = Value };
|
||||
}
|
||||
|
||||
public AILOpCodeConst(float Value) : this(ConstType.Single)
|
||||
{
|
||||
this.Value = new ImmVal { R4 = Value };
|
||||
}
|
||||
|
||||
public AILOpCodeConst(double Value) : this(ConstType.Double)
|
||||
{
|
||||
this.Value = new ImmVal { R8 = Value };
|
||||
}
|
||||
|
||||
public void Emit(AILEmitter Context)
|
||||
{
|
||||
switch (Type)
|
||||
{
|
||||
case ConstType.Int32: Context.Generator.EmitLdc_I4(Value.I4); break;
|
||||
|
||||
case ConstType.Int64:
|
||||
{
|
||||
if (Value.I8 >= int.MinValue &&
|
||||
Value.I8 <= int.MaxValue)
|
||||
{
|
||||
Context.Generator.EmitLdc_I4(Value.I4);
|
||||
|
||||
Context.Generator.Emit(OpCodes.Conv_I8);
|
||||
}
|
||||
else
|
||||
{
|
||||
Context.Generator.Emit(OpCodes.Ldc_I8, Value.I8);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ConstType.Single: Context.Generator.Emit(OpCodes.Ldc_R4, Value.R4); break;
|
||||
case ConstType.Double: Context.Generator.Emit(OpCodes.Ldc_R8, Value.R8); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
82
Ryujinx/Cpu/Translation/AILOpCodeLoad.cs
Normal file
82
Ryujinx/Cpu/Translation/AILOpCodeLoad.cs
Normal file
|
@ -0,0 +1,82 @@
|
|||
using ChocolArm64.State;
|
||||
using System;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace ChocolArm64.Translation
|
||||
{
|
||||
struct AILOpCodeLoad : IAILEmit
|
||||
{
|
||||
public int Index { get; private set; }
|
||||
|
||||
public AIoType IoType { get; private set; }
|
||||
|
||||
public Type OperType { get; private set; }
|
||||
|
||||
public AILOpCodeLoad(int Index, AIoType IoType) : this(Index, IoType, null) { }
|
||||
|
||||
public AILOpCodeLoad(int Index, AIoType IoType, Type OperType)
|
||||
{
|
||||
this.IoType = IoType;
|
||||
this.Index = Index;
|
||||
this.OperType = OperType;
|
||||
}
|
||||
|
||||
public void Emit(AILEmitter Context)
|
||||
{
|
||||
switch (IoType & AIoType.Mask)
|
||||
{
|
||||
case AIoType.Arg: EmitLdarg(Context, Index); break;
|
||||
case AIoType.Fields: EmitLdfld(Context, Index); break;
|
||||
case AIoType.Flag: EmitLdloc(Context, Index, ARegisterType.Flag); break;
|
||||
case AIoType.Int: EmitLdloc(Context, Index, ARegisterType.Int); break;
|
||||
case AIoType.Vector: EmitLdloc(Context, Index, ARegisterType.Vector); break;
|
||||
}
|
||||
}
|
||||
|
||||
private void EmitLdarg(AILEmitter Context, int Index)
|
||||
{
|
||||
Context.Generator.EmitLdarg(Index);
|
||||
}
|
||||
|
||||
private void EmitLdfld(AILEmitter Context, int Index)
|
||||
{
|
||||
long IntInputs = Context.LocalAlloc.GetIntInputs(Context.GetILBlock(Index));
|
||||
long VecInputs = Context.LocalAlloc.GetVecInputs(Context.GetILBlock(Index));
|
||||
|
||||
LoadLocals(Context, IntInputs, ARegisterType.Int);
|
||||
LoadLocals(Context, VecInputs, ARegisterType.Vector);
|
||||
}
|
||||
|
||||
private void LoadLocals(AILEmitter Context, long Inputs, ARegisterType BaseType)
|
||||
{
|
||||
for (int Bit = 0; Bit < 64; Bit++)
|
||||
{
|
||||
long Mask = 1L << Bit;
|
||||
|
||||
if ((Inputs & Mask) != 0)
|
||||
{
|
||||
ARegister Reg = AILEmitter.GetRegFromBit(Bit, BaseType);
|
||||
|
||||
Context.Generator.EmitLdarg(ATranslatedSub.RegistersArgIdx);
|
||||
Context.Generator.Emit(OpCodes.Ldfld, Reg.GetField());
|
||||
|
||||
AILConv.EmitConv(
|
||||
Context,
|
||||
Context.GetFieldType(Reg.Type),
|
||||
Context.GetLocalType(Reg));
|
||||
|
||||
Context.Generator.EmitStloc(Context.GetLocalIndex(Reg));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void EmitLdloc(AILEmitter Context, int Index, ARegisterType Type)
|
||||
{
|
||||
ARegister Reg = new ARegister(Index, Type);
|
||||
|
||||
Context.Generator.EmitLdloc(Context.GetLocalIndex(Reg));
|
||||
|
||||
AILConv.EmitConv(Context, Context.GetLocalType(Reg), OperType);
|
||||
}
|
||||
}
|
||||
}
|
17
Ryujinx/Cpu/Translation/AILOpCodeLog.cs
Normal file
17
Ryujinx/Cpu/Translation/AILOpCodeLog.cs
Normal file
|
@ -0,0 +1,17 @@
|
|||
namespace ChocolArm64.Translation
|
||||
{
|
||||
struct AILOpCodeLog : IAILEmit
|
||||
{
|
||||
private string Text;
|
||||
|
||||
public AILOpCodeLog(string Text)
|
||||
{
|
||||
this.Text = Text;
|
||||
}
|
||||
|
||||
public void Emit(AILEmitter Context)
|
||||
{
|
||||
Context.Generator.EmitWriteLine(Text);
|
||||
}
|
||||
}
|
||||
}
|
82
Ryujinx/Cpu/Translation/AILOpCodeStore.cs
Normal file
82
Ryujinx/Cpu/Translation/AILOpCodeStore.cs
Normal file
|
@ -0,0 +1,82 @@
|
|||
using ChocolArm64.State;
|
||||
using System;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace ChocolArm64.Translation
|
||||
{
|
||||
struct AILOpCodeStore : IAILEmit
|
||||
{
|
||||
public AIoType IoType { get; private set; }
|
||||
|
||||
public Type OperType { get; private set; }
|
||||
|
||||
public int Index { get; private set; }
|
||||
|
||||
public AILOpCodeStore(int Index, AIoType IoType) : this(Index, IoType, null) { }
|
||||
|
||||
public AILOpCodeStore(int Index, AIoType IoType, Type OperType)
|
||||
{
|
||||
this.IoType = IoType;
|
||||
this.Index = Index;
|
||||
this.OperType = OperType;
|
||||
}
|
||||
|
||||
public void Emit(AILEmitter Context)
|
||||
{
|
||||
switch (IoType & AIoType.Mask)
|
||||
{
|
||||
case AIoType.Arg: EmitStarg(Context, Index); break;
|
||||
case AIoType.Fields: EmitStfld(Context, Index); break;
|
||||
case AIoType.Flag: EmitStloc(Context, Index, ARegisterType.Flag); break;
|
||||
case AIoType.Int: EmitStloc(Context, Index, ARegisterType.Int); break;
|
||||
case AIoType.Vector: EmitStloc(Context, Index, ARegisterType.Vector); break;
|
||||
}
|
||||
}
|
||||
|
||||
private void EmitStarg(AILEmitter Context, int Index)
|
||||
{
|
||||
Context.Generator.EmitStarg(Index);
|
||||
}
|
||||
|
||||
private void EmitStfld(AILEmitter Context, int Index)
|
||||
{
|
||||
long IntOutputs = Context.LocalAlloc.GetIntOutputs(Context.GetILBlock(Index));
|
||||
long VecOutputs = Context.LocalAlloc.GetVecOutputs(Context.GetILBlock(Index));
|
||||
|
||||
StoreLocals(Context, IntOutputs, ARegisterType.Int);
|
||||
StoreLocals(Context, VecOutputs, ARegisterType.Vector);
|
||||
}
|
||||
|
||||
private void StoreLocals(AILEmitter Context, long Outputs, ARegisterType BaseType)
|
||||
{
|
||||
for (int Bit = 0; Bit < 64; Bit++)
|
||||
{
|
||||
long Mask = 1L << Bit;
|
||||
|
||||
if ((Outputs & Mask) != 0)
|
||||
{
|
||||
ARegister Reg = AILEmitter.GetRegFromBit(Bit, BaseType);
|
||||
|
||||
Context.Generator.EmitLdarg(ATranslatedSub.RegistersArgIdx);
|
||||
Context.Generator.EmitLdloc(Context.GetLocalIndex(Reg));
|
||||
|
||||
AILConv.EmitConv(
|
||||
Context,
|
||||
Context.GetLocalType(Reg),
|
||||
Context.GetFieldType(Reg.Type));
|
||||
|
||||
Context.Generator.Emit(OpCodes.Stfld, Reg.GetField());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void EmitStloc(AILEmitter Context, int Index, ARegisterType Type)
|
||||
{
|
||||
ARegister Reg = new ARegister(Index, Type);
|
||||
|
||||
AILConv.EmitConv(Context, OperType, Context.GetLocalType(Reg));
|
||||
|
||||
Context.Generator.EmitStloc(Context.GetLocalIndex(Reg));
|
||||
}
|
||||
}
|
||||
}
|
18
Ryujinx/Cpu/Translation/AIoType.cs
Normal file
18
Ryujinx/Cpu/Translation/AIoType.cs
Normal file
|
@ -0,0 +1,18 @@
|
|||
using System;
|
||||
|
||||
namespace ChocolArm64.Translation
|
||||
{
|
||||
[Flags]
|
||||
enum AIoType
|
||||
{
|
||||
Arg,
|
||||
Fields,
|
||||
Flag,
|
||||
Int,
|
||||
Float,
|
||||
Vector,
|
||||
Mask = 0xff,
|
||||
VectorI = Vector | 1 << 8,
|
||||
VectorF = Vector | 1 << 9
|
||||
}
|
||||
}
|
231
Ryujinx/Cpu/Translation/ALocalAlloc.cs
Normal file
231
Ryujinx/Cpu/Translation/ALocalAlloc.cs
Normal file
|
@ -0,0 +1,231 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace ChocolArm64.Translation
|
||||
{
|
||||
class ALocalAlloc
|
||||
{
|
||||
private class PathIo
|
||||
{
|
||||
private Dictionary<AILBlock, long> AllInputs;
|
||||
private Dictionary<AILBlock, long> CmnOutputs;
|
||||
|
||||
private long AllOutputs;
|
||||
|
||||
public PathIo()
|
||||
{
|
||||
AllInputs = new Dictionary<AILBlock, long>();
|
||||
CmnOutputs = new Dictionary<AILBlock, long>();
|
||||
}
|
||||
|
||||
public PathIo(AILBlock Root, long Inputs, long Outputs) : this()
|
||||
{
|
||||
Set(Root, Inputs, Outputs);
|
||||
}
|
||||
|
||||
public void Set(AILBlock Root, long Inputs, long Outputs)
|
||||
{
|
||||
if (!AllInputs.TryAdd(Root, Inputs))
|
||||
{
|
||||
AllInputs[Root] |= Inputs;
|
||||
}
|
||||
|
||||
if (!CmnOutputs.TryAdd(Root, Outputs))
|
||||
{
|
||||
CmnOutputs[Root] &= Outputs;
|
||||
}
|
||||
|
||||
AllOutputs |= Outputs;
|
||||
}
|
||||
|
||||
public long GetInputs(AILBlock Root)
|
||||
{
|
||||
if (AllInputs.TryGetValue(Root, out long Inputs))
|
||||
{
|
||||
return Inputs | (AllOutputs & ~CmnOutputs[Root]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long GetOutputs()
|
||||
{
|
||||
return AllOutputs;
|
||||
}
|
||||
}
|
||||
|
||||
private Dictionary<AILBlock, PathIo> IntPaths;
|
||||
private Dictionary<AILBlock, PathIo> VecPaths;
|
||||
|
||||
private struct BlockIo
|
||||
{
|
||||
public AILBlock Block;
|
||||
public AILBlock Entry;
|
||||
|
||||
public long IntInputs;
|
||||
public long VecInputs;
|
||||
public long IntOutputs;
|
||||
public long VecOutputs;
|
||||
}
|
||||
|
||||
private const int MaxOptGraphLength = 120;
|
||||
|
||||
public ALocalAlloc(AILBlock[] Graph, AILBlock Root)
|
||||
{
|
||||
IntPaths = new Dictionary<AILBlock, PathIo>();
|
||||
VecPaths = new Dictionary<AILBlock, PathIo>();
|
||||
|
||||
if (Graph.Length < MaxOptGraphLength)
|
||||
{
|
||||
InitializeOptimal(Graph, Root);
|
||||
}
|
||||
else
|
||||
{
|
||||
InitializeFast(Graph);
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeOptimal(AILBlock[] Graph, AILBlock Root)
|
||||
{
|
||||
//This will go through all possible paths on the graph,
|
||||
//and store all inputs/outputs for each block. A register
|
||||
//that was previously written to already is not considered an input.
|
||||
//When a block can be reached by more than one path, then the
|
||||
//output from all paths needs to be set for this block, and
|
||||
//only outputs present in all of the parent blocks can be considered
|
||||
//when doing input elimination. Each block chain have a root, that's where
|
||||
//the code starts executing. They are present on the subroutine start point,
|
||||
//and on call return points too (address written to X30 by BL).
|
||||
HashSet<BlockIo> Visited = new HashSet<BlockIo>();
|
||||
|
||||
Queue<BlockIo> Unvisited = new Queue<BlockIo>();
|
||||
|
||||
void Enqueue(BlockIo Block)
|
||||
{
|
||||
if (!Visited.Contains(Block))
|
||||
{
|
||||
Unvisited.Enqueue(Block);
|
||||
|
||||
Visited.Add(Block);
|
||||
}
|
||||
}
|
||||
|
||||
Enqueue(new BlockIo()
|
||||
{
|
||||
Block = Root,
|
||||
Entry = Root
|
||||
});
|
||||
|
||||
while (Unvisited.Count > 0)
|
||||
{
|
||||
BlockIo Current = Unvisited.Dequeue();
|
||||
|
||||
Current.IntInputs |= Current.Block.IntInputs & ~Current.IntOutputs;
|
||||
Current.VecInputs |= Current.Block.VecInputs & ~Current.VecOutputs;
|
||||
Current.IntOutputs |= Current.Block.IntOutputs;
|
||||
Current.VecOutputs |= Current.Block.VecOutputs;
|
||||
|
||||
//Check if this is a exit block
|
||||
//(a block that returns or calls another sub).
|
||||
if ((Current.Block.Next == null &&
|
||||
Current.Block.Branch == null) || Current.Block.HasStateStore)
|
||||
{
|
||||
if (!IntPaths.TryGetValue(Current.Block, out PathIo IntPath))
|
||||
{
|
||||
IntPaths.Add(Current.Block, IntPath = new PathIo());
|
||||
}
|
||||
|
||||
if (!VecPaths.TryGetValue(Current.Block, out PathIo VecPath))
|
||||
{
|
||||
VecPaths.Add(Current.Block, VecPath = new PathIo());
|
||||
}
|
||||
|
||||
IntPath.Set(Current.Entry, Current.IntInputs, Current.IntOutputs);
|
||||
VecPath.Set(Current.Entry, Current.VecInputs, Current.VecOutputs);
|
||||
}
|
||||
|
||||
void EnqueueFromCurrent(AILBlock Block, bool RetTarget)
|
||||
{
|
||||
BlockIo BlkIO = new BlockIo() { Block = Block };
|
||||
|
||||
if (RetTarget)
|
||||
{
|
||||
BlkIO.Entry = Block;
|
||||
BlkIO.IntInputs = 0;
|
||||
BlkIO.VecInputs = 0;
|
||||
BlkIO.IntOutputs = 0;
|
||||
BlkIO.VecOutputs = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
BlkIO.Entry = Current.Entry;
|
||||
BlkIO.IntInputs = Current.IntInputs;
|
||||
BlkIO.VecInputs = Current.VecInputs;
|
||||
BlkIO.IntOutputs = Current.IntOutputs;
|
||||
BlkIO.VecOutputs = Current.VecOutputs;
|
||||
}
|
||||
|
||||
Enqueue(BlkIO);
|
||||
}
|
||||
|
||||
if (Current.Block.Next != null)
|
||||
{
|
||||
EnqueueFromCurrent(Current.Block.Next, Current.Block.HasStateStore);
|
||||
}
|
||||
|
||||
if (Current.Block.Branch != null)
|
||||
{
|
||||
EnqueueFromCurrent(Current.Block.Branch, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeFast(AILBlock[] Graph)
|
||||
{
|
||||
//This is WAY faster than InitializeOptimal, but results in
|
||||
//uneeded loads and stores, so the resulting code will be slower.
|
||||
long IntInputs = 0;
|
||||
long IntOutputs = 0;
|
||||
long VecInputs = 0;
|
||||
long VecOutputs = 0;
|
||||
|
||||
foreach (AILBlock Block in Graph)
|
||||
{
|
||||
IntInputs |= Block.IntInputs;
|
||||
IntOutputs |= Block.IntOutputs;
|
||||
VecInputs |= Block.VecInputs;
|
||||
VecOutputs |= Block.VecOutputs;
|
||||
}
|
||||
|
||||
//It's possible that not all code paths writes to those output registers,
|
||||
//in those cases if we attempt to write an output registers that was
|
||||
//not written, we will be just writing zero and messing up the old register value.
|
||||
//So we just need to ensure that all outputs are loaded.
|
||||
IntInputs |= IntOutputs;
|
||||
VecInputs |= VecOutputs;
|
||||
|
||||
foreach (AILBlock Block in Graph)
|
||||
{
|
||||
IntPaths.Add(Block, new PathIo(Block, IntInputs, IntOutputs));
|
||||
VecPaths.Add(Block, new PathIo(Block, VecInputs, VecOutputs));
|
||||
}
|
||||
}
|
||||
|
||||
public long GetIntInputs(AILBlock Root) => GetInputsImpl(Root, IntPaths.Values);
|
||||
public long GetVecInputs(AILBlock Root) => GetInputsImpl(Root, VecPaths.Values);
|
||||
|
||||
private long GetInputsImpl(AILBlock Root, IEnumerable<PathIo> Values)
|
||||
{
|
||||
long Inputs = 0;
|
||||
|
||||
foreach (PathIo Path in Values)
|
||||
{
|
||||
Inputs |= Path.GetInputs(Root);
|
||||
}
|
||||
|
||||
return Inputs;
|
||||
}
|
||||
|
||||
public long GetIntOutputs(AILBlock Block) => IntPaths[Block].GetOutputs();
|
||||
public long GetVecOutputs(AILBlock Block) => VecPaths[Block].GetOutputs();
|
||||
}
|
||||
}
|
7
Ryujinx/Cpu/Translation/IAILEmit.cs
Normal file
7
Ryujinx/Cpu/Translation/IAILEmit.cs
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace ChocolArm64.Translation
|
||||
{
|
||||
interface IAILEmit
|
||||
{
|
||||
void Emit(AILEmitter Context);
|
||||
}
|
||||
}
|
129
Ryujinx/Cpu/Translation/ILGeneratorEx.cs
Normal file
129
Ryujinx/Cpu/Translation/ILGeneratorEx.cs
Normal file
|
@ -0,0 +1,129 @@
|
|||
using System;
|
||||
|
||||
namespace ChocolArm64
|
||||
{
|
||||
using System.Reflection.Emit;
|
||||
|
||||
static class ILGeneratorEx
|
||||
{
|
||||
public static void EmitLdc_I4(this ILGenerator Generator,int Value)
|
||||
{
|
||||
switch (Value)
|
||||
{
|
||||
case 0: Generator.Emit(OpCodes.Ldc_I4_0); break;
|
||||
case 1: Generator.Emit(OpCodes.Ldc_I4_1); break;
|
||||
case 2: Generator.Emit(OpCodes.Ldc_I4_2); break;
|
||||
case 3: Generator.Emit(OpCodes.Ldc_I4_3); break;
|
||||
case 4: Generator.Emit(OpCodes.Ldc_I4_4); break;
|
||||
case 5: Generator.Emit(OpCodes.Ldc_I4_5); break;
|
||||
case 6: Generator.Emit(OpCodes.Ldc_I4_6); break;
|
||||
case 7: Generator.Emit(OpCodes.Ldc_I4_7); break;
|
||||
case 8: Generator.Emit(OpCodes.Ldc_I4_8); break;
|
||||
case -1: Generator.Emit(OpCodes.Ldc_I4_M1); break;
|
||||
default: Generator.Emit(OpCodes.Ldc_I4, Value); break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void EmitLdarg(this ILGenerator Generator, int Index)
|
||||
{
|
||||
switch (Index)
|
||||
{
|
||||
case 0: Generator.Emit(OpCodes.Ldarg_0); break;
|
||||
case 1: Generator.Emit(OpCodes.Ldarg_1); break;
|
||||
case 2: Generator.Emit(OpCodes.Ldarg_2); break;
|
||||
case 3: Generator.Emit(OpCodes.Ldarg_3); break;
|
||||
|
||||
default:
|
||||
if ((uint)Index <= byte.MaxValue)
|
||||
{
|
||||
Generator.Emit(OpCodes.Ldarg_S, (byte)Index);
|
||||
}
|
||||
else if ((uint)Index < ushort.MaxValue)
|
||||
{
|
||||
Generator.Emit(OpCodes.Ldarg, (short)Index);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(Index));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void EmitStarg(this ILGenerator Generator, int Index)
|
||||
{
|
||||
if ((uint)Index <= byte.MaxValue)
|
||||
{
|
||||
Generator.Emit(OpCodes.Starg_S, (byte)Index);
|
||||
}
|
||||
else if ((uint)Index < ushort.MaxValue)
|
||||
{
|
||||
Generator.Emit(OpCodes.Starg, (short)Index);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(Index));
|
||||
}
|
||||
}
|
||||
|
||||
public static void EmitLdloc(this ILGenerator Generator, int Index)
|
||||
{
|
||||
switch (Index)
|
||||
{
|
||||
case 0: Generator.Emit(OpCodes.Ldloc_0); break;
|
||||
case 1: Generator.Emit(OpCodes.Ldloc_1); break;
|
||||
case 2: Generator.Emit(OpCodes.Ldloc_2); break;
|
||||
case 3: Generator.Emit(OpCodes.Ldloc_3); break;
|
||||
|
||||
default:
|
||||
if ((uint)Index <= byte.MaxValue)
|
||||
{
|
||||
Generator.Emit(OpCodes.Ldloc_S, (byte)Index);
|
||||
}
|
||||
else if ((uint)Index < ushort.MaxValue)
|
||||
{
|
||||
Generator.Emit(OpCodes.Ldloc, (short)Index);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(Index));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void EmitStloc(this ILGenerator Generator, int Index)
|
||||
{
|
||||
switch (Index)
|
||||
{
|
||||
case 0: Generator.Emit(OpCodes.Stloc_0); break;
|
||||
case 1: Generator.Emit(OpCodes.Stloc_1); break;
|
||||
case 2: Generator.Emit(OpCodes.Stloc_2); break;
|
||||
case 3: Generator.Emit(OpCodes.Stloc_3); break;
|
||||
|
||||
default:
|
||||
if ((uint)Index <= byte.MaxValue)
|
||||
{
|
||||
Generator.Emit(OpCodes.Stloc_S, (byte)Index);
|
||||
}
|
||||
else if ((uint)Index < ushort.MaxValue)
|
||||
{
|
||||
Generator.Emit(OpCodes.Stloc, (short)Index);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(Index));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void EmitLdargSeq(this ILGenerator Generator, int Count)
|
||||
{
|
||||
for (int Index = 0; Index < Count; Index++)
|
||||
{
|
||||
Generator.EmitLdarg(Index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue