This commit is contained in:
gdkchan 2018-02-04 20:08:20 -03:00
commit b7e1d9930d
230 changed files with 17548 additions and 0 deletions

View 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);
}
}
}
}

View 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 };
}
}

View 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;
}
}
}

View 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);
}
}
}

View 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;
}
}
}

View 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);
}
}
}

View 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));
}
}
}

View 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);
}
}
}

View 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;
}
}
}
}

View 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);
}
}
}

View 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);
}
}
}

View 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));
}
}
}

View 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
}
}

View 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();
}
}

View file

@ -0,0 +1,7 @@
namespace ChocolArm64.Translation
{
interface IAILEmit
{
void Emit(AILEmitter Context);
}
}

View 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);
}
}
}
}