diff --git a/ChocolArm64/Instructions/InstEmitFlow.cs b/ChocolArm64/Instructions/InstEmitFlow.cs index 111055124e..a842dca9d1 100644 --- a/ChocolArm64/Instructions/InstEmitFlow.cs +++ b/ChocolArm64/Instructions/InstEmitFlow.cs @@ -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); diff --git a/ChocolArm64/Instructions/InstEmitFlowHelper.cs b/ChocolArm64/Instructions/InstEmitFlowHelper.cs index 495799c931..3e696d8843 100644 --- a/ChocolArm64/Instructions/InstEmitFlowHelper.cs +++ b/ChocolArm64/Instructions/InstEmitFlowHelper.cs @@ -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) diff --git a/ChocolArm64/Translation/ILEmitterCtx.cs b/ChocolArm64/Translation/ILEmitterCtx.cs index f8848bea24..543e2414be 100644 --- a/ChocolArm64/Translation/ILEmitterCtx.cs +++ b/ChocolArm64/Translation/ILEmitterCtx.cs @@ -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) diff --git a/ChocolArm64/Translation/ILMethodBuilder.cs b/ChocolArm64/Translation/ILMethodBuilder.cs index d46991343a..892f831be3 100644 --- a/ChocolArm64/Translation/ILMethodBuilder.cs +++ b/ChocolArm64/Translation/ILMethodBuilder.cs @@ -30,70 +30,28 @@ namespace ChocolArm64.Translation { LocalAlloc = new LocalAlloc(_ilBlocks, _ilBlocks[0]); - List subArgs = new List(); - - 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(); _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 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)) diff --git a/ChocolArm64/Translation/TranslatedSub.cs b/ChocolArm64/Translation/TranslatedSub.cs index b1601e944d..43644bb718 100644 --- a/ChocolArm64/Translation/TranslatedSub.cs +++ b/ChocolArm64/Translation/TranslatedSub.cs @@ -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 SubArgs { get; private set; } - public TranslationTier Tier { get; private set; } - public TranslatedSub(DynamicMethod method, List 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) diff --git a/ChocolArm64/Translation/Translator.cs b/ChocolArm64/Translation/Translator.cs index 33c2c58a1d..18fe5b6481 100644 --- a/ChocolArm64/Translation/Translator.cs +++ b/ChocolArm64/Translation/Translator.cs @@ -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))