From 5e724cf24e3d696b95be859c055a617e5d37bf80 Mon Sep 17 00:00:00 2001 From: LDj3SNuD <35856442+LDj3SNuD@users.noreply.github.com> Date: Tue, 16 Jun 2020 20:28:02 +0200 Subject: [PATCH] Add Profiled Persistent Translation Cache. (#769) * Delete DelegateTypes.cs * Delete DelegateCache.cs * Add files via upload * Update Horizon.cs * Update Program.cs * Update MainWindow.cs * Update Aot.cs * Update RelocEntry.cs * Update Translator.cs * Update MemoryManager.cs * Update InstEmitMemoryHelper.cs * Update Delegates.cs * Nit. * Nit. * Nit. * 10 fewer MSIL bytes for us * Add comment. Nits. * Update Translator.cs * Update Aot.cs * Nits. * Opt.. * Opt.. * Opt.. * Opt.. * Allow to change compression level. * Update MemoryManager.cs * Update Translator.cs * Manage corner cases during the save phase. Nits. * Update Aot.cs * Translator response tweak for Aot disabled. Nit. * Nit. * Nits. * Create DelegateHelpers.cs * Update Delegates.cs * Nit. * Nit. * Nits. * Fix due to #784. * Fixes due to #757 & #841. * Fix due to #846. * Fix due to #847. * Use MethodInfo for managed method calls. Use IR methods instead of managed methods about Max/Min (S/U). Follow-ups & Nits. * Add missing exception messages. Reintroduce slow path for Fmov_Vi. Implement slow path for Fmov_Si. * Switch to the new folder structure. Nits. * Impl. index-based relocation information. Impl. cache file version field. * Nit. * Address gdkchan comments. Mainly: - fixed cache file corruption issue on exit; - exposed a way to disable AOT on the GUI. * Address AcK77 comment. * Address Thealexbarney, jduncanator & emmauss comments. Header magic, CpuId (FI) & Aot -> Ptc. * Adaptation to the new application reloading system. Improvements to the call system of managed methods. Follow-ups. Nits. * Get the same boot times as on master when PTC is disabled. * Profiled Aot. * A32 support (#897). * #975 support (1 of 2). * #975 support (2 of 2). * Rebase fix & nits. * Some fixes and nits (still one bug left). * One fix & nits. * Tests fix (by gdk) & nits. * Support translations not only in high quality and rejit. Nits. * Added possibility to skip translations and continue execution, using `ESC` key. * Update SettingsWindow.cs * Update GLRenderer.cs * Update Ptc.cs * Disabled Profiled PTC by default as requested in the past by gdk. * Fix rejit bug. Increased number of parallel translations. Add stack unwinding stuffs support (1 of 2). Nits. * Add stack unwinding stuffs support (2 of 2). Tuned number of parallel translations. * Restored the ability to assemble jumps with 8-bit offset when Profiled PTC is disabled or during profiling. Modifications due to rebase. Nits. * Limited profiling of the functions to be translated to the addresses belonging to the range of static objects only. * Nits. * Nits. * Update Delegates.cs * Nit. * Update InstEmitSimdArithmetic.cs * Address riperiperi comments. * Fixed the issue of unjustifiably longer boot times at the second boot than at the first boot, measured at the same time or reference point and with the same number of translated functions. * Implemented a simple redundant load/save mechanism. Halved the value of Decoder.MaxInstsPerFunction more appropriate for the current performance of the Translator. Replaced by Logger.PrintError to Logger.PrintDebug in TexturePool.cs about the supposed invalid texture format to avoid the spawn of the log. Nits. * Nit. Improved Logger.PrintError in TexturePool.cs to avoid log spawn. Added missing code for FZ handling (in output) for fp max/min instructions (slow paths). * Add configuration migration for PTC Co-authored-by: Thog --- ARMeilleure/ARMeilleure.csproj | 4 + .../CodeGen/Optimizations/ConstantFolding.cs | 8 +- .../CodeGen/Unwinding/UnwindPseudoOp.cs | 11 + .../Unwinding/UnwindPseudoOperation.cs | 11 - ARMeilleure/CodeGen/X86/Assembler.cs | 28 +- ARMeilleure/CodeGen/X86/CodeGenContext.cs | 144 ++-- ARMeilleure/CodeGen/X86/CodeGenerator.cs | 7 +- ARMeilleure/Decoders/Decoder.cs | 2 +- ARMeilleure/Diagnostics/Logger.cs | 11 +- ARMeilleure/Instructions/DelegateTypes.cs | 94 --- ARMeilleure/Instructions/InstEmitException.cs | 13 +- .../Instructions/InstEmitException32.cs | 8 +- .../Instructions/InstEmitFlowHelper.cs | 40 +- ARMeilleure/Instructions/InstEmitHash.cs | 21 +- ARMeilleure/Instructions/InstEmitMemoryEx.cs | 3 +- .../Instructions/InstEmitMemoryEx32.cs | 2 +- .../Instructions/InstEmitMemoryExHelper.cs | 52 +- .../Instructions/InstEmitMemoryHelper.cs | 146 ++-- .../Instructions/InstEmitSimdArithmetic.cs | 266 +++--- .../Instructions/InstEmitSimdArithmetic32.cs | 54 +- ARMeilleure/Instructions/InstEmitSimdCmp.cs | 85 +- ARMeilleure/Instructions/InstEmitSimdCmp32.cs | 71 +- .../Instructions/InstEmitSimdCrypto.cs | 12 +- .../Instructions/InstEmitSimdCrypto32.cs | 12 +- ARMeilleure/Instructions/InstEmitSimdCvt.cs | 137 ++-- ARMeilleure/Instructions/InstEmitSimdCvt32.cs | 98 +-- ARMeilleure/Instructions/InstEmitSimdHash.cs | 20 +- .../Instructions/InstEmitSimdHelper.cs | 106 +-- .../Instructions/InstEmitSimdHelper32.cs | 56 +- ARMeilleure/Instructions/InstEmitSimdMove.cs | 69 +- ARMeilleure/Instructions/InstEmitSimdShift.cs | 23 +- .../Instructions/InstEmitSimdShift32.cs | 10 +- ARMeilleure/Instructions/InstEmitSystem.cs | 39 +- ARMeilleure/Instructions/InstEmitSystem32.cs | 71 +- ARMeilleure/Instructions/NativeInterface.cs | 8 +- ARMeilleure/Instructions/SoftFloat.cs | 28 + .../IntermediateRepresentation/Operand.cs | 15 +- .../OperandHelper.cs | 4 +- ARMeilleure/Translation/ArmEmitterContext.cs | 2 +- ARMeilleure/Translation/Compiler.cs | 20 +- ARMeilleure/Translation/DelegateCache.cs | 26 - ARMeilleure/Translation/DelegateHelper.cs | 107 +++ ARMeilleure/Translation/DelegateInfo.cs | 19 + ARMeilleure/Translation/Delegates.cs | 305 +++++++ ARMeilleure/Translation/DirectCallStubs.cs | 44 +- ARMeilleure/Translation/EmitterContext.cs | 74 +- ARMeilleure/Translation/JitCache.cs | 17 +- ARMeilleure/Translation/JitUnwindWindows.cs | 16 +- ARMeilleure/Translation/JumpTable.cs | 156 ++-- ARMeilleure/Translation/PTC/EncodingCache.cs | 9 + ARMeilleure/Translation/PTC/Ptc.cs | 768 ++++++++++++++++++ ARMeilleure/Translation/PTC/PtcInfo.cs | 68 ++ ARMeilleure/Translation/PTC/PtcJumpTable.cs | 222 +++++ ARMeilleure/Translation/PTC/PtcProfiler.cs | 267 ++++++ ARMeilleure/Translation/PTC/PtcState.cs | 10 + ARMeilleure/Translation/PTC/RelocEntry.cs | 19 + ARMeilleure/Translation/PriorityQueue.cs | 39 - ARMeilleure/Translation/RejitRequest.cs | 2 +- ARMeilleure/Translation/TranslatedFunction.cs | 31 +- ARMeilleure/Translation/Translator.cs | 92 ++- CONFIG.md | 6 +- .../Configuration/ConfigurationFileFormat.cs | 7 +- .../Configuration/ConfigurationState.cs | 20 +- Ryujinx.Common/Logging/LogClass.cs | 1 + Ryujinx.Graphics.Gpu/Image/TexturePool.cs | 5 +- Ryujinx.HLE/HOS/ApplicationLoader.cs | 29 +- Ryujinx.HLE/HOS/Horizon.cs | 3 +- Ryujinx.HLE/HOS/ProgramLoader.cs | 6 +- Ryujinx.HLE/Switch.cs | 2 + Ryujinx.Tests/Cpu/CpuTest.cs | 4 +- Ryujinx.Tests/Cpu/CpuTest32.cs | 10 +- Ryujinx/Config.json | 3 +- Ryujinx/Program.cs | 9 +- Ryujinx/Ui/GLRenderer.cs | 17 +- Ryujinx/Ui/MainWindow.cs | 8 + Ryujinx/Ui/SettingsWindow.cs | 7 + Ryujinx/Ui/SettingsWindow.glade | 20 +- Ryujinx/_schema.json | 14 +- 78 files changed, 3066 insertions(+), 1207 deletions(-) create mode 100644 ARMeilleure/CodeGen/Unwinding/UnwindPseudoOp.cs delete mode 100644 ARMeilleure/CodeGen/Unwinding/UnwindPseudoOperation.cs delete mode 100644 ARMeilleure/Instructions/DelegateTypes.cs delete mode 100644 ARMeilleure/Translation/DelegateCache.cs create mode 100644 ARMeilleure/Translation/DelegateHelper.cs create mode 100644 ARMeilleure/Translation/DelegateInfo.cs create mode 100644 ARMeilleure/Translation/Delegates.cs create mode 100644 ARMeilleure/Translation/PTC/EncodingCache.cs create mode 100644 ARMeilleure/Translation/PTC/Ptc.cs create mode 100644 ARMeilleure/Translation/PTC/PtcInfo.cs create mode 100644 ARMeilleure/Translation/PTC/PtcJumpTable.cs create mode 100644 ARMeilleure/Translation/PTC/PtcProfiler.cs create mode 100644 ARMeilleure/Translation/PTC/PtcState.cs create mode 100644 ARMeilleure/Translation/PTC/RelocEntry.cs delete mode 100644 ARMeilleure/Translation/PriorityQueue.cs diff --git a/ARMeilleure/ARMeilleure.csproj b/ARMeilleure/ARMeilleure.csproj index 24a52567e..2fac4f639 100644 --- a/ARMeilleure/ARMeilleure.csproj +++ b/ARMeilleure/ARMeilleure.csproj @@ -27,4 +27,8 @@ + + + + diff --git a/ARMeilleure/CodeGen/Optimizations/ConstantFolding.cs b/ARMeilleure/CodeGen/Optimizations/ConstantFolding.cs index 84eedee0e..eff53217f 100644 --- a/ARMeilleure/CodeGen/Optimizations/ConstantFolding.cs +++ b/ARMeilleure/CodeGen/Optimizations/ConstantFolding.cs @@ -14,7 +14,7 @@ namespace ARMeilleure.CodeGen.Optimizations return; } - if (!AreAllSourcesConstant(operation)) + if (!AreAllSourcesConstantAndCFEnabled(operation)) { return; } @@ -212,11 +212,13 @@ namespace ARMeilleure.CodeGen.Optimizations } } - private static bool AreAllSourcesConstant(Operation operation) + private static bool AreAllSourcesConstantAndCFEnabled(Operation operation) { for (int index = 0; index < operation.SourcesCount; index++) { - if (operation.GetSource(index).Kind != OperandKind.Constant) + Operand srcOp = operation.GetSource(index); + + if (srcOp.Kind != OperandKind.Constant || srcOp.DisableCF) { return false; } diff --git a/ARMeilleure/CodeGen/Unwinding/UnwindPseudoOp.cs b/ARMeilleure/CodeGen/Unwinding/UnwindPseudoOp.cs new file mode 100644 index 000000000..4a8288a28 --- /dev/null +++ b/ARMeilleure/CodeGen/Unwinding/UnwindPseudoOp.cs @@ -0,0 +1,11 @@ +namespace ARMeilleure.CodeGen.Unwinding +{ + enum UnwindPseudoOp + { + PushReg = 0, + SetFrame = 1, + AllocStack = 2, + SaveReg = 3, + SaveXmm128 = 4 + } +} \ No newline at end of file diff --git a/ARMeilleure/CodeGen/Unwinding/UnwindPseudoOperation.cs b/ARMeilleure/CodeGen/Unwinding/UnwindPseudoOperation.cs deleted file mode 100644 index 44ed23f5c..000000000 --- a/ARMeilleure/CodeGen/Unwinding/UnwindPseudoOperation.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace ARMeilleure.CodeGen.Unwinding -{ - enum UnwindPseudoOp - { - PushReg, - SetFrame, - AllocStack, - SaveReg, - SaveXmm128 - } -} \ No newline at end of file diff --git a/ARMeilleure/CodeGen/X86/Assembler.cs b/ARMeilleure/CodeGen/X86/Assembler.cs index 5ad54289c..99df3cb56 100644 --- a/ARMeilleure/CodeGen/X86/Assembler.cs +++ b/ARMeilleure/CodeGen/X86/Assembler.cs @@ -1,4 +1,5 @@ using ARMeilleure.IntermediateRepresentation; +using ARMeilleure.Translation.PTC; using System; using System.Diagnostics; using System.IO; @@ -64,6 +65,9 @@ namespace ARMeilleure.CodeGen.X86 private Stream _stream; + private PtcInfo _ptcInfo; + private bool _ptcDisabled; + static Assembler() { _instTable = new InstructionInfo[(int)X86Instruction.Count]; @@ -273,9 +277,12 @@ namespace ARMeilleure.CodeGen.X86 _instTable[(int)inst] = info; } - public Assembler(Stream stream) + public Assembler(Stream stream, PtcInfo ptcInfo = null) { _stream = stream; + + _ptcInfo = ptcInfo; + _ptcDisabled = ptcInfo == null; } public void Add(Operand dest, Operand source, OperandType type) @@ -456,7 +463,7 @@ namespace ARMeilleure.CodeGen.X86 public void Jcc(X86Condition condition, long offset) { - if (ConstFitsOnS8(offset)) + if (_ptcDisabled && ConstFitsOnS8(offset)) { WriteByte((byte)(0x70 | (int)condition)); @@ -477,7 +484,7 @@ namespace ARMeilleure.CodeGen.X86 public void Jmp(long offset) { - if (ConstFitsOnS8(offset)) + if (_ptcDisabled && ConstFitsOnS8(offset)) { WriteByte(0xeb); @@ -915,6 +922,8 @@ namespace ARMeilleure.CodeGen.X86 } else if (dest != null && dest.Kind == OperandKind.Register && info.OpRImm64 != BadOp) { + int? index = source.PtcIndex; + int rexPrefix = GetRexPrefix(dest, source, type, rrm: false); if (rexPrefix != 0) @@ -924,6 +933,11 @@ namespace ARMeilleure.CodeGen.X86 WriteByte((byte)(info.OpRImm64 + (dest.GetRegister().Index & 0b111))); + if (_ptcInfo != null && index != null) + { + _ptcInfo.WriteRelocEntry(new RelocEntry((int)_stream.Position, (int)index)); + } + WriteUInt64(imm); } else @@ -1316,9 +1330,9 @@ namespace ARMeilleure.CodeGen.X86 return ConstFitsOnS32(value); } - public static int GetJccLength(long offset) + public static int GetJccLength(long offset, bool ptcDisabled = true) { - if (ConstFitsOnS8(offset < 0 ? offset - 2 : offset)) + if (ptcDisabled && ConstFitsOnS8(offset < 0 ? offset - 2 : offset)) { return 2; } @@ -1332,9 +1346,9 @@ namespace ARMeilleure.CodeGen.X86 } } - public static int GetJmpLength(long offset) + public static int GetJmpLength(long offset, bool ptcDisabled = true) { - if (ConstFitsOnS8(offset < 0 ? offset - 2 : offset)) + if (ptcDisabled && ConstFitsOnS8(offset < 0 ? offset - 2 : offset)) { return 2; } diff --git a/ARMeilleure/CodeGen/X86/CodeGenContext.cs b/ARMeilleure/CodeGen/X86/CodeGenContext.cs index d719b5164..da147cca2 100644 --- a/ARMeilleure/CodeGen/X86/CodeGenContext.cs +++ b/ARMeilleure/CodeGen/X86/CodeGenContext.cs @@ -1,6 +1,7 @@ using ARMeilleure.CodeGen.RegisterAllocators; using ARMeilleure.Common; using ARMeilleure.IntermediateRepresentation; +using ARMeilleure.Translation.PTC; using System.Collections.Generic; using System.Diagnostics; using System.IO; @@ -13,6 +14,9 @@ namespace ARMeilleure.CodeGen.X86 private Stream _stream; + private PtcInfo _ptcInfo; + private bool _ptcDisabled; + public int StreamOffset => (int)_stream.Length; public AllocationResult AllocResult { get; } @@ -40,7 +44,7 @@ namespace ARMeilleure.CodeGen.X86 public int InstSize { get; set; } - public Jump(BasicBlock target, long jumpPosition) + public Jump(BasicBlock target, long jumpPosition, int instSize = 0) { IsConditional = false; Condition = 0; @@ -49,10 +53,10 @@ namespace ARMeilleure.CodeGen.X86 RelativeOffset = 0; - InstSize = 0; + InstSize = instSize; } - public Jump(X86Condition condition, BasicBlock target, long jumpPosition) + public Jump(X86Condition condition, BasicBlock target, long jumpPosition, int instSize = 0) { IsConditional = true; Condition = condition; @@ -61,7 +65,7 @@ namespace ARMeilleure.CodeGen.X86 RelativeOffset = 0; - InstSize = 0; + InstSize = instSize; } } @@ -72,13 +76,13 @@ namespace ARMeilleure.CodeGen.X86 private long _jNearPosition; private int _jNearLength; - public CodeGenContext(Stream stream, AllocationResult allocResult, int maxCallArgs, int blocksCount) + public CodeGenContext(Stream stream, AllocationResult allocResult, int maxCallArgs, int blocksCount, PtcInfo ptcInfo = null) { _stream = stream; AllocResult = allocResult; - Assembler = new Assembler(stream); + Assembler = new Assembler(stream, ptcInfo); CallArgsRegionSize = GetCallArgsRegionSize(allocResult, maxCallArgs, out int xmmSaveRegionSize); XmmSaveRegionSize = xmmSaveRegionSize; @@ -86,6 +90,9 @@ namespace ARMeilleure.CodeGen.X86 _blockOffsets = new long[blocksCount]; _jumps = new List(); + + _ptcInfo = ptcInfo; + _ptcDisabled = ptcInfo == null; } private int GetCallArgsRegionSize(AllocationResult allocResult, int maxCallArgs, out int xmmSaveRegionSize) @@ -136,23 +143,41 @@ namespace ARMeilleure.CodeGen.X86 public void JumpTo(BasicBlock target) { - _jumps.Add(new Jump(target, _stream.Position)); + if (_ptcDisabled) + { + _jumps.Add(new Jump(target, _stream.Position)); - WritePadding(ReservedBytesForJump); + WritePadding(ReservedBytesForJump); + } + else + { + _jumps.Add(new Jump(target, _stream.Position, 5)); + + WritePadding(5); + } } public void JumpTo(X86Condition condition, BasicBlock target) { - _jumps.Add(new Jump(condition, target, _stream.Position)); + if (_ptcDisabled) + { + _jumps.Add(new Jump(condition, target, _stream.Position)); - WritePadding(ReservedBytesForJump); + WritePadding(ReservedBytesForJump); + } + else + { + _jumps.Add(new Jump(condition, target, _stream.Position, 6)); + + WritePadding(6); + } } public void JumpToNear(X86Condition condition) { _jNearCondition = condition; _jNearPosition = _stream.Position; - _jNearLength = Assembler.GetJccLength(0); + _jNearLength = Assembler.GetJccLength(0, _ptcDisabled); _stream.Seek(_jNearLength, SeekOrigin.Current); } @@ -165,7 +190,7 @@ namespace ARMeilleure.CodeGen.X86 long offset = currentPosition - (_jNearPosition + _jNearLength); - Debug.Assert(_jNearLength == Assembler.GetJccLength(offset), "Relative offset doesn't fit on near jump."); + Debug.Assert(_jNearLength == Assembler.GetJccLength(offset, _ptcDisabled), "Relative offset doesn't fit on near jump."); Assembler.Jcc(_jNearCondition, offset); @@ -197,55 +222,62 @@ namespace ARMeilleure.CodeGen.X86 long offset = jumpTarget - jump.JumpPosition; - if (offset < 0) + if (_ptcDisabled) { - for (int index2 = index - 1; index2 >= 0; index2--) + if (offset < 0) { - Jump jump2 = _jumps[index2]; - - if (jump2.JumpPosition < jumpTarget) + for (int index2 = index - 1; index2 >= 0; index2--) { - break; + Jump jump2 = _jumps[index2]; + + if (jump2.JumpPosition < jumpTarget) + { + break; + } + + offset -= jump2.InstSize - ReservedBytesForJump; + } + } + else + { + for (int index2 = index + 1; index2 < _jumps.Count; index2++) + { + Jump jump2 = _jumps[index2]; + + if (jump2.JumpPosition >= jumpTarget) + { + break; + } + + offset += jump2.InstSize - ReservedBytesForJump; } - offset -= jump2.InstSize - ReservedBytesForJump; + offset -= ReservedBytesForJump; + } + + if (jump.IsConditional) + { + jump.InstSize = Assembler.GetJccLength(offset); + } + else + { + jump.InstSize = Assembler.GetJmpLength(offset); + } + + // The jump is relative to the next instruction, not the current one. + // Since we didn't know the next instruction address when calculating + // the offset (as the size of the current jump instruction was not known), + // we now need to compensate the offset with the jump instruction size. + // It's also worth noting that: + // - This is only needed for backward jumps. + // - The GetJmpLength and GetJccLength also compensates the offset + // internally when computing the jump instruction size. + if (offset < 0) + { + offset -= jump.InstSize; } } else - { - for (int index2 = index + 1; index2 < _jumps.Count; index2++) - { - Jump jump2 = _jumps[index2]; - - if (jump2.JumpPosition >= jumpTarget) - { - break; - } - - offset += jump2.InstSize - ReservedBytesForJump; - } - - offset -= ReservedBytesForJump; - } - - if (jump.IsConditional) - { - jump.InstSize = Assembler.GetJccLength(offset); - } - else - { - jump.InstSize = Assembler.GetJmpLength(offset); - } - - // The jump is relative to the next instruction, not the current one. - // Since we didn't know the next instruction address when calculating - // the offset (as the size of the current jump instruction was not know), - // we now need to compensate the offset with the jump instruction size. - // It's also worth to note that: - // - This is only needed for backward jumps. - // - The GetJmpLength and GetJccLength also compensates the offset - // internally when computing the jump instruction size. - if (offset < 0) { offset -= jump.InstSize; } @@ -267,7 +299,7 @@ namespace ARMeilleure.CodeGen.X86 using (MemoryStream codeStream = new MemoryStream()) { - Assembler assembler = new Assembler(codeStream); + Assembler assembler = new Assembler(codeStream, _ptcInfo); byte[] buffer; @@ -278,7 +310,7 @@ namespace ARMeilleure.CodeGen.X86 buffer = new byte[jump.JumpPosition - _stream.Position]; _stream.Read(buffer, 0, buffer.Length); - _stream.Seek(ReservedBytesForJump, SeekOrigin.Current); + _stream.Seek(_ptcDisabled ? ReservedBytesForJump : jump.InstSize, SeekOrigin.Current); codeStream.Write(buffer); @@ -298,6 +330,8 @@ namespace ARMeilleure.CodeGen.X86 codeStream.Write(buffer); + _ptcInfo?.WriteCode(codeStream); + return codeStream.ToArray(); } } diff --git a/ARMeilleure/CodeGen/X86/CodeGenerator.cs b/ARMeilleure/CodeGen/X86/CodeGenerator.cs index 0faba6dd6..e7e7553ed 100644 --- a/ARMeilleure/CodeGen/X86/CodeGenerator.cs +++ b/ARMeilleure/CodeGen/X86/CodeGenerator.cs @@ -5,6 +5,7 @@ using ARMeilleure.Common; using ARMeilleure.Diagnostics; using ARMeilleure.IntermediateRepresentation; using ARMeilleure.Translation; +using ARMeilleure.Translation.PTC; using System; using System.Collections.Generic; using System.Diagnostics; @@ -100,7 +101,7 @@ namespace ARMeilleure.CodeGen.X86 _instTable[(int)inst] = func; } - public static CompiledFunction Generate(CompilerContext cctx) + public static CompiledFunction Generate(CompilerContext cctx, PtcInfo ptcInfo = null) { ControlFlowGraph cfg = cctx.Cfg; @@ -158,10 +159,12 @@ namespace ARMeilleure.CodeGen.X86 using (MemoryStream stream = new MemoryStream()) { - CodeGenContext context = new CodeGenContext(stream, allocResult, maxCallArgs, cfg.Blocks.Count); + CodeGenContext context = new CodeGenContext(stream, allocResult, maxCallArgs, cfg.Blocks.Count, ptcInfo); UnwindInfo unwindInfo = WritePrologue(context); + ptcInfo?.WriteUnwindInfo(unwindInfo); + for (BasicBlock block = cfg.Blocks.First; block != null; block = block.ListNext) { context.EnterBlock(block); diff --git a/ARMeilleure/Decoders/Decoder.cs b/ARMeilleure/Decoders/Decoder.cs index 5ba12fb7b..6117b807d 100644 --- a/ARMeilleure/Decoders/Decoder.cs +++ b/ARMeilleure/Decoders/Decoder.cs @@ -12,7 +12,7 @@ namespace ARMeilleure.Decoders // We define a limit on the number of instructions that a function may have, // this prevents functions being potentially too large, which would // take too long to compile and use too much memory. - private const int MaxInstsPerFunction = 5000; + private const int MaxInstsPerFunction = 2500; // For lower code quality translation, we set a lower limit since we're blocking execution. private const int MaxInstsPerFunctionLowCq = 500; diff --git a/ARMeilleure/Diagnostics/Logger.cs b/ARMeilleure/Diagnostics/Logger.cs index 8ff630918..07a60667e 100644 --- a/ARMeilleure/Diagnostics/Logger.cs +++ b/ARMeilleure/Diagnostics/Logger.cs @@ -6,46 +6,41 @@ namespace ARMeilleure.Diagnostics { static class Logger { -#pragma warning disable CS0169 private static long _startTime; private static long[] _accumulatedTime; -#pragma warning restore CS0169 static Logger() { _accumulatedTime = new long[(int)PassName.Count]; } + [Conditional("M_DEBUG")] public static void StartPass(PassName name) { -#if M_DEBUG WriteOutput(name + " pass started..."); _startTime = Stopwatch.GetTimestamp(); -#endif } + [Conditional("M_DEBUG")] public static void EndPass(PassName name, ControlFlowGraph cfg) { -#if M_DEBUG EndPass(name); WriteOutput("IR after " + name + " pass:"); WriteOutput(IRDumper.GetDump(cfg)); -#endif } + [Conditional("M_DEBUG")] public static void EndPass(PassName name) { -#if M_DEBUG long elapsedTime = Stopwatch.GetTimestamp() - _startTime; _accumulatedTime[(int)name] += elapsedTime; WriteOutput($"{name} pass ended after {GetMilliseconds(_accumulatedTime[(int)name])} ms..."); -#endif } private static long GetMilliseconds(long ticks) diff --git a/ARMeilleure/Instructions/DelegateTypes.cs b/ARMeilleure/Instructions/DelegateTypes.cs deleted file mode 100644 index 41614f88e..000000000 --- a/ARMeilleure/Instructions/DelegateTypes.cs +++ /dev/null @@ -1,94 +0,0 @@ -using ARMeilleure.State; -using System; - -namespace ARMeilleure.Instructions -{ - delegate bool _Bool(); - - delegate double _F64_F64(double a1); - delegate double _F64_F64_Bool(double a1, bool a2); - delegate double _F64_F64_F64(double a1, double a2); - delegate double _F64_F64_F64_Bool(double a1, double a2, bool a3); - delegate double _F64_F64_F64_F64(double a1, double a2, double a3); - delegate double _F64_F64_F64_F64_Bool(double a1, double a2, double a3, bool a4); - delegate double _F64_F64_MidpointRounding(double a1, MidpointRounding a2); - - delegate float _F32_F32(float a1); - delegate float _F32_F32_Bool(float a1, bool a2); - delegate float _F32_F32_F32(float a1, float a2); - delegate float _F32_F32_F32_Bool(float a1, float a2, bool a3); - delegate float _F32_F32_F32_F32(float a1, float a2, float a3); - delegate float _F32_F32_F32_F32_Bool(float a1, float a2, float a3, bool a4); - delegate float _F32_F32_MidpointRounding(float a1, MidpointRounding a2); - delegate float _F32_U16(ushort a1); - - delegate int _S32_F32(float a1); - delegate int _S32_F32_F32_Bool(float a1, float a2, bool a3); - delegate int _S32_F64(double a1); - delegate int _S32_F64_F64_Bool(double a1, double a2, bool a3); - delegate int _S32_U64_U16(ulong a1, ushort a2); - delegate int _S32_U64_U32(ulong a1, uint a2); - delegate int _S32_U64_U64(ulong a1, ulong a2); - delegate int _S32_U64_U8(ulong a1, byte a2); - delegate int _S32_U64_V128(ulong a1, V128 a2); - - delegate long _S64_F32(float a1); - delegate long _S64_F64(double a1); - delegate long _S64_S64(long a1); - delegate long _S64_S64_S32(long a1, int a2); - delegate long _S64_S64_S64(long a1, long a2); - delegate long _S64_S64_S64_Bool_S32(long a1, long a2, bool a3, int a4); - delegate long _S64_S64_S64_S32(long a1, long a2, int a3); - delegate long _S64_U64_S32(ulong a1, int a2); - delegate long _S64_U64_S64(ulong a1, long a2); - - delegate ushort _U16_F32(float a1); - delegate ushort _U16_U64(ulong a1); - - delegate uint _U32(); - delegate uint _U32_F32(float a1); - delegate uint _U32_F64(double a1); - delegate uint _U32_U32(uint a1); - delegate uint _U32_U32_U16(uint a1, ushort a2); - delegate uint _U32_U32_U32(uint a1, uint a2); - delegate uint _U32_U32_U64(uint a1, ulong a2); - delegate uint _U32_U32_U8(uint a1, byte a2); - delegate uint _U32_U64(ulong a1); - - delegate ulong _U64(); - delegate ulong _U64_F32(float a1); - delegate ulong _U64_F64(double a1); - delegate ulong _U64_S64_S32(long a1, int a2); - delegate ulong _U64_S64_U64(long a1, ulong a2); - delegate ulong _U64_U64(ulong a1); - delegate ulong _U64_U64_S32(ulong a1, int a2); - delegate ulong _U64_U64_S64_S32(ulong a1, long a2, int a3); - delegate ulong _U64_U64_U64(ulong a1, ulong a2); - delegate ulong _U64_U64_U64_Bool_S32(ulong a1, ulong a2, bool a3, int a4); - - delegate byte _U8_U64(ulong a1); - - delegate V128 _V128_U64(ulong a1); - delegate V128 _V128_V128(V128 a1); - delegate V128 _V128_V128_S32_V128(V128 a1, int a2, V128 a3); - delegate V128 _V128_V128_S32_V128_V128(V128 a1, int a2, V128 a3, V128 a4); - delegate V128 _V128_V128_S32_V128_V128_V128(V128 a1, int a2, V128 a3, V128 a4, V128 a5); - delegate V128 _V128_V128_S32_V128_V128_V128_V128(V128 a1, int a2, V128 a3, V128 a4, V128 a5, V128 a6); - delegate V128 _V128_V128_U32_V128(V128 a1, uint a2, V128 a3); - delegate V128 _V128_V128_V128(V128 a1, V128 a2); - delegate V128 _V128_V128_V128_S32_V128(V128 a1, V128 a2, int a3, V128 a4); - delegate V128 _V128_V128_V128_S32_V128_V128(V128 a1, V128 a2, int a3, V128 a4, V128 a5); - delegate V128 _V128_V128_V128_S32_V128_V128_V128(V128 a1, V128 a2, int a3, V128 a4, V128 a5, V128 a6); - delegate V128 _V128_V128_V128_S32_V128_V128_V128_V128(V128 a1, V128 a2, int a3, V128 a4, V128 a5, V128 a6, V128 a7); - delegate V128 _V128_V128_V128_V128(V128 a1, V128 a2, V128 a3); - - delegate void _Void(); - delegate void _Void_U32(uint a1); - delegate void _Void_U64(ulong a1); - delegate void _Void_U64_S32(ulong a1, int a2); - delegate void _Void_U64_U16(ulong a1, ushort a2); - delegate void _Void_U64_U32(ulong a1, uint a2); - delegate void _Void_U64_U64(ulong a1, ulong a2); - delegate void _Void_U64_U8(ulong a1, byte a2); - delegate void _Void_U64_V128(ulong a1, V128 a2); -} diff --git a/ARMeilleure/Instructions/InstEmitException.cs b/ARMeilleure/Instructions/InstEmitException.cs index f0bde242a..cdf6cf345 100644 --- a/ARMeilleure/Instructions/InstEmitException.cs +++ b/ARMeilleure/Instructions/InstEmitException.cs @@ -1,6 +1,5 @@ using ARMeilleure.Decoders; using ARMeilleure.Translation; -using System; using static ARMeilleure.Instructions.InstEmitFlowHelper; using static ARMeilleure.IntermediateRepresentation.OperandHelper; @@ -11,21 +10,21 @@ namespace ARMeilleure.Instructions { public static void Brk(ArmEmitterContext context) { - EmitExceptionCall(context, NativeInterface.Break); + EmitExceptionCall(context, nameof(NativeInterface.Break)); } public static void Svc(ArmEmitterContext context) { - EmitExceptionCall(context, NativeInterface.SupervisorCall); + EmitExceptionCall(context, nameof(NativeInterface.SupervisorCall)); } - private static void EmitExceptionCall(ArmEmitterContext context, _Void_U64_S32 func) + private static void EmitExceptionCall(ArmEmitterContext context, string name) { OpCodeException op = (OpCodeException)context.CurrOp; context.StoreToContext(); - context.Call(func, Const(op.Address), Const(op.Id)); + context.Call(typeof(NativeInterface).GetMethod(name), Const(op.Address), Const(op.Id)); context.LoadFromContext(); @@ -39,11 +38,11 @@ namespace ARMeilleure.Instructions { OpCode op = context.CurrOp; - Delegate dlg = new _Void_U64_S32(NativeInterface.Undefined); + string name = nameof(NativeInterface.Undefined); context.StoreToContext(); - context.Call(dlg, Const(op.Address), Const(op.RawOpCode)); + context.Call(typeof(NativeInterface).GetMethod(name), Const(op.Address), Const(op.RawOpCode)); context.LoadFromContext(); diff --git a/ARMeilleure/Instructions/InstEmitException32.cs b/ARMeilleure/Instructions/InstEmitException32.cs index 8ffad1d1f..fd64e7bf4 100644 --- a/ARMeilleure/Instructions/InstEmitException32.cs +++ b/ARMeilleure/Instructions/InstEmitException32.cs @@ -10,21 +10,21 @@ namespace ARMeilleure.Instructions { public static void Svc(ArmEmitterContext context) { - EmitExceptionCall(context, NativeInterface.SupervisorCall); + EmitExceptionCall(context, nameof(NativeInterface.SupervisorCall)); } public static void Trap(ArmEmitterContext context) { - EmitExceptionCall(context, NativeInterface.Break); + EmitExceptionCall(context, nameof(NativeInterface.Break)); } - private static void EmitExceptionCall(ArmEmitterContext context, _Void_U64_S32 func) + private static void EmitExceptionCall(ArmEmitterContext context, string name) { OpCode32Exception op = (OpCode32Exception)context.CurrOp; context.StoreToContext(); - context.Call(func, Const(op.Address), Const(op.Id)); + context.Call(typeof(NativeInterface).GetMethod(name), Const(op.Address), Const(op.Id)); context.LoadFromContext(); diff --git a/ARMeilleure/Instructions/InstEmitFlowHelper.cs b/ARMeilleure/Instructions/InstEmitFlowHelper.cs index 7b244296b..e99afa757 100644 --- a/ARMeilleure/Instructions/InstEmitFlowHelper.cs +++ b/ARMeilleure/Instructions/InstEmitFlowHelper.cs @@ -2,6 +2,7 @@ using ARMeilleure.Decoders; using ARMeilleure.IntermediateRepresentation; using ARMeilleure.State; using ARMeilleure.Translation; +using ARMeilleure.Translation.PTC; using System; using static ARMeilleure.Instructions.InstEmitHelper; @@ -223,6 +224,7 @@ namespace ARMeilleure.Instructions public static void EmitTailContinue(ArmEmitterContext context, Operand address, bool allowRejit = false) { bool useTailContinue = true; // Left option here as it may be useful if we need to return to managed rather than tail call in future. (eg. for debug) + if (useTailContinue) { if (context.HighCq) @@ -230,7 +232,7 @@ namespace ARMeilleure.Instructions // If we're doing a tail continue in HighCq, reserve a space in the jump table to avoid calling back to the translator. // This will always try to get a HighCq version of our continue target as well. EmitJumpTableBranch(context, address, true); - } + } else { if (allowRejit) @@ -238,11 +240,11 @@ namespace ARMeilleure.Instructions address = context.BitwiseOr(address, Const(CallFlag)); } - Operand fallbackAddr = context.Call(new _U64_U64(NativeInterface.GetFunctionAddress), address); + Operand fallbackAddr = context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFunctionAddress)), address); EmitNativeCall(context, fallbackAddr, true); } - } + } else { context.Return(address); @@ -260,7 +262,7 @@ namespace ARMeilleure.Instructions private static void EmitBranchFallback(ArmEmitterContext context, Operand address, bool isJump) { address = context.BitwiseOr(address, Const(address.Type, (long)CallFlag)); // Set call flag. - Operand fallbackAddr = context.Call(new _U64_U64(NativeInterface.GetFunctionAddress), address); + Operand fallbackAddr = context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFunctionAddress)), address); EmitNativeCall(context, fallbackAddr, isJump); } @@ -290,12 +292,12 @@ namespace ARMeilleure.Instructions }; // Currently this uses a size of 1, as higher values inflate code size for no real benefit. - for (int i = 0; i < JumpTable.DynamicTableElems; i++) + for (int i = 0; i < JumpTable.DynamicTableElems; i++) { if (i == JumpTable.DynamicTableElems - 1) { emitTableEntry(fallbackLabel); // If this is the last entry, avoid emitting the additional label and add. - } + } else { Operand nextLabel = Label(); @@ -339,7 +341,18 @@ namespace ARMeilleure.Instructions int entry = context.JumpTable.ReserveDynamicEntry(isJump); int jumpOffset = entry * JumpTable.JumpTableStride * JumpTable.DynamicTableElems; - Operand dynTablePtr = Const(context.JumpTable.DynamicPointer.ToInt64() + jumpOffset); + + Operand dynTablePtr; + + if (Ptc.State == PtcState.Disabled) + { + dynTablePtr = Const(context.JumpTable.DynamicPointer.ToInt64() + jumpOffset); + } + else + { + dynTablePtr = Const(context.JumpTable.DynamicPointer.ToInt64(), true, Ptc.DynamicPointerIndex); + dynTablePtr = context.Add(dynTablePtr, Const((long)jumpOffset)); + } EmitDynamicTableCall(context, dynTablePtr, address, isJump); } @@ -349,8 +362,17 @@ namespace ARMeilleure.Instructions int jumpOffset = entry * JumpTable.JumpTableStride + 8; // Offset directly to the host address. - // TODO: Relocatable jump table ptr for AOT. Would prefer a solution to patch this constant into functions as they are loaded rather than calculate at runtime. - Operand tableEntryPtr = Const(context.JumpTable.JumpPointer.ToInt64() + jumpOffset); + Operand tableEntryPtr; + + if (Ptc.State == PtcState.Disabled) + { + tableEntryPtr = Const(context.JumpTable.JumpPointer.ToInt64() + jumpOffset); + } + else + { + tableEntryPtr = Const(context.JumpTable.JumpPointer.ToInt64(), true, Ptc.JumpPointerIndex); + tableEntryPtr = context.Add(tableEntryPtr, Const((long)jumpOffset)); + } Operand funcAddr = context.Load(OperandType.I64, tableEntryPtr); diff --git a/ARMeilleure/Instructions/InstEmitHash.cs b/ARMeilleure/Instructions/InstEmitHash.cs index 8a539666e..2a8b34888 100644 --- a/ARMeilleure/Instructions/InstEmitHash.cs +++ b/ARMeilleure/Instructions/InstEmitHash.cs @@ -3,7 +3,6 @@ using ARMeilleure.Decoders; using ARMeilleure.IntermediateRepresentation; using ARMeilleure.Translation; -using System; using static ARMeilleure.Instructions.InstEmitHelper; using static ARMeilleure.Instructions.InstEmitSimdHelper; @@ -21,7 +20,7 @@ namespace ARMeilleure.Instructions } else { - EmitCrc32Call(context, new _U32_U32_U8(SoftFallback.Crc32b)); + EmitCrc32Call(context, nameof(SoftFallback.Crc32b)); } } @@ -33,7 +32,7 @@ namespace ARMeilleure.Instructions } else { - EmitCrc32Call(context, new _U32_U32_U16(SoftFallback.Crc32h)); + EmitCrc32Call(context, nameof(SoftFallback.Crc32h)); } } @@ -45,7 +44,7 @@ namespace ARMeilleure.Instructions } else { - EmitCrc32Call(context, new _U32_U32_U32(SoftFallback.Crc32w)); + EmitCrc32Call(context, nameof(SoftFallback.Crc32w)); } } @@ -57,7 +56,7 @@ namespace ARMeilleure.Instructions } else { - EmitCrc32Call(context, new _U32_U32_U64(SoftFallback.Crc32x)); + EmitCrc32Call(context, nameof(SoftFallback.Crc32x)); } } @@ -69,7 +68,7 @@ namespace ARMeilleure.Instructions } else { - EmitCrc32Call(context, new _U32_U32_U8(SoftFallback.Crc32cb)); + EmitCrc32Call(context, nameof(SoftFallback.Crc32cb)); } } @@ -81,7 +80,7 @@ namespace ARMeilleure.Instructions } else { - EmitCrc32Call(context, new _U32_U32_U16(SoftFallback.Crc32ch)); + EmitCrc32Call(context, nameof(SoftFallback.Crc32ch)); } } @@ -93,7 +92,7 @@ namespace ARMeilleure.Instructions } else { - EmitCrc32Call(context, new _U32_U32_U32(SoftFallback.Crc32cw)); + EmitCrc32Call(context, nameof(SoftFallback.Crc32cw)); } } @@ -105,7 +104,7 @@ namespace ARMeilleure.Instructions } else { - EmitCrc32Call(context, new _U32_U32_U64(SoftFallback.Crc32cx)); + EmitCrc32Call(context, nameof(SoftFallback.Crc32cx)); } } @@ -170,14 +169,14 @@ namespace ARMeilleure.Instructions SetIntOrZR(context, op.Rd, context.VectorExtract(OperandType.I32, tmp, 2)); } - private static void EmitCrc32Call(ArmEmitterContext context, Delegate dlg) + private static void EmitCrc32Call(ArmEmitterContext context, string name) { OpCodeAluBinary op = (OpCodeAluBinary)context.CurrOp; Operand n = GetIntOrZR(context, op.Rn); Operand m = GetIntOrZR(context, op.Rm); - Operand d = context.Call(dlg, n, m); + Operand d = context.Call(typeof(SoftFallback).GetMethod(name), n, m); SetIntOrZR(context, op.Rd, d); } diff --git a/ARMeilleure/Instructions/InstEmitMemoryEx.cs b/ARMeilleure/Instructions/InstEmitMemoryEx.cs index 93c20cb58..7ca019dea 100644 --- a/ARMeilleure/Instructions/InstEmitMemoryEx.cs +++ b/ARMeilleure/Instructions/InstEmitMemoryEx.cs @@ -23,7 +23,7 @@ namespace ARMeilleure.Instructions public static void Clrex(ArmEmitterContext context) { - context.Call(new _Void(NativeInterface.ClearExclusive)); + context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ClearExclusive))); } public static void Dmb(ArmEmitterContext context) => EmitBarrier(context); @@ -101,6 +101,7 @@ namespace ARMeilleure.Instructions SetIntOrZR(context, op.Rt, value); } } + public static void Pfrm(ArmEmitterContext context) { // Memory Prefetch, execute as no-op. diff --git a/ARMeilleure/Instructions/InstEmitMemoryEx32.cs b/ARMeilleure/Instructions/InstEmitMemoryEx32.cs index 0ab990f87..e8e660ee8 100644 --- a/ARMeilleure/Instructions/InstEmitMemoryEx32.cs +++ b/ARMeilleure/Instructions/InstEmitMemoryEx32.cs @@ -13,7 +13,7 @@ namespace ARMeilleure.Instructions { public static void Clrex(ArmEmitterContext context) { - context.Call(new _Void(NativeInterface.ClearExclusive)); + context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ClearExclusive))); } public static void Dmb(ArmEmitterContext context) => EmitBarrier(context); diff --git a/ARMeilleure/Instructions/InstEmitMemoryExHelper.cs b/ARMeilleure/Instructions/InstEmitMemoryExHelper.cs index 00a5385bd..059b9b6a4 100644 --- a/ARMeilleure/Instructions/InstEmitMemoryExHelper.cs +++ b/ARMeilleure/Instructions/InstEmitMemoryExHelper.cs @@ -1,6 +1,6 @@ using ARMeilleure.IntermediateRepresentation; using ARMeilleure.Translation; -using System; +using System.Reflection; namespace ARMeilleure.Instructions { @@ -12,32 +12,32 @@ namespace ARMeilleure.Instructions bool exclusive, int size) { - Delegate fallbackMethodDlg = null; + MethodInfo info = null; if (exclusive) { switch (size) { - case 0: fallbackMethodDlg = new _U8_U64(NativeInterface.ReadByteExclusive); break; - case 1: fallbackMethodDlg = new _U16_U64(NativeInterface.ReadUInt16Exclusive); break; - case 2: fallbackMethodDlg = new _U32_U64(NativeInterface.ReadUInt32Exclusive); break; - case 3: fallbackMethodDlg = new _U64_U64(NativeInterface.ReadUInt64Exclusive); break; - case 4: fallbackMethodDlg = new _V128_U64(NativeInterface.ReadVector128Exclusive); break; + case 0: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadByteExclusive)); break; + case 1: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt16Exclusive)); break; + case 2: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt32Exclusive)); break; + case 3: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt64Exclusive)); break; + case 4: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadVector128Exclusive)); break; } } else { switch (size) { - case 0: fallbackMethodDlg = new _U8_U64(NativeInterface.ReadByte); break; - case 1: fallbackMethodDlg = new _U16_U64(NativeInterface.ReadUInt16); break; - case 2: fallbackMethodDlg = new _U32_U64(NativeInterface.ReadUInt32); break; - case 3: fallbackMethodDlg = new _U64_U64(NativeInterface.ReadUInt64); break; - case 4: fallbackMethodDlg = new _V128_U64(NativeInterface.ReadVector128); break; + case 0: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadByte)); break; + case 1: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt16)); break; + case 2: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt32)); break; + case 3: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt64)); break; + case 4: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadVector128)); break; } } - return context.Call(fallbackMethodDlg, address); + return context.Call(info, address); } public static Operand EmitStoreExclusive( @@ -52,33 +52,33 @@ namespace ARMeilleure.Instructions value = context.ConvertI64ToI32(value); } - Delegate fallbackMethodDlg = null; + MethodInfo info = null; if (exclusive) { switch (size) { - case 0: fallbackMethodDlg = new _S32_U64_U8(NativeInterface.WriteByteExclusive); break; - case 1: fallbackMethodDlg = new _S32_U64_U16(NativeInterface.WriteUInt16Exclusive); break; - case 2: fallbackMethodDlg = new _S32_U64_U32(NativeInterface.WriteUInt32Exclusive); break; - case 3: fallbackMethodDlg = new _S32_U64_U64(NativeInterface.WriteUInt64Exclusive); break; - case 4: fallbackMethodDlg = new _S32_U64_V128(NativeInterface.WriteVector128Exclusive); break; + case 0: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteByteExclusive)); break; + case 1: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt16Exclusive)); break; + case 2: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt32Exclusive)); break; + case 3: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt64Exclusive)); break; + case 4: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteVector128Exclusive)); break; } - return context.Call(fallbackMethodDlg, address, value); + return context.Call(info, address, value); } else { switch (size) { - case 0: fallbackMethodDlg = new _Void_U64_U8(NativeInterface.WriteByte); break; - case 1: fallbackMethodDlg = new _Void_U64_U16(NativeInterface.WriteUInt16); break; - case 2: fallbackMethodDlg = new _Void_U64_U32(NativeInterface.WriteUInt32); break; - case 3: fallbackMethodDlg = new _Void_U64_U64(NativeInterface.WriteUInt64); break; - case 4: fallbackMethodDlg = new _Void_U64_V128(NativeInterface.WriteVector128); break; + case 0: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteByte)); break; + case 1: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt16)); break; + case 2: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt32)); break; + case 3: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt64)); break; + case 4: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteVector128)); break; } - context.Call(fallbackMethodDlg, address, value); + context.Call(info, address, value); return null; } diff --git a/ARMeilleure/Instructions/InstEmitMemoryHelper.cs b/ARMeilleure/Instructions/InstEmitMemoryHelper.cs index b6a4d3912..18e27e5a7 100644 --- a/ARMeilleure/Instructions/InstEmitMemoryHelper.cs +++ b/ARMeilleure/Instructions/InstEmitMemoryHelper.cs @@ -1,7 +1,9 @@ using ARMeilleure.Decoders; using ARMeilleure.IntermediateRepresentation; using ARMeilleure.Translation; +using ARMeilleure.Translation.PTC; using System; +using System.Reflection; using static ARMeilleure.Instructions.InstEmitHelper; using static ARMeilleure.IntermediateRepresentation.OperandHelper; @@ -144,21 +146,10 @@ namespace ARMeilleure.Instructions switch (size) { - case 0: - value = context.Load8(physAddr); - break; - - case 1: - value = context.Load16(physAddr); - break; - - case 2: - value = context.Load(OperandType.I32, physAddr); - break; - - case 3: - value = context.Load(OperandType.I64, physAddr); - break; + case 0: value = context.Load8 (physAddr); break; + case 1: value = context.Load16(physAddr); break; + case 2: value = context.Load (OperandType.I32, physAddr); break; + case 3: value = context.Load (OperandType.I64, physAddr); break; } SetInt(context, rt, value); @@ -196,25 +187,11 @@ namespace ARMeilleure.Instructions switch (size) { - case 0: - value = context.VectorInsert8(vector, context.Load8(physAddr), elem); - break; - - case 1: - value = context.VectorInsert16(vector, context.Load16(physAddr), elem); - break; - - case 2: - value = context.VectorInsert(vector, context.Load(OperandType.I32, physAddr), elem); - break; - - case 3: - value = context.VectorInsert(vector, context.Load(OperandType.I64, physAddr), elem); - break; - - case 4: - value = context.Load(OperandType.V128, physAddr); - break; + case 0: value = context.VectorInsert8 (vector, context.Load8(physAddr), elem); break; + case 1: value = context.VectorInsert16(vector, context.Load16(physAddr), elem); break; + case 2: value = context.VectorInsert (vector, context.Load(OperandType.I32, physAddr), elem); break; + case 3: value = context.VectorInsert (vector, context.Load(OperandType.I64, physAddr), elem); break; + case 4: value = context.Load (OperandType.V128, physAddr); break; } context.Copy(GetVec(rt), value); @@ -294,25 +271,11 @@ namespace ARMeilleure.Instructions switch (size) { - case 0: - context.Store8(physAddr, context.VectorExtract8(value, elem)); - break; - - case 1: - context.Store16(physAddr, context.VectorExtract16(value, elem)); - break; - - case 2: - context.Store(physAddr, context.VectorExtract(OperandType.FP32, value, elem)); - break; - - case 3: - context.Store(physAddr, context.VectorExtract(OperandType.FP64, value, elem)); - break; - - case 4: - context.Store(physAddr, value); - break; + case 0: context.Store8 (physAddr, context.VectorExtract8(value, elem)); break; + case 1: context.Store16(physAddr, context.VectorExtract16(value, elem)); break; + case 2: context.Store (physAddr, context.VectorExtract(OperandType.FP32, value, elem)); break; + case 3: context.Store (physAddr, context.VectorExtract(OperandType.FP64, value, elem)); break; + case 4: context.Store (physAddr, value); break; } context.MarkLabel(lblEnd); @@ -333,7 +296,9 @@ namespace ARMeilleure.Instructions int ptLevelSize = 1 << ptLevelBits; int ptLevelMask = ptLevelSize - 1; - Operand pte = Const(context.Memory.PageTablePointer.ToInt64()); + Operand pte = Ptc.State == PtcState.Disabled + ? Const(context.Memory.PageTablePointer.ToInt64()) + : Const(context.Memory.PageTablePointer.ToInt64(), true, Ptc.PageTablePointerIndex); int bit = PageBits; @@ -375,17 +340,17 @@ namespace ARMeilleure.Instructions private static void EmitReadIntFallback(ArmEmitterContext context, Operand address, int rt, int size) { - Delegate fallbackMethodDlg = null; + MethodInfo info = null; switch (size) { - case 0: fallbackMethodDlg = new _U8_U64 (NativeInterface.ReadByte); break; - case 1: fallbackMethodDlg = new _U16_U64(NativeInterface.ReadUInt16); break; - case 2: fallbackMethodDlg = new _U32_U64(NativeInterface.ReadUInt32); break; - case 3: fallbackMethodDlg = new _U64_U64(NativeInterface.ReadUInt64); break; + case 0: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadByte)); break; + case 1: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt16)); break; + case 2: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt32)); break; + case 3: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt64)); break; } - SetInt(context, rt, context.Call(fallbackMethodDlg, address)); + SetInt(context, rt, context.Call(info, address)); } private static void EmitReadVectorFallback( @@ -396,18 +361,18 @@ namespace ARMeilleure.Instructions int elem, int size) { - Delegate fallbackMethodDlg = null; + MethodInfo info = null; switch (size) { - case 0: fallbackMethodDlg = new _U8_U64 (NativeInterface.ReadByte); break; - case 1: fallbackMethodDlg = new _U16_U64 (NativeInterface.ReadUInt16); break; - case 2: fallbackMethodDlg = new _U32_U64 (NativeInterface.ReadUInt32); break; - case 3: fallbackMethodDlg = new _U64_U64 (NativeInterface.ReadUInt64); break; - case 4: fallbackMethodDlg = new _V128_U64(NativeInterface.ReadVector128); break; + case 0: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadByte)); break; + case 1: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt16)); break; + case 2: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt32)); break; + case 3: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt64)); break; + case 4: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadVector128)); break; } - Operand value = context.Call(fallbackMethodDlg, address); + Operand value = context.Call(info, address); switch (size) { @@ -422,14 +387,14 @@ namespace ARMeilleure.Instructions private static void EmitWriteIntFallback(ArmEmitterContext context, Operand address, int rt, int size) { - Delegate fallbackMethodDlg = null; + MethodInfo info = null; switch (size) { - case 0: fallbackMethodDlg = new _Void_U64_U8 (NativeInterface.WriteByte); break; - case 1: fallbackMethodDlg = new _Void_U64_U16(NativeInterface.WriteUInt16); break; - case 2: fallbackMethodDlg = new _Void_U64_U32(NativeInterface.WriteUInt32); break; - case 3: fallbackMethodDlg = new _Void_U64_U64(NativeInterface.WriteUInt64); break; + case 0: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteByte)); break; + case 1: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt16)); break; + case 2: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt32)); break; + case 3: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt64)); break; } Operand value = GetInt(context, rt); @@ -439,7 +404,7 @@ namespace ARMeilleure.Instructions value = context.ConvertI64ToI32(value); } - context.Call(fallbackMethodDlg, address, value); + context.Call(info, address, value); } private static void EmitWriteVectorFallback( @@ -449,15 +414,15 @@ namespace ARMeilleure.Instructions int elem, int size) { - Delegate fallbackMethodDlg = null; + MethodInfo info = null; switch (size) { - case 0: fallbackMethodDlg = new _Void_U64_U8 (NativeInterface.WriteByte); break; - case 1: fallbackMethodDlg = new _Void_U64_U16 (NativeInterface.WriteUInt16); break; - case 2: fallbackMethodDlg = new _Void_U64_U32 (NativeInterface.WriteUInt32); break; - case 3: fallbackMethodDlg = new _Void_U64_U64 (NativeInterface.WriteUInt64); break; - case 4: fallbackMethodDlg = new _Void_U64_V128(NativeInterface.WriteVector128); break; + case 0: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteByte)); break; + case 1: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt16)); break; + case 2: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt32)); break; + case 3: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt64)); break; + case 4: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteVector128)); break; } Operand value = null; @@ -466,21 +431,10 @@ namespace ARMeilleure.Instructions { switch (size) { - case 0: - value = context.VectorExtract8(GetVec(rt), elem); - break; - - case 1: - value = context.VectorExtract16(GetVec(rt), elem); - break; - - case 2: - value = context.VectorExtract(OperandType.I32, GetVec(rt), elem); - break; - - case 3: - value = context.VectorExtract(OperandType.I64, GetVec(rt), elem); - break; + case 0: value = context.VectorExtract8 (GetVec(rt), elem); break; + case 1: value = context.VectorExtract16(GetVec(rt), elem); break; + case 2: value = context.VectorExtract (OperandType.I32, GetVec(rt), elem); break; + case 3: value = context.VectorExtract (OperandType.I64, GetVec(rt), elem); break; } } else @@ -488,7 +442,7 @@ namespace ARMeilleure.Instructions value = GetVec(rt); } - context.Call(fallbackMethodDlg, address, value); + context.Call(info, address, value); } private static Operand GetInt(ArmEmitterContext context, int rt) @@ -571,4 +525,4 @@ namespace ARMeilleure.Instructions return m; } } -} \ No newline at end of file +} diff --git a/ARMeilleure/Instructions/InstEmitSimdArithmetic.cs b/ARMeilleure/Instructions/InstEmitSimdArithmetic.cs index 8c2d604ca..b3041aac5 100644 --- a/ARMeilleure/Instructions/InstEmitSimdArithmetic.cs +++ b/ARMeilleure/Instructions/InstEmitSimdArithmetic.cs @@ -6,6 +6,7 @@ using ARMeilleure.IntermediateRepresentation; using ARMeilleure.State; using ARMeilleure.Translation; using System; +using System.Diagnostics; using static ARMeilleure.Instructions.InstEmitHelper; using static ARMeilleure.Instructions.InstEmitSimdHelper; @@ -106,7 +107,7 @@ namespace ARMeilleure.Instructions { Operand ne = EmitVectorExtractZx(context, op.Rn, index, op.Size); - Operand de = context.Call(new _U64_U64_S32(SoftFallback.CountLeadingSigns), ne, Const(eSize)); + Operand de = context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.CountLeadingSigns)), ne, Const(eSize)); res = EmitVectorInsert(context, res, de, index, op.Size); } @@ -128,16 +129,7 @@ namespace ARMeilleure.Instructions { Operand ne = EmitVectorExtractZx(context, op.Rn, index, op.Size); - Operand de; - - if (eSize == 64) - { - de = context.CountLeadingZeros(ne); - } - else - { - de = context.Call(new _U64_U64_S32(SoftFallback.CountLeadingZeros), ne, Const(eSize)); - } + Operand de = context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.CountLeadingZeros)), ne, Const(eSize)); res = EmitVectorInsert(context, res, de, index, op.Size); } @@ -165,7 +157,7 @@ namespace ARMeilleure.Instructions } else { - de = context.Call(new _U64_U64(SoftFallback.CountSetBits8), ne); + de = context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.CountSetBits8)), ne); } res = EmitVectorInsert(context, res, de, index, 0); @@ -203,9 +195,9 @@ namespace ARMeilleure.Instructions { EmitScalarBinaryOpF(context, (op1, op2) => { - Operand res = EmitSoftFloatCall(context, SoftFloat32.FPSub, SoftFloat64.FPSub, op1, op2); + Operand res = EmitSoftFloatCall(context, nameof(SoftFloat32.FPSub), op1, op2); - return EmitUnaryMathCall(context, MathF.Abs, Math.Abs, res); + return EmitUnaryMathCall(context, nameof(Math.Abs), res); }); } } @@ -244,9 +236,9 @@ namespace ARMeilleure.Instructions { EmitVectorBinaryOpF(context, (op1, op2) => { - Operand res = EmitSoftFloatCall(context, SoftFloat32.FPSub, SoftFloat64.FPSub, op1, op2); + Operand res = EmitSoftFloatCall(context, nameof(SoftFloat32.FPSub), op1, op2); - return EmitUnaryMathCall(context, MathF.Abs, Math.Abs, res); + return EmitUnaryMathCall(context, nameof(Math.Abs), res); }); } } @@ -274,7 +266,7 @@ namespace ARMeilleure.Instructions { EmitScalarUnaryOpF(context, (op1) => { - return EmitUnaryMathCall(context, MathF.Abs, Math.Abs, op1); + return EmitUnaryMathCall(context, nameof(Math.Abs), op1); }); } } @@ -309,7 +301,7 @@ namespace ARMeilleure.Instructions { EmitVectorUnaryOpF(context, (op1) => { - return EmitUnaryMathCall(context, MathF.Abs, Math.Abs, op1); + return EmitUnaryMathCall(context, nameof(Math.Abs), op1); }); } } @@ -328,7 +320,7 @@ namespace ARMeilleure.Instructions { EmitScalarBinaryOpF(context, (op1, op2) => { - return EmitSoftFloatCall(context, SoftFloat32.FPAdd, SoftFloat64.FPAdd, op1, op2); + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPAdd), op1, op2); }); } } @@ -347,7 +339,7 @@ namespace ARMeilleure.Instructions { EmitVectorBinaryOpF(context, (op1, op2) => { - return EmitSoftFloatCall(context, SoftFloat32.FPAdd, SoftFloat64.FPAdd, op1, op2); + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPAdd), op1, op2); }); } } @@ -380,7 +372,7 @@ namespace ARMeilleure.Instructions Operand ne0 = context.VectorExtract(type, GetVec(op.Rn), 0); Operand ne1 = context.VectorExtract(type, GetVec(op.Rn), 1); - Operand res = EmitSoftFloatCall(context, SoftFloat32.FPAdd, SoftFloat64.FPAdd, ne0, ne1); + Operand res = EmitSoftFloatCall(context, nameof(SoftFloat32.FPAdd), ne0, ne1); context.Copy(GetVec(op.Rd), context.VectorInsert(context.VectorZero(), res, 0)); } @@ -396,7 +388,7 @@ namespace ARMeilleure.Instructions { EmitVectorPairwiseOpF(context, (op1, op2) => { - return EmitSoftFloatCall(context, SoftFloat32.FPAdd, SoftFloat64.FPAdd, op1, op2); + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPAdd), op1, op2); }); } } @@ -415,7 +407,7 @@ namespace ARMeilleure.Instructions { EmitScalarBinaryOpF(context, (op1, op2) => { - return EmitSoftFloatCall(context, SoftFloat32.FPDiv, SoftFloat64.FPDiv, op1, op2); + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPDiv), op1, op2); }); } } @@ -434,7 +426,7 @@ namespace ARMeilleure.Instructions { EmitVectorBinaryOpF(context, (op1, op2) => { - return EmitSoftFloatCall(context, SoftFloat32.FPDiv, SoftFloat64.FPDiv, op1, op2); + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPDiv), op1, op2); }); } } @@ -469,7 +461,7 @@ namespace ARMeilleure.Instructions { EmitScalarTernaryRaOpF(context, (op1, op2, op3) => { - return EmitSoftFloatCall(context, SoftFloat32.FPMulAdd, SoftFloat64.FPMulAdd, op1, op2, op3); + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPMulAdd), op1, op2, op3); }); } } @@ -484,7 +476,7 @@ namespace ARMeilleure.Instructions { EmitScalarBinaryOpF(context, (op1, op2) => { - return EmitSoftFloatCall(context, SoftFloat32.FPMax, SoftFloat64.FPMax, op1, op2); + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPMax), op1, op2); }); } } @@ -499,7 +491,7 @@ namespace ARMeilleure.Instructions { EmitVectorBinaryOpF(context, (op1, op2) => { - return EmitSoftFloatCall(context, SoftFloat32.FPMax, SoftFloat64.FPMax, op1, op2); + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPMax), op1, op2); }); } } @@ -514,7 +506,7 @@ namespace ARMeilleure.Instructions { EmitScalarBinaryOpF(context, (op1, op2) => { - return EmitSoftFloatCall(context, SoftFloat32.FPMaxNum, SoftFloat64.FPMaxNum, op1, op2); + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPMaxNum), op1, op2); }); } } @@ -529,7 +521,7 @@ namespace ARMeilleure.Instructions { EmitVectorBinaryOpF(context, (op1, op2) => { - return EmitSoftFloatCall(context, SoftFloat32.FPMaxNum, SoftFloat64.FPMaxNum, op1, op2); + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPMaxNum), op1, op2); }); } } @@ -538,7 +530,7 @@ namespace ARMeilleure.Instructions { EmitVectorAcrossVectorOpF(context, (op1, op2) => { - return context.Call(new _F32_F32_F32(SoftFloat32.FPMaxNum), op1, op2); + return context.Call(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMaxNum)), op1, op2); }); } @@ -552,7 +544,7 @@ namespace ARMeilleure.Instructions { EmitVectorPairwiseOpF(context, (op1, op2) => { - return EmitSoftFloatCall(context, SoftFloat32.FPMax, SoftFloat64.FPMax, op1, op2); + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPMax), op1, op2); }); } } @@ -567,7 +559,7 @@ namespace ARMeilleure.Instructions { EmitScalarBinaryOpF(context, (op1, op2) => { - return EmitSoftFloatCall(context, SoftFloat32.FPMin, SoftFloat64.FPMin, op1, op2); + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPMin), op1, op2); }); } } @@ -582,7 +574,7 @@ namespace ARMeilleure.Instructions { EmitVectorBinaryOpF(context, (op1, op2) => { - return EmitSoftFloatCall(context, SoftFloat32.FPMin, SoftFloat64.FPMin, op1, op2); + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPMin), op1, op2); }); } } @@ -597,7 +589,7 @@ namespace ARMeilleure.Instructions { EmitScalarBinaryOpF(context, (op1, op2) => { - return EmitSoftFloatCall(context, SoftFloat32.FPMinNum, SoftFloat64.FPMinNum, op1, op2); + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPMinNum), op1, op2); }); } } @@ -612,7 +604,7 @@ namespace ARMeilleure.Instructions { EmitVectorBinaryOpF(context, (op1, op2) => { - return EmitSoftFloatCall(context, SoftFloat32.FPMinNum, SoftFloat64.FPMinNum, op1, op2); + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPMinNum), op1, op2); }); } } @@ -621,7 +613,7 @@ namespace ARMeilleure.Instructions { EmitVectorAcrossVectorOpF(context, (op1, op2) => { - return context.Call(new _F32_F32_F32(SoftFloat32.FPMinNum), op1, op2); + return context.Call(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMinNum)), op1, op2); }); } @@ -635,7 +627,7 @@ namespace ARMeilleure.Instructions { EmitVectorPairwiseOpF(context, (op1, op2) => { - return EmitSoftFloatCall(context, SoftFloat32.FPMin, SoftFloat64.FPMin, op1, op2); + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPMin), op1, op2); }); } } @@ -686,7 +678,7 @@ namespace ARMeilleure.Instructions { EmitVectorTernaryOpF(context, (op1, op2, op3) => { - return EmitSoftFloatCall(context, SoftFloat32.FPMulAdd, SoftFloat64.FPMulAdd, op1, op2, op3); + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPMulAdd), op1, op2, op3); }); } } @@ -735,7 +727,7 @@ namespace ARMeilleure.Instructions { EmitVectorTernaryOpByElemF(context, (op1, op2, op3) => { - return EmitSoftFloatCall(context, SoftFloat32.FPMulAdd, SoftFloat64.FPMulAdd, op1, op2, op3); + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPMulAdd), op1, op2, op3); }); } } @@ -786,7 +778,7 @@ namespace ARMeilleure.Instructions { EmitVectorTernaryOpF(context, (op1, op2, op3) => { - return EmitSoftFloatCall(context, SoftFloat32.FPMulSub, SoftFloat64.FPMulSub, op1, op2, op3); + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPMulSub), op1, op2, op3); }); } } @@ -835,7 +827,7 @@ namespace ARMeilleure.Instructions { EmitVectorTernaryOpByElemF(context, (op1, op2, op3) => { - return EmitSoftFloatCall(context, SoftFloat32.FPMulSub, SoftFloat64.FPMulSub, op1, op2, op3); + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPMulSub), op1, op2, op3); }); } } @@ -870,7 +862,7 @@ namespace ARMeilleure.Instructions { EmitScalarTernaryRaOpF(context, (op1, op2, op3) => { - return EmitSoftFloatCall(context, SoftFloat32.FPMulSub, SoftFloat64.FPMulSub, op1, op2, op3); + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPMulSub), op1, op2, op3); }); } } @@ -889,7 +881,7 @@ namespace ARMeilleure.Instructions { EmitScalarBinaryOpF(context, (op1, op2) => { - return EmitSoftFloatCall(context, SoftFloat32.FPMul, SoftFloat64.FPMul, op1, op2); + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPMul), op1, op2); }); } } @@ -913,7 +905,7 @@ namespace ARMeilleure.Instructions { EmitVectorBinaryOpF(context, (op1, op2) => { - return EmitSoftFloatCall(context, SoftFloat32.FPMul, SoftFloat64.FPMul, op1, op2); + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPMul), op1, op2); }); } } @@ -963,7 +955,7 @@ namespace ARMeilleure.Instructions { EmitVectorBinaryOpByElemF(context, (op1, op2) => { - return EmitSoftFloatCall(context, SoftFloat32.FPMul, SoftFloat64.FPMul, op1, op2); + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPMul), op1, op2); }); } } @@ -972,7 +964,7 @@ namespace ARMeilleure.Instructions { EmitScalarBinaryOpF(context, (op1, op2) => { - return EmitSoftFloatCall(context, SoftFloat32.FPMulX, SoftFloat64.FPMulX, op1, op2); + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPMulX), op1, op2); }); } @@ -980,7 +972,7 @@ namespace ARMeilleure.Instructions { EmitScalarBinaryOpByElemF(context, (op1, op2) => { - return EmitSoftFloatCall(context, SoftFloat32.FPMulX, SoftFloat64.FPMulX, op1, op2); + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPMulX), op1, op2); }); } @@ -988,7 +980,7 @@ namespace ARMeilleure.Instructions { EmitVectorBinaryOpF(context, (op1, op2) => { - return EmitSoftFloatCall(context, SoftFloat32.FPMulX, SoftFloat64.FPMulX, op1, op2); + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPMulX), op1, op2); }); } @@ -996,7 +988,7 @@ namespace ARMeilleure.Instructions { EmitVectorBinaryOpByElemF(context, (op1, op2) => { - return EmitSoftFloatCall(context, SoftFloat32.FPMulX, SoftFloat64.FPMulX, op1, op2); + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPMulX), op1, op2); }); } @@ -1103,7 +1095,7 @@ namespace ARMeilleure.Instructions { EmitScalarTernaryRaOpF(context, (op1, op2, op3) => { - return EmitSoftFloatCall(context, SoftFloat32.FPNegMulAdd, SoftFloat64.FPNegMulAdd, op1, op2, op3); + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPNegMulAdd), op1, op2, op3); }); } } @@ -1146,7 +1138,7 @@ namespace ARMeilleure.Instructions { EmitScalarTernaryRaOpF(context, (op1, op2, op3) => { - return EmitSoftFloatCall(context, SoftFloat32.FPNegMulSub, SoftFloat64.FPNegMulSub, op1, op2, op3); + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPNegMulSub), op1, op2, op3); }); } } @@ -1170,7 +1162,7 @@ namespace ARMeilleure.Instructions { EmitScalarUnaryOpF(context, (op1) => { - return EmitSoftFloatCall(context, SoftFloat32.FPRecipEstimate, SoftFloat64.FPRecipEstimate, op1); + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPRecipEstimate), op1); }); } } @@ -1189,7 +1181,7 @@ namespace ARMeilleure.Instructions { EmitVectorUnaryOpF(context, (op1) => { - return EmitSoftFloatCall(context, SoftFloat32.FPRecipEstimate, SoftFloat64.FPRecipEstimate, op1); + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPRecipEstimate), op1); }); } } @@ -1227,7 +1219,7 @@ namespace ARMeilleure.Instructions { EmitScalarBinaryOpF(context, (op1, op2) => { - return EmitSoftFloatCall(context, SoftFloat32.FPRecipStepFused, SoftFloat64.FPRecipStepFused, op1, op2); + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPRecipStepFused), op1, op2); }); } } @@ -1270,7 +1262,7 @@ namespace ARMeilleure.Instructions { EmitVectorBinaryOpF(context, (op1, op2) => { - return EmitSoftFloatCall(context, SoftFloat32.FPRecipStepFused, SoftFloat64.FPRecipStepFused, op1, op2); + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPRecipStepFused), op1, op2); }); } } @@ -1279,7 +1271,7 @@ namespace ARMeilleure.Instructions { EmitScalarUnaryOpF(context, (op1) => { - return EmitSoftFloatCall(context, SoftFloat32.FPRecpX, SoftFloat64.FPRecpX, op1); + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPRecpX), op1); }); } @@ -1307,11 +1299,11 @@ namespace ARMeilleure.Instructions { if (op.Size == 0) { - return context.Call(new _F32_F32(SoftFallback.RoundF), op1); + return context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.RoundF)), op1); } else /* if (op.Size == 1) */ { - return context.Call(new _F64_F64(SoftFallback.Round), op1); + return context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Round)), op1); } }); } @@ -1326,11 +1318,11 @@ namespace ARMeilleure.Instructions { if (sizeF == 0) { - return context.Call(new _F32_F32(SoftFallback.RoundF), op1); + return context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.RoundF)), op1); } else /* if (sizeF == 1) */ { - return context.Call(new _F64_F64(SoftFallback.Round), op1); + return context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Round)), op1); } }); } @@ -1345,7 +1337,7 @@ namespace ARMeilleure.Instructions { EmitScalarUnaryOpF(context, (op1) => { - return EmitUnaryMathCall(context, MathF.Floor, Math.Floor, op1); + return EmitUnaryMathCall(context, nameof(Math.Floor), op1); }); } } @@ -1360,7 +1352,7 @@ namespace ARMeilleure.Instructions { EmitVectorUnaryOpF(context, (op1) => { - return EmitUnaryMathCall(context, MathF.Floor, Math.Floor, op1); + return EmitUnaryMathCall(context, nameof(Math.Floor), op1); }); } } @@ -1405,7 +1397,7 @@ namespace ARMeilleure.Instructions { EmitScalarUnaryOpF(context, (op1) => { - return EmitUnaryMathCall(context, MathF.Ceiling, Math.Ceiling, op1); + return EmitUnaryMathCall(context, nameof(Math.Ceiling), op1); }); } } @@ -1420,7 +1412,7 @@ namespace ARMeilleure.Instructions { EmitVectorUnaryOpF(context, (op1) => { - return EmitUnaryMathCall(context, MathF.Ceiling, Math.Ceiling, op1); + return EmitUnaryMathCall(context, nameof(Math.Ceiling), op1); }); } } @@ -1433,11 +1425,11 @@ namespace ARMeilleure.Instructions { if (op.Size == 0) { - return context.Call(new _F32_F32(SoftFallback.RoundF), op1); + return context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.RoundF)), op1); } else /* if (op.Size == 1) */ { - return context.Call(new _F64_F64(SoftFallback.Round), op1); + return context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Round)), op1); } }); } @@ -1452,11 +1444,11 @@ namespace ARMeilleure.Instructions { if (sizeF == 0) { - return context.Call(new _F32_F32(SoftFallback.RoundF), op1); + return context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.RoundF)), op1); } else /* if (sizeF == 1) */ { - return context.Call(new _F64_F64(SoftFallback.Round), op1); + return context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Round)), op1); } }); } @@ -1471,7 +1463,7 @@ namespace ARMeilleure.Instructions { EmitScalarUnaryOpF(context, (op1) => { - return EmitUnaryMathCall(context, MathF.Truncate, Math.Truncate, op1); + return EmitUnaryMathCall(context, nameof(Math.Truncate), op1); }); } } @@ -1486,7 +1478,7 @@ namespace ARMeilleure.Instructions { EmitVectorUnaryOpF(context, (op1) => { - return EmitUnaryMathCall(context, MathF.Truncate, Math.Truncate, op1); + return EmitUnaryMathCall(context, nameof(Math.Truncate), op1); }); } } @@ -1505,7 +1497,7 @@ namespace ARMeilleure.Instructions { EmitScalarUnaryOpF(context, (op1) => { - return EmitSoftFloatCall(context, SoftFloat32.FPRSqrtEstimate, SoftFloat64.FPRSqrtEstimate, op1); + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPRSqrtEstimate), op1); }); } } @@ -1524,7 +1516,7 @@ namespace ARMeilleure.Instructions { EmitVectorUnaryOpF(context, (op1) => { - return EmitSoftFloatCall(context, SoftFloat32.FPRSqrtEstimate, SoftFloat64.FPRSqrtEstimate, op1); + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPRSqrtEstimate), op1); }); } } @@ -1566,7 +1558,7 @@ namespace ARMeilleure.Instructions { EmitScalarBinaryOpF(context, (op1, op2) => { - return EmitSoftFloatCall(context, SoftFloat32.FPRSqrtStepFused, SoftFloat64.FPRSqrtStepFused, op1, op2); + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPRSqrtStepFused), op1, op2); }); } } @@ -1613,7 +1605,7 @@ namespace ARMeilleure.Instructions { EmitVectorBinaryOpF(context, (op1, op2) => { - return EmitSoftFloatCall(context, SoftFloat32.FPRSqrtStepFused, SoftFloat64.FPRSqrtStepFused, op1, op2); + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPRSqrtStepFused), op1, op2); }); } } @@ -1628,7 +1620,7 @@ namespace ARMeilleure.Instructions { EmitScalarUnaryOpF(context, (op1) => { - return EmitSoftFloatCall(context, SoftFloat32.FPSqrt, SoftFloat64.FPSqrt, op1); + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPSqrt), op1); }); } } @@ -1643,7 +1635,7 @@ namespace ARMeilleure.Instructions { EmitVectorUnaryOpF(context, (op1) => { - return EmitSoftFloatCall(context, SoftFloat32.FPSqrt, SoftFloat64.FPSqrt, op1); + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPSqrt), op1); }); } } @@ -1662,7 +1654,7 @@ namespace ARMeilleure.Instructions { EmitScalarBinaryOpF(context, (op1, op2) => { - return EmitSoftFloatCall(context, SoftFloat32.FPSub, SoftFloat64.FPSub, op1, op2); + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPSub), op1, op2); }); } } @@ -1681,7 +1673,7 @@ namespace ARMeilleure.Instructions { EmitVectorBinaryOpF(context, (op1, op2) => { - return EmitSoftFloatCall(context, SoftFloat32.FPSub, SoftFloat64.FPSub, op1, op2); + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPSub), op1, op2); }); } } @@ -1690,7 +1682,7 @@ namespace ARMeilleure.Instructions { if (Optimizations.UseSse41) { - EmitSse41Mul_AddSub(context, AddSub.Add); + EmitSse41VectorMul_AddSub(context, AddSub.Add); } else { @@ -1713,7 +1705,7 @@ namespace ARMeilleure.Instructions { if (Optimizations.UseSse41) { - EmitSse41Mul_AddSub(context, AddSub.Subtract); + EmitSse41VectorMul_AddSub(context, AddSub.Subtract); } else { @@ -1736,7 +1728,7 @@ namespace ARMeilleure.Instructions { if (Optimizations.UseSse41) { - EmitSse41Mul_AddSub(context, AddSub.None); + EmitSse41VectorMul_AddSub(context, AddSub.None); } else { @@ -1805,14 +1797,14 @@ namespace ARMeilleure.Instructions public static void Sabd_V(ArmEmitterContext context) { - if (Optimizations.UseSse2) + if (Optimizations.UseSse41) { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; Operand n = GetVec(op.Rn); Operand m = GetVec(op.Rm); - EmitSse41Sabd(context, op, n, m, isLong: false); + EmitSse41VectorSabdOp(context, op, n, m, isLong: false); } else { @@ -1845,7 +1837,7 @@ namespace ARMeilleure.Instructions n = context.AddIntrinsic(movInst, n); m = context.AddIntrinsic(movInst, m); - EmitSse41Sabd(context, op, n, m, isLong: true); + EmitSse41VectorSabdOp(context, op, n, m, isLong: true); } else { @@ -2027,9 +2019,7 @@ namespace ARMeilleure.Instructions } else { - Delegate dlg = new _S64_S64_S64(Math.Max); - - EmitVectorBinaryOpSx(context, (op1, op2) => context.Call(dlg, op1, op2)); + EmitVectorBinaryOpSx(context, (op1, op2) => EmitMax64Op(context, op1, op2, signed: true)); } } @@ -2041,17 +2031,13 @@ namespace ARMeilleure.Instructions } else { - Delegate dlg = new _S64_S64_S64(Math.Max); - - EmitVectorPairwiseOpSx(context, (op1, op2) => context.Call(dlg, op1, op2)); + EmitVectorPairwiseOpSx(context, (op1, op2) => EmitMax64Op(context, op1, op2, signed: true)); } } public static void Smaxv_V(ArmEmitterContext context) { - Delegate dlg = new _S64_S64_S64(Math.Max); - - EmitVectorAcrossVectorOpSx(context, (op1, op2) => context.Call(dlg, op1, op2)); + EmitVectorAcrossVectorOpSx(context, (op1, op2) => EmitMax64Op(context, op1, op2, signed: true)); } public static void Smin_V(ArmEmitterContext context) @@ -2076,9 +2062,7 @@ namespace ARMeilleure.Instructions } else { - Delegate dlg = new _S64_S64_S64(Math.Min); - - EmitVectorBinaryOpSx(context, (op1, op2) => context.Call(dlg, op1, op2)); + EmitVectorBinaryOpSx(context, (op1, op2) => EmitMin64Op(context, op1, op2, signed: true)); } } @@ -2090,17 +2074,13 @@ namespace ARMeilleure.Instructions } else { - Delegate dlg = new _S64_S64_S64(Math.Min); - - EmitVectorPairwiseOpSx(context, (op1, op2) => context.Call(dlg, op1, op2)); + EmitVectorPairwiseOpSx(context, (op1, op2) => EmitMin64Op(context, op1, op2, signed: true)); } } public static void Sminv_V(ArmEmitterContext context) { - Delegate dlg = new _S64_S64_S64(Math.Min); - - EmitVectorAcrossVectorOpSx(context, (op1, op2) => context.Call(dlg, op1, op2)); + EmitVectorAcrossVectorOpSx(context, (op1, op2) => EmitMin64Op(context, op1, op2, signed: true)); } public static void Smlal_V(ArmEmitterContext context) @@ -2458,7 +2438,7 @@ namespace ARMeilleure.Instructions Operand n = GetVec(op.Rn); Operand m = GetVec(op.Rm); - EmitSse41Uabd(context, op, n, m, isLong: false); + EmitSse41VectorUabdOp(context, op, n, m, isLong: false); } else { @@ -2491,7 +2471,7 @@ namespace ARMeilleure.Instructions n = context.AddIntrinsic(movInst, n); m = context.AddIntrinsic(movInst, m); - EmitSse41Uabd(context, op, n, m, isLong: true); + EmitSse41VectorUabdOp(context, op, n, m, isLong: true); } else { @@ -2666,9 +2646,7 @@ namespace ARMeilleure.Instructions } else { - Delegate dlg = new _U64_U64_U64(Math.Max); - - EmitVectorBinaryOpZx(context, (op1, op2) => context.Call(dlg, op1, op2)); + EmitVectorBinaryOpZx(context, (op1, op2) => EmitMax64Op(context, op1, op2, signed: false)); } } @@ -2680,17 +2658,13 @@ namespace ARMeilleure.Instructions } else { - Delegate dlg = new _U64_U64_U64(Math.Max); - - EmitVectorPairwiseOpZx(context, (op1, op2) => context.Call(dlg, op1, op2)); + EmitVectorPairwiseOpZx(context, (op1, op2) => EmitMax64Op(context, op1, op2, signed: false)); } } public static void Umaxv_V(ArmEmitterContext context) { - Delegate dlg = new _U64_U64_U64(Math.Max); - - EmitVectorAcrossVectorOpZx(context, (op1, op2) => context.Call(dlg, op1, op2)); + EmitVectorAcrossVectorOpZx(context, (op1, op2) => EmitMax64Op(context, op1, op2, signed: false)); } public static void Umin_V(ArmEmitterContext context) @@ -2715,9 +2689,7 @@ namespace ARMeilleure.Instructions } else { - Delegate dlg = new _U64_U64_U64(Math.Min); - - EmitVectorBinaryOpZx(context, (op1, op2) => context.Call(dlg, op1, op2)); + EmitVectorBinaryOpZx(context, (op1, op2) => EmitMin64Op(context, op1, op2, signed: false)); } } @@ -2729,17 +2701,13 @@ namespace ARMeilleure.Instructions } else { - Delegate dlg = new _U64_U64_U64(Math.Min); - - EmitVectorPairwiseOpZx(context, (op1, op2) => context.Call(dlg, op1, op2)); + EmitVectorPairwiseOpZx(context, (op1, op2) => EmitMin64Op(context, op1, op2, signed: false)); } } public static void Uminv_V(ArmEmitterContext context) { - Delegate dlg = new _U64_U64_U64(Math.Min); - - EmitVectorAcrossVectorOpZx(context, (op1, op2) => context.Call(dlg, op1, op2)); + EmitVectorAcrossVectorOpZx(context, (op1, op2) => EmitMin64Op(context, op1, op2, signed: false)); } public static void Umlal_V(ArmEmitterContext context) @@ -3081,7 +3049,29 @@ namespace ARMeilleure.Instructions context.Copy(d, res); } - public static void EmitScalarRoundOpF(ArmEmitterContext context, FPRoundingMode roundMode) + private static Operand EmitMax64Op(ArmEmitterContext context, Operand op1, Operand op2, bool signed) + { + Debug.Assert(op1.Type == OperandType.I64 && op2.Type == OperandType.I64); + + Operand cmp = signed + ? context.ICompareGreaterOrEqual (op1, op2) + : context.ICompareGreaterOrEqualUI(op1, op2); + + return context.ConditionalSelect(cmp, op1, op2); + } + + private static Operand EmitMin64Op(ArmEmitterContext context, Operand op1, Operand op2, bool signed) + { + Debug.Assert(op1.Type == OperandType.I64 && op2.Type == OperandType.I64); + + Operand cmp = signed + ? context.ICompareLessOrEqual (op1, op2) + : context.ICompareLessOrEqualUI(op1, op2); + + return context.ConditionalSelect(cmp, op1, op2); + } + + private static void EmitScalarRoundOpF(ArmEmitterContext context, FPRoundingMode roundMode) { OpCodeSimd op = (OpCodeSimd)context.CurrOp; @@ -3103,7 +3093,7 @@ namespace ARMeilleure.Instructions context.Copy(GetVec(op.Rd), res); } - public static void EmitVectorRoundOpF(ArmEmitterContext context, FPRoundingMode roundMode) + private static void EmitVectorRoundOpF(ArmEmitterContext context, FPRoundingMode roundMode) { OpCodeSimd op = (OpCodeSimd)context.CurrOp; @@ -3220,14 +3210,14 @@ namespace ARMeilleure.Instructions Subtract } - private static void EmitSse41Mul_AddSub(ArmEmitterContext context, AddSub addSub) + private static void EmitSse41VectorMul_AddSub(ArmEmitterContext context, AddSub addSub) { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; Operand n = GetVec(op.Rn); Operand m = GetVec(op.Rm); - Operand res = null; + Operand res; if (op.Size == 0) { @@ -3257,23 +3247,15 @@ namespace ARMeilleure.Instructions if (addSub == AddSub.Add) { - switch (op.Size) - { - case 0: res = context.AddIntrinsic(Intrinsic.X86Paddb, d, res); break; - case 1: res = context.AddIntrinsic(Intrinsic.X86Paddw, d, res); break; - case 2: res = context.AddIntrinsic(Intrinsic.X86Paddd, d, res); break; - case 3: res = context.AddIntrinsic(Intrinsic.X86Paddq, d, res); break; - } + Intrinsic addInst = X86PaddInstruction[op.Size]; + + res = context.AddIntrinsic(addInst, d, res); } else if (addSub == AddSub.Subtract) { - switch (op.Size) - { - case 0: res = context.AddIntrinsic(Intrinsic.X86Psubb, d, res); break; - case 1: res = context.AddIntrinsic(Intrinsic.X86Psubw, d, res); break; - case 2: res = context.AddIntrinsic(Intrinsic.X86Psubd, d, res); break; - case 3: res = context.AddIntrinsic(Intrinsic.X86Psubq, d, res); break; - } + Intrinsic subInst = X86PsubInstruction[op.Size]; + + res = context.AddIntrinsic(subInst, d, res); } if (op.RegisterSize == RegisterSize.Simd64) @@ -3284,7 +3266,7 @@ namespace ARMeilleure.Instructions context.Copy(d, res); } - private static void EmitSse41Sabd( + private static void EmitSse41VectorSabdOp( ArmEmitterContext context, OpCodeSimdReg op, Operand n, @@ -3317,7 +3299,7 @@ namespace ARMeilleure.Instructions context.Copy(GetVec(op.Rd), res); } - private static void EmitSse41Uabd( + private static void EmitSse41VectorUabdOp( ArmEmitterContext context, OpCodeSimdReg op, Operand n, diff --git a/ARMeilleure/Instructions/InstEmitSimdArithmetic32.cs b/ARMeilleure/Instructions/InstEmitSimdArithmetic32.cs index 19a10f681..fdc1bb469 100644 --- a/ARMeilleure/Instructions/InstEmitSimdArithmetic32.cs +++ b/ARMeilleure/Instructions/InstEmitSimdArithmetic32.cs @@ -27,7 +27,7 @@ namespace ARMeilleure.Instructions } else { - EmitScalarUnaryOpF32(context, (op1) => EmitUnaryMathCall(context, MathF.Abs, Math.Abs, op1)); + EmitScalarUnaryOpF32(context, (op1) => EmitUnaryMathCall(context, nameof(Math.Abs), op1)); } } @@ -46,7 +46,7 @@ namespace ARMeilleure.Instructions } else { - EmitVectorUnaryOpF32(context, (op1) => EmitUnaryMathCall(context, MathF.Abs, Math.Abs, op1)); + EmitVectorUnaryOpF32(context, (op1) => EmitUnaryMathCall(context, nameof(Math.Abs), op1)); } } else @@ -74,7 +74,7 @@ namespace ARMeilleure.Instructions } else { - EmitScalarBinaryOpF32(context, (op1, op2) => EmitSoftFloatCall(context, SoftFloat32.FPAdd, SoftFloat64.FPAdd, op1, op2)); + EmitScalarBinaryOpF32(context, (op1, op2) => EmitSoftFloatCall(context, nameof(SoftFloat32.FPAdd), op1, op2)); } } @@ -90,7 +90,7 @@ namespace ARMeilleure.Instructions } else { - EmitVectorBinaryOpF32(context, (op1, op2) => EmitSoftFloatCallDefaultFpscr(context, SoftFloat32.FPAddFpscr, SoftFloat64.FPAddFpscr, op1, op2)); + EmitVectorBinaryOpF32(context, (op1, op2) => EmitSoftFloatCallDefaultFpscr(context, nameof(SoftFloat32.FPAddFpscr), op1, op2)); } } @@ -330,7 +330,7 @@ namespace ARMeilleure.Instructions { EmitScalarTernaryOpF32(context, (op1, op2, op3) => { - return EmitSoftFloatCall(context, SoftFloat32.FPNegMulAdd, SoftFloat64.FPNegMulAdd, op1, op2, op3); + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPNegMulAdd), op1, op2, op3); }); } } @@ -371,7 +371,7 @@ namespace ARMeilleure.Instructions { EmitScalarTernaryOpF32(context, (op1, op2, op3) => { - return EmitSoftFloatCall(context, SoftFloat32.FPNegMulSub, SoftFloat64.FPNegMulSub, op1, op2, op3); + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPNegMulSub), op1, op2, op3); }); } } @@ -423,7 +423,7 @@ namespace ARMeilleure.Instructions { EmitScalarBinaryOpF32(context, (op1, op2) => { - return EmitSoftFloatCall(context, SoftFloat32.FPDiv, SoftFloat64.FPDiv, op1, op2); + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPDiv), op1, op2); }); } } @@ -436,7 +436,7 @@ namespace ARMeilleure.Instructions } else { - EmitScalarBinaryOpF32(context, (op1, op2) => EmitSoftFloatCall(context, SoftFloat32.FPMaxNum, SoftFloat64.FPMaxNum, op1, op2)); + EmitScalarBinaryOpF32(context, (op1, op2) => EmitSoftFloatCall(context, nameof(SoftFloat32.FPMaxNum), op1, op2)); } } @@ -448,7 +448,7 @@ namespace ARMeilleure.Instructions } else { - EmitVectorBinaryOpSx32(context, (op1, op2) => EmitSoftFloatCallDefaultFpscr(context, SoftFloat32.FPMaxNumFpscr, SoftFloat64.FPMaxNumFpscr, op1, op2)); + EmitVectorBinaryOpSx32(context, (op1, op2) => EmitSoftFloatCallDefaultFpscr(context, nameof(SoftFloat32.FPMaxNumFpscr), op1, op2)); } } @@ -460,7 +460,7 @@ namespace ARMeilleure.Instructions } else { - EmitScalarBinaryOpF32(context, (op1, op2) => EmitSoftFloatCall(context, SoftFloat32.FPMinNum, SoftFloat64.FPMinNum, op1, op2)); + EmitScalarBinaryOpF32(context, (op1, op2) => EmitSoftFloatCall(context, nameof(SoftFloat32.FPMinNum), op1, op2)); } } @@ -472,7 +472,7 @@ namespace ARMeilleure.Instructions } else { - EmitVectorBinaryOpSx32(context, (op1, op2) => EmitSoftFloatCallDefaultFpscr(context, SoftFloat32.FPMinNumFpscr, SoftFloat64.FPMinNumFpscr, op1, op2)); + EmitVectorBinaryOpSx32(context, (op1, op2) => EmitSoftFloatCallDefaultFpscr(context, nameof(SoftFloat32.FPMinNumFpscr), op1, op2)); } } @@ -486,7 +486,7 @@ namespace ARMeilleure.Instructions { EmitVectorBinaryOpF32(context, (op1, op2) => { - return EmitSoftFloatCallDefaultFpscr(context, SoftFloat32.FPMaxFpscr, SoftFloat64.FPMaxFpscr, op1, op2); + return EmitSoftFloatCallDefaultFpscr(context, nameof(SoftFloat32.FPMaxFpscr), op1, op2); }); } } @@ -529,7 +529,7 @@ namespace ARMeilleure.Instructions { EmitVectorBinaryOpF32(context, (op1, op2) => { - return EmitSoftFloatCallDefaultFpscr(context, SoftFloat32.FPMinFpscr, SoftFloat64.FPMinFpscr, op1, op2); + return EmitSoftFloatCallDefaultFpscr(context, nameof(SoftFloat32.FPMinFpscr), op1, op2); }); } } @@ -579,7 +579,7 @@ namespace ARMeilleure.Instructions { EmitScalarTernaryOpF32(context, (op1, op2, op3) => { - return EmitSoftFloatCall(context, SoftFloat32.FPMulAdd, SoftFloat64.FPMulAdd, op1, op2, op3); + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPMulAdd), op1, op2, op3); }); } } @@ -598,7 +598,7 @@ namespace ARMeilleure.Instructions { EmitVectorTernaryOpF32(context, (op1, op2, op3) => { - return EmitSoftFloatCallDefaultFpscr(context, SoftFloat32.FPMulAddFpscr, SoftFloat64.FPMulAddFpscr, op1, op2, op3); + return EmitSoftFloatCallDefaultFpscr(context, nameof(SoftFloat32.FPMulAddFpscr), op1, op2, op3); }); } } @@ -624,7 +624,7 @@ namespace ARMeilleure.Instructions } else { - EmitVectorsByScalarOpF32(context, (op1, op2, op3) => EmitSoftFloatCallDefaultFpscr(context, SoftFloat32.FPMulAddFpscr, SoftFloat64.FPMulAddFpscr, op1, op2, op3)); + EmitVectorsByScalarOpF32(context, (op1, op2, op3) => EmitSoftFloatCallDefaultFpscr(context, nameof(SoftFloat32.FPMulAddFpscr), op1, op2, op3)); } } else @@ -650,7 +650,7 @@ namespace ARMeilleure.Instructions { EmitScalarTernaryOpF32(context, (op1, op2, op3) => { - return EmitSoftFloatCall(context, SoftFloat32.FPMulSub, SoftFloat64.FPMulSub, op1, op2, op3); + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPMulSub), op1, op2, op3); }); } } @@ -669,7 +669,7 @@ namespace ARMeilleure.Instructions { EmitVectorTernaryOpF32(context, (op1, op2, op3) => { - return EmitSoftFloatCallDefaultFpscr(context, SoftFloat32.FPMulSubFpscr, SoftFloat64.FPMulSubFpscr, op1, op2, op3); + return EmitSoftFloatCallDefaultFpscr(context, nameof(SoftFloat32.FPMulSubFpscr), op1, op2, op3); }); } } @@ -695,7 +695,7 @@ namespace ARMeilleure.Instructions } else { - EmitVectorsByScalarOpF32(context, (op1, op2, op3) => EmitSoftFloatCallDefaultFpscr(context, SoftFloat32.FPMulSubFpscr, SoftFloat64.FPMulSubFpscr, op1, op2, op3)); + EmitVectorsByScalarOpF32(context, (op1, op2, op3) => EmitSoftFloatCallDefaultFpscr(context, nameof(SoftFloat32.FPMulSubFpscr), op1, op2, op3)); } } else @@ -725,7 +725,7 @@ namespace ARMeilleure.Instructions { EmitScalarBinaryOpF32(context, (op1, op2) => { - return EmitSoftFloatCall(context, SoftFloat32.FPMul, SoftFloat64.FPMul, op1, op2); + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPMul), op1, op2); }); } } @@ -744,7 +744,7 @@ namespace ARMeilleure.Instructions { EmitVectorBinaryOpF32(context, (op1, op2) => { - return EmitSoftFloatCallDefaultFpscr(context, SoftFloat32.FPMulFpscr, SoftFloat64.FPMulFpscr, op1, op2); + return EmitSoftFloatCallDefaultFpscr(context, nameof(SoftFloat32.FPMulFpscr), op1, op2); }); } } @@ -779,7 +779,7 @@ namespace ARMeilleure.Instructions } else { - EmitVectorByScalarOpF32(context, (op1, op2) => EmitSoftFloatCallDefaultFpscr(context, SoftFloat32.FPMulFpscr, SoftFloat64.FPMulFpscr, op1, op2)); + EmitVectorByScalarOpF32(context, (op1, op2) => EmitSoftFloatCallDefaultFpscr(context, nameof(SoftFloat32.FPMulFpscr), op1, op2)); } } else @@ -938,7 +938,7 @@ namespace ARMeilleure.Instructions { EmitVectorUnaryOpF32(context, (op1) => { - return EmitSoftFloatCallDefaultFpscr(context, SoftFloat32.FPRecipEstimateFpscr, SoftFloat64.FPRecipEstimateFpscr, op1); + return EmitSoftFloatCallDefaultFpscr(context, nameof(SoftFloat32.FPRecipEstimateFpscr), op1); }); } } @@ -980,7 +980,7 @@ namespace ARMeilleure.Instructions { EmitVectorBinaryOpF32(context, (op1, op2) => { - return EmitSoftFloatCall(context, SoftFloat32.FPRecipStep, SoftFloat64.FPRecipStep, op1, op2); + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPRecipStep), op1, op2); }); } } @@ -1001,7 +1001,7 @@ namespace ARMeilleure.Instructions { EmitVectorUnaryOpF32(context, (op1) => { - return EmitSoftFloatCallDefaultFpscr(context, SoftFloat32.FPRSqrtEstimateFpscr, SoftFloat64.FPRSqrtEstimateFpscr, op1); + return EmitSoftFloatCallDefaultFpscr(context, nameof(SoftFloat32.FPRSqrtEstimateFpscr), op1); }); } } @@ -1047,7 +1047,7 @@ namespace ARMeilleure.Instructions { EmitVectorBinaryOpF32(context, (op1, op2) => { - return EmitSoftFloatCall(context, SoftFloat32.FPRSqrtStep, SoftFloat64.FPRSqrtStep, op1, op2); + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPRSqrtStep), op1, op2); }); } } @@ -1089,7 +1089,7 @@ namespace ARMeilleure.Instructions { EmitScalarUnaryOpF32(context, (op1) => { - return EmitSoftFloatCall(context, SoftFloat32.FPSqrt, SoftFloat64.FPSqrt, op1); + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPSqrt), op1); }); } } diff --git a/ARMeilleure/Instructions/InstEmitSimdCmp.cs b/ARMeilleure/Instructions/InstEmitSimdCmp.cs index d11adf194..ff97084bd 100644 --- a/ARMeilleure/Instructions/InstEmitSimdCmp.cs +++ b/ARMeilleure/Instructions/InstEmitSimdCmp.cs @@ -288,49 +288,49 @@ namespace ARMeilleure.Instructions public static void Facge_S(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseAvx) { - EmitSse2CmpOpF(context, CmpCondition.GreaterThanOrEqual, scalar: true, absolute: true); + EmitSse2OrAvxCmpOpF(context, CmpCondition.GreaterThanOrEqual, scalar: true, absolute: true); } else { - EmitCmpOpF(context, SoftFloat32.FPCompareGE, SoftFloat64.FPCompareGE, scalar: true, absolute: true); + EmitCmpOpF(context, nameof(SoftFloat32.FPCompareGE), scalar: true, absolute: true); } } public static void Facge_V(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseAvx) { - EmitSse2CmpOpF(context, CmpCondition.GreaterThanOrEqual, scalar: false, absolute: true); + EmitSse2OrAvxCmpOpF(context, CmpCondition.GreaterThanOrEqual, scalar: false, absolute: true); } else { - EmitCmpOpF(context, SoftFloat32.FPCompareGE, SoftFloat64.FPCompareGE, scalar: false, absolute: true); + EmitCmpOpF(context, nameof(SoftFloat32.FPCompareGE), scalar: false, absolute: true); } } public static void Facgt_S(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseAvx) { - EmitSse2CmpOpF(context, CmpCondition.GreaterThan, scalar: true, absolute: true); + EmitSse2OrAvxCmpOpF(context, CmpCondition.GreaterThan, scalar: true, absolute: true); } else { - EmitCmpOpF(context, SoftFloat32.FPCompareGT, SoftFloat64.FPCompareGT, scalar: true, absolute: true); + EmitCmpOpF(context, nameof(SoftFloat32.FPCompareGT), scalar: true, absolute: true); } } public static void Facgt_V(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseAvx) { - EmitSse2CmpOpF(context, CmpCondition.GreaterThan, scalar: false, absolute: true); + EmitSse2OrAvxCmpOpF(context, CmpCondition.GreaterThan, scalar: false, absolute: true); } else { - EmitCmpOpF(context, SoftFloat32.FPCompareGT, SoftFloat64.FPCompareGT, scalar: false, absolute: true); + EmitCmpOpF(context, nameof(SoftFloat32.FPCompareGT), scalar: false, absolute: true); } } @@ -348,11 +348,11 @@ namespace ARMeilleure.Instructions { if (Optimizations.FastFP && Optimizations.UseSse2) { - EmitSse2CmpOpF(context, CmpCondition.Equal, scalar: true); + EmitSse2OrAvxCmpOpF(context, CmpCondition.Equal, scalar: true); } else { - EmitCmpOpF(context, SoftFloat32.FPCompareEQ, SoftFloat64.FPCompareEQ, scalar: true); + EmitCmpOpF(context, nameof(SoftFloat32.FPCompareEQ), scalar: true); } } @@ -360,11 +360,11 @@ namespace ARMeilleure.Instructions { if (Optimizations.FastFP && Optimizations.UseSse2) { - EmitSse2CmpOpF(context, CmpCondition.Equal, scalar: false); + EmitSse2OrAvxCmpOpF(context, CmpCondition.Equal, scalar: false); } else { - EmitCmpOpF(context, SoftFloat32.FPCompareEQ, SoftFloat64.FPCompareEQ, scalar: false); + EmitCmpOpF(context, nameof(SoftFloat32.FPCompareEQ), scalar: false); } } @@ -372,11 +372,11 @@ namespace ARMeilleure.Instructions { if (Optimizations.FastFP && Optimizations.UseAvx) { - EmitSse2CmpOpF(context, CmpCondition.GreaterThanOrEqual, scalar: true); + EmitSse2OrAvxCmpOpF(context, CmpCondition.GreaterThanOrEqual, scalar: true); } else { - EmitCmpOpF(context, SoftFloat32.FPCompareGE, SoftFloat64.FPCompareGE, scalar: true); + EmitCmpOpF(context, nameof(SoftFloat32.FPCompareGE), scalar: true); } } @@ -384,11 +384,11 @@ namespace ARMeilleure.Instructions { if (Optimizations.FastFP && Optimizations.UseAvx) { - EmitSse2CmpOpF(context, CmpCondition.GreaterThanOrEqual, scalar: false); + EmitSse2OrAvxCmpOpF(context, CmpCondition.GreaterThanOrEqual, scalar: false); } else { - EmitCmpOpF(context, SoftFloat32.FPCompareGE, SoftFloat64.FPCompareGE, scalar: false); + EmitCmpOpF(context, nameof(SoftFloat32.FPCompareGE), scalar: false); } } @@ -396,11 +396,11 @@ namespace ARMeilleure.Instructions { if (Optimizations.FastFP && Optimizations.UseAvx) { - EmitSse2CmpOpF(context, CmpCondition.GreaterThan, scalar: true); + EmitSse2OrAvxCmpOpF(context, CmpCondition.GreaterThan, scalar: true); } else { - EmitCmpOpF(context, SoftFloat32.FPCompareGT, SoftFloat64.FPCompareGT, scalar: true); + EmitCmpOpF(context, nameof(SoftFloat32.FPCompareGT), scalar: true); } } @@ -408,11 +408,11 @@ namespace ARMeilleure.Instructions { if (Optimizations.FastFP && Optimizations.UseAvx) { - EmitSse2CmpOpF(context, CmpCondition.GreaterThan, scalar: false); + EmitSse2OrAvxCmpOpF(context, CmpCondition.GreaterThan, scalar: false); } else { - EmitCmpOpF(context, SoftFloat32.FPCompareGT, SoftFloat64.FPCompareGT, scalar: false); + EmitCmpOpF(context, nameof(SoftFloat32.FPCompareGT), scalar: false); } } @@ -420,11 +420,11 @@ namespace ARMeilleure.Instructions { if (Optimizations.FastFP && Optimizations.UseSse2) { - EmitSse2CmpOpF(context, CmpCondition.LessThanOrEqual, scalar: true); + EmitSse2OrAvxCmpOpF(context, CmpCondition.LessThanOrEqual, scalar: true); } else { - EmitCmpOpF(context, SoftFloat32.FPCompareLE, SoftFloat64.FPCompareLE, scalar: true); + EmitCmpOpF(context, nameof(SoftFloat32.FPCompareLE), scalar: true); } } @@ -432,11 +432,11 @@ namespace ARMeilleure.Instructions { if (Optimizations.FastFP && Optimizations.UseSse2) { - EmitSse2CmpOpF(context, CmpCondition.LessThanOrEqual, scalar: false); + EmitSse2OrAvxCmpOpF(context, CmpCondition.LessThanOrEqual, scalar: false); } else { - EmitCmpOpF(context, SoftFloat32.FPCompareLE, SoftFloat64.FPCompareLE, scalar: false); + EmitCmpOpF(context, nameof(SoftFloat32.FPCompareLE), scalar: false); } } @@ -444,11 +444,11 @@ namespace ARMeilleure.Instructions { if (Optimizations.FastFP && Optimizations.UseSse2) { - EmitSse2CmpOpF(context, CmpCondition.LessThan, scalar: true); + EmitSse2OrAvxCmpOpF(context, CmpCondition.LessThan, scalar: true); } else { - EmitCmpOpF(context, SoftFloat32.FPCompareLT, SoftFloat64.FPCompareLT, scalar: true); + EmitCmpOpF(context, nameof(SoftFloat32.FPCompareLT), scalar: true); } } @@ -456,11 +456,11 @@ namespace ARMeilleure.Instructions { if (Optimizations.FastFP && Optimizations.UseSse2) { - EmitSse2CmpOpF(context, CmpCondition.LessThan, scalar: false); + EmitSse2OrAvxCmpOpF(context, CmpCondition.LessThan, scalar: false); } else { - EmitCmpOpF(context, SoftFloat32.FPCompareLT, SoftFloat64.FPCompareLT, scalar: false); + EmitCmpOpF(context, nameof(SoftFloat32.FPCompareLT), scalar: false); } } @@ -592,11 +592,7 @@ namespace ARMeilleure.Instructions me = context.VectorExtract(type, GetVec(op.Rm), 0); } - Delegate dlg = op.Size != 0 - ? (Delegate)new _S32_F64_F64_Bool(SoftFloat64.FPCompare) - : (Delegate)new _S32_F32_F32_Bool(SoftFloat32.FPCompare); - - Operand nzcv = context.Call(dlg, ne, me, Const(signalNaNs)); + Operand nzcv = EmitSoftFloatCall(context, nameof(SoftFloat32.FPCompare), ne, me, Const(signalNaNs)); EmitSetNzcv(context, nzcv); } @@ -683,12 +679,7 @@ namespace ARMeilleure.Instructions context.Copy(GetVec(op.Rd), res); } - private static void EmitCmpOpF( - ArmEmitterContext context, - _F32_F32_F32 f32, - _F64_F64_F64 f64, - bool scalar, - bool absolute = false) + private static void EmitCmpOpF(ArmEmitterContext context, string name, bool scalar, bool absolute = false) { OpCodeSimd op = (OpCodeSimd)context.CurrOp; @@ -716,11 +707,11 @@ namespace ARMeilleure.Instructions if (absolute) { - ne = EmitUnaryMathCall(context, MathF.Abs, Math.Abs, ne); - me = EmitUnaryMathCall(context, MathF.Abs, Math.Abs, me); + ne = EmitUnaryMathCall(context, nameof(Math.Abs), ne); + me = EmitUnaryMathCall(context, nameof(Math.Abs), me); } - Operand e = EmitSoftFloatCall(context, f32, f64, ne, me); + Operand e = EmitSoftFloatCall(context, name, ne, me); res = context.VectorInsert(res, e, index); } @@ -728,7 +719,7 @@ namespace ARMeilleure.Instructions context.Copy(GetVec(op.Rd), res); } - private static void EmitSse2CmpOpF(ArmEmitterContext context, CmpCondition cond, bool scalar, bool absolute = false) + private static void EmitSse2OrAvxCmpOpF(ArmEmitterContext context, CmpCondition cond, bool scalar, bool absolute = false) { OpCodeSimd op = (OpCodeSimd)context.CurrOp; diff --git a/ARMeilleure/Instructions/InstEmitSimdCmp32.cs b/ARMeilleure/Instructions/InstEmitSimdCmp32.cs index a4f64ad63..db9250538 100644 --- a/ARMeilleure/Instructions/InstEmitSimdCmp32.cs +++ b/ARMeilleure/Instructions/InstEmitSimdCmp32.cs @@ -3,6 +3,7 @@ using ARMeilleure.IntermediateRepresentation; using ARMeilleure.State; using ARMeilleure.Translation; using System; +using System.Reflection; using static ARMeilleure.Instructions.InstEmitHelper; using static ARMeilleure.Instructions.InstEmitSimdHelper; @@ -19,11 +20,11 @@ namespace ARMeilleure.Instructions { if (Optimizations.FastFP && Optimizations.UseSse2) { - EmitSse2CmpOpF32(context, CmpCondition.Equal, false); + EmitSse2OrAvxCmpOpF32(context, CmpCondition.Equal, false); } else { - EmitCmpOpF32(context, SoftFloat32.FPCompareEQFpscr, SoftFloat64.FPCompareEQFpscr, false); + EmitCmpOpF32(context, nameof(SoftFloat32.FPCompareEQFpscr), false); } } @@ -40,11 +41,11 @@ namespace ARMeilleure.Instructions { if (Optimizations.FastFP && Optimizations.UseSse2) { - EmitSse2CmpOpF32(context, CmpCondition.Equal, true); + EmitSse2OrAvxCmpOpF32(context, CmpCondition.Equal, true); } else { - EmitCmpOpF32(context, SoftFloat32.FPCompareEQFpscr, SoftFloat64.FPCompareEQFpscr, true); + EmitCmpOpF32(context, nameof(SoftFloat32.FPCompareEQFpscr), true); } } else @@ -55,13 +56,13 @@ namespace ARMeilleure.Instructions public static void Vcge_V(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseAvx) { - EmitSse2CmpOpF32(context, CmpCondition.GreaterThanOrEqual, false); + EmitSse2OrAvxCmpOpF32(context, CmpCondition.GreaterThanOrEqual, false); } else { - EmitCmpOpF32(context, SoftFloat32.FPCompareGEFpscr, SoftFloat64.FPCompareGEFpscr, false); + EmitCmpOpF32(context, nameof(SoftFloat32.FPCompareGEFpscr), false); } } @@ -78,15 +79,15 @@ namespace ARMeilleure.Instructions if (op.F) { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseAvx) { - EmitSse2CmpOpF32(context, CmpCondition.GreaterThanOrEqual, true); + EmitSse2OrAvxCmpOpF32(context, CmpCondition.GreaterThanOrEqual, true); } else { - EmitCmpOpF32(context, SoftFloat32.FPCompareGEFpscr, SoftFloat64.FPCompareGEFpscr, true); + EmitCmpOpF32(context, nameof(SoftFloat32.FPCompareGEFpscr), true); } - } + } else { EmitCmpOpI32(context, context.ICompareGreaterOrEqual, context.ICompareGreaterOrEqualUI, true, true); @@ -95,13 +96,13 @@ namespace ARMeilleure.Instructions public static void Vcgt_V(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseAvx) { - EmitSse2CmpOpF32(context, CmpCondition.GreaterThan, false); + EmitSse2OrAvxCmpOpF32(context, CmpCondition.GreaterThan, false); } else { - EmitCmpOpF32(context, SoftFloat32.FPCompareGTFpscr, SoftFloat64.FPCompareGTFpscr, false); + EmitCmpOpF32(context, nameof(SoftFloat32.FPCompareGTFpscr), false); } } @@ -118,13 +119,13 @@ namespace ARMeilleure.Instructions if (op.F) { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseAvx) { - EmitSse2CmpOpF32(context, CmpCondition.GreaterThan, true); + EmitSse2OrAvxCmpOpF32(context, CmpCondition.GreaterThan, true); } else { - EmitCmpOpF32(context, SoftFloat32.FPCompareGTFpscr, SoftFloat64.FPCompareGTFpscr, true); + EmitCmpOpF32(context, nameof(SoftFloat32.FPCompareGTFpscr), true); } } else @@ -141,11 +142,11 @@ namespace ARMeilleure.Instructions { if (Optimizations.FastFP && Optimizations.UseSse2) { - EmitSse2CmpOpF32(context, CmpCondition.LessThanOrEqual, true); + EmitSse2OrAvxCmpOpF32(context, CmpCondition.LessThanOrEqual, true); } else { - EmitCmpOpF32(context, SoftFloat32.FPCompareLEFpscr, SoftFloat64.FPCompareLEFpscr, true); + EmitCmpOpF32(context, nameof(SoftFloat32.FPCompareLEFpscr), true); } } else @@ -162,11 +163,11 @@ namespace ARMeilleure.Instructions { if (Optimizations.FastFP && Optimizations.UseSse2) { - EmitSse2CmpOpF32(context, CmpCondition.LessThan, true); + EmitSse2OrAvxCmpOpF32(context, CmpCondition.LessThan, true); } else { - EmitCmpOpF32(context, SoftFloat32.FPCompareLTFpscr, SoftFloat64.FPCompareLTFpscr, true); + EmitCmpOpF32(context, nameof(SoftFloat32.FPCompareLTFpscr), true); } } else @@ -175,11 +176,7 @@ namespace ARMeilleure.Instructions } } - private static void EmitCmpOpF32( - ArmEmitterContext context, - _F32_F32_F32_Bool f32, - _F64_F64_F64_Bool f64, - bool zero) + private static void EmitCmpOpF32(ArmEmitterContext context, string name, bool zero) { Operand one = Const(1); if (zero) @@ -190,11 +187,11 @@ namespace ARMeilleure.Instructions if (type == OperandType.FP64) { - return context.Call(f64, m, ConstF(0.0), one); + return context.Call(typeof(SoftFloat64).GetMethod(name), m, ConstF(0.0d), one); } else { - return context.Call(f32, m, ConstF(0.0f), one); + return context.Call(typeof(SoftFloat32).GetMethod(name), m, ConstF(0.0f), one); } }); } @@ -206,11 +203,11 @@ namespace ARMeilleure.Instructions if (type == OperandType.FP64) { - return context.Call(f64, n, m, one); + return context.Call(typeof(SoftFloat64).GetMethod(name), n, m, one); } else { - return context.Call(f32, n, m, one); + return context.Call(typeof(SoftFloat32).GetMethod(name), n, m, one); } }); } @@ -241,7 +238,7 @@ namespace ARMeilleure.Instructions return ZerosOrOnes(context, signedOp(m, zeroV), type); }); - } + } else { EmitVectorUnaryOpZx32(context, (m) => @@ -258,7 +255,7 @@ namespace ARMeilleure.Instructions if (signed) { EmitVectorBinaryOpSx32(context, (n, m) => ZerosOrOnes(context, signedOp(n, m), n.Type)); - } + } else { EmitVectorBinaryOpZx32(context, (n, m) => ZerosOrOnes(context, unsignedOp(n, m), n.Type)); @@ -351,11 +348,11 @@ namespace ARMeilleure.Instructions me = ExtractScalar(context, type, op.Vm); } - Delegate dlg = sizeF != 0 - ? (Delegate)new _S32_F64_F64_Bool(SoftFloat64.FPCompare) - : (Delegate)new _S32_F32_F32_Bool(SoftFloat32.FPCompare); + MethodInfo info = sizeF != 0 + ? typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompare)) + : typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompare)); - Operand nzcv = context.Call(dlg, ne, me, Const(signalNaNs)); + Operand nzcv = context.Call(info, ne, me, Const(signalNaNs)); EmitSetFPSCRFlags(context, nzcv); } @@ -389,7 +386,7 @@ namespace ARMeilleure.Instructions SetFpFlag(context, FPState.NFlag, n); } - private static void EmitSse2CmpOpF32(ArmEmitterContext context, CmpCondition cond, bool zero) + private static void EmitSse2OrAvxCmpOpF32(ArmEmitterContext context, CmpCondition cond, bool zero) { OpCode32Simd op = (OpCode32Simd)context.CurrOp; diff --git a/ARMeilleure/Instructions/InstEmitSimdCrypto.cs b/ARMeilleure/Instructions/InstEmitSimdCrypto.cs index 5b4705676..db24e0290 100644 --- a/ARMeilleure/Instructions/InstEmitSimdCrypto.cs +++ b/ARMeilleure/Instructions/InstEmitSimdCrypto.cs @@ -16,13 +16,14 @@ namespace ARMeilleure.Instructions Operand n = GetVec(op.Rn); Operand res; + if (Optimizations.UseAesni) { res = context.AddIntrinsic(Intrinsic.X86Aesdeclast, context.AddIntrinsic(Intrinsic.X86Xorpd, d, n), context.VectorZero()); } else { - res = context.Call(new _V128_V128_V128(SoftFallback.Decrypt), d, n); + res = context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Decrypt)), d, n); } context.Copy(d, res); @@ -36,13 +37,14 @@ namespace ARMeilleure.Instructions Operand n = GetVec(op.Rn); Operand res; + if (Optimizations.UseAesni) { res = context.AddIntrinsic(Intrinsic.X86Aesenclast, context.AddIntrinsic(Intrinsic.X86Xorpd, d, n), context.VectorZero()); } else { - res = context.Call(new _V128_V128_V128(SoftFallback.Encrypt), d, n); + res = context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Encrypt)), d, n); } context.Copy(d, res); @@ -55,13 +57,14 @@ namespace ARMeilleure.Instructions Operand n = GetVec(op.Rn); Operand res; + if (Optimizations.UseAesni) { res = context.AddIntrinsic(Intrinsic.X86Aesimc, n); } else { - res = context.Call(new _V128_V128(SoftFallback.InverseMixColumns), n); + res = context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.InverseMixColumns)), n); } context.Copy(GetVec(op.Rd), res); @@ -74,6 +77,7 @@ namespace ARMeilleure.Instructions Operand n = GetVec(op.Rn); Operand res; + if (Optimizations.UseAesni) { Operand roundKey = context.VectorZero(); @@ -86,7 +90,7 @@ namespace ARMeilleure.Instructions } else { - res = context.Call(new _V128_V128(SoftFallback.MixColumns), n); + res = context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.MixColumns)), n); } context.Copy(GetVec(op.Rd), res); diff --git a/ARMeilleure/Instructions/InstEmitSimdCrypto32.cs b/ARMeilleure/Instructions/InstEmitSimdCrypto32.cs index f62fd3071..f713a388c 100644 --- a/ARMeilleure/Instructions/InstEmitSimdCrypto32.cs +++ b/ARMeilleure/Instructions/InstEmitSimdCrypto32.cs @@ -16,13 +16,14 @@ namespace ARMeilleure.Instructions Operand n = GetVecA32(op.Qm); Operand res; + if (Optimizations.UseAesni) { res = context.AddIntrinsic(Intrinsic.X86Aesdeclast, context.AddIntrinsic(Intrinsic.X86Xorpd, d, n), context.VectorZero()); } else { - res = context.Call(new _V128_V128_V128(SoftFallback.Decrypt), d, n); + res = context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Decrypt)), d, n); } context.Copy(d, res); @@ -36,13 +37,14 @@ namespace ARMeilleure.Instructions Operand n = GetVecA32(op.Qm); Operand res; + if (Optimizations.UseAesni) { res = context.AddIntrinsic(Intrinsic.X86Aesenclast, context.AddIntrinsic(Intrinsic.X86Xorpd, d, n), context.VectorZero()); } else { - res = context.Call(new _V128_V128_V128(SoftFallback.Encrypt), d, n); + res = context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Encrypt)), d, n); } context.Copy(d, res); @@ -55,13 +57,14 @@ namespace ARMeilleure.Instructions Operand n = GetVecA32(op.Qm); Operand res; + if (Optimizations.UseAesni) { res = context.AddIntrinsic(Intrinsic.X86Aesimc, n); } else { - res = context.Call(new _V128_V128(SoftFallback.InverseMixColumns), n); + res = context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.InverseMixColumns)), n); } context.Copy(GetVecA32(op.Qd), res); @@ -74,6 +77,7 @@ namespace ARMeilleure.Instructions Operand n = GetVecA32(op.Qm); Operand res; + if (Optimizations.UseAesni) { Operand roundKey = context.VectorZero(); @@ -86,7 +90,7 @@ namespace ARMeilleure.Instructions } else { - res = context.Call(new _V128_V128(SoftFallback.MixColumns), n); + res = context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.MixColumns)), n); } context.Copy(GetVecA32(op.Qd), res); diff --git a/ARMeilleure/Instructions/InstEmitSimdCvt.cs b/ARMeilleure/Instructions/InstEmitSimdCvt.cs index 49f0365b4..9696fa287 100644 --- a/ARMeilleure/Instructions/InstEmitSimdCvt.cs +++ b/ARMeilleure/Instructions/InstEmitSimdCvt.cs @@ -4,6 +4,7 @@ using ARMeilleure.State; using ARMeilleure.Translation; using System; using System.Diagnostics; +using System.Reflection; using static ARMeilleure.Instructions.InstEmitHelper; using static ARMeilleure.Instructions.InstEmitSimdHelper; @@ -61,9 +62,7 @@ namespace ARMeilleure.Instructions { Operand ne = context.VectorExtract(OperandType.FP32, GetVec(op.Rn), 0); - Delegate dlg = new _U16_F32(SoftFloat32_16.FPConvert); - - Operand res = context.Call(dlg, ne); + Operand res = context.Call(typeof(SoftFloat32_16).GetMethod(nameof(SoftFloat32_16.FPConvert)), ne); res = context.ZeroExtend16(OperandType.I64, res); @@ -73,9 +72,7 @@ namespace ARMeilleure.Instructions { Operand ne = EmitVectorExtractZx(context, op.Rn, 0, 1); - Delegate dlg = new _F32_U16(SoftFloat16_32.FPConvert); - - Operand res = context.Call(dlg, ne); + Operand res = context.Call(typeof(SoftFloat16_32).GetMethod(nameof(SoftFloat16_32.FPConvert)), ne); context.Copy(GetVec(op.Rd), context.VectorInsert(context.VectorZero(), res, 0)); } @@ -161,9 +158,7 @@ namespace ARMeilleure.Instructions { Operand ne = EmitVectorExtractZx(context, op.Rn, part + index, 1); - Delegate dlg = new _F32_U16(SoftFloat16_32.FPConvert); - - Operand e = context.Call(dlg, ne); + Operand e = context.Call(typeof(SoftFloat16_32).GetMethod(nameof(SoftFloat16_32.FPConvert)), ne); res = context.VectorInsert(res, e, index); } @@ -189,7 +184,7 @@ namespace ARMeilleure.Instructions } else { - EmitFcvt_s_Gp(context, (op1) => EmitUnaryMathCall(context, MathF.Floor, Math.Floor, op1)); + EmitFcvt_s_Gp(context, (op1) => EmitUnaryMathCall(context, nameof(Math.Floor), op1)); } } @@ -201,7 +196,7 @@ namespace ARMeilleure.Instructions } else { - EmitFcvt_u_Gp(context, (op1) => EmitUnaryMathCall(context, MathF.Floor, Math.Floor, op1)); + EmitFcvt_u_Gp(context, (op1) => EmitUnaryMathCall(context, nameof(Math.Floor), op1)); } } @@ -247,9 +242,7 @@ namespace ARMeilleure.Instructions if (sizeF == 0) { - Delegate dlg = new _U16_F32(SoftFloat32_16.FPConvert); - - Operand e = context.Call(dlg, ne); + Operand e = context.Call(typeof(SoftFloat32_16).GetMethod(nameof(SoftFloat32_16.FPConvert)), ne); e = context.ZeroExtend16(OperandType.I64, e); @@ -271,7 +264,7 @@ namespace ARMeilleure.Instructions { if (Optimizations.UseSse41) { - EmitSse41Fcvts(context, FPRoundingMode.ToNearest, scalar: true); + EmitSse41FcvtsOpF(context, FPRoundingMode.ToNearest, scalar: true); } else { @@ -283,7 +276,7 @@ namespace ARMeilleure.Instructions { if (Optimizations.UseSse41) { - EmitSse41Fcvts(context, FPRoundingMode.ToNearest, scalar: false); + EmitSse41FcvtsOpF(context, FPRoundingMode.ToNearest, scalar: false); } else { @@ -295,7 +288,7 @@ namespace ARMeilleure.Instructions { if (Optimizations.UseSse41) { - EmitSse41Fcvtu(context, FPRoundingMode.ToNearest, scalar: true); + EmitSse41FcvtuOpF(context, FPRoundingMode.ToNearest, scalar: true); } else { @@ -307,7 +300,7 @@ namespace ARMeilleure.Instructions { if (Optimizations.UseSse41) { - EmitSse41Fcvtu(context, FPRoundingMode.ToNearest, scalar: false); + EmitSse41FcvtuOpF(context, FPRoundingMode.ToNearest, scalar: false); } else { @@ -323,7 +316,7 @@ namespace ARMeilleure.Instructions } else { - EmitFcvt_s_Gp(context, (op1) => EmitUnaryMathCall(context, MathF.Ceiling, Math.Ceiling, op1)); + EmitFcvt_s_Gp(context, (op1) => EmitUnaryMathCall(context, nameof(Math.Ceiling), op1)); } } @@ -335,7 +328,7 @@ namespace ARMeilleure.Instructions } else { - EmitFcvt_u_Gp(context, (op1) => EmitUnaryMathCall(context, MathF.Ceiling, Math.Ceiling, op1)); + EmitFcvt_u_Gp(context, (op1) => EmitUnaryMathCall(context, nameof(Math.Ceiling), op1)); } } @@ -367,7 +360,7 @@ namespace ARMeilleure.Instructions { if (Optimizations.UseSse41) { - EmitSse41Fcvts(context, FPRoundingMode.TowardsZero, scalar: true); + EmitSse41FcvtsOpF(context, FPRoundingMode.TowardsZero, scalar: true); } else { @@ -379,7 +372,7 @@ namespace ARMeilleure.Instructions { if (Optimizations.UseSse41) { - EmitSse41Fcvts(context, FPRoundingMode.TowardsZero, scalar: false); + EmitSse41FcvtsOpF(context, FPRoundingMode.TowardsZero, scalar: false); } else { @@ -391,7 +384,7 @@ namespace ARMeilleure.Instructions { if (Optimizations.UseSse41) { - EmitSse41Fcvts(context, FPRoundingMode.TowardsZero, scalar: false); + EmitSse41FcvtsOpF(context, FPRoundingMode.TowardsZero, scalar: false); } else { @@ -427,7 +420,7 @@ namespace ARMeilleure.Instructions { if (Optimizations.UseSse41) { - EmitSse41Fcvtu(context, FPRoundingMode.TowardsZero, scalar: true); + EmitSse41FcvtuOpF(context, FPRoundingMode.TowardsZero, scalar: true); } else { @@ -439,7 +432,7 @@ namespace ARMeilleure.Instructions { if (Optimizations.UseSse41) { - EmitSse41Fcvtu(context, FPRoundingMode.TowardsZero, scalar: false); + EmitSse41FcvtuOpF(context, FPRoundingMode.TowardsZero, scalar: false); } else { @@ -451,7 +444,7 @@ namespace ARMeilleure.Instructions { if (Optimizations.UseSse41) { - EmitSse41Fcvtu(context, FPRoundingMode.TowardsZero, scalar: false); + EmitSse41FcvtuOpF(context, FPRoundingMode.TowardsZero, scalar: false); } else { @@ -497,7 +490,7 @@ namespace ARMeilleure.Instructions { if (Optimizations.UseSse2) { - EmitSse2Scvtf(context, scalar: true); + EmitSse2ScvtfOp(context, scalar: true); } else { @@ -517,7 +510,7 @@ namespace ARMeilleure.Instructions { if (Optimizations.UseSse2) { - EmitSse2Scvtf(context, scalar: false); + EmitSse2ScvtfOp(context, scalar: false); } else { @@ -529,7 +522,7 @@ namespace ARMeilleure.Instructions { if (Optimizations.UseSse2) { - EmitSse2Scvtf(context, scalar: false); + EmitSse2ScvtfOp(context, scalar: false); } else { @@ -565,7 +558,7 @@ namespace ARMeilleure.Instructions { if (Optimizations.UseSse2) { - EmitSse2Ucvtf(context, scalar: true); + EmitSse2UcvtfOp(context, scalar: true); } else { @@ -585,7 +578,7 @@ namespace ARMeilleure.Instructions { if (Optimizations.UseSse2) { - EmitSse2Ucvtf(context, scalar: false); + EmitSse2UcvtfOp(context, scalar: false); } else { @@ -597,7 +590,7 @@ namespace ARMeilleure.Instructions { if (Optimizations.UseSse2) { - EmitSse2Ucvtf(context, scalar: false); + EmitSse2UcvtfOp(context, scalar: false); } else { @@ -628,21 +621,21 @@ namespace ARMeilleure.Instructions if (sizeF == 0) { - Delegate dlg = signed - ? (Delegate)new _S32_F32(SoftFallback.SatF32ToS32) - : (Delegate)new _U32_F32(SoftFallback.SatF32ToU32); + MethodInfo info = signed + ? typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToS32)) + : typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToU32)); - e = context.Call(dlg, e); + e = context.Call(info, e); e = context.ZeroExtend32(OperandType.I64, e); } else /* if (sizeF == 1) */ { - Delegate dlg = signed - ? (Delegate)new _S64_F64(SoftFallback.SatF64ToS64) - : (Delegate)new _U64_F64(SoftFallback.SatF64ToU64); + MethodInfo info = signed + ? typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToS64)) + : typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToU64)); - e = context.Call(dlg, e); + e = context.Call(info, e); } res = EmitVectorInsert(context, res, e, index, sizeI); @@ -676,21 +669,21 @@ namespace ARMeilleure.Instructions if (sizeF == 0) { - Delegate dlg = signed - ? (Delegate)new _S32_F32(SoftFallback.SatF32ToS32) - : (Delegate)new _U32_F32(SoftFallback.SatF32ToU32); + MethodInfo info = signed + ? typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToS32)) + : typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToU32)); - e = context.Call(dlg, e); + e = context.Call(info, e); e = context.ZeroExtend32(OperandType.I64, e); } else /* if (sizeF == 1) */ { - Delegate dlg = signed - ? (Delegate)new _S64_F64(SoftFallback.SatF64ToS64) - : (Delegate)new _U64_F64(SoftFallback.SatF64ToU64); + MethodInfo info = signed + ? typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToS64)) + : typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToU64)); - e = context.Call(dlg, e); + e = context.Call(info, e); } res = EmitVectorInsert(context, res, e, index, sizeI); @@ -809,22 +802,22 @@ namespace ARMeilleure.Instructions value = EmitF2iFBitsMul(context, value, fBits); + MethodInfo info; + if (context.CurrOp.RegisterSize == RegisterSize.Int32) { - Delegate dlg = value.Type == OperandType.FP32 - ? (Delegate)new _S32_F32(SoftFallback.SatF32ToS32) - : (Delegate)new _S32_F64(SoftFallback.SatF64ToS32); - - return context.Call(dlg, value); + info = value.Type == OperandType.FP32 + ? typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToS32)) + : typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToS32)); } else { - Delegate dlg = value.Type == OperandType.FP32 - ? (Delegate)new _S64_F32(SoftFallback.SatF32ToS64) - : (Delegate)new _S64_F64(SoftFallback.SatF64ToS64); - - return context.Call(dlg, value); + info = value.Type == OperandType.FP32 + ? typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToS64)) + : typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToS64)); } + + return context.Call(info, value); } private static Operand EmitScalarFcvtu(ArmEmitterContext context, Operand value, int fBits) @@ -833,22 +826,22 @@ namespace ARMeilleure.Instructions value = EmitF2iFBitsMul(context, value, fBits); + MethodInfo info; + if (context.CurrOp.RegisterSize == RegisterSize.Int32) { - Delegate dlg = value.Type == OperandType.FP32 - ? (Delegate)new _U32_F32(SoftFallback.SatF32ToU32) - : (Delegate)new _U32_F64(SoftFallback.SatF64ToU32); - - return context.Call(dlg, value); + info = value.Type == OperandType.FP32 + ? typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToU32)) + : typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToU32)); } else { - Delegate dlg = value.Type == OperandType.FP32 - ? (Delegate)new _U64_F32(SoftFallback.SatF32ToU64) - : (Delegate)new _U64_F64(SoftFallback.SatF64ToU64); - - return context.Call(dlg, value); + info = value.Type == OperandType.FP32 + ? typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToU64)) + : typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToU64)); } + + return context.Call(info, value); } private static Operand EmitF2iFBitsMul(ArmEmitterContext context, Operand value, int fBits) @@ -925,7 +918,7 @@ namespace ARMeilleure.Instructions return res; } - private static void EmitSse2Scvtf(ArmEmitterContext context, bool scalar) + private static void EmitSse2ScvtfOp(ArmEmitterContext context, bool scalar) { OpCodeSimd op = (OpCodeSimd)context.CurrOp; @@ -990,7 +983,7 @@ namespace ARMeilleure.Instructions } } - private static void EmitSse2Ucvtf(ArmEmitterContext context, bool scalar) + private static void EmitSse2UcvtfOp(ArmEmitterContext context, bool scalar) { OpCodeSimd op = (OpCodeSimd)context.CurrOp; @@ -1079,7 +1072,7 @@ namespace ARMeilleure.Instructions } } - private static void EmitSse41Fcvts(ArmEmitterContext context, FPRoundingMode roundMode, bool scalar) + private static void EmitSse41FcvtsOpF(ArmEmitterContext context, FPRoundingMode roundMode, bool scalar) { OpCodeSimd op = (OpCodeSimd)context.CurrOp; @@ -1170,7 +1163,7 @@ namespace ARMeilleure.Instructions } } - private static void EmitSse41Fcvtu(ArmEmitterContext context, FPRoundingMode roundMode, bool scalar) + private static void EmitSse41FcvtuOpF(ArmEmitterContext context, FPRoundingMode roundMode, bool scalar) { OpCodeSimd op = (OpCodeSimd)context.CurrOp; diff --git a/ARMeilleure/Instructions/InstEmitSimdCvt32.cs b/ARMeilleure/Instructions/InstEmitSimdCvt32.cs index 4f2139a42..00b8ffd63 100644 --- a/ARMeilleure/Instructions/InstEmitSimdCvt32.cs +++ b/ARMeilleure/Instructions/InstEmitSimdCvt32.cs @@ -4,6 +4,7 @@ using ARMeilleure.State; using ARMeilleure.Translation; using System; using System.Diagnostics; +using System.Reflection; using static ARMeilleure.Instructions.InstEmitHelper; using static ARMeilleure.Instructions.InstEmitSimdHelper; @@ -20,7 +21,7 @@ namespace ARMeilleure.Instructions { // Move the low bit to the top. return ((vd & 0x1) << 4) | (vd >> 1); - } + } else { // Move the high bit to the bottom. @@ -30,29 +31,22 @@ namespace ARMeilleure.Instructions private static Operand EmitSaturateFloatToInt(ArmEmitterContext context, Operand op1, bool unsigned) { + MethodInfo info; + if (op1.Type == OperandType.FP64) { - if (unsigned) - { - return context.Call(new _U32_F64(SoftFallback.SatF64ToU32), op1); - } - else - { - return context.Call(new _S32_F64(SoftFallback.SatF64ToS32), op1); - } - + info = unsigned + ? typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToU32)) + : typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToS32)); } else { - if (unsigned) - { - return context.Call(new _U32_F32(SoftFallback.SatF32ToU32), op1); - } - else - { - return context.Call(new _S32_F32(SoftFallback.SatF32ToS32), op1); - } + info = unsigned + ? typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToU32)) + : typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToS32)); } + + return context.Call(info, op1); } public static void Vcvt_V(ArmEmitterContext context) @@ -96,7 +90,7 @@ namespace ARMeilleure.Instructions res2 = context.AddIntrinsic(Intrinsic.X86Cvtdq2ps, res2); return context.AddIntrinsic(Intrinsic.X86Addps, res, res2); - } + } else { return context.AddIntrinsic(Intrinsic.X86Cvtdq2ps, n); @@ -114,9 +108,7 @@ namespace ARMeilleure.Instructions EmitVectorUnaryOpSx32(context, (op1) => EmitFPConvert(context, op1, floatSize, true)); } } - } - } public static void Vcvt_FD(ArmEmitterContext context) @@ -173,29 +165,22 @@ namespace ARMeilleure.Instructions // TODO: Fast Path. if (roundWithFpscr) { + MethodInfo info; + if (floatSize == OperandType.FP64) { - if (unsigned) - { - asInteger = context.Call(new _U32_F64(SoftFallback.DoubleToUInt32), toConvert); - } - else - { - asInteger = context.Call(new _S32_F64(SoftFallback.DoubleToInt32), toConvert); - } - + info = unsigned + ? typeof(SoftFallback).GetMethod(nameof(SoftFallback.DoubleToUInt32)) + : typeof(SoftFallback).GetMethod(nameof(SoftFallback.DoubleToInt32)); } else { - if (unsigned) - { - asInteger = context.Call(new _U32_F32(SoftFallback.FloatToUInt32), toConvert); - } - else - { - asInteger = context.Call(new _S32_F32(SoftFallback.FloatToInt32), toConvert); - } + info = unsigned + ? typeof(SoftFallback).GetMethod(nameof(SoftFallback.FloatToUInt32)) + : typeof(SoftFallback).GetMethod(nameof(SoftFallback.FloatToInt32)); } + + asInteger = context.Call(info, toConvert); } else { @@ -205,7 +190,7 @@ namespace ARMeilleure.Instructions InsertScalar(context, op.Vd, asInteger); } - } + } else { bool unsigned = op.Opc == 0; @@ -218,22 +203,17 @@ namespace ARMeilleure.Instructions } } - public static Operand EmitRoundMathCall(ArmEmitterContext context, MidpointRounding roundMode, Operand n) + private static Operand EmitRoundMathCall(ArmEmitterContext context, MidpointRounding roundMode, Operand n) { IOpCode32Simd op = (IOpCode32Simd)context.CurrOp; - Delegate dlg; + string name = nameof(Math.Round); - if ((op.Size & 1) == 0) - { - dlg = new _F32_F32_MidpointRounding(MathF.Round); - } - else /* if ((op.Size & 1) == 1) */ - { - dlg = new _F64_F64_MidpointRounding(Math.Round); - } + MethodInfo info = (op.Size & 1) == 0 + ? typeof(MathF).GetMethod(name, new Type[] { typeof(float), typeof(MidpointRounding) }) + : typeof(Math). GetMethod(name, new Type[] { typeof(double), typeof(MidpointRounding) }); - return context.Call(dlg, n, Const((int)roundMode)); + return context.Call(info, n, Const((int)roundMode)); } private static FPRoundingMode RMToRoundMode(int rm) @@ -282,10 +262,10 @@ namespace ARMeilleure.Instructions toConvert = EmitRoundMathCall(context, MidpointRounding.ToEven, toConvert); break; case 0b10: // Towards positive infinity - toConvert = EmitUnaryMathCall(context, MathF.Ceiling, Math.Ceiling, toConvert); + toConvert = EmitUnaryMathCall(context, nameof(Math.Ceiling), toConvert); break; case 0b11: // Towards negative infinity - toConvert = EmitUnaryMathCall(context, MathF.Floor, Math.Floor, toConvert); + toConvert = EmitUnaryMathCall(context, nameof(Math.Floor), toConvert); break; } @@ -316,7 +296,7 @@ namespace ARMeilleure.Instructions return context.AddIntrinsic(inst, m, Const(X86GetRoundControl(roundMode))); }); } - else + else { Operand toConvert = ExtractScalar(context, floatSize, op.Vm); @@ -329,10 +309,10 @@ namespace ARMeilleure.Instructions toConvert = EmitRoundMathCall(context, MidpointRounding.ToEven, toConvert); break; case 0b10: // Towards positive infinity - toConvert = EmitUnaryMathCall(context, MathF.Ceiling, Math.Ceiling, toConvert); + toConvert = EmitUnaryMathCall(context, nameof(Math.Ceiling), toConvert); break; case 0b11: // Towards negative infinity - toConvert = EmitUnaryMathCall(context, MathF.Floor, Math.Floor, toConvert); + toConvert = EmitUnaryMathCall(context, nameof(Math.Floor), toConvert); break; } @@ -351,10 +331,10 @@ namespace ARMeilleure.Instructions Intrinsic inst = (op.Size & 1) == 0 ? Intrinsic.X86Roundss : Intrinsic.X86Roundsd; return context.AddIntrinsic(inst, m, Const(X86GetRoundControl(FPRoundingMode.TowardsZero))); }); - } + } else { - EmitScalarUnaryOpF32(context, (op1) => EmitUnaryMathCall(context, MathF.Truncate, Math.Truncate, op1)); + EmitScalarUnaryOpF32(context, (op1) => EmitUnaryMathCall(context, nameof(Math.Truncate), op1)); } } @@ -423,7 +403,7 @@ namespace ARMeilleure.Instructions if (signed) { dRes = context.BitwiseExclusiveOr(nIntOrLong, nInt); - } + } else { dRes = context.BitwiseExclusiveOr(nIntOrLong2, nInt); @@ -527,7 +507,7 @@ namespace ARMeilleure.Instructions if (signed) { return context.AddIntrinsic(Intrinsic.X86Pxor, nInt, nRes); - } + } else { Operand dRes = context.AddIntrinsic(Intrinsic.X86Pxor, nInt2, nRes); diff --git a/ARMeilleure/Instructions/InstEmitSimdHash.cs b/ARMeilleure/Instructions/InstEmitSimdHash.cs index 4ed960612..ed8b4afdc 100644 --- a/ARMeilleure/Instructions/InstEmitSimdHash.cs +++ b/ARMeilleure/Instructions/InstEmitSimdHash.cs @@ -19,7 +19,7 @@ namespace ARMeilleure.Instructions Operand m = GetVec(op.Rm); - Operand res = context.Call(new _V128_V128_U32_V128(SoftFallback.HashChoose), d, ne, m); + Operand res = context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashChoose)), d, ne, m); context.Copy(GetVec(op.Rd), res); } @@ -30,7 +30,7 @@ namespace ARMeilleure.Instructions Operand ne = context.VectorExtract(OperandType.I32, GetVec(op.Rn), 0); - Operand res = context.Call(new _U32_U32(SoftFallback.FixedRotate), ne); + Operand res = context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.FixedRotate)), ne); context.Copy(GetVec(op.Rd), context.VectorCreateScalar(res)); } @@ -45,7 +45,7 @@ namespace ARMeilleure.Instructions Operand m = GetVec(op.Rm); - Operand res = context.Call(new _V128_V128_U32_V128(SoftFallback.HashMajority), d, ne, m); + Operand res = context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashMajority)), d, ne, m); context.Copy(GetVec(op.Rd), res); } @@ -60,7 +60,7 @@ namespace ARMeilleure.Instructions Operand m = GetVec(op.Rm); - Operand res = context.Call(new _V128_V128_U32_V128(SoftFallback.HashParity), d, ne, m); + Operand res = context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashParity)), d, ne, m); context.Copy(GetVec(op.Rd), res); } @@ -73,7 +73,7 @@ namespace ARMeilleure.Instructions Operand n = GetVec(op.Rn); Operand m = GetVec(op.Rm); - Operand res = context.Call(new _V128_V128_V128_V128(SoftFallback.Sha1SchedulePart1), d, n, m); + Operand res = context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Sha1SchedulePart1)), d, n, m); context.Copy(GetVec(op.Rd), res); } @@ -85,7 +85,7 @@ namespace ARMeilleure.Instructions Operand d = GetVec(op.Rd); Operand n = GetVec(op.Rn); - Operand res = context.Call(new _V128_V128_V128(SoftFallback.Sha1SchedulePart2), d, n); + Operand res = context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Sha1SchedulePart2)), d, n); context.Copy(GetVec(op.Rd), res); } @@ -100,7 +100,7 @@ namespace ARMeilleure.Instructions Operand n = GetVec(op.Rn); Operand m = GetVec(op.Rm); - Operand res = context.Call(new _V128_V128_V128_V128(SoftFallback.HashLower), d, n, m); + Operand res = context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashLower)), d, n, m); context.Copy(GetVec(op.Rd), res); } @@ -113,7 +113,7 @@ namespace ARMeilleure.Instructions Operand n = GetVec(op.Rn); Operand m = GetVec(op.Rm); - Operand res = context.Call(new _V128_V128_V128_V128(SoftFallback.HashUpper), d, n, m); + Operand res = context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashUpper)), d, n, m); context.Copy(GetVec(op.Rd), res); } @@ -125,7 +125,7 @@ namespace ARMeilleure.Instructions Operand d = GetVec(op.Rd); Operand n = GetVec(op.Rn); - Operand res = context.Call(new _V128_V128_V128(SoftFallback.Sha256SchedulePart1), d, n); + Operand res = context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Sha256SchedulePart1)), d, n); context.Copy(GetVec(op.Rd), res); } @@ -138,7 +138,7 @@ namespace ARMeilleure.Instructions Operand n = GetVec(op.Rn); Operand m = GetVec(op.Rm); - Operand res = context.Call(new _V128_V128_V128_V128(SoftFallback.Sha256SchedulePart2), d, n, m); + Operand res = context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Sha256SchedulePart2)), d, n, m); context.Copy(GetVec(op.Rd), res); } diff --git a/ARMeilleure/Instructions/InstEmitSimdHelper.cs b/ARMeilleure/Instructions/InstEmitSimdHelper.cs index bf8d54c18..912c22600 100644 --- a/ARMeilleure/Instructions/InstEmitSimdHelper.cs +++ b/ARMeilleure/Instructions/InstEmitSimdHelper.cs @@ -4,6 +4,7 @@ using ARMeilleure.State; using ARMeilleure.Translation; using System; using System.Diagnostics; +using System.Reflection; using static ARMeilleure.Instructions.InstEmitHelper; using static ARMeilleure.IntermediateRepresentation.OperandHelper; @@ -310,68 +311,39 @@ namespace ARMeilleure.Instructions context.Copy(GetVec(op.Rd), res); } - public static Operand EmitUnaryMathCall(ArmEmitterContext context, _F32_F32 f32, _F64_F64 f64, Operand n) + public static Operand EmitUnaryMathCall(ArmEmitterContext context, string name, Operand n) { IOpCodeSimd op = (IOpCodeSimd)context.CurrOp; - return (op.Size & 1) == 0 ? context.Call(f32, n) : context.Call(f64, n); + MethodInfo info = (op.Size & 1) == 0 + ? typeof(MathF).GetMethod(name, new Type[] { typeof(float) }) + : typeof(Math). GetMethod(name, new Type[] { typeof(double) }); + + return context.Call(info, n); } public static Operand EmitRoundMathCall(ArmEmitterContext context, MidpointRounding roundMode, Operand n) { IOpCodeSimd op = (IOpCodeSimd)context.CurrOp; - Delegate dlg; + string name = nameof(Math.Round); - if ((op.Size & 1) == 0) - { - dlg = new _F32_F32_MidpointRounding(MathF.Round); - } - else /* if ((op.Size & 1) == 1) */ - { - dlg = new _F64_F64_MidpointRounding(Math.Round); - } + MethodInfo info = (op.Size & 1) == 0 + ? typeof(MathF).GetMethod(name, new Type[] { typeof(float), typeof(MidpointRounding) }) + : typeof(Math). GetMethod(name, new Type[] { typeof(double), typeof(MidpointRounding) }); - return context.Call(dlg, n, Const((int)roundMode)); + return context.Call(info, n, Const((int)roundMode)); } - public static Operand EmitSoftFloatCall( - ArmEmitterContext context, - _F32_F32 f32, - _F64_F64 f64, - params Operand[] callArgs) + public static Operand EmitSoftFloatCall(ArmEmitterContext context, string name, params Operand[] callArgs) { IOpCodeSimd op = (IOpCodeSimd)context.CurrOp; - Delegate dlg = (op.Size & 1) == 0 ? (Delegate)f32 : (Delegate)f64; + MethodInfo info = (op.Size & 1) == 0 + ? typeof(SoftFloat32).GetMethod(name) + : typeof(SoftFloat64).GetMethod(name); - return context.Call(dlg, callArgs); - } - - public static Operand EmitSoftFloatCall( - ArmEmitterContext context, - _F32_F32_F32 f32, - _F64_F64_F64 f64, - params Operand[] callArgs) - { - IOpCodeSimd op = (IOpCodeSimd)context.CurrOp; - - Delegate dlg = (op.Size & 1) == 0 ? (Delegate)f32 : (Delegate)f64; - - return context.Call(dlg, callArgs); - } - - public static Operand EmitSoftFloatCall( - ArmEmitterContext context, - _F32_F32_F32_F32 f32, - _F64_F64_F64_F64 f64, - params Operand[] callArgs) - { - IOpCodeSimd op = (IOpCodeSimd)context.CurrOp; - - Delegate dlg = (op.Size & 1) == 0 ? (Delegate)f32 : (Delegate)f64; - - return context.Call(dlg, callArgs); + return context.Call(info, callArgs); } public static void EmitScalarBinaryOpByElemF(ArmEmitterContext context, Func2I emit) @@ -1425,22 +1397,22 @@ namespace ARMeilleure.Instructions throw new ArgumentOutOfRangeException(nameof(sizeDst)); } - Delegate dlg; + MethodInfo info; if (signedSrc) { - dlg = signedDst - ? (Delegate)new _S64_S64_S32(SoftFallback.SignedSrcSignedDstSatQ) - : (Delegate)new _U64_S64_S32(SoftFallback.SignedSrcUnsignedDstSatQ); + info = signedDst + ? typeof(SoftFallback).GetMethod(nameof(SoftFallback.SignedSrcSignedDstSatQ)) + : typeof(SoftFallback).GetMethod(nameof(SoftFallback.SignedSrcUnsignedDstSatQ)); } else { - dlg = signedDst - ? (Delegate)new _S64_U64_S32(SoftFallback.UnsignedSrcSignedDstSatQ) - : (Delegate)new _U64_U64_S32(SoftFallback.UnsignedSrcUnsignedDstSatQ); + info = signedDst + ? typeof(SoftFallback).GetMethod(nameof(SoftFallback.UnsignedSrcSignedDstSatQ)) + : typeof(SoftFallback).GetMethod(nameof(SoftFallback.UnsignedSrcUnsignedDstSatQ)); } - return context.Call(dlg, op, Const(sizeDst)); + return context.Call(info, op, Const(sizeDst)); } // TSrc (64bit) == TDst (64bit); signed. @@ -1448,7 +1420,7 @@ namespace ARMeilleure.Instructions { Debug.Assert(((OpCodeSimd)context.CurrOp).Size == 3, "Invalid element size."); - return context.Call(new _S64_S64(SoftFallback.UnarySignedSatQAbsOrNeg), op); + return context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.UnarySignedSatQAbsOrNeg)), op); } // TSrcs (64bit) == TDst (64bit); signed, unsigned. @@ -1456,11 +1428,11 @@ namespace ARMeilleure.Instructions { Debug.Assert(((OpCodeSimd)context.CurrOp).Size == 3, "Invalid element size."); - Delegate dlg = signed - ? (Delegate)new _S64_S64_S64(SoftFallback.BinarySignedSatQAdd) - : (Delegate)new _U64_U64_U64(SoftFallback.BinaryUnsignedSatQAdd); + MethodInfo info = signed + ? typeof(SoftFallback).GetMethod(nameof(SoftFallback.BinarySignedSatQAdd)) + : typeof(SoftFallback).GetMethod(nameof(SoftFallback.BinaryUnsignedSatQAdd)); - return context.Call(dlg, op1, op2); + return context.Call(info, op1, op2); } // TSrcs (64bit) == TDst (64bit); signed, unsigned. @@ -1468,11 +1440,11 @@ namespace ARMeilleure.Instructions { Debug.Assert(((OpCodeSimd)context.CurrOp).Size == 3, "Invalid element size."); - Delegate dlg = signed - ? (Delegate)new _S64_S64_S64(SoftFallback.BinarySignedSatQSub) - : (Delegate)new _U64_U64_U64(SoftFallback.BinaryUnsignedSatQSub); + MethodInfo info = signed + ? typeof(SoftFallback).GetMethod(nameof(SoftFallback.BinarySignedSatQSub)) + : typeof(SoftFallback).GetMethod(nameof(SoftFallback.BinaryUnsignedSatQSub)); - return context.Call(dlg, op1, op2); + return context.Call(info, op1, op2); } // TSrcs (64bit) == TDst (64bit); signed, unsigned. @@ -1480,11 +1452,11 @@ namespace ARMeilleure.Instructions { Debug.Assert(((OpCodeSimd)context.CurrOp).Size == 3, "Invalid element size."); - Delegate dlg = signed - ? (Delegate)new _S64_U64_S64(SoftFallback.BinarySignedSatQAcc) - : (Delegate)new _U64_S64_U64(SoftFallback.BinaryUnsignedSatQAcc); + MethodInfo info = signed + ? typeof(SoftFallback).GetMethod(nameof(SoftFallback.BinarySignedSatQAcc)) + : typeof(SoftFallback).GetMethod(nameof(SoftFallback.BinaryUnsignedSatQAcc)); - return context.Call(dlg, op1, op2); + return context.Call(info, op1, op2); } public static Operand EmitFloatAbs(ArmEmitterContext context, Operand value, bool single, bool vector) @@ -1493,7 +1465,7 @@ namespace ARMeilleure.Instructions if (single) { mask = vector ? X86GetAllElements(context, -0f) : X86GetScalar(context, -0f); - } + } else { mask = vector ? X86GetAllElements(context, -0d) : X86GetScalar(context, -0d); diff --git a/ARMeilleure/Instructions/InstEmitSimdHelper32.cs b/ARMeilleure/Instructions/InstEmitSimdHelper32.cs index 67dc25c7c..9697715a1 100644 --- a/ARMeilleure/Instructions/InstEmitSimdHelper32.cs +++ b/ARMeilleure/Instructions/InstEmitSimdHelper32.cs @@ -3,6 +3,8 @@ using ARMeilleure.IntermediateRepresentation; using ARMeilleure.Translation; using System; using System.Diagnostics; +using System.Reflection; + using static ARMeilleure.Instructions.InstEmitHelper; using static ARMeilleure.Instructions.InstEmitSimdHelper; using static ARMeilleure.IntermediateRepresentation.OperandHelper; @@ -39,7 +41,7 @@ namespace ARMeilleure.Instructions { // From dreg. return context.VectorExtract(type, GetVecA32(reg >> 1), reg & 1); - } + } else { // From sreg. @@ -575,7 +577,7 @@ namespace ARMeilleure.Instructions if (targetSide == 1) { return context.AddIntrinsic(Intrinsic.X86Movlhps, input, input); // Low to high. - } + } else { return context.AddIntrinsic(Intrinsic.X86Movhlps, input, input); // High to low. @@ -592,7 +594,7 @@ namespace ARMeilleure.Instructions if (targetSide == 1) { return context.AddIntrinsic(Intrinsic.X86Shufpd, target, value, Const(shuffleMask)); - } + } else { return context.AddIntrinsic(Intrinsic.X86Shufpd, value, target, Const(shuffleMask)); @@ -622,7 +624,7 @@ namespace ARMeilleure.Instructions if (Optimizations.UseSse41) { return context.AddIntrinsic(Intrinsic.X86Insertps, target, value, Const(index << 4)); - } + } else { target = EmitSwapScalar(context, target, index, doubleWidth); // Swap value to replace into element 0. @@ -642,7 +644,7 @@ namespace ARMeilleure.Instructions { int shuffleMask = 1; // Swap top and bottom. (b0 = 1, b1 = 0) return context.AddIntrinsic(Intrinsic.X86Shufpd, target, target, Const(shuffleMask)); - } + } else { int shuffleMask = (3 << 6) | (2 << 4) | (1 << 2) | index; // Swap index and 0. (others remain) @@ -1000,52 +1002,18 @@ namespace ARMeilleure.Instructions // Generic Functions - public static Operand EmitSoftFloatCallDefaultFpscr( - ArmEmitterContext context, - _F32_F32_Bool f32, - _F64_F64_Bool f64, - params Operand[] callArgs) + public static Operand EmitSoftFloatCallDefaultFpscr(ArmEmitterContext context, string name, params Operand[] callArgs) { IOpCodeSimd op = (IOpCodeSimd)context.CurrOp; - Delegate dlg = (op.Size & 1) == 0 ? (Delegate)f32 : (Delegate)f64; + MethodInfo info = (op.Size & 1) == 0 + ? typeof(SoftFloat32).GetMethod(name) + : typeof(SoftFloat64).GetMethod(name); Array.Resize(ref callArgs, callArgs.Length + 1); callArgs[callArgs.Length - 1] = Const(1); - return context.Call(dlg, callArgs); - } - - public static Operand EmitSoftFloatCallDefaultFpscr( - ArmEmitterContext context, - _F32_F32_F32_Bool f32, - _F64_F64_F64_Bool f64, - params Operand[] callArgs) - { - IOpCodeSimd op = (IOpCodeSimd)context.CurrOp; - - Delegate dlg = (op.Size & 1) == 0 ? (Delegate)f32 : (Delegate)f64; - - Array.Resize(ref callArgs, callArgs.Length + 1); - callArgs[callArgs.Length - 1] = Const(1); - - return context.Call(dlg, callArgs); - } - - public static Operand EmitSoftFloatCallDefaultFpscr( - ArmEmitterContext context, - _F32_F32_F32_F32_Bool f32, - _F64_F64_F64_F64_Bool f64, - params Operand[] callArgs) - { - IOpCodeSimd op = (IOpCodeSimd)context.CurrOp; - - Delegate dlg = (op.Size & 1) == 0 ? (Delegate)f32 : (Delegate)f64; - - Array.Resize(ref callArgs, callArgs.Length + 1); - callArgs[callArgs.Length - 1] = Const(1); - - return context.Call(dlg, callArgs); + return context.Call(info, callArgs); } public static Operand EmitVectorExtractSx32(ArmEmitterContext context, int reg, int index, int size) diff --git a/ARMeilleure/Instructions/InstEmitSimdMove.cs b/ARMeilleure/Instructions/InstEmitSimdMove.cs index a1a4635f9..12fc71c99 100644 --- a/ARMeilleure/Instructions/InstEmitSimdMove.cs +++ b/ARMeilleure/Instructions/InstEmitSimdMove.cs @@ -1,8 +1,8 @@ using ARMeilleure.Decoders; using ARMeilleure.IntermediateRepresentation; using ARMeilleure.Translation; -using System; using System.Collections.Generic; +using System.Reflection; using static ARMeilleure.Instructions.InstEmitHelper; using static ARMeilleure.Instructions.InstEmitSimdHelper; @@ -284,13 +284,26 @@ namespace ARMeilleure.Instructions { OpCodeSimdFmov op = (OpCodeSimdFmov)context.CurrOp; - if (op.Size == 0) + if (Optimizations.UseSse2) { - context.Copy(GetVec(op.Rd), X86GetScalar(context, (int)op.Immediate)); + if (op.Size == 0) + { + context.Copy(GetVec(op.Rd), X86GetScalar(context, (int)op.Immediate)); + } + else + { + context.Copy(GetVec(op.Rd), X86GetScalar(context, op.Immediate)); + } } else { - context.Copy(GetVec(op.Rd), X86GetScalar(context, op.Immediate)); + Operand e = Const(op.Immediate); + + Operand res = context.VectorZero(); + + res = EmitVectorInsert(context, res, e, 0, op.Size + 2); + + context.Copy(GetVec(op.Rd), res); } } @@ -350,7 +363,7 @@ namespace ARMeilleure.Instructions { if (Optimizations.UseSse2) { - EmitSse2MoviMvni(context, not: false); + EmitSse2VectorMoviMvniOp(context, not: false); } else { @@ -362,7 +375,7 @@ namespace ARMeilleure.Instructions { if (Optimizations.UseSse2) { - EmitSse2MoviMvni(context, not: true); + EmitSse2VectorMoviMvniOp(context, not: true); } else { @@ -476,7 +489,7 @@ namespace ARMeilleure.Instructions EmitVectorZip(context, part: 1); } - private static void EmitSse2MoviMvni(ArmEmitterContext context, bool not) + private static void EmitSse2VectorMoviMvniOp(ArmEmitterContext context, bool not) { OpCodeSimdImm op = (OpCodeSimdImm)context.CurrOp; @@ -593,32 +606,30 @@ namespace ARMeilleure.Instructions args.Add(GetVec((op.Rn + index) & 0x1F)); } - Delegate dlg = null; + MethodInfo info = null; - switch (op.Size) + if (isTbl) { - case 1: dlg = isTbl - ? (Delegate)new _V128_V128_S32_V128 (SoftFallback.Tbl1) - : (Delegate)new _V128_V128_V128_S32_V128(SoftFallback.Tbx1); - break; - - case 2: dlg = isTbl - ? (Delegate)new _V128_V128_S32_V128_V128 (SoftFallback.Tbl2) - : (Delegate)new _V128_V128_V128_S32_V128_V128(SoftFallback.Tbx2); - break; - - case 3: dlg = isTbl - ? (Delegate)new _V128_V128_S32_V128_V128_V128 (SoftFallback.Tbl3) - : (Delegate)new _V128_V128_V128_S32_V128_V128_V128(SoftFallback.Tbx3); - break; - - case 4: dlg = isTbl - ? (Delegate)new _V128_V128_S32_V128_V128_V128_V128 (SoftFallback.Tbl4) - : (Delegate)new _V128_V128_V128_S32_V128_V128_V128_V128(SoftFallback.Tbx4); - break; + switch (op.Size) + { + case 1: info = typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl1)); break; + case 2: info = typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl2)); break; + case 3: info = typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl3)); break; + case 4: info = typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl4)); break; + } + } + else + { + switch (op.Size) + { + case 1: info = typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx1)); break; + case 2: info = typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx2)); break; + case 3: info = typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx3)); break; + case 4: info = typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx4)); break; + } } - context.Copy(d, context.Call(dlg, args.ToArray())); + context.Copy(d, context.Call(info, args.ToArray())); } } diff --git a/ARMeilleure/Instructions/InstEmitSimdShift.cs b/ARMeilleure/Instructions/InstEmitSimdShift.cs index 19c0a74d2..0b3d85aeb 100644 --- a/ARMeilleure/Instructions/InstEmitSimdShift.cs +++ b/ARMeilleure/Instructions/InstEmitSimdShift.cs @@ -5,6 +5,7 @@ using ARMeilleure.IntermediateRepresentation; using ARMeilleure.Translation; using System; using System.Diagnostics; +using System.Reflection; using static ARMeilleure.Instructions.InstEmitHelper; using static ARMeilleure.Instructions.InstEmitSimdHelper; @@ -198,7 +199,7 @@ namespace ARMeilleure.Instructions Operand ne = EmitVectorExtractSx(context, op.Rn, index, op.Size); Operand me = EmitVectorExtractSx(context, op.Rm, index, op.Size); - Operand e = context.Call(new _S64_S64_S64_Bool_S32(SoftFallback.SignedShlRegSatQ), ne, me, Const(1), Const(op.Size)); + Operand e = context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SignedShlRegSatQ)), ne, me, Const(1), Const(op.Size)); res = EmitVectorInsert(context, res, e, index, op.Size); } @@ -239,7 +240,7 @@ namespace ARMeilleure.Instructions Operand ne = EmitVectorExtractSx(context, op.Rn, index, op.Size); Operand me = EmitVectorExtractSx(context, op.Rm, index, op.Size); - Operand e = context.Call(new _S64_S64_S64_Bool_S32(SoftFallback.SignedShlRegSatQ), ne, me, Const(0), Const(op.Size)); + Operand e = context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SignedShlRegSatQ)), ne, me, Const(0), Const(op.Size)); res = EmitVectorInsert(context, res, e, index, op.Size); } @@ -290,7 +291,7 @@ namespace ARMeilleure.Instructions Operand ne = EmitVectorExtractSx(context, op.Rn, index, op.Size); Operand me = EmitVectorExtractSx(context, op.Rm, index, op.Size); - Operand e = context.Call(new _S64_S64_S64_Bool_S32(SoftFallback.SignedShlReg), ne, me, Const(1), Const(op.Size)); + Operand e = context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SignedShlReg)), ne, me, Const(1), Const(op.Size)); res = EmitVectorInsert(context, res, e, index, op.Size); } @@ -403,7 +404,7 @@ namespace ARMeilleure.Instructions Operand ne = EmitVectorExtractSx(context, op.Rn, index, op.Size); Operand me = EmitVectorExtractSx(context, op.Rm, index, op.Size); - Operand e = context.Call(new _S64_S64_S64_Bool_S32(SoftFallback.SignedShlReg), ne, me, Const(0), Const(op.Size)); + Operand e = context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SignedShlReg)), ne, me, Const(0), Const(op.Size)); res = EmitVectorInsert(context, res, e, index, op.Size); } @@ -527,7 +528,7 @@ namespace ARMeilleure.Instructions Operand ne = EmitVectorExtractZx(context, op.Rn, index, op.Size); Operand me = EmitVectorExtractZx(context, op.Rm, index, op.Size); - Operand e = context.Call(new _U64_U64_U64_Bool_S32(SoftFallback.UnsignedShlRegSatQ), ne, me, Const(1), Const(op.Size)); + Operand e = context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.UnsignedShlRegSatQ)), ne, me, Const(1), Const(op.Size)); res = EmitVectorInsert(context, res, e, index, op.Size); } @@ -558,7 +559,7 @@ namespace ARMeilleure.Instructions Operand ne = EmitVectorExtractZx(context, op.Rn, index, op.Size); Operand me = EmitVectorExtractZx(context, op.Rm, index, op.Size); - Operand e = context.Call(new _U64_U64_U64_Bool_S32(SoftFallback.UnsignedShlRegSatQ), ne, me, Const(0), Const(op.Size)); + Operand e = context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.UnsignedShlRegSatQ)), ne, me, Const(0), Const(op.Size)); res = EmitVectorInsert(context, res, e, index, op.Size); } @@ -589,7 +590,7 @@ namespace ARMeilleure.Instructions Operand ne = EmitVectorExtractZx(context, op.Rn, index, op.Size); Operand me = EmitVectorExtractZx(context, op.Rm, index, op.Size); - Operand e = context.Call(new _U64_U64_U64_Bool_S32(SoftFallback.UnsignedShlReg), ne, me, Const(1), Const(op.Size)); + Operand e = context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.UnsignedShlReg)), ne, me, Const(1), Const(op.Size)); res = EmitVectorInsert(context, res, e, index, op.Size); } @@ -1026,11 +1027,11 @@ namespace ARMeilleure.Instructions long roundConst, int shift) { - Delegate dlg = signed - ? (Delegate)new _S64_S64_S64_S32(SoftFallback.SignedShrImm64) - : (Delegate)new _U64_U64_S64_S32(SoftFallback.UnsignedShrImm64); + MethodInfo info = signed + ? typeof(SoftFallback).GetMethod(nameof(SoftFallback.SignedShrImm64)) + : typeof(SoftFallback).GetMethod(nameof(SoftFallback.UnsignedShrImm64)); - return context.Call(dlg, value, Const(roundConst), Const(shift)); + return context.Call(info, value, Const(roundConst), Const(shift)); } private static void EmitVectorShImmWidenBinarySx(ArmEmitterContext context, Func2I emit, int imm) diff --git a/ARMeilleure/Instructions/InstEmitSimdShift32.cs b/ARMeilleure/Instructions/InstEmitSimdShift32.cs index e57c92a3f..b9055c30c 100644 --- a/ARMeilleure/Instructions/InstEmitSimdShift32.cs +++ b/ARMeilleure/Instructions/InstEmitSimdShift32.cs @@ -1,9 +1,9 @@ using ARMeilleure.Decoders; using ARMeilleure.IntermediateRepresentation; -using ARMeilleure.State; using ARMeilleure.Translation; using System; using System.Diagnostics; +using System.Reflection; using static ARMeilleure.Instructions.InstEmitSimdHelper32; using static ARMeilleure.IntermediateRepresentation.OperandHelper; @@ -244,11 +244,11 @@ namespace ARMeilleure.Instructions long roundConst, int shift) { - Delegate dlg = signed - ? (Delegate)new _S64_S64_S64_S32(SoftFallback.SignedShrImm64) - : (Delegate)new _U64_U64_S64_S32(SoftFallback.UnsignedShrImm64); + MethodInfo info = signed + ? typeof(SoftFallback).GetMethod(nameof(SoftFallback.SignedShrImm64)) + : typeof(SoftFallback).GetMethod(nameof(SoftFallback.UnsignedShrImm64)); - return context.Call(dlg, value, Const(roundConst), Const(shift)); + return context.Call(info, value, Const(roundConst), Const(shift)); } private static Operand EmitSatQ(ArmEmitterContext context, Operand value, int eSize, bool signed) diff --git a/ARMeilleure/Instructions/InstEmitSystem.cs b/ARMeilleure/Instructions/InstEmitSystem.cs index c13f0c3e4..827c3a793 100644 --- a/ARMeilleure/Instructions/InstEmitSystem.cs +++ b/ARMeilleure/Instructions/InstEmitSystem.cs @@ -3,6 +3,7 @@ using ARMeilleure.IntermediateRepresentation; using ARMeilleure.State; using ARMeilleure.Translation; using System; +using System.Reflection; using static ARMeilleure.Instructions.InstEmitHelper; using static ARMeilleure.IntermediateRepresentation.OperandHelper; @@ -27,44 +28,44 @@ namespace ARMeilleure.Instructions { OpCodeSystem op = (OpCodeSystem)context.CurrOp; - Delegate dlg; + MethodInfo info; switch (GetPackedId(op)) { - case 0b11_011_0000_0000_001: dlg = new _U64(NativeInterface.GetCtrEl0); break; - case 0b11_011_0000_0000_111: dlg = new _U64(NativeInterface.GetDczidEl0); break; - case 0b11_011_0100_0010_000: EmitGetNzcv(context); return; - case 0b11_011_0100_0100_000: dlg = new _U64(NativeInterface.GetFpcr); break; - case 0b11_011_0100_0100_001: dlg = new _U64(NativeInterface.GetFpsr); break; - case 0b11_011_1101_0000_010: dlg = new _U64(NativeInterface.GetTpidrEl0); break; - case 0b11_011_1101_0000_011: dlg = new _U64(NativeInterface.GetTpidr); break; - case 0b11_011_1110_0000_000: dlg = new _U64(NativeInterface.GetCntfrqEl0); break; - case 0b11_011_1110_0000_001: dlg = new _U64(NativeInterface.GetCntpctEl0); break; - case 0b11_011_1110_0000_010: dlg = new _U64(NativeInterface.GetCntvctEl0); break; + case 0b11_011_0000_0000_001: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCtrEl0)); break; + case 0b11_011_0000_0000_111: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetDczidEl0)); break; + case 0b11_011_0100_0010_000: EmitGetNzcv(context); return; + case 0b11_011_0100_0100_000: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFpcr)); break; + case 0b11_011_0100_0100_001: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFpsr)); break; + case 0b11_011_1101_0000_010: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrEl0)); break; + case 0b11_011_1101_0000_011: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidr)); break; + case 0b11_011_1110_0000_000: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntfrqEl0)); break; + case 0b11_011_1110_0000_001: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntpctEl0)); break; + case 0b11_011_1110_0000_010: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntvctEl0)); break; default: throw new NotImplementedException($"Unknown MRS 0x{op.RawOpCode:X8} at 0x{op.Address:X16}."); } - SetIntOrZR(context, op.Rt, context.Call(dlg)); + SetIntOrZR(context, op.Rt, context.Call(info)); } public static void Msr(ArmEmitterContext context) { OpCodeSystem op = (OpCodeSystem)context.CurrOp; - Delegate dlg; + MethodInfo info; switch (GetPackedId(op)) { - case 0b11_011_0100_0010_000: EmitSetNzcv(context); return; - case 0b11_011_0100_0100_000: dlg = new _Void_U64(NativeInterface.SetFpcr); break; - case 0b11_011_0100_0100_001: dlg = new _Void_U64(NativeInterface.SetFpsr); break; - case 0b11_011_1101_0000_010: dlg = new _Void_U64(NativeInterface.SetTpidrEl0); break; + case 0b11_011_0100_0010_000: EmitSetNzcv(context); return; + case 0b11_011_0100_0100_000: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpcr)); break; + case 0b11_011_0100_0100_001: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsr)); break; + case 0b11_011_1101_0000_010: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetTpidrEl0)); break; default: throw new NotImplementedException($"Unknown MSR 0x{op.RawOpCode:X8} at 0x{op.Address:X16}."); } - context.Call(dlg, GetIntOrZR(context, op.Rt)); + context.Call(info, GetIntOrZR(context, op.Rt)); } public static void Nop(ArmEmitterContext context) @@ -90,7 +91,7 @@ namespace ARMeilleure.Instructions { Operand address = context.Add(t, Const(offset)); - context.Call(new _Void_U64_U64(NativeInterface.WriteUInt64), address, Const(0L)); + context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt64)), address, Const(0L)); } break; diff --git a/ARMeilleure/Instructions/InstEmitSystem32.cs b/ARMeilleure/Instructions/InstEmitSystem32.cs index ebf829a04..14f73c3ac 100644 --- a/ARMeilleure/Instructions/InstEmitSystem32.cs +++ b/ARMeilleure/Instructions/InstEmitSystem32.cs @@ -3,6 +3,7 @@ using ARMeilleure.IntermediateRepresentation; using ARMeilleure.State; using ARMeilleure.Translation; using System; +using System.Reflection; using static ARMeilleure.Instructions.InstEmitHelper; using static ARMeilleure.IntermediateRepresentation.OperandHelper; @@ -18,6 +19,7 @@ namespace ARMeilleure.Instructions if (op.Coproc != 15) { InstEmit.Und(context); + return; } @@ -26,7 +28,8 @@ namespace ARMeilleure.Instructions throw new NotImplementedException($"Unknown MRC Opc1 0x{op.Opc1:X16} at 0x{op.Address:X16}."); } - Delegate dlg; + MethodInfo info; + switch (op.CRn) { case 13: // Process and Thread Info. @@ -34,13 +37,16 @@ namespace ARMeilleure.Instructions { throw new NotImplementedException($"Unknown MRC CRm 0x{op.CRm:X16} at 0x{op.Address:X16}."); } + switch (op.Opc2) { case 2: - dlg = new _Void_U32(NativeInterface.SetTpidrEl032); break; + info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetTpidrEl032)); break; + default: throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X16} at 0x{op.Address:X16}."); } + break; case 7: @@ -51,18 +57,20 @@ namespace ARMeilleure.Instructions { case 5: // Data Memory Barrier Register. return; // No-op. + default: throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X16} at 0x{op.Address:X16}."); } + default: throw new NotImplementedException($"Unknown MRC CRm 0x{op.CRm:X16} at 0x{op.Address:X16}."); } - default: + default: throw new NotImplementedException($"Unknown MRC 0x{op.RawOpCode:X8} at 0x{op.Address:X16}."); } - context.Call(dlg, GetIntA32(context, op.Rt)); + context.Call(info, GetIntA32(context, op.Rt)); } public static void Mrc(ArmEmitterContext context) @@ -72,6 +80,7 @@ namespace ARMeilleure.Instructions if (op.Coproc != 15) { InstEmit.Und(context); + return; } @@ -80,7 +89,8 @@ namespace ARMeilleure.Instructions throw new NotImplementedException($"Unknown MRC Opc1 0x{op.Opc1:X16} at 0x{op.Address:X16}."); } - Delegate dlg; + MethodInfo info; + switch (op.CRn) { case 13: // Process and Thread Info. @@ -88,30 +98,35 @@ namespace ARMeilleure.Instructions { throw new NotImplementedException($"Unknown MRC CRm 0x{op.CRm:X16} at 0x{op.Address:X16}."); } + switch (op.Opc2) { case 2: - dlg = new _U32(NativeInterface.GetTpidrEl032); break; + info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrEl032)); break; + case 3: - dlg = new _U32(NativeInterface.GetTpidr32); break; + info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidr32)); break; + default: throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X16} at 0x{op.Address:X16}."); } + break; - default: + + default: throw new NotImplementedException($"Unknown MRC 0x{op.RawOpCode:X8} at 0x{op.Address:X16}."); } if (op.Rt == RegisterAlias.Aarch32Pc) { // Special behavior: copy NZCV flags into APSR. - EmitSetNzcv(context, context.Call(dlg)); - + EmitSetNzcv(context, context.Call(info)); + return; } else { - SetIntA32(context, op.Rt, context.Call(dlg)); + SetIntA32(context, op.Rt, context.Call(info)); } } @@ -122,28 +137,33 @@ namespace ARMeilleure.Instructions if (op.Coproc != 15) { InstEmit.Und(context); + return; } - var opc = op.MrrcOp; + int opc = op.MrrcOp; + + MethodInfo info; - Delegate dlg; switch (op.CRm) { case 14: // Timer. switch (opc) { case 0: - dlg = new _U64(NativeInterface.GetCntpctEl0); break; + info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntpctEl0)); break; + default: throw new NotImplementedException($"Unknown MRRC Opc1 0x{opc:X16} at 0x{op.Address:X16}."); } + break; - default: + + default: throw new NotImplementedException($"Unknown MRRC 0x{op.RawOpCode:X8} at 0x{op.Address:X16}."); } - Operand result = context.Call(dlg); + Operand result = context.Call(info); SetIntA32(context, op.Rt, context.ConvertI64ToI32(result)); SetIntA32(context, op.CRn, context.ConvertI64ToI32(context.ShiftRightUI(result, Const(32)))); @@ -162,16 +182,18 @@ namespace ARMeilleure.Instructions SetFlag(context, PState.CFlag, GetFpFlag(FPState.CFlag)); SetFlag(context, PState.ZFlag, GetFpFlag(FPState.ZFlag)); SetFlag(context, PState.NFlag, GetFpFlag(FPState.NFlag)); + return; } - Delegate dlg; + MethodInfo info; + switch (op.Sreg) { case 0b0000: // FPSID throw new NotImplementedException("Supervisor Only"); case 0b0001: // FPSCR - dlg = new _U32(NativeInterface.GetFpscr); break; + info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFpscr)); break; case 0b0101: // MVFR2 throw new NotImplementedException("MVFR2"); case 0b0110: // MVFR1 @@ -180,24 +202,25 @@ namespace ARMeilleure.Instructions throw new NotImplementedException("MVFR0"); case 0b1000: // FPEXC throw new NotImplementedException("Supervisor Only"); - default: + default: throw new NotImplementedException($"Unknown VMRS 0x{op.RawOpCode:X8} at 0x{op.Address:X16}."); } - SetIntA32(context, op.Rt, context.Call(dlg)); + SetIntA32(context, op.Rt, context.Call(info)); } public static void Vmsr(ArmEmitterContext context) { OpCode32SimdSpecial op = (OpCode32SimdSpecial)context.CurrOp; - Delegate dlg; + MethodInfo info; + switch (op.Sreg) { case 0b0000: // FPSID throw new NotImplementedException("Supervisor Only"); case 0b0001: // FPSCR - dlg = new _Void_U32(NativeInterface.SetFpscr); break; + info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpscr)); break; case 0b0101: // MVFR2 throw new NotImplementedException("MVFR2"); case 0b0110: // MVFR1 @@ -206,11 +229,11 @@ namespace ARMeilleure.Instructions throw new NotImplementedException("MVFR0"); case 0b1000: // FPEXC throw new NotImplementedException("Supervisor Only"); - default: + default: throw new NotImplementedException($"Unknown VMSR 0x{op.RawOpCode:X8} at 0x{op.Address:X16}."); } - context.Call(dlg, GetIntA32(context, op.Rt)); + context.Call(info, GetIntA32(context, op.Rt)); } private static void EmitSetNzcv(ArmEmitterContext context, Operand t) diff --git a/ARMeilleure/Instructions/NativeInterface.cs b/ARMeilleure/Instructions/NativeInterface.cs index 49faab357..1b2a92885 100644 --- a/ARMeilleure/Instructions/NativeInterface.cs +++ b/ARMeilleure/Instructions/NativeInterface.cs @@ -407,18 +407,22 @@ namespace ARMeilleure.Instructions public static ulong GetFunctionAddress(ulong address) { TranslatedFunction function = _context.Translator.GetOrTranslate(address, GetContext().ExecutionMode); - return (ulong)function.GetPointer().ToInt64(); + + return (ulong)function.FuncPtr.ToInt64(); } public static ulong GetIndirectFunctionAddress(ulong address, ulong entryAddress) { TranslatedFunction function = _context.Translator.GetOrTranslate(address, GetContext().ExecutionMode); - ulong ptr = (ulong)function.GetPointer().ToInt64(); + + ulong ptr = (ulong)function.FuncPtr.ToInt64(); + if (function.HighCq) { // Rewrite the host function address in the table to point to the highCq function. Marshal.WriteInt64((IntPtr)entryAddress, 8, (long)ptr); } + return ptr; } diff --git a/ARMeilleure/Instructions/SoftFloat.cs b/ARMeilleure/Instructions/SoftFloat.cs index d3e15a2ce..ec66bb863 100644 --- a/ARMeilleure/Instructions/SoftFloat.cs +++ b/ARMeilleure/Instructions/SoftFloat.cs @@ -903,6 +903,13 @@ namespace ARMeilleure.Instructions else { result = value1; + + if ((fpcr & FPCR.Fz) != 0 && float.IsSubnormal(result)) + { + context.Fpsr |= FPSR.Ufc; + + result = FPZero(result < 0f); + } } } else @@ -987,6 +994,13 @@ namespace ARMeilleure.Instructions else { result = value1; + + if ((fpcr & FPCR.Fz) != 0 && float.IsSubnormal(result)) + { + context.Fpsr |= FPSR.Ufc; + + result = FPZero(result < 0f); + } } } else @@ -2196,6 +2210,13 @@ namespace ARMeilleure.Instructions else { result = value1; + + if ((fpcr & FPCR.Fz) != 0 && double.IsSubnormal(result)) + { + context.Fpsr |= FPSR.Ufc; + + result = FPZero(result < 0d); + } } } else @@ -2280,6 +2301,13 @@ namespace ARMeilleure.Instructions else { result = value1; + + if ((fpcr & FPCR.Fz) != 0 && double.IsSubnormal(result)) + { + context.Fpsr |= FPSR.Ufc; + + result = FPZero(result < 0d); + } } } else diff --git a/ARMeilleure/IntermediateRepresentation/Operand.cs b/ARMeilleure/IntermediateRepresentation/Operand.cs index 2d5e762ac..6f6caea77 100644 --- a/ARMeilleure/IntermediateRepresentation/Operand.cs +++ b/ARMeilleure/IntermediateRepresentation/Operand.cs @@ -6,11 +6,13 @@ namespace ARMeilleure.IntermediateRepresentation class Operand { public OperandKind Kind { get; private set; } - public OperandType Type { get; private set; } public ulong Value { get; private set; } + public bool DisableCF { get; private set; } + public int? PtcIndex { get; private set; } + public List Assignments { get; } public List Uses { get; } @@ -26,14 +28,19 @@ namespace ARMeilleure.IntermediateRepresentation Type = type; } - public Operand With(OperandKind kind, OperandType type = OperandType.None, ulong value = 0) + public Operand With(OperandKind kind, OperandType type = OperandType.None, ulong value = 0, bool disableCF = false, int? index = null) { Kind = kind; Type = type; + Value = value; + DisableCF = disableCF; + PtcIndex = index; + Assignments.Clear(); Uses.Clear(); + return this; } @@ -47,9 +54,9 @@ namespace ARMeilleure.IntermediateRepresentation return With(OperandKind.Constant, OperandType.I32, value); } - public Operand With(long value) + public Operand With(long value, bool disableCF = false, int? index = null) { - return With(OperandKind.Constant, OperandType.I64, (ulong)value); + return With(OperandKind.Constant, OperandType.I64, (ulong)value, disableCF, index); } public Operand With(ulong value) diff --git a/ARMeilleure/IntermediateRepresentation/OperandHelper.cs b/ARMeilleure/IntermediateRepresentation/OperandHelper.cs index cfdb32d98..100409771 100644 --- a/ARMeilleure/IntermediateRepresentation/OperandHelper.cs +++ b/ARMeilleure/IntermediateRepresentation/OperandHelper.cs @@ -34,9 +34,9 @@ namespace ARMeilleure.IntermediateRepresentation return Operand().With(value); } - public static Operand Const(long value) + public static Operand Const(long value, bool disableCF = false, int? index = null) { - return Operand().With(value); + return Operand().With(value, disableCF, index); } public static Operand Const(ulong value) diff --git a/ARMeilleure/Translation/ArmEmitterContext.cs b/ARMeilleure/Translation/ArmEmitterContext.cs index a905c722b..7ce6a3f43 100644 --- a/ARMeilleure/Translation/ArmEmitterContext.cs +++ b/ARMeilleure/Translation/ArmEmitterContext.cs @@ -11,7 +11,7 @@ namespace ARMeilleure.Translation { class ArmEmitterContext : EmitterContext { - private Dictionary _labels; + private readonly Dictionary _labels; private OpCode _optOpLastCompare; private OpCode _optOpLastFlagSet; diff --git a/ARMeilleure/Translation/Compiler.cs b/ARMeilleure/Translation/Compiler.cs index ec2f29688..934c0db6f 100644 --- a/ARMeilleure/Translation/Compiler.cs +++ b/ARMeilleure/Translation/Compiler.cs @@ -7,18 +7,30 @@ using System.Runtime.InteropServices; namespace ARMeilleure.Translation { + using PTC; + static class Compiler { - public static T Compile(ControlFlowGraph cfg, OperandType[] argTypes, OperandType retType, CompilerOptions options) + public static T Compile( + ControlFlowGraph cfg, + OperandType[] argTypes, + OperandType retType, + CompilerOptions options, + PtcInfo ptcInfo = null) { - CompiledFunction func = Compile(cfg, argTypes, retType, options); + CompiledFunction func = Compile(cfg, argTypes, retType, options, ptcInfo); IntPtr codePtr = JitCache.Map(func); return Marshal.GetDelegateForFunctionPointer(codePtr); } - public static CompiledFunction Compile(ControlFlowGraph cfg, OperandType[] argTypes, OperandType retType, CompilerOptions options) + public static CompiledFunction Compile( + ControlFlowGraph cfg, + OperandType[] argTypes, + OperandType retType, + CompilerOptions options, + PtcInfo ptcInfo = null) { Logger.StartPass(PassName.Dominance); @@ -45,7 +57,7 @@ namespace ARMeilleure.Translation CompilerContext cctx = new CompilerContext(cfg, argTypes, retType, options); - return CodeGenerator.Generate(cctx); + return CodeGenerator.Generate(cctx, ptcInfo); } } } \ No newline at end of file diff --git a/ARMeilleure/Translation/DelegateCache.cs b/ARMeilleure/Translation/DelegateCache.cs deleted file mode 100644 index 7328c61a6..000000000 --- a/ARMeilleure/Translation/DelegateCache.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Reflection; - -namespace ARMeilleure.Translation -{ - static class DelegateCache - { - private static ConcurrentDictionary _delegates; - - static DelegateCache() - { - _delegates = new ConcurrentDictionary(); - } - - public static Delegate GetOrAdd(Delegate dlg) - { - return _delegates.GetOrAdd(GetKey(dlg.Method), (key) => dlg); - } - - private static string GetKey(MethodInfo info) - { - return $"{info.DeclaringType.FullName}.{info.Name}"; - } - } -} \ No newline at end of file diff --git a/ARMeilleure/Translation/DelegateHelper.cs b/ARMeilleure/Translation/DelegateHelper.cs new file mode 100644 index 000000000..f021d1160 --- /dev/null +++ b/ARMeilleure/Translation/DelegateHelper.cs @@ -0,0 +1,107 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; + +namespace ARMeilleure.Translation +{ + static class DelegateHelper + { + private const string DelegateTypesAssemblyName = "JitDelegateTypes"; + + private static readonly ModuleBuilder _modBuilder; + + private static readonly Dictionary _delegateTypesCache; + + static DelegateHelper() + { + AssemblyBuilder asmBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName(DelegateTypesAssemblyName), AssemblyBuilderAccess.Run); + + _modBuilder = asmBuilder.DefineDynamicModule(DelegateTypesAssemblyName); + + _delegateTypesCache = new Dictionary(); + } + + public static Delegate GetDelegate(MethodInfo info) + { + if (info == null) + { + throw new ArgumentNullException(nameof(info)); + } + + Type[] parameters = info.GetParameters().Select(pI => pI.ParameterType).ToArray(); + Type returnType = info.ReturnType; + + Type delegateType = GetDelegateType(parameters, returnType); + + return Delegate.CreateDelegate(delegateType, info); + } + + private static Type GetDelegateType(Type[] parameters, Type returnType) + { + string key = GetFunctionSignatureKey(parameters, returnType); + + if (!_delegateTypesCache.TryGetValue(key, out Type delegateType)) + { + delegateType = MakeDelegateType(parameters, returnType, key); + + _delegateTypesCache.TryAdd(key, delegateType); + } + + return delegateType; + } + + private static string GetFunctionSignatureKey(Type[] parameters, Type returnType) + { + string sig = GetTypeName(returnType); + + foreach (Type type in parameters) + { + sig += '_' + GetTypeName(type); + } + + return sig; + } + + private static string GetTypeName(Type type) + { + return type.FullName.Replace(".", string.Empty); + } + + private const MethodAttributes CtorAttributes = + MethodAttributes.RTSpecialName | + MethodAttributes.HideBySig | + MethodAttributes.Public; + + private const TypeAttributes DelegateTypeAttributes = + TypeAttributes.Class | + TypeAttributes.Public | + TypeAttributes.Sealed | + TypeAttributes.AnsiClass | + TypeAttributes.AutoClass; + + private const MethodImplAttributes ImplAttributes = + MethodImplAttributes.Runtime | + MethodImplAttributes.Managed; + + private const MethodAttributes InvokeAttributes = + MethodAttributes.Public | + MethodAttributes.HideBySig | + MethodAttributes.NewSlot | + MethodAttributes.Virtual; + + private static readonly Type[] _delegateCtorSignature = { typeof(object), typeof(IntPtr) }; + + private static Type MakeDelegateType(Type[] parameters, Type returnType, string name) + { + TypeBuilder builder = _modBuilder.DefineType(name, DelegateTypeAttributes, typeof(MulticastDelegate)); + + builder.DefineConstructor(CtorAttributes, CallingConventions.Standard, _delegateCtorSignature).SetImplementationFlags(ImplAttributes); + + builder.DefineMethod("Invoke", InvokeAttributes, returnType, parameters).SetImplementationFlags(ImplAttributes); + + return builder.CreateTypeInfo(); + } + } +} diff --git a/ARMeilleure/Translation/DelegateInfo.cs b/ARMeilleure/Translation/DelegateInfo.cs new file mode 100644 index 000000000..e68cfc1b7 --- /dev/null +++ b/ARMeilleure/Translation/DelegateInfo.cs @@ -0,0 +1,19 @@ +using System; +using System.Runtime.InteropServices; + +namespace ARMeilleure.Translation +{ + sealed class DelegateInfo + { + private readonly Delegate _dlg; // Ensure that this delegate will not be garbage collected. + + public IntPtr FuncPtr { get; } + + public DelegateInfo(Delegate dlg) + { + _dlg = dlg; + + FuncPtr = Marshal.GetFunctionPointerForDelegate(dlg); + } + } +} diff --git a/ARMeilleure/Translation/Delegates.cs b/ARMeilleure/Translation/Delegates.cs new file mode 100644 index 000000000..addb15f6f --- /dev/null +++ b/ARMeilleure/Translation/Delegates.cs @@ -0,0 +1,305 @@ +using ARMeilleure.Instructions; +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace ARMeilleure.Translation +{ + static class Delegates + { + public static bool TryGetDelegateFuncPtrByIndex(int index, out IntPtr funcPtr) + { + if (index >= 0 && index < _delegates.Count) + { + funcPtr = _delegates.Values[index].FuncPtr; // O(1). + + return true; + } + else + { + funcPtr = default; + + return false; + } + } + + public static IntPtr GetDelegateFuncPtrByIndex(int index) + { + if (index < 0 || index >= _delegates.Count) + { + throw new ArgumentOutOfRangeException($"({nameof(index)} = {index})"); + } + + return _delegates.Values[index].FuncPtr; // O(1). + } + + public static IntPtr GetDelegateFuncPtr(MethodInfo info) + { + if (info == null) + { + throw new ArgumentNullException(nameof(info)); + } + + string key = GetKey(info); + + if (!_delegates.TryGetValue(key, out DelegateInfo dlgInfo)) // O(log(n)). + { + throw new KeyNotFoundException($"({nameof(key)} = {key})"); + } + + return dlgInfo.FuncPtr; + } + + public static int GetDelegateIndex(MethodInfo info) + { + if (info == null) + { + throw new ArgumentNullException(nameof(info)); + } + + string key = GetKey(info); + + int index = _delegates.IndexOfKey(key); // O(log(n)). + + if (index == -1) + { + throw new KeyNotFoundException($"({nameof(key)} = {key})"); + } + + return index; + } + + private static void SetDelegateInfo(MethodInfo info) + { + string key = GetKey(info); + + Delegate dlg = DelegateHelper.GetDelegate(info); + + _delegates.Add(key, new DelegateInfo(dlg)); // ArgumentException (key). + } + + private static string GetKey(MethodInfo info) + { + return $"{info.DeclaringType.Name}.{info.Name}"; + } + + private static readonly SortedList _delegates; + + static Delegates() + { + _delegates = new SortedList(); + + SetDelegateInfo(typeof(Math).GetMethod(nameof(Math.Abs), new Type[] { typeof(double) })); + SetDelegateInfo(typeof(Math).GetMethod(nameof(Math.Ceiling), new Type[] { typeof(double) })); + SetDelegateInfo(typeof(Math).GetMethod(nameof(Math.Floor), new Type[] { typeof(double) })); + SetDelegateInfo(typeof(Math).GetMethod(nameof(Math.Round), new Type[] { typeof(double), typeof(MidpointRounding) })); + SetDelegateInfo(typeof(Math).GetMethod(nameof(Math.Truncate), new Type[] { typeof(double) })); + + SetDelegateInfo(typeof(MathF).GetMethod(nameof(MathF.Abs), new Type[] { typeof(float) })); + SetDelegateInfo(typeof(MathF).GetMethod(nameof(MathF.Ceiling), new Type[] { typeof(float) })); + SetDelegateInfo(typeof(MathF).GetMethod(nameof(MathF.Floor), new Type[] { typeof(float) })); + SetDelegateInfo(typeof(MathF).GetMethod(nameof(MathF.Round), new Type[] { typeof(float), typeof(MidpointRounding) })); + SetDelegateInfo(typeof(MathF).GetMethod(nameof(MathF.Truncate), new Type[] { typeof(float) })); + + SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.Break))); + SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.CheckSynchronization))); + SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ClearExclusive))); + SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntfrqEl0))); + SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntpctEl0))); + SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntvctEl0))); + SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCtrEl0))); + SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetDczidEl0))); + SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFpcr))); + SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFpscr))); // A32 only. + SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFpsr))); + SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFunctionAddress))); + SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetIndirectFunctionAddress))); + SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidr))); + SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidr32))); // A32 only. + SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrEl0))); + SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrEl032))); // A32 only. + SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadByte))); + SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadByteExclusive))); + SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt16))); + SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt16Exclusive))); + SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt32))); + SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt32Exclusive))); + SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt64))); + SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt64Exclusive))); + SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadVector128))); + SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadVector128Exclusive))); + SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpcr))); + SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpscr))); // A32 only. + SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsr))); + SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetTpidrEl0))); + SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetTpidrEl032))); // A32 only. + SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SupervisorCall))); + SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.Undefined))); + SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteByte))); + SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteByteExclusive))); + SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt16))); + SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt16Exclusive))); + SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt32))); + SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt32Exclusive))); + SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt64))); + SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt64Exclusive))); + SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteVector128))); + SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteVector128Exclusive))); + + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.BinarySignedSatQAcc))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.BinarySignedSatQAdd))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.BinarySignedSatQSub))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.BinaryUnsignedSatQAcc))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.BinaryUnsignedSatQAdd))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.BinaryUnsignedSatQSub))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.CountLeadingSigns))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.CountLeadingZeros))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.CountSetBits8))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32b))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32cb))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32ch))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32cw))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32cx))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32h))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32w))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32x))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Decrypt))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.DoubleToInt32))); // A32 only. + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.DoubleToUInt32))); // A32 only. + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Encrypt))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.FixedRotate))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.FloatToInt32))); // A32 only. + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.FloatToUInt32))); // A32 only. + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashChoose))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashLower))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashMajority))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashParity))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashUpper))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.InverseMixColumns))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.MixColumns))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Round))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.RoundF))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToS32))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToS64))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToU32))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToU64))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToS32))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToS64))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToU32))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToU64))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Sha1SchedulePart1))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Sha1SchedulePart2))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Sha256SchedulePart1))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Sha256SchedulePart2))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SignedShlReg))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SignedShlRegSatQ))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SignedShrImm64))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SignedSrcSignedDstSatQ))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SignedSrcUnsignedDstSatQ))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl1))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl2))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl3))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl4))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx1))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx2))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx3))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx4))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.UnarySignedSatQAbsOrNeg))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.UnsignedShlReg))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.UnsignedShlRegSatQ))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.UnsignedShrImm64))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.UnsignedSrcSignedDstSatQ))); + SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.UnsignedSrcUnsignedDstSatQ))); + + SetDelegateInfo(typeof(SoftFloat16_32).GetMethod(nameof(SoftFloat16_32.FPConvert))); + + SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPAdd))); + SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPAddFpscr))); // A32 only. + SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompare))); + SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareEQ))); + SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareEQFpscr))); // A32 only. + SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareGE))); + SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareGEFpscr))); // A32 only. + SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareGT))); + SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareGTFpscr))); // A32 only. + SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareLE))); + SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareLEFpscr))); // A32 only. + SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareLT))); + SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareLTFpscr))); // A32 only. + SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPDiv))); + SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMax))); + SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMaxFpscr))); // A32 only. + SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMaxNum))); + SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMaxNumFpscr))); // A32 only. + SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMin))); + SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMinFpscr))); // A32 only. + SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMinNum))); + SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMinNumFpscr))); // A32 only. + SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMul))); + SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulFpscr))); // A32 only. + SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulAdd))); + SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulAddFpscr))); // A32 only. + SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulSub))); + SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulSubFpscr))); // A32 only. + SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulX))); + SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPNegMulAdd))); + SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPNegMulSub))); + SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecipEstimate))); + SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecipEstimateFpscr))); // A32 only. + SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecipStep))); // A32 only. + SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecipStepFused))); + SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecpX))); + SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRSqrtEstimate))); + SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRSqrtEstimateFpscr))); // A32 only. + SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRSqrtStep))); // A32 only. + SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRSqrtStepFused))); + SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPSqrt))); + SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPSub))); + + SetDelegateInfo(typeof(SoftFloat32_16).GetMethod(nameof(SoftFloat32_16.FPConvert))); + + SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPAdd))); + SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPAddFpscr))); // A32 only. + SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompare))); + SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareEQ))); + SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareEQFpscr))); // A32 only. + SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareGE))); + SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareGEFpscr))); // A32 only. + SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareGT))); + SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareGTFpscr))); // A32 only. + SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareLE))); + SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareLEFpscr))); // A32 only. + SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareLT))); + SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareLTFpscr))); // A32 only. + SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPDiv))); + SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMax))); + SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMaxFpscr))); // A32 only. + SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMaxNum))); + SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMaxNumFpscr))); // A32 only. + SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMin))); + SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMinFpscr))); // A32 only. + SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMinNum))); + SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMinNumFpscr))); // A32 only. + SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMul))); + SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulFpscr))); // A32 only. + SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulAdd))); + SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulAddFpscr))); // A32 only. + SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulSub))); + SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulSubFpscr))); // A32 only. + SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulX))); + SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPNegMulAdd))); + SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPNegMulSub))); + SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecipEstimate))); + SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecipEstimateFpscr))); // A32 only. + SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecipStep))); // A32 only. + SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecipStepFused))); + SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecpX))); + SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRSqrtEstimate))); + SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRSqrtEstimateFpscr))); // A32 only. + SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRSqrtStep))); // A32 only. + SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRSqrtStepFused))); + SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPSqrt))); + SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPSub))); + } + } +} diff --git a/ARMeilleure/Translation/DirectCallStubs.cs b/ARMeilleure/Translation/DirectCallStubs.cs index 42a78c719..7c11fdb2f 100644 --- a/ARMeilleure/Translation/DirectCallStubs.cs +++ b/ARMeilleure/Translation/DirectCallStubs.cs @@ -2,6 +2,7 @@ using ARMeilleure.IntermediateRepresentation; using ARMeilleure.State; using System; +using System.Diagnostics; using System.Runtime.InteropServices; using static ARMeilleure.IntermediateRepresentation.OperandHelper; @@ -12,10 +13,10 @@ namespace ARMeilleure.Translation { private delegate long GuestFunction(IntPtr nativeContextPtr); - private static GuestFunction _directCallStub; - private static GuestFunction _directTailCallStub; - private static GuestFunction _indirectCallStub; - private static GuestFunction _indirectTailCallStub; + private static IntPtr _directCallStubPtr; + private static IntPtr _directTailCallStubPtr; + private static IntPtr _indirectCallStubPtr; + private static IntPtr _indirectTailCallStubPtr; private static readonly object _lock = new object(); private static bool _initialized; @@ -23,25 +24,32 @@ namespace ARMeilleure.Translation public static void InitializeStubs() { if (_initialized) return; + lock (_lock) { if (_initialized) return; - _directCallStub = GenerateDirectCallStub(false); - _directTailCallStub = GenerateDirectCallStub(true); - _indirectCallStub = GenerateIndirectCallStub(false); - _indirectTailCallStub = GenerateIndirectCallStub(true); + + _directCallStubPtr = Marshal.GetFunctionPointerForDelegate(GenerateDirectCallStub(false)); + _directTailCallStubPtr = Marshal.GetFunctionPointerForDelegate(GenerateDirectCallStub(true)); + _indirectCallStubPtr = Marshal.GetFunctionPointerForDelegate(GenerateIndirectCallStub(false)); + _indirectTailCallStubPtr = Marshal.GetFunctionPointerForDelegate(GenerateIndirectCallStub(true)); + _initialized = true; } } public static IntPtr DirectCallStub(bool tailCall) { - return Marshal.GetFunctionPointerForDelegate(tailCall ? _directTailCallStub : _directCallStub); + Debug.Assert(_initialized); + + return tailCall ? _directTailCallStubPtr : _directCallStubPtr; } public static IntPtr IndirectCallStub(bool tailCall) { - return Marshal.GetFunctionPointerForDelegate(tailCall ? _indirectTailCallStub : _indirectCallStub); + Debug.Assert(_initialized); + + return tailCall ? _indirectTailCallStubPtr : _indirectCallStubPtr; } private static void EmitCall(EmitterContext context, Operand address, bool tailCall) @@ -70,21 +78,18 @@ namespace ARMeilleure.Translation Operand address = context.Load(OperandType.I64, context.Add(nativeContextPtr, Const((long)NativeContext.GetCallAddressOffset()))); address = context.BitwiseOr(address, Const(address.Type, 1)); // Set call flag. - Operand functionAddr = context.Call(new _U64_U64(NativeInterface.GetFunctionAddress), address); + Operand functionAddr = context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFunctionAddress)), address); EmitCall(context, functionAddr, tailCall); ControlFlowGraph cfg = context.GetControlFlowGraph(); - OperandType[] argTypes = new OperandType[] - { - OperandType.I64 - }; + OperandType[] argTypes = new OperandType[] { OperandType.I64 }; return Compiler.Compile(cfg, argTypes, OperandType.I64, CompilerOptions.HighCq); } /// - /// Generates a stub that is used to find function addresses and add them to an indirect table. + /// Generates a stub that is used to find function addresses and add them to an indirect table. /// Used for indirect calls entries (already claimed) when their jump table does not have the host address yet. /// Takes a NativeContext like a translated guest function, and extracts the target indirect table entry from the NativeContext. /// If the function we find is highCq, the entry in the table is updated to point to that function rather than this stub. @@ -100,17 +105,14 @@ namespace ARMeilleure.Translation // We need to find the missing function. If the function is HighCq, then it replaces this stub in the indirect table. // Either way, we call it afterwards. - Operand functionAddr = context.Call(new _U64_U64_U64(NativeInterface.GetIndirectFunctionAddress), address, entryAddress); + Operand functionAddr = context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetIndirectFunctionAddress)), address, entryAddress); // Call and save the function. EmitCall(context, functionAddr, tailCall); ControlFlowGraph cfg = context.GetControlFlowGraph(); - OperandType[] argTypes = new OperandType[] - { - OperandType.I64 - }; + OperandType[] argTypes = new OperandType[] { OperandType.I64 }; return Compiler.Compile(cfg, argTypes, OperandType.I64, CompilerOptions.HighCq); } diff --git a/ARMeilleure/Translation/EmitterContext.cs b/ARMeilleure/Translation/EmitterContext.cs index f14c920e2..656f17040 100644 --- a/ARMeilleure/Translation/EmitterContext.cs +++ b/ARMeilleure/Translation/EmitterContext.cs @@ -3,12 +3,14 @@ using ARMeilleure.IntermediateRepresentation; using ARMeilleure.State; using System; using System.Collections.Generic; -using System.Runtime.InteropServices; +using System.Reflection; using static ARMeilleure.IntermediateRepresentation.OperandHelper; namespace ARMeilleure.Translation { + using PTC; + class EmitterContext { private Dictionary _irLabels; @@ -79,42 +81,52 @@ namespace ARMeilleure.Translation return Add(Instruction.ByteSwap, Local(op1.Type), op1); } - public Operand Call(Delegate func, params Operand[] callArgs) + public Operand Call(MethodInfo info, params Operand[] callArgs) { - // Add the delegate to the cache to ensure it will not be garbage collected. - func = DelegateCache.GetOrAdd(func); + if (Ptc.State == PtcState.Disabled) + { + IntPtr funcPtr = Delegates.GetDelegateFuncPtr(info); - IntPtr ptr = Marshal.GetFunctionPointerForDelegate(func); + OperandType returnType = GetOperandType(info.ReturnType); - Symbols.Add((ulong)ptr.ToInt64(), func.Method.Name); + Symbols.Add((ulong)funcPtr.ToInt64(), info.Name); - OperandType returnType = GetOperandType(func.Method.ReturnType); + return Call(Const(funcPtr.ToInt64()), returnType, callArgs); + } + else + { + int index = Delegates.GetDelegateIndex(info); - return Call(Const(ptr.ToInt64()), returnType, callArgs); + IntPtr funcPtr = Delegates.GetDelegateFuncPtrByIndex(index); + + OperandType returnType = GetOperandType(info.ReturnType); + + Symbols.Add((ulong)funcPtr.ToInt64(), info.Name); + + return Call(Const(funcPtr.ToInt64(), true, index), returnType, callArgs); + } } - private static Dictionary _typeCodeToOperandTypeMap = - new Dictionary() - { - { TypeCode.Boolean, OperandType.I32 }, - { TypeCode.Byte, OperandType.I32 }, - { TypeCode.Char, OperandType.I32 }, - { TypeCode.Double, OperandType.FP64 }, - { TypeCode.Int16, OperandType.I32 }, - { TypeCode.Int32, OperandType.I32 }, - { TypeCode.Int64, OperandType.I64 }, - { TypeCode.SByte, OperandType.I32 }, - { TypeCode.Single, OperandType.FP32 }, - { TypeCode.UInt16, OperandType.I32 }, - { TypeCode.UInt32, OperandType.I32 }, - { TypeCode.UInt64, OperandType.I64 } - }; - private static OperandType GetOperandType(Type type) { - if (_typeCodeToOperandTypeMap.TryGetValue(Type.GetTypeCode(type), out OperandType ot)) + if (type == typeof(bool) || type == typeof(byte) || + type == typeof(char) || type == typeof(short) || + type == typeof(int) || type == typeof(sbyte) || + type == typeof(ushort) || type == typeof(uint)) { - return ot; + return OperandType.I32; + } + else if (type == typeof(long) || type == typeof(ulong)) + { + return OperandType.I64; + } + else if (type == typeof(double)) + { + return OperandType.FP64; + } + else if (type == typeof(float)) + { + return OperandType.FP32; } else if (type == typeof(V128)) { @@ -124,8 +136,10 @@ namespace ARMeilleure.Translation { return OperandType.None; } - - throw new ArgumentException($"Invalid type \"{type.Name}\"."); + else + { + throw new ArgumentException($"Invalid type \"{type.Name}\"."); + } } public Operand Call(Operand address, OperandType returnType, params Operand[] callArgs) @@ -615,4 +629,4 @@ namespace ARMeilleure.Translation return new ControlFlowGraph(_irBlocks.First, _irBlocks); } } -} \ No newline at end of file +} diff --git a/ARMeilleure/Translation/JitCache.cs b/ARMeilleure/Translation/JitCache.cs index 32d40c20d..a828b4edf 100644 --- a/ARMeilleure/Translation/JitCache.cs +++ b/ARMeilleure/Translation/JitCache.cs @@ -12,11 +12,12 @@ namespace ARMeilleure.Translation private const int PageSize = 4 * 1024; private const int PageMask = PageSize - 1; - private const int CodeAlignment = 4; // Bytes + private const int CodeAlignment = 4; // Bytes. private const int CacheSize = 2047 * 1024 * 1024; private static ReservedRegion _jitRegion; private static int _offset; + private static readonly List _cacheEntries = new List(); private static readonly object _lock = new object(); @@ -25,19 +26,23 @@ namespace ARMeilleure.Translation public static void Initialize(IJitMemoryAllocator allocator) { if (_initialized) return; + lock (_lock) { if (_initialized) return; + _jitRegion = new ReservedRegion(allocator, CacheSize); if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - _jitRegion.ExpandIfNeeded(PageSize); + _jitRegion.ExpandIfNeeded((ulong)PageSize); + JitUnwindWindows.InstallFunctionTableHandler(_jitRegion.Pointer, CacheSize); // The first page is used for the table based SEH structs. _offset = PageSize; } + _initialized = true; } } @@ -97,13 +102,13 @@ namespace ARMeilleure.Translation _offset += codeSize; - _jitRegion.ExpandIfNeeded((ulong)_offset); - - if ((ulong)(uint)_offset > CacheSize) + if (_offset > CacheSize) { - throw new OutOfMemoryException(); + throw new OutOfMemoryException("JIT Cache exhausted."); } + _jitRegion.ExpandIfNeeded((ulong)_offset); + return allocOffset; } diff --git a/ARMeilleure/Translation/JitUnwindWindows.cs b/ARMeilleure/Translation/JitUnwindWindows.cs index 3f5b3282f..e118d1294 100644 --- a/ARMeilleure/Translation/JitUnwindWindows.cs +++ b/ARMeilleure/Translation/JitUnwindWindows.cs @@ -27,7 +27,7 @@ namespace ARMeilleure.Translation public unsafe fixed ushort UnwindCodes[MaxUnwindCodesArraySize]; } - private enum UnwindOperation + private enum UnwindOp { PushNonvol = 0, AllocLarge = 1, @@ -117,12 +117,12 @@ namespace ARMeilleure.Translation if (stackOffset <= 0xFFFF0) { - _unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOperation.SaveXmm128, entry.PrologOffset, entry.RegIndex); + _unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.SaveXmm128, entry.PrologOffset, entry.RegIndex); _unwindInfo->UnwindCodes[codeIndex++] = (ushort)(stackOffset / 16); } else { - _unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOperation.SaveXmm128Far, entry.PrologOffset, entry.RegIndex); + _unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.SaveXmm128Far, entry.PrologOffset, entry.RegIndex); _unwindInfo->UnwindCodes[codeIndex++] = (ushort)(stackOffset >> 0); _unwindInfo->UnwindCodes[codeIndex++] = (ushort)(stackOffset >> 16); } @@ -138,16 +138,16 @@ namespace ARMeilleure.Translation if (allocSize <= 128) { - _unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOperation.AllocSmall, entry.PrologOffset, (allocSize / 8) - 1); + _unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.AllocSmall, entry.PrologOffset, (allocSize / 8) - 1); } else if (allocSize <= 0x7FFF8) { - _unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOperation.AllocLarge, entry.PrologOffset, 0); + _unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.AllocLarge, entry.PrologOffset, 0); _unwindInfo->UnwindCodes[codeIndex++] = (ushort)(allocSize / 8); } else { - _unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOperation.AllocLarge, entry.PrologOffset, 1); + _unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.AllocLarge, entry.PrologOffset, 1); _unwindInfo->UnwindCodes[codeIndex++] = (ushort)(allocSize >> 0); _unwindInfo->UnwindCodes[codeIndex++] = (ushort)(allocSize >> 16); } @@ -157,7 +157,7 @@ namespace ARMeilleure.Translation case UnwindPseudoOp.PushReg: { - _unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOperation.PushNonvol, entry.PrologOffset, entry.RegIndex); + _unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.PushNonvol, entry.PrologOffset, entry.RegIndex); break; } @@ -180,7 +180,7 @@ namespace ARMeilleure.Translation return _runtimeFunction; } - private static ushort PackUnwindOp(UnwindOperation op, int prologOffset, int opInfo) + private static ushort PackUnwindOp(UnwindOp op, int prologOffset, int opInfo) { return (ushort)(prologOffset | ((int)op << 8) | (opInfo << 12)); } diff --git a/ARMeilleure/Translation/JumpTable.cs b/ARMeilleure/Translation/JumpTable.cs index 40ea0fcee..fe7a6ec3b 100644 --- a/ARMeilleure/Translation/JumpTable.cs +++ b/ARMeilleure/Translation/JumpTable.cs @@ -3,11 +3,14 @@ using ARMeilleure.Memory; using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Diagnostics; using System.Runtime.InteropServices; using System.Threading; namespace ARMeilleure.Translation { + using PTC; + class JumpTable { // The jump table is a block of (guestAddress, hostAddress) function mappings. @@ -15,10 +18,9 @@ namespace ARMeilleure.Translation // reserved specifically for each call. // The _dependants dictionary can be used to update the hostAddress for any functions that change. - public const int JumpTableStride = 16; // 8 byte guest address, 8 byte host address + public const int JumpTableStride = 16; // 8 byte guest address, 8 byte host address. private const int JumpTableSize = 1048576; - private const int JumpTableByteSize = JumpTableSize * JumpTableStride; // The dynamic table is also a block of (guestAddress, hostAddress) function mappings. @@ -32,74 +34,125 @@ namespace ARMeilleure.Translation // If it is 0, NativeInterface is called to find the rejited address of the call. // If none is found, the hostAddress entry stays at 0. Otherwise, the new address is placed in the entry. - // If the table size is exhausted and we didn't find our desired address, we fall back to requesting + // If the table size is exhausted and we didn't find our desired address, we fall back to requesting // the function from the JIT. - private const int DynamicTableSize = 1048576; - public const int DynamicTableElems = 1; public const int DynamicTableStride = DynamicTableElems * JumpTableStride; - private const int DynamicTableByteSize = DynamicTableSize * JumpTableStride * DynamicTableElems; + private const int DynamicTableSize = 1048576; + private const int DynamicTableByteSize = DynamicTableSize * DynamicTableStride; - private int _tableEnd = 0; + private readonly ReservedRegion _jumpRegion; + private readonly ReservedRegion _dynamicRegion; + + private int _tableEnd = 0; private int _dynTableEnd = 0; - private ConcurrentDictionary _targets; - private ConcurrentDictionary> _dependants; // TODO: Attach to TranslatedFunction or a wrapper class. - - private ReservedRegion _jumpRegion; - private ReservedRegion _dynamicRegion; - public IntPtr JumpPointer => _jumpRegion.Pointer; + public IntPtr JumpPointer => _jumpRegion.Pointer; public IntPtr DynamicPointer => _dynamicRegion.Pointer; + public int TableEnd => _tableEnd; + public int DynTableEnd => _dynTableEnd; + + public ConcurrentDictionary Targets { get; } + public ConcurrentDictionary> Dependants { get; } // TODO: Attach to TranslatedFunction or a wrapper class. + public JumpTable(IJitMemoryAllocator allocator) { - _jumpRegion = new ReservedRegion(allocator, JumpTableByteSize); + _jumpRegion = new ReservedRegion(allocator, JumpTableByteSize); _dynamicRegion = new ReservedRegion(allocator, DynamicTableByteSize); - _targets = new ConcurrentDictionary(); - _dependants = new ConcurrentDictionary>(); + Targets = new ConcurrentDictionary(); + Dependants = new ConcurrentDictionary>(); Symbols.Add((ulong)_jumpRegion.Pointer.ToInt64(), JumpTableByteSize, JumpTableStride, "JMP_TABLE"); Symbols.Add((ulong)_dynamicRegion.Pointer.ToInt64(), DynamicTableByteSize, DynamicTableStride, "DYN_TABLE"); } + public void Initialize(PtcJumpTable ptcJumpTable, ConcurrentDictionary funcs) + { + _tableEnd = ptcJumpTable.TableEnd; + _dynTableEnd = ptcJumpTable.DynTableEnd; + + foreach (ulong guestAddress in ptcJumpTable.Targets) + { + if (funcs.TryGetValue(guestAddress, out TranslatedFunction func)) + { + Targets.TryAdd(guestAddress, func); + } + else + { + throw new KeyNotFoundException($"({nameof(guestAddress)} = 0x{guestAddress:X16})"); + } + } + + foreach (var item in ptcJumpTable.Dependants) + { + Dependants.TryAdd(item.Key, new LinkedList(item.Value)); + } + } + public void RegisterFunction(ulong address, TranslatedFunction func) { address &= ~3UL; - _targets.AddOrUpdate(address, func, (key, oldFunc) => func); - long funcPtr = func.GetPointer().ToInt64(); + Targets.AddOrUpdate(address, func, (key, oldFunc) => func); + long funcPtr = func.FuncPtr.ToInt64(); // Update all jump table entries that target this address. - if (_dependants.TryGetValue(address, out LinkedList myDependants)) + if (Dependants.TryGetValue(address, out LinkedList myDependants)) { lock (myDependants) { - foreach (var entry in myDependants) + foreach (int entry in myDependants) { - IntPtr addr = _jumpRegion.Pointer + entry * JumpTableStride; + IntPtr addr = GetEntryAddressJumpTable(entry); + Marshal.WriteInt64(addr, 8, funcPtr); } } } } + public int ReserveTableEntry(long ownerAddress, long address, bool isJump) + { + int entry = Interlocked.Increment(ref _tableEnd); + + ExpandIfNeededJumpTable(entry); + + // Is the address we have already registered? If so, put the function address in the jump table. + // If not, it will point to the direct call stub. + long value = DirectCallStubs.DirectCallStub(isJump).ToInt64(); + if (Targets.TryGetValue((ulong)address, out TranslatedFunction func)) + { + value = func.FuncPtr.ToInt64(); + } + + // Make sure changes to the function at the target address update this jump table entry. + LinkedList targetDependants = Dependants.GetOrAdd((ulong)address, (addr) => new LinkedList()); + lock (targetDependants) + { + targetDependants.AddLast(entry); + } + + IntPtr addr = GetEntryAddressJumpTable(entry); + + Marshal.WriteInt64(addr, 0, address); + Marshal.WriteInt64(addr, 8, value); + + return entry; + } + public int ReserveDynamicEntry(bool isJump) { int entry = Interlocked.Increment(ref _dynTableEnd); - if (entry >= DynamicTableSize) - { - throw new OutOfMemoryException("JIT Dynamic Jump Table exhausted."); - } - _dynamicRegion.ExpandIfNeeded((ulong)((entry + 1) * DynamicTableStride)); + ExpandIfNeededDynamicTable(entry); // Initialize all host function pointers to the indirect call stub. - - IntPtr addr = _dynamicRegion.Pointer + entry * DynamicTableStride; - long stubPtr = (long)DirectCallStubs.IndirectCallStub(isJump); + IntPtr addr = GetEntryAddressDynamicTable(entry); + long stubPtr = DirectCallStubs.IndirectCallStub(isJump).ToInt64(); for (int i = 0; i < DynamicTableElems; i++) { @@ -109,37 +162,46 @@ namespace ARMeilleure.Translation return entry; } - public int ReserveTableEntry(long ownerAddress, long address, bool isJump) + public void ExpandIfNeededJumpTable(int entries) { - int entry = Interlocked.Increment(ref _tableEnd); - if (entry >= JumpTableSize) + Debug.Assert(entries > 0); + + if (entries < JumpTableSize) + { + _jumpRegion.ExpandIfNeeded((ulong)((entries + 1) * JumpTableStride)); + } + else { throw new OutOfMemoryException("JIT Direct Jump Table exhausted."); } + } - _jumpRegion.ExpandIfNeeded((ulong)((entry + 1) * JumpTableStride)); + public void ExpandIfNeededDynamicTable(int entries) + { + Debug.Assert(entries > 0); - // Is the address we have already registered? If so, put the function address in the jump table. - // If not, it will point to the direct call stub. - long value = (long)DirectCallStubs.DirectCallStub(isJump); - if (_targets.TryGetValue((ulong)address, out TranslatedFunction func)) + if (entries < DynamicTableSize) { - value = func.GetPointer().ToInt64(); + _dynamicRegion.ExpandIfNeeded((ulong)((entries + 1) * DynamicTableStride)); } - - // Make sure changes to the function at the target address update this jump table entry. - LinkedList targetDependants = _dependants.GetOrAdd((ulong)address, (addr) => new LinkedList()); - lock (targetDependants) + else { - targetDependants.AddLast(entry); + throw new OutOfMemoryException("JIT Dynamic Jump Table exhausted."); } + } - IntPtr addr = _jumpRegion.Pointer + entry * JumpTableStride; + public IntPtr GetEntryAddressJumpTable(int entry) + { + Debug.Assert(entry >= 1 && entry <= _tableEnd); - Marshal.WriteInt64(addr, 0, address); - Marshal.WriteInt64(addr, 8, value); + return _jumpRegion.Pointer + entry * JumpTableStride; + } - return entry; + public IntPtr GetEntryAddressDynamicTable(int entry) + { + Debug.Assert(entry >= 1 && entry <= _dynTableEnd); + + return _dynamicRegion.Pointer + entry * DynamicTableStride; } } } diff --git a/ARMeilleure/Translation/PTC/EncodingCache.cs b/ARMeilleure/Translation/PTC/EncodingCache.cs new file mode 100644 index 000000000..b87e0d7a3 --- /dev/null +++ b/ARMeilleure/Translation/PTC/EncodingCache.cs @@ -0,0 +1,9 @@ +using System.Text; + +namespace ARMeilleure.Translation.PTC +{ + internal static class EncodingCache + { + internal static readonly Encoding UTF8NoBOM = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true); + } +} \ No newline at end of file diff --git a/ARMeilleure/Translation/PTC/Ptc.cs b/ARMeilleure/Translation/PTC/Ptc.cs new file mode 100644 index 000000000..76d3d7e1b --- /dev/null +++ b/ARMeilleure/Translation/PTC/Ptc.cs @@ -0,0 +1,768 @@ +using ARMeilleure.CodeGen; +using ARMeilleure.CodeGen.Unwinding; +using ARMeilleure.Memory; +using Ryujinx.Common.Logging; +using System; +using System.Buffers.Binary; +using System.Collections.Concurrent; +using System.Diagnostics; +using System.IO; +using System.IO.Compression; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics.X86; +using System.Runtime.Serialization.Formatters.Binary; +using System.Threading; +using System.Threading.Tasks; + +namespace ARMeilleure.Translation.PTC +{ + public static class Ptc + { + private const string HeaderMagic = "PTChd"; + + private const int InternalVersion = 0; //! To be incremented manually for each change to the ARMeilleure project. + + private const string BaseDir = "Ryujinx"; + + private const string ActualDir = "0"; + private const string BackupDir = "1"; + + private const string TitleIdTextDefault = "0000000000000000"; + private const string DisplayVersionDefault = "0"; + + internal const int PageTablePointerIndex = -1; // Must be a negative value. + internal const int JumpPointerIndex = -2; // Must be a negative value. + internal const int DynamicPointerIndex = -3; // Must be a negative value. + + private const CompressionLevel SaveCompressionLevel = CompressionLevel.Fastest; + + private static readonly MemoryStream _infosStream; + private static readonly MemoryStream _codesStream; + private static readonly MemoryStream _relocsStream; + private static readonly MemoryStream _unwindInfosStream; + + private static readonly BinaryWriter _infosWriter; + + private static readonly BinaryFormatter _binaryFormatter; + + private static readonly ManualResetEvent _waitEvent; + + private static readonly AutoResetEvent _loggerEvent; + + private static readonly string _basePath; + + private static readonly object _lock; + + private static bool _disposed; + + private static volatile int _translateCount; + private static volatile int _rejitCount; + + internal static PtcJumpTable PtcJumpTable { get; private set; } + + internal static string TitleIdText { get; private set; } + internal static string DisplayVersion { get; private set; } + + internal static string CachePathActual { get; private set; } + internal static string CachePathBackup { get; private set; } + + internal static PtcState State { get; private set; } + + static Ptc() + { + _infosStream = new MemoryStream(); + _codesStream = new MemoryStream(); + _relocsStream = new MemoryStream(); + _unwindInfosStream = new MemoryStream(); + + _infosWriter = new BinaryWriter(_infosStream, EncodingCache.UTF8NoBOM, true); + + _binaryFormatter = new BinaryFormatter(); + + _waitEvent = new ManualResetEvent(true); + + _loggerEvent = new AutoResetEvent(false); + + _basePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), BaseDir); + + _lock = new object(); + + _disposed = false; + + PtcJumpTable = new PtcJumpTable(); + + TitleIdText = TitleIdTextDefault; + DisplayVersion = DisplayVersionDefault; + + CachePathActual = string.Empty; + CachePathBackup = string.Empty; + + Disable(); + + AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; + AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit; + } + + public static void Initialize(string titleIdText, string displayVersion, bool enabled) + { + Wait(); + ClearMemoryStreams(); + PtcJumpTable.Clear(); + + PtcProfiler.Stop(); + PtcProfiler.Wait(); + PtcProfiler.ClearEntries(); + + if (String.IsNullOrEmpty(titleIdText) || titleIdText == TitleIdTextDefault) + { + TitleIdText = TitleIdTextDefault; + DisplayVersion = DisplayVersionDefault; + + CachePathActual = string.Empty; + CachePathBackup = string.Empty; + + Disable(); + + return; + } + + Logger.PrintInfo(LogClass.Ptc, $"Initializing Profiled Persistent Translation Cache (enabled: {enabled})."); + + TitleIdText = titleIdText; + DisplayVersion = !String.IsNullOrEmpty(displayVersion) ? displayVersion : DisplayVersionDefault; + + if (enabled) + { + string workPathActual = Path.Combine(_basePath, "games", TitleIdText, "cache", "cpu", ActualDir); + string workPathBackup = Path.Combine(_basePath, "games", TitleIdText, "cache", "cpu", BackupDir); + + if (!Directory.Exists(workPathActual)) + { + Directory.CreateDirectory(workPathActual); + } + + if (!Directory.Exists(workPathBackup)) + { + Directory.CreateDirectory(workPathBackup); + } + + CachePathActual = Path.Combine(workPathActual, DisplayVersion); + CachePathBackup = Path.Combine(workPathBackup, DisplayVersion); + + Enable(); + + PreLoad(); + PtcProfiler.PreLoad(); + } + else + { + CachePathActual = string.Empty; + CachePathBackup = string.Empty; + + Disable(); + } + } + + internal static void ClearMemoryStreams() + { + _infosStream.SetLength(0L); + _codesStream.SetLength(0L); + _relocsStream.SetLength(0L); + _unwindInfosStream.SetLength(0L); + } + + private static void PreLoad() + { + string fileNameActual = String.Concat(CachePathActual, ".cache"); + string fileNameBackup = String.Concat(CachePathBackup, ".cache"); + + FileInfo fileInfoActual = new FileInfo(fileNameActual); + FileInfo fileInfoBackup = new FileInfo(fileNameBackup); + + if (fileInfoActual.Exists && fileInfoActual.Length != 0L) + { + if (!Load(fileNameActual)) + { + if (fileInfoBackup.Exists && fileInfoBackup.Length != 0L) + { + Load(fileNameBackup); + } + } + } + else if (fileInfoBackup.Exists && fileInfoBackup.Length != 0L) + { + Load(fileNameBackup); + } + } + + private static bool Load(string fileName) + { + using (FileStream compressedStream = new FileStream(fileName, FileMode.Open)) + using (DeflateStream deflateStream = new DeflateStream(compressedStream, CompressionMode.Decompress, true)) + using (MemoryStream stream = new MemoryStream()) + using (System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create()) + { + int hashSize = md5.HashSize / 8; + + deflateStream.CopyTo(stream); + + stream.Seek(0L, SeekOrigin.Begin); + + byte[] currentHash = new byte[hashSize]; + stream.Read(currentHash, 0, hashSize); + + byte[] expectedHash = md5.ComputeHash(stream); + + if (!CompareHash(currentHash, expectedHash)) + { + InvalidateCompressedStream(compressedStream); + + return false; + } + + stream.Seek((long)hashSize, SeekOrigin.Begin); + + Header header = ReadHeader(stream); + + if (header.Magic != HeaderMagic) + { + InvalidateCompressedStream(compressedStream); + + return false; + } + + if (header.CacheFileVersion != InternalVersion) + { + InvalidateCompressedStream(compressedStream); + + return false; + } + + if (header.FeatureInfo != GetFeatureInfo()) + { + InvalidateCompressedStream(compressedStream); + + return false; + } + + if (header.InfosLen % InfoEntry.Stride != 0) + { + InvalidateCompressedStream(compressedStream); + + return false; + } + + byte[] infosBuf = new byte[header.InfosLen]; + byte[] codesBuf = new byte[header.CodesLen]; + byte[] relocsBuf = new byte[header.RelocsLen]; + byte[] unwindInfosBuf = new byte[header.UnwindInfosLen]; + + stream.Read(infosBuf, 0, header.InfosLen); + stream.Read(codesBuf, 0, header.CodesLen); + stream.Read(relocsBuf, 0, header.RelocsLen); + stream.Read(unwindInfosBuf, 0, header.UnwindInfosLen); + + try + { + PtcJumpTable = (PtcJumpTable)_binaryFormatter.Deserialize(stream); + } + catch + { + PtcJumpTable = new PtcJumpTable(); + + InvalidateCompressedStream(compressedStream); + + return false; + } + + _infosStream.Write(infosBuf, 0, header.InfosLen); + _codesStream.Write(codesBuf, 0, header.CodesLen); + _relocsStream.Write(relocsBuf, 0, header.RelocsLen); + _unwindInfosStream.Write(unwindInfosBuf, 0, header.UnwindInfosLen); + + return true; + } + } + + private static bool CompareHash(ReadOnlySpan currentHash, ReadOnlySpan expectedHash) + { + return currentHash.SequenceEqual(expectedHash); + } + + private static Header ReadHeader(MemoryStream stream) + { + using (BinaryReader headerReader = new BinaryReader(stream, EncodingCache.UTF8NoBOM, true)) + { + Header header = new Header(); + + header.Magic = headerReader.ReadString(); + + header.CacheFileVersion = headerReader.ReadInt32(); + header.FeatureInfo = headerReader.ReadUInt64(); + + header.InfosLen = headerReader.ReadInt32(); + header.CodesLen = headerReader.ReadInt32(); + header.RelocsLen = headerReader.ReadInt32(); + header.UnwindInfosLen = headerReader.ReadInt32(); + + return header; + } + } + + private static void InvalidateCompressedStream(FileStream compressedStream) + { + compressedStream.SetLength(0L); + } + + private static void PreSave(object state) + { + _waitEvent.Reset(); + + string fileNameActual = String.Concat(CachePathActual, ".cache"); + string fileNameBackup = String.Concat(CachePathBackup, ".cache"); + + FileInfo fileInfoActual = new FileInfo(fileNameActual); + + if (fileInfoActual.Exists && fileInfoActual.Length != 0L) + { + File.Copy(fileNameActual, fileNameBackup, true); + } + + Save(fileNameActual); + + _waitEvent.Set(); + } + + private static void Save(string fileName) + { + using (MemoryStream stream = new MemoryStream()) + using (System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create()) + { + int hashSize = md5.HashSize / 8; + + stream.Seek((long)hashSize, SeekOrigin.Begin); + + WriteHeader(stream); + + _infosStream.WriteTo(stream); + _codesStream.WriteTo(stream); + _relocsStream.WriteTo(stream); + _unwindInfosStream.WriteTo(stream); + + _binaryFormatter.Serialize(stream, PtcJumpTable); + + stream.Seek((long)hashSize, SeekOrigin.Begin); + byte[] hash = md5.ComputeHash(stream); + + stream.Seek(0L, SeekOrigin.Begin); + stream.Write(hash, 0, hashSize); + + using (FileStream compressedStream = new FileStream(fileName, FileMode.OpenOrCreate)) + using (DeflateStream deflateStream = new DeflateStream(compressedStream, SaveCompressionLevel, true)) + { + try + { + stream.WriteTo(deflateStream); + } + catch + { + compressedStream.Position = 0L; + } + + if (compressedStream.Position < compressedStream.Length) + { + compressedStream.SetLength(compressedStream.Position); + } + } + } + } + + private static void WriteHeader(MemoryStream stream) + { + using (BinaryWriter headerWriter = new BinaryWriter(stream, EncodingCache.UTF8NoBOM, true)) + { + headerWriter.Write((string)HeaderMagic); // Header.Magic + + headerWriter.Write((int)InternalVersion); // Header.CacheFileVersion + headerWriter.Write((ulong)GetFeatureInfo()); // Header.FeatureInfo + + headerWriter.Write((int)_infosStream.Length); // Header.InfosLen + headerWriter.Write((int)_codesStream.Length); // Header.CodesLen + headerWriter.Write((int)_relocsStream.Length); // Header.RelocsLen + headerWriter.Write((int)_unwindInfosStream.Length); // Header.UnwindInfosLen + } + } + + internal static void LoadTranslations(ConcurrentDictionary funcs, IntPtr pageTablePointer, JumpTable jumpTable) + { + if ((int)_infosStream.Length == 0 || + (int)_codesStream.Length == 0 || + (int)_relocsStream.Length == 0 || + (int)_unwindInfosStream.Length == 0) + { + return; + } + + Debug.Assert(funcs.Count == 0); + + _infosStream.Seek(0L, SeekOrigin.Begin); + _codesStream.Seek(0L, SeekOrigin.Begin); + _relocsStream.Seek(0L, SeekOrigin.Begin); + _unwindInfosStream.Seek(0L, SeekOrigin.Begin); + + using (BinaryReader infosReader = new BinaryReader(_infosStream, EncodingCache.UTF8NoBOM, true)) + using (BinaryReader codesReader = new BinaryReader(_codesStream, EncodingCache.UTF8NoBOM, true)) + using (BinaryReader relocsReader = new BinaryReader(_relocsStream, EncodingCache.UTF8NoBOM, true)) + using (BinaryReader unwindInfosReader = new BinaryReader(_unwindInfosStream, EncodingCache.UTF8NoBOM, true)) + { + int infosEntriesCount = (int)_infosStream.Length / InfoEntry.Stride; + + for (int i = 0; i < infosEntriesCount; i++) + { + InfoEntry infoEntry = ReadInfo(infosReader); + + byte[] code = ReadCode(codesReader, infoEntry.CodeLen); + + if (infoEntry.RelocEntriesCount != 0) + { + RelocEntry[] relocEntries = GetRelocEntries(relocsReader, infoEntry.RelocEntriesCount); + + PatchCode(code, relocEntries, pageTablePointer, jumpTable); + } + + UnwindInfo unwindInfo = ReadUnwindInfo(unwindInfosReader); + + TranslatedFunction func = FastTranslate(code, unwindInfo, infoEntry.HighCq); + + funcs.AddOrUpdate((ulong)infoEntry.Address, func, (key, oldFunc) => func.HighCq && !oldFunc.HighCq ? func : oldFunc); + } + } + + if (_infosStream.Position < _infosStream.Length || + _codesStream.Position < _codesStream.Length || + _relocsStream.Position < _relocsStream.Length || + _unwindInfosStream.Position < _unwindInfosStream.Length) + { + throw new Exception("Could not reach the end of one or more memory streams."); + } + + jumpTable.Initialize(PtcJumpTable, funcs); + + PtcJumpTable.WriteJumpTable(jumpTable, funcs); + PtcJumpTable.WriteDynamicTable(jumpTable); + } + + private static InfoEntry ReadInfo(BinaryReader infosReader) + { + InfoEntry infoEntry = new InfoEntry(); + + infoEntry.Address = infosReader.ReadInt64(); + infoEntry.HighCq = infosReader.ReadBoolean(); + infoEntry.CodeLen = infosReader.ReadInt32(); + infoEntry.RelocEntriesCount = infosReader.ReadInt32(); + + return infoEntry; + } + + private static byte[] ReadCode(BinaryReader codesReader, int codeLen) + { + byte[] codeBuf = new byte[codeLen]; + + codesReader.Read(codeBuf, 0, codeLen); + + return codeBuf; + } + + private static RelocEntry[] GetRelocEntries(BinaryReader relocsReader, int relocEntriesCount) + { + RelocEntry[] relocEntries = new RelocEntry[relocEntriesCount]; + + for (int i = 0; i < relocEntriesCount; i++) + { + int position = relocsReader.ReadInt32(); + int index = relocsReader.ReadInt32(); + + relocEntries[i] = new RelocEntry(position, index); + } + + return relocEntries; + } + + private static void PatchCode(Span code, RelocEntry[] relocEntries, IntPtr pageTablePointer, JumpTable jumpTable) + { + foreach (RelocEntry relocEntry in relocEntries) + { + ulong imm; + + if (relocEntry.Index == PageTablePointerIndex) + { + imm = (ulong)pageTablePointer.ToInt64(); + } + else if (relocEntry.Index == JumpPointerIndex) + { + imm = (ulong)jumpTable.JumpPointer.ToInt64(); + } + else if (relocEntry.Index == DynamicPointerIndex) + { + imm = (ulong)jumpTable.DynamicPointer.ToInt64(); + } + else if (Delegates.TryGetDelegateFuncPtrByIndex(relocEntry.Index, out IntPtr funcPtr)) + { + imm = (ulong)funcPtr.ToInt64(); + } + else + { + throw new Exception($"Unexpected reloc entry {relocEntry}."); + } + + BinaryPrimitives.WriteUInt64LittleEndian(code.Slice(relocEntry.Position, 8), imm); + } + } + + private static UnwindInfo ReadUnwindInfo(BinaryReader unwindInfosReader) + { + int pushEntriesLength = unwindInfosReader.ReadInt32(); + + UnwindPushEntry[] pushEntries = new UnwindPushEntry[pushEntriesLength]; + + for (int i = 0; i < pushEntriesLength; i++) + { + int pseudoOp = unwindInfosReader.ReadInt32(); + int prologOffset = unwindInfosReader.ReadInt32(); + int regIndex = unwindInfosReader.ReadInt32(); + int stackOffsetOrAllocSize = unwindInfosReader.ReadInt32(); + + pushEntries[i] = new UnwindPushEntry((UnwindPseudoOp)pseudoOp, prologOffset, regIndex, stackOffsetOrAllocSize); + } + + int prologueSize = unwindInfosReader.ReadInt32(); + + return new UnwindInfo(pushEntries, prologueSize); + } + + private static TranslatedFunction FastTranslate(byte[] code, UnwindInfo unwindInfo, bool highCq) + { + CompiledFunction cFunc = new CompiledFunction(code, unwindInfo); + + IntPtr codePtr = JitCache.Map(cFunc); + + GuestFunction gFunc = Marshal.GetDelegateForFunctionPointer(codePtr); + + TranslatedFunction tFunc = new TranslatedFunction(gFunc, highCq); + + return tFunc; + } + + internal static void MakeAndSaveTranslations(ConcurrentDictionary funcs, IMemoryManager memory, JumpTable jumpTable) + { + if (PtcProfiler.ProfiledFuncs.Count == 0) + { + return; + } + + _translateCount = 0; + _rejitCount = 0; + + ThreadPool.QueueUserWorkItem(TranslationLogger, (funcs.Count, PtcProfiler.ProfiledFuncs.Count)); + + int maxDegreeOfParallelism = (Environment.ProcessorCount * 3) / 4; + + Parallel.ForEach(PtcProfiler.ProfiledFuncs, new ParallelOptions { MaxDegreeOfParallelism = maxDegreeOfParallelism }, (item, state) => + { + ulong address = item.Key; + + Debug.Assert(PtcProfiler.IsAddressInStaticCodeRange(address)); + + if (!funcs.ContainsKey(address)) + { + TranslatedFunction func = Translator.Translate(memory, jumpTable, address, item.Value.mode, item.Value.highCq); + + funcs.TryAdd(address, func); + + if (func.HighCq) + { + jumpTable.RegisterFunction(address, func); + } + + Interlocked.Increment(ref _translateCount); + } + else if (item.Value.highCq && !funcs[address].HighCq) + { + TranslatedFunction func = Translator.Translate(memory, jumpTable, address, item.Value.mode, highCq: true); + + funcs[address] = func; + + jumpTable.RegisterFunction(address, func); + + Interlocked.Increment(ref _rejitCount); + } + + if (State != PtcState.Enabled) + { + state.Stop(); + } + }); + + _loggerEvent.Set(); + + if (_translateCount != 0 || _rejitCount != 0) + { + PtcJumpTable.Initialize(jumpTable); + + PtcJumpTable.ReadJumpTable(jumpTable); + PtcJumpTable.ReadDynamicTable(jumpTable); + + ThreadPool.QueueUserWorkItem(PreSave); + } + } + + private static void TranslationLogger(object state) + { + const int refreshRate = 1; // Seconds. + + (int funcsCount, int ProfiledFuncsCount) = ((int, int))state; + + do + { + Logger.PrintInfo(LogClass.Ptc, $"{funcsCount + _translateCount} of {ProfiledFuncsCount} functions to translate - {_rejitCount} functions rejited"); + } + while (!_loggerEvent.WaitOne(refreshRate * 1000)); + + Logger.PrintInfo(LogClass.Ptc, $"{funcsCount + _translateCount} of {ProfiledFuncsCount} functions to translate - {_rejitCount} functions rejited"); + } + + internal static void WriteInfoCodeReloc(long address, bool highCq, PtcInfo ptcInfo) + { + lock (_lock) + { + // WriteInfo. + _infosWriter.Write((long)address); // InfoEntry.Address + _infosWriter.Write((bool)highCq); // InfoEntry.HighCq + _infosWriter.Write((int)ptcInfo.CodeStream.Length); // InfoEntry.CodeLen + _infosWriter.Write((int)ptcInfo.RelocEntriesCount); // InfoEntry.RelocEntriesCount + + // WriteCode. + ptcInfo.CodeStream.WriteTo(_codesStream); + + // WriteReloc. + ptcInfo.RelocStream.WriteTo(_relocsStream); + + // WriteUnwindInfo. + ptcInfo.UnwindInfoStream.WriteTo(_unwindInfosStream); + } + } + + private static ulong GetFeatureInfo() + { + ulong featureInfo = 0ul; + + featureInfo |= (Sse3.IsSupported ? 1ul : 0ul) << 0; + featureInfo |= (Pclmulqdq.IsSupported ? 1ul : 0ul) << 1; + featureInfo |= (Ssse3.IsSupported ? 1ul : 0ul) << 9; + featureInfo |= (Fma.IsSupported ? 1ul : 0ul) << 12; + featureInfo |= (Sse41.IsSupported ? 1ul : 0ul) << 19; + featureInfo |= (Sse42.IsSupported ? 1ul : 0ul) << 20; + featureInfo |= (Popcnt.IsSupported ? 1ul : 0ul) << 23; + featureInfo |= (Aes.IsSupported ? 1ul : 0ul) << 25; + featureInfo |= (Avx.IsSupported ? 1ul : 0ul) << 28; + featureInfo |= (Sse.IsSupported ? 1ul : 0ul) << 57; + featureInfo |= (Sse2.IsSupported ? 1ul : 0ul) << 58; + + return featureInfo; + } + + private struct Header + { + public string Magic; + + public int CacheFileVersion; + public ulong FeatureInfo; + + public int InfosLen; + public int CodesLen; + public int RelocsLen; + public int UnwindInfosLen; + } + + private struct InfoEntry + { + public const int Stride = 17; // Bytes. + + public long Address; + public bool HighCq; + public int CodeLen; + public int RelocEntriesCount; + } + + private static void Enable() + { + State = PtcState.Enabled; + } + + public static void Continue() + { + if (State == PtcState.Enabled) + { + State = PtcState.Continuing; + } + } + + public static void Close() + { + if (State == PtcState.Enabled || + State == PtcState.Continuing) + { + State = PtcState.Closing; + } + } + + internal static void Disable() + { + State = PtcState.Disabled; + } + + private static void Wait() + { + _waitEvent.WaitOne(); + } + + public static void Dispose() + { + if (!_disposed) + { + _disposed = true; + + AppDomain.CurrentDomain.UnhandledException -= CurrentDomain_UnhandledException; + AppDomain.CurrentDomain.ProcessExit -= CurrentDomain_ProcessExit; + + Wait(); + _waitEvent.Dispose(); + + _infosWriter.Dispose(); + + _infosStream.Dispose(); + _codesStream.Dispose(); + _relocsStream.Dispose(); + _unwindInfosStream.Dispose(); + } + } + + private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) + { + Close(); + PtcProfiler.Stop(); + + if (e.IsTerminating) + { + Dispose(); + PtcProfiler.Dispose(); + } + } + + private static void CurrentDomain_ProcessExit(object sender, EventArgs e) + { + Dispose(); + PtcProfiler.Dispose(); + } + } +} \ No newline at end of file diff --git a/ARMeilleure/Translation/PTC/PtcInfo.cs b/ARMeilleure/Translation/PTC/PtcInfo.cs new file mode 100644 index 000000000..f03eb6ba4 --- /dev/null +++ b/ARMeilleure/Translation/PTC/PtcInfo.cs @@ -0,0 +1,68 @@ +using ARMeilleure.CodeGen.Unwinding; +using System; +using System.IO; + +namespace ARMeilleure.Translation.PTC +{ + sealed class PtcInfo : IDisposable + { + private readonly BinaryWriter _relocWriter; + private readonly BinaryWriter _unwindInfoWriter; + + public MemoryStream CodeStream { get; } + public MemoryStream RelocStream { get; } + public MemoryStream UnwindInfoStream { get; } + + public int RelocEntriesCount { get; private set; } + + public PtcInfo() + { + CodeStream = new MemoryStream(); + RelocStream = new MemoryStream(); + UnwindInfoStream = new MemoryStream(); + + _relocWriter = new BinaryWriter(RelocStream, EncodingCache.UTF8NoBOM, true); + _unwindInfoWriter = new BinaryWriter(UnwindInfoStream, EncodingCache.UTF8NoBOM, true); + + RelocEntriesCount = 0; + } + + public void WriteCode(MemoryStream codeStream) + { + codeStream.WriteTo(CodeStream); + } + + public void WriteRelocEntry(RelocEntry relocEntry) + { + _relocWriter.Write((int)relocEntry.Position); + _relocWriter.Write((int)relocEntry.Index); + + RelocEntriesCount++; + } + + public void WriteUnwindInfo(UnwindInfo unwindInfo) + { + _unwindInfoWriter.Write((int)unwindInfo.PushEntries.Length); + + foreach (UnwindPushEntry unwindPushEntry in unwindInfo.PushEntries) + { + _unwindInfoWriter.Write((int)unwindPushEntry.PseudoOp); + _unwindInfoWriter.Write((int)unwindPushEntry.PrologOffset); + _unwindInfoWriter.Write((int)unwindPushEntry.RegIndex); + _unwindInfoWriter.Write((int)unwindPushEntry.StackOffsetOrAllocSize); + } + + _unwindInfoWriter.Write((int)unwindInfo.PrologSize); + } + + public void Dispose() + { + _relocWriter.Dispose(); + _unwindInfoWriter.Dispose(); + + CodeStream.Dispose(); + RelocStream.Dispose(); + UnwindInfoStream.Dispose(); + } + } +} diff --git a/ARMeilleure/Translation/PTC/PtcJumpTable.cs b/ARMeilleure/Translation/PTC/PtcJumpTable.cs new file mode 100644 index 000000000..0a3ae2403 --- /dev/null +++ b/ARMeilleure/Translation/PTC/PtcJumpTable.cs @@ -0,0 +1,222 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace ARMeilleure.Translation.PTC +{ + [Serializable] + class PtcJumpTable + { + private readonly List> _jumpTable; + private readonly List> _dynamicTable; + + private readonly List _targets; + private readonly Dictionary> _dependants; + + public int TableEnd => _jumpTable.Count; + public int DynTableEnd => _dynamicTable.Count; + + public List Targets => _targets; + public Dictionary> Dependants => _dependants; + + public PtcJumpTable() + { + _jumpTable = new List>(); + _dynamicTable = new List>(); + + _targets = new List(); + _dependants = new Dictionary>(); + } + + public void Initialize(JumpTable jumpTable) + { + _targets.Clear(); + + foreach (ulong guestAddress in jumpTable.Targets.Keys) + { + _targets.Add(guestAddress); + } + + _dependants.Clear(); + + foreach (var item in jumpTable.Dependants) + { + _dependants.Add(item.Key, new LinkedList(item.Value)); + } + } + + public void Clear() + { + _jumpTable.Clear(); + _dynamicTable.Clear(); + + _targets.Clear(); + _dependants.Clear(); + } + + public void WriteJumpTable(JumpTable jumpTable, ConcurrentDictionary funcs) + { + jumpTable.ExpandIfNeededJumpTable(TableEnd); + + int entry = 0; + + foreach (var item in _jumpTable) + { + entry += 1; + + long guestAddress = item.Key; + DirectHostAddress directHostAddress = item.Value; + + long hostAddress; + + if (directHostAddress == DirectHostAddress.CallStub) + { + hostAddress = DirectCallStubs.DirectCallStub(false).ToInt64(); + } + else if (directHostAddress == DirectHostAddress.TailCallStub) + { + hostAddress = DirectCallStubs.DirectCallStub(true).ToInt64(); + } + else if (directHostAddress == DirectHostAddress.Host) + { + if (funcs.TryGetValue((ulong)guestAddress, out TranslatedFunction func)) + { + hostAddress = func.FuncPtr.ToInt64(); + } + else + { + throw new KeyNotFoundException($"({nameof(guestAddress)} = 0x{(ulong)guestAddress:X16})"); + } + } + else + { + throw new InvalidOperationException(nameof(directHostAddress)); + } + + IntPtr addr = jumpTable.GetEntryAddressJumpTable(entry); + + Marshal.WriteInt64(addr, 0, guestAddress); + Marshal.WriteInt64(addr, 8, hostAddress); + } + } + + public void WriteDynamicTable(JumpTable jumpTable) + { + if (JumpTable.DynamicTableElems > 1) + { + throw new NotSupportedException(); + } + + jumpTable.ExpandIfNeededDynamicTable(DynTableEnd); + + int entry = 0; + + foreach (var item in _dynamicTable) + { + entry += 1; + + long guestAddress = item.Key; + IndirectHostAddress indirectHostAddress = item.Value; + + long hostAddress; + + if (indirectHostAddress == IndirectHostAddress.CallStub) + { + hostAddress = DirectCallStubs.IndirectCallStub(false).ToInt64(); + } + else if (indirectHostAddress == IndirectHostAddress.TailCallStub) + { + hostAddress = DirectCallStubs.IndirectCallStub(true).ToInt64(); + } + else + { + throw new InvalidOperationException(nameof(indirectHostAddress)); + } + + IntPtr addr = jumpTable.GetEntryAddressDynamicTable(entry); + + Marshal.WriteInt64(addr, 0, guestAddress); + Marshal.WriteInt64(addr, 8, hostAddress); + } + } + + public void ReadJumpTable(JumpTable jumpTable) + { + _jumpTable.Clear(); + + for (int entry = 1; entry <= jumpTable.TableEnd; entry++) + { + IntPtr addr = jumpTable.GetEntryAddressJumpTable(entry); + + long guestAddress = Marshal.ReadInt64(addr, 0); + long hostAddress = Marshal.ReadInt64(addr, 8); + + DirectHostAddress directHostAddress; + + if (hostAddress == DirectCallStubs.DirectCallStub(false).ToInt64()) + { + directHostAddress = DirectHostAddress.CallStub; + } + else if (hostAddress == DirectCallStubs.DirectCallStub(true).ToInt64()) + { + directHostAddress = DirectHostAddress.TailCallStub; + } + else + { + directHostAddress = DirectHostAddress.Host; + } + + _jumpTable.Add(new KeyValuePair(guestAddress, directHostAddress)); + } + } + + public void ReadDynamicTable(JumpTable jumpTable) + { + if (JumpTable.DynamicTableElems > 1) + { + throw new NotSupportedException(); + } + + _dynamicTable.Clear(); + + for (int entry = 1; entry <= jumpTable.DynTableEnd; entry++) + { + IntPtr addr = jumpTable.GetEntryAddressDynamicTable(entry); + + long guestAddress = Marshal.ReadInt64(addr, 0); + long hostAddress = Marshal.ReadInt64(addr, 8); + + IndirectHostAddress indirectHostAddress; + + if (hostAddress == DirectCallStubs.IndirectCallStub(false).ToInt64()) + { + indirectHostAddress = IndirectHostAddress.CallStub; + } + else if (hostAddress == DirectCallStubs.IndirectCallStub(true).ToInt64()) + { + indirectHostAddress = IndirectHostAddress.TailCallStub; + } + else + { + throw new InvalidOperationException($"({nameof(hostAddress)} = 0x{hostAddress:X16})"); + } + + _dynamicTable.Add(new KeyValuePair(guestAddress, indirectHostAddress)); + } + } + + private enum DirectHostAddress + { + CallStub, + TailCallStub, + Host + } + + private enum IndirectHostAddress + { + CallStub, + TailCallStub + } + } +} \ No newline at end of file diff --git a/ARMeilleure/Translation/PTC/PtcProfiler.cs b/ARMeilleure/Translation/PTC/PtcProfiler.cs new file mode 100644 index 000000000..dcc312750 --- /dev/null +++ b/ARMeilleure/Translation/PTC/PtcProfiler.cs @@ -0,0 +1,267 @@ +using ARMeilleure.State; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.IO.Compression; +using System.Runtime.Serialization.Formatters.Binary; +using System.Security.Cryptography; +using System.Threading; + +namespace ARMeilleure.Translation.PTC +{ + public static class PtcProfiler + { + private const int SaveInterval = 30; // Seconds. + + private const CompressionLevel SaveCompressionLevel = CompressionLevel.Fastest; + + private static readonly BinaryFormatter _binaryFormatter; + + private static readonly System.Timers.Timer _timer; + + private static readonly ManualResetEvent _waitEvent; + + private static readonly object _lock; + + private static bool _disposed; + + internal static Dictionary ProfiledFuncs { get; private set; } //! Not to be modified. + + internal static bool Enabled { get; private set; } + + public static ulong StaticCodeStart { internal get; set; } + public static int StaticCodeSize { internal get; set; } + + static PtcProfiler() + { + _binaryFormatter = new BinaryFormatter(); + + _timer = new System.Timers.Timer((double)SaveInterval * 1000d); + _timer.Elapsed += PreSave; + + _waitEvent = new ManualResetEvent(true); + + _lock = new object(); + + _disposed = false; + + ProfiledFuncs = new Dictionary(); + + Enabled = false; + } + + internal static void AddEntry(ulong address, ExecutionMode mode, bool highCq) + { + if (IsAddressInStaticCodeRange(address)) + { + lock (_lock) + { + Debug.Assert(!highCq && !ProfiledFuncs.ContainsKey(address)); + + ProfiledFuncs.TryAdd(address, (mode, highCq)); + } + } + } + + internal static void UpdateEntry(ulong address, ExecutionMode mode, bool highCq) + { + if (IsAddressInStaticCodeRange(address)) + { + lock (_lock) + { + Debug.Assert(highCq && ProfiledFuncs.ContainsKey(address)); + + ProfiledFuncs[address] = (mode, highCq); + } + } + } + + internal static bool IsAddressInStaticCodeRange(ulong address) + { + return address >= StaticCodeStart && address < StaticCodeStart + (ulong)StaticCodeSize; + } + + internal static void ClearEntries() + { + ProfiledFuncs.Clear(); + } + + internal static void PreLoad() + { + string fileNameActual = String.Concat(Ptc.CachePathActual, ".info"); + string fileNameBackup = String.Concat(Ptc.CachePathBackup, ".info"); + + FileInfo fileInfoActual = new FileInfo(fileNameActual); + FileInfo fileInfoBackup = new FileInfo(fileNameBackup); + + if (fileInfoActual.Exists && fileInfoActual.Length != 0L) + { + if (!Load(fileNameActual)) + { + if (fileInfoBackup.Exists && fileInfoBackup.Length != 0L) + { + Load(fileNameBackup); + } + } + } + else if (fileInfoBackup.Exists && fileInfoBackup.Length != 0L) + { + Load(fileNameBackup); + } + } + + private static bool Load(string fileName) + { + using (FileStream compressedStream = new FileStream(fileName, FileMode.Open)) + using (DeflateStream deflateStream = new DeflateStream(compressedStream, CompressionMode.Decompress, true)) + using (MemoryStream stream = new MemoryStream()) + using (MD5 md5 = MD5.Create()) + { + int hashSize = md5.HashSize / 8; + + deflateStream.CopyTo(stream); + + stream.Seek(0L, SeekOrigin.Begin); + + byte[] currentHash = new byte[hashSize]; + stream.Read(currentHash, 0, hashSize); + + byte[] expectedHash = md5.ComputeHash(stream); + + if (!CompareHash(currentHash, expectedHash)) + { + InvalidateCompressedStream(compressedStream); + + return false; + } + + stream.Seek((long)hashSize, SeekOrigin.Begin); + + try + { + ProfiledFuncs = (Dictionary)_binaryFormatter.Deserialize(stream); + } + catch + { + ProfiledFuncs = new Dictionary(); + + InvalidateCompressedStream(compressedStream); + + return false; + } + + return true; + } + } + + private static bool CompareHash(ReadOnlySpan currentHash, ReadOnlySpan expectedHash) + { + return currentHash.SequenceEqual(expectedHash); + } + + private static void InvalidateCompressedStream(FileStream compressedStream) + { + compressedStream.SetLength(0L); + } + + private static void PreSave(object source, System.Timers.ElapsedEventArgs e) + { + _waitEvent.Reset(); + + string fileNameActual = String.Concat(Ptc.CachePathActual, ".info"); + string fileNameBackup = String.Concat(Ptc.CachePathBackup, ".info"); + + FileInfo fileInfoActual = new FileInfo(fileNameActual); + + if (fileInfoActual.Exists && fileInfoActual.Length != 0L) + { + File.Copy(fileNameActual, fileNameBackup, true); + } + + Save(fileNameActual); + + _waitEvent.Set(); + } + + private static void Save(string fileName) + { + using (MemoryStream stream = new MemoryStream()) + using (MD5 md5 = MD5.Create()) + { + int hashSize = md5.HashSize / 8; + + stream.Seek((long)hashSize, SeekOrigin.Begin); + + lock (_lock) + { + _binaryFormatter.Serialize(stream, ProfiledFuncs); + } + + stream.Seek((long)hashSize, SeekOrigin.Begin); + byte[] hash = md5.ComputeHash(stream); + + stream.Seek(0L, SeekOrigin.Begin); + stream.Write(hash, 0, hashSize); + + using (FileStream compressedStream = new FileStream(fileName, FileMode.OpenOrCreate)) + using (DeflateStream deflateStream = new DeflateStream(compressedStream, SaveCompressionLevel, true)) + { + try + { + stream.WriteTo(deflateStream); + } + catch + { + compressedStream.Position = 0L; + } + + if (compressedStream.Position < compressedStream.Length) + { + compressedStream.SetLength(compressedStream.Position); + } + } + } + } + + internal static void Start() + { + if (Ptc.State == PtcState.Enabled || + Ptc.State == PtcState.Continuing) + { + Enabled = true; + + _timer.Enabled = true; + } + } + + public static void Stop() + { + Enabled = false; + + if (!_disposed) + { + _timer.Enabled = false; + } + } + + internal static void Wait() + { + _waitEvent.WaitOne(); + } + + public static void Dispose() + { + if (!_disposed) + { + _disposed = true; + + _timer.Elapsed -= PreSave; + _timer.Dispose(); + + Wait(); + _waitEvent.Dispose(); + } + } + } +} \ No newline at end of file diff --git a/ARMeilleure/Translation/PTC/PtcState.cs b/ARMeilleure/Translation/PTC/PtcState.cs new file mode 100644 index 000000000..ca4f41080 --- /dev/null +++ b/ARMeilleure/Translation/PTC/PtcState.cs @@ -0,0 +1,10 @@ +namespace ARMeilleure.Translation.PTC +{ + enum PtcState + { + Enabled, + Continuing, + Closing, + Disabled + } +} \ No newline at end of file diff --git a/ARMeilleure/Translation/PTC/RelocEntry.cs b/ARMeilleure/Translation/PTC/RelocEntry.cs new file mode 100644 index 000000000..3d729fbb0 --- /dev/null +++ b/ARMeilleure/Translation/PTC/RelocEntry.cs @@ -0,0 +1,19 @@ +namespace ARMeilleure.Translation.PTC +{ + struct RelocEntry + { + public int Position; + public int Index; + + public RelocEntry(int position, int index) + { + Position = position; + Index = index; + } + + public override string ToString() + { + return $"({nameof(Position)} = {Position}, {nameof(Index)} = {Index})"; + } + } +} diff --git a/ARMeilleure/Translation/PriorityQueue.cs b/ARMeilleure/Translation/PriorityQueue.cs deleted file mode 100644 index 000a5009f..000000000 --- a/ARMeilleure/Translation/PriorityQueue.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.Collections.Concurrent; - -namespace ARMeilleure.Translation -{ - class PriorityQueue - { - private ConcurrentStack[] _queues; - - public PriorityQueue(int priorities) - { - _queues = new ConcurrentStack[priorities]; - - for (int index = 0; index < priorities; index++) - { - _queues[index] = new ConcurrentStack(); - } - } - - public void Enqueue(int priority, T value) - { - _queues[priority].Push(value); - } - - public bool TryDequeue(out T value) - { - for (int index = 0; index < _queues.Length; index++) - { - if (_queues[index].TryPop(out value)) - { - return true; - } - } - - value = default(T); - - return false; - } - } -} \ No newline at end of file diff --git a/ARMeilleure/Translation/RejitRequest.cs b/ARMeilleure/Translation/RejitRequest.cs index e0b0e0b92..1bed5c0ae 100644 --- a/ARMeilleure/Translation/RejitRequest.cs +++ b/ARMeilleure/Translation/RejitRequest.cs @@ -1,4 +1,4 @@ -using ARMeilleure.State; +using ARMeilleure.State; namespace ARMeilleure.Translation { diff --git a/ARMeilleure/Translation/TranslatedFunction.cs b/ARMeilleure/Translation/TranslatedFunction.cs index f1dc6deeb..36fae50ab 100644 --- a/ARMeilleure/Translation/TranslatedFunction.cs +++ b/ARMeilleure/Translation/TranslatedFunction.cs @@ -4,22 +4,23 @@ using System.Threading; namespace ARMeilleure.Translation { - class TranslatedFunction + sealed class TranslatedFunction { private const int MinCallsForRejit = 100; - private GuestFunction _func; - private IntPtr _funcPtr; + private readonly GuestFunction _func; // Ensure that this delegate will not be garbage collected. - private bool _rejit; - private int _callCount; + private int _callCount = 0; - public bool HighCq => !_rejit; + public bool HighCq { get; } + public IntPtr FuncPtr { get; } - public TranslatedFunction(GuestFunction func, bool rejit) + public TranslatedFunction(GuestFunction func, bool highCq) { - _func = func; - _rejit = rejit; + _func = func; + + HighCq = highCq; + FuncPtr = Marshal.GetFunctionPointerForDelegate(func); } public ulong Execute(State.ExecutionContext context) @@ -29,17 +30,7 @@ namespace ARMeilleure.Translation public bool ShouldRejit() { - return _rejit && Interlocked.Increment(ref _callCount) == MinCallsForRejit; - } - - public IntPtr GetPointer() - { - if (_funcPtr == IntPtr.Zero) - { - _funcPtr = Marshal.GetFunctionPointerForDelegate(_func); - } - - return _funcPtr; + return !HighCq && Interlocked.Increment(ref _callCount) == MinCallsForRejit; } } } \ No newline at end of file diff --git a/ARMeilleure/Translation/Translator.cs b/ARMeilleure/Translation/Translator.cs index 700b54c23..1c2ead4fa 100644 --- a/ARMeilleure/Translation/Translator.cs +++ b/ARMeilleure/Translation/Translator.cs @@ -13,22 +13,22 @@ using static ARMeilleure.IntermediateRepresentation.OperationHelper; namespace ARMeilleure.Translation { + using PTC; + public class Translator { private const ulong CallFlag = InstEmitFlowHelper.CallFlag; - private const bool AlwaysTranslateFunctions = true; // If false, only translates a single block for lowCq. - private readonly IMemoryManager _memory; private readonly ConcurrentDictionary _funcs; - private readonly JumpTable _jumpTable; - - private readonly PriorityQueue _backgroundQueue; + private readonly ConcurrentStack _backgroundStack; private readonly AutoResetEvent _backgroundTranslatorEvent; + private readonly JumpTable _jumpTable; + private volatile int _threadCount; public Translator(IJitMemoryAllocator allocator, IMemoryManager memory) @@ -37,32 +37,45 @@ namespace ARMeilleure.Translation _funcs = new ConcurrentDictionary(); - _jumpTable = new JumpTable(allocator); - - _backgroundQueue = new PriorityQueue(2); + _backgroundStack = new ConcurrentStack(); _backgroundTranslatorEvent = new AutoResetEvent(false); + _jumpTable = new JumpTable(allocator); + JitCache.Initialize(allocator); + DirectCallStubs.InitializeStubs(); + + if (Ptc.State == PtcState.Enabled) + { + Ptc.LoadTranslations(_funcs, memory.PageTablePointer, _jumpTable); + } } - private void TranslateQueuedSubs() + private void TranslateStackedSubs() { while (_threadCount != 0) { - if (_backgroundQueue.TryDequeue(out RejitRequest request)) + if (_backgroundStack.TryPop(out RejitRequest request)) { - TranslatedFunction func = Translate(request.Address, request.Mode, highCq: true); + TranslatedFunction func = Translate(_memory, _jumpTable, request.Address, request.Mode, highCq: true); _funcs.AddOrUpdate(request.Address, func, (key, oldFunc) => func); + _jumpTable.RegisterFunction(request.Address, func); + + if (PtcProfiler.Enabled) + { + PtcProfiler.UpdateEntry(request.Address, request.Mode, highCq: true); + } } else { _backgroundTranslatorEvent.WaitOne(); } } + _backgroundTranslatorEvent.Set(); // Wake up any other background translator threads, to encourage them to exit. } @@ -70,16 +83,27 @@ namespace ARMeilleure.Translation { if (Interlocked.Increment(ref _threadCount) == 1) { + if (Ptc.State == PtcState.Enabled) + { + Ptc.MakeAndSaveTranslations(_funcs, _memory, _jumpTable); + } + + PtcProfiler.Start(); + + Ptc.Disable(); + // Simple heuristic, should be user configurable in future. (1 for 4 core/ht or less, 2 for 6 core+ht etc). // All threads are normal priority except from the last, which just fills as much of the last core as the os lets it with a low priority. // If we only have one rejit thread, it should be normal priority as highCq code is performance critical. // TODO: Use physical cores rather than logical. This only really makes sense for processors with hyperthreading. Requires OS specific code. int unboundedThreadCount = Math.Max(1, (Environment.ProcessorCount - 6) / 3); - int threadCount = Math.Min(4, unboundedThreadCount); + int threadCount = Math.Min(4, unboundedThreadCount); + for (int i = 0; i < threadCount; i++) { bool last = i != 0 && i == unboundedThreadCount - 1; - Thread backgroundTranslatorThread = new Thread(TranslateQueuedSubs) + + Thread backgroundTranslatorThread = new Thread(TranslateStackedSubs) { Name = "CPU.BackgroundTranslatorThread." + i, Priority = last ? ThreadPriority.Lowest : ThreadPriority.Normal @@ -130,13 +154,19 @@ namespace ARMeilleure.Translation if (!_funcs.TryGetValue(address, out TranslatedFunction func)) { - func = Translate(address, mode, highCq: false); + func = Translate(_memory, _jumpTable, address, mode, highCq: false); _funcs.TryAdd(address, func); + + if (PtcProfiler.Enabled) + { + PtcProfiler.AddEntry(address, mode, highCq: false); + } } - else if (isCallTarget && func.ShouldRejit()) + + if (isCallTarget && func.ShouldRejit()) { - _backgroundQueue.Enqueue(0, new RejitRequest(address, mode)); + _backgroundStack.Push(new RejitRequest(address, mode)); _backgroundTranslatorEvent.Set(); } @@ -144,18 +174,16 @@ namespace ARMeilleure.Translation return func; } - private TranslatedFunction Translate(ulong address, ExecutionMode mode, bool highCq) + internal static TranslatedFunction Translate(IMemoryManager memory, JumpTable jumpTable, ulong address, ExecutionMode mode, bool highCq) { - ArmEmitterContext context = new ArmEmitterContext(_memory, _jumpTable, (long)address, highCq, Aarch32Mode.User); + ArmEmitterContext context = new ArmEmitterContext(memory, jumpTable, (long)address, highCq, Aarch32Mode.User); PrepareOperandPool(highCq); PrepareOperationPool(highCq); Logger.StartPass(PassName.Decoding); - Block[] blocks = AlwaysTranslateFunctions - ? Decoder.DecodeFunction (_memory, address, mode, highCq) - : Decoder.DecodeBasicBlock(_memory, address, mode); + Block[] blocks = Decoder.DecodeFunction(memory, address, mode, highCq); Logger.EndPass(PassName.Decoding); @@ -182,12 +210,26 @@ namespace ARMeilleure.Translation CompilerOptions options = highCq ? CompilerOptions.HighCq : CompilerOptions.None; - GuestFunction func = Compiler.Compile(cfg, argTypes, OperandType.I64, options); + GuestFunction func; + + if (Ptc.State == PtcState.Disabled) + { + func = Compiler.Compile(cfg, argTypes, OperandType.I64, options); + } + else + { + using (PtcInfo ptcInfo = new PtcInfo()) + { + func = Compiler.Compile(cfg, argTypes, OperandType.I64, options, ptcInfo); + + Ptc.WriteInfoCodeReloc((long)address, highCq, ptcInfo); + } + } ResetOperandPool(highCq); ResetOperationPool(highCq); - return new TranslatedFunction(func, rejit: !highCq); + return new TranslatedFunction(func, highCq); } private static ControlFlowGraph EmitAndGetCFG(ArmEmitterContext context, Block[] blocks) @@ -264,7 +306,7 @@ namespace ARMeilleure.Translation context.BranchIfTrue(lblNonZero, count); - Operand running = context.Call(new _Bool(NativeInterface.CheckSynchronization)); + Operand running = context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.CheckSynchronization))); context.BranchIfTrue(lblExit, running); @@ -281,4 +323,4 @@ namespace ARMeilleure.Translation context.MarkLabel(lblExit); } } -} \ No newline at end of file +} diff --git a/CONFIG.md b/CONFIG.md index 9b60c6162..14a51359f 100644 --- a/CONFIG.md +++ b/CONFIG.md @@ -44,7 +44,11 @@ - `enable_multicore_scheduling` *(bool)* - Enable or Disable Multi-core scheduling of threads + Enable or disable multi-core scheduling of threads + +- `enable_ptc` *(bool)* + + Enable or disable profiled translation cache persistency - `enable_fs_integrity_checks` *(bool)* diff --git a/Ryujinx.Common/Configuration/ConfigurationFileFormat.cs b/Ryujinx.Common/Configuration/ConfigurationFileFormat.cs index db542ad7b..e2431ac62 100644 --- a/Ryujinx.Common/Configuration/ConfigurationFileFormat.cs +++ b/Ryujinx.Common/Configuration/ConfigurationFileFormat.cs @@ -13,7 +13,7 @@ namespace Ryujinx.Configuration /// /// The current version of the file format /// - public const int CurrentVersion = 7; + public const int CurrentVersion = 8; public int Version { get; set; } @@ -112,6 +112,11 @@ namespace Ryujinx.Configuration /// public bool EnableMulticoreScheduling { get; set; } + /// + /// Enables or disables profiled translation cache persistency + /// + public bool EnablePtc { get; set; } + /// /// Enables integrity checks on Game content files /// diff --git a/Ryujinx.Common/Configuration/ConfigurationState.cs b/Ryujinx.Common/Configuration/ConfigurationState.cs index d0a00cb51..26bbabc7b 100644 --- a/Ryujinx.Common/Configuration/ConfigurationState.cs +++ b/Ryujinx.Common/Configuration/ConfigurationState.cs @@ -1,4 +1,4 @@ -using Ryujinx.Common; +using Ryujinx.Common; using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.Logging; using Ryujinx.Configuration.Hid; @@ -172,6 +172,11 @@ namespace Ryujinx.Configuration /// public ReactiveObject EnableMulticoreScheduling { get; private set; } + /// + /// Enables or disables profiled translation cache persistency + /// + public ReactiveObject EnablePtc { get; private set; } + /// /// Enables integrity checks on Game content files /// @@ -195,6 +200,7 @@ namespace Ryujinx.Configuration SystemTimeOffset = new ReactiveObject(); EnableDockedMode = new ReactiveObject(); EnableMulticoreScheduling = new ReactiveObject(); + EnablePtc = new ReactiveObject(); EnableFsIntegrityChecks = new ReactiveObject(); FsGlobalAccessLogMode = new ReactiveObject(); IgnoreMissingServices = new ReactiveObject(); @@ -337,6 +343,7 @@ namespace Ryujinx.Configuration EnableDiscordIntegration = EnableDiscordIntegration, EnableVsync = Graphics.EnableVsync, EnableMulticoreScheduling = System.EnableMulticoreScheduling, + EnablePtc = System.EnablePtc, EnableFsIntegrityChecks = System.EnableFsIntegrityChecks, FsGlobalAccessLogMode = System.FsGlobalAccessLogMode, IgnoreMissingServices = System.IgnoreMissingServices, @@ -385,6 +392,7 @@ namespace Ryujinx.Configuration EnableDiscordIntegration.Value = true; Graphics.EnableVsync.Value = true; System.EnableMulticoreScheduling.Value = true; + System.EnablePtc.Value = false; System.EnableFsIntegrityChecks.Value = true; System.FsGlobalAccessLogMode.Value = 0; System.IgnoreMissingServices.Value = false; @@ -570,6 +578,15 @@ namespace Ryujinx.Configuration } } + if (configurationFileFormat.Version < 8) + { + Common.Logging.Logger.PrintWarning(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 8."); + + configurationFileFormat.EnablePtc = false; + + configurationFileUpdated = true; + } + List inputConfig = new List(); foreach (ControllerConfig controllerConfig in configurationFileFormat.ControllerConfig) { @@ -600,6 +617,7 @@ namespace Ryujinx.Configuration EnableDiscordIntegration.Value = configurationFileFormat.EnableDiscordIntegration; Graphics.EnableVsync.Value = configurationFileFormat.EnableVsync; System.EnableMulticoreScheduling.Value = configurationFileFormat.EnableMulticoreScheduling; + System.EnablePtc.Value = configurationFileFormat.EnablePtc; System.EnableFsIntegrityChecks.Value = configurationFileFormat.EnableFsIntegrityChecks; System.FsGlobalAccessLogMode.Value = configurationFileFormat.FsGlobalAccessLogMode; System.IgnoreMissingServices.Value = configurationFileFormat.IgnoreMissingServices; diff --git a/Ryujinx.Common/Logging/LogClass.cs b/Ryujinx.Common/Logging/LogClass.cs index dcf380a17..2120c9cb0 100644 --- a/Ryujinx.Common/Logging/LogClass.cs +++ b/Ryujinx.Common/Logging/LogClass.cs @@ -14,6 +14,7 @@ namespace Ryujinx.Common.Logging KernelScheduler, KernelSvc, Loader, + Ptc, Service, ServiceAcc, ServiceAm, diff --git a/Ryujinx.Graphics.Gpu/Image/TexturePool.cs b/Ryujinx.Graphics.Gpu/Image/TexturePool.cs index 3286a5ceb..639fa69da 100644 --- a/Ryujinx.Graphics.Gpu/Image/TexturePool.cs +++ b/Ryujinx.Graphics.Gpu/Image/TexturePool.cs @@ -149,7 +149,10 @@ namespace Ryujinx.Graphics.Gpu.Image if (!FormatTable.TryGetTextureFormat(format, srgb, out FormatInfo formatInfo)) { - Logger.PrintError(LogClass.Gpu, $"Invalid texture format 0x{format:X} (sRGB: {srgb})."); + if ((long)address > 0L && (int)format > 0) + { + Logger.PrintError(LogClass.Gpu, $"Invalid texture format 0x{format:X} (sRGB: {srgb})."); + } formatInfo = FormatInfo.Default; } diff --git a/Ryujinx.HLE/HOS/ApplicationLoader.cs b/Ryujinx.HLE/HOS/ApplicationLoader.cs index c8bb37c51..c44c40b55 100644 --- a/Ryujinx.HLE/HOS/ApplicationLoader.cs +++ b/Ryujinx.HLE/HOS/ApplicationLoader.cs @@ -1,3 +1,4 @@ +using ARMeilleure.Translation.PTC; using LibHac; using LibHac.Account; using LibHac.Common; @@ -6,7 +7,6 @@ using LibHac.FsSystem; using LibHac.FsSystem.NcaUtils; using LibHac.Ncm; using LibHac.Ns; -using LibHac.Spl; using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; using Ryujinx.HLE.FileSystem; @@ -31,17 +31,19 @@ namespace Ryujinx.HLE.HOS private readonly ContentManager _contentManager; private readonly VirtualFileSystem _fileSystem; - public IntegrityCheckLevel FsIntegrityCheckLevel => _device.System.FsIntegrityCheckLevel; + public BlitStruct ControlData { get; set; } + + public string TitleName { get; private set; } + public string DisplayVersion { get; private set; } public ulong TitleId { get; private set; } public string TitleIdText => TitleId.ToString("x16"); - public string TitleName { get; private set; } - - public string TitleVersionString { get; private set; } public bool TitleIs64Bit { get; private set; } - public BlitStruct ControlData { get; set; } + public bool EnablePtc => _device.System.EnablePtc; + + public IntegrityCheckLevel FsIntegrityCheckLevel => _device.System.FsIntegrityCheckLevel; public ApplicationLoader(Switch device, VirtualFileSystem fileSystem, ContentManager contentManager) { @@ -69,7 +71,7 @@ namespace Ryujinx.HLE.HOS } } - private (Nca Main, Nca Patch, Nca Control) GetGameData(PartitionFileSystem pfs) + private (Nca main, Nca patch, Nca control) GetGameData(PartitionFileSystem pfs) { Nca mainNca = null; Nca patchNca = null; @@ -284,11 +286,6 @@ namespace Ryujinx.HLE.HOS _fileSystem.SetRomFs(dataStorage.AsStream(FileAccess.Read)); } - LoadExeFs(codeFs, out Npdm metaData); - - TitleId = metaData.Aci0.TitleId; - TitleIs64Bit = metaData.Is64Bit; - if (controlNca != null) { ReadControlData(controlNca); @@ -298,12 +295,14 @@ namespace Ryujinx.HLE.HOS ControlData.ByteSpan.Clear(); } + LoadExeFs(codeFs, out _); + if (TitleId != 0) { EnsureSaveData(new TitleId(TitleId)); } - Logger.PrintInfo(LogClass.Loader, $"Application Loaded: {TitleName} v{TitleVersionString} [{TitleIdText}] [{(TitleIs64Bit ? "64-bit" : "32-bit")}]"); + Logger.PrintInfo(LogClass.Loader, $"Application Loaded: {TitleName} v{DisplayVersion} [{TitleIdText}] [{(TitleIs64Bit ? "64-bit" : "32-bit")}]"); } public void ReadControlData(Nca controlNca) @@ -327,7 +326,7 @@ namespace Ryujinx.HLE.HOS .FirstOrDefault(x => x.Name[0] != 0).Name.ToString(); } - TitleVersionString = ControlData.Value.DisplayVersion.ToString(); + DisplayVersion = ControlData.Value.DisplayVersion.ToString(); } } else @@ -382,6 +381,8 @@ namespace Ryujinx.HLE.HOS _contentManager.LoadEntries(_device); + Ptc.Initialize(TitleIdText, DisplayVersion, EnablePtc); + ProgramLoader.LoadNsos(_device.System.KernelContext, metaData, executables: nsos.ToArray()); } diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs index 34e16e79c..c1baae307 100644 --- a/Ryujinx.HLE/HOS/Horizon.cs +++ b/Ryujinx.HLE/HOS/Horizon.cs @@ -66,6 +66,7 @@ namespace Ryujinx.HLE.HOS #pragma warning restore CS0649 private bool _isDisposed; + public bool EnablePtc { get; set; } public IntegrityCheckLevel FsIntegrityCheckLevel { get; set; } @@ -297,4 +298,4 @@ namespace Ryujinx.HLE.HOS } } } -} \ No newline at end of file +} diff --git a/Ryujinx.HLE/HOS/ProgramLoader.cs b/Ryujinx.HLE/HOS/ProgramLoader.cs index 07b1a18b2..1158925ab 100644 --- a/Ryujinx.HLE/HOS/ProgramLoader.cs +++ b/Ryujinx.HLE/HOS/ProgramLoader.cs @@ -1,3 +1,4 @@ +using ARMeilleure.Translation.PTC; using Ryujinx.Common; using Ryujinx.Common.Logging; using Ryujinx.Cpu; @@ -10,7 +11,7 @@ using Ryujinx.HLE.Loaders.Npdm; namespace Ryujinx.HLE.HOS { - class ProgramLoader + static class ProgramLoader { private const bool AslrEnabled = true; @@ -169,6 +170,9 @@ namespace Ryujinx.HLE.HOS } } + PtcProfiler.StaticCodeStart = codeStart; + PtcProfiler.StaticCodeSize = codeSize; + int codePagesCount = codeSize / KMemoryManager.PageSize; int personalMmHeapPagesCount = metaData.PersonalMmHeapSize / KMemoryManager.PageSize; diff --git a/Ryujinx.HLE/Switch.cs b/Ryujinx.HLE/Switch.cs index d5c0b3b29..5713bd9ef 100644 --- a/Ryujinx.HLE/Switch.cs +++ b/Ryujinx.HLE/Switch.cs @@ -80,6 +80,8 @@ namespace Ryujinx.HLE System.EnableMultiCoreScheduling(); } + System.EnablePtc = ConfigurationState.Instance.System.EnablePtc; + System.FsIntegrityCheckLevel = GetIntegrityCheckLevel(); System.GlobalAccessLogMode = ConfigurationState.Instance.System.FsGlobalAccessLogMode; diff --git a/Ryujinx.Tests/Cpu/CpuTest.cs b/Ryujinx.Tests/Cpu/CpuTest.cs index b3f316f43..9e37c2114 100644 --- a/Ryujinx.Tests/Cpu/CpuTest.cs +++ b/Ryujinx.Tests/Cpu/CpuTest.cs @@ -1,11 +1,10 @@ using ARMeilleure.State; - using NUnit.Framework; using Ryujinx.Cpu; using Ryujinx.Memory; using Ryujinx.Tests.Unicorn; - using System; + using MemoryPermission = Ryujinx.Tests.Unicorn.MemoryPermission; namespace Ryujinx.Tests.Cpu @@ -69,6 +68,7 @@ namespace Ryujinx.Tests.Cpu _memory.Dispose(); _context.Dispose(); _ram.Dispose(); + _memory = null; _context = null; _cpuContext = null; diff --git a/Ryujinx.Tests/Cpu/CpuTest32.cs b/Ryujinx.Tests/Cpu/CpuTest32.cs index 9b8b89a1a..e43bf5976 100644 --- a/Ryujinx.Tests/Cpu/CpuTest32.cs +++ b/Ryujinx.Tests/Cpu/CpuTest32.cs @@ -1,11 +1,10 @@ using ARMeilleure.State; - using NUnit.Framework; using Ryujinx.Cpu; using Ryujinx.Memory; using Ryujinx.Tests.Unicorn; - using System; + using MemoryPermission = Ryujinx.Tests.Unicorn.MemoryPermission; namespace Ryujinx.Tests.Cpu @@ -45,7 +44,7 @@ namespace Ryujinx.Tests.Cpu public void Setup() { _currAddress = 0x1000; - _size = 0x1000; + _size = 0x1000; _entryPoint = _currAddress; @@ -73,8 +72,9 @@ namespace Ryujinx.Tests.Cpu _memory.Dispose(); _context.Dispose(); _ram.Dispose(); - _memory = null; - _context = null; + + _memory = null; + _context = null; _cpuContext = null; _unicornEmu = null; } diff --git a/Ryujinx/Config.json b/Ryujinx/Config.json index 2479d2d5e..e87d34b10 100644 --- a/Ryujinx/Config.json +++ b/Ryujinx/Config.json @@ -1,5 +1,5 @@ { - "version": 7, + "version": 8, "max_anisotropy": -1, "graphics_shaders_dump_path": "", "logging_enable_debug": false, @@ -19,6 +19,7 @@ "enable_discord_integration": true, "enable_vsync": true, "enable_multicore_scheduling": true, + "enable_ptc": false, "enable_fs_integrity_checks": true, "fs_global_access_log_mode": 0, "ignore_missing_services": false, diff --git a/Ryujinx/Program.cs b/Ryujinx/Program.cs index 08a9859da..cb8ab48ec 100644 --- a/Ryujinx/Program.cs +++ b/Ryujinx/Program.cs @@ -1,3 +1,4 @@ +using ARMeilleure.Translation.PTC; using Gtk; using Ryujinx.Common.Logging; using Ryujinx.Common.SystemInfo; @@ -110,10 +111,16 @@ namespace Ryujinx Logger.PrintError(LogClass.Application, $"Unhandled exception caught: {exception}"); + Ptc.Close(); + PtcProfiler.Stop(); + if (e.IsTerminating) { Logger.Shutdown(); + + Ptc.Dispose(); + PtcProfiler.Dispose(); } } } -} \ No newline at end of file +} diff --git a/Ryujinx/Ui/GLRenderer.cs b/Ryujinx/Ui/GLRenderer.cs index 3ac935f31..b3471259a 100644 --- a/Ryujinx/Ui/GLRenderer.cs +++ b/Ryujinx/Ui/GLRenderer.cs @@ -1,3 +1,4 @@ +using ARMeilleure.Translation.PTC; using Gdk; using OpenTK; using OpenTK.Graphics; @@ -183,8 +184,8 @@ namespace Ryujinx.Ui string titleNameSection = string.IsNullOrWhiteSpace(_device.Application.TitleName) ? string.Empty : $" - {_device.Application.TitleName}"; - string titleVersionSection = string.IsNullOrWhiteSpace(_device.Application.TitleVersionString) ? string.Empty - : $" v{_device.Application.TitleVersionString}"; + string titleVersionSection = string.IsNullOrWhiteSpace(_device.Application.DisplayVersion) ? string.Empty + : $" v{_device.Application.DisplayVersion}"; string titleIdSection = string.IsNullOrWhiteSpace(_device.Application.TitleIdText) ? string.Empty : $" ({_device.Application.TitleIdText.ToUpper()})"; @@ -378,7 +379,17 @@ namespace Ryujinx.Ui { Gtk.Application.Invoke(delegate { - HandleScreenState(OpenTK.Input.Keyboard.GetState()); + KeyboardState keyboard = OpenTK.Input.Keyboard.GetState(); + + HandleScreenState(keyboard); + + if (keyboard.IsKeyDown(OpenTK.Input.Key.Delete)) + { + if (!ParentWindow.State.HasFlag(Gdk.WindowState.Fullscreen)) + { + Ptc.Continue(); + } + } }); } diff --git a/Ryujinx/Ui/MainWindow.cs b/Ryujinx/Ui/MainWindow.cs index bd83d8597..342acc9f8 100644 --- a/Ryujinx/Ui/MainWindow.cs +++ b/Ryujinx/Ui/MainWindow.cs @@ -1,3 +1,4 @@ +using ARMeilleure.Translation.PTC; using Gtk; using LibHac.Common; using LibHac.Ns; @@ -470,6 +471,9 @@ namespace Ryujinx.Ui _glWidget.Start(); + Ptc.Close(); + PtcProfiler.Stop(); + device.Dispose(); _deviceExitStatus.Set(); @@ -597,6 +601,10 @@ namespace Ryujinx.Ui Profile.FinishProfiling(); DiscordIntegrationModule.Exit(); Logger.Shutdown(); + + Ptc.Dispose(); + PtcProfiler.Dispose(); + Application.Quit(); } diff --git a/Ryujinx/Ui/SettingsWindow.cs b/Ryujinx/Ui/SettingsWindow.cs index 1b24e72bf..42764a7da 100644 --- a/Ryujinx/Ui/SettingsWindow.cs +++ b/Ryujinx/Ui/SettingsWindow.cs @@ -35,6 +35,7 @@ namespace Ryujinx.Ui [GUI] CheckButton _discordToggle; [GUI] CheckButton _vSyncToggle; [GUI] CheckButton _multiSchedToggle; + [GUI] CheckButton _ptcToggle; [GUI] CheckButton _fsicToggle; [GUI] CheckButton _ignoreToggle; [GUI] CheckButton _directKeyboardAccess; @@ -152,6 +153,11 @@ namespace Ryujinx.Ui _multiSchedToggle.Click(); } + if (ConfigurationState.Instance.System.EnablePtc) + { + _ptcToggle.Click(); + } + if (ConfigurationState.Instance.System.EnableFsIntegrityChecks) { _fsicToggle.Click(); @@ -381,6 +387,7 @@ namespace Ryujinx.Ui ConfigurationState.Instance.EnableDiscordIntegration.Value = _discordToggle.Active; ConfigurationState.Instance.Graphics.EnableVsync.Value = _vSyncToggle.Active; ConfigurationState.Instance.System.EnableMulticoreScheduling.Value = _multiSchedToggle.Active; + ConfigurationState.Instance.System.EnablePtc.Value = _ptcToggle.Active; ConfigurationState.Instance.System.EnableFsIntegrityChecks.Value = _fsicToggle.Active; ConfigurationState.Instance.System.IgnoreMissingServices.Value = _ignoreToggle.Active; ConfigurationState.Instance.Hid.EnableKeyboard.Value = _directKeyboardAccess.Active; diff --git a/Ryujinx/Ui/SettingsWindow.glade b/Ryujinx/Ui/SettingsWindow.glade index ea662de09..a0eab22fe 100644 --- a/Ryujinx/Ui/SettingsWindow.glade +++ b/Ryujinx/Ui/SettingsWindow.glade @@ -1398,6 +1398,24 @@ 5 + + + Enable Profiled Persistent Translation Cache + True + True + False + Enables or disables profiled translation cache persistency + start + 5 + 5 + True + + + False + True + 6 + + Enable FS Integrity Checks @@ -1413,7 +1431,7 @@ False True - 6 + 7 diff --git a/Ryujinx/_schema.json b/Ryujinx/_schema.json index e89e2bf7d..c940d4c90 100644 --- a/Ryujinx/_schema.json +++ b/Ryujinx/_schema.json @@ -19,6 +19,7 @@ "docked_mode", "enable_vsync", "enable_multicore_scheduling", + "enable_ptc", "enable_fs_integrity_checks", "fs_global_access_log_mode", "controller_type", @@ -478,6 +479,17 @@ false ] }, + "enable_ptc": { + "$id": "#/properties/enable_ptc", + "type": "boolean", + "title": "Enable Profiled Persistent Translation Cache", + "description": "Enables or disables profiled translation cache persistency", + "default": false, + "examples": [ + true, + false + ] + }, "enable_fs_integrity_checks": { "$id": "#/properties/enable_fs_integrity_checks", "type": "boolean", @@ -581,7 +593,7 @@ "$id": "#/properties/enable_keyboard", "type": "boolean", "title": "(HID) Keyboard Enable", - "description": "Enable or disable direct keyboard access (HID) support (Provides games access to your keyboard as a text entry device).", + "description": "Enable or disable direct keyboard access (HID) support (Provides games access to your keyboard as a text entry device)", "default": true, "examples": [ true,