Misc. CPU improvements (#519)
* Fix and simplify TranslatorCache * Fix some assignment alignments, remove some unused usings * Changes to ILEmitter, separate it from ILEmitterCtx * Rename ILEmitter to ILMethodBuilder * Rename LdrLit and *_Fix opcodes * Revert TranslatorCache impl to the more performant one, fix a few issues with it * Allow EmitOpCode to be called even after everything has been emitted * Make Emit and AdvanceOpCode private, simplify it a bit now that it starts emiting from the entry point * Remove unneeded temp use * Add missing exit call on TestExclusive * Use better hash * Implement the == and != operators
This commit is contained in:
parent
f1529b1bc2
commit
36e8e074c9
41 changed files with 943 additions and 915 deletions
|
@ -28,11 +28,11 @@ namespace ChocolArm64.Decoders
|
||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static (Block[] Graph, Block Root) DecodeSubroutine(
|
public static Block DecodeSubroutine(
|
||||||
TranslatorCache cache,
|
TranslatorCache cache,
|
||||||
CpuThreadState state,
|
CpuThreadState state,
|
||||||
MemoryManager memory,
|
MemoryManager memory,
|
||||||
long start)
|
long start)
|
||||||
{
|
{
|
||||||
Dictionary<long, Block> visited = new Dictionary<long, Block>();
|
Dictionary<long, Block> visited = new Dictionary<long, Block>();
|
||||||
Dictionary<long, Block> visitedEnd = new Dictionary<long, Block>();
|
Dictionary<long, Block> visitedEnd = new Dictionary<long, Block>();
|
||||||
|
@ -53,7 +53,7 @@ namespace ChocolArm64.Decoders
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
Block root = Enqueue(start);
|
Block entry = Enqueue(start);
|
||||||
|
|
||||||
while (blocks.Count > 0)
|
while (blocks.Count > 0)
|
||||||
{
|
{
|
||||||
|
@ -118,33 +118,7 @@ namespace ChocolArm64.Decoders
|
||||||
visitedEnd.Add(current.EndPosition, current);
|
visitedEnd.Add(current.EndPosition, current);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Make and sort Graph blocks array by position.
|
return entry;
|
||||||
Block[] graph = new Block[visited.Count];
|
|
||||||
|
|
||||||
while (visited.Count > 0)
|
|
||||||
{
|
|
||||||
ulong firstPos = ulong.MaxValue;
|
|
||||||
|
|
||||||
foreach (Block block in visited.Values)
|
|
||||||
{
|
|
||||||
if (firstPos > (ulong)block.Position)
|
|
||||||
firstPos = (ulong)block.Position;
|
|
||||||
}
|
|
||||||
|
|
||||||
Block current = visited[(long)firstPos];
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
graph[graph.Length - visited.Count] = current;
|
|
||||||
|
|
||||||
visited.Remove(current.Position);
|
|
||||||
|
|
||||||
current = current.Next;
|
|
||||||
}
|
|
||||||
while (current != null);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (graph, root);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void FillBlock(CpuThreadState state, MemoryManager memory, Block block)
|
private static void FillBlock(CpuThreadState state, MemoryManager memory, Block block)
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
using ChocolArm64.Instructions;
|
using ChocolArm64.Instructions;
|
||||||
using ChocolArm64.State;
|
|
||||||
|
|
||||||
namespace ChocolArm64.Decoders
|
namespace ChocolArm64.Decoders
|
||||||
{
|
{
|
||||||
|
@ -12,8 +11,8 @@ namespace ChocolArm64.Decoders
|
||||||
|
|
||||||
public OpCodeAlu64(Inst inst, long position, int opCode) : base(inst, position, opCode)
|
public OpCodeAlu64(Inst inst, long position, int opCode) : base(inst, position, opCode)
|
||||||
{
|
{
|
||||||
Rd = (opCode >> 0) & 0x1f;
|
Rd = (opCode >> 0) & 0x1f;
|
||||||
Rn = (opCode >> 5) & 0x1f;
|
Rn = (opCode >> 5) & 0x1f;
|
||||||
DataOp = (DataOp)((opCode >> 24) & 0x3);
|
DataOp = (DataOp)((opCode >> 24) & 0x3);
|
||||||
|
|
||||||
RegisterSize = (opCode >> 31) != 0
|
RegisterSize = (opCode >> 31) != 0
|
||||||
|
|
|
@ -22,7 +22,7 @@ namespace ChocolArm64.Decoders
|
||||||
|
|
||||||
Shift = shift;
|
Shift = shift;
|
||||||
|
|
||||||
Rm = (opCode >> 16) & 0x1f;
|
Rm = (opCode >> 16) & 0x1f;
|
||||||
ShiftType = (ShiftType)((opCode >> 22) & 0x3);
|
ShiftType = (ShiftType)((opCode >> 22) & 0x3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,9 +11,9 @@ namespace ChocolArm64.Decoders
|
||||||
|
|
||||||
public OpCodeAluRx64(Inst inst, long position, int opCode) : base(inst, position, opCode)
|
public OpCodeAluRx64(Inst inst, long position, int opCode) : base(inst, position, opCode)
|
||||||
{
|
{
|
||||||
Shift = (opCode >> 10) & 0x7;
|
Shift = (opCode >> 10) & 0x7;
|
||||||
IntType = (IntType)((opCode >> 13) & 0x7);
|
IntType = (IntType)((opCode >> 13) & 0x7);
|
||||||
Rm = (opCode >> 16) & 0x1f;
|
Rm = (opCode >> 16) & 0x1f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,5 +1,4 @@
|
||||||
using ChocolArm64.Instructions;
|
using ChocolArm64.Instructions;
|
||||||
using ChocolArm64.State;
|
|
||||||
|
|
||||||
namespace ChocolArm64.Decoders
|
namespace ChocolArm64.Decoders
|
||||||
{
|
{
|
||||||
|
|
|
@ -21,9 +21,9 @@ namespace ChocolArm64.Decoders
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Nzcv = (opCode >> 0) & 0xf;
|
Nzcv = (opCode >> 0) & 0xf;
|
||||||
Cond = (Cond)((opCode >> 12) & 0xf);
|
Cond = (Cond)((opCode >> 12) & 0xf);
|
||||||
RmImm = (opCode >> 16) & 0x1f;
|
RmImm = (opCode >> 16) & 0x1f;
|
||||||
|
|
||||||
Rd = CpuThreadState.ZrIndex;
|
Rd = CpuThreadState.ZrIndex;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ namespace ChocolArm64.Decoders
|
||||||
|
|
||||||
public OpCodeCsel64(Inst inst, long position, int opCode) : base(inst, position, opCode)
|
public OpCodeCsel64(Inst inst, long position, int opCode) : base(inst, position, opCode)
|
||||||
{
|
{
|
||||||
Rm = (opCode >> 16) & 0x1f;
|
Rm = (opCode >> 16) & 0x1f;
|
||||||
Cond = (Cond)((opCode >> 12) & 0xf);
|
Cond = (Cond)((opCode >> 12) & 0xf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,7 @@ namespace ChocolArm64.Decoders
|
||||||
if (WBack || Unscaled)
|
if (WBack || Unscaled)
|
||||||
{
|
{
|
||||||
//9-bits Signed Immediate.
|
//9-bits Signed Immediate.
|
||||||
Imm = (opCode << 43) >> 55;
|
Imm = (opCode << 11) >> 23;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -11,10 +11,10 @@ namespace ChocolArm64.Decoders
|
||||||
|
|
||||||
public OpCodeMemReg64(Inst inst, long position, int opCode) : base(inst, position, opCode)
|
public OpCodeMemReg64(Inst inst, long position, int opCode) : base(inst, position, opCode)
|
||||||
{
|
{
|
||||||
Shift = ((opCode >> 12) & 0x1) != 0;
|
Shift = ((opCode >> 12) & 0x1) != 0;
|
||||||
IntType = (IntType)((opCode >> 13) & 0x7);
|
IntType = (IntType)((opCode >> 13) & 0x7);
|
||||||
Rm = (opCode >> 16) & 0x1f;
|
Rm = (opCode >> 16) & 0x1f;
|
||||||
Extend64 = ((opCode >> 22) & 0x3) == 2;
|
Extend64 = ((opCode >> 22) & 0x3) == 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,5 +1,4 @@
|
||||||
using ChocolArm64.Instructions;
|
using ChocolArm64.Instructions;
|
||||||
using ChocolArm64.State;
|
|
||||||
|
|
||||||
namespace ChocolArm64.Decoders
|
namespace ChocolArm64.Decoders
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
using ChocolArm64.Instructions;
|
using ChocolArm64.Instructions;
|
||||||
using ChocolArm64.State;
|
|
||||||
|
|
||||||
namespace ChocolArm64.Decoders
|
namespace ChocolArm64.Decoders
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
using ChocolArm64.Instructions;
|
using ChocolArm64.Instructions;
|
||||||
using ChocolArm64.State;
|
|
||||||
|
|
||||||
namespace ChocolArm64.Decoders
|
namespace ChocolArm64.Decoders
|
||||||
{
|
{
|
||||||
|
@ -61,12 +60,12 @@ namespace ChocolArm64.Decoders
|
||||||
else if ((modeHigh & 0b110) == 0b100)
|
else if ((modeHigh & 0b110) == 0b100)
|
||||||
{
|
{
|
||||||
//16-bits shifted Immediate.
|
//16-bits shifted Immediate.
|
||||||
Size = 1; imm <<= (modeHigh & 1) << 3;
|
Size = 1; imm <<= (modeHigh & 1) << 3;
|
||||||
}
|
}
|
||||||
else if ((modeHigh & 0b100) == 0b000)
|
else if ((modeHigh & 0b100) == 0b000)
|
||||||
{
|
{
|
||||||
//32-bits shifted Immediate.
|
//32-bits shifted Immediate.
|
||||||
Size = 2; imm <<= modeHigh << 3;
|
Size = 2; imm <<= modeHigh << 3;
|
||||||
}
|
}
|
||||||
else if ((modeHigh & 0b111) == 0b110)
|
else if ((modeHigh & 0b111) == 0b110)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
using ChocolArm64.Instructions;
|
using ChocolArm64.Instructions;
|
||||||
using ChocolArm64.State;
|
|
||||||
|
|
||||||
namespace ChocolArm64.Decoders
|
namespace ChocolArm64.Decoders
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
using ChocolArm64.Instructions;
|
using ChocolArm64.Instructions;
|
||||||
using ChocolArm64.State;
|
|
||||||
|
|
||||||
namespace ChocolArm64.Decoders
|
namespace ChocolArm64.Decoders
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,9 +2,9 @@ namespace ChocolArm64.Decoders
|
||||||
{
|
{
|
||||||
enum ShiftType
|
enum ShiftType
|
||||||
{
|
{
|
||||||
Lsl,
|
Lsl = 0,
|
||||||
Lsr,
|
Lsr = 1,
|
||||||
Asr,
|
Asr = 2,
|
||||||
Ror
|
Ror = 3
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -48,7 +48,7 @@ namespace ChocolArm64.Instructions
|
||||||
|
|
||||||
if (context.CurrBlock.Next != null)
|
if (context.CurrBlock.Next != null)
|
||||||
{
|
{
|
||||||
context.EmitLoadState(context.CurrBlock.Next);
|
context.EmitLoadState();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -73,7 +73,7 @@ namespace ChocolArm64.Instructions
|
||||||
|
|
||||||
if (context.CurrBlock.Next != null)
|
if (context.CurrBlock.Next != null)
|
||||||
{
|
{
|
||||||
context.EmitLoadState(context.CurrBlock.Next);
|
context.EmitLoadState();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -58,7 +58,7 @@ namespace ChocolArm64.Instructions
|
||||||
|
|
||||||
context.Emit(OpCodes.Pop);
|
context.Emit(OpCodes.Pop);
|
||||||
|
|
||||||
context.EmitLoadState(context.CurrBlock.Next);
|
context.EmitLoadState();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -73,13 +73,10 @@ namespace ChocolArm64.Instructions
|
||||||
OpCodeBReg64 op = (OpCodeBReg64)context.CurrOp;
|
OpCodeBReg64 op = (OpCodeBReg64)context.CurrOp;
|
||||||
|
|
||||||
context.EmitLdintzr(op.Rn);
|
context.EmitLdintzr(op.Rn);
|
||||||
context.EmitSttmp();
|
|
||||||
|
|
||||||
context.EmitLdc_I(op.Position + 4);
|
context.EmitLdc_I(op.Position + 4);
|
||||||
context.EmitStint(CpuThreadState.LrIndex);
|
context.EmitStint(CpuThreadState.LrIndex);
|
||||||
context.EmitStoreState();
|
context.EmitStoreState();
|
||||||
|
|
||||||
context.EmitLdtmp();
|
|
||||||
context.Emit(OpCodes.Ret);
|
context.Emit(OpCodes.Ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -60,7 +60,7 @@ namespace ChocolArm64.Instructions
|
||||||
EmitWBackIfNeeded(context);
|
EmitWBackIfNeeded(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void LdrLit(ILEmitterCtx context)
|
public static void Ldr_Literal(ILEmitterCtx context)
|
||||||
{
|
{
|
||||||
IOpCodeLit64 op = (IOpCodeLit64)context.CurrOp;
|
IOpCodeLit64 op = (IOpCodeLit64)context.CurrOp;
|
||||||
|
|
||||||
|
|
|
@ -244,7 +244,7 @@ namespace ChocolArm64.Instructions
|
||||||
EmitFcvt_s_Gp(context, () => { });
|
EmitFcvt_s_Gp(context, () => { });
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Fcvtzs_Gp_Fix(ILEmitterCtx context)
|
public static void Fcvtzs_Gp_Fixed(ILEmitterCtx context)
|
||||||
{
|
{
|
||||||
EmitFcvtzs_Gp_Fix(context);
|
EmitFcvtzs_Gp_Fix(context);
|
||||||
}
|
}
|
||||||
|
@ -264,7 +264,7 @@ namespace ChocolArm64.Instructions
|
||||||
EmitFcvt_u_Gp(context, () => { });
|
EmitFcvt_u_Gp(context, () => { });
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Fcvtzu_Gp_Fix(ILEmitterCtx context)
|
public static void Fcvtzu_Gp_Fixed(ILEmitterCtx context)
|
||||||
{
|
{
|
||||||
EmitFcvtzu_Gp_Fix(context);
|
EmitFcvtzu_Gp_Fix(context);
|
||||||
}
|
}
|
||||||
|
|
|
@ -119,6 +119,8 @@ namespace ChocolArm64.Memory
|
||||||
|
|
||||||
if (!_monitors.TryGetValue(core, out ArmMonitor threadMon))
|
if (!_monitors.TryGetValue(core, out ArmMonitor threadMon))
|
||||||
{
|
{
|
||||||
|
Monitor.Exit(_monitors);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -24,7 +24,7 @@ namespace ChocolArm64
|
||||||
|
|
||||||
public DynamicMethod Method { get; private set; }
|
public DynamicMethod Method { get; private set; }
|
||||||
|
|
||||||
public ReadOnlyCollection<Register> Params { get; private set; }
|
public ReadOnlyCollection<Register> SubArgs { get; private set; }
|
||||||
|
|
||||||
private HashSet<long> _callers;
|
private HashSet<long> _callers;
|
||||||
|
|
||||||
|
@ -34,20 +34,10 @@ namespace ChocolArm64
|
||||||
|
|
||||||
private bool _needsReJit;
|
private bool _needsReJit;
|
||||||
|
|
||||||
public TranslatedSub(DynamicMethod method, List<Register> Params)
|
public TranslatedSub(DynamicMethod method, List<Register> subArgs)
|
||||||
{
|
{
|
||||||
if (method == null)
|
Method = method ?? throw new ArgumentNullException(nameof(method));;
|
||||||
{
|
SubArgs = subArgs?.AsReadOnly() ?? throw new ArgumentNullException(nameof(subArgs));
|
||||||
throw new ArgumentNullException(nameof(method));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Params == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(Params));
|
|
||||||
}
|
|
||||||
|
|
||||||
Method = method;
|
|
||||||
this.Params = Params.AsReadOnly();
|
|
||||||
|
|
||||||
_callers = new HashSet<long>();
|
_callers = new HashSet<long>();
|
||||||
|
|
||||||
|
@ -89,7 +79,7 @@ namespace ChocolArm64
|
||||||
|
|
||||||
generator.EmitLdargSeq(FixedArgTypes.Length);
|
generator.EmitLdargSeq(FixedArgTypes.Length);
|
||||||
|
|
||||||
foreach (Register reg in Params)
|
foreach (Register reg in SubArgs)
|
||||||
{
|
{
|
||||||
generator.EmitLdarg(StateArgIdx);
|
generator.EmitLdarg(StateArgIdx);
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,6 @@ namespace ChocolArm64.Translation
|
||||||
{
|
{
|
||||||
interface IILEmit
|
interface IILEmit
|
||||||
{
|
{
|
||||||
void Emit(ILEmitter context);
|
void Emit(ILMethodBuilder context);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,6 +2,6 @@ namespace ChocolArm64.Translation
|
||||||
{
|
{
|
||||||
struct ILBarrier : IILEmit
|
struct ILBarrier : IILEmit
|
||||||
{
|
{
|
||||||
public void Emit(ILEmitter context) { }
|
public void Emit(ILMethodBuilder context) { }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -14,19 +14,21 @@ namespace ChocolArm64.Translation
|
||||||
|
|
||||||
public bool HasStateStore { get; private set; }
|
public bool HasStateStore { get; private set; }
|
||||||
|
|
||||||
public List<IILEmit> IlEmitters { get; private set; }
|
private List<IILEmit> _emitters;
|
||||||
|
|
||||||
|
public int Count => _emitters.Count;
|
||||||
|
|
||||||
public ILBlock Next { get; set; }
|
public ILBlock Next { get; set; }
|
||||||
public ILBlock Branch { get; set; }
|
public ILBlock Branch { get; set; }
|
||||||
|
|
||||||
public ILBlock()
|
public ILBlock()
|
||||||
{
|
{
|
||||||
IlEmitters = new List<IILEmit>();
|
_emitters = new List<IILEmit>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Add(IILEmit ilEmitter)
|
public void Add(IILEmit emitter)
|
||||||
{
|
{
|
||||||
if (ilEmitter is ILBarrier)
|
if (emitter is ILBarrier)
|
||||||
{
|
{
|
||||||
//Those barriers are used to separate the groups of CIL
|
//Those barriers are used to separate the groups of CIL
|
||||||
//opcodes emitted by each ARM instruction.
|
//opcodes emitted by each ARM instruction.
|
||||||
|
@ -35,7 +37,7 @@ namespace ChocolArm64.Translation
|
||||||
IntAwOutputs = IntOutputs;
|
IntAwOutputs = IntOutputs;
|
||||||
VecAwOutputs = VecOutputs;
|
VecAwOutputs = VecOutputs;
|
||||||
}
|
}
|
||||||
else if (ilEmitter is IlOpCodeLoad ld && ILEmitter.IsRegIndex(ld.Index))
|
else if (emitter is ILOpCodeLoad ld && ILMethodBuilder.IsRegIndex(ld.Index))
|
||||||
{
|
{
|
||||||
switch (ld.IoType)
|
switch (ld.IoType)
|
||||||
{
|
{
|
||||||
|
@ -44,30 +46,26 @@ namespace ChocolArm64.Translation
|
||||||
case IoType.Vector: VecInputs |= (1L << ld.Index) & ~VecAwOutputs; break;
|
case IoType.Vector: VecInputs |= (1L << ld.Index) & ~VecAwOutputs; break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (ilEmitter is IlOpCodeStore st)
|
else if (emitter is ILOpCodeStore st && ILMethodBuilder.IsRegIndex(st.Index))
|
||||||
{
|
{
|
||||||
if (ILEmitter.IsRegIndex(st.Index))
|
switch (st.IoType)
|
||||||
{
|
{
|
||||||
switch (st.IoType)
|
case IoType.Flag: IntOutputs |= (1L << st.Index) << 32; break;
|
||||||
{
|
case IoType.Int: IntOutputs |= 1L << st.Index; break;
|
||||||
case IoType.Flag: IntOutputs |= (1L << st.Index) << 32; break;
|
case IoType.Vector: VecOutputs |= 1L << st.Index; break;
|
||||||
case IoType.Int: IntOutputs |= 1L << st.Index; break;
|
|
||||||
case IoType.Vector: VecOutputs |= 1L << st.Index; break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (st.IoType == IoType.Fields)
|
|
||||||
{
|
|
||||||
HasStateStore = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (emitter is ILOpCodeStoreState)
|
||||||
|
{
|
||||||
|
HasStateStore = true;
|
||||||
|
}
|
||||||
|
|
||||||
IlEmitters.Add(ilEmitter);
|
_emitters.Add(emitter);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Emit(ILEmitter context)
|
public void Emit(ILMethodBuilder context)
|
||||||
{
|
{
|
||||||
foreach (IILEmit ilEmitter in IlEmitters)
|
foreach (IILEmit ilEmitter in _emitters)
|
||||||
{
|
{
|
||||||
ilEmitter.Emit(context);
|
ilEmitter.Emit(context);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,15 +14,20 @@ namespace ChocolArm64.Translation
|
||||||
|
|
||||||
private Dictionary<long, ILLabel> _labels;
|
private Dictionary<long, ILLabel> _labels;
|
||||||
|
|
||||||
private int _blkIndex;
|
private long _subPosition;
|
||||||
|
|
||||||
private int _opcIndex;
|
private int _opcIndex;
|
||||||
|
|
||||||
private Block[] _graph;
|
private Block _currBlock;
|
||||||
private Block _root;
|
|
||||||
public Block CurrBlock => _graph[_blkIndex];
|
|
||||||
public OpCode64 CurrOp => _graph[_blkIndex].OpCodes[_opcIndex];
|
|
||||||
|
|
||||||
private ILEmitter _emitter;
|
public Block CurrBlock => _currBlock;
|
||||||
|
public OpCode64 CurrOp => _currBlock?.OpCodes[_opcIndex];
|
||||||
|
|
||||||
|
private Dictionary<Block, ILBlock> _visitedBlocks;
|
||||||
|
|
||||||
|
private Queue<Block> _branchTargets;
|
||||||
|
|
||||||
|
private List<ILBlock> _ilBlocks;
|
||||||
|
|
||||||
private ILBlock _ilBlock;
|
private ILBlock _ilBlock;
|
||||||
|
|
||||||
|
@ -33,69 +38,61 @@ namespace ChocolArm64.Translation
|
||||||
//values needed by some functions, since IL doesn't have a swap instruction.
|
//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
|
//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.
|
//for the other registers. Any value >= 64 or < 0 will do.
|
||||||
private const int Tmp1Index = -1;
|
private const int IntTmpIndex = -1;
|
||||||
private const int Tmp2Index = -2;
|
private const int RorTmpIndex = -2;
|
||||||
private const int Tmp3Index = -3;
|
private const int CmpOptTmp1Index = -3;
|
||||||
private const int Tmp4Index = -4;
|
private const int CmpOptTmp2Index = -4;
|
||||||
private const int Tmp5Index = -5;
|
private const int VecTmp1Index = -5;
|
||||||
private const int Tmp6Index = -6;
|
private const int VecTmp2Index = -6;
|
||||||
|
|
||||||
public ILEmitterCtx(
|
public ILEmitterCtx(TranslatorCache cache, Block graph)
|
||||||
TranslatorCache cache,
|
|
||||||
Block[] graph,
|
|
||||||
Block root,
|
|
||||||
string subName)
|
|
||||||
{
|
{
|
||||||
_cache = cache ?? throw new ArgumentNullException(nameof(cache));
|
_cache = cache ?? throw new ArgumentNullException(nameof(cache));
|
||||||
_graph = graph ?? throw new ArgumentNullException(nameof(graph));
|
_currBlock = graph ?? throw new ArgumentNullException(nameof(graph));
|
||||||
_root = root ?? throw new ArgumentNullException(nameof(root));
|
|
||||||
|
|
||||||
_labels = new Dictionary<long, ILLabel>();
|
_labels = new Dictionary<long, ILLabel>();
|
||||||
|
|
||||||
_emitter = new ILEmitter(graph, root, subName);
|
_visitedBlocks = new Dictionary<Block, ILBlock>();
|
||||||
|
|
||||||
_ilBlock = _emitter.GetIlBlock(0);
|
_visitedBlocks.Add(graph, new ILBlock());
|
||||||
|
|
||||||
_opcIndex = -1;
|
_branchTargets = new Queue<Block>();
|
||||||
|
|
||||||
if (graph.Length == 0 || !AdvanceOpCode())
|
_ilBlocks = new List<ILBlock>();
|
||||||
{
|
|
||||||
throw new ArgumentException(nameof(graph));
|
_subPosition = graph.Position;
|
||||||
}
|
|
||||||
|
ResetBlockState();
|
||||||
|
|
||||||
|
AdvanceOpCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
public TranslatedSub GetSubroutine()
|
public ILBlock[] GetILBlocks()
|
||||||
{
|
{
|
||||||
return _emitter.GetSubroutine();
|
EmitAllOpCodes();
|
||||||
|
|
||||||
|
return _ilBlocks.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool AdvanceOpCode()
|
private void EmitAllOpCodes()
|
||||||
{
|
{
|
||||||
if (_opcIndex + 1 == CurrBlock.OpCodes.Count &&
|
do
|
||||||
_blkIndex + 1 == _graph.Length)
|
|
||||||
{
|
{
|
||||||
return false;
|
EmitOpCode();
|
||||||
}
|
}
|
||||||
|
while (AdvanceOpCode());
|
||||||
while (++_opcIndex >= (CurrBlock?.OpCodes.Count ?? 0))
|
|
||||||
{
|
|
||||||
_blkIndex++;
|
|
||||||
_opcIndex = -1;
|
|
||||||
|
|
||||||
_optOpLastFlagSet = null;
|
|
||||||
_optOpLastCompare = null;
|
|
||||||
|
|
||||||
_ilBlock = _emitter.GetIlBlock(_blkIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void EmitOpCode()
|
private void EmitOpCode()
|
||||||
{
|
{
|
||||||
|
if (_currBlock == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (_opcIndex == 0)
|
if (_opcIndex == 0)
|
||||||
{
|
{
|
||||||
MarkLabel(GetLabel(CurrBlock.Position));
|
MarkLabel(GetLabel(_currBlock.Position));
|
||||||
|
|
||||||
EmitSynchronization();
|
EmitSynchronization();
|
||||||
}
|
}
|
||||||
|
@ -109,7 +106,7 @@ namespace ChocolArm64.Translation
|
||||||
{
|
{
|
||||||
EmitLdarg(TranslatedSub.StateArgIdx);
|
EmitLdarg(TranslatedSub.StateArgIdx);
|
||||||
|
|
||||||
EmitLdc_I4(CurrBlock.OpCodes.Count);
|
EmitLdc_I4(_currBlock.OpCodes.Count);
|
||||||
|
|
||||||
EmitPrivateCall(typeof(CpuThreadState), nameof(CpuThreadState.Synchronize));
|
EmitPrivateCall(typeof(CpuThreadState), nameof(CpuThreadState.Synchronize));
|
||||||
|
|
||||||
|
@ -126,9 +123,86 @@ namespace ChocolArm64.Translation
|
||||||
MarkLabel(lblContinue);
|
MarkLabel(lblContinue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool AdvanceOpCode()
|
||||||
|
{
|
||||||
|
if (_currBlock == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (++_opcIndex >= _currBlock.OpCodes.Count)
|
||||||
|
{
|
||||||
|
if (!AdvanceBlock())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResetBlockState();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool AdvanceBlock()
|
||||||
|
{
|
||||||
|
if (_currBlock.Branch != null)
|
||||||
|
{
|
||||||
|
if (_visitedBlocks.TryAdd(_currBlock.Branch, _ilBlock.Branch))
|
||||||
|
{
|
||||||
|
_branchTargets.Enqueue(_currBlock.Branch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_currBlock.Next != null)
|
||||||
|
{
|
||||||
|
if (_visitedBlocks.TryAdd(_currBlock.Next, _ilBlock.Next))
|
||||||
|
{
|
||||||
|
_currBlock = _currBlock.Next;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Emit(OpCodes.Br, GetLabel(_currBlock.Next.Position));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return _branchTargets.TryDequeue(out _currBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ResetBlockState()
|
||||||
|
{
|
||||||
|
_ilBlock = _visitedBlocks[_currBlock];
|
||||||
|
|
||||||
|
_ilBlocks.Add(_ilBlock);
|
||||||
|
|
||||||
|
_ilBlock.Next = GetOrCreateILBlock(_currBlock.Next);
|
||||||
|
_ilBlock.Branch = GetOrCreateILBlock(_currBlock.Branch);
|
||||||
|
|
||||||
|
_opcIndex = -1;
|
||||||
|
|
||||||
|
_optOpLastFlagSet = null;
|
||||||
|
_optOpLastCompare = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ILBlock GetOrCreateILBlock(Block block)
|
||||||
|
{
|
||||||
|
if (block == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_visitedBlocks.TryGetValue(block, out ILBlock ilBlock))
|
||||||
|
{
|
||||||
|
return ilBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ILBlock();
|
||||||
|
}
|
||||||
|
|
||||||
public bool TryOptEmitSubroutineCall()
|
public bool TryOptEmitSubroutineCall()
|
||||||
{
|
{
|
||||||
if (CurrBlock.Next == null)
|
if (_currBlock.Next == null)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -148,7 +222,7 @@ namespace ChocolArm64.Translation
|
||||||
EmitLdarg(index);
|
EmitLdarg(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (Register reg in subroutine.Params)
|
foreach (Register reg in subroutine.SubArgs)
|
||||||
{
|
{
|
||||||
switch (reg.Type)
|
switch (reg.Type)
|
||||||
{
|
{
|
||||||
|
@ -160,7 +234,7 @@ namespace ChocolArm64.Translation
|
||||||
|
|
||||||
EmitCall(subroutine.Method);
|
EmitCall(subroutine.Method);
|
||||||
|
|
||||||
subroutine.AddCaller(_root.Position);
|
subroutine.AddCaller(_subPosition);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -171,11 +245,11 @@ namespace ChocolArm64.Translation
|
||||||
|
|
||||||
InstEmitAluHelper.EmitDataLoadOpers(this);
|
InstEmitAluHelper.EmitDataLoadOpers(this);
|
||||||
|
|
||||||
Stloc(Tmp4Index, IoType.Int);
|
Stloc(CmpOptTmp2Index, IoType.Int);
|
||||||
Stloc(Tmp3Index, IoType.Int);
|
Stloc(CmpOptTmp1Index, IoType.Int);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Dictionary<Cond, System.Reflection.Emit.OpCode> _branchOps = new Dictionary<Cond, System.Reflection.Emit.OpCode>()
|
private Dictionary<Cond, OpCode> _branchOps = new Dictionary<Cond, OpCode>()
|
||||||
{
|
{
|
||||||
{ Cond.Eq, OpCodes.Beq },
|
{ Cond.Eq, OpCodes.Beq },
|
||||||
{ Cond.Ne, OpCodes.Bne_Un },
|
{ Cond.Ne, OpCodes.Bne_Un },
|
||||||
|
@ -191,15 +265,15 @@ namespace ChocolArm64.Translation
|
||||||
|
|
||||||
public void EmitCondBranch(ILLabel target, Cond cond)
|
public void EmitCondBranch(ILLabel target, Cond cond)
|
||||||
{
|
{
|
||||||
System.Reflection.Emit.OpCode ilOp;
|
OpCode ilOp;
|
||||||
|
|
||||||
int intCond = (int)cond;
|
int intCond = (int)cond;
|
||||||
|
|
||||||
if (_optOpLastCompare != null &&
|
if (_optOpLastCompare != null &&
|
||||||
_optOpLastCompare == _optOpLastFlagSet && _branchOps.ContainsKey(cond))
|
_optOpLastCompare == _optOpLastFlagSet && _branchOps.ContainsKey(cond))
|
||||||
{
|
{
|
||||||
Ldloc(Tmp3Index, IoType.Int, _optOpLastCompare.RegisterSize);
|
Ldloc(CmpOptTmp1Index, IoType.Int, _optOpLastCompare.RegisterSize);
|
||||||
Ldloc(Tmp4Index, IoType.Int, _optOpLastCompare.RegisterSize);
|
Ldloc(CmpOptTmp2Index, IoType.Int, _optOpLastCompare.RegisterSize);
|
||||||
|
|
||||||
ilOp = _branchOps[cond];
|
ilOp = _branchOps[cond];
|
||||||
}
|
}
|
||||||
|
@ -285,11 +359,11 @@ namespace ChocolArm64.Translation
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void EmitLsl(int amount) => EmitIlShift(amount, OpCodes.Shl);
|
public void EmitLsl(int amount) => EmitILShift(amount, OpCodes.Shl);
|
||||||
public void EmitLsr(int amount) => EmitIlShift(amount, OpCodes.Shr_Un);
|
public void EmitLsr(int amount) => EmitILShift(amount, OpCodes.Shr_Un);
|
||||||
public void EmitAsr(int amount) => EmitIlShift(amount, OpCodes.Shr);
|
public void EmitAsr(int amount) => EmitILShift(amount, OpCodes.Shr);
|
||||||
|
|
||||||
private void EmitIlShift(int amount, System.Reflection.Emit.OpCode ilOp)
|
private void EmitILShift(int amount, OpCode ilOp)
|
||||||
{
|
{
|
||||||
if (amount > 0)
|
if (amount > 0)
|
||||||
{
|
{
|
||||||
|
@ -303,14 +377,14 @@ namespace ChocolArm64.Translation
|
||||||
{
|
{
|
||||||
if (amount > 0)
|
if (amount > 0)
|
||||||
{
|
{
|
||||||
Stloc(Tmp2Index, IoType.Int);
|
Stloc(RorTmpIndex, IoType.Int);
|
||||||
Ldloc(Tmp2Index, IoType.Int);
|
Ldloc(RorTmpIndex, IoType.Int);
|
||||||
|
|
||||||
EmitLdc_I4(amount);
|
EmitLdc_I4(amount);
|
||||||
|
|
||||||
Emit(OpCodes.Shr_Un);
|
Emit(OpCodes.Shr_Un);
|
||||||
|
|
||||||
Ldloc(Tmp2Index, IoType.Int);
|
Ldloc(RorTmpIndex, IoType.Int);
|
||||||
|
|
||||||
EmitLdc_I4(CurrOp.GetBitsCount() - amount);
|
EmitLdc_I4(CurrOp.GetBitsCount() - amount);
|
||||||
|
|
||||||
|
@ -336,12 +410,12 @@ namespace ChocolArm64.Translation
|
||||||
_ilBlock.Add(label);
|
_ilBlock.Add(label);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Emit(System.Reflection.Emit.OpCode ilOp)
|
public void Emit(OpCode ilOp)
|
||||||
{
|
{
|
||||||
_ilBlock.Add(new ILOpCode(ilOp));
|
_ilBlock.Add(new ILOpCode(ilOp));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Emit(System.Reflection.Emit.OpCode ilOp, ILLabel label)
|
public void Emit(OpCode ilOp, ILLabel label)
|
||||||
{
|
{
|
||||||
_ilBlock.Add(new ILOpCodeBranch(ilOp, label));
|
_ilBlock.Add(new ILOpCodeBranch(ilOp, label));
|
||||||
}
|
}
|
||||||
|
@ -353,7 +427,7 @@ namespace ChocolArm64.Translation
|
||||||
|
|
||||||
public void EmitLdarg(int index)
|
public void EmitLdarg(int index)
|
||||||
{
|
{
|
||||||
_ilBlock.Add(new IlOpCodeLoad(index, IoType.Arg));
|
_ilBlock.Add(new ILOpCodeLoad(index, IoType.Arg));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void EmitLdintzr(int index)
|
public void EmitLdintzr(int index)
|
||||||
|
@ -380,24 +454,29 @@ namespace ChocolArm64.Translation
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void EmitLoadState(Block retBlk)
|
public void EmitLoadState()
|
||||||
{
|
{
|
||||||
_ilBlock.Add(new IlOpCodeLoad(Array.IndexOf(_graph, retBlk), IoType.Fields));
|
if (_ilBlock.Next == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Can't load state for next block, because there's no next block.");
|
||||||
|
}
|
||||||
|
|
||||||
|
_ilBlock.Add(new ILOpCodeLoadState(_ilBlock.Next));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void EmitStoreState()
|
public void EmitStoreState()
|
||||||
{
|
{
|
||||||
_ilBlock.Add(new IlOpCodeStore(Array.IndexOf(_graph, CurrBlock), IoType.Fields));
|
_ilBlock.Add(new ILOpCodeStoreState(_ilBlock));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void EmitLdtmp() => EmitLdint(Tmp1Index);
|
public void EmitLdtmp() => EmitLdint(IntTmpIndex);
|
||||||
public void EmitSttmp() => EmitStint(Tmp1Index);
|
public void EmitSttmp() => EmitStint(IntTmpIndex);
|
||||||
|
|
||||||
public void EmitLdvectmp() => EmitLdvec(Tmp5Index);
|
public void EmitLdvectmp() => EmitLdvec(VecTmp1Index);
|
||||||
public void EmitStvectmp() => EmitStvec(Tmp5Index);
|
public void EmitStvectmp() => EmitStvec(VecTmp1Index);
|
||||||
|
|
||||||
public void EmitLdvectmp2() => EmitLdvec(Tmp6Index);
|
public void EmitLdvectmp2() => EmitLdvec(VecTmp2Index);
|
||||||
public void EmitStvectmp2() => EmitStvec(Tmp6Index);
|
public void EmitStvectmp2() => EmitStvec(VecTmp2Index);
|
||||||
|
|
||||||
public void EmitLdint(int index) => Ldloc(index, IoType.Int);
|
public void EmitLdint(int index) => Ldloc(index, IoType.Int);
|
||||||
public void EmitStint(int index) => Stloc(index, IoType.Int);
|
public void EmitStint(int index) => Stloc(index, IoType.Int);
|
||||||
|
@ -415,17 +494,17 @@ namespace ChocolArm64.Translation
|
||||||
|
|
||||||
private void Ldloc(int index, IoType ioType)
|
private void Ldloc(int index, IoType ioType)
|
||||||
{
|
{
|
||||||
_ilBlock.Add(new IlOpCodeLoad(index, ioType, CurrOp.RegisterSize));
|
_ilBlock.Add(new ILOpCodeLoad(index, ioType, CurrOp.RegisterSize));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Ldloc(int index, IoType ioType, RegisterSize registerSize)
|
private void Ldloc(int index, IoType ioType, RegisterSize registerSize)
|
||||||
{
|
{
|
||||||
_ilBlock.Add(new IlOpCodeLoad(index, ioType, registerSize));
|
_ilBlock.Add(new ILOpCodeLoad(index, ioType, registerSize));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Stloc(int index, IoType ioType)
|
private void Stloc(int index, IoType ioType)
|
||||||
{
|
{
|
||||||
_ilBlock.Add(new IlOpCodeStore(index, ioType, CurrOp.RegisterSize));
|
_ilBlock.Add(new ILOpCodeStore(index, ioType, CurrOp.RegisterSize));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void EmitCallPropGet(Type objType, string propName)
|
public void EmitCallPropGet(Type objType, string propName)
|
||||||
|
@ -536,7 +615,7 @@ namespace ChocolArm64.Translation
|
||||||
EmitZnCheck(OpCodes.Clt, (int)PState.NBit);
|
EmitZnCheck(OpCodes.Clt, (int)PState.NBit);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void EmitZnCheck(System.Reflection.Emit.OpCode ilCmpOp, int flag)
|
private void EmitZnCheck(OpCode ilCmpOp, int flag)
|
||||||
{
|
{
|
||||||
Emit(OpCodes.Dup);
|
Emit(OpCodes.Dup);
|
||||||
Emit(OpCodes.Ldc_I4_0);
|
Emit(OpCodes.Ldc_I4_0);
|
||||||
|
|
|
@ -8,12 +8,12 @@ namespace ChocolArm64.Translation
|
||||||
|
|
||||||
private Label _lbl;
|
private Label _lbl;
|
||||||
|
|
||||||
public void Emit(ILEmitter context)
|
public void Emit(ILMethodBuilder context)
|
||||||
{
|
{
|
||||||
context.Generator.MarkLabel(GetLabel(context));
|
context.Generator.MarkLabel(GetLabel(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Label GetLabel(ILEmitter context)
|
public Label GetLabel(ILMethodBuilder context)
|
||||||
{
|
{
|
||||||
if (!_hasLabel)
|
if (!_hasLabel)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
using ChocolArm64.Decoders;
|
|
||||||
using ChocolArm64.State;
|
using ChocolArm64.State;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -7,7 +6,7 @@ using System.Runtime.Intrinsics;
|
||||||
|
|
||||||
namespace ChocolArm64.Translation
|
namespace ChocolArm64.Translation
|
||||||
{
|
{
|
||||||
class ILEmitter
|
class ILMethodBuilder
|
||||||
{
|
{
|
||||||
public LocalAlloc LocalAlloc { get; private set; }
|
public LocalAlloc LocalAlloc { get; private set; }
|
||||||
|
|
||||||
|
@ -17,70 +16,23 @@ namespace ChocolArm64.Translation
|
||||||
|
|
||||||
private ILBlock[] _ilBlocks;
|
private ILBlock[] _ilBlocks;
|
||||||
|
|
||||||
private ILBlock _root;
|
|
||||||
|
|
||||||
private TranslatedSub _subroutine;
|
|
||||||
|
|
||||||
private string _subName;
|
private string _subName;
|
||||||
|
|
||||||
private int _localsCount;
|
private int _localsCount;
|
||||||
|
|
||||||
public ILEmitter(Block[] graph, Block root, string subName)
|
public ILMethodBuilder(ILBlock[] ilBlocks, string subName)
|
||||||
{
|
{
|
||||||
_subName = subName;
|
_ilBlocks = ilBlocks;
|
||||||
|
_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()
|
public TranslatedSub GetSubroutine()
|
||||||
{
|
{
|
||||||
LocalAlloc = new LocalAlloc(_ilBlocks, _root);
|
LocalAlloc = new LocalAlloc(_ilBlocks, _ilBlocks[0]);
|
||||||
|
|
||||||
InitSubroutine();
|
List<Register> subArgs = new List<Register>();
|
||||||
InitLocals();
|
|
||||||
|
|
||||||
foreach (ILBlock ilBlock in _ilBlocks)
|
void SetArgs(long inputs, RegisterType baseType)
|
||||||
{
|
|
||||||
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++)
|
for (int bit = 0; bit < 64; bit++)
|
||||||
{
|
{
|
||||||
|
@ -88,37 +40,43 @@ namespace ChocolArm64.Translation
|
||||||
|
|
||||||
if ((inputs & mask) != 0)
|
if ((inputs & mask) != 0)
|
||||||
{
|
{
|
||||||
Params.Add(GetRegFromBit(bit, baseType));
|
subArgs.Add(GetRegFromBit(bit, baseType));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SetParams(LocalAlloc.GetIntInputs(_root), RegisterType.Int);
|
SetArgs(LocalAlloc.GetIntInputs(_ilBlocks[0]), RegisterType.Int);
|
||||||
SetParams(LocalAlloc.GetVecInputs(_root), RegisterType.Vector);
|
SetArgs(LocalAlloc.GetVecInputs(_ilBlocks[0]), RegisterType.Vector);
|
||||||
|
|
||||||
DynamicMethod mthd = new DynamicMethod(_subName, typeof(long), GetParamTypes(Params));
|
DynamicMethod method = new DynamicMethod(_subName, typeof(long), GetArgumentTypes(subArgs));
|
||||||
|
|
||||||
Generator = mthd.GetILGenerator();
|
Generator = method.GetILGenerator();
|
||||||
|
|
||||||
_subroutine = new TranslatedSub(mthd, Params);
|
TranslatedSub subroutine = new TranslatedSub(method, subArgs);
|
||||||
}
|
|
||||||
|
|
||||||
private void InitLocals()
|
int argsStart = TranslatedSub.FixedArgTypes.Length;
|
||||||
{
|
|
||||||
int paramsStart = TranslatedSub.FixedArgTypes.Length;
|
|
||||||
|
|
||||||
_locals = new Dictionary<Register, int>();
|
_locals = new Dictionary<Register, int>();
|
||||||
|
|
||||||
for (int index = 0; index < _subroutine.Params.Count; index++)
|
_localsCount = 0;
|
||||||
{
|
|
||||||
Register reg = _subroutine.Params[index];
|
|
||||||
|
|
||||||
Generator.EmitLdarg(index + paramsStart);
|
for (int index = 0; index < subroutine.SubArgs.Count; index++)
|
||||||
|
{
|
||||||
|
Register reg = subroutine.SubArgs[index];
|
||||||
|
|
||||||
|
Generator.EmitLdarg(index + argsStart);
|
||||||
Generator.EmitStloc(GetLocalIndex(reg));
|
Generator.EmitStloc(GetLocalIndex(reg));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach (ILBlock ilBlock in _ilBlocks)
|
||||||
|
{
|
||||||
|
ilBlock.Emit(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
return subroutine;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Type[] GetParamTypes(IList<Register> Params)
|
private Type[] GetArgumentTypes(IList<Register> Params)
|
||||||
{
|
{
|
||||||
Type[] fixedArgs = TranslatedSub.FixedArgTypes;
|
Type[] fixedArgs = TranslatedSub.FixedArgTypes;
|
||||||
|
|
||||||
|
@ -140,7 +98,7 @@ namespace ChocolArm64.Translation
|
||||||
{
|
{
|
||||||
if (!_locals.TryGetValue(reg, out int index))
|
if (!_locals.TryGetValue(reg, out int index))
|
||||||
{
|
{
|
||||||
Generator.DeclareLocal(GetLocalType(reg));
|
Generator.DeclareLocal(GetFieldType(reg.Type));
|
||||||
|
|
||||||
index = _localsCount++;
|
index = _localsCount++;
|
||||||
|
|
||||||
|
@ -150,9 +108,7 @@ namespace ChocolArm64.Translation
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Type GetLocalType(Register reg) => GetFieldType(reg.Type);
|
private static Type GetFieldType(RegisterType regType)
|
||||||
|
|
||||||
public Type GetFieldType(RegisterType regType)
|
|
||||||
{
|
{
|
||||||
switch (regType)
|
switch (regType)
|
||||||
{
|
{
|
||||||
|
@ -182,7 +138,7 @@ namespace ChocolArm64.Translation
|
||||||
|
|
||||||
public static bool IsRegIndex(int index)
|
public static bool IsRegIndex(int index)
|
||||||
{
|
{
|
||||||
return index >= 0 && index < 32;
|
return (uint)index < 32;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -11,7 +11,7 @@ namespace ChocolArm64.Translation
|
||||||
_ilOp = ilOp;
|
_ilOp = ilOp;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Emit(ILEmitter context)
|
public void Emit(ILMethodBuilder context)
|
||||||
{
|
{
|
||||||
context.Generator.Emit(_ilOp);
|
context.Generator.Emit(_ilOp);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ namespace ChocolArm64.Translation
|
||||||
_label = label;
|
_label = label;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Emit(ILEmitter context)
|
public void Emit(ILMethodBuilder context)
|
||||||
{
|
{
|
||||||
context.Generator.Emit(_ilOp, _label.GetLabel(context));
|
context.Generator.Emit(_ilOp, _label.GetLabel(context));
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ namespace ChocolArm64.Translation
|
||||||
_mthdInfo = mthdInfo;
|
_mthdInfo = mthdInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Emit(ILEmitter context)
|
public void Emit(ILMethodBuilder context)
|
||||||
{
|
{
|
||||||
context.Generator.Emit(OpCodes.Call, _mthdInfo);
|
context.Generator.Emit(OpCodes.Call, _mthdInfo);
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,7 @@ namespace ChocolArm64.Translation
|
||||||
_value = new ImmVal { R8 = value };
|
_value = new ImmVal { R8 = value };
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Emit(ILEmitter context)
|
public void Emit(ILMethodBuilder context)
|
||||||
{
|
{
|
||||||
switch (_type)
|
switch (_type)
|
||||||
{
|
{
|
||||||
|
|
|
@ -3,7 +3,7 @@ using System.Reflection.Emit;
|
||||||
|
|
||||||
namespace ChocolArm64.Translation
|
namespace ChocolArm64.Translation
|
||||||
{
|
{
|
||||||
struct IlOpCodeLoad : IILEmit
|
struct ILOpCodeLoad : IILEmit
|
||||||
{
|
{
|
||||||
public int Index { get; private set; }
|
public int Index { get; private set; }
|
||||||
|
|
||||||
|
@ -11,55 +11,26 @@ namespace ChocolArm64.Translation
|
||||||
|
|
||||||
public RegisterSize RegisterSize { get; private set; }
|
public RegisterSize RegisterSize { get; private set; }
|
||||||
|
|
||||||
public IlOpCodeLoad(int index, IoType ioType, RegisterSize registerSize = 0)
|
public ILOpCodeLoad(int index, IoType ioType, RegisterSize registerSize = 0)
|
||||||
{
|
{
|
||||||
Index = index;
|
Index = index;
|
||||||
IoType = ioType;
|
IoType = ioType;
|
||||||
RegisterSize = registerSize;
|
RegisterSize = registerSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Emit(ILEmitter context)
|
public void Emit(ILMethodBuilder context)
|
||||||
{
|
{
|
||||||
switch (IoType)
|
switch (IoType)
|
||||||
{
|
{
|
||||||
case IoType.Arg: context.Generator.EmitLdarg(Index); break;
|
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.Flag: EmitLdloc(context, Index, RegisterType.Flag); break;
|
||||||
case IoType.Int: EmitLdloc(context, Index, RegisterType.Int); break;
|
case IoType.Int: EmitLdloc(context, Index, RegisterType.Int); break;
|
||||||
case IoType.Vector: EmitLdloc(context, Index, RegisterType.Vector); break;
|
case IoType.Vector: EmitLdloc(context, Index, RegisterType.Vector); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LoadLocals(ILEmitter context, long inputs, RegisterType baseType)
|
private void EmitLdloc(ILMethodBuilder context, int index, RegisterType registerType)
|
||||||
{
|
|
||||||
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);
|
Register reg = new Register(index, registerType);
|
||||||
|
|
||||||
|
|
42
ChocolArm64/Translation/ILOpCodeLoadState.cs
Normal file
42
ChocolArm64/Translation/ILOpCodeLoadState.cs
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
using ChocolArm64.State;
|
||||||
|
using System.Reflection.Emit;
|
||||||
|
|
||||||
|
namespace ChocolArm64.Translation
|
||||||
|
{
|
||||||
|
struct ILOpCodeLoadState : IILEmit
|
||||||
|
{
|
||||||
|
private ILBlock _block;
|
||||||
|
|
||||||
|
public ILOpCodeLoadState(ILBlock block)
|
||||||
|
{
|
||||||
|
_block = block;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Emit(ILMethodBuilder context)
|
||||||
|
{
|
||||||
|
long intInputs = context.LocalAlloc.GetIntInputs(_block);
|
||||||
|
long vecInputs = context.LocalAlloc.GetVecInputs(_block);
|
||||||
|
|
||||||
|
LoadLocals(context, intInputs, RegisterType.Int);
|
||||||
|
LoadLocals(context, vecInputs, RegisterType.Vector);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadLocals(ILMethodBuilder context, long inputs, RegisterType baseType)
|
||||||
|
{
|
||||||
|
for (int bit = 0; bit < 64; bit++)
|
||||||
|
{
|
||||||
|
long mask = 1L << bit;
|
||||||
|
|
||||||
|
if ((inputs & mask) != 0)
|
||||||
|
{
|
||||||
|
Register reg = ILMethodBuilder.GetRegFromBit(bit, baseType);
|
||||||
|
|
||||||
|
context.Generator.EmitLdarg(TranslatedSub.StateArgIdx);
|
||||||
|
context.Generator.Emit(OpCodes.Ldfld, reg.GetField());
|
||||||
|
|
||||||
|
context.Generator.EmitStloc(context.GetLocalIndex(reg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,7 +9,7 @@ namespace ChocolArm64.Translation
|
||||||
_text = text;
|
_text = text;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Emit(ILEmitter context)
|
public void Emit(ILMethodBuilder context)
|
||||||
{
|
{
|
||||||
context.Generator.EmitWriteLine(_text);
|
context.Generator.EmitWriteLine(_text);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ using System.Reflection.Emit;
|
||||||
|
|
||||||
namespace ChocolArm64.Translation
|
namespace ChocolArm64.Translation
|
||||||
{
|
{
|
||||||
struct IlOpCodeStore : IILEmit
|
struct ILOpCodeStore : IILEmit
|
||||||
{
|
{
|
||||||
public int Index { get; private set; }
|
public int Index { get; private set; }
|
||||||
|
|
||||||
|
@ -11,55 +11,26 @@ namespace ChocolArm64.Translation
|
||||||
|
|
||||||
public RegisterSize RegisterSize { get; private set; }
|
public RegisterSize RegisterSize { get; private set; }
|
||||||
|
|
||||||
public IlOpCodeStore(int index, IoType ioType, RegisterSize registerSize = 0)
|
public ILOpCodeStore(int index, IoType ioType, RegisterSize registerSize = 0)
|
||||||
{
|
{
|
||||||
Index = index;
|
Index = index;
|
||||||
IoType = ioType;
|
IoType = ioType;
|
||||||
RegisterSize = registerSize;
|
RegisterSize = registerSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Emit(ILEmitter context)
|
public void Emit(ILMethodBuilder context)
|
||||||
{
|
{
|
||||||
switch (IoType)
|
switch (IoType)
|
||||||
{
|
{
|
||||||
case IoType.Arg: context.Generator.EmitStarg(Index); break;
|
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.Flag: EmitStloc(context, Index, RegisterType.Flag); break;
|
||||||
case IoType.Int: EmitStloc(context, Index, RegisterType.Int); break;
|
case IoType.Int: EmitStloc(context, Index, RegisterType.Int); break;
|
||||||
case IoType.Vector: EmitStloc(context, Index, RegisterType.Vector); break;
|
case IoType.Vector: EmitStloc(context, Index, RegisterType.Vector); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void StoreLocals(ILEmitter context, long outputs, RegisterType baseType)
|
private void EmitStloc(ILMethodBuilder context, int index, RegisterType registerType)
|
||||||
{
|
|
||||||
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);
|
Register reg = new Register(index, registerType);
|
||||||
|
|
||||||
|
|
42
ChocolArm64/Translation/ILOpCodeStoreState.cs
Normal file
42
ChocolArm64/Translation/ILOpCodeStoreState.cs
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
using ChocolArm64.State;
|
||||||
|
using System.Reflection.Emit;
|
||||||
|
|
||||||
|
namespace ChocolArm64.Translation
|
||||||
|
{
|
||||||
|
struct ILOpCodeStoreState : IILEmit
|
||||||
|
{
|
||||||
|
private ILBlock _block;
|
||||||
|
|
||||||
|
public ILOpCodeStoreState(ILBlock block)
|
||||||
|
{
|
||||||
|
_block = block;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Emit(ILMethodBuilder context)
|
||||||
|
{
|
||||||
|
long intOutputs = context.LocalAlloc.GetIntOutputs(_block);
|
||||||
|
long vecOutputs = context.LocalAlloc.GetVecOutputs(_block);
|
||||||
|
|
||||||
|
StoreLocals(context, intOutputs, RegisterType.Int);
|
||||||
|
StoreLocals(context, vecOutputs, RegisterType.Vector);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void StoreLocals(ILMethodBuilder context, long outputs, RegisterType baseType)
|
||||||
|
{
|
||||||
|
for (int bit = 0; bit < 64; bit++)
|
||||||
|
{
|
||||||
|
long mask = 1L << bit;
|
||||||
|
|
||||||
|
if ((outputs & mask) != 0)
|
||||||
|
{
|
||||||
|
Register reg = ILMethodBuilder.GetRegFromBit(bit, baseType);
|
||||||
|
|
||||||
|
context.Generator.EmitLdarg(TranslatedSub.StateArgIdx);
|
||||||
|
context.Generator.EmitLdloc(context.GetLocalIndex(reg));
|
||||||
|
|
||||||
|
context.Generator.Emit(OpCodes.Stfld, reg.GetField());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,15 +1,10 @@
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace ChocolArm64.Translation
|
namespace ChocolArm64.Translation
|
||||||
{
|
{
|
||||||
[Flags]
|
|
||||||
enum IoType
|
enum IoType
|
||||||
{
|
{
|
||||||
Arg,
|
Arg,
|
||||||
Fields,
|
|
||||||
Flag,
|
Flag,
|
||||||
Int,
|
Int,
|
||||||
Float,
|
|
||||||
Vector
|
Vector
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace ChocolArm64.Translation
|
namespace ChocolArm64.Translation
|
||||||
|
@ -65,11 +66,41 @@ namespace ChocolArm64.Translation
|
||||||
public long VecInputs;
|
public long VecInputs;
|
||||||
public long IntOutputs;
|
public long IntOutputs;
|
||||||
public long VecOutputs;
|
public long VecOutputs;
|
||||||
|
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
if (!(obj is BlockIo other))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return other.Block == Block &&
|
||||||
|
other.Entry == Entry &&
|
||||||
|
other.IntInputs == IntInputs &&
|
||||||
|
other.VecInputs == VecInputs &&
|
||||||
|
other.IntOutputs == IntOutputs &&
|
||||||
|
other.VecOutputs == VecOutputs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return HashCode.Combine(Block, Entry, IntInputs, VecInputs, IntOutputs, VecOutputs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator ==(BlockIo lhs, BlockIo rhs)
|
||||||
|
{
|
||||||
|
return lhs.Equals(rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator !=(BlockIo lhs, BlockIo rhs)
|
||||||
|
{
|
||||||
|
return !(lhs == rhs);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private const int MaxOptGraphLength = 40;
|
private const int MaxOptGraphLength = 40;
|
||||||
|
|
||||||
public LocalAlloc(ILBlock[] graph, ILBlock root)
|
public LocalAlloc(ILBlock[] graph, ILBlock entry)
|
||||||
{
|
{
|
||||||
_intPaths = new Dictionary<ILBlock, PathIo>();
|
_intPaths = new Dictionary<ILBlock, PathIo>();
|
||||||
_vecPaths = new Dictionary<ILBlock, PathIo>();
|
_vecPaths = new Dictionary<ILBlock, PathIo>();
|
||||||
|
@ -77,7 +108,7 @@ namespace ChocolArm64.Translation
|
||||||
if (graph.Length > 1 &&
|
if (graph.Length > 1 &&
|
||||||
graph.Length < MaxOptGraphLength)
|
graph.Length < MaxOptGraphLength)
|
||||||
{
|
{
|
||||||
InitializeOptimal(graph, root);
|
InitializeOptimal(graph, entry);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -85,7 +116,7 @@ namespace ChocolArm64.Translation
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeOptimal(ILBlock[] graph, ILBlock root)
|
private void InitializeOptimal(ILBlock[] graph, ILBlock entry)
|
||||||
{
|
{
|
||||||
//This will go through all possible paths on the graph,
|
//This will go through all possible paths on the graph,
|
||||||
//and store all inputs/outputs for each block. A register
|
//and store all inputs/outputs for each block. A register
|
||||||
|
@ -93,7 +124,7 @@ namespace ChocolArm64.Translation
|
||||||
//When a block can be reached by more than one path, then the
|
//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
|
//output from all paths needs to be set for this block, and
|
||||||
//only outputs present in all of the parent blocks can be considered
|
//only outputs present in all of the parent blocks can be considered
|
||||||
//when doing input elimination. Each block chain have a root, that's where
|
//when doing input elimination. Each block chain have a entry, that's where
|
||||||
//the code starts executing. They are present on the subroutine start point,
|
//the code starts executing. They are present on the subroutine start point,
|
||||||
//and on call return points too (address written to X30 by BL).
|
//and on call return points too (address written to X30 by BL).
|
||||||
HashSet<BlockIo> visited = new HashSet<BlockIo>();
|
HashSet<BlockIo> visited = new HashSet<BlockIo>();
|
||||||
|
@ -112,8 +143,8 @@ namespace ChocolArm64.Translation
|
||||||
|
|
||||||
Enqueue(new BlockIo()
|
Enqueue(new BlockIo()
|
||||||
{
|
{
|
||||||
Block = root,
|
Block = entry,
|
||||||
Entry = root
|
Entry = entry
|
||||||
});
|
});
|
||||||
|
|
||||||
while (unvisited.Count > 0)
|
while (unvisited.Count > 0)
|
||||||
|
@ -146,22 +177,22 @@ namespace ChocolArm64.Translation
|
||||||
|
|
||||||
void EnqueueFromCurrent(ILBlock block, bool retTarget)
|
void EnqueueFromCurrent(ILBlock block, bool retTarget)
|
||||||
{
|
{
|
||||||
BlockIo blkIO = new BlockIo() { Block = block };
|
BlockIo blockIo = new BlockIo() { Block = block };
|
||||||
|
|
||||||
if (retTarget)
|
if (retTarget)
|
||||||
{
|
{
|
||||||
blkIO.Entry = block;
|
blockIo.Entry = block;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
blkIO.Entry = current.Entry;
|
blockIo.Entry = current.Entry;
|
||||||
blkIO.IntInputs = current.IntInputs;
|
blockIo.IntInputs = current.IntInputs;
|
||||||
blkIO.VecInputs = current.VecInputs;
|
blockIo.VecInputs = current.VecInputs;
|
||||||
blkIO.IntOutputs = current.IntOutputs;
|
blockIo.IntOutputs = current.IntOutputs;
|
||||||
blkIO.VecOutputs = current.VecOutputs;
|
blockIo.VecOutputs = current.VecOutputs;
|
||||||
}
|
}
|
||||||
|
|
||||||
Enqueue(blkIO);
|
Enqueue(blockIo);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (current.Block.Next != null)
|
if (current.Block.Next != null)
|
||||||
|
@ -179,7 +210,7 @@ namespace ChocolArm64.Translation
|
||||||
private void InitializeFast(ILBlock[] graph)
|
private void InitializeFast(ILBlock[] graph)
|
||||||
{
|
{
|
||||||
//This is WAY faster than InitializeOptimal, but results in
|
//This is WAY faster than InitializeOptimal, but results in
|
||||||
//uneeded loads and stores, so the resulting code will be slower.
|
//unneeded loads and stores, so the resulting code will be slower.
|
||||||
long intInputs = 0, intOutputs = 0;
|
long intInputs = 0, intOutputs = 0;
|
||||||
long vecInputs = 0, vecOutputs = 0;
|
long vecInputs = 0, vecOutputs = 0;
|
||||||
|
|
||||||
|
|
|
@ -83,47 +83,45 @@ namespace ChocolArm64
|
||||||
{
|
{
|
||||||
Block block = Decoder.DecodeBasicBlock(state, memory, position);
|
Block block = Decoder.DecodeBasicBlock(state, memory, position);
|
||||||
|
|
||||||
Block[] graph = new Block[] { block };
|
ILEmitterCtx context = new ILEmitterCtx(_cache, block);
|
||||||
|
|
||||||
string subName = GetSubroutineName(position);
|
string subName = GetSubroutineName(position);
|
||||||
|
|
||||||
ILEmitterCtx context = new ILEmitterCtx(_cache, graph, block, subName);
|
ILMethodBuilder ilMthdBuilder = new ILMethodBuilder(context.GetILBlocks(), subName);
|
||||||
|
|
||||||
do
|
TranslatedSub subroutine = ilMthdBuilder.GetSubroutine();
|
||||||
{
|
|
||||||
context.EmitOpCode();
|
|
||||||
}
|
|
||||||
while (context.AdvanceOpCode());
|
|
||||||
|
|
||||||
TranslatedSub subroutine = context.GetSubroutine();
|
|
||||||
|
|
||||||
subroutine.SetType(TranslatedSubType.SubTier0);
|
subroutine.SetType(TranslatedSubType.SubTier0);
|
||||||
|
|
||||||
_cache.AddOrUpdate(position, subroutine, block.OpCodes.Count);
|
_cache.AddOrUpdate(position, subroutine, block.OpCodes.Count);
|
||||||
|
|
||||||
OpCode64 lastOp = block.GetLastOp();
|
|
||||||
|
|
||||||
return subroutine;
|
return subroutine;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TranslateTier1(CpuThreadState state, MemoryManager memory, long position)
|
private void TranslateTier1(CpuThreadState state, MemoryManager memory, long position)
|
||||||
{
|
{
|
||||||
(Block[] graph, Block root) = Decoder.DecodeSubroutine(_cache, state, memory, position);
|
Block graph = Decoder.DecodeSubroutine(_cache, state, memory, position);
|
||||||
|
|
||||||
|
ILEmitterCtx context = new ILEmitterCtx(_cache, graph);
|
||||||
|
|
||||||
|
ILBlock[] ilBlocks = context.GetILBlocks();
|
||||||
|
|
||||||
string subName = GetSubroutineName(position);
|
string subName = GetSubroutineName(position);
|
||||||
|
|
||||||
ILEmitterCtx context = new ILEmitterCtx(_cache, graph, root, subName);
|
ILMethodBuilder ilMthdBuilder = new ILMethodBuilder(ilBlocks, subName);
|
||||||
|
|
||||||
if (context.CurrBlock.Position != position)
|
TranslatedSub subroutine = ilMthdBuilder.GetSubroutine();
|
||||||
|
|
||||||
|
subroutine.SetType(TranslatedSubType.SubTier1);
|
||||||
|
|
||||||
|
int ilOpCount = 0;
|
||||||
|
|
||||||
|
foreach (ILBlock ilBlock in ilBlocks)
|
||||||
{
|
{
|
||||||
context.Emit(OpCodes.Br, context.GetLabel(position));
|
ilOpCount += ilBlock.Count;
|
||||||
}
|
}
|
||||||
|
|
||||||
do
|
_cache.AddOrUpdate(position, subroutine, ilOpCount);
|
||||||
{
|
|
||||||
context.EmitOpCode();
|
|
||||||
}
|
|
||||||
while (context.AdvanceOpCode());
|
|
||||||
|
|
||||||
//Mark all methods that calls this method for ReJiting,
|
//Mark all methods that calls this method for ReJiting,
|
||||||
//since we can now call it directly which is faster.
|
//since we can now call it directly which is faster.
|
||||||
|
@ -137,29 +135,11 @@ namespace ChocolArm64
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TranslatedSub subroutine = context.GetSubroutine();
|
|
||||||
|
|
||||||
subroutine.SetType(TranslatedSubType.SubTier1);
|
|
||||||
|
|
||||||
_cache.AddOrUpdate(position, subroutine, GetGraphInstCount(graph));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetSubroutineName(long position)
|
private string GetSubroutineName(long position)
|
||||||
{
|
{
|
||||||
return $"Sub{position:x16}";
|
return $"Sub{position:x16}";
|
||||||
}
|
}
|
||||||
|
|
||||||
private int GetGraphInstCount(Block[] graph)
|
|
||||||
{
|
|
||||||
int size = 0;
|
|
||||||
|
|
||||||
foreach (Block block in graph)
|
|
||||||
{
|
|
||||||
size += block.OpCodes.Count;
|
|
||||||
}
|
|
||||||
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -9,8 +9,8 @@ namespace ChocolArm64
|
||||||
{
|
{
|
||||||
class TranslatorCache
|
class TranslatorCache
|
||||||
{
|
{
|
||||||
//Maximum size of the cache, in bytes, measured in ARM code size.
|
//Maximum size of the cache, the unit used is completely arbitrary.
|
||||||
private const int MaxTotalSize = 4 * 1024 * 256;
|
private const int MaxTotalSize = 0x800000;
|
||||||
|
|
||||||
//Minimum time required in milliseconds for a method to be eligible for deletion.
|
//Minimum time required in milliseconds for a method to be eligible for deletion.
|
||||||
private const int MinTimeDelta = 2 * 60000;
|
private const int MinTimeDelta = 2 * 60000;
|
||||||
|
@ -63,10 +63,10 @@ namespace ChocolArm64
|
||||||
{
|
{
|
||||||
ClearCacheIfNeeded();
|
ClearCacheIfNeeded();
|
||||||
|
|
||||||
_totalSize += size;
|
|
||||||
|
|
||||||
lock (_sortedCache)
|
lock (_sortedCache)
|
||||||
{
|
{
|
||||||
|
_totalSize += size;
|
||||||
|
|
||||||
LinkedListNode<long> node = _sortedCache.AddLast(position);
|
LinkedListNode<long> node = _sortedCache.AddLast(position);
|
||||||
|
|
||||||
CacheBucket newBucket = new CacheBucket(subroutine, node, size);
|
CacheBucket newBucket = new CacheBucket(subroutine, node, size);
|
||||||
|
@ -98,11 +98,18 @@ namespace ChocolArm64
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
bucket.CallCount = 0;
|
//The bucket value on the dictionary may have changed between the
|
||||||
|
//time we get the value from the dictionary, and we acquire the
|
||||||
|
//lock. So we need to ensure we are working with the latest value,
|
||||||
|
//we can do that by getting the value again, inside the lock.
|
||||||
|
if (_cache.TryGetValue(position, out CacheBucket latestBucket))
|
||||||
|
{
|
||||||
|
latestBucket.CallCount = 0;
|
||||||
|
|
||||||
_sortedCache.Remove(bucket.Node);
|
_sortedCache.Remove(latestBucket.Node);
|
||||||
|
|
||||||
bucket.UpdateNode(_sortedCache.AddLast(position));
|
latestBucket.UpdateNode(_sortedCache.AddLast(position));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue