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/ILEmitterCtx.cs | |
| 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/ILEmitterCtx.cs')
| -rw-r--r-- | ChocolArm64/Translation/ILEmitterCtx.cs | 554 |
1 files changed, 554 insertions, 0 deletions
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); + } + } +} |
