From de406efb7558fea3859c5448a067b769ed25d5b0 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Fri, 30 Nov 2018 21:34:44 -0300 Subject: [PATCH] Changes to ILEmitter, separate it from ILEmitterCtx --- ChocolArm64/Decoders/Decoder.cs | 40 +--- ChocolArm64/Instructions/InstEmitException.cs | 4 +- ChocolArm64/Instructions/InstEmitFlow.cs | 2 +- ChocolArm64/TranslatedSub.cs | 20 +- ChocolArm64/Translation/ILBlock.cs | 38 ++-- ChocolArm64/Translation/ILEmitter.cs | 104 +++------ ChocolArm64/Translation/ILEmitterCtx.cs | 211 ++++++++++++------ ChocolArm64/Translation/ILOpCodeLoad.cs | 33 +-- ChocolArm64/Translation/ILOpCodeLoadState.cs | 42 ++++ ChocolArm64/Translation/ILOpCodeStore.cs | 33 +-- ChocolArm64/Translation/ILOpCodeStoreState.cs | 42 ++++ ChocolArm64/Translation/IoType.cs | 5 - ChocolArm64/Translation/LocalAlloc.cs | 55 +++-- ChocolArm64/Translator.cs | 49 ++-- ChocolArm64/TranslatorCache.cs | 4 +- 15 files changed, 354 insertions(+), 328 deletions(-) create mode 100644 ChocolArm64/Translation/ILOpCodeLoadState.cs create mode 100644 ChocolArm64/Translation/ILOpCodeStoreState.cs diff --git a/ChocolArm64/Decoders/Decoder.cs b/ChocolArm64/Decoders/Decoder.cs index db43ac4fe8..1d4f397ac0 100644 --- a/ChocolArm64/Decoders/Decoder.cs +++ b/ChocolArm64/Decoders/Decoder.cs @@ -28,11 +28,11 @@ namespace ChocolArm64.Decoders return block; } - public static (Block[] Graph, Block Root) DecodeSubroutine( - TranslatorCache cache, - CpuThreadState state, - MemoryManager memory, - long start) + public static Block DecodeSubroutine( + TranslatorCache cache, + CpuThreadState state, + MemoryManager memory, + long start) { Dictionary visited = new Dictionary(); Dictionary visitedEnd = new Dictionary(); @@ -53,7 +53,7 @@ namespace ChocolArm64.Decoders return output; } - Block root = Enqueue(start); + Block entry = Enqueue(start); while (blocks.Count > 0) { @@ -118,33 +118,7 @@ namespace ChocolArm64.Decoders visitedEnd.Add(current.EndPosition, current); } - //Make and sort Graph blocks array by position. - Block[] graph = new Block[visited.Count]; - - while (visited.Count > 0) - { - ulong firstPos = ulong.MaxValue; - - foreach (Block block in visited.Values) - { - if (firstPos > (ulong)block.Position) - firstPos = (ulong)block.Position; - } - - Block current = visited[(long)firstPos]; - - do - { - graph[graph.Length - visited.Count] = current; - - visited.Remove(current.Position); - - current = current.Next; - } - while (current != null); - } - - return (graph, root); + return entry; } private static void FillBlock(CpuThreadState state, MemoryManager memory, Block block) diff --git a/ChocolArm64/Instructions/InstEmitException.cs b/ChocolArm64/Instructions/InstEmitException.cs index 8325a3978f..9444397ae6 100644 --- a/ChocolArm64/Instructions/InstEmitException.cs +++ b/ChocolArm64/Instructions/InstEmitException.cs @@ -48,7 +48,7 @@ namespace ChocolArm64.Instructions if (context.CurrBlock.Next != null) { - context.EmitLoadState(context.CurrBlock.Next); + context.EmitLoadState(); } else { @@ -73,7 +73,7 @@ namespace ChocolArm64.Instructions if (context.CurrBlock.Next != null) { - context.EmitLoadState(context.CurrBlock.Next); + context.EmitLoadState(); } else { diff --git a/ChocolArm64/Instructions/InstEmitFlow.cs b/ChocolArm64/Instructions/InstEmitFlow.cs index 0e9f7cb017..354ba86a67 100644 --- a/ChocolArm64/Instructions/InstEmitFlow.cs +++ b/ChocolArm64/Instructions/InstEmitFlow.cs @@ -58,7 +58,7 @@ namespace ChocolArm64.Instructions context.Emit(OpCodes.Pop); - context.EmitLoadState(context.CurrBlock.Next); + context.EmitLoadState(); } else { diff --git a/ChocolArm64/TranslatedSub.cs b/ChocolArm64/TranslatedSub.cs index 8b3ec3f013..653abcca90 100644 --- a/ChocolArm64/TranslatedSub.cs +++ b/ChocolArm64/TranslatedSub.cs @@ -24,7 +24,7 @@ namespace ChocolArm64 public DynamicMethod Method { get; private set; } - public ReadOnlyCollection Params { get; private set; } + public ReadOnlyCollection SubArgs { get; private set; } private HashSet _callers; @@ -34,20 +34,10 @@ namespace ChocolArm64 private bool _needsReJit; - public TranslatedSub(DynamicMethod method, List Params) + public TranslatedSub(DynamicMethod method, List subArgs) { - if (method == null) - { - throw new ArgumentNullException(nameof(method)); - } - - if (Params == null) - { - throw new ArgumentNullException(nameof(Params)); - } - - Method = method; - this.Params = Params.AsReadOnly(); + Method = method ?? throw new ArgumentNullException(nameof(method));; + SubArgs = subArgs?.AsReadOnly() ?? throw new ArgumentNullException(nameof(subArgs)); _callers = new HashSet(); @@ -89,7 +79,7 @@ namespace ChocolArm64 generator.EmitLdargSeq(FixedArgTypes.Length); - foreach (Register reg in Params) + foreach (Register reg in SubArgs) { generator.EmitLdarg(StateArgIdx); diff --git a/ChocolArm64/Translation/ILBlock.cs b/ChocolArm64/Translation/ILBlock.cs index d51e8d9ec3..bd5d5656d7 100644 --- a/ChocolArm64/Translation/ILBlock.cs +++ b/ChocolArm64/Translation/ILBlock.cs @@ -14,19 +14,21 @@ namespace ChocolArm64.Translation public bool HasStateStore { get; private set; } - public List IlEmitters { get; private set; } + private List _emitters; + + public int Count => _emitters.Count; public ILBlock Next { get; set; } public ILBlock Branch { get; set; } public ILBlock() { - IlEmitters = new List(); + _emitters = new List(); } - public void Add(IILEmit ilEmitter) + public void Add(IILEmit emitter) { - if (ilEmitter is ILBarrier) + if (emitter is ILBarrier) { //Those barriers are used to separate the groups of CIL //opcodes emitted by each ARM instruction. @@ -35,7 +37,7 @@ namespace ChocolArm64.Translation IntAwOutputs = IntOutputs; VecAwOutputs = VecOutputs; } - else if (ilEmitter is IlOpCodeLoad ld && ILEmitter.IsRegIndex(ld.Index)) + else if (emitter is ILOpCodeLoad ld && ILEmitter.IsRegIndex(ld.Index)) { switch (ld.IoType) { @@ -44,30 +46,26 @@ namespace ChocolArm64.Translation case IoType.Vector: VecInputs |= (1L << ld.Index) & ~VecAwOutputs; break; } } - else if (ilEmitter is IlOpCodeStore st) + else if (emitter is ILOpCodeStore st && ILEmitter.IsRegIndex(st.Index)) { - if (ILEmitter.IsRegIndex(st.Index)) + switch (st.IoType) { - 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; + 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; } } + else if (emitter is ILOpCodeStoreState) + { + HasStateStore = true; + } - IlEmitters.Add(ilEmitter); + _emitters.Add(emitter); } public void Emit(ILEmitter context) { - foreach (IILEmit ilEmitter in IlEmitters) + foreach (IILEmit ilEmitter in _emitters) { ilEmitter.Emit(context); } diff --git a/ChocolArm64/Translation/ILEmitter.cs b/ChocolArm64/Translation/ILEmitter.cs index 543528d716..da961842fc 100644 --- a/ChocolArm64/Translation/ILEmitter.cs +++ b/ChocolArm64/Translation/ILEmitter.cs @@ -1,4 +1,3 @@ -using ChocolArm64.Decoders; using ChocolArm64.State; using System; using System.Collections.Generic; @@ -17,70 +16,23 @@ namespace ChocolArm64.Translation private ILBlock[] _ilBlocks; - private ILBlock _root; - - private TranslatedSub _subroutine; - private string _subName; private int _localsCount; - public ILEmitter(Block[] graph, Block root, string subName) + public ILEmitter(ILBlock[] ilBlocks, string subName) { - _subName = subName; - - _locals = new Dictionary(); - - _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)]; + _ilBlocks = ilBlocks; + _subName = subName; } - public ILBlock GetIlBlock(int index) => _ilBlocks[index]; - public TranslatedSub GetSubroutine() { - LocalAlloc = new LocalAlloc(_ilBlocks, _root); + LocalAlloc = new LocalAlloc(_ilBlocks, _ilBlocks[0]); - InitSubroutine(); - InitLocals(); + List subArgs = new List(); - foreach (ILBlock ilBlock in _ilBlocks) - { - ilBlock.Emit(this); - } - - return _subroutine; - } - - private void InitSubroutine() - { - List Params = new List(); - - void SetParams(long inputs, RegisterType baseType) + void SetArgs(long inputs, RegisterType baseType) { for (int bit = 0; bit < 64; bit++) { @@ -88,37 +40,43 @@ namespace ChocolArm64.Translation if ((inputs & mask) != 0) { - Params.Add(GetRegFromBit(bit, baseType)); + subArgs.Add(GetRegFromBit(bit, baseType)); } } } - SetParams(LocalAlloc.GetIntInputs(_root), RegisterType.Int); - SetParams(LocalAlloc.GetVecInputs(_root), RegisterType.Vector); + SetArgs(LocalAlloc.GetIntInputs(_ilBlocks[0]), RegisterType.Int); + SetArgs(LocalAlloc.GetVecInputs(_ilBlocks[0]), RegisterType.Vector); - DynamicMethod mthd = new DynamicMethod(_subName, typeof(long), GetParamTypes(Params)); + DynamicMethod method = new DynamicMethod(_subName, typeof(long), GetArgumentTypes(subArgs)); - Generator = mthd.GetILGenerator(); + Generator = method.GetILGenerator(); - _subroutine = new TranslatedSub(mthd, Params); - } + TranslatedSub subroutine = new TranslatedSub(method, subArgs); - private void InitLocals() - { - int paramsStart = TranslatedSub.FixedArgTypes.Length; + int argsStart = TranslatedSub.FixedArgTypes.Length; _locals = new Dictionary(); - for (int index = 0; index < _subroutine.Params.Count; index++) - { - Register reg = _subroutine.Params[index]; + _localsCount = 0; - Generator.EmitLdarg(index + paramsStart); + for (int index = 0; index < subroutine.SubArgs.Count; index++) + { + Register reg = subroutine.SubArgs[index]; + + Generator.EmitLdarg(index + argsStart); Generator.EmitStloc(GetLocalIndex(reg)); } + + foreach (ILBlock ilBlock in _ilBlocks) + { + ilBlock.Emit(this); + } + + return subroutine; } - private Type[] GetParamTypes(IList Params) + private Type[] GetArgumentTypes(IList Params) { Type[] fixedArgs = TranslatedSub.FixedArgTypes; @@ -140,7 +98,7 @@ namespace ChocolArm64.Translation { if (!_locals.TryGetValue(reg, out int index)) { - Generator.DeclareLocal(GetLocalType(reg)); + Generator.DeclareLocal(GetFieldType(reg.Type)); index = _localsCount++; @@ -150,9 +108,7 @@ namespace ChocolArm64.Translation return index; } - public Type GetLocalType(Register reg) => GetFieldType(reg.Type); - - public Type GetFieldType(RegisterType regType) + private static Type GetFieldType(RegisterType regType) { switch (regType) { @@ -182,7 +138,7 @@ namespace ChocolArm64.Translation public static bool IsRegIndex(int index) { - return index >= 0 && index < 32; + return (uint)index < 32; } } } \ No newline at end of file diff --git a/ChocolArm64/Translation/ILEmitterCtx.cs b/ChocolArm64/Translation/ILEmitterCtx.cs index c1d1be366e..19a5ee3d7b 100644 --- a/ChocolArm64/Translation/ILEmitterCtx.cs +++ b/ChocolArm64/Translation/ILEmitterCtx.cs @@ -14,15 +14,20 @@ namespace ChocolArm64.Translation private Dictionary _labels; - private int _blkIndex; + private long _subPosition; + private int _opcIndex; - private Block[] _graph; - private Block _root; - public Block CurrBlock => _graph[_blkIndex]; - public OpCode64 CurrOp => _graph[_blkIndex].OpCodes[_opcIndex]; + private Block _currBlock; - private ILEmitter _emitter; + public Block CurrBlock => _currBlock; + public OpCode64 CurrOp => _currBlock.OpCodes[_opcIndex]; + + private Dictionary _visitedBlocks; + + private Queue _branchTargets; + + private List _ilBlocks; private ILBlock _ilBlock; @@ -33,69 +38,119 @@ namespace ChocolArm64.Translation //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; + private const int IntTmpIndex = -1; + private const int RorTmpIndex = -2; + private const int CmpOptTmp1Index = -3; + private const int CmpOptTmp2Index = -4; + private const int VecTmp1Index = -5; + private const int VecTmp2Index = -6; - public ILEmitterCtx( - TranslatorCache cache, - Block[] graph, - Block root, - string subName) + public ILEmitterCtx(TranslatorCache cache, Block graph) { - _cache = cache ?? throw new ArgumentNullException(nameof(cache)); - _graph = graph ?? throw new ArgumentNullException(nameof(graph)); - _root = root ?? throw new ArgumentNullException(nameof(root)); + _cache = cache ?? throw new ArgumentNullException(nameof(cache)); + _currBlock = graph ?? throw new ArgumentNullException(nameof(graph)); _labels = new Dictionary(); - _emitter = new ILEmitter(graph, root, subName); + _visitedBlocks = new Dictionary(); - _ilBlock = _emitter.GetIlBlock(0); + _visitedBlocks.Add(graph, new ILBlock()); - _opcIndex = -1; + _branchTargets = new Queue(); - if (graph.Length == 0 || !AdvanceOpCode()) - { - throw new ArgumentException(nameof(graph)); - } - } + _ilBlocks = new List(); - public TranslatedSub GetSubroutine() - { - return _emitter.GetSubroutine(); + _subPosition = graph.Position; + + ResetBlockState(); + + AdvanceOpCode(); } public bool AdvanceOpCode() { - if (_opcIndex + 1 == CurrBlock.OpCodes.Count && - _blkIndex + 1 == _graph.Length) + if (_currBlock == null) { return false; } - while (++_opcIndex >= (CurrBlock?.OpCodes.Count ?? 0)) + while (++_opcIndex >= _currBlock.OpCodes.Count) { - _blkIndex++; - _opcIndex = -1; + if (!AdvanceBlock()) + { + --_opcIndex; - _optOpLastFlagSet = null; - _optOpLastCompare = null; + return false; + } - _ilBlock = _emitter.GetIlBlock(_blkIndex); + ResetBlockState(); } return true; } + private bool AdvanceBlock() + { + if (_currBlock.Branch != null) + { + if (_visitedBlocks.TryAdd(_currBlock.Branch, _ilBlock.Branch)) + { + _branchTargets.Enqueue(_currBlock.Branch); + } + } + + if (_currBlock.Next != null) + { + if (_visitedBlocks.TryAdd(_currBlock.Next, _ilBlock.Next)) + { + _currBlock = _currBlock.Next; + + return true; + } + else + { + Emit(OpCodes.Br, GetLabel(_currBlock.Next.Position)); + } + } + + return _branchTargets.TryDequeue(out _currBlock); + } + + private void ResetBlockState() + { + _ilBlock = _visitedBlocks[_currBlock]; + + _ilBlocks.Add(_ilBlock); + + _ilBlock.Next = GetOrCreateILBlock(_currBlock.Next); + _ilBlock.Branch = GetOrCreateILBlock(_currBlock.Branch); + + _opcIndex = -1; + + _optOpLastFlagSet = null; + _optOpLastCompare = null; + } + + private ILBlock GetOrCreateILBlock(Block block) + { + if (block == null) + { + return null; + } + + if (_visitedBlocks.TryGetValue(block, out ILBlock ilBlock)) + { + return ilBlock; + } + + return new ILBlock(); + } + public void EmitOpCode() { if (_opcIndex == 0) { - MarkLabel(GetLabel(CurrBlock.Position)); + MarkLabel(GetLabel(_currBlock.Position)); EmitSynchronization(); } @@ -109,7 +164,7 @@ namespace ChocolArm64.Translation { EmitLdarg(TranslatedSub.StateArgIdx); - EmitLdc_I4(CurrBlock.OpCodes.Count); + EmitLdc_I4(_currBlock.OpCodes.Count); EmitPrivateCall(typeof(CpuThreadState), nameof(CpuThreadState.Synchronize)); @@ -126,9 +181,14 @@ namespace ChocolArm64.Translation MarkLabel(lblContinue); } + public ILBlock[] GetILBlocks() + { + return _ilBlocks.ToArray(); + } + public bool TryOptEmitSubroutineCall() { - if (CurrBlock.Next == null) + if (_currBlock.Next == null) { return false; } @@ -148,7 +208,7 @@ namespace ChocolArm64.Translation EmitLdarg(index); } - foreach (Register reg in subroutine.Params) + foreach (Register reg in subroutine.SubArgs) { switch (reg.Type) { @@ -160,7 +220,7 @@ namespace ChocolArm64.Translation EmitCall(subroutine.Method); - subroutine.AddCaller(_root.Position); + subroutine.AddCaller(_subPosition); return true; } @@ -171,11 +231,11 @@ namespace ChocolArm64.Translation InstEmitAluHelper.EmitDataLoadOpers(this); - Stloc(Tmp4Index, IoType.Int); - Stloc(Tmp3Index, IoType.Int); + Stloc(CmpOptTmp2Index, IoType.Int); + Stloc(CmpOptTmp1Index, IoType.Int); } - private Dictionary _branchOps = new Dictionary() + private Dictionary _branchOps = new Dictionary() { { Cond.Eq, OpCodes.Beq }, { Cond.Ne, OpCodes.Bne_Un }, @@ -191,15 +251,15 @@ namespace ChocolArm64.Translation public void EmitCondBranch(ILLabel target, Cond cond) { - System.Reflection.Emit.OpCode ilOp; + 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); + Ldloc(CmpOptTmp1Index, IoType.Int, _optOpLastCompare.RegisterSize); + Ldloc(CmpOptTmp2Index, IoType.Int, _optOpLastCompare.RegisterSize); ilOp = _branchOps[cond]; } @@ -285,11 +345,11 @@ namespace ChocolArm64.Translation } } - 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); + 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) + private void EmitILShift(int amount, OpCode ilOp) { if (amount > 0) { @@ -303,14 +363,14 @@ namespace ChocolArm64.Translation { if (amount > 0) { - Stloc(Tmp2Index, IoType.Int); - Ldloc(Tmp2Index, IoType.Int); + Stloc(RorTmpIndex, IoType.Int); + Ldloc(RorTmpIndex, IoType.Int); EmitLdc_I4(amount); Emit(OpCodes.Shr_Un); - Ldloc(Tmp2Index, IoType.Int); + Ldloc(RorTmpIndex, IoType.Int); EmitLdc_I4(CurrOp.GetBitsCount() - amount); @@ -336,12 +396,12 @@ namespace ChocolArm64.Translation _ilBlock.Add(label); } - public void Emit(System.Reflection.Emit.OpCode ilOp) + public void Emit(OpCode ilOp) { _ilBlock.Add(new ILOpCode(ilOp)); } - public void Emit(System.Reflection.Emit.OpCode ilOp, ILLabel label) + public void Emit(OpCode ilOp, ILLabel label) { _ilBlock.Add(new ILOpCodeBranch(ilOp, label)); } @@ -353,7 +413,7 @@ namespace ChocolArm64.Translation public void EmitLdarg(int index) { - _ilBlock.Add(new IlOpCodeLoad(index, IoType.Arg)); + _ilBlock.Add(new ILOpCodeLoad(index, IoType.Arg)); } public void EmitLdintzr(int index) @@ -380,24 +440,29 @@ namespace ChocolArm64.Translation } } - public void EmitLoadState(Block retBlk) + public void EmitLoadState() { - _ilBlock.Add(new IlOpCodeLoad(Array.IndexOf(_graph, retBlk), IoType.Fields)); + if (_ilBlock.Next == null) + { + throw new InvalidOperationException("Can't load state for next block, because there's no next block."); + } + + _ilBlock.Add(new ILOpCodeLoadState(_ilBlock.Next)); } public void EmitStoreState() { - _ilBlock.Add(new IlOpCodeStore(Array.IndexOf(_graph, CurrBlock), IoType.Fields)); + _ilBlock.Add(new ILOpCodeStoreState(_ilBlock)); } - public void EmitLdtmp() => EmitLdint(Tmp1Index); - public void EmitSttmp() => EmitStint(Tmp1Index); + public void EmitLdtmp() => EmitLdint(IntTmpIndex); + public void EmitSttmp() => EmitStint(IntTmpIndex); - public void EmitLdvectmp() => EmitLdvec(Tmp5Index); - public void EmitStvectmp() => EmitStvec(Tmp5Index); + public void EmitLdvectmp() => EmitLdvec(VecTmp1Index); + public void EmitStvectmp() => EmitStvec(VecTmp1Index); - public void EmitLdvectmp2() => EmitLdvec(Tmp6Index); - public void EmitStvectmp2() => EmitStvec(Tmp6Index); + public void EmitLdvectmp2() => EmitLdvec(VecTmp2Index); + public void EmitStvectmp2() => EmitStvec(VecTmp2Index); public void EmitLdint(int index) => Ldloc(index, IoType.Int); public void EmitStint(int index) => Stloc(index, IoType.Int); @@ -415,17 +480,17 @@ namespace ChocolArm64.Translation private void Ldloc(int index, IoType ioType) { - _ilBlock.Add(new IlOpCodeLoad(index, ioType, CurrOp.RegisterSize)); + _ilBlock.Add(new ILOpCodeLoad(index, ioType, CurrOp.RegisterSize)); } private void Ldloc(int index, IoType ioType, RegisterSize registerSize) { - _ilBlock.Add(new IlOpCodeLoad(index, ioType, registerSize)); + _ilBlock.Add(new ILOpCodeLoad(index, ioType, registerSize)); } private void Stloc(int index, IoType ioType) { - _ilBlock.Add(new IlOpCodeStore(index, ioType, CurrOp.RegisterSize)); + _ilBlock.Add(new ILOpCodeStore(index, ioType, CurrOp.RegisterSize)); } public void EmitCallPropGet(Type objType, string propName) @@ -536,7 +601,7 @@ namespace ChocolArm64.Translation EmitZnCheck(OpCodes.Clt, (int)PState.NBit); } - private void EmitZnCheck(System.Reflection.Emit.OpCode ilCmpOp, int flag) + private void EmitZnCheck(OpCode ilCmpOp, int flag) { Emit(OpCodes.Dup); Emit(OpCodes.Ldc_I4_0); diff --git a/ChocolArm64/Translation/ILOpCodeLoad.cs b/ChocolArm64/Translation/ILOpCodeLoad.cs index 9dae10cc4b..8d74090a2f 100644 --- a/ChocolArm64/Translation/ILOpCodeLoad.cs +++ b/ChocolArm64/Translation/ILOpCodeLoad.cs @@ -3,7 +3,7 @@ using System.Reflection.Emit; namespace ChocolArm64.Translation { - struct IlOpCodeLoad : IILEmit + struct ILOpCodeLoad : IILEmit { public int Index { get; private set; } @@ -11,7 +11,7 @@ namespace ChocolArm64.Translation public RegisterSize RegisterSize { get; private set; } - public IlOpCodeLoad(int index, IoType ioType, RegisterSize registerSize = 0) + public ILOpCodeLoad(int index, IoType ioType, RegisterSize registerSize = 0) { Index = index; IoType = ioType; @@ -24,41 +24,12 @@ namespace ChocolArm64.Translation { 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); diff --git a/ChocolArm64/Translation/ILOpCodeLoadState.cs b/ChocolArm64/Translation/ILOpCodeLoadState.cs new file mode 100644 index 0000000000..f813bb14e6 --- /dev/null +++ b/ChocolArm64/Translation/ILOpCodeLoadState.cs @@ -0,0 +1,42 @@ +using ChocolArm64.State; +using System.Reflection.Emit; + +namespace ChocolArm64.Translation +{ + struct ILOpCodeLoadState : IILEmit + { + private ILBlock _block; + + public ILOpCodeLoadState(ILBlock block) + { + _block = block; + } + + public void Emit(ILEmitter context) + { + long intInputs = context.LocalAlloc.GetIntInputs(_block); + long vecInputs = context.LocalAlloc.GetVecInputs(_block); + + LoadLocals(context, intInputs, RegisterType.Int); + LoadLocals(context, vecInputs, RegisterType.Vector); + } + + 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)); + } + } + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Translation/ILOpCodeStore.cs b/ChocolArm64/Translation/ILOpCodeStore.cs index 41326fe127..eff8868b68 100644 --- a/ChocolArm64/Translation/ILOpCodeStore.cs +++ b/ChocolArm64/Translation/ILOpCodeStore.cs @@ -3,7 +3,7 @@ using System.Reflection.Emit; namespace ChocolArm64.Translation { - struct IlOpCodeStore : IILEmit + struct ILOpCodeStore : IILEmit { public int Index { get; private set; } @@ -11,7 +11,7 @@ namespace ChocolArm64.Translation public RegisterSize RegisterSize { get; private set; } - public IlOpCodeStore(int index, IoType ioType, RegisterSize registerSize = 0) + public ILOpCodeStore(int index, IoType ioType, RegisterSize registerSize = 0) { Index = index; IoType = ioType; @@ -24,41 +24,12 @@ namespace ChocolArm64.Translation { 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); diff --git a/ChocolArm64/Translation/ILOpCodeStoreState.cs b/ChocolArm64/Translation/ILOpCodeStoreState.cs new file mode 100644 index 0000000000..74bd994169 --- /dev/null +++ b/ChocolArm64/Translation/ILOpCodeStoreState.cs @@ -0,0 +1,42 @@ +using ChocolArm64.State; +using System.Reflection.Emit; + +namespace ChocolArm64.Translation +{ + struct ILOpCodeStoreState : IILEmit + { + private ILBlock _block; + + public ILOpCodeStoreState(ILBlock block) + { + _block = block; + } + + public void Emit(ILEmitter context) + { + long intOutputs = context.LocalAlloc.GetIntOutputs(_block); + long vecOutputs = context.LocalAlloc.GetVecOutputs(_block); + + StoreLocals(context, intOutputs, RegisterType.Int); + StoreLocals(context, vecOutputs, RegisterType.Vector); + } + + 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()); + } + } + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Translation/IoType.cs b/ChocolArm64/Translation/IoType.cs index 290b159fc5..c7710e0c67 100644 --- a/ChocolArm64/Translation/IoType.cs +++ b/ChocolArm64/Translation/IoType.cs @@ -1,15 +1,10 @@ -using System; - namespace ChocolArm64.Translation { - [Flags] enum IoType { Arg, - Fields, Flag, Int, - Float, Vector } } \ No newline at end of file diff --git a/ChocolArm64/Translation/LocalAlloc.cs b/ChocolArm64/Translation/LocalAlloc.cs index 8237bd5454..1ce0230ee3 100644 --- a/ChocolArm64/Translation/LocalAlloc.cs +++ b/ChocolArm64/Translation/LocalAlloc.cs @@ -65,11 +65,36 @@ namespace ChocolArm64.Translation public long VecInputs; public long IntOutputs; public long VecOutputs; + + public override bool Equals(object obj) + { + if (!(obj is BlockIo other)) + { + return false; + } + + return other.Block == Block && + other.Entry == Entry && + other.IntInputs == IntInputs && + other.VecInputs == VecInputs && + other.IntOutputs == IntOutputs && + other.VecOutputs == VecOutputs; + } + + public override int GetHashCode() + { + return Block.GetHashCode() * 23 ^ + Entry.GetHashCode() * 23 ^ + IntInputs.GetHashCode() * 23 ^ + VecInputs.GetHashCode() * 23 ^ + IntOutputs.GetHashCode() * 23 ^ + VecOutputs.GetHashCode(); + } } private const int MaxOptGraphLength = 40; - public LocalAlloc(ILBlock[] graph, ILBlock root) + public LocalAlloc(ILBlock[] graph, ILBlock entry) { _intPaths = new Dictionary(); _vecPaths = new Dictionary(); @@ -77,7 +102,7 @@ namespace ChocolArm64.Translation if (graph.Length > 1 && graph.Length < MaxOptGraphLength) { - InitializeOptimal(graph, root); + InitializeOptimal(graph, entry); } else { @@ -85,7 +110,7 @@ namespace ChocolArm64.Translation } } - private void InitializeOptimal(ILBlock[] graph, ILBlock root) + private void InitializeOptimal(ILBlock[] graph, ILBlock entry) { //This will go through all possible paths on the graph, //and store all inputs/outputs for each block. A register @@ -93,7 +118,7 @@ namespace ChocolArm64.Translation //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 + //when doing input elimination. Each block chain have a entry, 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 visited = new HashSet(); @@ -112,8 +137,8 @@ namespace ChocolArm64.Translation Enqueue(new BlockIo() { - Block = root, - Entry = root + Block = entry, + Entry = entry }); while (unvisited.Count > 0) @@ -146,22 +171,22 @@ namespace ChocolArm64.Translation void EnqueueFromCurrent(ILBlock block, bool retTarget) { - BlockIo blkIO = new BlockIo() { Block = block }; + BlockIo blockIo = new BlockIo() { Block = block }; if (retTarget) { - blkIO.Entry = block; + blockIo.Entry = block; } else { - blkIO.Entry = current.Entry; - blkIO.IntInputs = current.IntInputs; - blkIO.VecInputs = current.VecInputs; - blkIO.IntOutputs = current.IntOutputs; - blkIO.VecOutputs = current.VecOutputs; + blockIo.Entry = current.Entry; + blockIo.IntInputs = current.IntInputs; + blockIo.VecInputs = current.VecInputs; + blockIo.IntOutputs = current.IntOutputs; + blockIo.VecOutputs = current.VecOutputs; } - Enqueue(blkIO); + Enqueue(blockIo); } if (current.Block.Next != null) @@ -179,7 +204,7 @@ namespace ChocolArm64.Translation 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. + //unneeded loads and stores, so the resulting code will be slower. long intInputs = 0, intOutputs = 0; long vecInputs = 0, vecOutputs = 0; diff --git a/ChocolArm64/Translator.cs b/ChocolArm64/Translator.cs index 3bf06dc469..133c45b482 100644 --- a/ChocolArm64/Translator.cs +++ b/ChocolArm64/Translator.cs @@ -83,11 +83,7 @@ namespace ChocolArm64 { Block block = Decoder.DecodeBasicBlock(state, memory, position); - Block[] graph = new Block[] { block }; - - string subName = GetSubroutineName(position); - - ILEmitterCtx context = new ILEmitterCtx(_cache, graph, block, subName); + ILEmitterCtx context = new ILEmitterCtx(_cache, block); do { @@ -95,24 +91,24 @@ namespace ChocolArm64 } while (context.AdvanceOpCode()); - TranslatedSub subroutine = context.GetSubroutine(); + string subName = GetSubroutineName(position); + + ILEmitter ilMthdBuilder = new ILEmitter(context.GetILBlocks(), subName); + + TranslatedSub subroutine = ilMthdBuilder.GetSubroutine(); subroutine.SetType(TranslatedSubType.SubTier0); _cache.AddOrUpdate(position, subroutine, block.OpCodes.Count); - OpCode64 lastOp = block.GetLastOp(); - return subroutine; } private void TranslateTier1(CpuThreadState state, MemoryManager memory, long position) { - (Block[] graph, Block root) = Decoder.DecodeSubroutine(_cache, state, memory, position); + Block graph = Decoder.DecodeSubroutine(_cache, state, memory, position); - string subName = GetSubroutineName(position); - - ILEmitterCtx context = new ILEmitterCtx(_cache, graph, root, subName); + ILEmitterCtx context = new ILEmitterCtx(_cache, graph); if (context.CurrBlock.Position != position) { @@ -138,28 +134,29 @@ namespace ChocolArm64 } } - TranslatedSub subroutine = context.GetSubroutine(); + string subName = GetSubroutineName(position); + + ILBlock[] ilBlocks = context.GetILBlocks(); + + ILEmitter ilMthdBuilder = new ILEmitter(ilBlocks, subName); + + TranslatedSub subroutine = ilMthdBuilder.GetSubroutine(); subroutine.SetType(TranslatedSubType.SubTier1); - _cache.AddOrUpdate(position, subroutine, GetGraphInstCount(graph)); + int ilOpCount = 0; + + foreach (ILBlock ilBlock in ilBlocks) + { + ilOpCount += ilBlock.Count; + } + + _cache.AddOrUpdate(position, subroutine, ilOpCount); } private string GetSubroutineName(long position) { return $"Sub{position:x16}"; } - - private int GetGraphInstCount(Block[] graph) - { - int size = 0; - - foreach (Block block in graph) - { - size += block.OpCodes.Count; - } - - return size; - } } } \ No newline at end of file diff --git a/ChocolArm64/TranslatorCache.cs b/ChocolArm64/TranslatorCache.cs index f6d245dc3e..99c067708a 100644 --- a/ChocolArm64/TranslatorCache.cs +++ b/ChocolArm64/TranslatorCache.cs @@ -9,8 +9,8 @@ namespace ChocolArm64 { class TranslatorCache { - //Maximum size of the cache, in bytes, measured in ARM code size. - private const int MaxTotalSize = 4 * 1024 * 256; + //Maximum size of the cache, the unit used is completely arbitrary. + private const int MaxTotalSize = 0x100000; //Minimum time required in milliseconds for a method to be eligible for deletion. private const int MinTimeDelta = 2 * 60000;