aboutsummaryrefslogtreecommitdiff
path: root/ChocolArm64/Translation
diff options
context:
space:
mode:
authorAlex Barney <thealexbarney@gmail.com>2018-10-30 19:43:02 -0600
committergdkchan <gab.dark.100@gmail.com>2018-10-30 22:43:02 -0300
commit9cb57fb4bb3bbae0ae052a5af4a96a49fc5d864d (patch)
tree0c97425aeb311c142bc92a6fcc503cb2c07d4376 /ChocolArm64/Translation
parent5a87e58183578f5b84ca8d01cbb76aed11820f78 (diff)
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
Diffstat (limited to 'ChocolArm64/Translation')
-rw-r--r--ChocolArm64/Translation/AILBarrier.cs7
-rw-r--r--ChocolArm64/Translation/AILBlock.cs76
-rw-r--r--ChocolArm64/Translation/AILEmitter.cs188
-rw-r--r--ChocolArm64/Translation/AILEmitterCtx.cs554
-rw-r--r--ChocolArm64/Translation/AILLabel.cs28
-rw-r--r--ChocolArm64/Translation/AILOpCode.cs19
-rw-r--r--ChocolArm64/Translation/AILOpCodeBranch.cs21
-rw-r--r--ChocolArm64/Translation/AILOpCodeCall.cs20
-rw-r--r--ChocolArm64/Translation/AILOpCodeConst.cs65
-rw-r--r--ChocolArm64/Translation/AILOpCodeLoad.cs75
-rw-r--r--ChocolArm64/Translation/AILOpCodeLog.cs17
-rw-r--r--ChocolArm64/Translation/AILOpCodeStore.cs75
-rw-r--r--ChocolArm64/Translation/ALocalAlloc.cs229
-rw-r--r--ChocolArm64/Translation/IAILEmit.cs7
-rw-r--r--ChocolArm64/Translation/IILEmit.cs7
-rw-r--r--ChocolArm64/Translation/ILBarrier.cs7
-rw-r--r--ChocolArm64/Translation/ILBlock.cs76
-rw-r--r--ChocolArm64/Translation/ILEmitter.cs188
-rw-r--r--ChocolArm64/Translation/ILEmitterCtx.cs554
-rw-r--r--ChocolArm64/Translation/ILGeneratorEx.cs110
-rw-r--r--ChocolArm64/Translation/ILLabel.cs28
-rw-r--r--ChocolArm64/Translation/ILOpCode.cs19
-rw-r--r--ChocolArm64/Translation/ILOpCodeBranch.cs21
-rw-r--r--ChocolArm64/Translation/ILOpCodeCall.cs20
-rw-r--r--ChocolArm64/Translation/ILOpCodeConst.cs65
-rw-r--r--ChocolArm64/Translation/ILOpCodeLoad.cs75
-rw-r--r--ChocolArm64/Translation/ILOpCodeLog.cs17
-rw-r--r--ChocolArm64/Translation/ILOpCodeStore.cs75
-rw-r--r--ChocolArm64/Translation/IoType.cs (renamed from ChocolArm64/Translation/AIoType.cs)2
-rw-r--r--ChocolArm64/Translation/LocalAlloc.cs229
30 files changed, 1437 insertions, 1437 deletions
diff --git a/ChocolArm64/Translation/AILBarrier.cs b/ChocolArm64/Translation/AILBarrier.cs
deleted file mode 100644
index 25b08de3..00000000
--- a/ChocolArm64/Translation/AILBarrier.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace ChocolArm64.Translation
-{
- struct AILBarrier : IAILEmit
- {
- public void Emit(AILEmitter Context) { }
- }
-} \ No newline at end of file
diff --git a/ChocolArm64/Translation/AILBlock.cs b/ChocolArm64/Translation/AILBlock.cs
deleted file mode 100644
index e580e09c..00000000
--- a/ChocolArm64/Translation/AILBlock.cs
+++ /dev/null
@@ -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);
- }
- }
- }
-} \ No newline at end of file
diff --git a/ChocolArm64/Translation/AILEmitter.cs b/ChocolArm64/Translation/AILEmitter.cs
deleted file mode 100644
index 8c780535..00000000
--- a/ChocolArm64/Translation/AILEmitter.cs
+++ /dev/null
@@ -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;
- }
- }
-} \ No newline at end of file
diff --git a/ChocolArm64/Translation/AILEmitterCtx.cs b/ChocolArm64/Translation/AILEmitterCtx.cs
deleted file mode 100644
index e5288bc8..00000000
--- a/ChocolArm64/Translation/AILEmitterCtx.cs
+++ /dev/null
@@ -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);
- }
- }
-}
diff --git a/ChocolArm64/Translation/AILLabel.cs b/ChocolArm64/Translation/AILLabel.cs
deleted file mode 100644
index 0ee39ad7..00000000
--- a/ChocolArm64/Translation/AILLabel.cs
+++ /dev/null
@@ -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;
- }
- }
-} \ No newline at end of file
diff --git a/ChocolArm64/Translation/AILOpCode.cs b/ChocolArm64/Translation/AILOpCode.cs
deleted file mode 100644
index a4bc93a0..00000000
--- a/ChocolArm64/Translation/AILOpCode.cs
+++ /dev/null
@@ -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);
- }
- }
-} \ No newline at end of file
diff --git a/ChocolArm64/Translation/AILOpCodeBranch.cs b/ChocolArm64/Translation/AILOpCodeBranch.cs
deleted file mode 100644
index e4caad1f..00000000
--- a/ChocolArm64/Translation/AILOpCodeBranch.cs
+++ /dev/null
@@ -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));
- }
- }
-} \ No newline at end of file
diff --git a/ChocolArm64/Translation/AILOpCodeCall.cs b/ChocolArm64/Translation/AILOpCodeCall.cs
deleted file mode 100644
index 8cd944eb..00000000
--- a/ChocolArm64/Translation/AILOpCodeCall.cs
+++ /dev/null
@@ -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);
- }
- }
-} \ No newline at end of file
diff --git a/ChocolArm64/Translation/AILOpCodeConst.cs b/ChocolArm64/Translation/AILOpCodeConst.cs
deleted file mode 100644
index fee86407..00000000
--- a/ChocolArm64/Translation/AILOpCodeConst.cs
+++ /dev/null
@@ -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;
- }
- }
- }
-} \ No newline at end of file
diff --git a/ChocolArm64/Translation/AILOpCodeLoad.cs b/ChocolArm64/Translation/AILOpCodeLoad.cs
deleted file mode 100644
index d60ce539..00000000
--- a/ChocolArm64/Translation/AILOpCodeLoad.cs
+++ /dev/null
@@ -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);
- }
- }
- }
-} \ No newline at end of file
diff --git a/ChocolArm64/Translation/AILOpCodeLog.cs b/ChocolArm64/Translation/AILOpCodeLog.cs
deleted file mode 100644
index 1338ca1f..00000000
--- a/ChocolArm64/Translation/AILOpCodeLog.cs
+++ /dev/null
@@ -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);
- }
- }
-} \ No newline at end of file
diff --git a/ChocolArm64/Translation/AILOpCodeStore.cs b/ChocolArm64/Translation/AILOpCodeStore.cs
deleted file mode 100644
index a0feb437..00000000
--- a/ChocolArm64/Translation/AILOpCodeStore.cs
+++ /dev/null
@@ -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));
- }
- }
-} \ No newline at end of file
diff --git a/ChocolArm64/Translation/ALocalAlloc.cs b/ChocolArm64/Translation/ALocalAlloc.cs
deleted file mode 100644
index 8e904780..00000000
--- a/ChocolArm64/Translation/ALocalAlloc.cs
+++ /dev/null
@@ -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();
- }
-} \ No newline at end of file
diff --git a/ChocolArm64/Translation/IAILEmit.cs b/ChocolArm64/Translation/IAILEmit.cs
deleted file mode 100644
index 6e4e9a78..00000000
--- a/ChocolArm64/Translation/IAILEmit.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace ChocolArm64.Translation
-{
- interface IAILEmit
- {
- void Emit(AILEmitter Context);
- }
-} \ No newline at end of file
diff --git a/ChocolArm64/Translation/IILEmit.cs b/ChocolArm64/Translation/IILEmit.cs
new file mode 100644
index 00000000..3c3925ee
--- /dev/null
+++ b/ChocolArm64/Translation/IILEmit.cs
@@ -0,0 +1,7 @@
+namespace ChocolArm64.Translation
+{
+ interface IILEmit
+ {
+ void Emit(ILEmitter context);
+ }
+} \ No newline at end of file
diff --git a/ChocolArm64/Translation/ILBarrier.cs b/ChocolArm64/Translation/ILBarrier.cs
new file mode 100644
index 00000000..f931e992
--- /dev/null
+++ b/ChocolArm64/Translation/ILBarrier.cs
@@ -0,0 +1,7 @@
+namespace ChocolArm64.Translation
+{
+ struct ILBarrier : IILEmit
+ {
+ public void Emit(ILEmitter context) { }
+ }
+} \ No newline at end of file
diff --git a/ChocolArm64/Translation/ILBlock.cs b/ChocolArm64/Translation/ILBlock.cs
new file mode 100644
index 00000000..d51e8d9e
--- /dev/null
+++ b/ChocolArm64/Translation/ILBlock.cs
@@ -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);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/ChocolArm64/Translation/ILEmitter.cs b/ChocolArm64/Translation/ILEmitter.cs
new file mode 100644
index 00000000..543528d7
--- /dev/null
+++ b/ChocolArm64/Translation/ILEmitter.cs
@@ -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;
+ }
+ }
+} \ No newline at end of file
diff --git a/ChocolArm64/Translation/ILEmitterCtx.cs b/ChocolArm64/Translation/ILEmitterCtx.cs
new file mode 100644
index 00000000..c1d1be36
--- /dev/null
+++ b/ChocolArm64/Translation/ILEmitterCtx.cs
@@ -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);
+ }
+ }
+}
diff --git a/ChocolArm64/Translation/ILGeneratorEx.cs b/ChocolArm64/Translation/ILGeneratorEx.cs
index 40c6efa4..318098cc 100644
--- a/ChocolArm64/Translation/ILGeneratorEx.cs
+++ b/ChocolArm64/Translation/ILGeneratorEx.cs
@@ -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);
}
}
}
diff --git a/ChocolArm64/Translation/ILLabel.cs b/ChocolArm64/Translation/ILLabel.cs
new file mode 100644
index 00000000..4f96edcc
--- /dev/null
+++ b/ChocolArm64/Translation/ILLabel.cs
@@ -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;
+ }
+ }
+} \ No newline at end of file
diff --git a/ChocolArm64/Translation/ILOpCode.cs b/ChocolArm64/Translation/ILOpCode.cs
new file mode 100644
index 00000000..eb91639e
--- /dev/null
+++ b/ChocolArm64/Translation/ILOpCode.cs
@@ -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);
+ }
+ }
+} \ No newline at end of file
diff --git a/ChocolArm64/Translation/ILOpCodeBranch.cs b/ChocolArm64/Translation/ILOpCodeBranch.cs
new file mode 100644
index 00000000..b0ba2331
--- /dev/null
+++ b/ChocolArm64/Translation/ILOpCodeBranch.cs
@@ -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));
+ }
+ }
+} \ No newline at end of file
diff --git a/ChocolArm64/Translation/ILOpCodeCall.cs b/ChocolArm64/Translation/ILOpCodeCall.cs
new file mode 100644
index 00000000..0b591d46
--- /dev/null
+++ b/ChocolArm64/Translation/ILOpCodeCall.cs
@@ -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);
+ }
+ }
+} \ No newline at end of file
diff --git a/ChocolArm64/Translation/ILOpCodeConst.cs b/ChocolArm64/Translation/ILOpCodeConst.cs
new file mode 100644
index 00000000..5497eba1
--- /dev/null
+++ b/ChocolArm64/Translation/ILOpCodeConst.cs
@@ -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;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/ChocolArm64/Translation/ILOpCodeLoad.cs b/ChocolArm64/Translation/ILOpCodeLoad.cs
new file mode 100644
index 00000000..9dae10cc
--- /dev/null
+++ b/ChocolArm64/Translation/ILOpCodeLoad.cs
@@ -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);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/ChocolArm64/Translation/ILOpCodeLog.cs b/ChocolArm64/Translation/ILOpCodeLog.cs
new file mode 100644
index 00000000..2c77021b
--- /dev/null
+++ b/ChocolArm64/Translation/ILOpCodeLog.cs
@@ -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);
+ }
+ }
+} \ No newline at end of file
diff --git a/ChocolArm64/Translation/ILOpCodeStore.cs b/ChocolArm64/Translation/ILOpCodeStore.cs
new file mode 100644
index 00000000..41326fe1
--- /dev/null
+++ b/ChocolArm64/Translation/ILOpCodeStore.cs
@@ -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));
+ }
+ }
+} \ No newline at end of file
diff --git a/ChocolArm64/Translation/AIoType.cs b/ChocolArm64/Translation/IoType.cs
index 94f89081..290b159f 100644
--- a/ChocolArm64/Translation/AIoType.cs
+++ b/ChocolArm64/Translation/IoType.cs
@@ -3,7 +3,7 @@ using System;
namespace ChocolArm64.Translation
{
[Flags]
- enum AIoType
+ enum IoType
{
Arg,
Fields,
diff --git a/ChocolArm64/Translation/LocalAlloc.cs b/ChocolArm64/Translation/LocalAlloc.cs
new file mode 100644
index 00000000..8237bd54
--- /dev/null
+++ b/ChocolArm64/Translation/LocalAlloc.cs
@@ -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();
+ }
+} \ No newline at end of file