aboutsummaryrefslogtreecommitdiff
path: root/ChocolArm64/Translation
diff options
context:
space:
mode:
authoremmauss <emmausssss@gmail.com>2018-02-20 22:09:23 +0200
committergdkchan <gab.dark.100@gmail.com>2018-02-20 17:09:23 -0300
commit62b827f474f0aa2152dd339fcc7cf31084e16a0b (patch)
tree0e5c55b341aee4db0ccb841a084f253ec5e05657 /ChocolArm64/Translation
parentcb665bb715834526d73c9469d16114b287faaecd (diff)
Split main project into core,graphics and chocolarm4 subproject (#29)
Diffstat (limited to 'ChocolArm64/Translation')
-rw-r--r--ChocolArm64/Translation/AILBlock.cs65
-rw-r--r--ChocolArm64/Translation/AILEmitter.cs187
-rw-r--r--ChocolArm64/Translation/AILEmitterCtx.cs487
-rw-r--r--ChocolArm64/Translation/AILLabel.cs28
-rw-r--r--ChocolArm64/Translation/AILOpCode.cs19
-rw-r--r--ChocolArm64/Translation/AILOpCodeBranch.cs21
-rw-r--r--ChocolArm64/Translation/AILOpCodeCall.cs20
-rw-r--r--ChocolArm64/Translation/AILOpCodeConst.cs81
-rw-r--r--ChocolArm64/Translation/AILOpCodeLoad.cs77
-rw-r--r--ChocolArm64/Translation/AILOpCodeLog.cs17
-rw-r--r--ChocolArm64/Translation/AILOpCodeStore.cs75
-rw-r--r--ChocolArm64/Translation/AIoType.cs15
-rw-r--r--ChocolArm64/Translation/ALocalAlloc.cs231
-rw-r--r--ChocolArm64/Translation/IAILEmit.cs7
-rw-r--r--ChocolArm64/Translation/ILGeneratorEx.cs129
15 files changed, 1459 insertions, 0 deletions
diff --git a/ChocolArm64/Translation/AILBlock.cs b/ChocolArm64/Translation/AILBlock.cs
new file mode 100644
index 00000000..bed195aa
--- /dev/null
+++ b/ChocolArm64/Translation/AILBlock.cs
@@ -0,0 +1,65 @@
+using System.Collections.Generic;
+
+namespace ChocolArm64.Translation
+{
+ class AILBlock : IAILEmit
+ {
+ public long IntInputs { get; private set; }
+ public long IntOutputs { get; private set; }
+
+ public long VecInputs { get; private set; }
+ public long VecOutputs { 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 AILOpCodeLoad Ld && AILEmitter.IsRegIndex(Ld.Index))
+ {
+ switch (Ld.IoType)
+ {
+ case AIoType.Flag: IntInputs |= ((1L << Ld.Index) << 32) & ~IntOutputs; break;
+ case AIoType.Int: IntInputs |= (1L << Ld.Index) & ~IntOutputs; break;
+ case AIoType.Vector: VecInputs |= (1L << Ld.Index) & ~VecOutputs; 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
new file mode 100644
index 00000000..8f6e1210
--- /dev/null
+++ b/ChocolArm64/Translation/AILEmitter.cs
@@ -0,0 +1,187 @@
+using ChocolArm64.Decoder;
+using ChocolArm64.State;
+using System;
+using System.Collections.Generic;
+using System.Reflection.Emit;
+
+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 ATranslatedSub GetSubroutine()
+ {
+ LocalAlloc = new ALocalAlloc(ILBlocks, Root);
+
+ InitSubroutine();
+ InitLocals();
+
+ foreach (AILBlock ILBlock in ILBlocks)
+ {
+ ILBlock.Emit(this);
+ }
+
+ return Subroutine;
+ }
+
+ public AILBlock GetILBlock(int Index) => ILBlocks[Index];
+
+ 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 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 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(AVec);
+ }
+
+ 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
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
diff --git a/ChocolArm64/Translation/AILLabel.cs b/ChocolArm64/Translation/AILLabel.cs
new file mode 100644
index 00000000..0ee39ad7
--- /dev/null
+++ b/ChocolArm64/Translation/AILLabel.cs
@@ -0,0 +1,28 @@
+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
new file mode 100644
index 00000000..a4bc93a0
--- /dev/null
+++ b/ChocolArm64/Translation/AILOpCode.cs
@@ -0,0 +1,19 @@
+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
new file mode 100644
index 00000000..e4caad1f
--- /dev/null
+++ b/ChocolArm64/Translation/AILOpCodeBranch.cs
@@ -0,0 +1,21 @@
+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
new file mode 100644
index 00000000..8cd944eb
--- /dev/null
+++ b/ChocolArm64/Translation/AILOpCodeCall.cs
@@ -0,0 +1,20 @@
+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
new file mode 100644
index 00000000..80150ec5
--- /dev/null
+++ b/ChocolArm64/Translation/AILOpCodeConst.cs
@@ -0,0 +1,81 @@
+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:
+ {
+ if (Value.I8 >= int.MinValue &&
+ Value.I8 <= int.MaxValue)
+ {
+ Context.Generator.EmitLdc_I4(Value.I4);
+
+ Context.Generator.Emit(OpCodes.Conv_I8);
+ }
+ else
+ {
+ 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
new file mode 100644
index 00000000..7cb431e2
--- /dev/null
+++ b/ChocolArm64/Translation/AILOpCodeLoad.cs
@@ -0,0 +1,77 @@
+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) : this(Index, IoType, ARegisterSize.Int64) { }
+
+ public AILOpCodeLoad(int Index, AIoType IoType, ARegisterSize RegisterSize)
+ {
+ this.IoType = IoType;
+ this.Index = Index;
+ 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
new file mode 100644
index 00000000..1338ca1f
--- /dev/null
+++ b/ChocolArm64/Translation/AILOpCodeLog.cs
@@ -0,0 +1,17 @@
+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
new file mode 100644
index 00000000..c4ea53ab
--- /dev/null
+++ b/ChocolArm64/Translation/AILOpCodeStore.cs
@@ -0,0 +1,75 @@
+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 = ARegisterSize.Int64)
+ {
+ this.IoType = IoType;
+ this.Index = Index;
+ 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/AIoType.cs b/ChocolArm64/Translation/AIoType.cs
new file mode 100644
index 00000000..94f89081
--- /dev/null
+++ b/ChocolArm64/Translation/AIoType.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace ChocolArm64.Translation
+{
+ [Flags]
+ enum AIoType
+ {
+ Arg,
+ Fields,
+ Flag,
+ Int,
+ Float,
+ Vector
+ }
+} \ No newline at end of file
diff --git a/ChocolArm64/Translation/ALocalAlloc.cs b/ChocolArm64/Translation/ALocalAlloc.cs
new file mode 100644
index 00000000..0661ddc8
--- /dev/null
+++ b/ChocolArm64/Translation/ALocalAlloc.cs
@@ -0,0 +1,231 @@
+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 = 120;
+
+ public ALocalAlloc(AILBlock[] Graph, AILBlock Root)
+ {
+ IntPaths = new Dictionary<AILBlock, PathIo>();
+ VecPaths = new Dictionary<AILBlock, PathIo>();
+
+ if (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;
+ BlkIO.IntInputs = 0;
+ BlkIO.VecInputs = 0;
+ BlkIO.IntOutputs = 0;
+ BlkIO.VecOutputs = 0;
+ }
+ 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;
+ long IntOutputs = 0;
+ long VecInputs = 0;
+ long 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.
+ 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
new file mode 100644
index 00000000..6e4e9a78
--- /dev/null
+++ b/ChocolArm64/Translation/IAILEmit.cs
@@ -0,0 +1,7 @@
+namespace ChocolArm64.Translation
+{
+ interface IAILEmit
+ {
+ void Emit(AILEmitter Context);
+ }
+} \ No newline at end of file
diff --git a/ChocolArm64/Translation/ILGeneratorEx.cs b/ChocolArm64/Translation/ILGeneratorEx.cs
new file mode 100644
index 00000000..abb35ec3
--- /dev/null
+++ b/ChocolArm64/Translation/ILGeneratorEx.cs
@@ -0,0 +1,129 @@
+using System;
+
+namespace ChocolArm64
+{
+ using System.Reflection.Emit;
+
+ static class ILGeneratorEx
+ {
+ public static void EmitLdc_I4(this ILGenerator Generator,int 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;
+ }
+ }
+
+ public static void EmitLdarg(this ILGenerator Generator, int 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;
+
+ default:
+ if ((uint)Index <= byte.MaxValue)
+ {
+ Generator.Emit(OpCodes.Ldarg_S, (byte)Index);
+ }
+ else if ((uint)Index < ushort.MaxValue)
+ {
+ Generator.Emit(OpCodes.Ldarg, (short)Index);
+ }
+ else
+ {
+ throw new ArgumentOutOfRangeException(nameof(Index));
+ }
+ break;
+ }
+ }
+
+ public static void EmitStarg(this ILGenerator Generator, int Index)
+ {
+ if ((uint)Index <= byte.MaxValue)
+ {
+ Generator.Emit(OpCodes.Starg_S, (byte)Index);
+ }
+ else if ((uint)Index < ushort.MaxValue)
+ {
+ Generator.Emit(OpCodes.Starg, (short)Index);
+ }
+ else
+ {
+ throw new ArgumentOutOfRangeException(nameof(Index));
+ }
+ }
+
+ public static void EmitLdloc(this ILGenerator Generator, int 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;
+
+ default:
+ if ((uint)Index <= byte.MaxValue)
+ {
+ Generator.Emit(OpCodes.Ldloc_S, (byte)Index);
+ }
+ else if ((uint)Index < ushort.MaxValue)
+ {
+ Generator.Emit(OpCodes.Ldloc, (short)Index);
+ }
+ else
+ {
+ throw new ArgumentOutOfRangeException(nameof(Index));
+ }
+ break;
+ }
+ }
+
+ public static void EmitStloc(this ILGenerator Generator, int 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;
+
+ default:
+ if ((uint)Index <= byte.MaxValue)
+ {
+ Generator.Emit(OpCodes.Stloc_S, (byte)Index);
+ }
+ else if ((uint)Index < ushort.MaxValue)
+ {
+ Generator.Emit(OpCodes.Stloc, (short)Index);
+ }
+ else
+ {
+ throw new ArgumentOutOfRangeException(nameof(Index));
+ }
+ break;
+ }
+ }
+
+ public static void EmitLdargSeq(this ILGenerator Generator, int Count)
+ {
+ for (int Index = 0; Index < Count; Index++)
+ {
+ Generator.EmitLdarg(Index);
+ }
+ }
+ }
+} \ No newline at end of file