Adjust naming conventions for Ryujinx and ChocolArm64 projects (#484)

* Change naming convention for Ryujinx project

* Change naming convention for ChocolArm64 project

* Fix NaN

* Remove unneeded this. from Ryujinx project

* Adjust naming from new PRs

* Name changes based on feedback

* How did this get removed?

* Rebasing fix

* Change FP enum case

* Remove prefix from ChocolArm64 classes - Part 1

* Remove prefix from ChocolArm64 classes - Part 2

* Fix alignment from last commit's renaming

* Rename namespaces

* Rename stragglers

* Fix alignment

* Rename OpCode class

* Missed a few

* Adjust alignment
This commit is contained in:
Alex Barney 2018-10-30 19:43:02 -06:00 committed by gdkchan
parent 5a87e58183
commit 9cb57fb4bb
314 changed files with 19456 additions and 19456 deletions

View file

@ -1,7 +0,0 @@
namespace ChocolArm64.Translation
{
struct AILBarrier : IAILEmit
{
public void Emit(AILEmitter Context) { }
}
}

View file

@ -1,76 +0,0 @@
using System.Collections.Generic;
namespace ChocolArm64.Translation
{
class AILBlock : IAILEmit
{
public long IntInputs { get; private set; }
public long IntOutputs { get; private set; }
public long IntAwOutputs { get; private set; }
public long VecInputs { get; private set; }
public long VecOutputs { get; private set; }
public long VecAwOutputs { 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 AILBarrier)
{
//Those barriers are used to separate the groups of CIL
//opcodes emitted by each ARM instruction.
//We can only consider the new outputs for doing input elimination
//after all the CIL opcodes used by the instruction being emitted.
IntAwOutputs = IntOutputs;
VecAwOutputs = VecOutputs;
}
else if (ILEmitter is AILOpCodeLoad Ld && AILEmitter.IsRegIndex(Ld.Index))
{
switch (Ld.IoType)
{
case AIoType.Flag: IntInputs |= ((1L << Ld.Index) << 32) & ~IntAwOutputs; break;
case AIoType.Int: IntInputs |= (1L << Ld.Index) & ~IntAwOutputs; break;
case AIoType.Vector: VecInputs |= (1L << Ld.Index) & ~VecAwOutputs; break;
}
}
else if (ILEmitter is AILOpCodeStore St)
{
if (AILEmitter.IsRegIndex(St.Index))
{
switch (St.IoType)
{
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

@ -1,188 +0,0 @@
using ChocolArm64.Decoder;
using ChocolArm64.State;
using System;
using System.Collections.Generic;
using System.Reflection.Emit;
using System.Runtime.Intrinsics;
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 AILBlock GetILBlock(int Index) => ILBlocks[Index];
public ATranslatedSub GetSubroutine()
{
LocalAlloc = new ALocalAlloc(ILBlocks, Root);
InitSubroutine();
InitLocals();
foreach (AILBlock ILBlock in ILBlocks)
{
ILBlock.Emit(this);
}
return Subroutine;
}
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 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);
Generator.EmitStloc(GetLocalIndex(Reg));
}
}
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(Vector128<float>);
}
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

@ -1,554 +0,0 @@
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 ATranslatorCache Cache;
private Dictionary<long, AILLabel> Labels;
private int BlkIndex;
private int OpcIndex;
private ABlock[] Graph;
private ABlock Root;
public ABlock CurrBlock => Graph[BlkIndex];
public AOpCode CurrOp => Graph[BlkIndex].OpCodes[OpcIndex];
private AILEmitter Emitter;
private AILBlock ILBlock;
private AOpCode OptOpLastCompare;
private AOpCode OptOpLastFlagSet;
//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;
private const int Tmp5Index = -5;
private const int Tmp6Index = -6;
public AILEmitterCtx(
ATranslatorCache Cache,
ABlock[] Graph,
ABlock Root,
string SubName)
{
this.Cache = Cache ?? throw new ArgumentNullException(nameof(Cache));
this.Graph = Graph ?? throw new ArgumentNullException(nameof(Graph));
this.Root = Root ?? throw new ArgumentNullException(nameof(Root));
Labels = new Dictionary<long, AILLabel>();
Emitter = new AILEmitter(Graph, Root, SubName);
ILBlock = Emitter.GetILBlock(0);
OpcIndex = -1;
if (Graph.Length == 0 || !AdvanceOpCode())
{
throw new ArgumentException(nameof(Graph));
}
}
public ATranslatedSub GetSubroutine()
{
return Emitter.GetSubroutine();
}
public bool AdvanceOpCode()
{
if (OpcIndex + 1 == CurrBlock.OpCodes.Count &&
BlkIndex + 1 == Graph.Length)
{
return false;
}
while (++OpcIndex >= (CurrBlock?.OpCodes.Count ?? 0))
{
BlkIndex++;
OpcIndex = -1;
OptOpLastFlagSet = null;
OptOpLastCompare = null;
ILBlock = Emitter.GetILBlock(BlkIndex);
}
return true;
}
public void EmitOpCode()
{
if (OpcIndex == 0)
{
MarkLabel(GetLabel(CurrBlock.Position));
EmitSynchronization();
}
CurrOp.Emitter(this);
ILBlock.Add(new AILBarrier());
}
private void EmitSynchronization()
{
EmitLdarg(ATranslatedSub.StateArgIdx);
EmitLdc_I4(CurrBlock.OpCodes.Count);
EmitPrivateCall(typeof(AThreadState), nameof(AThreadState.Synchronize));
EmitLdc_I4(0);
AILLabel LblContinue = new AILLabel();
Emit(OpCodes.Bne_Un_S, LblContinue);
EmitLdc_I8(0);
Emit(OpCodes.Ret);
MarkLabel(LblContinue);
}
public bool TryOptEmitSubroutineCall()
{
if (CurrBlock.Next == null)
{
return false;
}
if (CurrOp.Emitter != AInstEmit.Bl)
{
return false;
}
if (!Cache.TryGetSubroutine(((AOpCodeBImmAl)CurrOp).Imm, out ATranslatedSub Subroutine))
{
return false;
}
for (int Index = 0; Index < ATranslatedSub.FixedArgTypes.Length; Index++)
{
EmitLdarg(Index);
}
foreach (ARegister Reg in Subroutine.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(Subroutine.Method);
Subroutine.AddCaller(Root.Position);
return true;
}
public void TryOptMarkCondWithoutCmp()
{
OptOpLastCompare = 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 (OptOpLastCompare != null &&
OptOpLastCompare == OptOpLastFlagSet && BranchOps.ContainsKey(Cond))
{
Ldloc(Tmp3Index, AIoType.Int, OptOpLastCompare.RegisterSize);
Ldloc(Tmp4Index, AIoType.Int, OptOpLastCompare.RegisterSize);
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;
}
bool Sz64 = CurrOp.RegisterSize != ARegisterSize.Int32;
if (Sz64 == (IntType == AIntType.UInt64 ||
IntType == AIntType.Int64))
{
return;
}
if (Sz64)
{
Emit(IntType >= AIntType.Int8
? OpCodes.Conv_I8
: OpCodes.Conv_U8);
}
else
{
Emit(OpCodes.Conv_U4);
}
}
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 != AThreadState.ZRIndex)
{
EmitLdint(Index);
}
else
{
EmitLdc_I(0);
}
}
public void EmitStintzr(int Index)
{
if (Index != AThreadState.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 EmitLdvectmp() => EmitLdvec(Tmp5Index);
public void EmitStvectmp() => EmitStvec(Tmp5Index);
public void EmitLdvectmp2() => EmitLdvec(Tmp6Index);
public void EmitStvectmp2() => EmitStvec(Tmp6Index);
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 EmitLdflg(int Index) => Ldloc(Index, AIoType.Flag);
public void EmitStflg(int Index)
{
OptOpLastFlagSet = CurrOp;
Stloc(Index, AIoType.Flag);
}
private void Ldloc(int Index, AIoType IoType)
{
ILBlock.Add(new AILOpCodeLoad(Index, IoType, CurrOp.RegisterSize));
}
private void Ldloc(int Index, AIoType IoType, ARegisterSize RegisterSize)
{
ILBlock.Add(new AILOpCodeLoad(Index, IoType, RegisterSize));
}
private void Stloc(int Index, AIoType IoType)
{
ILBlock.Add(new AILOpCodeStore(Index, IoType, CurrOp.RegisterSize));
}
public void EmitCallPropGet(Type ObjType, string PropName)
{
if (ObjType == null)
{
throw new ArgumentNullException(nameof(ObjType));
}
if (PropName == null)
{
throw new ArgumentNullException(nameof(PropName));
}
EmitCall(ObjType.GetMethod($"get_{PropName}"));
}
public void EmitCallPropSet(Type ObjType, string PropName)
{
if (ObjType == null)
{
throw new ArgumentNullException(nameof(ObjType));
}
if (PropName == null)
{
throw new ArgumentNullException(nameof(PropName));
}
EmitCall(ObjType.GetMethod($"set_{PropName}"));
}
public void EmitCall(Type ObjType, string MthdName)
{
if (ObjType == null)
{
throw new ArgumentNullException(nameof(ObjType));
}
if (MthdName == null)
{
throw new ArgumentNullException(nameof(MthdName));
}
EmitCall(ObjType.GetMethod(MthdName));
}
public void EmitPrivateCall(Type ObjType, string MthdName)
{
if (ObjType == null)
{
throw new ArgumentNullException(nameof(ObjType));
}
if (MthdName == null)
{
throw new ArgumentNullException(nameof(MthdName));
}
EmitCall(ObjType.GetMethod(MthdName, BindingFlags.Instance | BindingFlags.NonPublic));
}
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

@ -1,28 +0,0 @@
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

@ -1,19 +0,0 @@
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

@ -1,21 +0,0 @@
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

@ -1,20 +0,0 @@
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

@ -1,65 +0,0 @@
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: 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

@ -1,75 +0,0 @@
using ChocolArm64.State;
using System.Reflection.Emit;
namespace ChocolArm64.Translation
{
struct AILOpCodeLoad : IAILEmit
{
public int Index { get; private set; }
public AIoType IoType { get; private set; }
public ARegisterSize RegisterSize { get; private set; }
public AILOpCodeLoad(int Index, AIoType IoType, ARegisterSize RegisterSize = 0)
{
this.Index = Index;
this.IoType = IoType;
this.RegisterSize = RegisterSize;
}
public void Emit(AILEmitter Context)
{
switch (IoType)
{
case AIoType.Arg: Context.Generator.EmitLdarg(Index); break;
case AIoType.Fields:
{
long IntInputs = Context.LocalAlloc.GetIntInputs(Context.GetILBlock(Index));
long VecInputs = Context.LocalAlloc.GetVecInputs(Context.GetILBlock(Index));
LoadLocals(Context, IntInputs, ARegisterType.Int);
LoadLocals(Context, VecInputs, ARegisterType.Vector);
break;
}
case AIoType.Flag: EmitLdloc(Context, Index, ARegisterType.Flag); break;
case AIoType.Int: EmitLdloc(Context, Index, ARegisterType.Int); break;
case AIoType.Vector: EmitLdloc(Context, Index, ARegisterType.Vector); break;
}
}
private void LoadLocals(AILEmitter Context, long Inputs, ARegisterType BaseType)
{
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.StateArgIdx);
Context.Generator.Emit(OpCodes.Ldfld, Reg.GetField());
Context.Generator.EmitStloc(Context.GetLocalIndex(Reg));
}
}
}
private void EmitLdloc(AILEmitter Context, int Index, ARegisterType RegisterType)
{
ARegister Reg = new ARegister(Index, RegisterType);
Context.Generator.EmitLdloc(Context.GetLocalIndex(Reg));
if (RegisterType == ARegisterType.Int &&
RegisterSize == ARegisterSize.Int32)
{
Context.Generator.Emit(OpCodes.Conv_U4);
}
}
}
}

View file

@ -1,17 +0,0 @@
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

@ -1,75 +0,0 @@
using ChocolArm64.State;
using System.Reflection.Emit;
namespace ChocolArm64.Translation
{
struct AILOpCodeStore : IAILEmit
{
public int Index { get; private set; }
public AIoType IoType { get; private set; }
public ARegisterSize RegisterSize { get; private set; }
public AILOpCodeStore(int Index, AIoType IoType, ARegisterSize RegisterSize = 0)
{
this.Index = Index;
this.IoType = IoType;
this.RegisterSize = RegisterSize;
}
public void Emit(AILEmitter Context)
{
switch (IoType)
{
case AIoType.Arg: Context.Generator.EmitStarg(Index); break;
case AIoType.Fields:
{
long IntOutputs = Context.LocalAlloc.GetIntOutputs(Context.GetILBlock(Index));
long VecOutputs = Context.LocalAlloc.GetVecOutputs(Context.GetILBlock(Index));
StoreLocals(Context, IntOutputs, ARegisterType.Int);
StoreLocals(Context, VecOutputs, ARegisterType.Vector);
break;
}
case AIoType.Flag: EmitStloc(Context, Index, ARegisterType.Flag); break;
case AIoType.Int: EmitStloc(Context, Index, ARegisterType.Int); break;
case AIoType.Vector: EmitStloc(Context, Index, ARegisterType.Vector); break;
}
}
private void StoreLocals(AILEmitter Context, long Outputs, ARegisterType BaseType)
{
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.StateArgIdx);
Context.Generator.EmitLdloc(Context.GetLocalIndex(Reg));
Context.Generator.Emit(OpCodes.Stfld, Reg.GetField());
}
}
}
private void EmitStloc(AILEmitter Context, int Index, ARegisterType RegisterType)
{
ARegister Reg = new ARegister(Index, RegisterType);
if (RegisterType == ARegisterType.Int &&
RegisterSize == ARegisterSize.Int32)
{
Context.Generator.Emit(OpCodes.Conv_U8);
}
Context.Generator.EmitStloc(Context.GetLocalIndex(Reg));
}
}
}

View file

@ -1,229 +0,0 @@
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 = 40;
public ALocalAlloc(AILBlock[] Graph, AILBlock Root)
{
IntPaths = new Dictionary<AILBlock, PathIo>();
VecPaths = new Dictionary<AILBlock, PathIo>();
if (Graph.Length > 1 &&
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;
}
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, IntOutputs = 0;
long VecInputs = 0, 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.
if (Graph.Length > 1)
{
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

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

View file

@ -0,0 +1,7 @@
namespace ChocolArm64.Translation
{
interface IILEmit
{
void Emit(ILEmitter context);
}
}

View file

@ -0,0 +1,7 @@
namespace ChocolArm64.Translation
{
struct ILBarrier : IILEmit
{
public void Emit(ILEmitter context) { }
}
}

View file

@ -0,0 +1,76 @@
using System.Collections.Generic;
namespace ChocolArm64.Translation
{
class ILBlock : IILEmit
{
public long IntInputs { get; private set; }
public long IntOutputs { get; private set; }
public long IntAwOutputs { get; private set; }
public long VecInputs { get; private set; }
public long VecOutputs { get; private set; }
public long VecAwOutputs { get; private set; }
public bool HasStateStore { get; private set; }
public List<IILEmit> IlEmitters { get; private set; }
public ILBlock Next { get; set; }
public ILBlock Branch { get; set; }
public ILBlock()
{
IlEmitters = new List<IILEmit>();
}
public void Add(IILEmit ilEmitter)
{
if (ilEmitter is ILBarrier)
{
//Those barriers are used to separate the groups of CIL
//opcodes emitted by each ARM instruction.
//We can only consider the new outputs for doing input elimination
//after all the CIL opcodes used by the instruction being emitted.
IntAwOutputs = IntOutputs;
VecAwOutputs = VecOutputs;
}
else if (ilEmitter is IlOpCodeLoad ld && ILEmitter.IsRegIndex(ld.Index))
{
switch (ld.IoType)
{
case IoType.Flag: IntInputs |= ((1L << ld.Index) << 32) & ~IntAwOutputs; break;
case IoType.Int: IntInputs |= (1L << ld.Index) & ~IntAwOutputs; break;
case IoType.Vector: VecInputs |= (1L << ld.Index) & ~VecAwOutputs; break;
}
}
else if (ilEmitter is IlOpCodeStore st)
{
if (ILEmitter.IsRegIndex(st.Index))
{
switch (st.IoType)
{
case IoType.Flag: IntOutputs |= (1L << st.Index) << 32; break;
case IoType.Int: IntOutputs |= 1L << st.Index; break;
case IoType.Vector: VecOutputs |= 1L << st.Index; break;
}
}
if (st.IoType == IoType.Fields)
{
HasStateStore = true;
}
}
IlEmitters.Add(ilEmitter);
}
public void Emit(ILEmitter context)
{
foreach (IILEmit ilEmitter in IlEmitters)
{
ilEmitter.Emit(context);
}
}
}
}

View file

@ -0,0 +1,188 @@
using ChocolArm64.Decoders;
using ChocolArm64.State;
using System;
using System.Collections.Generic;
using System.Reflection.Emit;
using System.Runtime.Intrinsics;
namespace ChocolArm64.Translation
{
class ILEmitter
{
public LocalAlloc LocalAlloc { get; private set; }
public ILGenerator Generator { get; private set; }
private Dictionary<Register, int> _locals;
private ILBlock[] _ilBlocks;
private ILBlock _root;
private TranslatedSub _subroutine;
private string _subName;
private int _localsCount;
public ILEmitter(Block[] graph, Block root, string subName)
{
_subName = subName;
_locals = new Dictionary<Register, int>();
_ilBlocks = new ILBlock[graph.Length];
ILBlock GetBlock(int index)
{
if (index < 0 || index >= _ilBlocks.Length)
{
return null;
}
if (_ilBlocks[index] == null)
{
_ilBlocks[index] = new ILBlock();
}
return _ilBlocks[index];
}
for (int index = 0; index < _ilBlocks.Length; index++)
{
ILBlock block = GetBlock(index);
block.Next = GetBlock(Array.IndexOf(graph, graph[index].Next));
block.Branch = GetBlock(Array.IndexOf(graph, graph[index].Branch));
}
_root = _ilBlocks[Array.IndexOf(graph, root)];
}
public ILBlock GetIlBlock(int index) => _ilBlocks[index];
public TranslatedSub GetSubroutine()
{
LocalAlloc = new LocalAlloc(_ilBlocks, _root);
InitSubroutine();
InitLocals();
foreach (ILBlock ilBlock in _ilBlocks)
{
ilBlock.Emit(this);
}
return _subroutine;
}
private void InitSubroutine()
{
List<Register> Params = new List<Register>();
void SetParams(long inputs, RegisterType 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), RegisterType.Int);
SetParams(LocalAlloc.GetVecInputs(_root), RegisterType.Vector);
DynamicMethod mthd = new DynamicMethod(_subName, typeof(long), GetParamTypes(Params));
Generator = mthd.GetILGenerator();
_subroutine = new TranslatedSub(mthd, Params);
}
private void InitLocals()
{
int paramsStart = TranslatedSub.FixedArgTypes.Length;
_locals = new Dictionary<Register, int>();
for (int index = 0; index < _subroutine.Params.Count; index++)
{
Register reg = _subroutine.Params[index];
Generator.EmitLdarg(index + paramsStart);
Generator.EmitStloc(GetLocalIndex(reg));
}
}
private Type[] GetParamTypes(IList<Register> Params)
{
Type[] fixedArgs = TranslatedSub.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(Register reg)
{
if (!_locals.TryGetValue(reg, out int index))
{
Generator.DeclareLocal(GetLocalType(reg));
index = _localsCount++;
_locals.Add(reg, index);
}
return index;
}
public Type GetLocalType(Register reg) => GetFieldType(reg.Type);
public Type GetFieldType(RegisterType regType)
{
switch (regType)
{
case RegisterType.Flag: return typeof(bool);
case RegisterType.Int: return typeof(ulong);
case RegisterType.Vector: return typeof(Vector128<float>);
}
throw new ArgumentException(nameof(regType));
}
public static Register GetRegFromBit(int bit, RegisterType baseType)
{
if (bit < 32)
{
return new Register(bit, baseType);
}
else if (baseType == RegisterType.Int)
{
return new Register(bit & 0x1f, RegisterType.Flag);
}
else
{
throw new ArgumentOutOfRangeException(nameof(bit));
}
}
public static bool IsRegIndex(int index)
{
return index >= 0 && index < 32;
}
}
}

View file

@ -0,0 +1,554 @@
using ChocolArm64.Decoders;
using ChocolArm64.Instructions;
using ChocolArm64.State;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
namespace ChocolArm64.Translation
{
class ILEmitterCtx
{
private TranslatorCache _cache;
private Dictionary<long, ILLabel> _labels;
private int _blkIndex;
private int _opcIndex;
private Block[] _graph;
private Block _root;
public Block CurrBlock => _graph[_blkIndex];
public OpCode64 CurrOp => _graph[_blkIndex].OpCodes[_opcIndex];
private ILEmitter _emitter;
private ILBlock _ilBlock;
private OpCode64 _optOpLastCompare;
private OpCode64 _optOpLastFlagSet;
//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;
private const int Tmp5Index = -5;
private const int Tmp6Index = -6;
public ILEmitterCtx(
TranslatorCache cache,
Block[] graph,
Block root,
string subName)
{
_cache = cache ?? throw new ArgumentNullException(nameof(cache));
_graph = graph ?? throw new ArgumentNullException(nameof(graph));
_root = root ?? throw new ArgumentNullException(nameof(root));
_labels = new Dictionary<long, ILLabel>();
_emitter = new ILEmitter(graph, root, subName);
_ilBlock = _emitter.GetIlBlock(0);
_opcIndex = -1;
if (graph.Length == 0 || !AdvanceOpCode())
{
throw new ArgumentException(nameof(graph));
}
}
public TranslatedSub GetSubroutine()
{
return _emitter.GetSubroutine();
}
public bool AdvanceOpCode()
{
if (_opcIndex + 1 == CurrBlock.OpCodes.Count &&
_blkIndex + 1 == _graph.Length)
{
return false;
}
while (++_opcIndex >= (CurrBlock?.OpCodes.Count ?? 0))
{
_blkIndex++;
_opcIndex = -1;
_optOpLastFlagSet = null;
_optOpLastCompare = null;
_ilBlock = _emitter.GetIlBlock(_blkIndex);
}
return true;
}
public void EmitOpCode()
{
if (_opcIndex == 0)
{
MarkLabel(GetLabel(CurrBlock.Position));
EmitSynchronization();
}
CurrOp.Emitter(this);
_ilBlock.Add(new ILBarrier());
}
private void EmitSynchronization()
{
EmitLdarg(TranslatedSub.StateArgIdx);
EmitLdc_I4(CurrBlock.OpCodes.Count);
EmitPrivateCall(typeof(CpuThreadState), nameof(CpuThreadState.Synchronize));
EmitLdc_I4(0);
ILLabel lblContinue = new ILLabel();
Emit(OpCodes.Bne_Un_S, lblContinue);
EmitLdc_I8(0);
Emit(OpCodes.Ret);
MarkLabel(lblContinue);
}
public bool TryOptEmitSubroutineCall()
{
if (CurrBlock.Next == null)
{
return false;
}
if (CurrOp.Emitter != InstEmit.Bl)
{
return false;
}
if (!_cache.TryGetSubroutine(((OpCodeBImmAl64)CurrOp).Imm, out TranslatedSub subroutine))
{
return false;
}
for (int index = 0; index < TranslatedSub.FixedArgTypes.Length; index++)
{
EmitLdarg(index);
}
foreach (Register reg in subroutine.Params)
{
switch (reg.Type)
{
case RegisterType.Flag: Ldloc(reg.Index, IoType.Flag); break;
case RegisterType.Int: Ldloc(reg.Index, IoType.Int); break;
case RegisterType.Vector: Ldloc(reg.Index, IoType.Vector); break;
}
}
EmitCall(subroutine.Method);
subroutine.AddCaller(_root.Position);
return true;
}
public void TryOptMarkCondWithoutCmp()
{
_optOpLastCompare = CurrOp;
InstEmitAluHelper.EmitDataLoadOpers(this);
Stloc(Tmp4Index, IoType.Int);
Stloc(Tmp3Index, IoType.Int);
}
private Dictionary<Cond, System.Reflection.Emit.OpCode> _branchOps = new Dictionary<Cond, System.Reflection.Emit.OpCode>()
{
{ Cond.Eq, OpCodes.Beq },
{ Cond.Ne, OpCodes.Bne_Un },
{ Cond.GeUn, OpCodes.Bge_Un },
{ Cond.LtUn, OpCodes.Blt_Un },
{ Cond.GtUn, OpCodes.Bgt_Un },
{ Cond.LeUn, OpCodes.Ble_Un },
{ Cond.Ge, OpCodes.Bge },
{ Cond.Lt, OpCodes.Blt },
{ Cond.Gt, OpCodes.Bgt },
{ Cond.Le, OpCodes.Ble }
};
public void EmitCondBranch(ILLabel target, Cond cond)
{
System.Reflection.Emit.OpCode ilOp;
int intCond = (int)cond;
if (_optOpLastCompare != null &&
_optOpLastCompare == _optOpLastFlagSet && _branchOps.ContainsKey(cond))
{
Ldloc(Tmp3Index, IoType.Int, _optOpLastCompare.RegisterSize);
Ldloc(Tmp4Index, IoType.Int, _optOpLastCompare.RegisterSize);
ilOp = _branchOps[cond];
}
else if (intCond < 14)
{
int condTrue = intCond >> 1;
switch (condTrue)
{
case 0: EmitLdflg((int)PState.ZBit); break;
case 1: EmitLdflg((int)PState.CBit); break;
case 2: EmitLdflg((int)PState.NBit); break;
case 3: EmitLdflg((int)PState.VBit); break;
case 4:
EmitLdflg((int)PState.CBit);
EmitLdflg((int)PState.ZBit);
Emit(OpCodes.Not);
Emit(OpCodes.And);
break;
case 5:
case 6:
EmitLdflg((int)PState.NBit);
EmitLdflg((int)PState.VBit);
Emit(OpCodes.Ceq);
if (condTrue == 6)
{
EmitLdflg((int)PState.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(IntType intType)
{
switch (intType)
{
case IntType.UInt8: Emit(OpCodes.Conv_U1); break;
case IntType.UInt16: Emit(OpCodes.Conv_U2); break;
case IntType.UInt32: Emit(OpCodes.Conv_U4); break;
case IntType.UInt64: Emit(OpCodes.Conv_U8); break;
case IntType.Int8: Emit(OpCodes.Conv_I1); break;
case IntType.Int16: Emit(OpCodes.Conv_I2); break;
case IntType.Int32: Emit(OpCodes.Conv_I4); break;
case IntType.Int64: Emit(OpCodes.Conv_I8); break;
}
bool sz64 = CurrOp.RegisterSize != RegisterSize.Int32;
if (sz64 == (intType == IntType.UInt64 ||
intType == IntType.Int64))
{
return;
}
if (sz64)
{
Emit(intType >= IntType.Int8
? OpCodes.Conv_I8
: OpCodes.Conv_U8);
}
else
{
Emit(OpCodes.Conv_U4);
}
}
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, System.Reflection.Emit.OpCode ilOp)
{
if (amount > 0)
{
EmitLdc_I4(amount);
Emit(ilOp);
}
}
public void EmitRor(int amount)
{
if (amount > 0)
{
Stloc(Tmp2Index, IoType.Int);
Ldloc(Tmp2Index, IoType.Int);
EmitLdc_I4(amount);
Emit(OpCodes.Shr_Un);
Ldloc(Tmp2Index, IoType.Int);
EmitLdc_I4(CurrOp.GetBitsCount() - amount);
Emit(OpCodes.Shl);
Emit(OpCodes.Or);
}
}
public ILLabel GetLabel(long position)
{
if (!_labels.TryGetValue(position, out ILLabel output))
{
output = new ILLabel();
_labels.Add(position, output);
}
return output;
}
public void MarkLabel(ILLabel label)
{
_ilBlock.Add(label);
}
public void Emit(System.Reflection.Emit.OpCode ilOp)
{
_ilBlock.Add(new ILOpCode(ilOp));
}
public void Emit(System.Reflection.Emit.OpCode ilOp, ILLabel label)
{
_ilBlock.Add(new ILOpCodeBranch(ilOp, label));
}
public void Emit(string text)
{
_ilBlock.Add(new ILOpCodeLog(text));
}
public void EmitLdarg(int index)
{
_ilBlock.Add(new IlOpCodeLoad(index, IoType.Arg));
}
public void EmitLdintzr(int index)
{
if (index != CpuThreadState.ZrIndex)
{
EmitLdint(index);
}
else
{
EmitLdc_I(0);
}
}
public void EmitStintzr(int index)
{
if (index != CpuThreadState.ZrIndex)
{
EmitStint(index);
}
else
{
Emit(OpCodes.Pop);
}
}
public void EmitLoadState(Block retBlk)
{
_ilBlock.Add(new IlOpCodeLoad(Array.IndexOf(_graph, retBlk), IoType.Fields));
}
public void EmitStoreState()
{
_ilBlock.Add(new IlOpCodeStore(Array.IndexOf(_graph, CurrBlock), IoType.Fields));
}
public void EmitLdtmp() => EmitLdint(Tmp1Index);
public void EmitSttmp() => EmitStint(Tmp1Index);
public void EmitLdvectmp() => EmitLdvec(Tmp5Index);
public void EmitStvectmp() => EmitStvec(Tmp5Index);
public void EmitLdvectmp2() => EmitLdvec(Tmp6Index);
public void EmitStvectmp2() => EmitStvec(Tmp6Index);
public void EmitLdint(int index) => Ldloc(index, IoType.Int);
public void EmitStint(int index) => Stloc(index, IoType.Int);
public void EmitLdvec(int index) => Ldloc(index, IoType.Vector);
public void EmitStvec(int index) => Stloc(index, IoType.Vector);
public void EmitLdflg(int index) => Ldloc(index, IoType.Flag);
public void EmitStflg(int index)
{
_optOpLastFlagSet = CurrOp;
Stloc(index, IoType.Flag);
}
private void Ldloc(int index, IoType ioType)
{
_ilBlock.Add(new IlOpCodeLoad(index, ioType, CurrOp.RegisterSize));
}
private void Ldloc(int index, IoType ioType, RegisterSize registerSize)
{
_ilBlock.Add(new IlOpCodeLoad(index, ioType, registerSize));
}
private void Stloc(int index, IoType ioType)
{
_ilBlock.Add(new IlOpCodeStore(index, ioType, CurrOp.RegisterSize));
}
public void EmitCallPropGet(Type objType, string propName)
{
if (objType == null)
{
throw new ArgumentNullException(nameof(objType));
}
if (propName == null)
{
throw new ArgumentNullException(nameof(propName));
}
EmitCall(objType.GetMethod($"get_{propName}"));
}
public void EmitCallPropSet(Type objType, string propName)
{
if (objType == null)
{
throw new ArgumentNullException(nameof(objType));
}
if (propName == null)
{
throw new ArgumentNullException(nameof(propName));
}
EmitCall(objType.GetMethod($"set_{propName}"));
}
public void EmitCall(Type objType, string mthdName)
{
if (objType == null)
{
throw new ArgumentNullException(nameof(objType));
}
if (mthdName == null)
{
throw new ArgumentNullException(nameof(mthdName));
}
EmitCall(objType.GetMethod(mthdName));
}
public void EmitPrivateCall(Type objType, string mthdName)
{
if (objType == null)
{
throw new ArgumentNullException(nameof(objType));
}
if (mthdName == null)
{
throw new ArgumentNullException(nameof(mthdName));
}
EmitCall(objType.GetMethod(mthdName, BindingFlags.Instance | BindingFlags.NonPublic));
}
public void EmitCall(MethodInfo mthdInfo)
{
if (mthdInfo == null)
{
throw new ArgumentNullException(nameof(mthdInfo));
}
_ilBlock.Add(new ILOpCodeCall(mthdInfo));
}
public void EmitLdc_I(long value)
{
if (CurrOp.RegisterSize == RegisterSize.Int32)
{
EmitLdc_I4((int)value);
}
else
{
EmitLdc_I8(value);
}
}
public void EmitLdc_I4(int value)
{
_ilBlock.Add(new ILOpCodeConst(value));
}
public void EmitLdc_I8(long value)
{
_ilBlock.Add(new ILOpCodeConst(value));
}
public void EmitLdc_R4(float value)
{
_ilBlock.Add(new ILOpCodeConst(value));
}
public void EmitLdc_R8(double value)
{
_ilBlock.Add(new ILOpCodeConst(value));
}
public void EmitZnFlagCheck()
{
EmitZnCheck(OpCodes.Ceq, (int)PState.ZBit);
EmitZnCheck(OpCodes.Clt, (int)PState.NBit);
}
private void EmitZnCheck(System.Reflection.Emit.OpCode ilCmpOp, int flag)
{
Emit(OpCodes.Dup);
Emit(OpCodes.Ldc_I4_0);
if (CurrOp.RegisterSize != RegisterSize.Int32)
{
Emit(OpCodes.Conv_I8);
}
Emit(ilCmpOp);
EmitStflg(flag);
}
}
}

View file

@ -6,123 +6,123 @@ namespace ChocolArm64
static class ILGeneratorEx
{
public static void EmitLdc_I4(this ILGenerator Generator, int Value)
public static void EmitLdc_I4(this ILGenerator generator, int value)
{
switch (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;
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)
public static void EmitLdarg(this ILGenerator generator, int index)
{
switch (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;
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)
if ((uint)index <= byte.MaxValue)
{
Generator.Emit(OpCodes.Ldarg_S, (byte)Index);
generator.Emit(OpCodes.Ldarg_S, (byte)index);
}
else if ((uint)Index < ushort.MaxValue)
else if ((uint)index < ushort.MaxValue)
{
Generator.Emit(OpCodes.Ldarg, (short)Index);
generator.Emit(OpCodes.Ldarg, (short)index);
}
else
{
throw new ArgumentOutOfRangeException(nameof(Index));
throw new ArgumentOutOfRangeException(nameof(index));
}
break;
}
}
public static void EmitStarg(this ILGenerator Generator, int Index)
public static void EmitStarg(this ILGenerator generator, int index)
{
if ((uint)Index <= byte.MaxValue)
if ((uint)index <= byte.MaxValue)
{
Generator.Emit(OpCodes.Starg_S, (byte)Index);
generator.Emit(OpCodes.Starg_S, (byte)index);
}
else if ((uint)Index < ushort.MaxValue)
else if ((uint)index < ushort.MaxValue)
{
Generator.Emit(OpCodes.Starg, (short)Index);
generator.Emit(OpCodes.Starg, (short)index);
}
else
{
throw new ArgumentOutOfRangeException(nameof(Index));
throw new ArgumentOutOfRangeException(nameof(index));
}
}
public static void EmitLdloc(this ILGenerator Generator, int Index)
public static void EmitLdloc(this ILGenerator generator, int index)
{
switch (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;
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)
if ((uint)index <= byte.MaxValue)
{
Generator.Emit(OpCodes.Ldloc_S, (byte)Index);
generator.Emit(OpCodes.Ldloc_S, (byte)index);
}
else if ((uint)Index < ushort.MaxValue)
else if ((uint)index < ushort.MaxValue)
{
Generator.Emit(OpCodes.Ldloc, (short)Index);
generator.Emit(OpCodes.Ldloc, (short)index);
}
else
{
throw new ArgumentOutOfRangeException(nameof(Index));
throw new ArgumentOutOfRangeException(nameof(index));
}
break;
}
}
public static void EmitStloc(this ILGenerator Generator, int Index)
public static void EmitStloc(this ILGenerator generator, int index)
{
switch (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;
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)
if ((uint)index <= byte.MaxValue)
{
Generator.Emit(OpCodes.Stloc_S, (byte)Index);
generator.Emit(OpCodes.Stloc_S, (byte)index);
}
else if ((uint)Index < ushort.MaxValue)
else if ((uint)index < ushort.MaxValue)
{
Generator.Emit(OpCodes.Stloc, (short)Index);
generator.Emit(OpCodes.Stloc, (short)index);
}
else
{
throw new ArgumentOutOfRangeException(nameof(Index));
throw new ArgumentOutOfRangeException(nameof(index));
}
break;
}
}
public static void EmitLdargSeq(this ILGenerator Generator, int Count)
public static void EmitLdargSeq(this ILGenerator generator, int count)
{
for (int Index = 0; Index < Count; Index++)
for (int index = 0; index < count; index++)
{
Generator.EmitLdarg(Index);
generator.EmitLdarg(index);
}
}
}

View file

@ -0,0 +1,28 @@
using System.Reflection.Emit;
namespace ChocolArm64.Translation
{
class ILLabel : IILEmit
{
private bool _hasLabel;
private Label _lbl;
public void Emit(ILEmitter context)
{
context.Generator.MarkLabel(GetLabel(context));
}
public Label GetLabel(ILEmitter 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 ILOpCode : IILEmit
{
private OpCode _ilOp;
public ILOpCode(OpCode ilOp)
{
_ilOp = ilOp;
}
public void Emit(ILEmitter context)
{
context.Generator.Emit(_ilOp);
}
}
}

View file

@ -0,0 +1,21 @@
using System.Reflection.Emit;
namespace ChocolArm64.Translation
{
struct ILOpCodeBranch : IILEmit
{
private OpCode _ilOp;
private ILLabel _label;
public ILOpCodeBranch(OpCode ilOp, ILLabel label)
{
_ilOp = ilOp;
_label = label;
}
public void Emit(ILEmitter 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 ILOpCodeCall : IILEmit
{
private MethodInfo _mthdInfo;
public ILOpCodeCall(MethodInfo mthdInfo)
{
_mthdInfo = mthdInfo;
}
public void Emit(ILEmitter context)
{
context.Generator.Emit(OpCodes.Call, _mthdInfo);
}
}
}

View file

@ -0,0 +1,65 @@
using System.Reflection.Emit;
using System.Runtime.InteropServices;
namespace ChocolArm64.Translation
{
class ILOpCodeConst : IILEmit
{
[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 ILOpCodeConst(ConstType type)
{
_type = type;
}
public ILOpCodeConst(int value) : this(ConstType.Int32)
{
_value = new ImmVal { I4 = value };
}
public ILOpCodeConst(long value) : this(ConstType.Int64)
{
_value = new ImmVal { I8 = value };
}
public ILOpCodeConst(float value) : this(ConstType.Single)
{
_value = new ImmVal { R4 = value };
}
public ILOpCodeConst(double value) : this(ConstType.Double)
{
_value = new ImmVal { R8 = value };
}
public void Emit(ILEmitter context)
{
switch (_type)
{
case ConstType.Int32: context.Generator.EmitLdc_I4(_value.I4); break;
case ConstType.Int64: 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,75 @@
using ChocolArm64.State;
using System.Reflection.Emit;
namespace ChocolArm64.Translation
{
struct IlOpCodeLoad : IILEmit
{
public int Index { get; private set; }
public IoType IoType { get; private set; }
public RegisterSize RegisterSize { get; private set; }
public IlOpCodeLoad(int index, IoType ioType, RegisterSize registerSize = 0)
{
Index = index;
IoType = ioType;
RegisterSize = registerSize;
}
public void Emit(ILEmitter context)
{
switch (IoType)
{
case IoType.Arg: context.Generator.EmitLdarg(Index); break;
case IoType.Fields:
{
long intInputs = context.LocalAlloc.GetIntInputs(context.GetIlBlock(Index));
long vecInputs = context.LocalAlloc.GetVecInputs(context.GetIlBlock(Index));
LoadLocals(context, intInputs, RegisterType.Int);
LoadLocals(context, vecInputs, RegisterType.Vector);
break;
}
case IoType.Flag: EmitLdloc(context, Index, RegisterType.Flag); break;
case IoType.Int: EmitLdloc(context, Index, RegisterType.Int); break;
case IoType.Vector: EmitLdloc(context, Index, RegisterType.Vector); break;
}
}
private void LoadLocals(ILEmitter context, long inputs, RegisterType baseType)
{
for (int bit = 0; bit < 64; bit++)
{
long mask = 1L << bit;
if ((inputs & mask) != 0)
{
Register reg = ILEmitter.GetRegFromBit(bit, baseType);
context.Generator.EmitLdarg(TranslatedSub.StateArgIdx);
context.Generator.Emit(OpCodes.Ldfld, reg.GetField());
context.Generator.EmitStloc(context.GetLocalIndex(reg));
}
}
}
private void EmitLdloc(ILEmitter context, int index, RegisterType registerType)
{
Register reg = new Register(index, registerType);
context.Generator.EmitLdloc(context.GetLocalIndex(reg));
if (registerType == RegisterType.Int &&
RegisterSize == RegisterSize.Int32)
{
context.Generator.Emit(OpCodes.Conv_U4);
}
}
}
}

View file

@ -0,0 +1,17 @@
namespace ChocolArm64.Translation
{
struct ILOpCodeLog : IILEmit
{
private string _text;
public ILOpCodeLog(string text)
{
_text = text;
}
public void Emit(ILEmitter context)
{
context.Generator.EmitWriteLine(_text);
}
}
}

View file

@ -0,0 +1,75 @@
using ChocolArm64.State;
using System.Reflection.Emit;
namespace ChocolArm64.Translation
{
struct IlOpCodeStore : IILEmit
{
public int Index { get; private set; }
public IoType IoType { get; private set; }
public RegisterSize RegisterSize { get; private set; }
public IlOpCodeStore(int index, IoType ioType, RegisterSize registerSize = 0)
{
Index = index;
IoType = ioType;
RegisterSize = registerSize;
}
public void Emit(ILEmitter context)
{
switch (IoType)
{
case IoType.Arg: context.Generator.EmitStarg(Index); break;
case IoType.Fields:
{
long intOutputs = context.LocalAlloc.GetIntOutputs(context.GetIlBlock(Index));
long vecOutputs = context.LocalAlloc.GetVecOutputs(context.GetIlBlock(Index));
StoreLocals(context, intOutputs, RegisterType.Int);
StoreLocals(context, vecOutputs, RegisterType.Vector);
break;
}
case IoType.Flag: EmitStloc(context, Index, RegisterType.Flag); break;
case IoType.Int: EmitStloc(context, Index, RegisterType.Int); break;
case IoType.Vector: EmitStloc(context, Index, RegisterType.Vector); break;
}
}
private void StoreLocals(ILEmitter context, long outputs, RegisterType baseType)
{
for (int bit = 0; bit < 64; bit++)
{
long mask = 1L << bit;
if ((outputs & mask) != 0)
{
Register reg = ILEmitter.GetRegFromBit(bit, baseType);
context.Generator.EmitLdarg(TranslatedSub.StateArgIdx);
context.Generator.EmitLdloc(context.GetLocalIndex(reg));
context.Generator.Emit(OpCodes.Stfld, reg.GetField());
}
}
}
private void EmitStloc(ILEmitter context, int index, RegisterType registerType)
{
Register reg = new Register(index, registerType);
if (registerType == RegisterType.Int &&
RegisterSize == RegisterSize.Int32)
{
context.Generator.Emit(OpCodes.Conv_U8);
}
context.Generator.EmitStloc(context.GetLocalIndex(reg));
}
}
}

View file

@ -3,7 +3,7 @@ using System;
namespace ChocolArm64.Translation
{
[Flags]
enum AIoType
enum IoType
{
Arg,
Fields,

View file

@ -0,0 +1,229 @@
using System.Collections.Generic;
namespace ChocolArm64.Translation
{
class LocalAlloc
{
private class PathIo
{
private Dictionary<ILBlock, long> _allInputs;
private Dictionary<ILBlock, long> _cmnOutputs;
private long _allOutputs;
public PathIo()
{
_allInputs = new Dictionary<ILBlock, long>();
_cmnOutputs = new Dictionary<ILBlock, long>();
}
public PathIo(ILBlock root, long inputs, long outputs) : this()
{
Set(root, inputs, outputs);
}
public void Set(ILBlock 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(ILBlock root)
{
if (_allInputs.TryGetValue(root, out long inputs))
{
return inputs | (_allOutputs & ~_cmnOutputs[root]);
}
return 0;
}
public long GetOutputs()
{
return _allOutputs;
}
}
private Dictionary<ILBlock, PathIo> _intPaths;
private Dictionary<ILBlock, PathIo> _vecPaths;
private struct BlockIo
{
public ILBlock Block;
public ILBlock Entry;
public long IntInputs;
public long VecInputs;
public long IntOutputs;
public long VecOutputs;
}
private const int MaxOptGraphLength = 40;
public LocalAlloc(ILBlock[] graph, ILBlock root)
{
_intPaths = new Dictionary<ILBlock, PathIo>();
_vecPaths = new Dictionary<ILBlock, PathIo>();
if (graph.Length > 1 &&
graph.Length < MaxOptGraphLength)
{
InitializeOptimal(graph, root);
}
else
{
InitializeFast(graph);
}
}
private void InitializeOptimal(ILBlock[] graph, ILBlock 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(ILBlock block, bool retTarget)
{
BlockIo blkIO = new BlockIo() { Block = block };
if (retTarget)
{
blkIO.Entry = block;
}
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(ILBlock[] graph)
{
//This is WAY faster than InitializeOptimal, but results in
//uneeded loads and stores, so the resulting code will be slower.
long intInputs = 0, intOutputs = 0;
long vecInputs = 0, vecOutputs = 0;
foreach (ILBlock 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.
if (graph.Length > 1)
{
intInputs |= intOutputs;
vecInputs |= vecOutputs;
}
foreach (ILBlock block in graph)
{
_intPaths.Add(block, new PathIo(block, intInputs, intOutputs));
_vecPaths.Add(block, new PathIo(block, vecInputs, vecOutputs));
}
}
public long GetIntInputs(ILBlock root) => GetInputsImpl(root, _intPaths.Values);
public long GetVecInputs(ILBlock root) => GetInputsImpl(root, _vecPaths.Values);
private long GetInputsImpl(ILBlock root, IEnumerable<PathIo> values)
{
long inputs = 0;
foreach (PathIo path in values)
{
inputs |= path.GetInputs(root);
}
return inputs;
}
public long GetIntOutputs(ILBlock block) => _intPaths[block].GetOutputs();
public long GetVecOutputs(ILBlock block) => _vecPaths[block].GetOutputs();
}
}