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
commit c11987bb20
6 changed files with 44 additions and 122 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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