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.Decoders;
|
||||||
using ChocolArm64.State;
|
using ChocolArm64.State;
|
||||||
using ChocolArm64.Translation;
|
using ChocolArm64.Translation;
|
||||||
using System.Reflection;
|
|
||||||
using System.Reflection.Emit;
|
using System.Reflection.Emit;
|
||||||
|
|
||||||
|
using static ChocolArm64.Instructions.InstEmitFlowHelper;
|
||||||
|
|
||||||
namespace ChocolArm64.Instructions
|
namespace ChocolArm64.Instructions
|
||||||
{
|
{
|
||||||
static partial class InstEmit
|
static partial class InstEmit
|
||||||
|
@ -40,7 +41,7 @@ namespace ChocolArm64.Instructions
|
||||||
context.EmitStint(RegisterAlias.Lr);
|
context.EmitStint(RegisterAlias.Lr);
|
||||||
context.EmitStoreState();
|
context.EmitStoreState();
|
||||||
|
|
||||||
InstEmitFlowHelper.EmitCall(context, op.Imm);
|
EmitCall(context, op.Imm);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Blr(ILEmitterCtx context)
|
public static void Blr(ILEmitterCtx context)
|
||||||
|
@ -52,7 +53,7 @@ namespace ChocolArm64.Instructions
|
||||||
context.EmitStint(RegisterAlias.Lr);
|
context.EmitStint(RegisterAlias.Lr);
|
||||||
context.EmitStoreState();
|
context.EmitStoreState();
|
||||||
|
|
||||||
InstEmitFlowHelper.EmitCallVirtual(context);
|
EmitVirtualCall(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Br(ILEmitterCtx context)
|
public static void Br(ILEmitterCtx context)
|
||||||
|
@ -62,21 +63,7 @@ namespace ChocolArm64.Instructions
|
||||||
context.EmitStoreState();
|
context.EmitStoreState();
|
||||||
context.EmitLdintzr(op.Rn);
|
context.EmitLdintzr(op.Rn);
|
||||||
|
|
||||||
context.Emit(OpCodes.Dup);
|
EmitVirtualJump(context);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Cbnz(ILEmitterCtx context) => EmitCb(context, OpCodes.Bne_Un);
|
public static void Cbnz(ILEmitterCtx context) => EmitCb(context, OpCodes.Bne_Un);
|
||||||
|
|
|
@ -33,7 +33,17 @@ namespace ChocolArm64.Instructions
|
||||||
EmitContinueOrReturnCheck(context);
|
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.EmitSttmp();
|
||||||
context.EmitLdarg(TranslatedSub.StateArgIdx);
|
context.EmitLdarg(TranslatedSub.StateArgIdx);
|
||||||
|
@ -50,10 +60,26 @@ namespace ChocolArm64.Instructions
|
||||||
context.EmitLdarg(TranslatedSub.StateArgIdx);
|
context.EmitLdarg(TranslatedSub.StateArgIdx);
|
||||||
context.EmitLdarg(TranslatedSub.MemoryArgIdx);
|
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));
|
context.EmitCall(typeof(TranslatedSub), nameof(TranslatedSub.Execute));
|
||||||
|
|
||||||
|
if (!isJump)
|
||||||
|
{
|
||||||
EmitContinueOrReturnCheck(context);
|
EmitContinueOrReturnCheck(context);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
context.Emit(OpCodes.Ret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void EmitContinueOrReturnCheck(ILEmitterCtx context)
|
private static void EmitContinueOrReturnCheck(ILEmitterCtx context)
|
||||||
{
|
{
|
||||||
|
|
|
@ -277,16 +277,6 @@ namespace ChocolArm64.Translation
|
||||||
EmitLdarg(index);
|
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);
|
EmitCall(subroutine.Method);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -635,12 +625,7 @@ namespace ChocolArm64.Translation
|
||||||
|
|
||||||
public void EmitCall(MethodInfo mthdInfo)
|
public void EmitCall(MethodInfo mthdInfo)
|
||||||
{
|
{
|
||||||
if (mthdInfo == null)
|
_ilBlock.Add(new ILOpCodeCall(mthdInfo ?? throw new ArgumentNullException(nameof(mthdInfo))));
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(mthdInfo));
|
|
||||||
}
|
|
||||||
|
|
||||||
_ilBlock.Add(new ILOpCodeCall(mthdInfo));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void EmitLdc_I(long value)
|
public void EmitLdc_I(long value)
|
||||||
|
|
|
@ -30,70 +30,28 @@ namespace ChocolArm64.Translation
|
||||||
{
|
{
|
||||||
LocalAlloc = new LocalAlloc(_ilBlocks, _ilBlocks[0]);
|
LocalAlloc = new LocalAlloc(_ilBlocks, _ilBlocks[0]);
|
||||||
|
|
||||||
List<Register> subArgs = new List<Register>();
|
DynamicMethod method = new DynamicMethod(_subName, typeof(long), TranslatedSub.FixedArgTypes);
|
||||||
|
|
||||||
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));
|
|
||||||
|
|
||||||
Generator = method.GetILGenerator();
|
Generator = method.GetILGenerator();
|
||||||
|
|
||||||
TranslatedSub subroutine = new TranslatedSub(method, subArgs, tier);
|
TranslatedSub subroutine = new TranslatedSub(method, tier);
|
||||||
|
|
||||||
int argsStart = TranslatedSub.FixedArgTypes.Length;
|
|
||||||
|
|
||||||
_locals = new Dictionary<Register, int>();
|
_locals = new Dictionary<Register, int>();
|
||||||
|
|
||||||
_localsCount = 0;
|
_localsCount = 0;
|
||||||
|
|
||||||
for (int index = 0; index < subroutine.SubArgs.Count; index++)
|
new ILOpCodeLoadState(_ilBlocks[0]).Emit(this);
|
||||||
{
|
|
||||||
Register reg = subroutine.SubArgs[index];
|
|
||||||
|
|
||||||
Generator.EmitLdarg(index + argsStart);
|
|
||||||
Generator.EmitStloc(GetLocalIndex(reg));
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (ILBlock ilBlock in _ilBlocks)
|
foreach (ILBlock ilBlock in _ilBlocks)
|
||||||
{
|
{
|
||||||
ilBlock.Emit(this);
|
ilBlock.Emit(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
subroutine.PrepareMethod();
|
||||||
|
|
||||||
return subroutine;
|
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)
|
public int GetLocalIndex(Register reg)
|
||||||
{
|
{
|
||||||
if (!_locals.TryGetValue(reg, out int index))
|
if (!_locals.TryGetValue(reg, out int index))
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
using ChocolArm64.Memory;
|
using ChocolArm64.Memory;
|
||||||
using ChocolArm64.State;
|
using ChocolArm64.State;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Collections.ObjectModel;
|
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Reflection.Emit;
|
using System.Reflection.Emit;
|
||||||
|
|
||||||
|
@ -21,18 +19,12 @@ namespace ChocolArm64.Translation
|
||||||
|
|
||||||
public DynamicMethod Method { get; private set; }
|
public DynamicMethod Method { get; private set; }
|
||||||
|
|
||||||
public ReadOnlyCollection<Register> SubArgs { get; private set; }
|
|
||||||
|
|
||||||
public TranslationTier Tier { 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));;
|
Method = method ?? throw new ArgumentNullException(nameof(method));;
|
||||||
SubArgs = subArgs?.AsReadOnly() ?? throw new ArgumentNullException(nameof(subArgs));
|
|
||||||
|
|
||||||
Tier = tier;
|
Tier = tier;
|
||||||
|
|
||||||
PrepareDelegate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static TranslatedSub()
|
static TranslatedSub()
|
||||||
|
@ -60,27 +52,9 @@ namespace ChocolArm64.Translation
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PrepareDelegate()
|
public void PrepareMethod()
|
||||||
{
|
{
|
||||||
string name = $"{Method.Name}_Dispatch";
|
_execDelegate = (ArmSubroutine)Method.CreateDelegate(typeof(ArmSubroutine));
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public long Execute(CpuThreadState threadState, MemoryManager memory)
|
public long Execute(CpuThreadState threadState, MemoryManager memory)
|
||||||
|
|
|
@ -72,14 +72,6 @@ namespace ChocolArm64.Translation
|
||||||
state.CurrentTranslator = null;
|
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)
|
internal TranslatedSub GetOrTranslateVirtualSubroutine(CpuThreadState state, long position)
|
||||||
{
|
{
|
||||||
if (!_cache.TryGetSubroutine(position, out TranslatedSub sub))
|
if (!_cache.TryGetSubroutine(position, out TranslatedSub sub))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue