diff options
Diffstat (limited to 'ChocolArm64/Translation/AILEmitterCtx.cs')
| -rw-r--r-- | ChocolArm64/Translation/AILEmitterCtx.cs | 487 |
1 files changed, 487 insertions, 0 deletions
diff --git a/ChocolArm64/Translation/AILEmitterCtx.cs b/ChocolArm64/Translation/AILEmitterCtx.cs new file mode 100644 index 00000000..cf644540 --- /dev/null +++ b/ChocolArm64/Translation/AILEmitterCtx.cs @@ -0,0 +1,487 @@ +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 ATranslator Translator; + + private Dictionary<long, AILLabel> Labels; + + private AILEmitter Emitter; + + private AILBlock ILBlock; + + private AOpCode LastCmpOp; + private AOpCode LastFlagOp; + + private int BlkIndex; + private int OpcIndex; + + private ABlock[] Graph; + private ABlock Root; + public ABlock CurrBlock => Graph[BlkIndex]; + public AOpCode CurrOp => Graph[BlkIndex].OpCodes[OpcIndex]; + + //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; + + public AILEmitterCtx(ATranslator Translator, ABlock[] Graph, ABlock Root) + { + this.Translator = Translator; + this.Graph = Graph; + this.Root = Root; + + string SubName = $"Sub{Root.Position:X16}"; + + Labels = new Dictionary<long, AILLabel>(); + + Emitter = new AILEmitter(Graph, Root, SubName); + + ILBlock = Emitter.GetILBlock(0); + + OpcIndex = -1; + + if (!AdvanceOpCode()) + { + throw new ArgumentException(nameof(Graph)); + } + } + + public ATranslatedSub GetSubroutine() => Emitter.GetSubroutine(); + + public bool AdvanceOpCode() + { + while (++OpcIndex >= (CurrBlock?.OpCodes.Count ?? 0)) + { + if (BlkIndex + 1 >= Graph.Length) + { + return false; + } + + BlkIndex++; + OpcIndex = -1; + + ILBlock = Emitter.GetILBlock(BlkIndex); + } + + return true; + } + + public void EmitOpCode() + { + if (OpcIndex == 0) + { + MarkLabel(GetLabel(CurrBlock.Position)); + } + + CurrOp.Emitter(this); + } + + public bool TryOptEmitSubroutineCall() + { + if (!Translator.TryGetCachedSub(CurrOp, out ATranslatedSub Sub)) + { + return false; + } + + for (int Index = 0; Index < ATranslatedSub.FixedArgTypes.Length; Index++) + { + EmitLdarg(Index); + } + + foreach (ARegister Reg in Sub.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(Sub.Method); + + return true; + } + + public void TryOptMarkCondWithoutCmp() + { + LastCmpOp = 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 (LastCmpOp != null && LastFlagOp == LastCmpOp && BranchOps.ContainsKey(Cond)) + { + Ldloc(Tmp3Index, AIoType.Int, LastCmpOp.RegisterSize); + Ldloc(Tmp4Index, AIoType.Int, LastCmpOp.RegisterSize); + + if (LastCmpOp.Emitter == AInstEmit.Adds) + { + Emit(OpCodes.Neg); + } + + 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; + } + + if (IntType == AIntType.UInt64 || + IntType == AIntType.Int64) + { + return; + } + + if (CurrOp.RegisterSize != ARegisterSize.Int32) + { + Emit(IntType >= AIntType.Int8 + ? OpCodes.Conv_I8 + : OpCodes.Conv_U8); + } + } + + 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 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) + { + LastFlagOp = 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 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); + } + } +}
\ No newline at end of file |
