diff --git a/ARMeilleure/ARMeilleure.csproj b/ARMeilleure/ARMeilleure.csproj
index 24a52567ee..2fac4f6395 100644
--- a/ARMeilleure/ARMeilleure.csproj
+++ b/ARMeilleure/ARMeilleure.csproj
@@ -27,4 +27,8 @@
     <PackageReference Include="Mono.Posix.NETStandard" Version="1.0.0" />
   </ItemGroup>
 
+  <ItemGroup>
+    <ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
+  </ItemGroup>
+
 </Project>
diff --git a/ARMeilleure/CodeGen/Optimizations/ConstantFolding.cs b/ARMeilleure/CodeGen/Optimizations/ConstantFolding.cs
index 84eedee0e5..eff53217ff 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 0000000000..4a8288a28e
--- /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 44ed23f5cd..0000000000
--- 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 5ad54289ce..99df3cb565 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 d719b51640..da147cca29 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<Jump>();
+
+            _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 0faba6dd6c..e7e7553ede 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 5ba12fb7b0..6117b807d9 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 8ff630918f..07a60667e0 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 41614f88e5..0000000000
--- 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 f0bde242a6..cdf6cf3453 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 8ffad1d1fc..fd64e7bf4e 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 7b244296b9..e99afa7575 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 8a539666e9..2a8b348889 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 93c20cb588..7ca019dea0 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 0ab990f872..e8e660ee8b 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 00a5385bdf..059b9b6a40 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 b6a4d39124..18e27e5a74 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 8c2d604ca7..b3041aac5a 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 19a10f6812..fdc1bb469f 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 d11adf1943..ff97084bdf 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 a4f64ad63f..db92505387 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 5b4705676d..db24e02905 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 f62fd30711..f713a388cf 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 49f0365b40..9696fa2876 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 4f2139a421..00b8ffd63d 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 4ed960612f..ed8b4afdce 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 bf8d54c186..912c226009 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 67dc25c7cf..9697715a1f 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 a1a4635f97..12fc71c990 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 19c0a74d28..0b3d85aebb 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 e57c92a3f7..b9055c30c8 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 c13f0c3e48..827c3a7930 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 ebf829a041..14f73c3ac6 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 49faab3573..1b2a928859 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 d3e15a2ced..ec66bb8638 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 2d5e762acb..6f6caea77e 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<Node> Assignments { get; }
         public List<Node> 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 cfdb32d988..1004097718 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 a905c722b7..7ce6a3f43f 100644
--- a/ARMeilleure/Translation/ArmEmitterContext.cs
+++ b/ARMeilleure/Translation/ArmEmitterContext.cs
@@ -11,7 +11,7 @@ namespace ARMeilleure.Translation
 {
     class ArmEmitterContext : EmitterContext
     {
-        private Dictionary<ulong, Operand> _labels;
+        private readonly Dictionary<ulong, Operand> _labels;
 
         private OpCode _optOpLastCompare;
         private OpCode _optOpLastFlagSet;
diff --git a/ARMeilleure/Translation/Compiler.cs b/ARMeilleure/Translation/Compiler.cs
index ec2f296888..934c0db6f7 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<T>(ControlFlowGraph cfg, OperandType[] argTypes, OperandType retType, CompilerOptions options)
+        public static T Compile<T>(
+            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<T>(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 7328c61a67..0000000000
--- 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<string, Delegate> _delegates;
-
-        static DelegateCache()
-        {
-            _delegates = new ConcurrentDictionary<string, Delegate>();
-        }
-
-        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 0000000000..f021d1160a
--- /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<string, Type> _delegateTypesCache;
+
+        static DelegateHelper()
+        {
+            AssemblyBuilder asmBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName(DelegateTypesAssemblyName), AssemblyBuilderAccess.Run);
+
+            _modBuilder = asmBuilder.DefineDynamicModule(DelegateTypesAssemblyName);
+
+            _delegateTypesCache = new Dictionary<string, Type>();
+        }
+
+        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 0000000000..e68cfc1b76
--- /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<Delegate>(dlg);
+        }
+    }
+}
diff --git a/ARMeilleure/Translation/Delegates.cs b/ARMeilleure/Translation/Delegates.cs
new file mode 100644
index 0000000000..addb15f6fb
--- /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<string, DelegateInfo> _delegates;
+
+        static Delegates()
+        {
+            _delegates = new SortedList<string, DelegateInfo>();
+
+            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 42a78c7190..7c11fdb2fd 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<GuestFunction>(GenerateDirectCallStub(false));
+                _directTailCallStubPtr   = Marshal.GetFunctionPointerForDelegate<GuestFunction>(GenerateDirectCallStub(true));
+                _indirectCallStubPtr     = Marshal.GetFunctionPointerForDelegate<GuestFunction>(GenerateIndirectCallStub(false));
+                _indirectTailCallStubPtr = Marshal.GetFunctionPointerForDelegate<GuestFunction>(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<GuestFunction>(cfg, argTypes, OperandType.I64, CompilerOptions.HighCq);
         }
 
         /// <summary>
-        /// 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<GuestFunction>(cfg, argTypes, OperandType.I64, CompilerOptions.HighCq);
         }
diff --git a/ARMeilleure/Translation/EmitterContext.cs b/ARMeilleure/Translation/EmitterContext.cs
index f14c920e26..656f170403 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<Operand, BasicBlock> _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<Delegate>(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<TypeCode, OperandType> _typeCodeToOperandTypeMap =
-                   new Dictionary<TypeCode, OperandType>()
-        {
-            { 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 32d40c20d8..a828b4edf0 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<JitCacheEntry> _cacheEntries = new List<JitCacheEntry>();
 
         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 3f5b3282fa..e118d12948 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 40ea0fcee7..fe7a6ec3b2 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<ulong, TranslatedFunction> _targets;
-        private ConcurrentDictionary<ulong, LinkedList<int>> _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<ulong, TranslatedFunction> Targets    { get; }
+        public ConcurrentDictionary<ulong, LinkedList<int>>    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<ulong, TranslatedFunction>();
-            _dependants = new ConcurrentDictionary<ulong, LinkedList<int>>();
+            Targets    = new ConcurrentDictionary<ulong, TranslatedFunction>();
+            Dependants = new ConcurrentDictionary<ulong, LinkedList<int>>();
 
             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<ulong, TranslatedFunction> 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<int>(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<int> myDependants))
+            if (Dependants.TryGetValue(address, out LinkedList<int> 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<int> targetDependants = Dependants.GetOrAdd((ulong)address, (addr) => new LinkedList<int>());
+            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<int> targetDependants = _dependants.GetOrAdd((ulong)address, (addr) => new LinkedList<int>());
-            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 0000000000..b87e0d7a39
--- /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 0000000000..76d3d7e1bc
--- /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<byte> currentHash, ReadOnlySpan<byte> 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<ulong, TranslatedFunction> 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<byte> 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<GuestFunction>(codePtr);
+
+            TranslatedFunction tFunc = new TranslatedFunction(gFunc, highCq);
+
+            return tFunc;
+        }
+
+        internal static void MakeAndSaveTranslations(ConcurrentDictionary<ulong, TranslatedFunction> 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 0000000000..f03eb6ba41
--- /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 0000000000..0a3ae24034
--- /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<KeyValuePair<long, DirectHostAddress>>   _jumpTable;
+        private readonly List<KeyValuePair<long, IndirectHostAddress>> _dynamicTable;
+
+        private readonly List<ulong> _targets;
+        private readonly Dictionary<ulong, LinkedList<int>> _dependants;
+
+        public int TableEnd    => _jumpTable.Count;
+        public int DynTableEnd => _dynamicTable.Count;
+
+        public List<ulong> Targets => _targets;
+        public Dictionary<ulong, LinkedList<int>> Dependants => _dependants;
+
+        public PtcJumpTable()
+        {
+            _jumpTable    = new List<KeyValuePair<long, DirectHostAddress>>();
+            _dynamicTable = new List<KeyValuePair<long, IndirectHostAddress>>();
+
+            _targets    = new List<ulong>();
+            _dependants = new Dictionary<ulong, LinkedList<int>>();
+        }
+
+        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<int>(item.Value));
+            }
+        }
+
+        public void Clear()
+        {
+            _jumpTable.Clear();
+            _dynamicTable.Clear();
+
+            _targets.Clear();
+            _dependants.Clear();
+        }
+
+        public void WriteJumpTable(JumpTable jumpTable, ConcurrentDictionary<ulong, TranslatedFunction> 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<long, DirectHostAddress>(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<long, IndirectHostAddress>(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 0000000000..dcc312750b
--- /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<ulong, (ExecutionMode mode, bool highCq)> 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<ulong, (ExecutionMode, bool)>();
+
+            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<ulong, (ExecutionMode, bool)>)_binaryFormatter.Deserialize(stream);
+                }
+                catch
+                {
+                    ProfiledFuncs = new Dictionary<ulong, (ExecutionMode, bool)>();
+
+                    InvalidateCompressedStream(compressedStream);
+
+                    return false;
+                }
+
+                return true;
+            }
+        }
+
+        private static bool CompareHash(ReadOnlySpan<byte> currentHash, ReadOnlySpan<byte> 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 0000000000..ca4f410800
--- /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 0000000000..3d729fbb01
--- /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 000a5009f4..0000000000
--- a/ARMeilleure/Translation/PriorityQueue.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-using System.Collections.Concurrent;
-
-namespace ARMeilleure.Translation
-{
-    class PriorityQueue<T>
-    {
-        private ConcurrentStack<T>[] _queues;
-
-        public PriorityQueue(int priorities)
-        {
-            _queues = new ConcurrentStack<T>[priorities];
-
-            for (int index = 0; index < priorities; index++)
-            {
-                _queues[index] = new ConcurrentStack<T>();
-            }
-        }
-
-        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 e0b0e0b92a..1bed5c0ae3 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 f1dc6deeb0..36fae50ab5 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<GuestFunction>(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 700b54c230..1c2ead4fa0 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<ulong, TranslatedFunction> _funcs;
 
-        private readonly JumpTable _jumpTable;
-
-        private readonly PriorityQueue<RejitRequest> _backgroundQueue;
+        private readonly ConcurrentStack<RejitRequest> _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<ulong, TranslatedFunction>();
 
-            _jumpTable = new JumpTable(allocator);
-
-            _backgroundQueue = new PriorityQueue<RejitRequest>(2);
+            _backgroundStack = new ConcurrentStack<RejitRequest>();
 
             _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<GuestFunction>(cfg, argTypes, OperandType.I64, options);
+            GuestFunction func;
+
+            if (Ptc.State == PtcState.Disabled)
+            {
+                func = Compiler.Compile<GuestFunction>(cfg, argTypes, OperandType.I64, options);
+            }
+            else
+            {
+                using (PtcInfo ptcInfo = new PtcInfo())
+                {
+                    func = Compiler.Compile<GuestFunction>(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 9b60c6162b..14a51359f5 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 db542ad7bc..e2431ac62d 100644
--- a/Ryujinx.Common/Configuration/ConfigurationFileFormat.cs
+++ b/Ryujinx.Common/Configuration/ConfigurationFileFormat.cs
@@ -13,7 +13,7 @@ namespace Ryujinx.Configuration
         /// <summary>
         /// The current version of the file format
         /// </summary>
-        public const int CurrentVersion = 7;
+        public const int CurrentVersion = 8;
 
         public int Version { get; set; }
 
@@ -112,6 +112,11 @@ namespace Ryujinx.Configuration
         /// </summary>
         public bool EnableMulticoreScheduling { get; set; }
 
+        /// <summary>
+        /// Enables or disables profiled translation cache persistency
+        /// </summary>
+        public bool EnablePtc { get; set; }
+
         /// <summary>
         /// Enables integrity checks on Game content files
         /// </summary>
diff --git a/Ryujinx.Common/Configuration/ConfigurationState.cs b/Ryujinx.Common/Configuration/ConfigurationState.cs
index d0a00cb51c..26bbabc7bd 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
             /// </summary>
             public ReactiveObject<bool> EnableMulticoreScheduling { get; private set; }
 
+            /// <summary>
+            /// Enables or disables profiled translation cache persistency
+            /// </summary>
+            public ReactiveObject<bool> EnablePtc { get; private set; }
+
             /// <summary>
             /// Enables integrity checks on Game content files
             /// </summary>
@@ -195,6 +200,7 @@ namespace Ryujinx.Configuration
                 SystemTimeOffset          = new ReactiveObject<long>();
                 EnableDockedMode          = new ReactiveObject<bool>();
                 EnableMulticoreScheduling = new ReactiveObject<bool>();
+                EnablePtc                 = new ReactiveObject<bool>();
                 EnableFsIntegrityChecks   = new ReactiveObject<bool>();
                 FsGlobalAccessLogMode     = new ReactiveObject<int>();
                 IgnoreMissingServices     = new ReactiveObject<bool>();
@@ -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> inputConfig = new List<InputConfig>();
             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 dcf380a171..2120c9cb04 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 3286a5cebc..639fa69da3 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 c8bb37c51f..c44c40b55d 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<ApplicationControlProperty> 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<ApplicationControlProperty> 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 34e16e79c7..c1baae3078 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 07b1a18b20..1158925ab7 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 d5c0b3b29d..5713bd9ef9 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 b3f316f43d..9e37c2114c 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 9b8b89a1a8..e43bf5976f 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 2479d2d5e0..e87d34b109 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 08a9859daa..cb8ab48ecf 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 3ac935f311..b3471259a8 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 bd83d85979..342acc9f8f 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 1b24e72bf6..42764a7dab 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 ea662de09b..a0eab22feb 100644
--- a/Ryujinx/Ui/SettingsWindow.glade
+++ b/Ryujinx/Ui/SettingsWindow.glade
@@ -1398,6 +1398,24 @@
                                     <property name="position">5</property>
                                   </packing>
                                 </child>
+                                <child>
+                                  <object class="GtkCheckButton" id="_ptcToggle">
+                                    <property name="label" translatable="yes">Enable Profiled Persistent Translation Cache</property>
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">True</property>
+                                    <property name="receives_default">False</property>
+                                    <property name="tooltip_text" translatable="yes">Enables or disables profiled translation cache persistency</property>
+                                    <property name="halign">start</property>
+                                    <property name="margin_top">5</property>
+                                    <property name="margin_bottom">5</property>
+                                    <property name="draw_indicator">True</property>
+                                  </object>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="fill">True</property>
+                                    <property name="position">6</property>
+                                  </packing>
+                                </child>
                                 <child>
                                   <object class="GtkCheckButton" id="_fsicToggle">
                                     <property name="label" translatable="yes">Enable FS Integrity Checks</property>
@@ -1413,7 +1431,7 @@
                                   <packing>
                                     <property name="expand">False</property>
                                     <property name="fill">True</property>
-                                    <property name="position">6</property>
+                                    <property name="position">7</property>
                                   </packing>
                                 </child>
                               </object>
diff --git a/Ryujinx/_schema.json b/Ryujinx/_schema.json
index e89e2bf7d4..c940d4c90d 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,