Remove direct argument passing optimization, and enable tail calls for BR instructions
This commit is contained in:
parent
f24c91fa27
commit
c11987bb20
6 changed files with 44 additions and 122 deletions
|
@ -1,9 +1,10 @@
|
|||
using ChocolArm64.Decoders;
|
||||
using ChocolArm64.State;
|
||||
using ChocolArm64.Translation;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
using static ChocolArm64.Instructions.InstEmitFlowHelper;
|
||||
|
||||
namespace ChocolArm64.Instructions
|
||||
{
|
||||
static partial class InstEmit
|
||||
|
@ -40,7 +41,7 @@ namespace ChocolArm64.Instructions
|
|||
context.EmitStint(RegisterAlias.Lr);
|
||||
context.EmitStoreState();
|
||||
|
||||
InstEmitFlowHelper.EmitCall(context, op.Imm);
|
||||
EmitCall(context, op.Imm);
|
||||
}
|
||||
|
||||
public static void Blr(ILEmitterCtx context)
|
||||
|
@ -52,7 +53,7 @@ namespace ChocolArm64.Instructions
|
|||
context.EmitStint(RegisterAlias.Lr);
|
||||
context.EmitStoreState();
|
||||
|
||||
InstEmitFlowHelper.EmitCallVirtual(context);
|
||||
EmitVirtualCall(context);
|
||||
}
|
||||
|
||||
public static void Br(ILEmitterCtx context)
|
||||
|
@ -62,21 +63,7 @@ namespace ChocolArm64.Instructions
|
|||
context.EmitStoreState();
|
||||
context.EmitLdintzr(op.Rn);
|
||||
|
||||
context.Emit(OpCodes.Dup);
|
||||
|
||||
context.EmitSttmp();
|
||||
context.EmitLdarg(TranslatedSub.StateArgIdx);
|
||||
|
||||
context.EmitFieldLoad(typeof(CpuThreadState).GetField(nameof(CpuThreadState.CurrentTranslator),
|
||||
BindingFlags.Instance |
|
||||
BindingFlags.NonPublic));
|
||||
|
||||
context.EmitLdarg(TranslatedSub.StateArgIdx);
|
||||
context.EmitLdtmp();
|
||||
|
||||
context.EmitPrivateCall(typeof(Translator), nameof(Translator.TranslateVirtualSubroutine));
|
||||
|
||||
context.Emit(OpCodes.Ret);
|
||||
EmitVirtualJump(context);
|
||||
}
|
||||
|
||||
public static void Cbnz(ILEmitterCtx context) => EmitCb(context, OpCodes.Bne_Un);
|
||||
|
|
|
@ -33,7 +33,17 @@ namespace ChocolArm64.Instructions
|
|||
EmitContinueOrReturnCheck(context);
|
||||
}
|
||||
|
||||
public static void EmitCallVirtual(ILEmitterCtx context)
|
||||
public static void EmitVirtualCall(ILEmitterCtx context)
|
||||
{
|
||||
EmitVirtualCallOrJump(context, isJump: false);
|
||||
}
|
||||
|
||||
public static void EmitVirtualJump(ILEmitterCtx context)
|
||||
{
|
||||
EmitVirtualCallOrJump(context, isJump: true);
|
||||
}
|
||||
|
||||
private static void EmitVirtualCallOrJump(ILEmitterCtx context, bool isJump)
|
||||
{
|
||||
context.EmitSttmp();
|
||||
context.EmitLdarg(TranslatedSub.StateArgIdx);
|
||||
|
@ -50,9 +60,25 @@ namespace ChocolArm64.Instructions
|
|||
context.EmitLdarg(TranslatedSub.StateArgIdx);
|
||||
context.EmitLdarg(TranslatedSub.MemoryArgIdx);
|
||||
|
||||
if (isJump)
|
||||
{
|
||||
//The tail prefix allows the JIT to jump to the next function,
|
||||
//while releasing the stack space used by the current one.
|
||||
//This is ideal for BR ARM instructions, which are
|
||||
//basically indirect tail calls.
|
||||
context.Emit(OpCodes.Tailcall);
|
||||
}
|
||||
|
||||
context.EmitCall(typeof(TranslatedSub), nameof(TranslatedSub.Execute));
|
||||
|
||||
EmitContinueOrReturnCheck(context);
|
||||
if (!isJump)
|
||||
{
|
||||
EmitContinueOrReturnCheck(context);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Emit(OpCodes.Ret);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitContinueOrReturnCheck(ILEmitterCtx context)
|
||||
|
|
|
@ -277,16 +277,6 @@ namespace ChocolArm64.Translation
|
|||
EmitLdarg(index);
|
||||
}
|
||||
|
||||
foreach (Register reg in subroutine.SubArgs)
|
||||
{
|
||||
switch (reg.Type)
|
||||
{
|
||||
case RegisterType.Flag: Ldloc(reg.Index, IoType.Flag); break;
|
||||
case RegisterType.Int: Ldloc(reg.Index, IoType.Int); break;
|
||||
case RegisterType.Vector: Ldloc(reg.Index, IoType.Vector); break;
|
||||
}
|
||||
}
|
||||
|
||||
EmitCall(subroutine.Method);
|
||||
|
||||
return true;
|
||||
|
@ -635,12 +625,7 @@ namespace ChocolArm64.Translation
|
|||
|
||||
public void EmitCall(MethodInfo mthdInfo)
|
||||
{
|
||||
if (mthdInfo == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(mthdInfo));
|
||||
}
|
||||
|
||||
_ilBlock.Add(new ILOpCodeCall(mthdInfo));
|
||||
_ilBlock.Add(new ILOpCodeCall(mthdInfo ?? throw new ArgumentNullException(nameof(mthdInfo))));
|
||||
}
|
||||
|
||||
public void EmitLdc_I(long value)
|
||||
|
|
|
@ -30,70 +30,28 @@ namespace ChocolArm64.Translation
|
|||
{
|
||||
LocalAlloc = new LocalAlloc(_ilBlocks, _ilBlocks[0]);
|
||||
|
||||
List<Register> subArgs = new List<Register>();
|
||||
|
||||
void SetArgs(long inputs, RegisterType baseType)
|
||||
{
|
||||
for (int bit = 0; bit < 64; bit++)
|
||||
{
|
||||
long mask = 1L << bit;
|
||||
|
||||
if ((inputs & mask) != 0)
|
||||
{
|
||||
subArgs.Add(GetRegFromBit(bit, baseType));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SetArgs(LocalAlloc.GetIntInputs(_ilBlocks[0]), RegisterType.Int);
|
||||
SetArgs(LocalAlloc.GetVecInputs(_ilBlocks[0]), RegisterType.Vector);
|
||||
|
||||
DynamicMethod method = new DynamicMethod(_subName, typeof(long), GetArgumentTypes(subArgs));
|
||||
DynamicMethod method = new DynamicMethod(_subName, typeof(long), TranslatedSub.FixedArgTypes);
|
||||
|
||||
Generator = method.GetILGenerator();
|
||||
|
||||
TranslatedSub subroutine = new TranslatedSub(method, subArgs, tier);
|
||||
|
||||
int argsStart = TranslatedSub.FixedArgTypes.Length;
|
||||
TranslatedSub subroutine = new TranslatedSub(method, tier);
|
||||
|
||||
_locals = new Dictionary<Register, int>();
|
||||
|
||||
_localsCount = 0;
|
||||
|
||||
for (int index = 0; index < subroutine.SubArgs.Count; index++)
|
||||
{
|
||||
Register reg = subroutine.SubArgs[index];
|
||||
|
||||
Generator.EmitLdarg(index + argsStart);
|
||||
Generator.EmitStloc(GetLocalIndex(reg));
|
||||
}
|
||||
new ILOpCodeLoadState(_ilBlocks[0]).Emit(this);
|
||||
|
||||
foreach (ILBlock ilBlock in _ilBlocks)
|
||||
{
|
||||
ilBlock.Emit(this);
|
||||
}
|
||||
|
||||
subroutine.PrepareMethod();
|
||||
|
||||
return subroutine;
|
||||
}
|
||||
|
||||
private Type[] GetArgumentTypes(IList<Register> Params)
|
||||
{
|
||||
Type[] fixedArgs = TranslatedSub.FixedArgTypes;
|
||||
|
||||
Type[] output = new Type[Params.Count + fixedArgs.Length];
|
||||
|
||||
fixedArgs.CopyTo(output, 0);
|
||||
|
||||
int typeIdx = fixedArgs.Length;
|
||||
|
||||
for (int index = 0; index < Params.Count; index++)
|
||||
{
|
||||
output[typeIdx++] = GetFieldType(Params[index].Type);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
public int GetLocalIndex(Register reg)
|
||||
{
|
||||
if (!_locals.TryGetValue(reg, out int index))
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
using ChocolArm64.Memory;
|
||||
using ChocolArm64.State;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
|
@ -21,18 +19,12 @@ namespace ChocolArm64.Translation
|
|||
|
||||
public DynamicMethod Method { get; private set; }
|
||||
|
||||
public ReadOnlyCollection<Register> SubArgs { get; private set; }
|
||||
|
||||
public TranslationTier Tier { get; private set; }
|
||||
|
||||
public TranslatedSub(DynamicMethod method, List<Register> subArgs, TranslationTier tier)
|
||||
public TranslatedSub(DynamicMethod method, TranslationTier tier)
|
||||
{
|
||||
Method = method ?? throw new ArgumentNullException(nameof(method));;
|
||||
SubArgs = subArgs?.AsReadOnly() ?? throw new ArgumentNullException(nameof(subArgs));
|
||||
|
||||
Tier = tier;
|
||||
|
||||
PrepareDelegate();
|
||||
Method = method ?? throw new ArgumentNullException(nameof(method));;
|
||||
Tier = tier;
|
||||
}
|
||||
|
||||
static TranslatedSub()
|
||||
|
@ -60,27 +52,9 @@ namespace ChocolArm64.Translation
|
|||
}
|
||||
}
|
||||
|
||||
private void PrepareDelegate()
|
||||
public void PrepareMethod()
|
||||
{
|
||||
string name = $"{Method.Name}_Dispatch";
|
||||
|
||||
DynamicMethod mthd = new DynamicMethod(name, typeof(long), FixedArgTypes);
|
||||
|
||||
ILGenerator generator = mthd.GetILGenerator();
|
||||
|
||||
generator.EmitLdargSeq(FixedArgTypes.Length);
|
||||
|
||||
foreach (Register reg in SubArgs)
|
||||
{
|
||||
generator.EmitLdarg(StateArgIdx);
|
||||
|
||||
generator.Emit(OpCodes.Ldfld, reg.GetField());
|
||||
}
|
||||
|
||||
generator.Emit(OpCodes.Call, Method);
|
||||
generator.Emit(OpCodes.Ret);
|
||||
|
||||
_execDelegate = (ArmSubroutine)mthd.CreateDelegate(typeof(ArmSubroutine));
|
||||
_execDelegate = (ArmSubroutine)Method.CreateDelegate(typeof(ArmSubroutine));
|
||||
}
|
||||
|
||||
public long Execute(CpuThreadState threadState, MemoryManager memory)
|
||||
|
|
|
@ -72,14 +72,6 @@ namespace ChocolArm64.Translation
|
|||
state.CurrentTranslator = null;
|
||||
}
|
||||
|
||||
internal void TranslateVirtualSubroutine(CpuThreadState state, long position)
|
||||
{
|
||||
if (!_cache.TryGetSubroutine(position, out TranslatedSub sub) || sub.Tier == TranslationTier.Tier0)
|
||||
{
|
||||
_queue.Enqueue(new TranslatorQueueItem(position, state.GetExecutionMode(), TranslationTier.Tier1));
|
||||
}
|
||||
}
|
||||
|
||||
internal TranslatedSub GetOrTranslateVirtualSubroutine(CpuThreadState state, long position)
|
||||
{
|
||||
if (!_cache.TryGetSubroutine(position, out TranslatedSub sub))
|
||||
|
|
Loading…
Add table
Reference in a new issue