Implement speculative translation on the CPU (#515)
* Implement speculative translation on the cpu, and change the way how branches to unknown or untranslated addresses works * Port t0opt changes and other cleanups * Change namespace from translation related classes to ChocolArm64.Translation, other minor tweaks * Fix typo * Translate higher quality code for indirect jumps aswell, and on some cases that were missed when lower quality (tier 0) code was available * Remove debug print * Remove direct argument passing optimization, and enable tail calls for BR instructions * Call delegates directly with Callvirt rather than calling Execute, do not emit calls for tier 0 code * Remove unused property * Rename argument on ArmSubroutine delegate
This commit is contained in:
parent
f5b4f6ccc4
commit
a694420d11
21 changed files with 656 additions and 376 deletions
|
@ -1,4 +1,6 @@
|
|||
using ChocolArm64.State;
|
||||
using ChocolArm64.Translation;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace ChocolArm64.Instructions
|
||||
|
@ -7,12 +9,120 @@ namespace ChocolArm64.Instructions
|
|||
{
|
||||
public static void EmitCall(ILEmitterCtx context, long imm)
|
||||
{
|
||||
if (context.TryOptEmitSubroutineCall())
|
||||
if (context.Tier == TranslationTier.Tier0)
|
||||
{
|
||||
context.TranslateAhead(imm);
|
||||
|
||||
context.EmitLdc_I8(imm);
|
||||
|
||||
context.Emit(OpCodes.Ret);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!context.TryOptEmitSubroutineCall())
|
||||
{
|
||||
context.TranslateAhead(imm);
|
||||
|
||||
context.EmitLdarg(TranslatedSub.StateArgIdx);
|
||||
|
||||
context.EmitFieldLoad(typeof(CpuThreadState).GetField(nameof(CpuThreadState.CurrentTranslator),
|
||||
BindingFlags.Instance |
|
||||
BindingFlags.NonPublic));
|
||||
|
||||
context.EmitLdarg(TranslatedSub.StateArgIdx);
|
||||
context.EmitLdc_I8(imm);
|
||||
|
||||
context.EmitPrivateCall(typeof(Translator), nameof(Translator.GetOrTranslateSubroutine));
|
||||
|
||||
context.EmitLdarg(TranslatedSub.StateArgIdx);
|
||||
context.EmitLdarg(TranslatedSub.MemoryArgIdx);
|
||||
|
||||
context.EmitCall(typeof(TranslatedSub), nameof(TranslatedSub.Execute));
|
||||
}
|
||||
|
||||
EmitContinueOrReturnCheck(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)
|
||||
{
|
||||
if (context.Tier == TranslationTier.Tier0)
|
||||
{
|
||||
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);
|
||||
}
|
||||
else
|
||||
{
|
||||
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.GetOrTranslateVirtualSubroutine));
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
MethodInfo mthdInfo = typeof(ArmSubroutine).GetMethod("Invoke");
|
||||
|
||||
context.EmitCall(mthdInfo, isVirtual: true);
|
||||
|
||||
if (!isJump)
|
||||
{
|
||||
EmitContinueOrReturnCheck(context);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Emit(OpCodes.Ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitContinueOrReturnCheck(ILEmitterCtx context)
|
||||
{
|
||||
//Note: The return value of the called method will be placed
|
||||
//at the Stack, the return value is always a Int64 with the
|
||||
//return address of the function. We check if the address is
|
||||
//correct, if it isn't we keep returning until we reach the dispatcher.
|
||||
if (context.CurrBlock.Next != null)
|
||||
{
|
||||
//Note: the return value of the called method will be placed
|
||||
//at the Stack, the return value is always a Int64 with the
|
||||
//return address of the function. We check if the address is
|
||||
//correct, if it isn't we keep returning until we reach the dispatcher.
|
||||
context.Emit(OpCodes.Dup);
|
||||
|
||||
context.EmitLdc_I8(context.CurrOp.Position + 4);
|
||||
|
@ -30,8 +140,6 @@ namespace ChocolArm64.Instructions
|
|||
}
|
||||
else
|
||||
{
|
||||
context.EmitLdc_I8(imm);
|
||||
|
||||
context.Emit(OpCodes.Ret);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue