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
This commit is contained in:
parent
5a87e58183
commit
9cb57fb4bb
314 changed files with 19456 additions and 19456 deletions
|
@ -1,7 +0,0 @@
|
|||
namespace ChocolArm64.Translation
|
||||
{
|
||||
struct AILBarrier : IAILEmit
|
||||
{
|
||||
public void Emit(AILEmitter Context) { }
|
||||
}
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace ChocolArm64.Translation
|
||||
{
|
||||
class AILBlock : IAILEmit
|
||||
{
|
||||
public long IntInputs { get; private set; }
|
||||
public long IntOutputs { get; private set; }
|
||||
public long IntAwOutputs { get; private set; }
|
||||
|
||||
public long VecInputs { get; private set; }
|
||||
public long VecOutputs { get; private set; }
|
||||
public long VecAwOutputs { get; private set; }
|
||||
|
||||
public bool HasStateStore { get; private set; }
|
||||
|
||||
public List<IAILEmit> ILEmitters { get; private set; }
|
||||
|
||||
public AILBlock Next { get; set; }
|
||||
public AILBlock Branch { get; set; }
|
||||
|
||||
public AILBlock()
|
||||
{
|
||||
ILEmitters = new List<IAILEmit>();
|
||||
}
|
||||
|
||||
public void Add(IAILEmit ILEmitter)
|
||||
{
|
||||
if (ILEmitter is AILBarrier)
|
||||
{
|
||||
//Those barriers are used to separate the groups of CIL
|
||||
//opcodes emitted by each ARM instruction.
|
||||
//We can only consider the new outputs for doing input elimination
|
||||
//after all the CIL opcodes used by the instruction being emitted.
|
||||
IntAwOutputs = IntOutputs;
|
||||
VecAwOutputs = VecOutputs;
|
||||
}
|
||||
else if (ILEmitter is AILOpCodeLoad Ld && AILEmitter.IsRegIndex(Ld.Index))
|
||||
{
|
||||
switch (Ld.IoType)
|
||||
{
|
||||
case AIoType.Flag: IntInputs |= ((1L << Ld.Index) << 32) & ~IntAwOutputs; break;
|
||||
case AIoType.Int: IntInputs |= (1L << Ld.Index) & ~IntAwOutputs; break;
|
||||
case AIoType.Vector: VecInputs |= (1L << Ld.Index) & ~VecAwOutputs; break;
|
||||
}
|
||||
}
|
||||
else if (ILEmitter is AILOpCodeStore St)
|
||||
{
|
||||
if (AILEmitter.IsRegIndex(St.Index))
|
||||
{
|
||||
switch (St.IoType)
|
||||
{
|
||||
case AIoType.Flag: IntOutputs |= (1L << St.Index) << 32; break;
|
||||
case AIoType.Int: IntOutputs |= 1L << St.Index; break;
|
||||
case AIoType.Vector: VecOutputs |= 1L << St.Index; break;
|
||||
}
|
||||
}
|
||||
|
||||
if (St.IoType == AIoType.Fields)
|
||||
{
|
||||
HasStateStore = true;
|
||||
}
|
||||
}
|
||||
|
||||
ILEmitters.Add(ILEmitter);
|
||||
}
|
||||
|
||||
public void Emit(AILEmitter Context)
|
||||
{
|
||||
foreach (IAILEmit ILEmitter in ILEmitters)
|
||||
{
|
||||
ILEmitter.Emit(Context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,188 +0,0 @@
|
|||
using ChocolArm64.Decoder;
|
||||
using ChocolArm64.State;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection.Emit;
|
||||
using System.Runtime.Intrinsics;
|
||||
|
||||
namespace ChocolArm64.Translation
|
||||
{
|
||||
class AILEmitter
|
||||
{
|
||||
public ALocalAlloc LocalAlloc { get; private set; }
|
||||
|
||||
public ILGenerator Generator { get; private set; }
|
||||
|
||||
private Dictionary<ARegister, int> Locals;
|
||||
|
||||
private AILBlock[] ILBlocks;
|
||||
|
||||
private AILBlock Root;
|
||||
|
||||
private ATranslatedSub Subroutine;
|
||||
|
||||
private string SubName;
|
||||
|
||||
private int LocalsCount;
|
||||
|
||||
public AILEmitter(ABlock[] Graph, ABlock Root, string SubName)
|
||||
{
|
||||
this.SubName = SubName;
|
||||
|
||||
Locals = new Dictionary<ARegister, int>();
|
||||
|
||||
ILBlocks = new AILBlock[Graph.Length];
|
||||
|
||||
AILBlock GetBlock(int Index)
|
||||
{
|
||||
if (Index < 0 || Index >= ILBlocks.Length)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (ILBlocks[Index] == null)
|
||||
{
|
||||
ILBlocks[Index] = new AILBlock();
|
||||
}
|
||||
|
||||
return ILBlocks[Index];
|
||||
}
|
||||
|
||||
for (int Index = 0; Index < ILBlocks.Length; Index++)
|
||||
{
|
||||
AILBlock Block = GetBlock(Index);
|
||||
|
||||
Block.Next = GetBlock(Array.IndexOf(Graph, Graph[Index].Next));
|
||||
Block.Branch = GetBlock(Array.IndexOf(Graph, Graph[Index].Branch));
|
||||
}
|
||||
|
||||
this.Root = ILBlocks[Array.IndexOf(Graph, Root)];
|
||||
}
|
||||
|
||||
public AILBlock GetILBlock(int Index) => ILBlocks[Index];
|
||||
|
||||
public ATranslatedSub GetSubroutine()
|
||||
{
|
||||
LocalAlloc = new ALocalAlloc(ILBlocks, Root);
|
||||
|
||||
InitSubroutine();
|
||||
InitLocals();
|
||||
|
||||
foreach (AILBlock ILBlock in ILBlocks)
|
||||
{
|
||||
ILBlock.Emit(this);
|
||||
}
|
||||
|
||||
return Subroutine;
|
||||
}
|
||||
|
||||
private void InitSubroutine()
|
||||
{
|
||||
List<ARegister> Params = new List<ARegister>();
|
||||
|
||||
void SetParams(long Inputs, ARegisterType BaseType)
|
||||
{
|
||||
for (int Bit = 0; Bit < 64; Bit++)
|
||||
{
|
||||
long Mask = 1L << Bit;
|
||||
|
||||
if ((Inputs & Mask) != 0)
|
||||
{
|
||||
Params.Add(GetRegFromBit(Bit, BaseType));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SetParams(LocalAlloc.GetIntInputs(Root), ARegisterType.Int);
|
||||
SetParams(LocalAlloc.GetVecInputs(Root), ARegisterType.Vector);
|
||||
|
||||
DynamicMethod Mthd = new DynamicMethod(SubName, typeof(long), GetParamTypes(Params));
|
||||
|
||||
Generator = Mthd.GetILGenerator();
|
||||
|
||||
Subroutine = new ATranslatedSub(Mthd, Params);
|
||||
}
|
||||
|
||||
private void InitLocals()
|
||||
{
|
||||
int ParamsStart = ATranslatedSub.FixedArgTypes.Length;
|
||||
|
||||
Locals = new Dictionary<ARegister, int>();
|
||||
|
||||
for (int Index = 0; Index < Subroutine.Params.Count; Index++)
|
||||
{
|
||||
ARegister Reg = Subroutine.Params[Index];
|
||||
|
||||
Generator.EmitLdarg(Index + ParamsStart);
|
||||
Generator.EmitStloc(GetLocalIndex(Reg));
|
||||
}
|
||||
}
|
||||
|
||||
private Type[] GetParamTypes(IList<ARegister> Params)
|
||||
{
|
||||
Type[] FixedArgs = ATranslatedSub.FixedArgTypes;
|
||||
|
||||
Type[] Output = new Type[Params.Count + FixedArgs.Length];
|
||||
|
||||
FixedArgs.CopyTo(Output, 0);
|
||||
|
||||
int TypeIdx = FixedArgs.Length;
|
||||
|
||||
for (int Index = 0; Index < Params.Count; Index++)
|
||||
{
|
||||
Output[TypeIdx++] = GetFieldType(Params[Index].Type);
|
||||
}
|
||||
|
||||
return Output;
|
||||
}
|
||||
|
||||
public int GetLocalIndex(ARegister Reg)
|
||||
{
|
||||
if (!Locals.TryGetValue(Reg, out int Index))
|
||||
{
|
||||
Generator.DeclareLocal(GetLocalType(Reg));
|
||||
|
||||
Index = LocalsCount++;
|
||||
|
||||
Locals.Add(Reg, Index);
|
||||
}
|
||||
|
||||
return Index;
|
||||
}
|
||||
|
||||
public Type GetLocalType(ARegister Reg) => GetFieldType(Reg.Type);
|
||||
|
||||
public Type GetFieldType(ARegisterType RegType)
|
||||
{
|
||||
switch (RegType)
|
||||
{
|
||||
case ARegisterType.Flag: return typeof(bool);
|
||||
case ARegisterType.Int: return typeof(ulong);
|
||||
case ARegisterType.Vector: return typeof(Vector128<float>);
|
||||
}
|
||||
|
||||
throw new ArgumentException(nameof(RegType));
|
||||
}
|
||||
|
||||
public static ARegister GetRegFromBit(int Bit, ARegisterType BaseType)
|
||||
{
|
||||
if (Bit < 32)
|
||||
{
|
||||
return new ARegister(Bit, BaseType);
|
||||
}
|
||||
else if (BaseType == ARegisterType.Int)
|
||||
{
|
||||
return new ARegister(Bit & 0x1f, ARegisterType.Flag);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(Bit));
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsRegIndex(int Index)
|
||||
{
|
||||
return Index >= 0 && Index < 32;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,554 +0,0 @@
|
|||
using ChocolArm64.Decoder;
|
||||
using ChocolArm64.Instruction;
|
||||
using ChocolArm64.State;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace ChocolArm64.Translation
|
||||
{
|
||||
class AILEmitterCtx
|
||||
{
|
||||
private ATranslatorCache Cache;
|
||||
|
||||
private Dictionary<long, AILLabel> Labels;
|
||||
|
||||
private int BlkIndex;
|
||||
private int OpcIndex;
|
||||
|
||||
private ABlock[] Graph;
|
||||
private ABlock Root;
|
||||
public ABlock CurrBlock => Graph[BlkIndex];
|
||||
public AOpCode CurrOp => Graph[BlkIndex].OpCodes[OpcIndex];
|
||||
|
||||
private AILEmitter Emitter;
|
||||
|
||||
private AILBlock ILBlock;
|
||||
|
||||
private AOpCode OptOpLastCompare;
|
||||
private AOpCode OptOpLastFlagSet;
|
||||
|
||||
//This is the index of the temporary register, used to store temporary
|
||||
//values needed by some functions, since IL doesn't have a swap instruction.
|
||||
//You can use any value here as long it doesn't conflict with the indices
|
||||
//for the other registers. Any value >= 64 or < 0 will do.
|
||||
private const int Tmp1Index = -1;
|
||||
private const int Tmp2Index = -2;
|
||||
private const int Tmp3Index = -3;
|
||||
private const int Tmp4Index = -4;
|
||||
private const int Tmp5Index = -5;
|
||||
private const int Tmp6Index = -6;
|
||||
|
||||
public AILEmitterCtx(
|
||||
ATranslatorCache Cache,
|
||||
ABlock[] Graph,
|
||||
ABlock Root,
|
||||
string SubName)
|
||||
{
|
||||
this.Cache = Cache ?? throw new ArgumentNullException(nameof(Cache));
|
||||
this.Graph = Graph ?? throw new ArgumentNullException(nameof(Graph));
|
||||
this.Root = Root ?? throw new ArgumentNullException(nameof(Root));
|
||||
|
||||
Labels = new Dictionary<long, AILLabel>();
|
||||
|
||||
Emitter = new AILEmitter(Graph, Root, SubName);
|
||||
|
||||
ILBlock = Emitter.GetILBlock(0);
|
||||
|
||||
OpcIndex = -1;
|
||||
|
||||
if (Graph.Length == 0 || !AdvanceOpCode())
|
||||
{
|
||||
throw new ArgumentException(nameof(Graph));
|
||||
}
|
||||
}
|
||||
|
||||
public ATranslatedSub GetSubroutine()
|
||||
{
|
||||
return Emitter.GetSubroutine();
|
||||
}
|
||||
|
||||
public bool AdvanceOpCode()
|
||||
{
|
||||
if (OpcIndex + 1 == CurrBlock.OpCodes.Count &&
|
||||
BlkIndex + 1 == Graph.Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
while (++OpcIndex >= (CurrBlock?.OpCodes.Count ?? 0))
|
||||
{
|
||||
BlkIndex++;
|
||||
OpcIndex = -1;
|
||||
|
||||
OptOpLastFlagSet = null;
|
||||
OptOpLastCompare = null;
|
||||
|
||||
ILBlock = Emitter.GetILBlock(BlkIndex);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void EmitOpCode()
|
||||
{
|
||||
if (OpcIndex == 0)
|
||||
{
|
||||
MarkLabel(GetLabel(CurrBlock.Position));
|
||||
|
||||
EmitSynchronization();
|
||||
}
|
||||
|
||||
CurrOp.Emitter(this);
|
||||
|
||||
ILBlock.Add(new AILBarrier());
|
||||
}
|
||||
|
||||
private void EmitSynchronization()
|
||||
{
|
||||
EmitLdarg(ATranslatedSub.StateArgIdx);
|
||||
|
||||
EmitLdc_I4(CurrBlock.OpCodes.Count);
|
||||
|
||||
EmitPrivateCall(typeof(AThreadState), nameof(AThreadState.Synchronize));
|
||||
|
||||
EmitLdc_I4(0);
|
||||
|
||||
AILLabel LblContinue = new AILLabel();
|
||||
|
||||
Emit(OpCodes.Bne_Un_S, LblContinue);
|
||||
|
||||
EmitLdc_I8(0);
|
||||
|
||||
Emit(OpCodes.Ret);
|
||||
|
||||
MarkLabel(LblContinue);
|
||||
}
|
||||
|
||||
public bool TryOptEmitSubroutineCall()
|
||||
{
|
||||
if (CurrBlock.Next == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (CurrOp.Emitter != AInstEmit.Bl)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Cache.TryGetSubroutine(((AOpCodeBImmAl)CurrOp).Imm, out ATranslatedSub Subroutine))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int Index = 0; Index < ATranslatedSub.FixedArgTypes.Length; Index++)
|
||||
{
|
||||
EmitLdarg(Index);
|
||||
}
|
||||
|
||||
foreach (ARegister Reg in Subroutine.Params)
|
||||
{
|
||||
switch (Reg.Type)
|
||||
{
|
||||
case ARegisterType.Flag: Ldloc(Reg.Index, AIoType.Flag); break;
|
||||
case ARegisterType.Int: Ldloc(Reg.Index, AIoType.Int); break;
|
||||
case ARegisterType.Vector: Ldloc(Reg.Index, AIoType.Vector); break;
|
||||
}
|
||||
}
|
||||
|
||||
EmitCall(Subroutine.Method);
|
||||
|
||||
Subroutine.AddCaller(Root.Position);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void TryOptMarkCondWithoutCmp()
|
||||
{
|
||||
OptOpLastCompare = CurrOp;
|
||||
|
||||
AInstEmitAluHelper.EmitDataLoadOpers(this);
|
||||
|
||||
Stloc(Tmp4Index, AIoType.Int);
|
||||
Stloc(Tmp3Index, AIoType.Int);
|
||||
}
|
||||
|
||||
private Dictionary<ACond, OpCode> BranchOps = new Dictionary<ACond, OpCode>()
|
||||
{
|
||||
{ ACond.Eq, OpCodes.Beq },
|
||||
{ ACond.Ne, OpCodes.Bne_Un },
|
||||
{ ACond.Ge_Un, OpCodes.Bge_Un },
|
||||
{ ACond.Lt_Un, OpCodes.Blt_Un },
|
||||
{ ACond.Gt_Un, OpCodes.Bgt_Un },
|
||||
{ ACond.Le_Un, OpCodes.Ble_Un },
|
||||
{ ACond.Ge, OpCodes.Bge },
|
||||
{ ACond.Lt, OpCodes.Blt },
|
||||
{ ACond.Gt, OpCodes.Bgt },
|
||||
{ ACond.Le, OpCodes.Ble }
|
||||
};
|
||||
|
||||
public void EmitCondBranch(AILLabel Target, ACond Cond)
|
||||
{
|
||||
OpCode ILOp;
|
||||
|
||||
int IntCond = (int)Cond;
|
||||
|
||||
if (OptOpLastCompare != null &&
|
||||
OptOpLastCompare == OptOpLastFlagSet && BranchOps.ContainsKey(Cond))
|
||||
{
|
||||
Ldloc(Tmp3Index, AIoType.Int, OptOpLastCompare.RegisterSize);
|
||||
Ldloc(Tmp4Index, AIoType.Int, OptOpLastCompare.RegisterSize);
|
||||
|
||||
ILOp = BranchOps[Cond];
|
||||
}
|
||||
else if (IntCond < 14)
|
||||
{
|
||||
int CondTrue = IntCond >> 1;
|
||||
|
||||
switch (CondTrue)
|
||||
{
|
||||
case 0: EmitLdflg((int)APState.ZBit); break;
|
||||
case 1: EmitLdflg((int)APState.CBit); break;
|
||||
case 2: EmitLdflg((int)APState.NBit); break;
|
||||
case 3: EmitLdflg((int)APState.VBit); break;
|
||||
|
||||
case 4:
|
||||
EmitLdflg((int)APState.CBit);
|
||||
EmitLdflg((int)APState.ZBit);
|
||||
|
||||
Emit(OpCodes.Not);
|
||||
Emit(OpCodes.And);
|
||||
break;
|
||||
|
||||
case 5:
|
||||
case 6:
|
||||
EmitLdflg((int)APState.NBit);
|
||||
EmitLdflg((int)APState.VBit);
|
||||
|
||||
Emit(OpCodes.Ceq);
|
||||
|
||||
if (CondTrue == 6)
|
||||
{
|
||||
EmitLdflg((int)APState.ZBit);
|
||||
|
||||
Emit(OpCodes.Not);
|
||||
Emit(OpCodes.And);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
ILOp = (IntCond & 1) != 0
|
||||
? OpCodes.Brfalse
|
||||
: OpCodes.Brtrue;
|
||||
}
|
||||
else
|
||||
{
|
||||
ILOp = OpCodes.Br;
|
||||
}
|
||||
|
||||
Emit(ILOp, Target);
|
||||
}
|
||||
|
||||
public void EmitCast(AIntType IntType)
|
||||
{
|
||||
switch (IntType)
|
||||
{
|
||||
case AIntType.UInt8: Emit(OpCodes.Conv_U1); break;
|
||||
case AIntType.UInt16: Emit(OpCodes.Conv_U2); break;
|
||||
case AIntType.UInt32: Emit(OpCodes.Conv_U4); break;
|
||||
case AIntType.UInt64: Emit(OpCodes.Conv_U8); break;
|
||||
case AIntType.Int8: Emit(OpCodes.Conv_I1); break;
|
||||
case AIntType.Int16: Emit(OpCodes.Conv_I2); break;
|
||||
case AIntType.Int32: Emit(OpCodes.Conv_I4); break;
|
||||
case AIntType.Int64: Emit(OpCodes.Conv_I8); break;
|
||||
}
|
||||
|
||||
bool Sz64 = CurrOp.RegisterSize != ARegisterSize.Int32;
|
||||
|
||||
if (Sz64 == (IntType == AIntType.UInt64 ||
|
||||
IntType == AIntType.Int64))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Sz64)
|
||||
{
|
||||
Emit(IntType >= AIntType.Int8
|
||||
? OpCodes.Conv_I8
|
||||
: OpCodes.Conv_U8);
|
||||
}
|
||||
else
|
||||
{
|
||||
Emit(OpCodes.Conv_U4);
|
||||
}
|
||||
}
|
||||
|
||||
public void EmitLsl(int Amount) => EmitILShift(Amount, OpCodes.Shl);
|
||||
public void EmitLsr(int Amount) => EmitILShift(Amount, OpCodes.Shr_Un);
|
||||
public void EmitAsr(int Amount) => EmitILShift(Amount, OpCodes.Shr);
|
||||
|
||||
private void EmitILShift(int Amount, OpCode ILOp)
|
||||
{
|
||||
if (Amount > 0)
|
||||
{
|
||||
EmitLdc_I4(Amount);
|
||||
|
||||
Emit(ILOp);
|
||||
}
|
||||
}
|
||||
|
||||
public void EmitRor(int Amount)
|
||||
{
|
||||
if (Amount > 0)
|
||||
{
|
||||
Stloc(Tmp2Index, AIoType.Int);
|
||||
Ldloc(Tmp2Index, AIoType.Int);
|
||||
|
||||
EmitLdc_I4(Amount);
|
||||
|
||||
Emit(OpCodes.Shr_Un);
|
||||
|
||||
Ldloc(Tmp2Index, AIoType.Int);
|
||||
|
||||
EmitLdc_I4(CurrOp.GetBitsCount() - Amount);
|
||||
|
||||
Emit(OpCodes.Shl);
|
||||
Emit(OpCodes.Or);
|
||||
}
|
||||
}
|
||||
|
||||
public AILLabel GetLabel(long Position)
|
||||
{
|
||||
if (!Labels.TryGetValue(Position, out AILLabel Output))
|
||||
{
|
||||
Output = new AILLabel();
|
||||
|
||||
Labels.Add(Position, Output);
|
||||
}
|
||||
|
||||
return Output;
|
||||
}
|
||||
|
||||
public void MarkLabel(AILLabel Label)
|
||||
{
|
||||
ILBlock.Add(Label);
|
||||
}
|
||||
|
||||
public void Emit(OpCode ILOp)
|
||||
{
|
||||
ILBlock.Add(new AILOpCode(ILOp));
|
||||
}
|
||||
|
||||
public void Emit(OpCode ILOp, AILLabel Label)
|
||||
{
|
||||
ILBlock.Add(new AILOpCodeBranch(ILOp, Label));
|
||||
}
|
||||
|
||||
public void Emit(string Text)
|
||||
{
|
||||
ILBlock.Add(new AILOpCodeLog(Text));
|
||||
}
|
||||
|
||||
public void EmitLdarg(int Index)
|
||||
{
|
||||
ILBlock.Add(new AILOpCodeLoad(Index, AIoType.Arg));
|
||||
}
|
||||
|
||||
public void EmitLdintzr(int Index)
|
||||
{
|
||||
if (Index != AThreadState.ZRIndex)
|
||||
{
|
||||
EmitLdint(Index);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitLdc_I(0);
|
||||
}
|
||||
}
|
||||
|
||||
public void EmitStintzr(int Index)
|
||||
{
|
||||
if (Index != AThreadState.ZRIndex)
|
||||
{
|
||||
EmitStint(Index);
|
||||
}
|
||||
else
|
||||
{
|
||||
Emit(OpCodes.Pop);
|
||||
}
|
||||
}
|
||||
|
||||
public void EmitLoadState(ABlock RetBlk)
|
||||
{
|
||||
ILBlock.Add(new AILOpCodeLoad(Array.IndexOf(Graph, RetBlk), AIoType.Fields));
|
||||
}
|
||||
|
||||
public void EmitStoreState()
|
||||
{
|
||||
ILBlock.Add(new AILOpCodeStore(Array.IndexOf(Graph, CurrBlock), AIoType.Fields));
|
||||
}
|
||||
|
||||
public void EmitLdtmp() => EmitLdint(Tmp1Index);
|
||||
public void EmitSttmp() => EmitStint(Tmp1Index);
|
||||
|
||||
public void EmitLdvectmp() => EmitLdvec(Tmp5Index);
|
||||
public void EmitStvectmp() => EmitStvec(Tmp5Index);
|
||||
|
||||
public void EmitLdvectmp2() => EmitLdvec(Tmp6Index);
|
||||
public void EmitStvectmp2() => EmitStvec(Tmp6Index);
|
||||
|
||||
public void EmitLdint(int Index) => Ldloc(Index, AIoType.Int);
|
||||
public void EmitStint(int Index) => Stloc(Index, AIoType.Int);
|
||||
|
||||
public void EmitLdvec(int Index) => Ldloc(Index, AIoType.Vector);
|
||||
public void EmitStvec(int Index) => Stloc(Index, AIoType.Vector);
|
||||
|
||||
public void EmitLdflg(int Index) => Ldloc(Index, AIoType.Flag);
|
||||
public void EmitStflg(int Index)
|
||||
{
|
||||
OptOpLastFlagSet = CurrOp;
|
||||
|
||||
Stloc(Index, AIoType.Flag);
|
||||
}
|
||||
|
||||
private void Ldloc(int Index, AIoType IoType)
|
||||
{
|
||||
ILBlock.Add(new AILOpCodeLoad(Index, IoType, CurrOp.RegisterSize));
|
||||
}
|
||||
|
||||
private void Ldloc(int Index, AIoType IoType, ARegisterSize RegisterSize)
|
||||
{
|
||||
ILBlock.Add(new AILOpCodeLoad(Index, IoType, RegisterSize));
|
||||
}
|
||||
|
||||
private void Stloc(int Index, AIoType IoType)
|
||||
{
|
||||
ILBlock.Add(new AILOpCodeStore(Index, IoType, CurrOp.RegisterSize));
|
||||
}
|
||||
|
||||
public void EmitCallPropGet(Type ObjType, string PropName)
|
||||
{
|
||||
if (ObjType == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(ObjType));
|
||||
}
|
||||
|
||||
if (PropName == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(PropName));
|
||||
}
|
||||
|
||||
EmitCall(ObjType.GetMethod($"get_{PropName}"));
|
||||
}
|
||||
|
||||
public void EmitCallPropSet(Type ObjType, string PropName)
|
||||
{
|
||||
if (ObjType == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(ObjType));
|
||||
}
|
||||
|
||||
if (PropName == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(PropName));
|
||||
}
|
||||
|
||||
EmitCall(ObjType.GetMethod($"set_{PropName}"));
|
||||
}
|
||||
|
||||
public void EmitCall(Type ObjType, string MthdName)
|
||||
{
|
||||
if (ObjType == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(ObjType));
|
||||
}
|
||||
|
||||
if (MthdName == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(MthdName));
|
||||
}
|
||||
|
||||
EmitCall(ObjType.GetMethod(MthdName));
|
||||
}
|
||||
|
||||
public void EmitPrivateCall(Type ObjType, string MthdName)
|
||||
{
|
||||
if (ObjType == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(ObjType));
|
||||
}
|
||||
|
||||
if (MthdName == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(MthdName));
|
||||
}
|
||||
|
||||
EmitCall(ObjType.GetMethod(MthdName, BindingFlags.Instance | BindingFlags.NonPublic));
|
||||
}
|
||||
|
||||
public void EmitCall(MethodInfo MthdInfo)
|
||||
{
|
||||
if (MthdInfo == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(MthdInfo));
|
||||
}
|
||||
|
||||
ILBlock.Add(new AILOpCodeCall(MthdInfo));
|
||||
}
|
||||
|
||||
public void EmitLdc_I(long Value)
|
||||
{
|
||||
if (CurrOp.RegisterSize == ARegisterSize.Int32)
|
||||
{
|
||||
EmitLdc_I4((int)Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitLdc_I8(Value);
|
||||
}
|
||||
}
|
||||
|
||||
public void EmitLdc_I4(int Value)
|
||||
{
|
||||
ILBlock.Add(new AILOpCodeConst(Value));
|
||||
}
|
||||
|
||||
public void EmitLdc_I8(long Value)
|
||||
{
|
||||
ILBlock.Add(new AILOpCodeConst(Value));
|
||||
}
|
||||
|
||||
public void EmitLdc_R4(float Value)
|
||||
{
|
||||
ILBlock.Add(new AILOpCodeConst(Value));
|
||||
}
|
||||
|
||||
public void EmitLdc_R8(double Value)
|
||||
{
|
||||
ILBlock.Add(new AILOpCodeConst(Value));
|
||||
}
|
||||
|
||||
public void EmitZNFlagCheck()
|
||||
{
|
||||
EmitZNCheck(OpCodes.Ceq, (int)APState.ZBit);
|
||||
EmitZNCheck(OpCodes.Clt, (int)APState.NBit);
|
||||
}
|
||||
|
||||
private void EmitZNCheck(OpCode ILCmpOp, int Flag)
|
||||
{
|
||||
Emit(OpCodes.Dup);
|
||||
Emit(OpCodes.Ldc_I4_0);
|
||||
|
||||
if (CurrOp.RegisterSize != ARegisterSize.Int32)
|
||||
{
|
||||
Emit(OpCodes.Conv_I8);
|
||||
}
|
||||
|
||||
Emit(ILCmpOp);
|
||||
|
||||
EmitStflg(Flag);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
using System.Reflection.Emit;
|
||||
|
||||
namespace ChocolArm64.Translation
|
||||
{
|
||||
class AILLabel : IAILEmit
|
||||
{
|
||||
private bool HasLabel;
|
||||
|
||||
private Label Lbl;
|
||||
|
||||
public void Emit(AILEmitter Context)
|
||||
{
|
||||
Context.Generator.MarkLabel(GetLabel(Context));
|
||||
}
|
||||
|
||||
public Label GetLabel(AILEmitter Context)
|
||||
{
|
||||
if (!HasLabel)
|
||||
{
|
||||
Lbl = Context.Generator.DefineLabel();
|
||||
|
||||
HasLabel = true;
|
||||
}
|
||||
|
||||
return Lbl;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
using System.Reflection.Emit;
|
||||
|
||||
namespace ChocolArm64.Translation
|
||||
{
|
||||
struct AILOpCode : IAILEmit
|
||||
{
|
||||
private OpCode ILOp;
|
||||
|
||||
public AILOpCode(OpCode ILOp)
|
||||
{
|
||||
this.ILOp = ILOp;
|
||||
}
|
||||
|
||||
public void Emit(AILEmitter Context)
|
||||
{
|
||||
Context.Generator.Emit(ILOp);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
using System.Reflection.Emit;
|
||||
|
||||
namespace ChocolArm64.Translation
|
||||
{
|
||||
struct AILOpCodeBranch : IAILEmit
|
||||
{
|
||||
private OpCode ILOp;
|
||||
private AILLabel Label;
|
||||
|
||||
public AILOpCodeBranch(OpCode ILOp, AILLabel Label)
|
||||
{
|
||||
this.ILOp = ILOp;
|
||||
this.Label = Label;
|
||||
}
|
||||
|
||||
public void Emit(AILEmitter Context)
|
||||
{
|
||||
Context.Generator.Emit(ILOp, Label.GetLabel(Context));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace ChocolArm64.Translation
|
||||
{
|
||||
struct AILOpCodeCall : IAILEmit
|
||||
{
|
||||
private MethodInfo MthdInfo;
|
||||
|
||||
public AILOpCodeCall(MethodInfo MthdInfo)
|
||||
{
|
||||
this.MthdInfo = MthdInfo;
|
||||
}
|
||||
|
||||
public void Emit(AILEmitter Context)
|
||||
{
|
||||
Context.Generator.Emit(OpCodes.Call, MthdInfo);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
using System.Reflection.Emit;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace ChocolArm64.Translation
|
||||
{
|
||||
class AILOpCodeConst : IAILEmit
|
||||
{
|
||||
[StructLayout(LayoutKind.Explicit, Size = 8)]
|
||||
private struct ImmVal
|
||||
{
|
||||
[FieldOffset(0)] public int I4;
|
||||
[FieldOffset(0)] public long I8;
|
||||
[FieldOffset(0)] public float R4;
|
||||
[FieldOffset(0)] public double R8;
|
||||
}
|
||||
|
||||
private ImmVal Value;
|
||||
|
||||
private enum ConstType
|
||||
{
|
||||
Int32,
|
||||
Int64,
|
||||
Single,
|
||||
Double
|
||||
}
|
||||
|
||||
private ConstType Type;
|
||||
|
||||
private AILOpCodeConst(ConstType Type)
|
||||
{
|
||||
this.Type = Type;
|
||||
}
|
||||
|
||||
public AILOpCodeConst(int Value) : this(ConstType.Int32)
|
||||
{
|
||||
this.Value = new ImmVal { I4 = Value };
|
||||
}
|
||||
|
||||
public AILOpCodeConst(long Value) : this(ConstType.Int64)
|
||||
{
|
||||
this.Value = new ImmVal { I8 = Value };
|
||||
}
|
||||
|
||||
public AILOpCodeConst(float Value) : this(ConstType.Single)
|
||||
{
|
||||
this.Value = new ImmVal { R4 = Value };
|
||||
}
|
||||
|
||||
public AILOpCodeConst(double Value) : this(ConstType.Double)
|
||||
{
|
||||
this.Value = new ImmVal { R8 = Value };
|
||||
}
|
||||
|
||||
public void Emit(AILEmitter Context)
|
||||
{
|
||||
switch (Type)
|
||||
{
|
||||
case ConstType.Int32: Context.Generator.EmitLdc_I4(Value.I4); break;
|
||||
case ConstType.Int64: Context.Generator.Emit(OpCodes.Ldc_I8, Value.I8); break;
|
||||
case ConstType.Single: Context.Generator.Emit(OpCodes.Ldc_R4, Value.R4); break;
|
||||
case ConstType.Double: Context.Generator.Emit(OpCodes.Ldc_R8, Value.R8); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
using ChocolArm64.State;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace ChocolArm64.Translation
|
||||
{
|
||||
struct AILOpCodeLoad : IAILEmit
|
||||
{
|
||||
public int Index { get; private set; }
|
||||
|
||||
public AIoType IoType { get; private set; }
|
||||
|
||||
public ARegisterSize RegisterSize { get; private set; }
|
||||
|
||||
public AILOpCodeLoad(int Index, AIoType IoType, ARegisterSize RegisterSize = 0)
|
||||
{
|
||||
this.Index = Index;
|
||||
this.IoType = IoType;
|
||||
this.RegisterSize = RegisterSize;
|
||||
}
|
||||
|
||||
public void Emit(AILEmitter Context)
|
||||
{
|
||||
switch (IoType)
|
||||
{
|
||||
case AIoType.Arg: Context.Generator.EmitLdarg(Index); break;
|
||||
|
||||
case AIoType.Fields:
|
||||
{
|
||||
long IntInputs = Context.LocalAlloc.GetIntInputs(Context.GetILBlock(Index));
|
||||
long VecInputs = Context.LocalAlloc.GetVecInputs(Context.GetILBlock(Index));
|
||||
|
||||
LoadLocals(Context, IntInputs, ARegisterType.Int);
|
||||
LoadLocals(Context, VecInputs, ARegisterType.Vector);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case AIoType.Flag: EmitLdloc(Context, Index, ARegisterType.Flag); break;
|
||||
case AIoType.Int: EmitLdloc(Context, Index, ARegisterType.Int); break;
|
||||
case AIoType.Vector: EmitLdloc(Context, Index, ARegisterType.Vector); break;
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadLocals(AILEmitter Context, long Inputs, ARegisterType BaseType)
|
||||
{
|
||||
for (int Bit = 0; Bit < 64; Bit++)
|
||||
{
|
||||
long Mask = 1L << Bit;
|
||||
|
||||
if ((Inputs & Mask) != 0)
|
||||
{
|
||||
ARegister Reg = AILEmitter.GetRegFromBit(Bit, BaseType);
|
||||
|
||||
Context.Generator.EmitLdarg(ATranslatedSub.StateArgIdx);
|
||||
Context.Generator.Emit(OpCodes.Ldfld, Reg.GetField());
|
||||
|
||||
Context.Generator.EmitStloc(Context.GetLocalIndex(Reg));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void EmitLdloc(AILEmitter Context, int Index, ARegisterType RegisterType)
|
||||
{
|
||||
ARegister Reg = new ARegister(Index, RegisterType);
|
||||
|
||||
Context.Generator.EmitLdloc(Context.GetLocalIndex(Reg));
|
||||
|
||||
if (RegisterType == ARegisterType.Int &&
|
||||
RegisterSize == ARegisterSize.Int32)
|
||||
{
|
||||
Context.Generator.Emit(OpCodes.Conv_U4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
namespace ChocolArm64.Translation
|
||||
{
|
||||
struct AILOpCodeLog : IAILEmit
|
||||
{
|
||||
private string Text;
|
||||
|
||||
public AILOpCodeLog(string Text)
|
||||
{
|
||||
this.Text = Text;
|
||||
}
|
||||
|
||||
public void Emit(AILEmitter Context)
|
||||
{
|
||||
Context.Generator.EmitWriteLine(Text);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
using ChocolArm64.State;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace ChocolArm64.Translation
|
||||
{
|
||||
struct AILOpCodeStore : IAILEmit
|
||||
{
|
||||
public int Index { get; private set; }
|
||||
|
||||
public AIoType IoType { get; private set; }
|
||||
|
||||
public ARegisterSize RegisterSize { get; private set; }
|
||||
|
||||
public AILOpCodeStore(int Index, AIoType IoType, ARegisterSize RegisterSize = 0)
|
||||
{
|
||||
this.Index = Index;
|
||||
this.IoType = IoType;
|
||||
this.RegisterSize = RegisterSize;
|
||||
}
|
||||
|
||||
public void Emit(AILEmitter Context)
|
||||
{
|
||||
switch (IoType)
|
||||
{
|
||||
case AIoType.Arg: Context.Generator.EmitStarg(Index); break;
|
||||
|
||||
case AIoType.Fields:
|
||||
{
|
||||
long IntOutputs = Context.LocalAlloc.GetIntOutputs(Context.GetILBlock(Index));
|
||||
long VecOutputs = Context.LocalAlloc.GetVecOutputs(Context.GetILBlock(Index));
|
||||
|
||||
StoreLocals(Context, IntOutputs, ARegisterType.Int);
|
||||
StoreLocals(Context, VecOutputs, ARegisterType.Vector);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case AIoType.Flag: EmitStloc(Context, Index, ARegisterType.Flag); break;
|
||||
case AIoType.Int: EmitStloc(Context, Index, ARegisterType.Int); break;
|
||||
case AIoType.Vector: EmitStloc(Context, Index, ARegisterType.Vector); break;
|
||||
}
|
||||
}
|
||||
|
||||
private void StoreLocals(AILEmitter Context, long Outputs, ARegisterType BaseType)
|
||||
{
|
||||
for (int Bit = 0; Bit < 64; Bit++)
|
||||
{
|
||||
long Mask = 1L << Bit;
|
||||
|
||||
if ((Outputs & Mask) != 0)
|
||||
{
|
||||
ARegister Reg = AILEmitter.GetRegFromBit(Bit, BaseType);
|
||||
|
||||
Context.Generator.EmitLdarg(ATranslatedSub.StateArgIdx);
|
||||
Context.Generator.EmitLdloc(Context.GetLocalIndex(Reg));
|
||||
|
||||
Context.Generator.Emit(OpCodes.Stfld, Reg.GetField());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void EmitStloc(AILEmitter Context, int Index, ARegisterType RegisterType)
|
||||
{
|
||||
ARegister Reg = new ARegister(Index, RegisterType);
|
||||
|
||||
if (RegisterType == ARegisterType.Int &&
|
||||
RegisterSize == ARegisterSize.Int32)
|
||||
{
|
||||
Context.Generator.Emit(OpCodes.Conv_U8);
|
||||
}
|
||||
|
||||
Context.Generator.EmitStloc(Context.GetLocalIndex(Reg));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,229 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace ChocolArm64.Translation
|
||||
{
|
||||
class ALocalAlloc
|
||||
{
|
||||
private class PathIo
|
||||
{
|
||||
private Dictionary<AILBlock, long> AllInputs;
|
||||
private Dictionary<AILBlock, long> CmnOutputs;
|
||||
|
||||
private long AllOutputs;
|
||||
|
||||
public PathIo()
|
||||
{
|
||||
AllInputs = new Dictionary<AILBlock, long>();
|
||||
CmnOutputs = new Dictionary<AILBlock, long>();
|
||||
}
|
||||
|
||||
public PathIo(AILBlock Root, long Inputs, long Outputs) : this()
|
||||
{
|
||||
Set(Root, Inputs, Outputs);
|
||||
}
|
||||
|
||||
public void Set(AILBlock Root, long Inputs, long Outputs)
|
||||
{
|
||||
if (!AllInputs.TryAdd(Root, Inputs))
|
||||
{
|
||||
AllInputs[Root] |= Inputs;
|
||||
}
|
||||
|
||||
if (!CmnOutputs.TryAdd(Root, Outputs))
|
||||
{
|
||||
CmnOutputs[Root] &= Outputs;
|
||||
}
|
||||
|
||||
AllOutputs |= Outputs;
|
||||
}
|
||||
|
||||
public long GetInputs(AILBlock Root)
|
||||
{
|
||||
if (AllInputs.TryGetValue(Root, out long Inputs))
|
||||
{
|
||||
return Inputs | (AllOutputs & ~CmnOutputs[Root]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long GetOutputs()
|
||||
{
|
||||
return AllOutputs;
|
||||
}
|
||||
}
|
||||
|
||||
private Dictionary<AILBlock, PathIo> IntPaths;
|
||||
private Dictionary<AILBlock, PathIo> VecPaths;
|
||||
|
||||
private struct BlockIo
|
||||
{
|
||||
public AILBlock Block;
|
||||
public AILBlock Entry;
|
||||
|
||||
public long IntInputs;
|
||||
public long VecInputs;
|
||||
public long IntOutputs;
|
||||
public long VecOutputs;
|
||||
}
|
||||
|
||||
private const int MaxOptGraphLength = 40;
|
||||
|
||||
public ALocalAlloc(AILBlock[] Graph, AILBlock Root)
|
||||
{
|
||||
IntPaths = new Dictionary<AILBlock, PathIo>();
|
||||
VecPaths = new Dictionary<AILBlock, PathIo>();
|
||||
|
||||
if (Graph.Length > 1 &&
|
||||
Graph.Length < MaxOptGraphLength)
|
||||
{
|
||||
InitializeOptimal(Graph, Root);
|
||||
}
|
||||
else
|
||||
{
|
||||
InitializeFast(Graph);
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeOptimal(AILBlock[] Graph, AILBlock Root)
|
||||
{
|
||||
//This will go through all possible paths on the graph,
|
||||
//and store all inputs/outputs for each block. A register
|
||||
//that was previously written to already is not considered an input.
|
||||
//When a block can be reached by more than one path, then the
|
||||
//output from all paths needs to be set for this block, and
|
||||
//only outputs present in all of the parent blocks can be considered
|
||||
//when doing input elimination. Each block chain have a root, that's where
|
||||
//the code starts executing. They are present on the subroutine start point,
|
||||
//and on call return points too (address written to X30 by BL).
|
||||
HashSet<BlockIo> Visited = new HashSet<BlockIo>();
|
||||
|
||||
Queue<BlockIo> Unvisited = new Queue<BlockIo>();
|
||||
|
||||
void Enqueue(BlockIo Block)
|
||||
{
|
||||
if (!Visited.Contains(Block))
|
||||
{
|
||||
Unvisited.Enqueue(Block);
|
||||
|
||||
Visited.Add(Block);
|
||||
}
|
||||
}
|
||||
|
||||
Enqueue(new BlockIo()
|
||||
{
|
||||
Block = Root,
|
||||
Entry = Root
|
||||
});
|
||||
|
||||
while (Unvisited.Count > 0)
|
||||
{
|
||||
BlockIo Current = Unvisited.Dequeue();
|
||||
|
||||
Current.IntInputs |= Current.Block.IntInputs & ~Current.IntOutputs;
|
||||
Current.VecInputs |= Current.Block.VecInputs & ~Current.VecOutputs;
|
||||
Current.IntOutputs |= Current.Block.IntOutputs;
|
||||
Current.VecOutputs |= Current.Block.VecOutputs;
|
||||
|
||||
//Check if this is a exit block
|
||||
//(a block that returns or calls another sub).
|
||||
if ((Current.Block.Next == null &&
|
||||
Current.Block.Branch == null) || Current.Block.HasStateStore)
|
||||
{
|
||||
if (!IntPaths.TryGetValue(Current.Block, out PathIo IntPath))
|
||||
{
|
||||
IntPaths.Add(Current.Block, IntPath = new PathIo());
|
||||
}
|
||||
|
||||
if (!VecPaths.TryGetValue(Current.Block, out PathIo VecPath))
|
||||
{
|
||||
VecPaths.Add(Current.Block, VecPath = new PathIo());
|
||||
}
|
||||
|
||||
IntPath.Set(Current.Entry, Current.IntInputs, Current.IntOutputs);
|
||||
VecPath.Set(Current.Entry, Current.VecInputs, Current.VecOutputs);
|
||||
}
|
||||
|
||||
void EnqueueFromCurrent(AILBlock Block, bool RetTarget)
|
||||
{
|
||||
BlockIo BlkIO = new BlockIo() { Block = Block };
|
||||
|
||||
if (RetTarget)
|
||||
{
|
||||
BlkIO.Entry = Block;
|
||||
}
|
||||
else
|
||||
{
|
||||
BlkIO.Entry = Current.Entry;
|
||||
BlkIO.IntInputs = Current.IntInputs;
|
||||
BlkIO.VecInputs = Current.VecInputs;
|
||||
BlkIO.IntOutputs = Current.IntOutputs;
|
||||
BlkIO.VecOutputs = Current.VecOutputs;
|
||||
}
|
||||
|
||||
Enqueue(BlkIO);
|
||||
}
|
||||
|
||||
if (Current.Block.Next != null)
|
||||
{
|
||||
EnqueueFromCurrent(Current.Block.Next, Current.Block.HasStateStore);
|
||||
}
|
||||
|
||||
if (Current.Block.Branch != null)
|
||||
{
|
||||
EnqueueFromCurrent(Current.Block.Branch, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeFast(AILBlock[] Graph)
|
||||
{
|
||||
//This is WAY faster than InitializeOptimal, but results in
|
||||
//uneeded loads and stores, so the resulting code will be slower.
|
||||
long IntInputs = 0, IntOutputs = 0;
|
||||
long VecInputs = 0, VecOutputs = 0;
|
||||
|
||||
foreach (AILBlock Block in Graph)
|
||||
{
|
||||
IntInputs |= Block.IntInputs;
|
||||
IntOutputs |= Block.IntOutputs;
|
||||
VecInputs |= Block.VecInputs;
|
||||
VecOutputs |= Block.VecOutputs;
|
||||
}
|
||||
|
||||
//It's possible that not all code paths writes to those output registers,
|
||||
//in those cases if we attempt to write an output registers that was
|
||||
//not written, we will be just writing zero and messing up the old register value.
|
||||
//So we just need to ensure that all outputs are loaded.
|
||||
if (Graph.Length > 1)
|
||||
{
|
||||
IntInputs |= IntOutputs;
|
||||
VecInputs |= VecOutputs;
|
||||
}
|
||||
|
||||
foreach (AILBlock Block in Graph)
|
||||
{
|
||||
IntPaths.Add(Block, new PathIo(Block, IntInputs, IntOutputs));
|
||||
VecPaths.Add(Block, new PathIo(Block, VecInputs, VecOutputs));
|
||||
}
|
||||
}
|
||||
|
||||
public long GetIntInputs(AILBlock Root) => GetInputsImpl(Root, IntPaths.Values);
|
||||
public long GetVecInputs(AILBlock Root) => GetInputsImpl(Root, VecPaths.Values);
|
||||
|
||||
private long GetInputsImpl(AILBlock Root, IEnumerable<PathIo> Values)
|
||||
{
|
||||
long Inputs = 0;
|
||||
|
||||
foreach (PathIo Path in Values)
|
||||
{
|
||||
Inputs |= Path.GetInputs(Root);
|
||||
}
|
||||
|
||||
return Inputs;
|
||||
}
|
||||
|
||||
public long GetIntOutputs(AILBlock Block) => IntPaths[Block].GetOutputs();
|
||||
public long GetVecOutputs(AILBlock Block) => VecPaths[Block].GetOutputs();
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
namespace ChocolArm64.Translation
|
||||
{
|
||||
interface IAILEmit
|
||||
{
|
||||
void Emit(AILEmitter Context);
|
||||
}
|
||||
}
|
7
ChocolArm64/Translation/IILEmit.cs
Normal file
7
ChocolArm64/Translation/IILEmit.cs
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace ChocolArm64.Translation
|
||||
{
|
||||
interface IILEmit
|
||||
{
|
||||
void Emit(ILEmitter context);
|
||||
}
|
||||
}
|
7
ChocolArm64/Translation/ILBarrier.cs
Normal file
7
ChocolArm64/Translation/ILBarrier.cs
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace ChocolArm64.Translation
|
||||
{
|
||||
struct ILBarrier : IILEmit
|
||||
{
|
||||
public void Emit(ILEmitter context) { }
|
||||
}
|
||||
}
|
76
ChocolArm64/Translation/ILBlock.cs
Normal file
76
ChocolArm64/Translation/ILBlock.cs
Normal file
|
@ -0,0 +1,76 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace ChocolArm64.Translation
|
||||
{
|
||||
class ILBlock : IILEmit
|
||||
{
|
||||
public long IntInputs { get; private set; }
|
||||
public long IntOutputs { get; private set; }
|
||||
public long IntAwOutputs { get; private set; }
|
||||
|
||||
public long VecInputs { get; private set; }
|
||||
public long VecOutputs { get; private set; }
|
||||
public long VecAwOutputs { get; private set; }
|
||||
|
||||
public bool HasStateStore { get; private set; }
|
||||
|
||||
public List<IILEmit> IlEmitters { get; private set; }
|
||||
|
||||
public ILBlock Next { get; set; }
|
||||
public ILBlock Branch { get; set; }
|
||||
|
||||
public ILBlock()
|
||||
{
|
||||
IlEmitters = new List<IILEmit>();
|
||||
}
|
||||
|
||||
public void Add(IILEmit ilEmitter)
|
||||
{
|
||||
if (ilEmitter is ILBarrier)
|
||||
{
|
||||
//Those barriers are used to separate the groups of CIL
|
||||
//opcodes emitted by each ARM instruction.
|
||||
//We can only consider the new outputs for doing input elimination
|
||||
//after all the CIL opcodes used by the instruction being emitted.
|
||||
IntAwOutputs = IntOutputs;
|
||||
VecAwOutputs = VecOutputs;
|
||||
}
|
||||
else if (ilEmitter is IlOpCodeLoad ld && ILEmitter.IsRegIndex(ld.Index))
|
||||
{
|
||||
switch (ld.IoType)
|
||||
{
|
||||
case IoType.Flag: IntInputs |= ((1L << ld.Index) << 32) & ~IntAwOutputs; break;
|
||||
case IoType.Int: IntInputs |= (1L << ld.Index) & ~IntAwOutputs; break;
|
||||
case IoType.Vector: VecInputs |= (1L << ld.Index) & ~VecAwOutputs; break;
|
||||
}
|
||||
}
|
||||
else if (ilEmitter is IlOpCodeStore st)
|
||||
{
|
||||
if (ILEmitter.IsRegIndex(st.Index))
|
||||
{
|
||||
switch (st.IoType)
|
||||
{
|
||||
case IoType.Flag: IntOutputs |= (1L << st.Index) << 32; break;
|
||||
case IoType.Int: IntOutputs |= 1L << st.Index; break;
|
||||
case IoType.Vector: VecOutputs |= 1L << st.Index; break;
|
||||
}
|
||||
}
|
||||
|
||||
if (st.IoType == IoType.Fields)
|
||||
{
|
||||
HasStateStore = true;
|
||||
}
|
||||
}
|
||||
|
||||
IlEmitters.Add(ilEmitter);
|
||||
}
|
||||
|
||||
public void Emit(ILEmitter context)
|
||||
{
|
||||
foreach (IILEmit ilEmitter in IlEmitters)
|
||||
{
|
||||
ilEmitter.Emit(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
188
ChocolArm64/Translation/ILEmitter.cs
Normal file
188
ChocolArm64/Translation/ILEmitter.cs
Normal file
|
@ -0,0 +1,188 @@
|
|||
using ChocolArm64.Decoders;
|
||||
using ChocolArm64.State;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection.Emit;
|
||||
using System.Runtime.Intrinsics;
|
||||
|
||||
namespace ChocolArm64.Translation
|
||||
{
|
||||
class ILEmitter
|
||||
{
|
||||
public LocalAlloc LocalAlloc { get; private set; }
|
||||
|
||||
public ILGenerator Generator { get; private set; }
|
||||
|
||||
private Dictionary<Register, int> _locals;
|
||||
|
||||
private ILBlock[] _ilBlocks;
|
||||
|
||||
private ILBlock _root;
|
||||
|
||||
private TranslatedSub _subroutine;
|
||||
|
||||
private string _subName;
|
||||
|
||||
private int _localsCount;
|
||||
|
||||
public ILEmitter(Block[] graph, Block root, string subName)
|
||||
{
|
||||
_subName = subName;
|
||||
|
||||
_locals = new Dictionary<Register, int>();
|
||||
|
||||
_ilBlocks = new ILBlock[graph.Length];
|
||||
|
||||
ILBlock GetBlock(int index)
|
||||
{
|
||||
if (index < 0 || index >= _ilBlocks.Length)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (_ilBlocks[index] == null)
|
||||
{
|
||||
_ilBlocks[index] = new ILBlock();
|
||||
}
|
||||
|
||||
return _ilBlocks[index];
|
||||
}
|
||||
|
||||
for (int index = 0; index < _ilBlocks.Length; index++)
|
||||
{
|
||||
ILBlock block = GetBlock(index);
|
||||
|
||||
block.Next = GetBlock(Array.IndexOf(graph, graph[index].Next));
|
||||
block.Branch = GetBlock(Array.IndexOf(graph, graph[index].Branch));
|
||||
}
|
||||
|
||||
_root = _ilBlocks[Array.IndexOf(graph, root)];
|
||||
}
|
||||
|
||||
public ILBlock GetIlBlock(int index) => _ilBlocks[index];
|
||||
|
||||
public TranslatedSub GetSubroutine()
|
||||
{
|
||||
LocalAlloc = new LocalAlloc(_ilBlocks, _root);
|
||||
|
||||
InitSubroutine();
|
||||
InitLocals();
|
||||
|
||||
foreach (ILBlock ilBlock in _ilBlocks)
|
||||
{
|
||||
ilBlock.Emit(this);
|
||||
}
|
||||
|
||||
return _subroutine;
|
||||
}
|
||||
|
||||
private void InitSubroutine()
|
||||
{
|
||||
List<Register> Params = new List<Register>();
|
||||
|
||||
void SetParams(long inputs, RegisterType baseType)
|
||||
{
|
||||
for (int bit = 0; bit < 64; bit++)
|
||||
{
|
||||
long mask = 1L << bit;
|
||||
|
||||
if ((inputs & mask) != 0)
|
||||
{
|
||||
Params.Add(GetRegFromBit(bit, baseType));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SetParams(LocalAlloc.GetIntInputs(_root), RegisterType.Int);
|
||||
SetParams(LocalAlloc.GetVecInputs(_root), RegisterType.Vector);
|
||||
|
||||
DynamicMethod mthd = new DynamicMethod(_subName, typeof(long), GetParamTypes(Params));
|
||||
|
||||
Generator = mthd.GetILGenerator();
|
||||
|
||||
_subroutine = new TranslatedSub(mthd, Params);
|
||||
}
|
||||
|
||||
private void InitLocals()
|
||||
{
|
||||
int paramsStart = TranslatedSub.FixedArgTypes.Length;
|
||||
|
||||
_locals = new Dictionary<Register, int>();
|
||||
|
||||
for (int index = 0; index < _subroutine.Params.Count; index++)
|
||||
{
|
||||
Register reg = _subroutine.Params[index];
|
||||
|
||||
Generator.EmitLdarg(index + paramsStart);
|
||||
Generator.EmitStloc(GetLocalIndex(reg));
|
||||
}
|
||||
}
|
||||
|
||||
private Type[] GetParamTypes(IList<Register> Params)
|
||||
{
|
||||
Type[] fixedArgs = TranslatedSub.FixedArgTypes;
|
||||
|
||||
Type[] output = new Type[Params.Count + fixedArgs.Length];
|
||||
|
||||
fixedArgs.CopyTo(output, 0);
|
||||
|
||||
int typeIdx = fixedArgs.Length;
|
||||
|
||||
for (int index = 0; index < Params.Count; index++)
|
||||
{
|
||||
output[typeIdx++] = GetFieldType(Params[index].Type);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
public int GetLocalIndex(Register reg)
|
||||
{
|
||||
if (!_locals.TryGetValue(reg, out int index))
|
||||
{
|
||||
Generator.DeclareLocal(GetLocalType(reg));
|
||||
|
||||
index = _localsCount++;
|
||||
|
||||
_locals.Add(reg, index);
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
public Type GetLocalType(Register reg) => GetFieldType(reg.Type);
|
||||
|
||||
public Type GetFieldType(RegisterType regType)
|
||||
{
|
||||
switch (regType)
|
||||
{
|
||||
case RegisterType.Flag: return typeof(bool);
|
||||
case RegisterType.Int: return typeof(ulong);
|
||||
case RegisterType.Vector: return typeof(Vector128<float>);
|
||||
}
|
||||
|
||||
throw new ArgumentException(nameof(regType));
|
||||
}
|
||||
|
||||
public static Register GetRegFromBit(int bit, RegisterType baseType)
|
||||
{
|
||||
if (bit < 32)
|
||||
{
|
||||
return new Register(bit, baseType);
|
||||
}
|
||||
else if (baseType == RegisterType.Int)
|
||||
{
|
||||
return new Register(bit & 0x1f, RegisterType.Flag);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(bit));
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsRegIndex(int index)
|
||||
{
|
||||
return index >= 0 && index < 32;
|
||||
}
|
||||
}
|
||||
}
|
554
ChocolArm64/Translation/ILEmitterCtx.cs
Normal file
554
ChocolArm64/Translation/ILEmitterCtx.cs
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,123 +6,123 @@ namespace ChocolArm64
|
|||
|
||||
static class ILGeneratorEx
|
||||
{
|
||||
public static void EmitLdc_I4(this ILGenerator Generator, int Value)
|
||||
public static void EmitLdc_I4(this ILGenerator generator, int value)
|
||||
{
|
||||
switch (Value)
|
||||
switch (value)
|
||||
{
|
||||
case 0: Generator.Emit(OpCodes.Ldc_I4_0); break;
|
||||
case 1: Generator.Emit(OpCodes.Ldc_I4_1); break;
|
||||
case 2: Generator.Emit(OpCodes.Ldc_I4_2); break;
|
||||
case 3: Generator.Emit(OpCodes.Ldc_I4_3); break;
|
||||
case 4: Generator.Emit(OpCodes.Ldc_I4_4); break;
|
||||
case 5: Generator.Emit(OpCodes.Ldc_I4_5); break;
|
||||
case 6: Generator.Emit(OpCodes.Ldc_I4_6); break;
|
||||
case 7: Generator.Emit(OpCodes.Ldc_I4_7); break;
|
||||
case 8: Generator.Emit(OpCodes.Ldc_I4_8); break;
|
||||
case -1: Generator.Emit(OpCodes.Ldc_I4_M1); break;
|
||||
default: Generator.Emit(OpCodes.Ldc_I4, Value); break;
|
||||
case 0: generator.Emit(OpCodes.Ldc_I4_0); break;
|
||||
case 1: generator.Emit(OpCodes.Ldc_I4_1); break;
|
||||
case 2: generator.Emit(OpCodes.Ldc_I4_2); break;
|
||||
case 3: generator.Emit(OpCodes.Ldc_I4_3); break;
|
||||
case 4: generator.Emit(OpCodes.Ldc_I4_4); break;
|
||||
case 5: generator.Emit(OpCodes.Ldc_I4_5); break;
|
||||
case 6: generator.Emit(OpCodes.Ldc_I4_6); break;
|
||||
case 7: generator.Emit(OpCodes.Ldc_I4_7); break;
|
||||
case 8: generator.Emit(OpCodes.Ldc_I4_8); break;
|
||||
case -1: generator.Emit(OpCodes.Ldc_I4_M1); break;
|
||||
default: generator.Emit(OpCodes.Ldc_I4, value); break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void EmitLdarg(this ILGenerator Generator, int Index)
|
||||
public static void EmitLdarg(this ILGenerator generator, int index)
|
||||
{
|
||||
switch (Index)
|
||||
switch (index)
|
||||
{
|
||||
case 0: Generator.Emit(OpCodes.Ldarg_0); break;
|
||||
case 1: Generator.Emit(OpCodes.Ldarg_1); break;
|
||||
case 2: Generator.Emit(OpCodes.Ldarg_2); break;
|
||||
case 3: Generator.Emit(OpCodes.Ldarg_3); break;
|
||||
case 0: generator.Emit(OpCodes.Ldarg_0); break;
|
||||
case 1: generator.Emit(OpCodes.Ldarg_1); break;
|
||||
case 2: generator.Emit(OpCodes.Ldarg_2); break;
|
||||
case 3: generator.Emit(OpCodes.Ldarg_3); break;
|
||||
|
||||
default:
|
||||
if ((uint)Index <= byte.MaxValue)
|
||||
if ((uint)index <= byte.MaxValue)
|
||||
{
|
||||
Generator.Emit(OpCodes.Ldarg_S, (byte)Index);
|
||||
generator.Emit(OpCodes.Ldarg_S, (byte)index);
|
||||
}
|
||||
else if ((uint)Index < ushort.MaxValue)
|
||||
else if ((uint)index < ushort.MaxValue)
|
||||
{
|
||||
Generator.Emit(OpCodes.Ldarg, (short)Index);
|
||||
generator.Emit(OpCodes.Ldarg, (short)index);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(Index));
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void EmitStarg(this ILGenerator Generator, int Index)
|
||||
public static void EmitStarg(this ILGenerator generator, int index)
|
||||
{
|
||||
if ((uint)Index <= byte.MaxValue)
|
||||
if ((uint)index <= byte.MaxValue)
|
||||
{
|
||||
Generator.Emit(OpCodes.Starg_S, (byte)Index);
|
||||
generator.Emit(OpCodes.Starg_S, (byte)index);
|
||||
}
|
||||
else if ((uint)Index < ushort.MaxValue)
|
||||
else if ((uint)index < ushort.MaxValue)
|
||||
{
|
||||
Generator.Emit(OpCodes.Starg, (short)Index);
|
||||
generator.Emit(OpCodes.Starg, (short)index);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(Index));
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
}
|
||||
}
|
||||
|
||||
public static void EmitLdloc(this ILGenerator Generator, int Index)
|
||||
public static void EmitLdloc(this ILGenerator generator, int index)
|
||||
{
|
||||
switch (Index)
|
||||
switch (index)
|
||||
{
|
||||
case 0: Generator.Emit(OpCodes.Ldloc_0); break;
|
||||
case 1: Generator.Emit(OpCodes.Ldloc_1); break;
|
||||
case 2: Generator.Emit(OpCodes.Ldloc_2); break;
|
||||
case 3: Generator.Emit(OpCodes.Ldloc_3); break;
|
||||
case 0: generator.Emit(OpCodes.Ldloc_0); break;
|
||||
case 1: generator.Emit(OpCodes.Ldloc_1); break;
|
||||
case 2: generator.Emit(OpCodes.Ldloc_2); break;
|
||||
case 3: generator.Emit(OpCodes.Ldloc_3); break;
|
||||
|
||||
default:
|
||||
if ((uint)Index <= byte.MaxValue)
|
||||
if ((uint)index <= byte.MaxValue)
|
||||
{
|
||||
Generator.Emit(OpCodes.Ldloc_S, (byte)Index);
|
||||
generator.Emit(OpCodes.Ldloc_S, (byte)index);
|
||||
}
|
||||
else if ((uint)Index < ushort.MaxValue)
|
||||
else if ((uint)index < ushort.MaxValue)
|
||||
{
|
||||
Generator.Emit(OpCodes.Ldloc, (short)Index);
|
||||
generator.Emit(OpCodes.Ldloc, (short)index);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(Index));
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void EmitStloc(this ILGenerator Generator, int Index)
|
||||
public static void EmitStloc(this ILGenerator generator, int index)
|
||||
{
|
||||
switch (Index)
|
||||
switch (index)
|
||||
{
|
||||
case 0: Generator.Emit(OpCodes.Stloc_0); break;
|
||||
case 1: Generator.Emit(OpCodes.Stloc_1); break;
|
||||
case 2: Generator.Emit(OpCodes.Stloc_2); break;
|
||||
case 3: Generator.Emit(OpCodes.Stloc_3); break;
|
||||
case 0: generator.Emit(OpCodes.Stloc_0); break;
|
||||
case 1: generator.Emit(OpCodes.Stloc_1); break;
|
||||
case 2: generator.Emit(OpCodes.Stloc_2); break;
|
||||
case 3: generator.Emit(OpCodes.Stloc_3); break;
|
||||
|
||||
default:
|
||||
if ((uint)Index <= byte.MaxValue)
|
||||
if ((uint)index <= byte.MaxValue)
|
||||
{
|
||||
Generator.Emit(OpCodes.Stloc_S, (byte)Index);
|
||||
generator.Emit(OpCodes.Stloc_S, (byte)index);
|
||||
}
|
||||
else if ((uint)Index < ushort.MaxValue)
|
||||
else if ((uint)index < ushort.MaxValue)
|
||||
{
|
||||
Generator.Emit(OpCodes.Stloc, (short)Index);
|
||||
generator.Emit(OpCodes.Stloc, (short)index);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(Index));
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void EmitLdargSeq(this ILGenerator Generator, int Count)
|
||||
public static void EmitLdargSeq(this ILGenerator generator, int count)
|
||||
{
|
||||
for (int Index = 0; Index < Count; Index++)
|
||||
for (int index = 0; index < count; index++)
|
||||
{
|
||||
Generator.EmitLdarg(Index);
|
||||
generator.EmitLdarg(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
28
ChocolArm64/Translation/ILLabel.cs
Normal file
28
ChocolArm64/Translation/ILLabel.cs
Normal file
|
@ -0,0 +1,28 @@
|
|||
using System.Reflection.Emit;
|
||||
|
||||
namespace ChocolArm64.Translation
|
||||
{
|
||||
class ILLabel : IILEmit
|
||||
{
|
||||
private bool _hasLabel;
|
||||
|
||||
private Label _lbl;
|
||||
|
||||
public void Emit(ILEmitter context)
|
||||
{
|
||||
context.Generator.MarkLabel(GetLabel(context));
|
||||
}
|
||||
|
||||
public Label GetLabel(ILEmitter context)
|
||||
{
|
||||
if (!_hasLabel)
|
||||
{
|
||||
_lbl = context.Generator.DefineLabel();
|
||||
|
||||
_hasLabel = true;
|
||||
}
|
||||
|
||||
return _lbl;
|
||||
}
|
||||
}
|
||||
}
|
19
ChocolArm64/Translation/ILOpCode.cs
Normal file
19
ChocolArm64/Translation/ILOpCode.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
using System.Reflection.Emit;
|
||||
|
||||
namespace ChocolArm64.Translation
|
||||
{
|
||||
struct ILOpCode : IILEmit
|
||||
{
|
||||
private OpCode _ilOp;
|
||||
|
||||
public ILOpCode(OpCode ilOp)
|
||||
{
|
||||
_ilOp = ilOp;
|
||||
}
|
||||
|
||||
public void Emit(ILEmitter context)
|
||||
{
|
||||
context.Generator.Emit(_ilOp);
|
||||
}
|
||||
}
|
||||
}
|
21
ChocolArm64/Translation/ILOpCodeBranch.cs
Normal file
21
ChocolArm64/Translation/ILOpCodeBranch.cs
Normal file
|
@ -0,0 +1,21 @@
|
|||
using System.Reflection.Emit;
|
||||
|
||||
namespace ChocolArm64.Translation
|
||||
{
|
||||
struct ILOpCodeBranch : IILEmit
|
||||
{
|
||||
private OpCode _ilOp;
|
||||
private ILLabel _label;
|
||||
|
||||
public ILOpCodeBranch(OpCode ilOp, ILLabel label)
|
||||
{
|
||||
_ilOp = ilOp;
|
||||
_label = label;
|
||||
}
|
||||
|
||||
public void Emit(ILEmitter context)
|
||||
{
|
||||
context.Generator.Emit(_ilOp, _label.GetLabel(context));
|
||||
}
|
||||
}
|
||||
}
|
20
ChocolArm64/Translation/ILOpCodeCall.cs
Normal file
20
ChocolArm64/Translation/ILOpCodeCall.cs
Normal file
|
@ -0,0 +1,20 @@
|
|||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace ChocolArm64.Translation
|
||||
{
|
||||
struct ILOpCodeCall : IILEmit
|
||||
{
|
||||
private MethodInfo _mthdInfo;
|
||||
|
||||
public ILOpCodeCall(MethodInfo mthdInfo)
|
||||
{
|
||||
_mthdInfo = mthdInfo;
|
||||
}
|
||||
|
||||
public void Emit(ILEmitter context)
|
||||
{
|
||||
context.Generator.Emit(OpCodes.Call, _mthdInfo);
|
||||
}
|
||||
}
|
||||
}
|
65
ChocolArm64/Translation/ILOpCodeConst.cs
Normal file
65
ChocolArm64/Translation/ILOpCodeConst.cs
Normal file
|
@ -0,0 +1,65 @@
|
|||
using System.Reflection.Emit;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace ChocolArm64.Translation
|
||||
{
|
||||
class ILOpCodeConst : IILEmit
|
||||
{
|
||||
[StructLayout(LayoutKind.Explicit, Size = 8)]
|
||||
private struct ImmVal
|
||||
{
|
||||
[FieldOffset(0)] public int I4;
|
||||
[FieldOffset(0)] public long I8;
|
||||
[FieldOffset(0)] public float R4;
|
||||
[FieldOffset(0)] public double R8;
|
||||
}
|
||||
|
||||
private ImmVal _value;
|
||||
|
||||
private enum ConstType
|
||||
{
|
||||
Int32,
|
||||
Int64,
|
||||
Single,
|
||||
Double
|
||||
}
|
||||
|
||||
private ConstType _type;
|
||||
|
||||
private ILOpCodeConst(ConstType type)
|
||||
{
|
||||
_type = type;
|
||||
}
|
||||
|
||||
public ILOpCodeConst(int value) : this(ConstType.Int32)
|
||||
{
|
||||
_value = new ImmVal { I4 = value };
|
||||
}
|
||||
|
||||
public ILOpCodeConst(long value) : this(ConstType.Int64)
|
||||
{
|
||||
_value = new ImmVal { I8 = value };
|
||||
}
|
||||
|
||||
public ILOpCodeConst(float value) : this(ConstType.Single)
|
||||
{
|
||||
_value = new ImmVal { R4 = value };
|
||||
}
|
||||
|
||||
public ILOpCodeConst(double value) : this(ConstType.Double)
|
||||
{
|
||||
_value = new ImmVal { R8 = value };
|
||||
}
|
||||
|
||||
public void Emit(ILEmitter context)
|
||||
{
|
||||
switch (_type)
|
||||
{
|
||||
case ConstType.Int32: context.Generator.EmitLdc_I4(_value.I4); break;
|
||||
case ConstType.Int64: context.Generator.Emit(OpCodes.Ldc_I8, _value.I8); break;
|
||||
case ConstType.Single: context.Generator.Emit(OpCodes.Ldc_R4, _value.R4); break;
|
||||
case ConstType.Double: context.Generator.Emit(OpCodes.Ldc_R8, _value.R8); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
75
ChocolArm64/Translation/ILOpCodeLoad.cs
Normal file
75
ChocolArm64/Translation/ILOpCodeLoad.cs
Normal file
|
@ -0,0 +1,75 @@
|
|||
using ChocolArm64.State;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace ChocolArm64.Translation
|
||||
{
|
||||
struct IlOpCodeLoad : IILEmit
|
||||
{
|
||||
public int Index { get; private set; }
|
||||
|
||||
public IoType IoType { get; private set; }
|
||||
|
||||
public RegisterSize RegisterSize { get; private set; }
|
||||
|
||||
public IlOpCodeLoad(int index, IoType ioType, RegisterSize registerSize = 0)
|
||||
{
|
||||
Index = index;
|
||||
IoType = ioType;
|
||||
RegisterSize = registerSize;
|
||||
}
|
||||
|
||||
public void Emit(ILEmitter context)
|
||||
{
|
||||
switch (IoType)
|
||||
{
|
||||
case IoType.Arg: context.Generator.EmitLdarg(Index); break;
|
||||
|
||||
case IoType.Fields:
|
||||
{
|
||||
long intInputs = context.LocalAlloc.GetIntInputs(context.GetIlBlock(Index));
|
||||
long vecInputs = context.LocalAlloc.GetVecInputs(context.GetIlBlock(Index));
|
||||
|
||||
LoadLocals(context, intInputs, RegisterType.Int);
|
||||
LoadLocals(context, vecInputs, RegisterType.Vector);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case IoType.Flag: EmitLdloc(context, Index, RegisterType.Flag); break;
|
||||
case IoType.Int: EmitLdloc(context, Index, RegisterType.Int); break;
|
||||
case IoType.Vector: EmitLdloc(context, Index, RegisterType.Vector); break;
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadLocals(ILEmitter context, long inputs, RegisterType baseType)
|
||||
{
|
||||
for (int bit = 0; bit < 64; bit++)
|
||||
{
|
||||
long mask = 1L << bit;
|
||||
|
||||
if ((inputs & mask) != 0)
|
||||
{
|
||||
Register reg = ILEmitter.GetRegFromBit(bit, baseType);
|
||||
|
||||
context.Generator.EmitLdarg(TranslatedSub.StateArgIdx);
|
||||
context.Generator.Emit(OpCodes.Ldfld, reg.GetField());
|
||||
|
||||
context.Generator.EmitStloc(context.GetLocalIndex(reg));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void EmitLdloc(ILEmitter context, int index, RegisterType registerType)
|
||||
{
|
||||
Register reg = new Register(index, registerType);
|
||||
|
||||
context.Generator.EmitLdloc(context.GetLocalIndex(reg));
|
||||
|
||||
if (registerType == RegisterType.Int &&
|
||||
RegisterSize == RegisterSize.Int32)
|
||||
{
|
||||
context.Generator.Emit(OpCodes.Conv_U4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
17
ChocolArm64/Translation/ILOpCodeLog.cs
Normal file
17
ChocolArm64/Translation/ILOpCodeLog.cs
Normal file
|
@ -0,0 +1,17 @@
|
|||
namespace ChocolArm64.Translation
|
||||
{
|
||||
struct ILOpCodeLog : IILEmit
|
||||
{
|
||||
private string _text;
|
||||
|
||||
public ILOpCodeLog(string text)
|
||||
{
|
||||
_text = text;
|
||||
}
|
||||
|
||||
public void Emit(ILEmitter context)
|
||||
{
|
||||
context.Generator.EmitWriteLine(_text);
|
||||
}
|
||||
}
|
||||
}
|
75
ChocolArm64/Translation/ILOpCodeStore.cs
Normal file
75
ChocolArm64/Translation/ILOpCodeStore.cs
Normal file
|
@ -0,0 +1,75 @@
|
|||
using ChocolArm64.State;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace ChocolArm64.Translation
|
||||
{
|
||||
struct IlOpCodeStore : IILEmit
|
||||
{
|
||||
public int Index { get; private set; }
|
||||
|
||||
public IoType IoType { get; private set; }
|
||||
|
||||
public RegisterSize RegisterSize { get; private set; }
|
||||
|
||||
public IlOpCodeStore(int index, IoType ioType, RegisterSize registerSize = 0)
|
||||
{
|
||||
Index = index;
|
||||
IoType = ioType;
|
||||
RegisterSize = registerSize;
|
||||
}
|
||||
|
||||
public void Emit(ILEmitter context)
|
||||
{
|
||||
switch (IoType)
|
||||
{
|
||||
case IoType.Arg: context.Generator.EmitStarg(Index); break;
|
||||
|
||||
case IoType.Fields:
|
||||
{
|
||||
long intOutputs = context.LocalAlloc.GetIntOutputs(context.GetIlBlock(Index));
|
||||
long vecOutputs = context.LocalAlloc.GetVecOutputs(context.GetIlBlock(Index));
|
||||
|
||||
StoreLocals(context, intOutputs, RegisterType.Int);
|
||||
StoreLocals(context, vecOutputs, RegisterType.Vector);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case IoType.Flag: EmitStloc(context, Index, RegisterType.Flag); break;
|
||||
case IoType.Int: EmitStloc(context, Index, RegisterType.Int); break;
|
||||
case IoType.Vector: EmitStloc(context, Index, RegisterType.Vector); break;
|
||||
}
|
||||
}
|
||||
|
||||
private void StoreLocals(ILEmitter context, long outputs, RegisterType baseType)
|
||||
{
|
||||
for (int bit = 0; bit < 64; bit++)
|
||||
{
|
||||
long mask = 1L << bit;
|
||||
|
||||
if ((outputs & mask) != 0)
|
||||
{
|
||||
Register reg = ILEmitter.GetRegFromBit(bit, baseType);
|
||||
|
||||
context.Generator.EmitLdarg(TranslatedSub.StateArgIdx);
|
||||
context.Generator.EmitLdloc(context.GetLocalIndex(reg));
|
||||
|
||||
context.Generator.Emit(OpCodes.Stfld, reg.GetField());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void EmitStloc(ILEmitter context, int index, RegisterType registerType)
|
||||
{
|
||||
Register reg = new Register(index, registerType);
|
||||
|
||||
if (registerType == RegisterType.Int &&
|
||||
RegisterSize == RegisterSize.Int32)
|
||||
{
|
||||
context.Generator.Emit(OpCodes.Conv_U8);
|
||||
}
|
||||
|
||||
context.Generator.EmitStloc(context.GetLocalIndex(reg));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,7 +3,7 @@ using System;
|
|||
namespace ChocolArm64.Translation
|
||||
{
|
||||
[Flags]
|
||||
enum AIoType
|
||||
enum IoType
|
||||
{
|
||||
Arg,
|
||||
Fields,
|
229
ChocolArm64/Translation/LocalAlloc.cs
Normal file
229
ChocolArm64/Translation/LocalAlloc.cs
Normal file
|
@ -0,0 +1,229 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace ChocolArm64.Translation
|
||||
{
|
||||
class LocalAlloc
|
||||
{
|
||||
private class PathIo
|
||||
{
|
||||
private Dictionary<ILBlock, long> _allInputs;
|
||||
private Dictionary<ILBlock, long> _cmnOutputs;
|
||||
|
||||
private long _allOutputs;
|
||||
|
||||
public PathIo()
|
||||
{
|
||||
_allInputs = new Dictionary<ILBlock, long>();
|
||||
_cmnOutputs = new Dictionary<ILBlock, long>();
|
||||
}
|
||||
|
||||
public PathIo(ILBlock root, long inputs, long outputs) : this()
|
||||
{
|
||||
Set(root, inputs, outputs);
|
||||
}
|
||||
|
||||
public void Set(ILBlock root, long inputs, long outputs)
|
||||
{
|
||||
if (!_allInputs.TryAdd(root, inputs))
|
||||
{
|
||||
_allInputs[root] |= inputs;
|
||||
}
|
||||
|
||||
if (!_cmnOutputs.TryAdd(root, outputs))
|
||||
{
|
||||
_cmnOutputs[root] &= outputs;
|
||||
}
|
||||
|
||||
_allOutputs |= outputs;
|
||||
}
|
||||
|
||||
public long GetInputs(ILBlock root)
|
||||
{
|
||||
if (_allInputs.TryGetValue(root, out long inputs))
|
||||
{
|
||||
return inputs | (_allOutputs & ~_cmnOutputs[root]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long GetOutputs()
|
||||
{
|
||||
return _allOutputs;
|
||||
}
|
||||
}
|
||||
|
||||
private Dictionary<ILBlock, PathIo> _intPaths;
|
||||
private Dictionary<ILBlock, PathIo> _vecPaths;
|
||||
|
||||
private struct BlockIo
|
||||
{
|
||||
public ILBlock Block;
|
||||
public ILBlock Entry;
|
||||
|
||||
public long IntInputs;
|
||||
public long VecInputs;
|
||||
public long IntOutputs;
|
||||
public long VecOutputs;
|
||||
}
|
||||
|
||||
private const int MaxOptGraphLength = 40;
|
||||
|
||||
public LocalAlloc(ILBlock[] graph, ILBlock root)
|
||||
{
|
||||
_intPaths = new Dictionary<ILBlock, PathIo>();
|
||||
_vecPaths = new Dictionary<ILBlock, PathIo>();
|
||||
|
||||
if (graph.Length > 1 &&
|
||||
graph.Length < MaxOptGraphLength)
|
||||
{
|
||||
InitializeOptimal(graph, root);
|
||||
}
|
||||
else
|
||||
{
|
||||
InitializeFast(graph);
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeOptimal(ILBlock[] graph, ILBlock root)
|
||||
{
|
||||
//This will go through all possible paths on the graph,
|
||||
//and store all inputs/outputs for each block. A register
|
||||
//that was previously written to already is not considered an input.
|
||||
//When a block can be reached by more than one path, then the
|
||||
//output from all paths needs to be set for this block, and
|
||||
//only outputs present in all of the parent blocks can be considered
|
||||
//when doing input elimination. Each block chain have a root, that's where
|
||||
//the code starts executing. They are present on the subroutine start point,
|
||||
//and on call return points too (address written to X30 by BL).
|
||||
HashSet<BlockIo> visited = new HashSet<BlockIo>();
|
||||
|
||||
Queue<BlockIo> unvisited = new Queue<BlockIo>();
|
||||
|
||||
void Enqueue(BlockIo block)
|
||||
{
|
||||
if (!visited.Contains(block))
|
||||
{
|
||||
unvisited.Enqueue(block);
|
||||
|
||||
visited.Add(block);
|
||||
}
|
||||
}
|
||||
|
||||
Enqueue(new BlockIo()
|
||||
{
|
||||
Block = root,
|
||||
Entry = root
|
||||
});
|
||||
|
||||
while (unvisited.Count > 0)
|
||||
{
|
||||
BlockIo current = unvisited.Dequeue();
|
||||
|
||||
current.IntInputs |= current.Block.IntInputs & ~current.IntOutputs;
|
||||
current.VecInputs |= current.Block.VecInputs & ~current.VecOutputs;
|
||||
current.IntOutputs |= current.Block.IntOutputs;
|
||||
current.VecOutputs |= current.Block.VecOutputs;
|
||||
|
||||
//Check if this is a exit block
|
||||
//(a block that returns or calls another sub).
|
||||
if ((current.Block.Next == null &&
|
||||
current.Block.Branch == null) || current.Block.HasStateStore)
|
||||
{
|
||||
if (!_intPaths.TryGetValue(current.Block, out PathIo intPath))
|
||||
{
|
||||
_intPaths.Add(current.Block, intPath = new PathIo());
|
||||
}
|
||||
|
||||
if (!_vecPaths.TryGetValue(current.Block, out PathIo vecPath))
|
||||
{
|
||||
_vecPaths.Add(current.Block, vecPath = new PathIo());
|
||||
}
|
||||
|
||||
intPath.Set(current.Entry, current.IntInputs, current.IntOutputs);
|
||||
vecPath.Set(current.Entry, current.VecInputs, current.VecOutputs);
|
||||
}
|
||||
|
||||
void EnqueueFromCurrent(ILBlock block, bool retTarget)
|
||||
{
|
||||
BlockIo blkIO = new BlockIo() { Block = block };
|
||||
|
||||
if (retTarget)
|
||||
{
|
||||
blkIO.Entry = block;
|
||||
}
|
||||
else
|
||||
{
|
||||
blkIO.Entry = current.Entry;
|
||||
blkIO.IntInputs = current.IntInputs;
|
||||
blkIO.VecInputs = current.VecInputs;
|
||||
blkIO.IntOutputs = current.IntOutputs;
|
||||
blkIO.VecOutputs = current.VecOutputs;
|
||||
}
|
||||
|
||||
Enqueue(blkIO);
|
||||
}
|
||||
|
||||
if (current.Block.Next != null)
|
||||
{
|
||||
EnqueueFromCurrent(current.Block.Next, current.Block.HasStateStore);
|
||||
}
|
||||
|
||||
if (current.Block.Branch != null)
|
||||
{
|
||||
EnqueueFromCurrent(current.Block.Branch, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeFast(ILBlock[] graph)
|
||||
{
|
||||
//This is WAY faster than InitializeOptimal, but results in
|
||||
//uneeded loads and stores, so the resulting code will be slower.
|
||||
long intInputs = 0, intOutputs = 0;
|
||||
long vecInputs = 0, vecOutputs = 0;
|
||||
|
||||
foreach (ILBlock block in graph)
|
||||
{
|
||||
intInputs |= block.IntInputs;
|
||||
intOutputs |= block.IntOutputs;
|
||||
vecInputs |= block.VecInputs;
|
||||
vecOutputs |= block.VecOutputs;
|
||||
}
|
||||
|
||||
//It's possible that not all code paths writes to those output registers,
|
||||
//in those cases if we attempt to write an output registers that was
|
||||
//not written, we will be just writing zero and messing up the old register value.
|
||||
//So we just need to ensure that all outputs are loaded.
|
||||
if (graph.Length > 1)
|
||||
{
|
||||
intInputs |= intOutputs;
|
||||
vecInputs |= vecOutputs;
|
||||
}
|
||||
|
||||
foreach (ILBlock block in graph)
|
||||
{
|
||||
_intPaths.Add(block, new PathIo(block, intInputs, intOutputs));
|
||||
_vecPaths.Add(block, new PathIo(block, vecInputs, vecOutputs));
|
||||
}
|
||||
}
|
||||
|
||||
public long GetIntInputs(ILBlock root) => GetInputsImpl(root, _intPaths.Values);
|
||||
public long GetVecInputs(ILBlock root) => GetInputsImpl(root, _vecPaths.Values);
|
||||
|
||||
private long GetInputsImpl(ILBlock root, IEnumerable<PathIo> values)
|
||||
{
|
||||
long inputs = 0;
|
||||
|
||||
foreach (PathIo path in values)
|
||||
{
|
||||
inputs |= path.GetInputs(root);
|
||||
}
|
||||
|
||||
return inputs;
|
||||
}
|
||||
|
||||
public long GetIntOutputs(ILBlock block) => _intPaths[block].GetOutputs();
|
||||
public long GetVecOutputs(ILBlock block) => _vecPaths[block].GetOutputs();
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue