diff options
| author | gdkchan <gab.dark.100@gmail.com> | 2019-02-04 18:26:05 -0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-02-04 18:26:05 -0300 |
| commit | a694420d11ef74e4f0bf473be2b6f64635bc89c7 (patch) | |
| tree | 6c44e7a0633dca7b54d99ac3f01f0648fa602559 /ChocolArm64/Instructions | |
| parent | f5b4f6ccc4815cfac1fa3c103d8941a26d152d8a (diff) | |
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
Diffstat (limited to 'ChocolArm64/Instructions')
| -rw-r--r-- | ChocolArm64/Instructions/InstEmitFlow.cs | 30 | ||||
| -rw-r--r-- | ChocolArm64/Instructions/InstEmitFlowHelper.cs | 122 |
2 files changed, 138 insertions, 14 deletions
diff --git a/ChocolArm64/Instructions/InstEmitFlow.cs b/ChocolArm64/Instructions/InstEmitFlow.cs index 181c6a04..a842dca9 100644 --- a/ChocolArm64/Instructions/InstEmitFlow.cs +++ b/ChocolArm64/Instructions/InstEmitFlow.cs @@ -3,6 +3,8 @@ using ChocolArm64.State; using ChocolArm64.Translation; using System.Reflection.Emit; +using static ChocolArm64.Instructions.InstEmitFlowHelper; + namespace ChocolArm64.Instructions { static partial class InstEmit @@ -39,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) @@ -51,7 +53,7 @@ namespace ChocolArm64.Instructions context.EmitStint(RegisterAlias.Lr); context.EmitStoreState(); - context.Emit(OpCodes.Ret); + EmitVirtualCall(context); } public static void Br(ILEmitterCtx context) @@ -61,7 +63,7 @@ namespace ChocolArm64.Instructions context.EmitStoreState(); context.EmitLdintzr(op.Rn); - context.Emit(OpCodes.Ret); + EmitVirtualJump(context); } public static void Cbnz(ILEmitterCtx context) => EmitCb(context, OpCodes.Bne_Un); @@ -106,10 +108,17 @@ namespace ChocolArm64.Instructions { OpCodeBImm64 op = (OpCodeBImm64)context.CurrOp; - if (context.CurrBlock.Next != null && - context.CurrBlock.Branch != null) + if (context.CurrBlock.Branch != null) { context.EmitCondBranch(context.GetLabel(op.Imm), cond); + + if (context.CurrBlock.Next == null) + { + context.EmitStoreState(); + context.EmitLdc_I8(op.Position + 4); + + context.Emit(OpCodes.Ret); + } } else { @@ -135,10 +144,17 @@ namespace ChocolArm64.Instructions { OpCodeBImm64 op = (OpCodeBImm64)context.CurrOp; - if (context.CurrBlock.Next != null && - context.CurrBlock.Branch != null) + if (context.CurrBlock.Branch != null) { context.Emit(ilOp, context.GetLabel(op.Imm)); + + if (context.CurrBlock.Next == null) + { + context.EmitStoreState(); + context.EmitLdc_I8(op.Position + 4); + + context.Emit(OpCodes.Ret); + } } else { diff --git a/ChocolArm64/Instructions/InstEmitFlowHelper.cs b/ChocolArm64/Instructions/InstEmitFlowHelper.cs index cf093bb3..e93ef426 100644 --- a/ChocolArm64/Instructions/InstEmitFlowHelper.cs +++ b/ChocolArm64/Instructions/InstEmitFlowHelper.cs @@ -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); } } |
