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:
gdkchan 2018-12-10 22:58:52 -02:00 committed by GitHub
parent 5ed6e570cf
commit 01d9716d20
41 changed files with 943 additions and 915 deletions

View file

@ -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)

View file

@ -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

View file

@ -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);
} }
} }

View file

@ -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;
} }
} }
} }

View file

@ -1,5 +1,4 @@
using ChocolArm64.Instructions; using ChocolArm64.Instructions;
using ChocolArm64.State;
namespace ChocolArm64.Decoders namespace ChocolArm64.Decoders
{ {

View file

@ -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;
} }

View file

@ -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);
} }
} }

View file

@ -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
{ {

View file

@ -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;
} }
} }
} }

View file

@ -1,5 +1,4 @@
using ChocolArm64.Instructions; using ChocolArm64.Instructions;
using ChocolArm64.State;
namespace ChocolArm64.Decoders namespace ChocolArm64.Decoders
{ {

View file

@ -1,5 +1,4 @@
using ChocolArm64.Instructions; using ChocolArm64.Instructions;
using ChocolArm64.State;
namespace ChocolArm64.Decoders namespace ChocolArm64.Decoders
{ {

View file

@ -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)
{ {

View file

@ -1,5 +1,4 @@
using ChocolArm64.Instructions; using ChocolArm64.Instructions;
using ChocolArm64.State;
namespace ChocolArm64.Decoders namespace ChocolArm64.Decoders
{ {

View file

@ -1,5 +1,4 @@
using ChocolArm64.Instructions; using ChocolArm64.Instructions;
using ChocolArm64.State;
namespace ChocolArm64.Decoders namespace ChocolArm64.Decoders
{ {

View file

@ -2,9 +2,9 @@ namespace ChocolArm64.Decoders
{ {
enum ShiftType enum ShiftType
{ {
Lsl, Lsl = 0,
Lsr, Lsr = 1,
Asr, Asr = 2,
Ror Ror = 3
} }
} }

View file

@ -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
{ {

View file

@ -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);
} }

View file

@ -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;

View file

@ -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);
} }

View file

@ -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

View file

@ -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);

View file

@ -2,6 +2,6 @@ namespace ChocolArm64.Translation
{ {
interface IILEmit interface IILEmit
{ {
void Emit(ILEmitter context); void Emit(ILMethodBuilder context);
} }
} }

View file

@ -2,6 +2,6 @@ namespace ChocolArm64.Translation
{ {
struct ILBarrier : IILEmit struct ILBarrier : IILEmit
{ {
public void Emit(ILEmitter context) { } public void Emit(ILMethodBuilder context) { }
} }
} }

View file

@ -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);
} }

View file

@ -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);

View file

@ -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)
{ {

View file

@ -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;
} }
} }
} }

View file

@ -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);
} }

View file

@ -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));
} }

View file

@ -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);
} }

View file

@ -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)
{ {

View file

@ -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);

View 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));
}
}
}
}
}

View file

@ -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);
} }

View file

@ -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);

View 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());
}
}
}
}
}

View file

@ -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
} }
} }

View file

@ -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;

View file

@ -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;
}
} }
} }

View file

@ -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
{ {