diff options
author | gdkchan <gab.dark.100@gmail.com> | 2019-02-24 04:24:35 -0300 |
---|---|---|
committer | jduncanator <1518948+jduncanator@users.noreply.github.com> | 2019-02-24 18:24:35 +1100 |
commit | 5001f78b1d07b988709dd5f5d1009ebe9b44c669 (patch) | |
tree | bb1307949ea9102b8ae2b68fa7e182ed7b75b2df /ChocolArm64/Instructions/InstEmitMemoryHelper.cs | |
parent | a3d46e41335efd049042cc2e38b35c4077e8ed41 (diff) |
Optimize address translation and write tracking on the MMU (#571)
* Implement faster address translation and write tracking on the MMU
* Rename MemoryAlloc to MemoryManagement, and other nits
* Support multi-level page tables
* Fix typo
* Reword comment a bit
* Support scalar vector loads/stores on the memory fast path, and minor fixes
* Add missing cast
* Alignment
* Fix VirtualFree function signature
* Change MemoryProtection enum to uint aswell for consistency
Diffstat (limited to 'ChocolArm64/Instructions/InstEmitMemoryHelper.cs')
-rw-r--r-- | ChocolArm64/Instructions/InstEmitMemoryHelper.cs | 419 |
1 files changed, 383 insertions, 36 deletions
diff --git a/ChocolArm64/Instructions/InstEmitMemoryHelper.cs b/ChocolArm64/Instructions/InstEmitMemoryHelper.cs index f953564c..7645e363 100644 --- a/ChocolArm64/Instructions/InstEmitMemoryHelper.cs +++ b/ChocolArm64/Instructions/InstEmitMemoryHelper.cs @@ -1,13 +1,20 @@ using ChocolArm64.Decoders; using ChocolArm64.Memory; +using ChocolArm64.State; using ChocolArm64.Translation; using System; using System.Reflection.Emit; +using System.Runtime.Intrinsics.X86; namespace ChocolArm64.Instructions { static class InstEmitMemoryHelper { + private static int _tempIntAddress = ILEmitterCtx.GetIntTempIndex(); + private static int _tempIntValue = ILEmitterCtx.GetIntTempIndex(); + private static int _tempIntPtAddr = ILEmitterCtx.GetIntTempIndex(); + private static int _tempVecValue = ILEmitterCtx.GetVecTempIndex(); + private enum Extension { Zx, @@ -32,9 +39,10 @@ namespace ChocolArm64.Instructions private static void EmitReadCall(ILEmitterCtx context, Extension ext, int size) { - bool isSimd = GetIsSimd(context); + //Save the address into a temp. + context.EmitStint(_tempIntAddress); - string name = null; + bool isSimd = IsSimd(context); if (size < 0 || size > (isSimd ? 4 : 3)) { @@ -43,28 +51,27 @@ namespace ChocolArm64.Instructions if (isSimd) { - switch (size) + if (context.Tier == TranslationTier.Tier0 || !Sse2.IsSupported || size < 2) + { + EmitReadVectorFallback(context, size); + } + else { - case 0: name = nameof(MemoryManager.ReadVector8); break; - case 1: name = nameof(MemoryManager.ReadVector16); break; - case 2: name = nameof(MemoryManager.ReadVector32); break; - case 3: name = nameof(MemoryManager.ReadVector64); break; - case 4: name = nameof(MemoryManager.ReadVector128); break; + EmitReadVector(context, size); } } else { - switch (size) + if (context.Tier == TranslationTier.Tier0) { - case 0: name = nameof(MemoryManager.ReadByte); break; - case 1: name = nameof(MemoryManager.ReadUInt16); break; - case 2: name = nameof(MemoryManager.ReadUInt32); break; - case 3: name = nameof(MemoryManager.ReadUInt64); break; + EmitReadIntFallback(context, size); + } + else + { + EmitReadInt(context, size); } } - context.EmitCall(typeof(MemoryManager), name); - if (!isSimd) { if (ext == Extension.Sx32 || @@ -89,50 +96,390 @@ namespace ChocolArm64.Instructions public static void EmitWriteCall(ILEmitterCtx context, int size) { - bool isSimd = GetIsSimd(context); + bool isSimd = IsSimd(context); - string name = null; - - if (size < 0 || size > (isSimd ? 4 : 3)) + //Save the value into a temp. + if (isSimd) { - throw new ArgumentOutOfRangeException(nameof(size)); + context.EmitStvec(_tempVecValue); } + else + { + context.EmitStint(_tempIntValue); + } + + //Save the address into a temp. + context.EmitStint(_tempIntAddress); - if (size < 3 && !isSimd) + if (size < 0 || size > (isSimd ? 4 : 3)) { - context.Emit(OpCodes.Conv_I4); + throw new ArgumentOutOfRangeException(nameof(size)); } if (isSimd) { - switch (size) + if (context.Tier == TranslationTier.Tier0 || !Sse2.IsSupported || size < 2) + { + EmitWriteVectorFallback(context, size); + } + else { - case 0: name = nameof(MemoryManager.WriteVector8); break; - case 1: name = nameof(MemoryManager.WriteVector16); break; - case 2: name = nameof(MemoryManager.WriteVector32); break; - case 3: name = nameof(MemoryManager.WriteVector64); break; - case 4: name = nameof(MemoryManager.WriteVector128); break; + EmitWriteVector(context, size); } } else { - switch (size) + if (context.Tier == TranslationTier.Tier0) { - case 0: name = nameof(MemoryManager.WriteByte); break; - case 1: name = nameof(MemoryManager.WriteUInt16); break; - case 2: name = nameof(MemoryManager.WriteUInt32); break; - case 3: name = nameof(MemoryManager.WriteUInt64); break; + EmitWriteIntFallback(context, size); + } + else + { + EmitWriteInt(context, size); } } - - context.EmitCall(typeof(MemoryManager), name); } - private static bool GetIsSimd(ILEmitterCtx context) + private static bool IsSimd(ILEmitterCtx context) { return context.CurrOp is IOpCodeSimd64 && !(context.CurrOp is OpCodeSimdMemMs64 || context.CurrOp is OpCodeSimdMemSs64); } + + private static void EmitReadInt(ILEmitterCtx context, int size) + { + EmitAddressCheck(context, size); + + ILLabel lblFastPath = new ILLabel(); + ILLabel lblSlowPath = new ILLabel(); + ILLabel lblEnd = new ILLabel(); + + context.Emit(OpCodes.Brfalse_S, lblFastPath); + + context.MarkLabel(lblSlowPath); + + EmitReadIntFallback(context, size); + + context.Emit(OpCodes.Br, lblEnd); + + context.MarkLabel(lblFastPath); + + EmitPtPointerLoad(context, lblSlowPath); + + switch (size) + { + case 0: context.Emit(OpCodes.Ldind_U1); break; + case 1: context.Emit(OpCodes.Ldind_U2); break; + case 2: context.Emit(OpCodes.Ldind_U4); break; + case 3: context.Emit(OpCodes.Ldind_I8); break; + } + + context.MarkLabel(lblEnd); + } + + private static void EmitReadVector(ILEmitterCtx context, int size) + { + EmitAddressCheck(context, size); + + ILLabel lblFastPath = new ILLabel(); + ILLabel lblSlowPath = new ILLabel(); + ILLabel lblEnd = new ILLabel(); + + context.Emit(OpCodes.Brfalse_S, lblFastPath); + + context.MarkLabel(lblSlowPath); + + EmitReadVectorFallback(context, size); + + context.Emit(OpCodes.Br, lblEnd); + + context.MarkLabel(lblFastPath); + + EmitPtPointerLoad(context, lblSlowPath); + + switch (size) + { + case 2: context.EmitCall(typeof(Sse), nameof(Sse.LoadScalarVector128)); break; + + case 3: + { + Type[] types = new Type[] { typeof(double*) }; + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.LoadScalarVector128), types)); + + VectorHelper.EmitCall(context, nameof(VectorHelper.VectorDoubleToSingle)); + + break; + } + + case 4: context.EmitCall(typeof(Sse), nameof(Sse.LoadAlignedVector128)); break; + + throw new InvalidOperationException($"Invalid vector load size of {1 << size} bytes."); + } + + context.MarkLabel(lblEnd); + } + + private static void EmitWriteInt(ILEmitterCtx context, int size) + { + EmitAddressCheck(context, size); + + ILLabel lblFastPath = new ILLabel(); + ILLabel lblSlowPath = new ILLabel(); + ILLabel lblEnd = new ILLabel(); + + context.Emit(OpCodes.Brfalse_S, lblFastPath); + + context.MarkLabel(lblSlowPath); + + EmitWriteIntFallback(context, size); + + context.Emit(OpCodes.Br, lblEnd); + + context.MarkLabel(lblFastPath); + + EmitPtPointerLoad(context, lblSlowPath); + + context.EmitLdint(_tempIntValue); + + if (size < 3) + { + context.Emit(OpCodes.Conv_U4); + } + + switch (size) + { + case 0: context.Emit(OpCodes.Stind_I1); break; + case 1: context.Emit(OpCodes.Stind_I2); break; + case 2: context.Emit(OpCodes.Stind_I4); break; + case 3: context.Emit(OpCodes.Stind_I8); break; + } + + context.MarkLabel(lblEnd); + } + + private static void EmitWriteVector(ILEmitterCtx context, int size) + { + EmitAddressCheck(context, size); + + ILLabel lblFastPath = new ILLabel(); + ILLabel lblSlowPath = new ILLabel(); + ILLabel lblEnd = new ILLabel(); + + context.Emit(OpCodes.Brfalse_S, lblFastPath); + + context.MarkLabel(lblSlowPath); + + EmitWriteVectorFallback(context, size); + + context.Emit(OpCodes.Br, lblEnd); + + context.MarkLabel(lblFastPath); + + EmitPtPointerLoad(context, lblSlowPath); + + context.EmitLdvec(_tempVecValue); + + switch (size) + { + case 2: context.EmitCall(typeof(Sse), nameof(Sse.StoreScalar)); break; + + case 3: + { + VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleToDouble)); + + context.EmitCall(typeof(Sse2), nameof(Sse2.StoreScalar)); + + break; + } + + case 4: context.EmitCall(typeof(Sse), nameof(Sse.StoreAligned)); break; + + default: throw new InvalidOperationException($"Invalid vector store size of {1 << size} bytes."); + } + + context.MarkLabel(lblEnd); + } + + private static void EmitAddressCheck(ILEmitterCtx context, int size) + { + long addressCheckMask = ~(context.Memory.AddressSpaceSize - 1); + + addressCheckMask |= (1u << size) - 1; + + context.EmitLdint(_tempIntAddress); + + context.EmitLdc_I(addressCheckMask); + + context.Emit(OpCodes.And); + } + + private static void EmitPtPointerLoad(ILEmitterCtx context, ILLabel lblFallbackPath) + { + context.EmitLdc_I8(context.Memory.PageTable.ToInt64()); + + context.Emit(OpCodes.Conv_I); + + int bit = MemoryManager.PageBits; + + do + { + context.EmitLdint(_tempIntAddress); + + if (context.CurrOp.RegisterSize == RegisterSize.Int32) + { + context.Emit(OpCodes.Conv_U8); + } + + context.EmitLsr(bit); + + bit += context.Memory.PtLevelBits; + + if (bit < context.Memory.AddressSpaceBits) + { + context.EmitLdc_I8(context.Memory.PtLevelMask); + + context.Emit(OpCodes.And); + } + + context.EmitLdc_I8(IntPtr.Size); + + context.Emit(OpCodes.Mul); + context.Emit(OpCodes.Conv_I); + context.Emit(OpCodes.Add); + context.Emit(OpCodes.Ldind_I); + } + while (bit < context.Memory.AddressSpaceBits); + + if (!context.Memory.HasWriteWatchSupport) + { + context.Emit(OpCodes.Conv_U8); + + context.EmitStint(_tempIntPtAddr); + context.EmitLdint(_tempIntPtAddr); + + context.EmitLdc_I8(MemoryManager.PteFlagsMask); + + context.Emit(OpCodes.And); + + context.Emit(OpCodes.Brtrue, lblFallbackPath); + + context.EmitLdint(_tempIntPtAddr); + + context.Emit(OpCodes.Conv_I); + } + + context.EmitLdint(_tempIntAddress); + + context.EmitLdc_I(MemoryManager.PageMask); + + context.Emit(OpCodes.And); + context.Emit(OpCodes.Conv_I); + context.Emit(OpCodes.Add); + } + + private static void EmitReadIntFallback(ILEmitterCtx context, int size) + { + context.EmitLdarg(TranslatedSub.MemoryArgIdx); + context.EmitLdint(_tempIntAddress); + + if (context.CurrOp.RegisterSize == RegisterSize.Int32) + { + context.Emit(OpCodes.Conv_U8); + } + + string fallbackMethodName = null; + + switch (size) + { + case 0: fallbackMethodName = nameof(MemoryManager.ReadByte); break; + case 1: fallbackMethodName = nameof(MemoryManager.ReadUInt16); break; + case 2: fallbackMethodName = nameof(MemoryManager.ReadUInt32); break; + case 3: fallbackMethodName = nameof(MemoryManager.ReadUInt64); break; + } + + context.EmitCall(typeof(MemoryManager), fallbackMethodName); + } + + private static void EmitReadVectorFallback(ILEmitterCtx context, int size) + { + context.EmitLdarg(TranslatedSub.MemoryArgIdx); + context.EmitLdint(_tempIntAddress); + + if (context.CurrOp.RegisterSize == RegisterSize.Int32) + { + context.Emit(OpCodes.Conv_U8); + } + + string fallbackMethodName = null; + + switch (size) + { + case 0: fallbackMethodName = nameof(MemoryManager.ReadVector8); break; + case 1: fallbackMethodName = nameof(MemoryManager.ReadVector16); break; + case 2: fallbackMethodName = nameof(MemoryManager.ReadVector32); break; + case 3: fallbackMethodName = nameof(MemoryManager.ReadVector64); break; + case 4: fallbackMethodName = nameof(MemoryManager.ReadVector128); break; + } + + context.EmitCall(typeof(MemoryManager), fallbackMethodName); + } + + private static void EmitWriteIntFallback(ILEmitterCtx context, int size) + { + context.EmitLdarg(TranslatedSub.MemoryArgIdx); + context.EmitLdint(_tempIntAddress); + + if (context.CurrOp.RegisterSize == RegisterSize.Int32) + { + context.Emit(OpCodes.Conv_U8); + } + + context.EmitLdint(_tempIntValue); + + if (size < 3) + { + context.Emit(OpCodes.Conv_U4); + } + + string fallbackMethodName = null; + + switch (size) + { + case 0: fallbackMethodName = nameof(MemoryManager.WriteByte); break; + case 1: fallbackMethodName = nameof(MemoryManager.WriteUInt16); break; + case 2: fallbackMethodName = nameof(MemoryManager.WriteUInt32); break; + case 3: fallbackMethodName = nameof(MemoryManager.WriteUInt64); break; + } + + context.EmitCall(typeof(MemoryManager), fallbackMethodName); + } + + private static void EmitWriteVectorFallback(ILEmitterCtx context, int size) + { + context.EmitLdarg(TranslatedSub.MemoryArgIdx); + context.EmitLdint(_tempIntAddress); + + if (context.CurrOp.RegisterSize == RegisterSize.Int32) + { + context.Emit(OpCodes.Conv_U8); + } + + context.EmitLdvec(_tempVecValue); + + string fallbackMethodName = null; + + switch (size) + { + case 0: fallbackMethodName = nameof(MemoryManager.WriteVector8); break; + case 1: fallbackMethodName = nameof(MemoryManager.WriteVector16); break; + case 2: fallbackMethodName = nameof(MemoryManager.WriteVector32); break; + case 3: fallbackMethodName = nameof(MemoryManager.WriteVector64); break; + case 4: fallbackMethodName = nameof(MemoryManager.WriteVector128); break; + } + + context.EmitCall(typeof(MemoryManager), fallbackMethodName); + } } }
\ No newline at end of file |