diff options
| author | Alex Barney <thealexbarney@gmail.com> | 2018-10-30 19:43:02 -0600 |
|---|---|---|
| committer | gdkchan <gab.dark.100@gmail.com> | 2018-10-30 22:43:02 -0300 |
| commit | 9cb57fb4bb3bbae0ae052a5af4a96a49fc5d864d (patch) | |
| tree | 0c97425aeb311c142bc92a6fcc503cb2c07d4376 /ChocolArm64/Translation | |
| parent | 5a87e58183578f5b84ca8d01cbb76aed11820f78 (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')
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 |
