2019-04-26 06:55:12 +02:00
|
|
|
using ChocolArm64.IntermediateRepresentation;
|
2019-02-04 22:26:05 +01:00
|
|
|
using ChocolArm64.State;
|
2019-01-25 02:59:53 +01:00
|
|
|
using ChocolArm64.Translation;
|
2019-02-04 22:26:05 +01:00
|
|
|
using System.Reflection;
|
2019-01-25 02:59:53 +01:00
|
|
|
using System.Reflection.Emit;
|
|
|
|
|
|
|
|
namespace ChocolArm64.Instructions
|
|
|
|
{
|
|
|
|
static class InstEmitFlowHelper
|
|
|
|
{
|
|
|
|
public static void EmitCall(ILEmitterCtx context, long imm)
|
|
|
|
{
|
2019-02-04 22:26:05 +01:00
|
|
|
if (context.Tier == TranslationTier.Tier0)
|
|
|
|
{
|
2019-04-26 06:55:12 +02:00
|
|
|
context.EmitStoreContext();
|
2019-02-28 03:03:31 +01:00
|
|
|
|
2019-02-04 22:26:05 +01:00
|
|
|
context.TranslateAhead(imm);
|
|
|
|
|
|
|
|
context.EmitLdc_I8(imm);
|
|
|
|
|
|
|
|
context.Emit(OpCodes.Ret);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!context.TryOptEmitSubroutineCall())
|
|
|
|
{
|
2019-02-28 03:03:31 +01:00
|
|
|
context.HasSlowCall = true;
|
|
|
|
|
2019-04-26 06:55:12 +02:00
|
|
|
context.EmitStoreContext();
|
2019-02-28 03:03:31 +01:00
|
|
|
|
2019-02-04 22:26:05 +01:00
|
|
|
context.TranslateAhead(imm);
|
|
|
|
|
|
|
|
context.EmitLdarg(TranslatedSub.StateArgIdx);
|
|
|
|
|
2019-04-26 06:55:12 +02:00
|
|
|
context.EmitLdfld(typeof(CpuThreadState).GetField(nameof(CpuThreadState.CurrentTranslator),
|
2019-02-04 22:26:05 +01:00
|
|
|
BindingFlags.Instance |
|
|
|
|
BindingFlags.NonPublic));
|
|
|
|
|
|
|
|
context.EmitLdarg(TranslatedSub.StateArgIdx);
|
|
|
|
context.EmitLdc_I8(imm);
|
2019-02-28 03:03:31 +01:00
|
|
|
context.EmitLdc_I4((int)CallType.Call);
|
2019-02-04 22:26:05 +01:00
|
|
|
|
|
|
|
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.Ret);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
context.EmitSttmp();
|
|
|
|
context.EmitLdarg(TranslatedSub.StateArgIdx);
|
|
|
|
|
2019-04-26 06:55:12 +02:00
|
|
|
context.EmitLdfld(typeof(CpuThreadState).GetField(nameof(CpuThreadState.CurrentTranslator),
|
2019-02-04 22:26:05 +01:00
|
|
|
BindingFlags.Instance |
|
|
|
|
BindingFlags.NonPublic));
|
|
|
|
|
|
|
|
context.EmitLdarg(TranslatedSub.StateArgIdx);
|
|
|
|
context.EmitLdtmp();
|
2019-02-28 03:03:31 +01:00
|
|
|
context.EmitLdc_I4(isJump
|
|
|
|
? (int)CallType.VirtualJump
|
|
|
|
: (int)CallType.VirtualCall);
|
2019-02-04 22:26:05 +01:00
|
|
|
|
2019-02-28 03:03:31 +01:00
|
|
|
context.EmitPrivateCall(typeof(Translator), nameof(Translator.GetOrTranslateSubroutine));
|
2019-02-04 22:26:05 +01:00
|
|
|
|
|
|
|
context.EmitLdarg(TranslatedSub.StateArgIdx);
|
|
|
|
context.EmitLdarg(TranslatedSub.MemoryArgIdx);
|
|
|
|
|
|
|
|
if (isJump)
|
|
|
|
{
|
2019-07-02 04:39:22 +02:00
|
|
|
// 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.
|
2019-02-04 22:26:05 +01:00
|
|
|
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)
|
|
|
|
{
|
2019-07-02 04:39:22 +02:00
|
|
|
// 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.
|
2019-02-04 22:26:05 +01:00
|
|
|
if (context.CurrBlock.Next != null)
|
2019-01-25 02:59:53 +01:00
|
|
|
{
|
|
|
|
context.Emit(OpCodes.Dup);
|
|
|
|
|
|
|
|
context.EmitLdc_I8(context.CurrOp.Position + 4);
|
|
|
|
|
|
|
|
ILLabel lblContinue = new ILLabel();
|
|
|
|
|
|
|
|
context.Emit(OpCodes.Beq_S, lblContinue);
|
|
|
|
context.Emit(OpCodes.Ret);
|
|
|
|
|
|
|
|
context.MarkLabel(lblContinue);
|
|
|
|
|
|
|
|
context.Emit(OpCodes.Pop);
|
|
|
|
|
2019-04-26 06:55:12 +02:00
|
|
|
context.EmitLoadContext();
|
2019-01-25 02:59:53 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
context.Emit(OpCodes.Ret);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|