Remove direct argument passing optimization, and enable tail calls for BR instructions

This commit is contained in:
gdkchan 2019-01-29 23:10:17 -02:00
parent f24c91fa27
commit c11987bb20
6 changed files with 44 additions and 122 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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