0
0
Fork 0

Add Profiled Persistent Translation Cache. (#769)

* Delete DelegateTypes.cs

* Delete DelegateCache.cs

* Add files via upload

* Update Horizon.cs

* Update Program.cs

* Update MainWindow.cs

* Update Aot.cs

* Update RelocEntry.cs

* Update Translator.cs

* Update MemoryManager.cs

* Update InstEmitMemoryHelper.cs

* Update Delegates.cs

* Nit.

* Nit.

* Nit.

* 10 fewer MSIL bytes for us

* Add comment. Nits.

* Update Translator.cs

* Update Aot.cs

* Nits.

* Opt..

* Opt..

* Opt..

* Opt..

* Allow to change compression level.

* Update MemoryManager.cs

* Update Translator.cs

* Manage corner cases during the save phase. Nits.

* Update Aot.cs

* Translator response tweak for Aot disabled. Nit.

* Nit.

* Nits.

* Create DelegateHelpers.cs

* Update Delegates.cs

* Nit.

* Nit.

* Nits.

* Fix due to #784.

* Fixes due to #757 & #841.

* Fix due to #846.

* Fix due to #847.

* Use MethodInfo for managed method calls.

Use IR methods instead of managed methods about Max/Min (S/U).
Follow-ups & Nits.

* Add missing exception messages.

Reintroduce slow path for Fmov_Vi.
Implement slow path for Fmov_Si.

* Switch to the new folder structure.

Nits.

* Impl. index-based relocation information. Impl. cache file version field.

* Nit.

* Address gdkchan comments.

Mainly:
- fixed cache file corruption issue on exit; - exposed a way to disable AOT on the GUI.

* Address AcK77 comment.

* Address Thealexbarney, jduncanator & emmauss comments.

Header magic, CpuId (FI) & Aot -> Ptc.

* Adaptation to the new application reloading system.

Improvements to the call system of managed methods.
Follow-ups.
Nits.

* Get the same boot times as on master when PTC is disabled.

* Profiled Aot.

* A32 support (#897).

* #975 support (1 of 2).

* #975 support (2 of 2).

* Rebase fix & nits.

* Some fixes and nits (still one bug left).

* One fix & nits.

* Tests fix (by gdk) & nits.

* Support translations not only in high quality and rejit.

Nits.

* Added possibility to skip translations and continue execution, using `ESC` key.

* Update SettingsWindow.cs

* Update GLRenderer.cs

* Update Ptc.cs

* Disabled Profiled PTC by default as requested in the past by gdk.

* Fix rejit bug. Increased number of parallel translations. Add stack unwinding stuffs support (1 of 2).

Nits.

* Add stack unwinding stuffs support (2 of 2). Tuned number of parallel translations.

* Restored the ability to assemble jumps with 8-bit offset when Profiled PTC is disabled or during profiling.

Modifications due to rebase.
Nits.

* Limited profiling of the functions to be translated to the addresses belonging to the range of static objects only.

* Nits.

* Nits.

* Update Delegates.cs

* Nit.

* Update InstEmitSimdArithmetic.cs

* Address riperiperi comments.

* Fixed the issue of unjustifiably longer boot times at the second boot than at the first boot, measured at the same time or reference point and with the same number of translated functions.

* Implemented a simple redundant load/save mechanism.

Halved the value of Decoder.MaxInstsPerFunction more appropriate for the current performance of the Translator.
Replaced by Logger.PrintError to Logger.PrintDebug in TexturePool.cs about the supposed invalid texture format to avoid the spawn of the log.
Nits.

* Nit.

Improved Logger.PrintError in TexturePool.cs to avoid log spawn.
Added missing code for FZ handling (in output) for fp max/min instructions (slow paths).

* Add configuration migration for PTC

Co-authored-by: Thog <me@thog.eu>
This commit is contained in:
LDj3SNuD 2020-06-16 20:28:02 +02:00 committed by GitHub
parent fa286d3535
commit 5e724cf24e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
78 changed files with 3066 additions and 1207 deletions

View file

@ -27,4 +27,8 @@
<PackageReference Include="Mono.Posix.NETStandard" Version="1.0.0" /> <PackageReference Include="Mono.Posix.NETStandard" Version="1.0.0" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
</ItemGroup>
</Project> </Project>

View file

@ -14,7 +14,7 @@ namespace ARMeilleure.CodeGen.Optimizations
return; return;
} }
if (!AreAllSourcesConstant(operation)) if (!AreAllSourcesConstantAndCFEnabled(operation))
{ {
return; 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++) 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; return false;
} }

View file

@ -0,0 +1,11 @@
namespace ARMeilleure.CodeGen.Unwinding
{
enum UnwindPseudoOp
{
PushReg = 0,
SetFrame = 1,
AllocStack = 2,
SaveReg = 3,
SaveXmm128 = 4
}
}

View file

@ -1,11 +0,0 @@
namespace ARMeilleure.CodeGen.Unwinding
{
enum UnwindPseudoOp
{
PushReg,
SetFrame,
AllocStack,
SaveReg,
SaveXmm128
}
}

View file

@ -1,4 +1,5 @@
using ARMeilleure.IntermediateRepresentation; using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.Translation.PTC;
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
@ -64,6 +65,9 @@ namespace ARMeilleure.CodeGen.X86
private Stream _stream; private Stream _stream;
private PtcInfo _ptcInfo;
private bool _ptcDisabled;
static Assembler() static Assembler()
{ {
_instTable = new InstructionInfo[(int)X86Instruction.Count]; _instTable = new InstructionInfo[(int)X86Instruction.Count];
@ -273,9 +277,12 @@ namespace ARMeilleure.CodeGen.X86
_instTable[(int)inst] = info; _instTable[(int)inst] = info;
} }
public Assembler(Stream stream) public Assembler(Stream stream, PtcInfo ptcInfo = null)
{ {
_stream = stream; _stream = stream;
_ptcInfo = ptcInfo;
_ptcDisabled = ptcInfo == null;
} }
public void Add(Operand dest, Operand source, OperandType type) public void Add(Operand dest, Operand source, OperandType type)
@ -456,7 +463,7 @@ namespace ARMeilleure.CodeGen.X86
public void Jcc(X86Condition condition, long offset) public void Jcc(X86Condition condition, long offset)
{ {
if (ConstFitsOnS8(offset)) if (_ptcDisabled && ConstFitsOnS8(offset))
{ {
WriteByte((byte)(0x70 | (int)condition)); WriteByte((byte)(0x70 | (int)condition));
@ -477,7 +484,7 @@ namespace ARMeilleure.CodeGen.X86
public void Jmp(long offset) public void Jmp(long offset)
{ {
if (ConstFitsOnS8(offset)) if (_ptcDisabled && ConstFitsOnS8(offset))
{ {
WriteByte(0xeb); WriteByte(0xeb);
@ -915,6 +922,8 @@ namespace ARMeilleure.CodeGen.X86
} }
else if (dest != null && dest.Kind == OperandKind.Register && info.OpRImm64 != BadOp) else if (dest != null && dest.Kind == OperandKind.Register && info.OpRImm64 != BadOp)
{ {
int? index = source.PtcIndex;
int rexPrefix = GetRexPrefix(dest, source, type, rrm: false); int rexPrefix = GetRexPrefix(dest, source, type, rrm: false);
if (rexPrefix != 0) if (rexPrefix != 0)
@ -924,6 +933,11 @@ namespace ARMeilleure.CodeGen.X86
WriteByte((byte)(info.OpRImm64 + (dest.GetRegister().Index & 0b111))); WriteByte((byte)(info.OpRImm64 + (dest.GetRegister().Index & 0b111)));
if (_ptcInfo != null && index != null)
{
_ptcInfo.WriteRelocEntry(new RelocEntry((int)_stream.Position, (int)index));
}
WriteUInt64(imm); WriteUInt64(imm);
} }
else else
@ -1316,9 +1330,9 @@ namespace ARMeilleure.CodeGen.X86
return ConstFitsOnS32(value); 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; 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; return 2;
} }

View file

@ -1,6 +1,7 @@
using ARMeilleure.CodeGen.RegisterAllocators; using ARMeilleure.CodeGen.RegisterAllocators;
using ARMeilleure.Common; using ARMeilleure.Common;
using ARMeilleure.IntermediateRepresentation; using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.Translation.PTC;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
@ -13,6 +14,9 @@ namespace ARMeilleure.CodeGen.X86
private Stream _stream; private Stream _stream;
private PtcInfo _ptcInfo;
private bool _ptcDisabled;
public int StreamOffset => (int)_stream.Length; public int StreamOffset => (int)_stream.Length;
public AllocationResult AllocResult { get; } public AllocationResult AllocResult { get; }
@ -40,7 +44,7 @@ namespace ARMeilleure.CodeGen.X86
public int InstSize { get; set; } public int InstSize { get; set; }
public Jump(BasicBlock target, long jumpPosition) public Jump(BasicBlock target, long jumpPosition, int instSize = 0)
{ {
IsConditional = false; IsConditional = false;
Condition = 0; Condition = 0;
@ -49,10 +53,10 @@ namespace ARMeilleure.CodeGen.X86
RelativeOffset = 0; 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; IsConditional = true;
Condition = condition; Condition = condition;
@ -61,7 +65,7 @@ namespace ARMeilleure.CodeGen.X86
RelativeOffset = 0; RelativeOffset = 0;
InstSize = 0; InstSize = instSize;
} }
} }
@ -72,13 +76,13 @@ namespace ARMeilleure.CodeGen.X86
private long _jNearPosition; private long _jNearPosition;
private int _jNearLength; 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; _stream = stream;
AllocResult = allocResult; AllocResult = allocResult;
Assembler = new Assembler(stream); Assembler = new Assembler(stream, ptcInfo);
CallArgsRegionSize = GetCallArgsRegionSize(allocResult, maxCallArgs, out int xmmSaveRegionSize); CallArgsRegionSize = GetCallArgsRegionSize(allocResult, maxCallArgs, out int xmmSaveRegionSize);
XmmSaveRegionSize = xmmSaveRegionSize; XmmSaveRegionSize = xmmSaveRegionSize;
@ -86,6 +90,9 @@ namespace ARMeilleure.CodeGen.X86
_blockOffsets = new long[blocksCount]; _blockOffsets = new long[blocksCount];
_jumps = new List<Jump>(); _jumps = new List<Jump>();
_ptcInfo = ptcInfo;
_ptcDisabled = ptcInfo == null;
} }
private int GetCallArgsRegionSize(AllocationResult allocResult, int maxCallArgs, out int xmmSaveRegionSize) private int GetCallArgsRegionSize(AllocationResult allocResult, int maxCallArgs, out int xmmSaveRegionSize)
@ -135,24 +142,42 @@ namespace ARMeilleure.CodeGen.X86
} }
public void JumpTo(BasicBlock target) public void JumpTo(BasicBlock target)
{
if (_ptcDisabled)
{ {
_jumps.Add(new Jump(target, _stream.Position)); _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) public void JumpTo(X86Condition condition, BasicBlock target)
{
if (_ptcDisabled)
{ {
_jumps.Add(new Jump(condition, target, _stream.Position)); _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) public void JumpToNear(X86Condition condition)
{ {
_jNearCondition = condition; _jNearCondition = condition;
_jNearPosition = _stream.Position; _jNearPosition = _stream.Position;
_jNearLength = Assembler.GetJccLength(0); _jNearLength = Assembler.GetJccLength(0, _ptcDisabled);
_stream.Seek(_jNearLength, SeekOrigin.Current); _stream.Seek(_jNearLength, SeekOrigin.Current);
} }
@ -165,7 +190,7 @@ namespace ARMeilleure.CodeGen.X86
long offset = currentPosition - (_jNearPosition + _jNearLength); 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); Assembler.Jcc(_jNearCondition, offset);
@ -197,6 +222,8 @@ namespace ARMeilleure.CodeGen.X86
long offset = jumpTarget - jump.JumpPosition; long offset = jumpTarget - jump.JumpPosition;
if (_ptcDisabled)
{
if (offset < 0) if (offset < 0)
{ {
for (int index2 = index - 1; index2 >= 0; index2--) for (int index2 = index - 1; index2 >= 0; index2--)
@ -239,9 +266,9 @@ namespace ARMeilleure.CodeGen.X86
// The jump is relative to the next instruction, not the current one. // The jump is relative to the next instruction, not the current one.
// Since we didn't know the next instruction address when calculating // Since we didn't know the next instruction address when calculating
// the offset (as the size of the current jump instruction was not know), // 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. // we now need to compensate the offset with the jump instruction size.
// It's also worth to note that: // It's also worth noting that:
// - This is only needed for backward jumps. // - This is only needed for backward jumps.
// - The GetJmpLength and GetJccLength also compensates the offset // - The GetJmpLength and GetJccLength also compensates the offset
// internally when computing the jump instruction size. // internally when computing the jump instruction size.
@ -249,6 +276,11 @@ namespace ARMeilleure.CodeGen.X86
{ {
offset -= jump.InstSize; offset -= jump.InstSize;
} }
}
else
{
offset -= jump.InstSize;
}
if (jump.RelativeOffset != offset) if (jump.RelativeOffset != offset)
{ {
@ -267,7 +299,7 @@ namespace ARMeilleure.CodeGen.X86
using (MemoryStream codeStream = new MemoryStream()) using (MemoryStream codeStream = new MemoryStream())
{ {
Assembler assembler = new Assembler(codeStream); Assembler assembler = new Assembler(codeStream, _ptcInfo);
byte[] buffer; byte[] buffer;
@ -278,7 +310,7 @@ namespace ARMeilleure.CodeGen.X86
buffer = new byte[jump.JumpPosition - _stream.Position]; buffer = new byte[jump.JumpPosition - _stream.Position];
_stream.Read(buffer, 0, buffer.Length); _stream.Read(buffer, 0, buffer.Length);
_stream.Seek(ReservedBytesForJump, SeekOrigin.Current); _stream.Seek(_ptcDisabled ? ReservedBytesForJump : jump.InstSize, SeekOrigin.Current);
codeStream.Write(buffer); codeStream.Write(buffer);
@ -298,6 +330,8 @@ namespace ARMeilleure.CodeGen.X86
codeStream.Write(buffer); codeStream.Write(buffer);
_ptcInfo?.WriteCode(codeStream);
return codeStream.ToArray(); return codeStream.ToArray();
} }
} }

View file

@ -5,6 +5,7 @@ using ARMeilleure.Common;
using ARMeilleure.Diagnostics; using ARMeilleure.Diagnostics;
using ARMeilleure.IntermediateRepresentation; using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.Translation; using ARMeilleure.Translation;
using ARMeilleure.Translation.PTC;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
@ -100,7 +101,7 @@ namespace ARMeilleure.CodeGen.X86
_instTable[(int)inst] = func; _instTable[(int)inst] = func;
} }
public static CompiledFunction Generate(CompilerContext cctx) public static CompiledFunction Generate(CompilerContext cctx, PtcInfo ptcInfo = null)
{ {
ControlFlowGraph cfg = cctx.Cfg; ControlFlowGraph cfg = cctx.Cfg;
@ -158,10 +159,12 @@ namespace ARMeilleure.CodeGen.X86
using (MemoryStream stream = new MemoryStream()) 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); UnwindInfo unwindInfo = WritePrologue(context);
ptcInfo?.WriteUnwindInfo(unwindInfo);
for (BasicBlock block = cfg.Blocks.First; block != null; block = block.ListNext) for (BasicBlock block = cfg.Blocks.First; block != null; block = block.ListNext)
{ {
context.EnterBlock(block); context.EnterBlock(block);

View file

@ -12,7 +12,7 @@ namespace ARMeilleure.Decoders
// We define a limit on the number of instructions that a function may have, // We define a limit on the number of instructions that a function may have,
// this prevents functions being potentially too large, which would // this prevents functions being potentially too large, which would
// take too long to compile and use too much memory. // 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. // For lower code quality translation, we set a lower limit since we're blocking execution.
private const int MaxInstsPerFunctionLowCq = 500; private const int MaxInstsPerFunctionLowCq = 500;

View file

@ -6,46 +6,41 @@ namespace ARMeilleure.Diagnostics
{ {
static class Logger static class Logger
{ {
#pragma warning disable CS0169
private static long _startTime; private static long _startTime;
private static long[] _accumulatedTime; private static long[] _accumulatedTime;
#pragma warning restore CS0169
static Logger() static Logger()
{ {
_accumulatedTime = new long[(int)PassName.Count]; _accumulatedTime = new long[(int)PassName.Count];
} }
[Conditional("M_DEBUG")]
public static void StartPass(PassName name) public static void StartPass(PassName name)
{ {
#if M_DEBUG
WriteOutput(name + " pass started..."); WriteOutput(name + " pass started...");
_startTime = Stopwatch.GetTimestamp(); _startTime = Stopwatch.GetTimestamp();
#endif
} }
[Conditional("M_DEBUG")]
public static void EndPass(PassName name, ControlFlowGraph cfg) public static void EndPass(PassName name, ControlFlowGraph cfg)
{ {
#if M_DEBUG
EndPass(name); EndPass(name);
WriteOutput("IR after " + name + " pass:"); WriteOutput("IR after " + name + " pass:");
WriteOutput(IRDumper.GetDump(cfg)); WriteOutput(IRDumper.GetDump(cfg));
#endif
} }
[Conditional("M_DEBUG")]
public static void EndPass(PassName name) public static void EndPass(PassName name)
{ {
#if M_DEBUG
long elapsedTime = Stopwatch.GetTimestamp() - _startTime; long elapsedTime = Stopwatch.GetTimestamp() - _startTime;
_accumulatedTime[(int)name] += elapsedTime; _accumulatedTime[(int)name] += elapsedTime;
WriteOutput($"{name} pass ended after {GetMilliseconds(_accumulatedTime[(int)name])} ms..."); WriteOutput($"{name} pass ended after {GetMilliseconds(_accumulatedTime[(int)name])} ms...");
#endif
} }
private static long GetMilliseconds(long ticks) private static long GetMilliseconds(long ticks)

View file

@ -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);
}

View file

@ -1,6 +1,5 @@
using ARMeilleure.Decoders; using ARMeilleure.Decoders;
using ARMeilleure.Translation; using ARMeilleure.Translation;
using System;
using static ARMeilleure.Instructions.InstEmitFlowHelper; using static ARMeilleure.Instructions.InstEmitFlowHelper;
using static ARMeilleure.IntermediateRepresentation.OperandHelper; using static ARMeilleure.IntermediateRepresentation.OperandHelper;
@ -11,21 +10,21 @@ namespace ARMeilleure.Instructions
{ {
public static void Brk(ArmEmitterContext context) public static void Brk(ArmEmitterContext context)
{ {
EmitExceptionCall(context, NativeInterface.Break); EmitExceptionCall(context, nameof(NativeInterface.Break));
} }
public static void Svc(ArmEmitterContext context) 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; OpCodeException op = (OpCodeException)context.CurrOp;
context.StoreToContext(); 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(); context.LoadFromContext();
@ -39,11 +38,11 @@ namespace ARMeilleure.Instructions
{ {
OpCode op = context.CurrOp; OpCode op = context.CurrOp;
Delegate dlg = new _Void_U64_S32(NativeInterface.Undefined); string name = nameof(NativeInterface.Undefined);
context.StoreToContext(); 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(); context.LoadFromContext();

View file

@ -10,21 +10,21 @@ namespace ARMeilleure.Instructions
{ {
public static void Svc(ArmEmitterContext context) public static void Svc(ArmEmitterContext context)
{ {
EmitExceptionCall(context, NativeInterface.SupervisorCall); EmitExceptionCall(context, nameof(NativeInterface.SupervisorCall));
} }
public static void Trap(ArmEmitterContext context) 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; OpCode32Exception op = (OpCode32Exception)context.CurrOp;
context.StoreToContext(); 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(); context.LoadFromContext();

View file

@ -2,6 +2,7 @@ using ARMeilleure.Decoders;
using ARMeilleure.IntermediateRepresentation; using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.State; using ARMeilleure.State;
using ARMeilleure.Translation; using ARMeilleure.Translation;
using ARMeilleure.Translation.PTC;
using System; using System;
using static ARMeilleure.Instructions.InstEmitHelper; using static ARMeilleure.Instructions.InstEmitHelper;
@ -223,6 +224,7 @@ namespace ARMeilleure.Instructions
public static void EmitTailContinue(ArmEmitterContext context, Operand address, bool allowRejit = false) 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) 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 (useTailContinue)
{ {
if (context.HighCq) if (context.HighCq)
@ -238,7 +240,7 @@ namespace ARMeilleure.Instructions
address = context.BitwiseOr(address, Const(CallFlag)); 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); EmitNativeCall(context, fallbackAddr, true);
} }
@ -260,7 +262,7 @@ namespace ARMeilleure.Instructions
private static void EmitBranchFallback(ArmEmitterContext context, Operand address, bool isJump) private static void EmitBranchFallback(ArmEmitterContext context, Operand address, bool isJump)
{ {
address = context.BitwiseOr(address, Const(address.Type, (long)CallFlag)); // Set call flag. 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); EmitNativeCall(context, fallbackAddr, isJump);
} }
@ -339,7 +341,18 @@ namespace ARMeilleure.Instructions
int entry = context.JumpTable.ReserveDynamicEntry(isJump); int entry = context.JumpTable.ReserveDynamicEntry(isJump);
int jumpOffset = entry * JumpTable.JumpTableStride * JumpTable.DynamicTableElems; 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); EmitDynamicTableCall(context, dynTablePtr, address, isJump);
} }
@ -349,8 +362,17 @@ namespace ARMeilleure.Instructions
int jumpOffset = entry * JumpTable.JumpTableStride + 8; // Offset directly to the host address. 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;
Operand tableEntryPtr = Const(context.JumpTable.JumpPointer.ToInt64() + jumpOffset);
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); Operand funcAddr = context.Load(OperandType.I64, tableEntryPtr);

View file

@ -3,7 +3,6 @@
using ARMeilleure.Decoders; using ARMeilleure.Decoders;
using ARMeilleure.IntermediateRepresentation; using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.Translation; using ARMeilleure.Translation;
using System;
using static ARMeilleure.Instructions.InstEmitHelper; using static ARMeilleure.Instructions.InstEmitHelper;
using static ARMeilleure.Instructions.InstEmitSimdHelper; using static ARMeilleure.Instructions.InstEmitSimdHelper;
@ -21,7 +20,7 @@ namespace ARMeilleure.Instructions
} }
else else
{ {
EmitCrc32Call(context, new _U32_U32_U8(SoftFallback.Crc32b)); EmitCrc32Call(context, nameof(SoftFallback.Crc32b));
} }
} }
@ -33,7 +32,7 @@ namespace ARMeilleure.Instructions
} }
else else
{ {
EmitCrc32Call(context, new _U32_U32_U16(SoftFallback.Crc32h)); EmitCrc32Call(context, nameof(SoftFallback.Crc32h));
} }
} }
@ -45,7 +44,7 @@ namespace ARMeilleure.Instructions
} }
else else
{ {
EmitCrc32Call(context, new _U32_U32_U32(SoftFallback.Crc32w)); EmitCrc32Call(context, nameof(SoftFallback.Crc32w));
} }
} }
@ -57,7 +56,7 @@ namespace ARMeilleure.Instructions
} }
else else
{ {
EmitCrc32Call(context, new _U32_U32_U64(SoftFallback.Crc32x)); EmitCrc32Call(context, nameof(SoftFallback.Crc32x));
} }
} }
@ -69,7 +68,7 @@ namespace ARMeilleure.Instructions
} }
else else
{ {
EmitCrc32Call(context, new _U32_U32_U8(SoftFallback.Crc32cb)); EmitCrc32Call(context, nameof(SoftFallback.Crc32cb));
} }
} }
@ -81,7 +80,7 @@ namespace ARMeilleure.Instructions
} }
else else
{ {
EmitCrc32Call(context, new _U32_U32_U16(SoftFallback.Crc32ch)); EmitCrc32Call(context, nameof(SoftFallback.Crc32ch));
} }
} }
@ -93,7 +92,7 @@ namespace ARMeilleure.Instructions
} }
else else
{ {
EmitCrc32Call(context, new _U32_U32_U32(SoftFallback.Crc32cw)); EmitCrc32Call(context, nameof(SoftFallback.Crc32cw));
} }
} }
@ -105,7 +104,7 @@ namespace ARMeilleure.Instructions
} }
else 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)); 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; OpCodeAluBinary op = (OpCodeAluBinary)context.CurrOp;
Operand n = GetIntOrZR(context, op.Rn); Operand n = GetIntOrZR(context, op.Rn);
Operand m = GetIntOrZR(context, op.Rm); 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); SetIntOrZR(context, op.Rd, d);
} }

View file

@ -23,7 +23,7 @@ namespace ARMeilleure.Instructions
public static void Clrex(ArmEmitterContext context) 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); public static void Dmb(ArmEmitterContext context) => EmitBarrier(context);
@ -101,6 +101,7 @@ namespace ARMeilleure.Instructions
SetIntOrZR(context, op.Rt, value); SetIntOrZR(context, op.Rt, value);
} }
} }
public static void Pfrm(ArmEmitterContext context) public static void Pfrm(ArmEmitterContext context)
{ {
// Memory Prefetch, execute as no-op. // Memory Prefetch, execute as no-op.

View file

@ -13,7 +13,7 @@ namespace ARMeilleure.Instructions
{ {
public static void Clrex(ArmEmitterContext context) 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); public static void Dmb(ArmEmitterContext context) => EmitBarrier(context);

View file

@ -1,6 +1,6 @@
using ARMeilleure.IntermediateRepresentation; using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.Translation; using ARMeilleure.Translation;
using System; using System.Reflection;
namespace ARMeilleure.Instructions namespace ARMeilleure.Instructions
{ {
@ -12,32 +12,32 @@ namespace ARMeilleure.Instructions
bool exclusive, bool exclusive,
int size) int size)
{ {
Delegate fallbackMethodDlg = null; MethodInfo info = null;
if (exclusive) if (exclusive)
{ {
switch (size) switch (size)
{ {
case 0: fallbackMethodDlg = new _U8_U64(NativeInterface.ReadByteExclusive); break; case 0: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadByteExclusive)); break;
case 1: fallbackMethodDlg = new _U16_U64(NativeInterface.ReadUInt16Exclusive); break; case 1: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt16Exclusive)); break;
case 2: fallbackMethodDlg = new _U32_U64(NativeInterface.ReadUInt32Exclusive); break; case 2: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt32Exclusive)); break;
case 3: fallbackMethodDlg = new _U64_U64(NativeInterface.ReadUInt64Exclusive); break; case 3: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt64Exclusive)); break;
case 4: fallbackMethodDlg = new _V128_U64(NativeInterface.ReadVector128Exclusive); break; case 4: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadVector128Exclusive)); break;
} }
} }
else else
{ {
switch (size) switch (size)
{ {
case 0: fallbackMethodDlg = new _U8_U64(NativeInterface.ReadByte); break; case 0: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadByte)); break;
case 1: fallbackMethodDlg = new _U16_U64(NativeInterface.ReadUInt16); break; case 1: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt16)); break;
case 2: fallbackMethodDlg = new _U32_U64(NativeInterface.ReadUInt32); break; case 2: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt32)); break;
case 3: fallbackMethodDlg = new _U64_U64(NativeInterface.ReadUInt64); break; case 3: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt64)); break;
case 4: fallbackMethodDlg = new _V128_U64(NativeInterface.ReadVector128); 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( public static Operand EmitStoreExclusive(
@ -52,33 +52,33 @@ namespace ARMeilleure.Instructions
value = context.ConvertI64ToI32(value); value = context.ConvertI64ToI32(value);
} }
Delegate fallbackMethodDlg = null; MethodInfo info = null;
if (exclusive) if (exclusive)
{ {
switch (size) switch (size)
{ {
case 0: fallbackMethodDlg = new _S32_U64_U8(NativeInterface.WriteByteExclusive); break; case 0: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteByteExclusive)); break;
case 1: fallbackMethodDlg = new _S32_U64_U16(NativeInterface.WriteUInt16Exclusive); break; case 1: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt16Exclusive)); break;
case 2: fallbackMethodDlg = new _S32_U64_U32(NativeInterface.WriteUInt32Exclusive); break; case 2: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt32Exclusive)); break;
case 3: fallbackMethodDlg = new _S32_U64_U64(NativeInterface.WriteUInt64Exclusive); break; case 3: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt64Exclusive)); break;
case 4: fallbackMethodDlg = new _S32_U64_V128(NativeInterface.WriteVector128Exclusive); break; case 4: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteVector128Exclusive)); break;
} }
return context.Call(fallbackMethodDlg, address, value); return context.Call(info, address, value);
} }
else else
{ {
switch (size) switch (size)
{ {
case 0: fallbackMethodDlg = new _Void_U64_U8(NativeInterface.WriteByte); break; case 0: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteByte)); break;
case 1: fallbackMethodDlg = new _Void_U64_U16(NativeInterface.WriteUInt16); break; case 1: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt16)); break;
case 2: fallbackMethodDlg = new _Void_U64_U32(NativeInterface.WriteUInt32); break; case 2: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt32)); break;
case 3: fallbackMethodDlg = new _Void_U64_U64(NativeInterface.WriteUInt64); break; case 3: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt64)); break;
case 4: fallbackMethodDlg = new _Void_U64_V128(NativeInterface.WriteVector128); break; case 4: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteVector128)); break;
} }
context.Call(fallbackMethodDlg, address, value); context.Call(info, address, value);
return null; return null;
} }

View file

@ -1,7 +1,9 @@
using ARMeilleure.Decoders; using ARMeilleure.Decoders;
using ARMeilleure.IntermediateRepresentation; using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.Translation; using ARMeilleure.Translation;
using ARMeilleure.Translation.PTC;
using System; using System;
using System.Reflection;
using static ARMeilleure.Instructions.InstEmitHelper; using static ARMeilleure.Instructions.InstEmitHelper;
using static ARMeilleure.IntermediateRepresentation.OperandHelper; using static ARMeilleure.IntermediateRepresentation.OperandHelper;
@ -144,21 +146,10 @@ namespace ARMeilleure.Instructions
switch (size) switch (size)
{ {
case 0: case 0: value = context.Load8 (physAddr); break;
value = context.Load8(physAddr); case 1: value = context.Load16(physAddr); break;
break; case 2: value = context.Load (OperandType.I32, physAddr); break;
case 3: value = context.Load (OperandType.I64, 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); SetInt(context, rt, value);
@ -196,25 +187,11 @@ namespace ARMeilleure.Instructions
switch (size) switch (size)
{ {
case 0: case 0: value = context.VectorInsert8 (vector, context.Load8(physAddr), elem); break;
value = context.VectorInsert8(vector, context.Load8(physAddr), elem); case 1: value = context.VectorInsert16(vector, context.Load16(physAddr), elem); break;
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 1: case 4: value = context.Load (OperandType.V128, physAddr); break;
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); context.Copy(GetVec(rt), value);
@ -294,25 +271,11 @@ namespace ARMeilleure.Instructions
switch (size) switch (size)
{ {
case 0: case 0: context.Store8 (physAddr, context.VectorExtract8(value, elem)); break;
context.Store8(physAddr, context.VectorExtract8(value, elem)); case 1: context.Store16(physAddr, context.VectorExtract16(value, elem)); break;
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 1: case 4: context.Store (physAddr, value); break;
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); context.MarkLabel(lblEnd);
@ -333,7 +296,9 @@ namespace ARMeilleure.Instructions
int ptLevelSize = 1 << ptLevelBits; int ptLevelSize = 1 << ptLevelBits;
int ptLevelMask = ptLevelSize - 1; 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; int bit = PageBits;
@ -375,17 +340,17 @@ namespace ARMeilleure.Instructions
private static void EmitReadIntFallback(ArmEmitterContext context, Operand address, int rt, int size) private static void EmitReadIntFallback(ArmEmitterContext context, Operand address, int rt, int size)
{ {
Delegate fallbackMethodDlg = null; MethodInfo info = null;
switch (size) switch (size)
{ {
case 0: fallbackMethodDlg = new _U8_U64 (NativeInterface.ReadByte); break; case 0: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadByte)); break;
case 1: fallbackMethodDlg = new _U16_U64(NativeInterface.ReadUInt16); break; case 1: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt16)); break;
case 2: fallbackMethodDlg = new _U32_U64(NativeInterface.ReadUInt32); break; case 2: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt32)); break;
case 3: fallbackMethodDlg = new _U64_U64(NativeInterface.ReadUInt64); 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( private static void EmitReadVectorFallback(
@ -396,18 +361,18 @@ namespace ARMeilleure.Instructions
int elem, int elem,
int size) int size)
{ {
Delegate fallbackMethodDlg = null; MethodInfo info = null;
switch (size) switch (size)
{ {
case 0: fallbackMethodDlg = new _U8_U64 (NativeInterface.ReadByte); break; case 0: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadByte)); break;
case 1: fallbackMethodDlg = new _U16_U64 (NativeInterface.ReadUInt16); break; case 1: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt16)); break;
case 2: fallbackMethodDlg = new _U32_U64 (NativeInterface.ReadUInt32); break; case 2: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt32)); break;
case 3: fallbackMethodDlg = new _U64_U64 (NativeInterface.ReadUInt64); break; case 3: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt64)); break;
case 4: fallbackMethodDlg = new _V128_U64(NativeInterface.ReadVector128); 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) switch (size)
{ {
@ -422,14 +387,14 @@ namespace ARMeilleure.Instructions
private static void EmitWriteIntFallback(ArmEmitterContext context, Operand address, int rt, int size) private static void EmitWriteIntFallback(ArmEmitterContext context, Operand address, int rt, int size)
{ {
Delegate fallbackMethodDlg = null; MethodInfo info = null;
switch (size) switch (size)
{ {
case 0: fallbackMethodDlg = new _Void_U64_U8 (NativeInterface.WriteByte); break; case 0: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteByte)); break;
case 1: fallbackMethodDlg = new _Void_U64_U16(NativeInterface.WriteUInt16); break; case 1: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt16)); break;
case 2: fallbackMethodDlg = new _Void_U64_U32(NativeInterface.WriteUInt32); break; case 2: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt32)); break;
case 3: fallbackMethodDlg = new _Void_U64_U64(NativeInterface.WriteUInt64); break; case 3: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt64)); break;
} }
Operand value = GetInt(context, rt); Operand value = GetInt(context, rt);
@ -439,7 +404,7 @@ namespace ARMeilleure.Instructions
value = context.ConvertI64ToI32(value); value = context.ConvertI64ToI32(value);
} }
context.Call(fallbackMethodDlg, address, value); context.Call(info, address, value);
} }
private static void EmitWriteVectorFallback( private static void EmitWriteVectorFallback(
@ -449,15 +414,15 @@ namespace ARMeilleure.Instructions
int elem, int elem,
int size) int size)
{ {
Delegate fallbackMethodDlg = null; MethodInfo info = null;
switch (size) switch (size)
{ {
case 0: fallbackMethodDlg = new _Void_U64_U8 (NativeInterface.WriteByte); break; case 0: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteByte)); break;
case 1: fallbackMethodDlg = new _Void_U64_U16 (NativeInterface.WriteUInt16); break; case 1: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt16)); break;
case 2: fallbackMethodDlg = new _Void_U64_U32 (NativeInterface.WriteUInt32); break; case 2: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt32)); break;
case 3: fallbackMethodDlg = new _Void_U64_U64 (NativeInterface.WriteUInt64); break; case 3: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt64)); break;
case 4: fallbackMethodDlg = new _Void_U64_V128(NativeInterface.WriteVector128); break; case 4: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteVector128)); break;
} }
Operand value = null; Operand value = null;
@ -466,21 +431,10 @@ namespace ARMeilleure.Instructions
{ {
switch (size) switch (size)
{ {
case 0: case 0: value = context.VectorExtract8 (GetVec(rt), elem); break;
value = context.VectorExtract8(GetVec(rt), elem); case 1: value = context.VectorExtract16(GetVec(rt), elem); break;
break; case 2: value = context.VectorExtract (OperandType.I32, GetVec(rt), elem); break;
case 3: value = context.VectorExtract (OperandType.I64, 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 else
@ -488,7 +442,7 @@ namespace ARMeilleure.Instructions
value = GetVec(rt); value = GetVec(rt);
} }
context.Call(fallbackMethodDlg, address, value); context.Call(info, address, value);
} }
private static Operand GetInt(ArmEmitterContext context, int rt) private static Operand GetInt(ArmEmitterContext context, int rt)

View file

@ -6,6 +6,7 @@ using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.State; using ARMeilleure.State;
using ARMeilleure.Translation; using ARMeilleure.Translation;
using System; using System;
using System.Diagnostics;
using static ARMeilleure.Instructions.InstEmitHelper; using static ARMeilleure.Instructions.InstEmitHelper;
using static ARMeilleure.Instructions.InstEmitSimdHelper; using static ARMeilleure.Instructions.InstEmitSimdHelper;
@ -106,7 +107,7 @@ namespace ARMeilleure.Instructions
{ {
Operand ne = EmitVectorExtractZx(context, op.Rn, index, op.Size); 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); 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 ne = EmitVectorExtractZx(context, op.Rn, index, op.Size);
Operand de; Operand de = context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.CountLeadingZeros)), ne, Const(eSize));
if (eSize == 64)
{
de = context.CountLeadingZeros(ne);
}
else
{
de = context.Call(new _U64_U64_S32(SoftFallback.CountLeadingZeros), ne, Const(eSize));
}
res = EmitVectorInsert(context, res, de, index, op.Size); res = EmitVectorInsert(context, res, de, index, op.Size);
} }
@ -165,7 +157,7 @@ namespace ARMeilleure.Instructions
} }
else 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); res = EmitVectorInsert(context, res, de, index, 0);
@ -203,9 +195,9 @@ namespace ARMeilleure.Instructions
{ {
EmitScalarBinaryOpF(context, (op1, op2) => 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) => 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) => 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) => 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) => 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) => 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 ne0 = context.VectorExtract(type, GetVec(op.Rn), 0);
Operand ne1 = context.VectorExtract(type, GetVec(op.Rn), 1); 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)); context.Copy(GetVec(op.Rd), context.VectorInsert(context.VectorZero(), res, 0));
} }
@ -396,7 +388,7 @@ namespace ARMeilleure.Instructions
{ {
EmitVectorPairwiseOpF(context, (op1, op2) => 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) => 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) => 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) => 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) => 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) => 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) => 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) => 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) => 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) => 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) => 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) => 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) => 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) => 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) => 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) => 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) => 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) => 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) => 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) => 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) => 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) => 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) => 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) => 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) => 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) => 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) => 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) => 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) => 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) => 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) => 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) => 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) => 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) => 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) => 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) 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) */ 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) 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) */ 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) => 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) => 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) => 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) => 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) 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) */ 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) 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) */ 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) => 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) => 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) => 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) => 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) => 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) => 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) => 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) => 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) => 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) => 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) if (Optimizations.UseSse41)
{ {
EmitSse41Mul_AddSub(context, AddSub.Add); EmitSse41VectorMul_AddSub(context, AddSub.Add);
} }
else else
{ {
@ -1713,7 +1705,7 @@ namespace ARMeilleure.Instructions
{ {
if (Optimizations.UseSse41) if (Optimizations.UseSse41)
{ {
EmitSse41Mul_AddSub(context, AddSub.Subtract); EmitSse41VectorMul_AddSub(context, AddSub.Subtract);
} }
else else
{ {
@ -1736,7 +1728,7 @@ namespace ARMeilleure.Instructions
{ {
if (Optimizations.UseSse41) if (Optimizations.UseSse41)
{ {
EmitSse41Mul_AddSub(context, AddSub.None); EmitSse41VectorMul_AddSub(context, AddSub.None);
} }
else else
{ {
@ -1805,14 +1797,14 @@ namespace ARMeilleure.Instructions
public static void Sabd_V(ArmEmitterContext context) public static void Sabd_V(ArmEmitterContext context)
{ {
if (Optimizations.UseSse2) if (Optimizations.UseSse41)
{ {
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
Operand n = GetVec(op.Rn); Operand n = GetVec(op.Rn);
Operand m = GetVec(op.Rm); Operand m = GetVec(op.Rm);
EmitSse41Sabd(context, op, n, m, isLong: false); EmitSse41VectorSabdOp(context, op, n, m, isLong: false);
} }
else else
{ {
@ -1845,7 +1837,7 @@ namespace ARMeilleure.Instructions
n = context.AddIntrinsic(movInst, n); n = context.AddIntrinsic(movInst, n);
m = context.AddIntrinsic(movInst, m); m = context.AddIntrinsic(movInst, m);
EmitSse41Sabd(context, op, n, m, isLong: true); EmitSse41VectorSabdOp(context, op, n, m, isLong: true);
} }
else else
{ {
@ -2027,9 +2019,7 @@ namespace ARMeilleure.Instructions
} }
else else
{ {
Delegate dlg = new _S64_S64_S64(Math.Max); EmitVectorBinaryOpSx(context, (op1, op2) => EmitMax64Op(context, op1, op2, signed: true));
EmitVectorBinaryOpSx(context, (op1, op2) => context.Call(dlg, op1, op2));
} }
} }
@ -2041,17 +2031,13 @@ namespace ARMeilleure.Instructions
} }
else else
{ {
Delegate dlg = new _S64_S64_S64(Math.Max); EmitVectorPairwiseOpSx(context, (op1, op2) => EmitMax64Op(context, op1, op2, signed: true));
EmitVectorPairwiseOpSx(context, (op1, op2) => context.Call(dlg, op1, op2));
} }
} }
public static void Smaxv_V(ArmEmitterContext context) public static void Smaxv_V(ArmEmitterContext context)
{ {
Delegate dlg = new _S64_S64_S64(Math.Max); EmitVectorAcrossVectorOpSx(context, (op1, op2) => EmitMax64Op(context, op1, op2, signed: true));
EmitVectorAcrossVectorOpSx(context, (op1, op2) => context.Call(dlg, op1, op2));
} }
public static void Smin_V(ArmEmitterContext context) public static void Smin_V(ArmEmitterContext context)
@ -2076,9 +2062,7 @@ namespace ARMeilleure.Instructions
} }
else else
{ {
Delegate dlg = new _S64_S64_S64(Math.Min); EmitVectorBinaryOpSx(context, (op1, op2) => EmitMin64Op(context, op1, op2, signed: true));
EmitVectorBinaryOpSx(context, (op1, op2) => context.Call(dlg, op1, op2));
} }
} }
@ -2090,17 +2074,13 @@ namespace ARMeilleure.Instructions
} }
else else
{ {
Delegate dlg = new _S64_S64_S64(Math.Min); EmitVectorPairwiseOpSx(context, (op1, op2) => EmitMin64Op(context, op1, op2, signed: true));
EmitVectorPairwiseOpSx(context, (op1, op2) => context.Call(dlg, op1, op2));
} }
} }
public static void Sminv_V(ArmEmitterContext context) public static void Sminv_V(ArmEmitterContext context)
{ {
Delegate dlg = new _S64_S64_S64(Math.Min); EmitVectorAcrossVectorOpSx(context, (op1, op2) => EmitMin64Op(context, op1, op2, signed: true));
EmitVectorAcrossVectorOpSx(context, (op1, op2) => context.Call(dlg, op1, op2));
} }
public static void Smlal_V(ArmEmitterContext context) public static void Smlal_V(ArmEmitterContext context)
@ -2458,7 +2438,7 @@ namespace ARMeilleure.Instructions
Operand n = GetVec(op.Rn); Operand n = GetVec(op.Rn);
Operand m = GetVec(op.Rm); Operand m = GetVec(op.Rm);
EmitSse41Uabd(context, op, n, m, isLong: false); EmitSse41VectorUabdOp(context, op, n, m, isLong: false);
} }
else else
{ {
@ -2491,7 +2471,7 @@ namespace ARMeilleure.Instructions
n = context.AddIntrinsic(movInst, n); n = context.AddIntrinsic(movInst, n);
m = context.AddIntrinsic(movInst, m); m = context.AddIntrinsic(movInst, m);
EmitSse41Uabd(context, op, n, m, isLong: true); EmitSse41VectorUabdOp(context, op, n, m, isLong: true);
} }
else else
{ {
@ -2666,9 +2646,7 @@ namespace ARMeilleure.Instructions
} }
else else
{ {
Delegate dlg = new _U64_U64_U64(Math.Max); EmitVectorBinaryOpZx(context, (op1, op2) => EmitMax64Op(context, op1, op2, signed: false));
EmitVectorBinaryOpZx(context, (op1, op2) => context.Call(dlg, op1, op2));
} }
} }
@ -2680,17 +2658,13 @@ namespace ARMeilleure.Instructions
} }
else else
{ {
Delegate dlg = new _U64_U64_U64(Math.Max); EmitVectorPairwiseOpZx(context, (op1, op2) => EmitMax64Op(context, op1, op2, signed: false));
EmitVectorPairwiseOpZx(context, (op1, op2) => context.Call(dlg, op1, op2));
} }
} }
public static void Umaxv_V(ArmEmitterContext context) public static void Umaxv_V(ArmEmitterContext context)
{ {
Delegate dlg = new _U64_U64_U64(Math.Max); EmitVectorAcrossVectorOpZx(context, (op1, op2) => EmitMax64Op(context, op1, op2, signed: false));
EmitVectorAcrossVectorOpZx(context, (op1, op2) => context.Call(dlg, op1, op2));
} }
public static void Umin_V(ArmEmitterContext context) public static void Umin_V(ArmEmitterContext context)
@ -2715,9 +2689,7 @@ namespace ARMeilleure.Instructions
} }
else else
{ {
Delegate dlg = new _U64_U64_U64(Math.Min); EmitVectorBinaryOpZx(context, (op1, op2) => EmitMin64Op(context, op1, op2, signed: false));
EmitVectorBinaryOpZx(context, (op1, op2) => context.Call(dlg, op1, op2));
} }
} }
@ -2729,17 +2701,13 @@ namespace ARMeilleure.Instructions
} }
else else
{ {
Delegate dlg = new _U64_U64_U64(Math.Min); EmitVectorPairwiseOpZx(context, (op1, op2) => EmitMin64Op(context, op1, op2, signed: false));
EmitVectorPairwiseOpZx(context, (op1, op2) => context.Call(dlg, op1, op2));
} }
} }
public static void Uminv_V(ArmEmitterContext context) public static void Uminv_V(ArmEmitterContext context)
{ {
Delegate dlg = new _U64_U64_U64(Math.Min); EmitVectorAcrossVectorOpZx(context, (op1, op2) => EmitMin64Op(context, op1, op2, signed: false));
EmitVectorAcrossVectorOpZx(context, (op1, op2) => context.Call(dlg, op1, op2));
} }
public static void Umlal_V(ArmEmitterContext context) public static void Umlal_V(ArmEmitterContext context)
@ -3081,7 +3049,29 @@ namespace ARMeilleure.Instructions
context.Copy(d, res); 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; OpCodeSimd op = (OpCodeSimd)context.CurrOp;
@ -3103,7 +3093,7 @@ namespace ARMeilleure.Instructions
context.Copy(GetVec(op.Rd), res); 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; OpCodeSimd op = (OpCodeSimd)context.CurrOp;
@ -3220,14 +3210,14 @@ namespace ARMeilleure.Instructions
Subtract Subtract
} }
private static void EmitSse41Mul_AddSub(ArmEmitterContext context, AddSub addSub) private static void EmitSse41VectorMul_AddSub(ArmEmitterContext context, AddSub addSub)
{ {
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
Operand n = GetVec(op.Rn); Operand n = GetVec(op.Rn);
Operand m = GetVec(op.Rm); Operand m = GetVec(op.Rm);
Operand res = null; Operand res;
if (op.Size == 0) if (op.Size == 0)
{ {
@ -3257,23 +3247,15 @@ namespace ARMeilleure.Instructions
if (addSub == AddSub.Add) if (addSub == AddSub.Add)
{ {
switch (op.Size) Intrinsic addInst = X86PaddInstruction[op.Size];
{
case 0: res = context.AddIntrinsic(Intrinsic.X86Paddb, d, res); break; res = context.AddIntrinsic(addInst, d, res);
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;
}
} }
else if (addSub == AddSub.Subtract) else if (addSub == AddSub.Subtract)
{ {
switch (op.Size) Intrinsic subInst = X86PsubInstruction[op.Size];
{
case 0: res = context.AddIntrinsic(Intrinsic.X86Psubb, d, res); break; res = context.AddIntrinsic(subInst, d, res);
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;
}
} }
if (op.RegisterSize == RegisterSize.Simd64) if (op.RegisterSize == RegisterSize.Simd64)
@ -3284,7 +3266,7 @@ namespace ARMeilleure.Instructions
context.Copy(d, res); context.Copy(d, res);
} }
private static void EmitSse41Sabd( private static void EmitSse41VectorSabdOp(
ArmEmitterContext context, ArmEmitterContext context,
OpCodeSimdReg op, OpCodeSimdReg op,
Operand n, Operand n,
@ -3317,7 +3299,7 @@ namespace ARMeilleure.Instructions
context.Copy(GetVec(op.Rd), res); context.Copy(GetVec(op.Rd), res);
} }
private static void EmitSse41Uabd( private static void EmitSse41VectorUabdOp(
ArmEmitterContext context, ArmEmitterContext context,
OpCodeSimdReg op, OpCodeSimdReg op,
Operand n, Operand n,

View file

@ -27,7 +27,7 @@ namespace ARMeilleure.Instructions
} }
else 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 else
{ {
EmitVectorUnaryOpF32(context, (op1) => EmitUnaryMathCall(context, MathF.Abs, Math.Abs, op1)); EmitVectorUnaryOpF32(context, (op1) => EmitUnaryMathCall(context, nameof(Math.Abs), op1));
} }
} }
else else
@ -74,7 +74,7 @@ namespace ARMeilleure.Instructions
} }
else 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 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) => 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) => 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) => 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 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 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 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 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) => 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) => 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) => 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) => 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 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 else
@ -650,7 +650,7 @@ namespace ARMeilleure.Instructions
{ {
EmitScalarTernaryOpF32(context, (op1, op2, op3) => 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) => 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 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 else
@ -725,7 +725,7 @@ namespace ARMeilleure.Instructions
{ {
EmitScalarBinaryOpF32(context, (op1, op2) => 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) => 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 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 else
@ -938,7 +938,7 @@ namespace ARMeilleure.Instructions
{ {
EmitVectorUnaryOpF32(context, (op1) => 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) => 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) => 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) => 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) => EmitScalarUnaryOpF32(context, (op1) =>
{ {
return EmitSoftFloatCall(context, SoftFloat32.FPSqrt, SoftFloat64.FPSqrt, op1); return EmitSoftFloatCall(context, nameof(SoftFloat32.FPSqrt), op1);
}); });
} }
} }

View file

@ -288,49 +288,49 @@ namespace ARMeilleure.Instructions
public static void Facge_S(ArmEmitterContext context) 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 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) 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 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) 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 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) 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 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) if (Optimizations.FastFP && Optimizations.UseSse2)
{ {
EmitSse2CmpOpF(context, CmpCondition.Equal, scalar: true); EmitSse2OrAvxCmpOpF(context, CmpCondition.Equal, scalar: true);
} }
else 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) if (Optimizations.FastFP && Optimizations.UseSse2)
{ {
EmitSse2CmpOpF(context, CmpCondition.Equal, scalar: false); EmitSse2OrAvxCmpOpF(context, CmpCondition.Equal, scalar: false);
} }
else 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) if (Optimizations.FastFP && Optimizations.UseAvx)
{ {
EmitSse2CmpOpF(context, CmpCondition.GreaterThanOrEqual, scalar: true); EmitSse2OrAvxCmpOpF(context, CmpCondition.GreaterThanOrEqual, scalar: true);
} }
else 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) if (Optimizations.FastFP && Optimizations.UseAvx)
{ {
EmitSse2CmpOpF(context, CmpCondition.GreaterThanOrEqual, scalar: false); EmitSse2OrAvxCmpOpF(context, CmpCondition.GreaterThanOrEqual, scalar: false);
} }
else 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) if (Optimizations.FastFP && Optimizations.UseAvx)
{ {
EmitSse2CmpOpF(context, CmpCondition.GreaterThan, scalar: true); EmitSse2OrAvxCmpOpF(context, CmpCondition.GreaterThan, scalar: true);
} }
else 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) if (Optimizations.FastFP && Optimizations.UseAvx)
{ {
EmitSse2CmpOpF(context, CmpCondition.GreaterThan, scalar: false); EmitSse2OrAvxCmpOpF(context, CmpCondition.GreaterThan, scalar: false);
} }
else 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) if (Optimizations.FastFP && Optimizations.UseSse2)
{ {
EmitSse2CmpOpF(context, CmpCondition.LessThanOrEqual, scalar: true); EmitSse2OrAvxCmpOpF(context, CmpCondition.LessThanOrEqual, scalar: true);
} }
else 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) if (Optimizations.FastFP && Optimizations.UseSse2)
{ {
EmitSse2CmpOpF(context, CmpCondition.LessThanOrEqual, scalar: false); EmitSse2OrAvxCmpOpF(context, CmpCondition.LessThanOrEqual, scalar: false);
} }
else 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) if (Optimizations.FastFP && Optimizations.UseSse2)
{ {
EmitSse2CmpOpF(context, CmpCondition.LessThan, scalar: true); EmitSse2OrAvxCmpOpF(context, CmpCondition.LessThan, scalar: true);
} }
else 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) if (Optimizations.FastFP && Optimizations.UseSse2)
{ {
EmitSse2CmpOpF(context, CmpCondition.LessThan, scalar: false); EmitSse2OrAvxCmpOpF(context, CmpCondition.LessThan, scalar: false);
} }
else 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); me = context.VectorExtract(type, GetVec(op.Rm), 0);
} }
Delegate dlg = op.Size != 0 Operand nzcv = EmitSoftFloatCall(context, nameof(SoftFloat32.FPCompare), ne, me, Const(signalNaNs));
? (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));
EmitSetNzcv(context, nzcv); EmitSetNzcv(context, nzcv);
} }
@ -683,12 +679,7 @@ namespace ARMeilleure.Instructions
context.Copy(GetVec(op.Rd), res); context.Copy(GetVec(op.Rd), res);
} }
private static void EmitCmpOpF( private static void EmitCmpOpF(ArmEmitterContext context, string name, bool scalar, bool absolute = false)
ArmEmitterContext context,
_F32_F32_F32 f32,
_F64_F64_F64 f64,
bool scalar,
bool absolute = false)
{ {
OpCodeSimd op = (OpCodeSimd)context.CurrOp; OpCodeSimd op = (OpCodeSimd)context.CurrOp;
@ -716,11 +707,11 @@ namespace ARMeilleure.Instructions
if (absolute) if (absolute)
{ {
ne = EmitUnaryMathCall(context, MathF.Abs, Math.Abs, ne); ne = EmitUnaryMathCall(context, nameof(Math.Abs), ne);
me = EmitUnaryMathCall(context, MathF.Abs, Math.Abs, me); 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); res = context.VectorInsert(res, e, index);
} }
@ -728,7 +719,7 @@ namespace ARMeilleure.Instructions
context.Copy(GetVec(op.Rd), res); 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; OpCodeSimd op = (OpCodeSimd)context.CurrOp;

View file

@ -3,6 +3,7 @@ using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.State; using ARMeilleure.State;
using ARMeilleure.Translation; using ARMeilleure.Translation;
using System; using System;
using System.Reflection;
using static ARMeilleure.Instructions.InstEmitHelper; using static ARMeilleure.Instructions.InstEmitHelper;
using static ARMeilleure.Instructions.InstEmitSimdHelper; using static ARMeilleure.Instructions.InstEmitSimdHelper;
@ -19,11 +20,11 @@ namespace ARMeilleure.Instructions
{ {
if (Optimizations.FastFP && Optimizations.UseSse2) if (Optimizations.FastFP && Optimizations.UseSse2)
{ {
EmitSse2CmpOpF32(context, CmpCondition.Equal, false); EmitSse2OrAvxCmpOpF32(context, CmpCondition.Equal, false);
} }
else 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) if (Optimizations.FastFP && Optimizations.UseSse2)
{ {
EmitSse2CmpOpF32(context, CmpCondition.Equal, true); EmitSse2OrAvxCmpOpF32(context, CmpCondition.Equal, true);
} }
else else
{ {
EmitCmpOpF32(context, SoftFloat32.FPCompareEQFpscr, SoftFloat64.FPCompareEQFpscr, true); EmitCmpOpF32(context, nameof(SoftFloat32.FPCompareEQFpscr), true);
} }
} }
else else
@ -55,13 +56,13 @@ namespace ARMeilleure.Instructions
public static void Vcge_V(ArmEmitterContext context) 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 else
{ {
EmitCmpOpF32(context, SoftFloat32.FPCompareGEFpscr, SoftFloat64.FPCompareGEFpscr, false); EmitCmpOpF32(context, nameof(SoftFloat32.FPCompareGEFpscr), false);
} }
} }
@ -78,13 +79,13 @@ namespace ARMeilleure.Instructions
if (op.F) if (op.F)
{ {
if (Optimizations.FastFP && Optimizations.UseSse2) if (Optimizations.FastFP && Optimizations.UseAvx)
{ {
EmitSse2CmpOpF32(context, CmpCondition.GreaterThanOrEqual, true); EmitSse2OrAvxCmpOpF32(context, CmpCondition.GreaterThanOrEqual, true);
} }
else else
{ {
EmitCmpOpF32(context, SoftFloat32.FPCompareGEFpscr, SoftFloat64.FPCompareGEFpscr, true); EmitCmpOpF32(context, nameof(SoftFloat32.FPCompareGEFpscr), true);
} }
} }
else else
@ -95,13 +96,13 @@ namespace ARMeilleure.Instructions
public static void Vcgt_V(ArmEmitterContext context) 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 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 (op.F)
{ {
if (Optimizations.FastFP && Optimizations.UseSse2) if (Optimizations.FastFP && Optimizations.UseAvx)
{ {
EmitSse2CmpOpF32(context, CmpCondition.GreaterThan, true); EmitSse2OrAvxCmpOpF32(context, CmpCondition.GreaterThan, true);
} }
else else
{ {
EmitCmpOpF32(context, SoftFloat32.FPCompareGTFpscr, SoftFloat64.FPCompareGTFpscr, true); EmitCmpOpF32(context, nameof(SoftFloat32.FPCompareGTFpscr), true);
} }
} }
else else
@ -141,11 +142,11 @@ namespace ARMeilleure.Instructions
{ {
if (Optimizations.FastFP && Optimizations.UseSse2) if (Optimizations.FastFP && Optimizations.UseSse2)
{ {
EmitSse2CmpOpF32(context, CmpCondition.LessThanOrEqual, true); EmitSse2OrAvxCmpOpF32(context, CmpCondition.LessThanOrEqual, true);
} }
else else
{ {
EmitCmpOpF32(context, SoftFloat32.FPCompareLEFpscr, SoftFloat64.FPCompareLEFpscr, true); EmitCmpOpF32(context, nameof(SoftFloat32.FPCompareLEFpscr), true);
} }
} }
else else
@ -162,11 +163,11 @@ namespace ARMeilleure.Instructions
{ {
if (Optimizations.FastFP && Optimizations.UseSse2) if (Optimizations.FastFP && Optimizations.UseSse2)
{ {
EmitSse2CmpOpF32(context, CmpCondition.LessThan, true); EmitSse2OrAvxCmpOpF32(context, CmpCondition.LessThan, true);
} }
else else
{ {
EmitCmpOpF32(context, SoftFloat32.FPCompareLTFpscr, SoftFloat64.FPCompareLTFpscr, true); EmitCmpOpF32(context, nameof(SoftFloat32.FPCompareLTFpscr), true);
} }
} }
else else
@ -175,11 +176,7 @@ namespace ARMeilleure.Instructions
} }
} }
private static void EmitCmpOpF32( private static void EmitCmpOpF32(ArmEmitterContext context, string name, bool zero)
ArmEmitterContext context,
_F32_F32_F32_Bool f32,
_F64_F64_F64_Bool f64,
bool zero)
{ {
Operand one = Const(1); Operand one = Const(1);
if (zero) if (zero)
@ -190,11 +187,11 @@ namespace ARMeilleure.Instructions
if (type == OperandType.FP64) 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 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) if (type == OperandType.FP64)
{ {
return context.Call(f64, n, m, one); return context.Call(typeof(SoftFloat64).GetMethod(name), n, m, one);
} }
else else
{ {
return context.Call(f32, n, m, one); return context.Call(typeof(SoftFloat32).GetMethod(name), n, m, one);
} }
}); });
} }
@ -351,11 +348,11 @@ namespace ARMeilleure.Instructions
me = ExtractScalar(context, type, op.Vm); me = ExtractScalar(context, type, op.Vm);
} }
Delegate dlg = sizeF != 0 MethodInfo info = sizeF != 0
? (Delegate)new _S32_F64_F64_Bool(SoftFloat64.FPCompare) ? typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompare))
: (Delegate)new _S32_F32_F32_Bool(SoftFloat32.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); EmitSetFPSCRFlags(context, nzcv);
} }
@ -389,7 +386,7 @@ namespace ARMeilleure.Instructions
SetFpFlag(context, FPState.NFlag, n); 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; OpCode32Simd op = (OpCode32Simd)context.CurrOp;

View file

@ -16,13 +16,14 @@ namespace ARMeilleure.Instructions
Operand n = GetVec(op.Rn); Operand n = GetVec(op.Rn);
Operand res; Operand res;
if (Optimizations.UseAesni) if (Optimizations.UseAesni)
{ {
res = context.AddIntrinsic(Intrinsic.X86Aesdeclast, context.AddIntrinsic(Intrinsic.X86Xorpd, d, n), context.VectorZero()); res = context.AddIntrinsic(Intrinsic.X86Aesdeclast, context.AddIntrinsic(Intrinsic.X86Xorpd, d, n), context.VectorZero());
} }
else 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); context.Copy(d, res);
@ -36,13 +37,14 @@ namespace ARMeilleure.Instructions
Operand n = GetVec(op.Rn); Operand n = GetVec(op.Rn);
Operand res; Operand res;
if (Optimizations.UseAesni) if (Optimizations.UseAesni)
{ {
res = context.AddIntrinsic(Intrinsic.X86Aesenclast, context.AddIntrinsic(Intrinsic.X86Xorpd, d, n), context.VectorZero()); res = context.AddIntrinsic(Intrinsic.X86Aesenclast, context.AddIntrinsic(Intrinsic.X86Xorpd, d, n), context.VectorZero());
} }
else 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); context.Copy(d, res);
@ -55,13 +57,14 @@ namespace ARMeilleure.Instructions
Operand n = GetVec(op.Rn); Operand n = GetVec(op.Rn);
Operand res; Operand res;
if (Optimizations.UseAesni) if (Optimizations.UseAesni)
{ {
res = context.AddIntrinsic(Intrinsic.X86Aesimc, n); res = context.AddIntrinsic(Intrinsic.X86Aesimc, n);
} }
else 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); context.Copy(GetVec(op.Rd), res);
@ -74,6 +77,7 @@ namespace ARMeilleure.Instructions
Operand n = GetVec(op.Rn); Operand n = GetVec(op.Rn);
Operand res; Operand res;
if (Optimizations.UseAesni) if (Optimizations.UseAesni)
{ {
Operand roundKey = context.VectorZero(); Operand roundKey = context.VectorZero();
@ -86,7 +90,7 @@ namespace ARMeilleure.Instructions
} }
else 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); context.Copy(GetVec(op.Rd), res);

View file

@ -16,13 +16,14 @@ namespace ARMeilleure.Instructions
Operand n = GetVecA32(op.Qm); Operand n = GetVecA32(op.Qm);
Operand res; Operand res;
if (Optimizations.UseAesni) if (Optimizations.UseAesni)
{ {
res = context.AddIntrinsic(Intrinsic.X86Aesdeclast, context.AddIntrinsic(Intrinsic.X86Xorpd, d, n), context.VectorZero()); res = context.AddIntrinsic(Intrinsic.X86Aesdeclast, context.AddIntrinsic(Intrinsic.X86Xorpd, d, n), context.VectorZero());
} }
else 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); context.Copy(d, res);
@ -36,13 +37,14 @@ namespace ARMeilleure.Instructions
Operand n = GetVecA32(op.Qm); Operand n = GetVecA32(op.Qm);
Operand res; Operand res;
if (Optimizations.UseAesni) if (Optimizations.UseAesni)
{ {
res = context.AddIntrinsic(Intrinsic.X86Aesenclast, context.AddIntrinsic(Intrinsic.X86Xorpd, d, n), context.VectorZero()); res = context.AddIntrinsic(Intrinsic.X86Aesenclast, context.AddIntrinsic(Intrinsic.X86Xorpd, d, n), context.VectorZero());
} }
else 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); context.Copy(d, res);
@ -55,13 +57,14 @@ namespace ARMeilleure.Instructions
Operand n = GetVecA32(op.Qm); Operand n = GetVecA32(op.Qm);
Operand res; Operand res;
if (Optimizations.UseAesni) if (Optimizations.UseAesni)
{ {
res = context.AddIntrinsic(Intrinsic.X86Aesimc, n); res = context.AddIntrinsic(Intrinsic.X86Aesimc, n);
} }
else 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); context.Copy(GetVecA32(op.Qd), res);
@ -74,6 +77,7 @@ namespace ARMeilleure.Instructions
Operand n = GetVecA32(op.Qm); Operand n = GetVecA32(op.Qm);
Operand res; Operand res;
if (Optimizations.UseAesni) if (Optimizations.UseAesni)
{ {
Operand roundKey = context.VectorZero(); Operand roundKey = context.VectorZero();
@ -86,7 +90,7 @@ namespace ARMeilleure.Instructions
} }
else 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); context.Copy(GetVecA32(op.Qd), res);

View file

@ -4,6 +4,7 @@ using ARMeilleure.State;
using ARMeilleure.Translation; using ARMeilleure.Translation;
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Reflection;
using static ARMeilleure.Instructions.InstEmitHelper; using static ARMeilleure.Instructions.InstEmitHelper;
using static ARMeilleure.Instructions.InstEmitSimdHelper; using static ARMeilleure.Instructions.InstEmitSimdHelper;
@ -61,9 +62,7 @@ namespace ARMeilleure.Instructions
{ {
Operand ne = context.VectorExtract(OperandType.FP32, GetVec(op.Rn), 0); Operand ne = context.VectorExtract(OperandType.FP32, GetVec(op.Rn), 0);
Delegate dlg = new _U16_F32(SoftFloat32_16.FPConvert); Operand res = context.Call(typeof(SoftFloat32_16).GetMethod(nameof(SoftFloat32_16.FPConvert)), ne);
Operand res = context.Call(dlg, ne);
res = context.ZeroExtend16(OperandType.I64, res); res = context.ZeroExtend16(OperandType.I64, res);
@ -73,9 +72,7 @@ namespace ARMeilleure.Instructions
{ {
Operand ne = EmitVectorExtractZx(context, op.Rn, 0, 1); Operand ne = EmitVectorExtractZx(context, op.Rn, 0, 1);
Delegate dlg = new _F32_U16(SoftFloat16_32.FPConvert); Operand res = context.Call(typeof(SoftFloat16_32).GetMethod(nameof(SoftFloat16_32.FPConvert)), ne);
Operand res = context.Call(dlg, ne);
context.Copy(GetVec(op.Rd), context.VectorInsert(context.VectorZero(), res, 0)); 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); Operand ne = EmitVectorExtractZx(context, op.Rn, part + index, 1);
Delegate dlg = new _F32_U16(SoftFloat16_32.FPConvert); Operand e = context.Call(typeof(SoftFloat16_32).GetMethod(nameof(SoftFloat16_32.FPConvert)), ne);
Operand e = context.Call(dlg, ne);
res = context.VectorInsert(res, e, index); res = context.VectorInsert(res, e, index);
} }
@ -189,7 +184,7 @@ namespace ARMeilleure.Instructions
} }
else 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 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) if (sizeF == 0)
{ {
Delegate dlg = new _U16_F32(SoftFloat32_16.FPConvert); Operand e = context.Call(typeof(SoftFloat32_16).GetMethod(nameof(SoftFloat32_16.FPConvert)), ne);
Operand e = context.Call(dlg, ne);
e = context.ZeroExtend16(OperandType.I64, e); e = context.ZeroExtend16(OperandType.I64, e);
@ -271,7 +264,7 @@ namespace ARMeilleure.Instructions
{ {
if (Optimizations.UseSse41) if (Optimizations.UseSse41)
{ {
EmitSse41Fcvts(context, FPRoundingMode.ToNearest, scalar: true); EmitSse41FcvtsOpF(context, FPRoundingMode.ToNearest, scalar: true);
} }
else else
{ {
@ -283,7 +276,7 @@ namespace ARMeilleure.Instructions
{ {
if (Optimizations.UseSse41) if (Optimizations.UseSse41)
{ {
EmitSse41Fcvts(context, FPRoundingMode.ToNearest, scalar: false); EmitSse41FcvtsOpF(context, FPRoundingMode.ToNearest, scalar: false);
} }
else else
{ {
@ -295,7 +288,7 @@ namespace ARMeilleure.Instructions
{ {
if (Optimizations.UseSse41) if (Optimizations.UseSse41)
{ {
EmitSse41Fcvtu(context, FPRoundingMode.ToNearest, scalar: true); EmitSse41FcvtuOpF(context, FPRoundingMode.ToNearest, scalar: true);
} }
else else
{ {
@ -307,7 +300,7 @@ namespace ARMeilleure.Instructions
{ {
if (Optimizations.UseSse41) if (Optimizations.UseSse41)
{ {
EmitSse41Fcvtu(context, FPRoundingMode.ToNearest, scalar: false); EmitSse41FcvtuOpF(context, FPRoundingMode.ToNearest, scalar: false);
} }
else else
{ {
@ -323,7 +316,7 @@ namespace ARMeilleure.Instructions
} }
else 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 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) if (Optimizations.UseSse41)
{ {
EmitSse41Fcvts(context, FPRoundingMode.TowardsZero, scalar: true); EmitSse41FcvtsOpF(context, FPRoundingMode.TowardsZero, scalar: true);
} }
else else
{ {
@ -379,7 +372,7 @@ namespace ARMeilleure.Instructions
{ {
if (Optimizations.UseSse41) if (Optimizations.UseSse41)
{ {
EmitSse41Fcvts(context, FPRoundingMode.TowardsZero, scalar: false); EmitSse41FcvtsOpF(context, FPRoundingMode.TowardsZero, scalar: false);
} }
else else
{ {
@ -391,7 +384,7 @@ namespace ARMeilleure.Instructions
{ {
if (Optimizations.UseSse41) if (Optimizations.UseSse41)
{ {
EmitSse41Fcvts(context, FPRoundingMode.TowardsZero, scalar: false); EmitSse41FcvtsOpF(context, FPRoundingMode.TowardsZero, scalar: false);
} }
else else
{ {
@ -427,7 +420,7 @@ namespace ARMeilleure.Instructions
{ {
if (Optimizations.UseSse41) if (Optimizations.UseSse41)
{ {
EmitSse41Fcvtu(context, FPRoundingMode.TowardsZero, scalar: true); EmitSse41FcvtuOpF(context, FPRoundingMode.TowardsZero, scalar: true);
} }
else else
{ {
@ -439,7 +432,7 @@ namespace ARMeilleure.Instructions
{ {
if (Optimizations.UseSse41) if (Optimizations.UseSse41)
{ {
EmitSse41Fcvtu(context, FPRoundingMode.TowardsZero, scalar: false); EmitSse41FcvtuOpF(context, FPRoundingMode.TowardsZero, scalar: false);
} }
else else
{ {
@ -451,7 +444,7 @@ namespace ARMeilleure.Instructions
{ {
if (Optimizations.UseSse41) if (Optimizations.UseSse41)
{ {
EmitSse41Fcvtu(context, FPRoundingMode.TowardsZero, scalar: false); EmitSse41FcvtuOpF(context, FPRoundingMode.TowardsZero, scalar: false);
} }
else else
{ {
@ -497,7 +490,7 @@ namespace ARMeilleure.Instructions
{ {
if (Optimizations.UseSse2) if (Optimizations.UseSse2)
{ {
EmitSse2Scvtf(context, scalar: true); EmitSse2ScvtfOp(context, scalar: true);
} }
else else
{ {
@ -517,7 +510,7 @@ namespace ARMeilleure.Instructions
{ {
if (Optimizations.UseSse2) if (Optimizations.UseSse2)
{ {
EmitSse2Scvtf(context, scalar: false); EmitSse2ScvtfOp(context, scalar: false);
} }
else else
{ {
@ -529,7 +522,7 @@ namespace ARMeilleure.Instructions
{ {
if (Optimizations.UseSse2) if (Optimizations.UseSse2)
{ {
EmitSse2Scvtf(context, scalar: false); EmitSse2ScvtfOp(context, scalar: false);
} }
else else
{ {
@ -565,7 +558,7 @@ namespace ARMeilleure.Instructions
{ {
if (Optimizations.UseSse2) if (Optimizations.UseSse2)
{ {
EmitSse2Ucvtf(context, scalar: true); EmitSse2UcvtfOp(context, scalar: true);
} }
else else
{ {
@ -585,7 +578,7 @@ namespace ARMeilleure.Instructions
{ {
if (Optimizations.UseSse2) if (Optimizations.UseSse2)
{ {
EmitSse2Ucvtf(context, scalar: false); EmitSse2UcvtfOp(context, scalar: false);
} }
else else
{ {
@ -597,7 +590,7 @@ namespace ARMeilleure.Instructions
{ {
if (Optimizations.UseSse2) if (Optimizations.UseSse2)
{ {
EmitSse2Ucvtf(context, scalar: false); EmitSse2UcvtfOp(context, scalar: false);
} }
else else
{ {
@ -628,21 +621,21 @@ namespace ARMeilleure.Instructions
if (sizeF == 0) if (sizeF == 0)
{ {
Delegate dlg = signed MethodInfo info = signed
? (Delegate)new _S32_F32(SoftFallback.SatF32ToS32) ? typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToS32))
: (Delegate)new _U32_F32(SoftFallback.SatF32ToU32); : typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToU32));
e = context.Call(dlg, e); e = context.Call(info, e);
e = context.ZeroExtend32(OperandType.I64, e); e = context.ZeroExtend32(OperandType.I64, e);
} }
else /* if (sizeF == 1) */ else /* if (sizeF == 1) */
{ {
Delegate dlg = signed MethodInfo info = signed
? (Delegate)new _S64_F64(SoftFallback.SatF64ToS64) ? typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToS64))
: (Delegate)new _U64_F64(SoftFallback.SatF64ToU64); : typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToU64));
e = context.Call(dlg, e); e = context.Call(info, e);
} }
res = EmitVectorInsert(context, res, e, index, sizeI); res = EmitVectorInsert(context, res, e, index, sizeI);
@ -676,21 +669,21 @@ namespace ARMeilleure.Instructions
if (sizeF == 0) if (sizeF == 0)
{ {
Delegate dlg = signed MethodInfo info = signed
? (Delegate)new _S32_F32(SoftFallback.SatF32ToS32) ? typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToS32))
: (Delegate)new _U32_F32(SoftFallback.SatF32ToU32); : typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToU32));
e = context.Call(dlg, e); e = context.Call(info, e);
e = context.ZeroExtend32(OperandType.I64, e); e = context.ZeroExtend32(OperandType.I64, e);
} }
else /* if (sizeF == 1) */ else /* if (sizeF == 1) */
{ {
Delegate dlg = signed MethodInfo info = signed
? (Delegate)new _S64_F64(SoftFallback.SatF64ToS64) ? typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToS64))
: (Delegate)new _U64_F64(SoftFallback.SatF64ToU64); : typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToU64));
e = context.Call(dlg, e); e = context.Call(info, e);
} }
res = EmitVectorInsert(context, res, e, index, sizeI); res = EmitVectorInsert(context, res, e, index, sizeI);
@ -809,22 +802,22 @@ namespace ARMeilleure.Instructions
value = EmitF2iFBitsMul(context, value, fBits); value = EmitF2iFBitsMul(context, value, fBits);
MethodInfo info;
if (context.CurrOp.RegisterSize == RegisterSize.Int32) if (context.CurrOp.RegisterSize == RegisterSize.Int32)
{ {
Delegate dlg = value.Type == OperandType.FP32 info = value.Type == OperandType.FP32
? (Delegate)new _S32_F32(SoftFallback.SatF32ToS32) ? typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToS32))
: (Delegate)new _S32_F64(SoftFallback.SatF64ToS32); : typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToS32));
return context.Call(dlg, value);
} }
else else
{ {
Delegate dlg = value.Type == OperandType.FP32 info = value.Type == OperandType.FP32
? (Delegate)new _S64_F32(SoftFallback.SatF32ToS64) ? typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToS64))
: (Delegate)new _S64_F64(SoftFallback.SatF64ToS64); : typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToS64));
return context.Call(dlg, value);
} }
return context.Call(info, value);
} }
private static Operand EmitScalarFcvtu(ArmEmitterContext context, Operand value, int fBits) private static Operand EmitScalarFcvtu(ArmEmitterContext context, Operand value, int fBits)
@ -833,22 +826,22 @@ namespace ARMeilleure.Instructions
value = EmitF2iFBitsMul(context, value, fBits); value = EmitF2iFBitsMul(context, value, fBits);
MethodInfo info;
if (context.CurrOp.RegisterSize == RegisterSize.Int32) if (context.CurrOp.RegisterSize == RegisterSize.Int32)
{ {
Delegate dlg = value.Type == OperandType.FP32 info = value.Type == OperandType.FP32
? (Delegate)new _U32_F32(SoftFallback.SatF32ToU32) ? typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToU32))
: (Delegate)new _U32_F64(SoftFallback.SatF64ToU32); : typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToU32));
return context.Call(dlg, value);
} }
else else
{ {
Delegate dlg = value.Type == OperandType.FP32 info = value.Type == OperandType.FP32
? (Delegate)new _U64_F32(SoftFallback.SatF32ToU64) ? typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToU64))
: (Delegate)new _U64_F64(SoftFallback.SatF64ToU64); : typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToU64));
return context.Call(dlg, value);
} }
return context.Call(info, value);
} }
private static Operand EmitF2iFBitsMul(ArmEmitterContext context, Operand value, int fBits) private static Operand EmitF2iFBitsMul(ArmEmitterContext context, Operand value, int fBits)
@ -925,7 +918,7 @@ namespace ARMeilleure.Instructions
return res; return res;
} }
private static void EmitSse2Scvtf(ArmEmitterContext context, bool scalar) private static void EmitSse2ScvtfOp(ArmEmitterContext context, bool scalar)
{ {
OpCodeSimd op = (OpCodeSimd)context.CurrOp; 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; 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; 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; OpCodeSimd op = (OpCodeSimd)context.CurrOp;

View file

@ -4,6 +4,7 @@ using ARMeilleure.State;
using ARMeilleure.Translation; using ARMeilleure.Translation;
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Reflection;
using static ARMeilleure.Instructions.InstEmitHelper; using static ARMeilleure.Instructions.InstEmitHelper;
using static ARMeilleure.Instructions.InstEmitSimdHelper; using static ARMeilleure.Instructions.InstEmitSimdHelper;
@ -30,29 +31,22 @@ namespace ARMeilleure.Instructions
private static Operand EmitSaturateFloatToInt(ArmEmitterContext context, Operand op1, bool unsigned) private static Operand EmitSaturateFloatToInt(ArmEmitterContext context, Operand op1, bool unsigned)
{ {
MethodInfo info;
if (op1.Type == OperandType.FP64) if (op1.Type == OperandType.FP64)
{ {
if (unsigned) info = unsigned
{ ? typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToU32))
return context.Call(new _U32_F64(SoftFallback.SatF64ToU32), op1); : typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToS32));
} }
else else
{ {
return context.Call(new _S32_F64(SoftFallback.SatF64ToS32), op1); info = unsigned
? typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToU32))
: typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToS32));
} }
} return context.Call(info, op1);
else
{
if (unsigned)
{
return context.Call(new _U32_F32(SoftFallback.SatF32ToU32), op1);
}
else
{
return context.Call(new _S32_F32(SoftFallback.SatF32ToS32), op1);
}
}
} }
public static void Vcvt_V(ArmEmitterContext context) public static void Vcvt_V(ArmEmitterContext context)
@ -114,9 +108,7 @@ namespace ARMeilleure.Instructions
EmitVectorUnaryOpSx32(context, (op1) => EmitFPConvert(context, op1, floatSize, true)); EmitVectorUnaryOpSx32(context, (op1) => EmitFPConvert(context, op1, floatSize, true));
} }
} }
} }
} }
public static void Vcvt_FD(ArmEmitterContext context) public static void Vcvt_FD(ArmEmitterContext context)
@ -173,29 +165,22 @@ namespace ARMeilleure.Instructions
// TODO: Fast Path. // TODO: Fast Path.
if (roundWithFpscr) if (roundWithFpscr)
{ {
MethodInfo info;
if (floatSize == OperandType.FP64) if (floatSize == OperandType.FP64)
{ {
if (unsigned) info = unsigned
{ ? typeof(SoftFallback).GetMethod(nameof(SoftFallback.DoubleToUInt32))
asInteger = context.Call(new _U32_F64(SoftFallback.DoubleToUInt32), toConvert); : typeof(SoftFallback).GetMethod(nameof(SoftFallback.DoubleToInt32));
} }
else else
{ {
asInteger = context.Call(new _S32_F64(SoftFallback.DoubleToInt32), toConvert); info = unsigned
? typeof(SoftFallback).GetMethod(nameof(SoftFallback.FloatToUInt32))
: typeof(SoftFallback).GetMethod(nameof(SoftFallback.FloatToInt32));
} }
} asInteger = context.Call(info, toConvert);
else
{
if (unsigned)
{
asInteger = context.Call(new _U32_F32(SoftFallback.FloatToUInt32), toConvert);
}
else
{
asInteger = context.Call(new _S32_F32(SoftFallback.FloatToInt32), toConvert);
}
}
} }
else else
{ {
@ -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; IOpCode32Simd op = (IOpCode32Simd)context.CurrOp;
Delegate dlg; string name = nameof(Math.Round);
if ((op.Size & 1) == 0) MethodInfo info = (op.Size & 1) == 0
{ ? typeof(MathF).GetMethod(name, new Type[] { typeof(float), typeof(MidpointRounding) })
dlg = new _F32_F32_MidpointRounding(MathF.Round); : typeof(Math). GetMethod(name, new Type[] { typeof(double), typeof(MidpointRounding) });
}
else /* if ((op.Size & 1) == 1) */
{
dlg = new _F64_F64_MidpointRounding(Math.Round);
}
return context.Call(dlg, n, Const((int)roundMode)); return context.Call(info, n, Const((int)roundMode));
} }
private static FPRoundingMode RMToRoundMode(int rm) private static FPRoundingMode RMToRoundMode(int rm)
@ -282,10 +262,10 @@ namespace ARMeilleure.Instructions
toConvert = EmitRoundMathCall(context, MidpointRounding.ToEven, toConvert); toConvert = EmitRoundMathCall(context, MidpointRounding.ToEven, toConvert);
break; break;
case 0b10: // Towards positive infinity case 0b10: // Towards positive infinity
toConvert = EmitUnaryMathCall(context, MathF.Ceiling, Math.Ceiling, toConvert); toConvert = EmitUnaryMathCall(context, nameof(Math.Ceiling), toConvert);
break; break;
case 0b11: // Towards negative infinity case 0b11: // Towards negative infinity
toConvert = EmitUnaryMathCall(context, MathF.Floor, Math.Floor, toConvert); toConvert = EmitUnaryMathCall(context, nameof(Math.Floor), toConvert);
break; break;
} }
@ -329,10 +309,10 @@ namespace ARMeilleure.Instructions
toConvert = EmitRoundMathCall(context, MidpointRounding.ToEven, toConvert); toConvert = EmitRoundMathCall(context, MidpointRounding.ToEven, toConvert);
break; break;
case 0b10: // Towards positive infinity case 0b10: // Towards positive infinity
toConvert = EmitUnaryMathCall(context, MathF.Ceiling, Math.Ceiling, toConvert); toConvert = EmitUnaryMathCall(context, nameof(Math.Ceiling), toConvert);
break; break;
case 0b11: // Towards negative infinity case 0b11: // Towards negative infinity
toConvert = EmitUnaryMathCall(context, MathF.Floor, Math.Floor, toConvert); toConvert = EmitUnaryMathCall(context, nameof(Math.Floor), toConvert);
break; break;
} }
@ -354,7 +334,7 @@ namespace ARMeilleure.Instructions
} }
else else
{ {
EmitScalarUnaryOpF32(context, (op1) => EmitUnaryMathCall(context, MathF.Truncate, Math.Truncate, op1)); EmitScalarUnaryOpF32(context, (op1) => EmitUnaryMathCall(context, nameof(Math.Truncate), op1));
} }
} }

View file

@ -19,7 +19,7 @@ namespace ARMeilleure.Instructions
Operand m = GetVec(op.Rm); 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); context.Copy(GetVec(op.Rd), res);
} }
@ -30,7 +30,7 @@ namespace ARMeilleure.Instructions
Operand ne = context.VectorExtract(OperandType.I32, GetVec(op.Rn), 0); 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)); context.Copy(GetVec(op.Rd), context.VectorCreateScalar(res));
} }
@ -45,7 +45,7 @@ namespace ARMeilleure.Instructions
Operand m = GetVec(op.Rm); 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); context.Copy(GetVec(op.Rd), res);
} }
@ -60,7 +60,7 @@ namespace ARMeilleure.Instructions
Operand m = GetVec(op.Rm); 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); context.Copy(GetVec(op.Rd), res);
} }
@ -73,7 +73,7 @@ namespace ARMeilleure.Instructions
Operand n = GetVec(op.Rn); Operand n = GetVec(op.Rn);
Operand m = GetVec(op.Rm); 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); context.Copy(GetVec(op.Rd), res);
} }
@ -85,7 +85,7 @@ namespace ARMeilleure.Instructions
Operand d = GetVec(op.Rd); Operand d = GetVec(op.Rd);
Operand n = GetVec(op.Rn); 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); context.Copy(GetVec(op.Rd), res);
} }
@ -100,7 +100,7 @@ namespace ARMeilleure.Instructions
Operand n = GetVec(op.Rn); Operand n = GetVec(op.Rn);
Operand m = GetVec(op.Rm); 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); context.Copy(GetVec(op.Rd), res);
} }
@ -113,7 +113,7 @@ namespace ARMeilleure.Instructions
Operand n = GetVec(op.Rn); Operand n = GetVec(op.Rn);
Operand m = GetVec(op.Rm); 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); context.Copy(GetVec(op.Rd), res);
} }
@ -125,7 +125,7 @@ namespace ARMeilleure.Instructions
Operand d = GetVec(op.Rd); Operand d = GetVec(op.Rd);
Operand n = GetVec(op.Rn); 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); context.Copy(GetVec(op.Rd), res);
} }
@ -138,7 +138,7 @@ namespace ARMeilleure.Instructions
Operand n = GetVec(op.Rn); Operand n = GetVec(op.Rn);
Operand m = GetVec(op.Rm); 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); context.Copy(GetVec(op.Rd), res);
} }

View file

@ -4,6 +4,7 @@ using ARMeilleure.State;
using ARMeilleure.Translation; using ARMeilleure.Translation;
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Reflection;
using static ARMeilleure.Instructions.InstEmitHelper; using static ARMeilleure.Instructions.InstEmitHelper;
using static ARMeilleure.IntermediateRepresentation.OperandHelper; using static ARMeilleure.IntermediateRepresentation.OperandHelper;
@ -310,68 +311,39 @@ namespace ARMeilleure.Instructions
context.Copy(GetVec(op.Rd), res); 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; 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) public static Operand EmitRoundMathCall(ArmEmitterContext context, MidpointRounding roundMode, Operand n)
{ {
IOpCodeSimd op = (IOpCodeSimd)context.CurrOp; IOpCodeSimd op = (IOpCodeSimd)context.CurrOp;
Delegate dlg; string name = nameof(Math.Round);
if ((op.Size & 1) == 0) MethodInfo info = (op.Size & 1) == 0
{ ? typeof(MathF).GetMethod(name, new Type[] { typeof(float), typeof(MidpointRounding) })
dlg = new _F32_F32_MidpointRounding(MathF.Round); : typeof(Math). GetMethod(name, new Type[] { typeof(double), typeof(MidpointRounding) });
}
else /* if ((op.Size & 1) == 1) */ return context.Call(info, n, Const((int)roundMode));
{
dlg = new _F64_F64_MidpointRounding(Math.Round);
} }
return context.Call(dlg, n, Const((int)roundMode)); public static Operand EmitSoftFloatCall(ArmEmitterContext context, string name, params Operand[] callArgs)
}
public static Operand EmitSoftFloatCall(
ArmEmitterContext context,
_F32_F32 f32,
_F64_F64 f64,
params Operand[] callArgs)
{ {
IOpCodeSimd op = (IOpCodeSimd)context.CurrOp; 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); return context.Call(info, 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);
} }
public static void EmitScalarBinaryOpByElemF(ArmEmitterContext context, Func2I emit) public static void EmitScalarBinaryOpByElemF(ArmEmitterContext context, Func2I emit)
@ -1425,22 +1397,22 @@ namespace ARMeilleure.Instructions
throw new ArgumentOutOfRangeException(nameof(sizeDst)); throw new ArgumentOutOfRangeException(nameof(sizeDst));
} }
Delegate dlg; MethodInfo info;
if (signedSrc) if (signedSrc)
{ {
dlg = signedDst info = signedDst
? (Delegate)new _S64_S64_S32(SoftFallback.SignedSrcSignedDstSatQ) ? typeof(SoftFallback).GetMethod(nameof(SoftFallback.SignedSrcSignedDstSatQ))
: (Delegate)new _U64_S64_S32(SoftFallback.SignedSrcUnsignedDstSatQ); : typeof(SoftFallback).GetMethod(nameof(SoftFallback.SignedSrcUnsignedDstSatQ));
} }
else else
{ {
dlg = signedDst info = signedDst
? (Delegate)new _S64_U64_S32(SoftFallback.UnsignedSrcSignedDstSatQ) ? typeof(SoftFallback).GetMethod(nameof(SoftFallback.UnsignedSrcSignedDstSatQ))
: (Delegate)new _U64_U64_S32(SoftFallback.UnsignedSrcUnsignedDstSatQ); : 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. // TSrc (64bit) == TDst (64bit); signed.
@ -1448,7 +1420,7 @@ namespace ARMeilleure.Instructions
{ {
Debug.Assert(((OpCodeSimd)context.CurrOp).Size == 3, "Invalid element size."); 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. // TSrcs (64bit) == TDst (64bit); signed, unsigned.
@ -1456,11 +1428,11 @@ namespace ARMeilleure.Instructions
{ {
Debug.Assert(((OpCodeSimd)context.CurrOp).Size == 3, "Invalid element size."); Debug.Assert(((OpCodeSimd)context.CurrOp).Size == 3, "Invalid element size.");
Delegate dlg = signed MethodInfo info = signed
? (Delegate)new _S64_S64_S64(SoftFallback.BinarySignedSatQAdd) ? typeof(SoftFallback).GetMethod(nameof(SoftFallback.BinarySignedSatQAdd))
: (Delegate)new _U64_U64_U64(SoftFallback.BinaryUnsignedSatQAdd); : typeof(SoftFallback).GetMethod(nameof(SoftFallback.BinaryUnsignedSatQAdd));
return context.Call(dlg, op1, op2); return context.Call(info, op1, op2);
} }
// TSrcs (64bit) == TDst (64bit); signed, unsigned. // TSrcs (64bit) == TDst (64bit); signed, unsigned.
@ -1468,11 +1440,11 @@ namespace ARMeilleure.Instructions
{ {
Debug.Assert(((OpCodeSimd)context.CurrOp).Size == 3, "Invalid element size."); Debug.Assert(((OpCodeSimd)context.CurrOp).Size == 3, "Invalid element size.");
Delegate dlg = signed MethodInfo info = signed
? (Delegate)new _S64_S64_S64(SoftFallback.BinarySignedSatQSub) ? typeof(SoftFallback).GetMethod(nameof(SoftFallback.BinarySignedSatQSub))
: (Delegate)new _U64_U64_U64(SoftFallback.BinaryUnsignedSatQSub); : typeof(SoftFallback).GetMethod(nameof(SoftFallback.BinaryUnsignedSatQSub));
return context.Call(dlg, op1, op2); return context.Call(info, op1, op2);
} }
// TSrcs (64bit) == TDst (64bit); signed, unsigned. // TSrcs (64bit) == TDst (64bit); signed, unsigned.
@ -1480,11 +1452,11 @@ namespace ARMeilleure.Instructions
{ {
Debug.Assert(((OpCodeSimd)context.CurrOp).Size == 3, "Invalid element size."); Debug.Assert(((OpCodeSimd)context.CurrOp).Size == 3, "Invalid element size.");
Delegate dlg = signed MethodInfo info = signed
? (Delegate)new _S64_U64_S64(SoftFallback.BinarySignedSatQAcc) ? typeof(SoftFallback).GetMethod(nameof(SoftFallback.BinarySignedSatQAcc))
: (Delegate)new _U64_S64_U64(SoftFallback.BinaryUnsignedSatQAcc); : 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) public static Operand EmitFloatAbs(ArmEmitterContext context, Operand value, bool single, bool vector)

View file

@ -3,6 +3,8 @@ using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.Translation; using ARMeilleure.Translation;
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Reflection;
using static ARMeilleure.Instructions.InstEmitHelper; using static ARMeilleure.Instructions.InstEmitHelper;
using static ARMeilleure.Instructions.InstEmitSimdHelper; using static ARMeilleure.Instructions.InstEmitSimdHelper;
using static ARMeilleure.IntermediateRepresentation.OperandHelper; using static ARMeilleure.IntermediateRepresentation.OperandHelper;
@ -1000,52 +1002,18 @@ namespace ARMeilleure.Instructions
// Generic Functions // Generic Functions
public static Operand EmitSoftFloatCallDefaultFpscr( public static Operand EmitSoftFloatCallDefaultFpscr(ArmEmitterContext context, string name, params Operand[] callArgs)
ArmEmitterContext context,
_F32_F32_Bool f32,
_F64_F64_Bool f64,
params Operand[] callArgs)
{ {
IOpCodeSimd op = (IOpCodeSimd)context.CurrOp; 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); Array.Resize(ref callArgs, callArgs.Length + 1);
callArgs[callArgs.Length - 1] = Const(1); callArgs[callArgs.Length - 1] = Const(1);
return context.Call(dlg, callArgs); return context.Call(info, 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);
} }
public static Operand EmitVectorExtractSx32(ArmEmitterContext context, int reg, int index, int size) public static Operand EmitVectorExtractSx32(ArmEmitterContext context, int reg, int index, int size)

View file

@ -1,8 +1,8 @@
using ARMeilleure.Decoders; using ARMeilleure.Decoders;
using ARMeilleure.IntermediateRepresentation; using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.Translation; using ARMeilleure.Translation;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Reflection;
using static ARMeilleure.Instructions.InstEmitHelper; using static ARMeilleure.Instructions.InstEmitHelper;
using static ARMeilleure.Instructions.InstEmitSimdHelper; using static ARMeilleure.Instructions.InstEmitSimdHelper;
@ -284,6 +284,8 @@ namespace ARMeilleure.Instructions
{ {
OpCodeSimdFmov op = (OpCodeSimdFmov)context.CurrOp; OpCodeSimdFmov op = (OpCodeSimdFmov)context.CurrOp;
if (Optimizations.UseSse2)
{
if (op.Size == 0) if (op.Size == 0)
{ {
context.Copy(GetVec(op.Rd), X86GetScalar(context, (int)op.Immediate)); context.Copy(GetVec(op.Rd), X86GetScalar(context, (int)op.Immediate));
@ -293,6 +295,17 @@ namespace ARMeilleure.Instructions
context.Copy(GetVec(op.Rd), X86GetScalar(context, op.Immediate)); context.Copy(GetVec(op.Rd), X86GetScalar(context, op.Immediate));
} }
} }
else
{
Operand e = Const(op.Immediate);
Operand res = context.VectorZero();
res = EmitVectorInsert(context, res, e, 0, op.Size + 2);
context.Copy(GetVec(op.Rd), res);
}
}
public static void Fmov_Vi(ArmEmitterContext context) public static void Fmov_Vi(ArmEmitterContext context)
{ {
@ -350,7 +363,7 @@ namespace ARMeilleure.Instructions
{ {
if (Optimizations.UseSse2) if (Optimizations.UseSse2)
{ {
EmitSse2MoviMvni(context, not: false); EmitSse2VectorMoviMvniOp(context, not: false);
} }
else else
{ {
@ -362,7 +375,7 @@ namespace ARMeilleure.Instructions
{ {
if (Optimizations.UseSse2) if (Optimizations.UseSse2)
{ {
EmitSse2MoviMvni(context, not: true); EmitSse2VectorMoviMvniOp(context, not: true);
} }
else else
{ {
@ -476,7 +489,7 @@ namespace ARMeilleure.Instructions
EmitVectorZip(context, part: 1); 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; OpCodeSimdImm op = (OpCodeSimdImm)context.CurrOp;
@ -593,32 +606,30 @@ namespace ARMeilleure.Instructions
args.Add(GetVec((op.Rn + index) & 0x1F)); args.Add(GetVec((op.Rn + index) & 0x1F));
} }
Delegate dlg = null; MethodInfo info = null;
if (isTbl)
{
switch (op.Size) switch (op.Size)
{ {
case 1: dlg = isTbl case 1: info = typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl1)); break;
? (Delegate)new _V128_V128_S32_V128 (SoftFallback.Tbl1) case 2: info = typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl2)); break;
: (Delegate)new _V128_V128_V128_S32_V128(SoftFallback.Tbx1); case 3: info = typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl3)); break;
break; case 4: info = typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl4)); break;
}
case 2: dlg = isTbl }
? (Delegate)new _V128_V128_S32_V128_V128 (SoftFallback.Tbl2) else
: (Delegate)new _V128_V128_V128_S32_V128_V128(SoftFallback.Tbx2); {
break; switch (op.Size)
{
case 3: dlg = isTbl case 1: info = typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx1)); break;
? (Delegate)new _V128_V128_S32_V128_V128_V128 (SoftFallback.Tbl3) case 2: info = typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx2)); break;
: (Delegate)new _V128_V128_V128_S32_V128_V128_V128(SoftFallback.Tbx3); case 3: info = typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx3)); break;
break; case 4: info = typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx4)); 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;
} }
context.Copy(d, context.Call(dlg, args.ToArray())); context.Copy(d, context.Call(info, args.ToArray()));
} }
} }

View file

@ -5,6 +5,7 @@ using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.Translation; using ARMeilleure.Translation;
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Reflection;
using static ARMeilleure.Instructions.InstEmitHelper; using static ARMeilleure.Instructions.InstEmitHelper;
using static ARMeilleure.Instructions.InstEmitSimdHelper; using static ARMeilleure.Instructions.InstEmitSimdHelper;
@ -198,7 +199,7 @@ namespace ARMeilleure.Instructions
Operand ne = EmitVectorExtractSx(context, op.Rn, index, op.Size); Operand ne = EmitVectorExtractSx(context, op.Rn, index, op.Size);
Operand me = EmitVectorExtractSx(context, op.Rm, 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); 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 ne = EmitVectorExtractSx(context, op.Rn, index, op.Size);
Operand me = EmitVectorExtractSx(context, op.Rm, 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); 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 ne = EmitVectorExtractSx(context, op.Rn, index, op.Size);
Operand me = EmitVectorExtractSx(context, op.Rm, 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); 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 ne = EmitVectorExtractSx(context, op.Rn, index, op.Size);
Operand me = EmitVectorExtractSx(context, op.Rm, 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); 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 ne = EmitVectorExtractZx(context, op.Rn, index, op.Size);
Operand me = EmitVectorExtractZx(context, op.Rm, 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); 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 ne = EmitVectorExtractZx(context, op.Rn, index, op.Size);
Operand me = EmitVectorExtractZx(context, op.Rm, 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); 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 ne = EmitVectorExtractZx(context, op.Rn, index, op.Size);
Operand me = EmitVectorExtractZx(context, op.Rm, 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); res = EmitVectorInsert(context, res, e, index, op.Size);
} }
@ -1026,11 +1027,11 @@ namespace ARMeilleure.Instructions
long roundConst, long roundConst,
int shift) int shift)
{ {
Delegate dlg = signed MethodInfo info = signed
? (Delegate)new _S64_S64_S64_S32(SoftFallback.SignedShrImm64) ? typeof(SoftFallback).GetMethod(nameof(SoftFallback.SignedShrImm64))
: (Delegate)new _U64_U64_S64_S32(SoftFallback.UnsignedShrImm64); : 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) private static void EmitVectorShImmWidenBinarySx(ArmEmitterContext context, Func2I emit, int imm)

View file

@ -1,9 +1,9 @@
using ARMeilleure.Decoders; using ARMeilleure.Decoders;
using ARMeilleure.IntermediateRepresentation; using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.State;
using ARMeilleure.Translation; using ARMeilleure.Translation;
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Reflection;
using static ARMeilleure.Instructions.InstEmitSimdHelper32; using static ARMeilleure.Instructions.InstEmitSimdHelper32;
using static ARMeilleure.IntermediateRepresentation.OperandHelper; using static ARMeilleure.IntermediateRepresentation.OperandHelper;
@ -244,11 +244,11 @@ namespace ARMeilleure.Instructions
long roundConst, long roundConst,
int shift) int shift)
{ {
Delegate dlg = signed MethodInfo info = signed
? (Delegate)new _S64_S64_S64_S32(SoftFallback.SignedShrImm64) ? typeof(SoftFallback).GetMethod(nameof(SoftFallback.SignedShrImm64))
: (Delegate)new _U64_U64_S64_S32(SoftFallback.UnsignedShrImm64); : 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) private static Operand EmitSatQ(ArmEmitterContext context, Operand value, int eSize, bool signed)

View file

@ -3,6 +3,7 @@ using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.State; using ARMeilleure.State;
using ARMeilleure.Translation; using ARMeilleure.Translation;
using System; using System;
using System.Reflection;
using static ARMeilleure.Instructions.InstEmitHelper; using static ARMeilleure.Instructions.InstEmitHelper;
using static ARMeilleure.IntermediateRepresentation.OperandHelper; using static ARMeilleure.IntermediateRepresentation.OperandHelper;
@ -27,44 +28,44 @@ namespace ARMeilleure.Instructions
{ {
OpCodeSystem op = (OpCodeSystem)context.CurrOp; OpCodeSystem op = (OpCodeSystem)context.CurrOp;
Delegate dlg; MethodInfo info;
switch (GetPackedId(op)) switch (GetPackedId(op))
{ {
case 0b11_011_0000_0000_001: dlg = new _U64(NativeInterface.GetCtrEl0); break; case 0b11_011_0000_0000_001: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCtrEl0)); break;
case 0b11_011_0000_0000_111: dlg = new _U64(NativeInterface.GetDczidEl0); 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_0010_000: EmitGetNzcv(context); return;
case 0b11_011_0100_0100_000: dlg = new _U64(NativeInterface.GetFpcr); break; case 0b11_011_0100_0100_000: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFpcr)); break;
case 0b11_011_0100_0100_001: dlg = new _U64(NativeInterface.GetFpsr); break; case 0b11_011_0100_0100_001: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFpsr)); break;
case 0b11_011_1101_0000_010: dlg = new _U64(NativeInterface.GetTpidrEl0); break; case 0b11_011_1101_0000_010: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrEl0)); break;
case 0b11_011_1101_0000_011: dlg = new _U64(NativeInterface.GetTpidr); break; case 0b11_011_1101_0000_011: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidr)); break;
case 0b11_011_1110_0000_000: dlg = new _U64(NativeInterface.GetCntfrqEl0); break; case 0b11_011_1110_0000_000: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntfrqEl0)); break;
case 0b11_011_1110_0000_001: dlg = new _U64(NativeInterface.GetCntpctEl0); break; case 0b11_011_1110_0000_001: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntpctEl0)); break;
case 0b11_011_1110_0000_010: dlg = new _U64(NativeInterface.GetCntvctEl0); 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}."); 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) public static void Msr(ArmEmitterContext context)
{ {
OpCodeSystem op = (OpCodeSystem)context.CurrOp; OpCodeSystem op = (OpCodeSystem)context.CurrOp;
Delegate dlg; MethodInfo info;
switch (GetPackedId(op)) switch (GetPackedId(op))
{ {
case 0b11_011_0100_0010_000: EmitSetNzcv(context); return; 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_000: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpcr)); break;
case 0b11_011_0100_0100_001: dlg = new _Void_U64(NativeInterface.SetFpsr); break; case 0b11_011_0100_0100_001: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsr)); break;
case 0b11_011_1101_0000_010: dlg = new _Void_U64(NativeInterface.SetTpidrEl0); 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}."); 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) public static void Nop(ArmEmitterContext context)
@ -90,7 +91,7 @@ namespace ARMeilleure.Instructions
{ {
Operand address = context.Add(t, Const(offset)); 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; break;

View file

@ -3,6 +3,7 @@ using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.State; using ARMeilleure.State;
using ARMeilleure.Translation; using ARMeilleure.Translation;
using System; using System;
using System.Reflection;
using static ARMeilleure.Instructions.InstEmitHelper; using static ARMeilleure.Instructions.InstEmitHelper;
using static ARMeilleure.IntermediateRepresentation.OperandHelper; using static ARMeilleure.IntermediateRepresentation.OperandHelper;
@ -18,6 +19,7 @@ namespace ARMeilleure.Instructions
if (op.Coproc != 15) if (op.Coproc != 15)
{ {
InstEmit.Und(context); InstEmit.Und(context);
return; return;
} }
@ -26,7 +28,8 @@ namespace ARMeilleure.Instructions
throw new NotImplementedException($"Unknown MRC Opc1 0x{op.Opc1:X16} at 0x{op.Address:X16}."); throw new NotImplementedException($"Unknown MRC Opc1 0x{op.Opc1:X16} at 0x{op.Address:X16}.");
} }
Delegate dlg; MethodInfo info;
switch (op.CRn) switch (op.CRn)
{ {
case 13: // Process and Thread Info. 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}."); throw new NotImplementedException($"Unknown MRC CRm 0x{op.CRm:X16} at 0x{op.Address:X16}.");
} }
switch (op.Opc2) switch (op.Opc2)
{ {
case 2: case 2:
dlg = new _Void_U32(NativeInterface.SetTpidrEl032); break; info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetTpidrEl032)); break;
default: default:
throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X16} at 0x{op.Address:X16}."); throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X16} at 0x{op.Address:X16}.");
} }
break; break;
case 7: case 7:
@ -51,9 +57,11 @@ namespace ARMeilleure.Instructions
{ {
case 5: // Data Memory Barrier Register. case 5: // Data Memory Barrier Register.
return; // No-op. return; // No-op.
default: default:
throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X16} at 0x{op.Address:X16}."); throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X16} at 0x{op.Address:X16}.");
} }
default: default:
throw new NotImplementedException($"Unknown MRC CRm 0x{op.CRm:X16} at 0x{op.Address:X16}."); throw new NotImplementedException($"Unknown MRC CRm 0x{op.CRm:X16} at 0x{op.Address:X16}.");
} }
@ -62,7 +70,7 @@ namespace ARMeilleure.Instructions
throw new NotImplementedException($"Unknown MRC 0x{op.RawOpCode:X8} at 0x{op.Address:X16}."); 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) public static void Mrc(ArmEmitterContext context)
@ -72,6 +80,7 @@ namespace ARMeilleure.Instructions
if (op.Coproc != 15) if (op.Coproc != 15)
{ {
InstEmit.Und(context); InstEmit.Und(context);
return; return;
} }
@ -80,7 +89,8 @@ namespace ARMeilleure.Instructions
throw new NotImplementedException($"Unknown MRC Opc1 0x{op.Opc1:X16} at 0x{op.Address:X16}."); throw new NotImplementedException($"Unknown MRC Opc1 0x{op.Opc1:X16} at 0x{op.Address:X16}.");
} }
Delegate dlg; MethodInfo info;
switch (op.CRn) switch (op.CRn)
{ {
case 13: // Process and Thread Info. case 13: // Process and Thread Info.
@ -88,16 +98,21 @@ namespace ARMeilleure.Instructions
{ {
throw new NotImplementedException($"Unknown MRC CRm 0x{op.CRm:X16} at 0x{op.Address:X16}."); throw new NotImplementedException($"Unknown MRC CRm 0x{op.CRm:X16} at 0x{op.Address:X16}.");
} }
switch (op.Opc2) switch (op.Opc2)
{ {
case 2: case 2:
dlg = new _U32(NativeInterface.GetTpidrEl032); break; info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrEl032)); break;
case 3: case 3:
dlg = new _U32(NativeInterface.GetTpidr32); break; info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidr32)); break;
default: default:
throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X16} at 0x{op.Address:X16}."); throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X16} at 0x{op.Address:X16}.");
} }
break; break;
default: default:
throw new NotImplementedException($"Unknown MRC 0x{op.RawOpCode:X8} at 0x{op.Address:X16}."); throw new NotImplementedException($"Unknown MRC 0x{op.RawOpCode:X8} at 0x{op.Address:X16}.");
} }
@ -105,13 +120,13 @@ namespace ARMeilleure.Instructions
if (op.Rt == RegisterAlias.Aarch32Pc) if (op.Rt == RegisterAlias.Aarch32Pc)
{ {
// Special behavior: copy NZCV flags into APSR. // Special behavior: copy NZCV flags into APSR.
EmitSetNzcv(context, context.Call(dlg)); EmitSetNzcv(context, context.Call(info));
return; return;
} }
else 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) if (op.Coproc != 15)
{ {
InstEmit.Und(context); InstEmit.Und(context);
return; return;
} }
var opc = op.MrrcOp; int opc = op.MrrcOp;
MethodInfo info;
Delegate dlg;
switch (op.CRm) switch (op.CRm)
{ {
case 14: // Timer. case 14: // Timer.
switch (opc) switch (opc)
{ {
case 0: case 0:
dlg = new _U64(NativeInterface.GetCntpctEl0); break; info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntpctEl0)); break;
default: default:
throw new NotImplementedException($"Unknown MRRC Opc1 0x{opc:X16} at 0x{op.Address:X16}."); throw new NotImplementedException($"Unknown MRRC Opc1 0x{opc:X16} at 0x{op.Address:X16}.");
} }
break; break;
default: default:
throw new NotImplementedException($"Unknown MRRC 0x{op.RawOpCode:X8} at 0x{op.Address:X16}."); 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.Rt, context.ConvertI64ToI32(result));
SetIntA32(context, op.CRn, context.ConvertI64ToI32(context.ShiftRightUI(result, Const(32)))); 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.CFlag, GetFpFlag(FPState.CFlag));
SetFlag(context, PState.ZFlag, GetFpFlag(FPState.ZFlag)); SetFlag(context, PState.ZFlag, GetFpFlag(FPState.ZFlag));
SetFlag(context, PState.NFlag, GetFpFlag(FPState.NFlag)); SetFlag(context, PState.NFlag, GetFpFlag(FPState.NFlag));
return; return;
} }
Delegate dlg; MethodInfo info;
switch (op.Sreg) switch (op.Sreg)
{ {
case 0b0000: // FPSID case 0b0000: // FPSID
throw new NotImplementedException("Supervisor Only"); throw new NotImplementedException("Supervisor Only");
case 0b0001: // FPSCR case 0b0001: // FPSCR
dlg = new _U32(NativeInterface.GetFpscr); break; info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFpscr)); break;
case 0b0101: // MVFR2 case 0b0101: // MVFR2
throw new NotImplementedException("MVFR2"); throw new NotImplementedException("MVFR2");
case 0b0110: // MVFR1 case 0b0110: // MVFR1
@ -184,20 +206,21 @@ namespace ARMeilleure.Instructions
throw new NotImplementedException($"Unknown VMRS 0x{op.RawOpCode:X8} at 0x{op.Address:X16}."); 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) public static void Vmsr(ArmEmitterContext context)
{ {
OpCode32SimdSpecial op = (OpCode32SimdSpecial)context.CurrOp; OpCode32SimdSpecial op = (OpCode32SimdSpecial)context.CurrOp;
Delegate dlg; MethodInfo info;
switch (op.Sreg) switch (op.Sreg)
{ {
case 0b0000: // FPSID case 0b0000: // FPSID
throw new NotImplementedException("Supervisor Only"); throw new NotImplementedException("Supervisor Only");
case 0b0001: // FPSCR case 0b0001: // FPSCR
dlg = new _Void_U32(NativeInterface.SetFpscr); break; info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpscr)); break;
case 0b0101: // MVFR2 case 0b0101: // MVFR2
throw new NotImplementedException("MVFR2"); throw new NotImplementedException("MVFR2");
case 0b0110: // MVFR1 case 0b0110: // MVFR1
@ -210,7 +233,7 @@ namespace ARMeilleure.Instructions
throw new NotImplementedException($"Unknown VMSR 0x{op.RawOpCode:X8} at 0x{op.Address:X16}."); 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) private static void EmitSetNzcv(ArmEmitterContext context, Operand t)

View file

@ -407,18 +407,22 @@ namespace ARMeilleure.Instructions
public static ulong GetFunctionAddress(ulong address) public static ulong GetFunctionAddress(ulong address)
{ {
TranslatedFunction function = _context.Translator.GetOrTranslate(address, GetContext().ExecutionMode); 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) public static ulong GetIndirectFunctionAddress(ulong address, ulong entryAddress)
{ {
TranslatedFunction function = _context.Translator.GetOrTranslate(address, GetContext().ExecutionMode); TranslatedFunction function = _context.Translator.GetOrTranslate(address, GetContext().ExecutionMode);
ulong ptr = (ulong)function.GetPointer().ToInt64();
ulong ptr = (ulong)function.FuncPtr.ToInt64();
if (function.HighCq) if (function.HighCq)
{ {
// Rewrite the host function address in the table to point to the highCq function. // Rewrite the host function address in the table to point to the highCq function.
Marshal.WriteInt64((IntPtr)entryAddress, 8, (long)ptr); Marshal.WriteInt64((IntPtr)entryAddress, 8, (long)ptr);
} }
return ptr; return ptr;
} }

View file

@ -903,6 +903,13 @@ namespace ARMeilleure.Instructions
else else
{ {
result = value1; result = value1;
if ((fpcr & FPCR.Fz) != 0 && float.IsSubnormal(result))
{
context.Fpsr |= FPSR.Ufc;
result = FPZero(result < 0f);
}
} }
} }
else else
@ -987,6 +994,13 @@ namespace ARMeilleure.Instructions
else else
{ {
result = value1; result = value1;
if ((fpcr & FPCR.Fz) != 0 && float.IsSubnormal(result))
{
context.Fpsr |= FPSR.Ufc;
result = FPZero(result < 0f);
}
} }
} }
else else
@ -2196,6 +2210,13 @@ namespace ARMeilleure.Instructions
else else
{ {
result = value1; result = value1;
if ((fpcr & FPCR.Fz) != 0 && double.IsSubnormal(result))
{
context.Fpsr |= FPSR.Ufc;
result = FPZero(result < 0d);
}
} }
} }
else else
@ -2280,6 +2301,13 @@ namespace ARMeilleure.Instructions
else else
{ {
result = value1; result = value1;
if ((fpcr & FPCR.Fz) != 0 && double.IsSubnormal(result))
{
context.Fpsr |= FPSR.Ufc;
result = FPZero(result < 0d);
}
} }
} }
else else

View file

@ -6,11 +6,13 @@ namespace ARMeilleure.IntermediateRepresentation
class Operand class Operand
{ {
public OperandKind Kind { get; private set; } public OperandKind Kind { get; private set; }
public OperandType Type { get; private set; } public OperandType Type { get; private set; }
public ulong Value { 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> Assignments { get; }
public List<Node> Uses { get; } public List<Node> Uses { get; }
@ -26,14 +28,19 @@ namespace ARMeilleure.IntermediateRepresentation
Type = type; 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; Kind = kind;
Type = type; Type = type;
Value = value; Value = value;
DisableCF = disableCF;
PtcIndex = index;
Assignments.Clear(); Assignments.Clear();
Uses.Clear(); Uses.Clear();
return this; return this;
} }
@ -47,9 +54,9 @@ namespace ARMeilleure.IntermediateRepresentation
return With(OperandKind.Constant, OperandType.I32, value); 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) public Operand With(ulong value)

View file

@ -34,9 +34,9 @@ namespace ARMeilleure.IntermediateRepresentation
return Operand().With(value); 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) public static Operand Const(ulong value)

View file

@ -11,7 +11,7 @@ namespace ARMeilleure.Translation
{ {
class ArmEmitterContext : EmitterContext class ArmEmitterContext : EmitterContext
{ {
private Dictionary<ulong, Operand> _labels; private readonly Dictionary<ulong, Operand> _labels;
private OpCode _optOpLastCompare; private OpCode _optOpLastCompare;
private OpCode _optOpLastFlagSet; private OpCode _optOpLastFlagSet;

View file

@ -7,18 +7,30 @@ using System.Runtime.InteropServices;
namespace ARMeilleure.Translation namespace ARMeilleure.Translation
{ {
using PTC;
static class Compiler 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); IntPtr codePtr = JitCache.Map(func);
return Marshal.GetDelegateForFunctionPointer<T>(codePtr); 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); Logger.StartPass(PassName.Dominance);
@ -45,7 +57,7 @@ namespace ARMeilleure.Translation
CompilerContext cctx = new CompilerContext(cfg, argTypes, retType, options); CompilerContext cctx = new CompilerContext(cfg, argTypes, retType, options);
return CodeGenerator.Generate(cctx); return CodeGenerator.Generate(cctx, ptcInfo);
} }
} }
} }

View file

@ -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}";
}
}
}

View file

@ -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();
}
}
}

View file

@ -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);
}
}
}

View file

@ -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)));
}
}
}

View file

@ -2,6 +2,7 @@
using ARMeilleure.IntermediateRepresentation; using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.State; using ARMeilleure.State;
using System; using System;
using System.Diagnostics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using static ARMeilleure.IntermediateRepresentation.OperandHelper; using static ARMeilleure.IntermediateRepresentation.OperandHelper;
@ -12,10 +13,10 @@ namespace ARMeilleure.Translation
{ {
private delegate long GuestFunction(IntPtr nativeContextPtr); private delegate long GuestFunction(IntPtr nativeContextPtr);
private static GuestFunction _directCallStub; private static IntPtr _directCallStubPtr;
private static GuestFunction _directTailCallStub; private static IntPtr _directTailCallStubPtr;
private static GuestFunction _indirectCallStub; private static IntPtr _indirectCallStubPtr;
private static GuestFunction _indirectTailCallStub; private static IntPtr _indirectTailCallStubPtr;
private static readonly object _lock = new object(); private static readonly object _lock = new object();
private static bool _initialized; private static bool _initialized;
@ -23,25 +24,32 @@ namespace ARMeilleure.Translation
public static void InitializeStubs() public static void InitializeStubs()
{ {
if (_initialized) return; if (_initialized) return;
lock (_lock) lock (_lock)
{ {
if (_initialized) return; if (_initialized) return;
_directCallStub = GenerateDirectCallStub(false);
_directTailCallStub = GenerateDirectCallStub(true); _directCallStubPtr = Marshal.GetFunctionPointerForDelegate<GuestFunction>(GenerateDirectCallStub(false));
_indirectCallStub = GenerateIndirectCallStub(false); _directTailCallStubPtr = Marshal.GetFunctionPointerForDelegate<GuestFunction>(GenerateDirectCallStub(true));
_indirectTailCallStub = GenerateIndirectCallStub(true); _indirectCallStubPtr = Marshal.GetFunctionPointerForDelegate<GuestFunction>(GenerateIndirectCallStub(false));
_indirectTailCallStubPtr = Marshal.GetFunctionPointerForDelegate<GuestFunction>(GenerateIndirectCallStub(true));
_initialized = true; _initialized = true;
} }
} }
public static IntPtr DirectCallStub(bool tailCall) 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) 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) private static void EmitCall(EmitterContext context, Operand address, bool tailCall)
@ -70,15 +78,12 @@ namespace ARMeilleure.Translation
Operand address = context.Load(OperandType.I64, context.Add(nativeContextPtr, Const((long)NativeContext.GetCallAddressOffset()))); Operand address = context.Load(OperandType.I64, context.Add(nativeContextPtr, Const((long)NativeContext.GetCallAddressOffset())));
address = context.BitwiseOr(address, Const(address.Type, 1)); // Set call flag. 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); EmitCall(context, functionAddr, tailCall);
ControlFlowGraph cfg = context.GetControlFlowGraph(); ControlFlowGraph cfg = context.GetControlFlowGraph();
OperandType[] argTypes = new OperandType[] OperandType[] argTypes = new OperandType[] { OperandType.I64 };
{
OperandType.I64
};
return Compiler.Compile<GuestFunction>(cfg, argTypes, OperandType.I64, CompilerOptions.HighCq); return Compiler.Compile<GuestFunction>(cfg, argTypes, OperandType.I64, CompilerOptions.HighCq);
} }
@ -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. // 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. // 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. // Call and save the function.
EmitCall(context, functionAddr, tailCall); EmitCall(context, functionAddr, tailCall);
ControlFlowGraph cfg = context.GetControlFlowGraph(); ControlFlowGraph cfg = context.GetControlFlowGraph();
OperandType[] argTypes = new OperandType[] OperandType[] argTypes = new OperandType[] { OperandType.I64 };
{
OperandType.I64
};
return Compiler.Compile<GuestFunction>(cfg, argTypes, OperandType.I64, CompilerOptions.HighCq); return Compiler.Compile<GuestFunction>(cfg, argTypes, OperandType.I64, CompilerOptions.HighCq);
} }

View file

@ -3,12 +3,14 @@ using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.State; using ARMeilleure.State;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.InteropServices; using System.Reflection;
using static ARMeilleure.IntermediateRepresentation.OperandHelper; using static ARMeilleure.IntermediateRepresentation.OperandHelper;
namespace ARMeilleure.Translation namespace ARMeilleure.Translation
{ {
using PTC;
class EmitterContext class EmitterContext
{ {
private Dictionary<Operand, BasicBlock> _irLabels; private Dictionary<Operand, BasicBlock> _irLabels;
@ -79,42 +81,52 @@ namespace ARMeilleure.Translation
return Add(Instruction.ByteSwap, Local(op1.Type), op1); 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. if (Ptc.State == PtcState.Disabled)
func = DelegateCache.GetOrAdd(func); {
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);
return Call(Const(ptr.ToInt64()), returnType, callArgs);
} }
else
private static Dictionary<TypeCode, OperandType> _typeCodeToOperandTypeMap =
new Dictionary<TypeCode, OperandType>()
{ {
{ TypeCode.Boolean, OperandType.I32 }, int index = Delegates.GetDelegateIndex(info);
{ TypeCode.Byte, OperandType.I32 },
{ TypeCode.Char, OperandType.I32 }, IntPtr funcPtr = Delegates.GetDelegateFuncPtrByIndex(index);
{ TypeCode.Double, OperandType.FP64 },
{ TypeCode.Int16, OperandType.I32 }, OperandType returnType = GetOperandType(info.ReturnType);
{ TypeCode.Int32, OperandType.I32 },
{ TypeCode.Int64, OperandType.I64 }, Symbols.Add((ulong)funcPtr.ToInt64(), info.Name);
{ TypeCode.SByte, OperandType.I32 },
{ TypeCode.Single, OperandType.FP32 }, return Call(Const(funcPtr.ToInt64(), true, index), returnType, callArgs);
{ TypeCode.UInt16, OperandType.I32 }, }
{ TypeCode.UInt32, OperandType.I32 }, }
{ TypeCode.UInt64, OperandType.I64 }
};
private static OperandType GetOperandType(Type type) 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)) else if (type == typeof(V128))
{ {
@ -124,9 +136,11 @@ namespace ARMeilleure.Translation
{ {
return OperandType.None; return OperandType.None;
} }
else
{
throw new ArgumentException($"Invalid type \"{type.Name}\"."); throw new ArgumentException($"Invalid type \"{type.Name}\".");
} }
}
public Operand Call(Operand address, OperandType returnType, params Operand[] callArgs) public Operand Call(Operand address, OperandType returnType, params Operand[] callArgs)
{ {

View file

@ -12,11 +12,12 @@ namespace ARMeilleure.Translation
private const int PageSize = 4 * 1024; private const int PageSize = 4 * 1024;
private const int PageMask = PageSize - 1; 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 const int CacheSize = 2047 * 1024 * 1024;
private static ReservedRegion _jitRegion; private static ReservedRegion _jitRegion;
private static int _offset; private static int _offset;
private static readonly List<JitCacheEntry> _cacheEntries = new List<JitCacheEntry>(); private static readonly List<JitCacheEntry> _cacheEntries = new List<JitCacheEntry>();
private static readonly object _lock = new object(); private static readonly object _lock = new object();
@ -25,19 +26,23 @@ namespace ARMeilleure.Translation
public static void Initialize(IJitMemoryAllocator allocator) public static void Initialize(IJitMemoryAllocator allocator)
{ {
if (_initialized) return; if (_initialized) return;
lock (_lock) lock (_lock)
{ {
if (_initialized) return; if (_initialized) return;
_jitRegion = new ReservedRegion(allocator, CacheSize); _jitRegion = new ReservedRegion(allocator, CacheSize);
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{ {
_jitRegion.ExpandIfNeeded(PageSize); _jitRegion.ExpandIfNeeded((ulong)PageSize);
JitUnwindWindows.InstallFunctionTableHandler(_jitRegion.Pointer, CacheSize); JitUnwindWindows.InstallFunctionTableHandler(_jitRegion.Pointer, CacheSize);
// The first page is used for the table based SEH structs. // The first page is used for the table based SEH structs.
_offset = PageSize; _offset = PageSize;
} }
_initialized = true; _initialized = true;
} }
} }
@ -97,13 +102,13 @@ namespace ARMeilleure.Translation
_offset += codeSize; _offset += codeSize;
_jitRegion.ExpandIfNeeded((ulong)_offset); if (_offset > CacheSize)
if ((ulong)(uint)_offset > CacheSize)
{ {
throw new OutOfMemoryException(); throw new OutOfMemoryException("JIT Cache exhausted.");
} }
_jitRegion.ExpandIfNeeded((ulong)_offset);
return allocOffset; return allocOffset;
} }

View file

@ -27,7 +27,7 @@ namespace ARMeilleure.Translation
public unsafe fixed ushort UnwindCodes[MaxUnwindCodesArraySize]; public unsafe fixed ushort UnwindCodes[MaxUnwindCodesArraySize];
} }
private enum UnwindOperation private enum UnwindOp
{ {
PushNonvol = 0, PushNonvol = 0,
AllocLarge = 1, AllocLarge = 1,
@ -117,12 +117,12 @@ namespace ARMeilleure.Translation
if (stackOffset <= 0xFFFF0) 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); _unwindInfo->UnwindCodes[codeIndex++] = (ushort)(stackOffset / 16);
} }
else 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 >> 0);
_unwindInfo->UnwindCodes[codeIndex++] = (ushort)(stackOffset >> 16); _unwindInfo->UnwindCodes[codeIndex++] = (ushort)(stackOffset >> 16);
} }
@ -138,16 +138,16 @@ namespace ARMeilleure.Translation
if (allocSize <= 128) 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) 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); _unwindInfo->UnwindCodes[codeIndex++] = (ushort)(allocSize / 8);
} }
else 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 >> 0);
_unwindInfo->UnwindCodes[codeIndex++] = (ushort)(allocSize >> 16); _unwindInfo->UnwindCodes[codeIndex++] = (ushort)(allocSize >> 16);
} }
@ -157,7 +157,7 @@ namespace ARMeilleure.Translation
case UnwindPseudoOp.PushReg: case UnwindPseudoOp.PushReg:
{ {
_unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOperation.PushNonvol, entry.PrologOffset, entry.RegIndex); _unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.PushNonvol, entry.PrologOffset, entry.RegIndex);
break; break;
} }
@ -180,7 +180,7 @@ namespace ARMeilleure.Translation
return _runtimeFunction; 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)); return (ushort)(prologOffset | ((int)op << 8) | (opInfo << 12));
} }

View file

@ -3,11 +3,14 @@ using ARMeilleure.Memory;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
namespace ARMeilleure.Translation namespace ARMeilleure.Translation
{ {
using PTC;
class JumpTable class JumpTable
{ {
// The jump table is a block of (guestAddress, hostAddress) function mappings. // The jump table is a block of (guestAddress, hostAddress) function mappings.
@ -15,10 +18,9 @@ namespace ARMeilleure.Translation
// reserved specifically for each call. // reserved specifically for each call.
// The _dependants dictionary can be used to update the hostAddress for any functions that change. // 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 JumpTableSize = 1048576;
private const int JumpTableByteSize = JumpTableSize * JumpTableStride; private const int JumpTableByteSize = JumpTableSize * JumpTableStride;
// The dynamic table is also a block of (guestAddress, hostAddress) function mappings. // The dynamic table is also a block of (guestAddress, hostAddress) function mappings.
@ -35,71 +37,122 @@ namespace ARMeilleure.Translation
// 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. // the function from the JIT.
private const int DynamicTableSize = 1048576;
public const int DynamicTableElems = 1; public const int DynamicTableElems = 1;
public const int DynamicTableStride = DynamicTableElems * JumpTableStride; 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 readonly ReservedRegion _jumpRegion;
private readonly ReservedRegion _dynamicRegion;
private int _tableEnd = 0; private int _tableEnd = 0;
private int _dynTableEnd = 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 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) public JumpTable(IJitMemoryAllocator allocator)
{ {
_jumpRegion = new ReservedRegion(allocator, JumpTableByteSize); _jumpRegion = new ReservedRegion(allocator, JumpTableByteSize);
_dynamicRegion = new ReservedRegion(allocator, DynamicTableByteSize); _dynamicRegion = new ReservedRegion(allocator, DynamicTableByteSize);
_targets = new ConcurrentDictionary<ulong, TranslatedFunction>(); Targets = new ConcurrentDictionary<ulong, TranslatedFunction>();
_dependants = new ConcurrentDictionary<ulong, LinkedList<int>>(); Dependants = new ConcurrentDictionary<ulong, LinkedList<int>>();
Symbols.Add((ulong)_jumpRegion.Pointer.ToInt64(), JumpTableByteSize, JumpTableStride, "JMP_TABLE"); Symbols.Add((ulong)_jumpRegion.Pointer.ToInt64(), JumpTableByteSize, JumpTableStride, "JMP_TABLE");
Symbols.Add((ulong)_dynamicRegion.Pointer.ToInt64(), DynamicTableByteSize, DynamicTableStride, "DYN_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) public void RegisterFunction(ulong address, TranslatedFunction func)
{ {
address &= ~3UL; address &= ~3UL;
_targets.AddOrUpdate(address, func, (key, oldFunc) => func); Targets.AddOrUpdate(address, func, (key, oldFunc) => func);
long funcPtr = func.GetPointer().ToInt64(); long funcPtr = func.FuncPtr.ToInt64();
// Update all jump table entries that target this address. // 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) 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); 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) public int ReserveDynamicEntry(bool isJump)
{ {
int entry = Interlocked.Increment(ref _dynTableEnd); 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. // Initialize all host function pointers to the indirect call stub.
IntPtr addr = GetEntryAddressDynamicTable(entry);
IntPtr addr = _dynamicRegion.Pointer + entry * DynamicTableStride; long stubPtr = DirectCallStubs.IndirectCallStub(isJump).ToInt64();
long stubPtr = (long)DirectCallStubs.IndirectCallStub(isJump);
for (int i = 0; i < DynamicTableElems; i++) for (int i = 0; i < DynamicTableElems; i++)
{ {
@ -109,37 +162,46 @@ namespace ARMeilleure.Translation
return entry; return entry;
} }
public int ReserveTableEntry(long ownerAddress, long address, bool isJump) public void ExpandIfNeededJumpTable(int entries)
{ {
int entry = Interlocked.Increment(ref _tableEnd); Debug.Assert(entries > 0);
if (entry >= JumpTableSize)
if (entries < JumpTableSize)
{
_jumpRegion.ExpandIfNeeded((ulong)((entries + 1) * JumpTableStride));
}
else
{ {
throw new OutOfMemoryException("JIT Direct Jump Table exhausted."); throw new OutOfMemoryException("JIT Direct Jump Table exhausted.");
} }
_jumpRegion.ExpandIfNeeded((ulong)((entry + 1) * JumpTableStride));
// 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))
{
value = func.GetPointer().ToInt64();
} }
// Make sure changes to the function at the target address update this jump table entry. public void ExpandIfNeededDynamicTable(int entries)
LinkedList<int> targetDependants = _dependants.GetOrAdd((ulong)address, (addr) => new LinkedList<int>());
lock (targetDependants)
{ {
targetDependants.AddLast(entry); Debug.Assert(entries > 0);
if (entries < DynamicTableSize)
{
_dynamicRegion.ExpandIfNeeded((ulong)((entries + 1) * DynamicTableStride));
}
else
{
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); return _jumpRegion.Pointer + entry * JumpTableStride;
Marshal.WriteInt64(addr, 8, value); }
return entry; public IntPtr GetEntryAddressDynamicTable(int entry)
{
Debug.Assert(entry >= 1 && entry <= _dynTableEnd);
return _dynamicRegion.Pointer + entry * DynamicTableStride;
} }
} }
} }

View file

@ -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);
}
}

View file

@ -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();
}
}
}

View file

@ -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();
}
}
}

View file

@ -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
}
}
}

View file

@ -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();
}
}
}
}

View file

@ -0,0 +1,10 @@
namespace ARMeilleure.Translation.PTC
{
enum PtcState
{
Enabled,
Continuing,
Closing,
Disabled
}
}

View file

@ -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})";
}
}
}

View file

@ -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;
}
}
}

View file

@ -1,4 +1,4 @@
using ARMeilleure.State; using ARMeilleure.State;
namespace ARMeilleure.Translation namespace ARMeilleure.Translation
{ {

View file

@ -4,22 +4,23 @@ using System.Threading;
namespace ARMeilleure.Translation namespace ARMeilleure.Translation
{ {
class TranslatedFunction sealed class TranslatedFunction
{ {
private const int MinCallsForRejit = 100; private const int MinCallsForRejit = 100;
private GuestFunction _func; private readonly GuestFunction _func; // Ensure that this delegate will not be garbage collected.
private IntPtr _funcPtr;
private bool _rejit; private int _callCount = 0;
private int _callCount;
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; _func = func;
_rejit = rejit;
HighCq = highCq;
FuncPtr = Marshal.GetFunctionPointerForDelegate<GuestFunction>(func);
} }
public ulong Execute(State.ExecutionContext context) public ulong Execute(State.ExecutionContext context)
@ -29,17 +30,7 @@ namespace ARMeilleure.Translation
public bool ShouldRejit() public bool ShouldRejit()
{ {
return _rejit && Interlocked.Increment(ref _callCount) == MinCallsForRejit; return !HighCq && Interlocked.Increment(ref _callCount) == MinCallsForRejit;
}
public IntPtr GetPointer()
{
if (_funcPtr == IntPtr.Zero)
{
_funcPtr = Marshal.GetFunctionPointerForDelegate(_func);
}
return _funcPtr;
} }
} }
} }

View file

@ -13,22 +13,22 @@ using static ARMeilleure.IntermediateRepresentation.OperationHelper;
namespace ARMeilleure.Translation namespace ARMeilleure.Translation
{ {
using PTC;
public class Translator public class Translator
{ {
private const ulong CallFlag = InstEmitFlowHelper.CallFlag; 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 IMemoryManager _memory;
private readonly ConcurrentDictionary<ulong, TranslatedFunction> _funcs; private readonly ConcurrentDictionary<ulong, TranslatedFunction> _funcs;
private readonly JumpTable _jumpTable; private readonly ConcurrentStack<RejitRequest> _backgroundStack;
private readonly PriorityQueue<RejitRequest> _backgroundQueue;
private readonly AutoResetEvent _backgroundTranslatorEvent; private readonly AutoResetEvent _backgroundTranslatorEvent;
private readonly JumpTable _jumpTable;
private volatile int _threadCount; private volatile int _threadCount;
public Translator(IJitMemoryAllocator allocator, IMemoryManager memory) public Translator(IJitMemoryAllocator allocator, IMemoryManager memory)
@ -37,32 +37,45 @@ namespace ARMeilleure.Translation
_funcs = new ConcurrentDictionary<ulong, TranslatedFunction>(); _funcs = new ConcurrentDictionary<ulong, TranslatedFunction>();
_jumpTable = new JumpTable(allocator); _backgroundStack = new ConcurrentStack<RejitRequest>();
_backgroundQueue = new PriorityQueue<RejitRequest>(2);
_backgroundTranslatorEvent = new AutoResetEvent(false); _backgroundTranslatorEvent = new AutoResetEvent(false);
_jumpTable = new JumpTable(allocator);
JitCache.Initialize(allocator); JitCache.Initialize(allocator);
DirectCallStubs.InitializeStubs(); DirectCallStubs.InitializeStubs();
if (Ptc.State == PtcState.Enabled)
{
Ptc.LoadTranslations(_funcs, memory.PageTablePointer, _jumpTable);
}
} }
private void TranslateQueuedSubs() private void TranslateStackedSubs()
{ {
while (_threadCount != 0) 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); _funcs.AddOrUpdate(request.Address, func, (key, oldFunc) => func);
_jumpTable.RegisterFunction(request.Address, func); _jumpTable.RegisterFunction(request.Address, func);
if (PtcProfiler.Enabled)
{
PtcProfiler.UpdateEntry(request.Address, request.Mode, highCq: true);
}
} }
else else
{ {
_backgroundTranslatorEvent.WaitOne(); _backgroundTranslatorEvent.WaitOne();
} }
} }
_backgroundTranslatorEvent.Set(); // Wake up any other background translator threads, to encourage them to exit. _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 (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). // 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. // 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. // 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. // 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 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++) for (int i = 0; i < threadCount; i++)
{ {
bool last = i != 0 && i == unboundedThreadCount - 1; bool last = i != 0 && i == unboundedThreadCount - 1;
Thread backgroundTranslatorThread = new Thread(TranslateQueuedSubs)
Thread backgroundTranslatorThread = new Thread(TranslateStackedSubs)
{ {
Name = "CPU.BackgroundTranslatorThread." + i, Name = "CPU.BackgroundTranslatorThread." + i,
Priority = last ? ThreadPriority.Lowest : ThreadPriority.Normal Priority = last ? ThreadPriority.Lowest : ThreadPriority.Normal
@ -130,13 +154,19 @@ namespace ARMeilleure.Translation
if (!_funcs.TryGetValue(address, out TranslatedFunction func)) 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); _funcs.TryAdd(address, func);
}
else if (isCallTarget && func.ShouldRejit()) if (PtcProfiler.Enabled)
{ {
_backgroundQueue.Enqueue(0, new RejitRequest(address, mode)); PtcProfiler.AddEntry(address, mode, highCq: false);
}
}
if (isCallTarget && func.ShouldRejit())
{
_backgroundStack.Push(new RejitRequest(address, mode));
_backgroundTranslatorEvent.Set(); _backgroundTranslatorEvent.Set();
} }
@ -144,18 +174,16 @@ namespace ARMeilleure.Translation
return func; 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); PrepareOperandPool(highCq);
PrepareOperationPool(highCq); PrepareOperationPool(highCq);
Logger.StartPass(PassName.Decoding); Logger.StartPass(PassName.Decoding);
Block[] blocks = AlwaysTranslateFunctions Block[] blocks = Decoder.DecodeFunction(memory, address, mode, highCq);
? Decoder.DecodeFunction (_memory, address, mode, highCq)
: Decoder.DecodeBasicBlock(_memory, address, mode);
Logger.EndPass(PassName.Decoding); Logger.EndPass(PassName.Decoding);
@ -182,12 +210,26 @@ namespace ARMeilleure.Translation
CompilerOptions options = highCq ? CompilerOptions.HighCq : CompilerOptions.None; 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); ResetOperandPool(highCq);
ResetOperationPool(highCq); ResetOperationPool(highCq);
return new TranslatedFunction(func, rejit: !highCq); return new TranslatedFunction(func, highCq);
} }
private static ControlFlowGraph EmitAndGetCFG(ArmEmitterContext context, Block[] blocks) private static ControlFlowGraph EmitAndGetCFG(ArmEmitterContext context, Block[] blocks)
@ -264,7 +306,7 @@ namespace ARMeilleure.Translation
context.BranchIfTrue(lblNonZero, count); 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); context.BranchIfTrue(lblExit, running);

View file

@ -44,7 +44,11 @@
- `enable_multicore_scheduling` *(bool)* - `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)* - `enable_fs_integrity_checks` *(bool)*

View file

@ -13,7 +13,7 @@ namespace Ryujinx.Configuration
/// <summary> /// <summary>
/// The current version of the file format /// The current version of the file format
/// </summary> /// </summary>
public const int CurrentVersion = 7; public const int CurrentVersion = 8;
public int Version { get; set; } public int Version { get; set; }
@ -112,6 +112,11 @@ namespace Ryujinx.Configuration
/// </summary> /// </summary>
public bool EnableMulticoreScheduling { get; set; } public bool EnableMulticoreScheduling { get; set; }
/// <summary>
/// Enables or disables profiled translation cache persistency
/// </summary>
public bool EnablePtc { get; set; }
/// <summary> /// <summary>
/// Enables integrity checks on Game content files /// Enables integrity checks on Game content files
/// </summary> /// </summary>

View file

@ -1,4 +1,4 @@
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Configuration.Hid; using Ryujinx.Configuration.Hid;
@ -172,6 +172,11 @@ namespace Ryujinx.Configuration
/// </summary> /// </summary>
public ReactiveObject<bool> EnableMulticoreScheduling { get; private set; } public ReactiveObject<bool> EnableMulticoreScheduling { get; private set; }
/// <summary>
/// Enables or disables profiled translation cache persistency
/// </summary>
public ReactiveObject<bool> EnablePtc { get; private set; }
/// <summary> /// <summary>
/// Enables integrity checks on Game content files /// Enables integrity checks on Game content files
/// </summary> /// </summary>
@ -195,6 +200,7 @@ namespace Ryujinx.Configuration
SystemTimeOffset = new ReactiveObject<long>(); SystemTimeOffset = new ReactiveObject<long>();
EnableDockedMode = new ReactiveObject<bool>(); EnableDockedMode = new ReactiveObject<bool>();
EnableMulticoreScheduling = new ReactiveObject<bool>(); EnableMulticoreScheduling = new ReactiveObject<bool>();
EnablePtc = new ReactiveObject<bool>();
EnableFsIntegrityChecks = new ReactiveObject<bool>(); EnableFsIntegrityChecks = new ReactiveObject<bool>();
FsGlobalAccessLogMode = new ReactiveObject<int>(); FsGlobalAccessLogMode = new ReactiveObject<int>();
IgnoreMissingServices = new ReactiveObject<bool>(); IgnoreMissingServices = new ReactiveObject<bool>();
@ -337,6 +343,7 @@ namespace Ryujinx.Configuration
EnableDiscordIntegration = EnableDiscordIntegration, EnableDiscordIntegration = EnableDiscordIntegration,
EnableVsync = Graphics.EnableVsync, EnableVsync = Graphics.EnableVsync,
EnableMulticoreScheduling = System.EnableMulticoreScheduling, EnableMulticoreScheduling = System.EnableMulticoreScheduling,
EnablePtc = System.EnablePtc,
EnableFsIntegrityChecks = System.EnableFsIntegrityChecks, EnableFsIntegrityChecks = System.EnableFsIntegrityChecks,
FsGlobalAccessLogMode = System.FsGlobalAccessLogMode, FsGlobalAccessLogMode = System.FsGlobalAccessLogMode,
IgnoreMissingServices = System.IgnoreMissingServices, IgnoreMissingServices = System.IgnoreMissingServices,
@ -385,6 +392,7 @@ namespace Ryujinx.Configuration
EnableDiscordIntegration.Value = true; EnableDiscordIntegration.Value = true;
Graphics.EnableVsync.Value = true; Graphics.EnableVsync.Value = true;
System.EnableMulticoreScheduling.Value = true; System.EnableMulticoreScheduling.Value = true;
System.EnablePtc.Value = false;
System.EnableFsIntegrityChecks.Value = true; System.EnableFsIntegrityChecks.Value = true;
System.FsGlobalAccessLogMode.Value = 0; System.FsGlobalAccessLogMode.Value = 0;
System.IgnoreMissingServices.Value = false; 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>(); List<InputConfig> inputConfig = new List<InputConfig>();
foreach (ControllerConfig controllerConfig in configurationFileFormat.ControllerConfig) foreach (ControllerConfig controllerConfig in configurationFileFormat.ControllerConfig)
{ {
@ -600,6 +617,7 @@ namespace Ryujinx.Configuration
EnableDiscordIntegration.Value = configurationFileFormat.EnableDiscordIntegration; EnableDiscordIntegration.Value = configurationFileFormat.EnableDiscordIntegration;
Graphics.EnableVsync.Value = configurationFileFormat.EnableVsync; Graphics.EnableVsync.Value = configurationFileFormat.EnableVsync;
System.EnableMulticoreScheduling.Value = configurationFileFormat.EnableMulticoreScheduling; System.EnableMulticoreScheduling.Value = configurationFileFormat.EnableMulticoreScheduling;
System.EnablePtc.Value = configurationFileFormat.EnablePtc;
System.EnableFsIntegrityChecks.Value = configurationFileFormat.EnableFsIntegrityChecks; System.EnableFsIntegrityChecks.Value = configurationFileFormat.EnableFsIntegrityChecks;
System.FsGlobalAccessLogMode.Value = configurationFileFormat.FsGlobalAccessLogMode; System.FsGlobalAccessLogMode.Value = configurationFileFormat.FsGlobalAccessLogMode;
System.IgnoreMissingServices.Value = configurationFileFormat.IgnoreMissingServices; System.IgnoreMissingServices.Value = configurationFileFormat.IgnoreMissingServices;

View file

@ -14,6 +14,7 @@ namespace Ryujinx.Common.Logging
KernelScheduler, KernelScheduler,
KernelSvc, KernelSvc,
Loader, Loader,
Ptc,
Service, Service,
ServiceAcc, ServiceAcc,
ServiceAm, ServiceAm,

View file

@ -148,8 +148,11 @@ namespace Ryujinx.Graphics.Gpu.Image
bool srgb = descriptor.UnpackSrgb(); bool srgb = descriptor.UnpackSrgb();
if (!FormatTable.TryGetTextureFormat(format, srgb, out FormatInfo formatInfo)) if (!FormatTable.TryGetTextureFormat(format, srgb, out FormatInfo formatInfo))
{
if ((long)address > 0L && (int)format > 0)
{ {
Logger.PrintError(LogClass.Gpu, $"Invalid texture format 0x{format:X} (sRGB: {srgb})."); Logger.PrintError(LogClass.Gpu, $"Invalid texture format 0x{format:X} (sRGB: {srgb}).");
}
formatInfo = FormatInfo.Default; formatInfo = FormatInfo.Default;
} }

View file

@ -1,3 +1,4 @@
using ARMeilleure.Translation.PTC;
using LibHac; using LibHac;
using LibHac.Account; using LibHac.Account;
using LibHac.Common; using LibHac.Common;
@ -6,7 +7,6 @@ using LibHac.FsSystem;
using LibHac.FsSystem.NcaUtils; using LibHac.FsSystem.NcaUtils;
using LibHac.Ncm; using LibHac.Ncm;
using LibHac.Ns; using LibHac.Ns;
using LibHac.Spl;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem;
@ -31,17 +31,19 @@ namespace Ryujinx.HLE.HOS
private readonly ContentManager _contentManager; private readonly ContentManager _contentManager;
private readonly VirtualFileSystem _fileSystem; 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 ulong TitleId { get; private set; }
public string TitleIdText => TitleId.ToString("x16"); 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 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) 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 mainNca = null;
Nca patchNca = null; Nca patchNca = null;
@ -284,11 +286,6 @@ namespace Ryujinx.HLE.HOS
_fileSystem.SetRomFs(dataStorage.AsStream(FileAccess.Read)); _fileSystem.SetRomFs(dataStorage.AsStream(FileAccess.Read));
} }
LoadExeFs(codeFs, out Npdm metaData);
TitleId = metaData.Aci0.TitleId;
TitleIs64Bit = metaData.Is64Bit;
if (controlNca != null) if (controlNca != null)
{ {
ReadControlData(controlNca); ReadControlData(controlNca);
@ -298,12 +295,14 @@ namespace Ryujinx.HLE.HOS
ControlData.ByteSpan.Clear(); ControlData.ByteSpan.Clear();
} }
LoadExeFs(codeFs, out _);
if (TitleId != 0) if (TitleId != 0)
{ {
EnsureSaveData(new TitleId(TitleId)); 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) public void ReadControlData(Nca controlNca)
@ -327,7 +326,7 @@ namespace Ryujinx.HLE.HOS
.FirstOrDefault(x => x.Name[0] != 0).Name.ToString(); .FirstOrDefault(x => x.Name[0] != 0).Name.ToString();
} }
TitleVersionString = ControlData.Value.DisplayVersion.ToString(); DisplayVersion = ControlData.Value.DisplayVersion.ToString();
} }
} }
else else
@ -382,6 +381,8 @@ namespace Ryujinx.HLE.HOS
_contentManager.LoadEntries(_device); _contentManager.LoadEntries(_device);
Ptc.Initialize(TitleIdText, DisplayVersion, EnablePtc);
ProgramLoader.LoadNsos(_device.System.KernelContext, metaData, executables: nsos.ToArray()); ProgramLoader.LoadNsos(_device.System.KernelContext, metaData, executables: nsos.ToArray());
} }

View file

@ -66,6 +66,7 @@ namespace Ryujinx.HLE.HOS
#pragma warning restore CS0649 #pragma warning restore CS0649
private bool _isDisposed; private bool _isDisposed;
public bool EnablePtc { get; set; }
public IntegrityCheckLevel FsIntegrityCheckLevel { get; set; } public IntegrityCheckLevel FsIntegrityCheckLevel { get; set; }

View file

@ -1,3 +1,4 @@
using ARMeilleure.Translation.PTC;
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Cpu; using Ryujinx.Cpu;
@ -10,7 +11,7 @@ using Ryujinx.HLE.Loaders.Npdm;
namespace Ryujinx.HLE.HOS namespace Ryujinx.HLE.HOS
{ {
class ProgramLoader static class ProgramLoader
{ {
private const bool AslrEnabled = true; 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 codePagesCount = codeSize / KMemoryManager.PageSize;
int personalMmHeapPagesCount = metaData.PersonalMmHeapSize / KMemoryManager.PageSize; int personalMmHeapPagesCount = metaData.PersonalMmHeapSize / KMemoryManager.PageSize;

View file

@ -80,6 +80,8 @@ namespace Ryujinx.HLE
System.EnableMultiCoreScheduling(); System.EnableMultiCoreScheduling();
} }
System.EnablePtc = ConfigurationState.Instance.System.EnablePtc;
System.FsIntegrityCheckLevel = GetIntegrityCheckLevel(); System.FsIntegrityCheckLevel = GetIntegrityCheckLevel();
System.GlobalAccessLogMode = ConfigurationState.Instance.System.FsGlobalAccessLogMode; System.GlobalAccessLogMode = ConfigurationState.Instance.System.FsGlobalAccessLogMode;

View file

@ -1,11 +1,10 @@
using ARMeilleure.State; using ARMeilleure.State;
using NUnit.Framework; using NUnit.Framework;
using Ryujinx.Cpu; using Ryujinx.Cpu;
using Ryujinx.Memory; using Ryujinx.Memory;
using Ryujinx.Tests.Unicorn; using Ryujinx.Tests.Unicorn;
using System; using System;
using MemoryPermission = Ryujinx.Tests.Unicorn.MemoryPermission; using MemoryPermission = Ryujinx.Tests.Unicorn.MemoryPermission;
namespace Ryujinx.Tests.Cpu namespace Ryujinx.Tests.Cpu
@ -69,6 +68,7 @@ namespace Ryujinx.Tests.Cpu
_memory.Dispose(); _memory.Dispose();
_context.Dispose(); _context.Dispose();
_ram.Dispose(); _ram.Dispose();
_memory = null; _memory = null;
_context = null; _context = null;
_cpuContext = null; _cpuContext = null;

View file

@ -1,11 +1,10 @@
using ARMeilleure.State; using ARMeilleure.State;
using NUnit.Framework; using NUnit.Framework;
using Ryujinx.Cpu; using Ryujinx.Cpu;
using Ryujinx.Memory; using Ryujinx.Memory;
using Ryujinx.Tests.Unicorn; using Ryujinx.Tests.Unicorn;
using System; using System;
using MemoryPermission = Ryujinx.Tests.Unicorn.MemoryPermission; using MemoryPermission = Ryujinx.Tests.Unicorn.MemoryPermission;
namespace Ryujinx.Tests.Cpu namespace Ryujinx.Tests.Cpu
@ -73,6 +72,7 @@ namespace Ryujinx.Tests.Cpu
_memory.Dispose(); _memory.Dispose();
_context.Dispose(); _context.Dispose();
_ram.Dispose(); _ram.Dispose();
_memory = null; _memory = null;
_context = null; _context = null;
_cpuContext = null; _cpuContext = null;

View file

@ -1,5 +1,5 @@
{ {
"version": 7, "version": 8,
"max_anisotropy": -1, "max_anisotropy": -1,
"graphics_shaders_dump_path": "", "graphics_shaders_dump_path": "",
"logging_enable_debug": false, "logging_enable_debug": false,
@ -19,6 +19,7 @@
"enable_discord_integration": true, "enable_discord_integration": true,
"enable_vsync": true, "enable_vsync": true,
"enable_multicore_scheduling": true, "enable_multicore_scheduling": true,
"enable_ptc": false,
"enable_fs_integrity_checks": true, "enable_fs_integrity_checks": true,
"fs_global_access_log_mode": 0, "fs_global_access_log_mode": 0,
"ignore_missing_services": false, "ignore_missing_services": false,

View file

@ -1,3 +1,4 @@
using ARMeilleure.Translation.PTC;
using Gtk; using Gtk;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Common.SystemInfo; using Ryujinx.Common.SystemInfo;
@ -110,9 +111,15 @@ namespace Ryujinx
Logger.PrintError(LogClass.Application, $"Unhandled exception caught: {exception}"); Logger.PrintError(LogClass.Application, $"Unhandled exception caught: {exception}");
Ptc.Close();
PtcProfiler.Stop();
if (e.IsTerminating) if (e.IsTerminating)
{ {
Logger.Shutdown(); Logger.Shutdown();
Ptc.Dispose();
PtcProfiler.Dispose();
} }
} }
} }

View file

@ -1,3 +1,4 @@
using ARMeilleure.Translation.PTC;
using Gdk; using Gdk;
using OpenTK; using OpenTK;
using OpenTK.Graphics; using OpenTK.Graphics;
@ -183,8 +184,8 @@ namespace Ryujinx.Ui
string titleNameSection = string.IsNullOrWhiteSpace(_device.Application.TitleName) ? string.Empty string titleNameSection = string.IsNullOrWhiteSpace(_device.Application.TitleName) ? string.Empty
: $" - {_device.Application.TitleName}"; : $" - {_device.Application.TitleName}";
string titleVersionSection = string.IsNullOrWhiteSpace(_device.Application.TitleVersionString) ? string.Empty string titleVersionSection = string.IsNullOrWhiteSpace(_device.Application.DisplayVersion) ? string.Empty
: $" v{_device.Application.TitleVersionString}"; : $" v{_device.Application.DisplayVersion}";
string titleIdSection = string.IsNullOrWhiteSpace(_device.Application.TitleIdText) ? string.Empty string titleIdSection = string.IsNullOrWhiteSpace(_device.Application.TitleIdText) ? string.Empty
: $" ({_device.Application.TitleIdText.ToUpper()})"; : $" ({_device.Application.TitleIdText.ToUpper()})";
@ -378,7 +379,17 @@ namespace Ryujinx.Ui
{ {
Gtk.Application.Invoke(delegate 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();
}
}
}); });
} }

View file

@ -1,3 +1,4 @@
using ARMeilleure.Translation.PTC;
using Gtk; using Gtk;
using LibHac.Common; using LibHac.Common;
using LibHac.Ns; using LibHac.Ns;
@ -470,6 +471,9 @@ namespace Ryujinx.Ui
_glWidget.Start(); _glWidget.Start();
Ptc.Close();
PtcProfiler.Stop();
device.Dispose(); device.Dispose();
_deviceExitStatus.Set(); _deviceExitStatus.Set();
@ -597,6 +601,10 @@ namespace Ryujinx.Ui
Profile.FinishProfiling(); Profile.FinishProfiling();
DiscordIntegrationModule.Exit(); DiscordIntegrationModule.Exit();
Logger.Shutdown(); Logger.Shutdown();
Ptc.Dispose();
PtcProfiler.Dispose();
Application.Quit(); Application.Quit();
} }

View file

@ -35,6 +35,7 @@ namespace Ryujinx.Ui
[GUI] CheckButton _discordToggle; [GUI] CheckButton _discordToggle;
[GUI] CheckButton _vSyncToggle; [GUI] CheckButton _vSyncToggle;
[GUI] CheckButton _multiSchedToggle; [GUI] CheckButton _multiSchedToggle;
[GUI] CheckButton _ptcToggle;
[GUI] CheckButton _fsicToggle; [GUI] CheckButton _fsicToggle;
[GUI] CheckButton _ignoreToggle; [GUI] CheckButton _ignoreToggle;
[GUI] CheckButton _directKeyboardAccess; [GUI] CheckButton _directKeyboardAccess;
@ -152,6 +153,11 @@ namespace Ryujinx.Ui
_multiSchedToggle.Click(); _multiSchedToggle.Click();
} }
if (ConfigurationState.Instance.System.EnablePtc)
{
_ptcToggle.Click();
}
if (ConfigurationState.Instance.System.EnableFsIntegrityChecks) if (ConfigurationState.Instance.System.EnableFsIntegrityChecks)
{ {
_fsicToggle.Click(); _fsicToggle.Click();
@ -381,6 +387,7 @@ namespace Ryujinx.Ui
ConfigurationState.Instance.EnableDiscordIntegration.Value = _discordToggle.Active; ConfigurationState.Instance.EnableDiscordIntegration.Value = _discordToggle.Active;
ConfigurationState.Instance.Graphics.EnableVsync.Value = _vSyncToggle.Active; ConfigurationState.Instance.Graphics.EnableVsync.Value = _vSyncToggle.Active;
ConfigurationState.Instance.System.EnableMulticoreScheduling.Value = _multiSchedToggle.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.EnableFsIntegrityChecks.Value = _fsicToggle.Active;
ConfigurationState.Instance.System.IgnoreMissingServices.Value = _ignoreToggle.Active; ConfigurationState.Instance.System.IgnoreMissingServices.Value = _ignoreToggle.Active;
ConfigurationState.Instance.Hid.EnableKeyboard.Value = _directKeyboardAccess.Active; ConfigurationState.Instance.Hid.EnableKeyboard.Value = _directKeyboardAccess.Active;

View file

@ -1398,6 +1398,24 @@
<property name="position">5</property> <property name="position">5</property>
</packing> </packing>
</child> </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> <child>
<object class="GtkCheckButton" id="_fsicToggle"> <object class="GtkCheckButton" id="_fsicToggle">
<property name="label" translatable="yes">Enable FS Integrity Checks</property> <property name="label" translatable="yes">Enable FS Integrity Checks</property>
@ -1413,7 +1431,7 @@
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
<property name="fill">True</property> <property name="fill">True</property>
<property name="position">6</property> <property name="position">7</property>
</packing> </packing>
</child> </child>
</object> </object>

View file

@ -19,6 +19,7 @@
"docked_mode", "docked_mode",
"enable_vsync", "enable_vsync",
"enable_multicore_scheduling", "enable_multicore_scheduling",
"enable_ptc",
"enable_fs_integrity_checks", "enable_fs_integrity_checks",
"fs_global_access_log_mode", "fs_global_access_log_mode",
"controller_type", "controller_type",
@ -478,6 +479,17 @@
false 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": { "enable_fs_integrity_checks": {
"$id": "#/properties/enable_fs_integrity_checks", "$id": "#/properties/enable_fs_integrity_checks",
"type": "boolean", "type": "boolean",
@ -581,7 +593,7 @@
"$id": "#/properties/enable_keyboard", "$id": "#/properties/enable_keyboard",
"type": "boolean", "type": "boolean",
"title": "(HID) Keyboard Enable", "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, "default": true,
"examples": [ "examples": [
true, true,