mirror of
https://git.ryujinx.app/projects/Kenji-NX.git
synced 2026-10-04 15:18:54 +02:00
Compare commits
5
Commits
master
..
72ecef8b9f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
72ecef8b9f | ||
|
|
472b0e2bdb | ||
|
|
8f6d22d92d | ||
|
|
645a4c99af | ||
|
|
a94c18bc0a |
+19
-18
@@ -3,25 +3,25 @@
|
|||||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageVersion Include="Avalonia" Version="12.1.2" />
|
<PackageVersion Include="Avalonia" Version="11.3.16" />
|
||||||
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="12.1.2" />
|
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.13" />
|
||||||
<PackageVersion Include="Avalonia.Desktop" Version="12.1.2" />
|
<PackageVersion Include="Avalonia.Desktop" Version="11.3.16" />
|
||||||
<PackageVersion Include="AvaloniaUI.DiagnosticsSupport" Version="2.2.3" />
|
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.16" />
|
||||||
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="12.1.2" />
|
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.3.16" />
|
||||||
<PackageVersion Include="Svg.Controls.Avalonia" Version="12.0.0.17" />
|
<PackageVersion Include="Svg.Controls.Avalonia" Version="11.3.9.5" />
|
||||||
<PackageVersion Include="Svg.Controls.Skia.Avalonia" Version="12.0.0.17" />
|
<PackageVersion Include="Svg.Controls.Skia.Avalonia" Version="11.3.9.5" />
|
||||||
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
|
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
|
||||||
<PackageVersion Include="CommunityToolkit.Mvvm" Version="8.4.2" />
|
<PackageVersion Include="CommunityToolkit.Mvvm" Version="8.4.2" />
|
||||||
<PackageVersion Include="Concentus" Version="2.2.2" />
|
<PackageVersion Include="Concentus" Version="2.2.2" />
|
||||||
<PackageVersion Include="DiscordRichPresence" Version="1.6.1.70" />
|
<PackageVersion Include="DiscordRichPresence" Version="1.6.1.70" />
|
||||||
<PackageVersion Include="DynamicData" Version="9.4.33" />
|
<PackageVersion Include="DynamicData" Version="9.4.31" />
|
||||||
<PackageVersion Include="FluentAvaloniaUI" Version="3.1.0" />
|
<PackageVersion Include="FluentAvaloniaUI" Version="2.5.1" />
|
||||||
<PackageVersion Include="Gommon" Version="2.8.1.2" />
|
<PackageVersion Include="Gommon" Version="2.8.1.2" />
|
||||||
<PackageVersion Include="Humanizer" Version="3.0.10" />
|
<PackageVersion Include="Humanizer" Version="3.0.10" />
|
||||||
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
|
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
|
||||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.12.0" />
|
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.12.0" />
|
||||||
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.22.0" />
|
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.18.0" />
|
||||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.10.0" />
|
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.5.1" />
|
||||||
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.1" />
|
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.1" />
|
||||||
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
|
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
|
||||||
<PackageVersion Include="NetCoreServer" Version="8.0.7" />
|
<PackageVersion Include="NetCoreServer" Version="8.0.7" />
|
||||||
@@ -31,24 +31,25 @@
|
|||||||
<PackageVersion Include="OpenTK.Core" Version="4.9.4" />
|
<PackageVersion Include="OpenTK.Core" Version="4.9.4" />
|
||||||
<PackageVersion Include="OpenTK.Graphics" Version="4.9.4" />
|
<PackageVersion Include="OpenTK.Graphics" Version="4.9.4" />
|
||||||
<PackageVersion Include="OpenTK.Audio.OpenAL" Version="4.9.4" />
|
<PackageVersion Include="OpenTK.Audio.OpenAL" Version="4.9.4" />
|
||||||
|
<PackageVersion Include="OpenTK.Windowing.GraphicsLibraryFramework" Version="4.9.4" />
|
||||||
<PackageVersion Include="Open.NAT.Core" Version="2.1.0.5" />
|
<PackageVersion Include="Open.NAT.Core" Version="2.1.0.5" />
|
||||||
<PackageVersion Include="Ryujinx.Audio.OpenAL" Version="1.25.2" />
|
<PackageVersion Include="Ryujinx.Audio.OpenAL" Version="1.25.1" />
|
||||||
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies.Linux" Version="6.1.4-build6" />
|
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies.Linux" Version="6.1.4-build6" />
|
||||||
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies.macOS" Version="5.0.3-build14" />
|
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies.macOS" Version="5.0.3-build14" />
|
||||||
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies.Windows" Version="6.1.4-build6" />
|
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies.Windows" Version="6.1.4-build6" />
|
||||||
<PackageVersion Include="Ryujinx.Graphics.Vulkan.MoltenVK" Version="1.4.3-ryujinx.1" />
|
<PackageVersion Include="Ryujinx.Graphics.Vulkan.MoltenVK" Version="1.4.2-ryujinx.3" />
|
||||||
<PackageVersion Include="Ryujinx.LibHac" Version="0.21.0-alpha.133" />
|
<PackageVersion Include="Ryujinx.LibHac" Version="0.21.0-alpha.133" />
|
||||||
<PackageVersion Include="Ryujinx.SDL3-CS" Version="2026.918.0" />
|
<PackageVersion Include="Ryujinx.SDL3-CS" Version="2026.501.0" />
|
||||||
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
|
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
|
||||||
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
|
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
|
||||||
<PackageVersion Include="Silk.NET.Shaderc" Version="2.23.0" />
|
<PackageVersion Include="Silk.NET.Shaderc" Version="2.23.0" />
|
||||||
<PackageVersion Include="Silk.NET.Vulkan" Version="2.23.0" />
|
<PackageVersion Include="Silk.NET.Vulkan" Version="2.23.0" />
|
||||||
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.23.0" />
|
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.23.0" />
|
||||||
<PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.23.0" />
|
<PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.23.0" />
|
||||||
<PackageVersion Include="SkiaSharp" Version="3.119.4" />
|
<PackageVersion Include="SkiaSharp" Version="3.119.2" />
|
||||||
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="3.119.4" />
|
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="3.119.2" />
|
||||||
<PackageVersion Include="SPB" Version="0.0.4-build32" />
|
<PackageVersion Include="SPB" Version="0.0.4-build32" />
|
||||||
<PackageVersion Include="System.IO.Hashing" Version="10.0.12" />
|
<PackageVersion Include="System.IO.Hashing" Version="10.0.8" />
|
||||||
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.1.0" />
|
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.1.4-a40db6c" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
@@ -21,7 +21,6 @@
|
|||||||
<Project Path="src/Ryujinx.Graphics.Nvdec.Vp9/Ryujinx.Graphics.Nvdec.Vp9.csproj" />
|
<Project Path="src/Ryujinx.Graphics.Nvdec.Vp9/Ryujinx.Graphics.Nvdec.Vp9.csproj" />
|
||||||
<Project Path="src/Ryujinx.Graphics.Nvdec/Ryujinx.Graphics.Nvdec.csproj" />
|
<Project Path="src/Ryujinx.Graphics.Nvdec/Ryujinx.Graphics.Nvdec.csproj" />
|
||||||
<Project Path="src/Ryujinx.Graphics.OpenGL/Ryujinx.Graphics.OpenGL.csproj" />
|
<Project Path="src/Ryujinx.Graphics.OpenGL/Ryujinx.Graphics.OpenGL.csproj" />
|
||||||
<Project Path="src/Ryujinx.Graphics.RenderDocApi/Ryujinx.Graphics.RenderDocApi.csproj" />
|
|
||||||
<Project Path="src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj" />
|
<Project Path="src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj" />
|
||||||
<Project Path="src/Ryujinx.Graphics.Texture/Ryujinx.Graphics.Texture.csproj" />
|
<Project Path="src/Ryujinx.Graphics.Texture/Ryujinx.Graphics.Texture.csproj" />
|
||||||
<Project Path="src/Ryujinx.Graphics.Vic/Ryujinx.Graphics.Vic.csproj" />
|
<Project Path="src/Ryujinx.Graphics.Vic/Ryujinx.Graphics.Vic.csproj" />
|
||||||
|
|||||||
@@ -13,13 +13,13 @@ namespace ARMeilleure.CodeGen.Arm64
|
|||||||
|
|
||||||
public static void RunPass(ControlFlowGraph cfg)
|
public static void RunPass(ControlFlowGraph cfg)
|
||||||
{
|
{
|
||||||
Dictionary<ulong, Operand> constants = new();
|
var constants = new Dictionary<ulong, Operand>();
|
||||||
|
|
||||||
Operand GetConstantCopy(BasicBlock block, Operation operation, Operand source)
|
Operand GetConstantCopy(BasicBlock block, Operation operation, Operand source)
|
||||||
{
|
{
|
||||||
// If the constant has many uses, we also force a new constant mov to be added, in order
|
// If the constant has many uses, we also force a new constant mov to be added, in order
|
||||||
// to avoid overflow of the counts field (that is limited to 16 bits).
|
// to avoid overflow of the counts field (that is limited to 16 bits).
|
||||||
if (!constants.TryGetValue(source.Value, out Operand constant) || constant.UsesCount > MaxConstantUses)
|
if (!constants.TryGetValue(source.Value, out var constant) || constant.UsesCount > MaxConstantUses)
|
||||||
{
|
{
|
||||||
constant = Local(source.Type);
|
constant = Local(source.Type);
|
||||||
|
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ namespace ARMeilleure.CodeGen.Arm64
|
|||||||
|
|
||||||
public void Cset(Operand rd, ArmCondition condition)
|
public void Cset(Operand rd, ArmCondition condition)
|
||||||
{
|
{
|
||||||
Operand zr = Factory.Register(ZrRegister, RegisterType.Integer, rd.Type);
|
var zr = Factory.Register(ZrRegister, RegisterType.Integer, rd.Type);
|
||||||
Csinc(rd, zr, zr, (ArmCondition)((int)condition ^ 1));
|
Csinc(rd, zr, zr, (ArmCondition)((int)condition ^ 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ namespace ARMeilleure.CodeGen.Arm64
|
|||||||
|
|
||||||
long target = _stream.Position;
|
long target = _stream.Position;
|
||||||
|
|
||||||
if (_pendingBranches.TryGetValue(block, out List<(ArmCondition Condition, long BranchPos)> list))
|
if (_pendingBranches.TryGetValue(block, out var list))
|
||||||
{
|
{
|
||||||
foreach ((ArmCondition condition, long branchPos) in list)
|
foreach ((ArmCondition condition, long branchPos) in list)
|
||||||
{
|
{
|
||||||
@@ -119,7 +119,7 @@ namespace ARMeilleure.CodeGen.Arm64
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!_pendingBranches.TryGetValue(target, out List<(ArmCondition Condition, long BranchPos)> list))
|
if (!_pendingBranches.TryGetValue(target, out var list))
|
||||||
{
|
{
|
||||||
list = new List<(ArmCondition, long)>();
|
list = new List<(ArmCondition, long)>();
|
||||||
_pendingBranches.Add(target, list);
|
_pendingBranches.Add(target, list);
|
||||||
|
|||||||
@@ -321,7 +321,7 @@ namespace ARMeilleure.CodeGen.Arm64
|
|||||||
|
|
||||||
Debug.Assert(comp.Kind == OperandKind.Constant);
|
Debug.Assert(comp.Kind == OperandKind.Constant);
|
||||||
|
|
||||||
ArmCondition cond = ((Comparison)comp.AsInt32()).ToArmCondition();
|
var cond = ((Comparison)comp.AsInt32()).ToArmCondition();
|
||||||
|
|
||||||
GenerateCompareCommon(context, operation);
|
GenerateCompareCommon(context, operation);
|
||||||
|
|
||||||
@@ -353,7 +353,7 @@ namespace ARMeilleure.CodeGen.Arm64
|
|||||||
Debug.Assert(dest.Type == OperandType.I32);
|
Debug.Assert(dest.Type == OperandType.I32);
|
||||||
Debug.Assert(comp.Kind == OperandKind.Constant);
|
Debug.Assert(comp.Kind == OperandKind.Constant);
|
||||||
|
|
||||||
ArmCondition cond = ((Comparison)comp.AsInt32()).ToArmCondition();
|
var cond = ((Comparison)comp.AsInt32()).ToArmCondition();
|
||||||
|
|
||||||
GenerateCompareCommon(context, operation);
|
GenerateCompareCommon(context, operation);
|
||||||
|
|
||||||
|
|||||||
@@ -847,7 +847,7 @@ namespace ARMeilleure.CodeGen.Arm64
|
|||||||
|
|
||||||
Debug.Assert(comp.Kind == OperandKind.Constant);
|
Debug.Assert(comp.Kind == OperandKind.Constant);
|
||||||
|
|
||||||
Comparison compType = (Comparison)comp.AsInt32();
|
var compType = (Comparison)comp.AsInt32();
|
||||||
|
|
||||||
return compType is Comparison.Equal or Comparison.NotEqual;
|
return compType is Comparison.Equal or Comparison.NotEqual;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ namespace ARMeilleure.CodeGen
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a compiled function.
|
/// Represents a compiled function.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly struct CompiledFunction
|
readonly struct CompiledFunction
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the machine code of the <see cref="CompiledFunction"/>.
|
/// Gets the machine code of the <see cref="CompiledFunction"/>.
|
||||||
@@ -44,11 +44,10 @@ namespace ARMeilleure.CodeGen
|
|||||||
/// <typeparamref name="T"/> pointing to the mapped function.
|
/// <typeparamref name="T"/> pointing to the mapped function.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">Type of delegate</typeparam>
|
/// <typeparam name="T">Type of delegate</typeparam>
|
||||||
/// <param name="jitCache">The jit cache to map the function into</param>
|
|
||||||
/// <returns>A delegate of type <typeparamref name="T"/> pointing to the mapped function</returns>
|
/// <returns>A delegate of type <typeparamref name="T"/> pointing to the mapped function</returns>
|
||||||
public T Map<T>(JitCache jitCache)
|
public T Map<T>()
|
||||||
{
|
{
|
||||||
return MapWithPointer<T>(jitCache, out _);
|
return MapWithPointer<T>(out _);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -56,12 +55,11 @@ namespace ARMeilleure.CodeGen
|
|||||||
/// <typeparamref name="T"/> pointing to the mapped function.
|
/// <typeparamref name="T"/> pointing to the mapped function.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">Type of delegate</typeparam>
|
/// <typeparam name="T">Type of delegate</typeparam>
|
||||||
/// <param name="jitCache">The jit cache to map the function into</param>
|
|
||||||
/// <param name="codePointer">Pointer to the function code in memory</param>
|
/// <param name="codePointer">Pointer to the function code in memory</param>
|
||||||
/// <returns>A delegate of type <typeparamref name="T"/> pointing to the mapped function</returns>
|
/// <returns>A delegate of type <typeparamref name="T"/> pointing to the mapped function</returns>
|
||||||
public T MapWithPointer<T>(JitCache jitCache, out nint codePointer)
|
public T MapWithPointer<T>(out nint codePointer)
|
||||||
{
|
{
|
||||||
codePointer = jitCache.Map(this);
|
codePointer = JitCache.Map(this);
|
||||||
|
|
||||||
return Marshal.GetDelegateForFunctionPointer<T>(codePointer);
|
return Marshal.GetDelegateForFunctionPointer<T>(codePointer);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ namespace ARMeilleure.CodeGen.Linking
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a relocation.
|
/// Represents a relocation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly struct RelocEntry
|
readonly struct RelocEntry
|
||||||
{
|
{
|
||||||
public const int Stride = 13; // Bytes.
|
public const int Stride = 13; // Bytes.
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ namespace ARMeilleure.CodeGen.Linking
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents relocation information about a <see cref="CompiledFunction"/>.
|
/// Represents relocation information about a <see cref="CompiledFunction"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly struct RelocInfo
|
readonly struct RelocInfo
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets an empty <see cref="RelocInfo"/>.
|
/// Gets an empty <see cref="RelocInfo"/>.
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ namespace ARMeilleure.CodeGen.Linking
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a symbol.
|
/// Represents a symbol.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly struct Symbol
|
readonly struct Symbol
|
||||||
{
|
{
|
||||||
private readonly ulong _value;
|
private readonly ulong _value;
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ namespace ARMeilleure.CodeGen.Linking
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Types of <see cref="Symbol"/>.
|
/// Types of <see cref="Symbol"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public enum SymbolType : byte
|
enum SymbolType : byte
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Refers to nothing, i.e no symbol.
|
/// Refers to nothing, i.e no symbol.
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
|||||||
{
|
{
|
||||||
NumberLocals(cfg, regMasks.RegistersCount);
|
NumberLocals(cfg, regMasks.RegistersCount);
|
||||||
|
|
||||||
AllocationContext context = new(stackAlloc, regMasks, _intervals.Count);
|
var context = new AllocationContext(stackAlloc, regMasks, _intervals.Count);
|
||||||
|
|
||||||
BuildIntervals(cfg, context);
|
BuildIntervals(cfg, context);
|
||||||
|
|
||||||
|
|||||||
@@ -15,12 +15,12 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
|||||||
{
|
{
|
||||||
if (_count + 1 > _capacity)
|
if (_count + 1 > _capacity)
|
||||||
{
|
{
|
||||||
Span<LiveInterval> oldSpan = Span;
|
var oldSpan = Span;
|
||||||
|
|
||||||
_capacity = Math.Max(4, _capacity * 2);
|
_capacity = Math.Max(4, _capacity * 2);
|
||||||
_items = Allocators.References.Allocate<LiveInterval>((uint)_capacity);
|
_items = Allocators.References.Allocate<LiveInterval>((uint)_capacity);
|
||||||
|
|
||||||
Span<LiveInterval> newSpan = Span;
|
var newSpan = Span;
|
||||||
|
|
||||||
oldSpan.CopyTo(newSpan);
|
oldSpan.CopyTo(newSpan);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,12 +16,12 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
|||||||
{
|
{
|
||||||
if (Count + 1 > _capacity)
|
if (Count + 1 > _capacity)
|
||||||
{
|
{
|
||||||
Span<int> oldSpan = Span;
|
var oldSpan = Span;
|
||||||
|
|
||||||
_capacity = Math.Max(4, _capacity * 2);
|
_capacity = Math.Max(4, _capacity * 2);
|
||||||
_items = Allocators.Default.Allocate<int>((uint)_capacity);
|
_items = Allocators.Default.Allocate<int>((uint)_capacity);
|
||||||
|
|
||||||
Span<int> newSpan = Span;
|
var newSpan = Span;
|
||||||
|
|
||||||
oldSpan.CopyTo(newSpan);
|
oldSpan.CopyTo(newSpan);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
namespace ARMeilleure.CodeGen.Unwinding
|
namespace ARMeilleure.CodeGen.Unwinding
|
||||||
{
|
{
|
||||||
public struct UnwindInfo
|
struct UnwindInfo
|
||||||
{
|
{
|
||||||
public const int Stride = 4; // Bytes.
|
public const int Stride = 4; // Bytes.
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
namespace ARMeilleure.CodeGen.Unwinding
|
namespace ARMeilleure.CodeGen.Unwinding
|
||||||
{
|
{
|
||||||
public enum UnwindPseudoOp
|
enum UnwindPseudoOp
|
||||||
{
|
{
|
||||||
PushReg = 0,
|
PushReg = 0,
|
||||||
SetFrame = 1,
|
SetFrame = 1,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
namespace ARMeilleure.CodeGen.Unwinding
|
namespace ARMeilleure.CodeGen.Unwinding
|
||||||
{
|
{
|
||||||
public struct UnwindPushEntry
|
struct UnwindPushEntry
|
||||||
{
|
{
|
||||||
public const int Stride = 16; // Bytes.
|
public const int Stride = 16; // Bytes.
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using ARMeilleure.CodeGen.Linking;
|
using ARMeilleure.CodeGen.Linking;
|
||||||
using ARMeilleure.IntermediateRepresentation;
|
using ARMeilleure.IntermediateRepresentation;
|
||||||
using Microsoft.IO;
|
|
||||||
using Ryujinx.Common.Memory;
|
using Ryujinx.Common.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -1325,8 +1324,8 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
|
|
||||||
public (byte[], RelocInfo) GetCode()
|
public (byte[], RelocInfo) GetCode()
|
||||||
{
|
{
|
||||||
Span<Jump> jumps = CollectionsMarshal.AsSpan(_jumps);
|
var jumps = CollectionsMarshal.AsSpan(_jumps);
|
||||||
Span<Reloc> relocs = CollectionsMarshal.AsSpan(_relocs);
|
var relocs = CollectionsMarshal.AsSpan(_relocs);
|
||||||
|
|
||||||
// Write jump relative offsets.
|
// Write jump relative offsets.
|
||||||
bool modified;
|
bool modified;
|
||||||
@@ -1411,13 +1410,13 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
// Write the code, ignoring the dummy bytes after jumps, into a new stream.
|
// Write the code, ignoring the dummy bytes after jumps, into a new stream.
|
||||||
_stream.Seek(0, SeekOrigin.Begin);
|
_stream.Seek(0, SeekOrigin.Begin);
|
||||||
|
|
||||||
using RecyclableMemoryStream codeStream = MemoryStreamManager.Shared.GetStream();
|
using var codeStream = MemoryStreamManager.Shared.GetStream();
|
||||||
Assembler assembler = new(codeStream, HasRelocs);
|
var assembler = new Assembler(codeStream, HasRelocs);
|
||||||
|
|
||||||
bool hasRelocs = HasRelocs;
|
bool hasRelocs = HasRelocs;
|
||||||
int relocIndex = 0;
|
int relocIndex = 0;
|
||||||
int relocOffset = 0;
|
int relocOffset = 0;
|
||||||
RelocEntry[] relocEntries = hasRelocs
|
var relocEntries = hasRelocs
|
||||||
? new RelocEntry[relocs.Length]
|
? new RelocEntry[relocs.Length]
|
||||||
: Array.Empty<RelocEntry>();
|
: Array.Empty<RelocEntry>();
|
||||||
|
|
||||||
@@ -1470,8 +1469,8 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
|
|
||||||
_stream.CopyTo(codeStream);
|
_stream.CopyTo(codeStream);
|
||||||
|
|
||||||
byte[] code = codeStream.ToArray();
|
var code = codeStream.ToArray();
|
||||||
RelocInfo relocInfo = new(relocEntries);
|
var relocInfo = new RelocInfo(relocEntries);
|
||||||
|
|
||||||
return (code, relocInfo);
|
return (code, relocInfo);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -622,7 +622,7 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
|
|
||||||
Debug.Assert(comp.Kind == OperandKind.Constant);
|
Debug.Assert(comp.Kind == OperandKind.Constant);
|
||||||
|
|
||||||
X86Condition cond = ((Comparison)comp.AsInt32()).ToX86Condition();
|
var cond = ((Comparison)comp.AsInt32()).ToX86Condition();
|
||||||
|
|
||||||
GenerateCompareCommon(context, operation);
|
GenerateCompareCommon(context, operation);
|
||||||
|
|
||||||
@@ -660,7 +660,7 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
Debug.Assert(dest.Type == OperandType.I32);
|
Debug.Assert(dest.Type == OperandType.I32);
|
||||||
Debug.Assert(comp.Kind == OperandKind.Constant);
|
Debug.Assert(comp.Kind == OperandKind.Constant);
|
||||||
|
|
||||||
X86Condition cond = ((Comparison)comp.AsInt32()).ToX86Condition();
|
var cond = ((Comparison)comp.AsInt32()).ToX86Condition();
|
||||||
|
|
||||||
GenerateCompareCommon(context, operation);
|
GenerateCompareCommon(context, operation);
|
||||||
|
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
|
|
||||||
memGetXcr0.Reprotect(0, (ulong)asmGetXcr0.Length, MemoryPermission.ReadAndExecute);
|
memGetXcr0.Reprotect(0, (ulong)asmGetXcr0.Length, MemoryPermission.ReadAndExecute);
|
||||||
|
|
||||||
GetXcr0 fGetXcr0 = Marshal.GetDelegateForFunctionPointer<GetXcr0>(memGetXcr0.Pointer);
|
var fGetXcr0 = Marshal.GetDelegateForFunctionPointer<GetXcr0>(memGetXcr0.Pointer);
|
||||||
|
|
||||||
return fGetXcr0();
|
return fGetXcr0();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -759,7 +759,7 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
|
|
||||||
Debug.Assert(comp.Kind == OperandKind.Constant);
|
Debug.Assert(comp.Kind == OperandKind.Constant);
|
||||||
|
|
||||||
Comparison compType = (Comparison)comp.AsInt32();
|
var compType = (Comparison)comp.AsInt32();
|
||||||
|
|
||||||
return compType is Comparison.Equal or Comparison.NotEqual;
|
return compType is Comparison.Equal or Comparison.NotEqual;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,13 +13,13 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
|
|
||||||
public static void RunPass(ControlFlowGraph cfg)
|
public static void RunPass(ControlFlowGraph cfg)
|
||||||
{
|
{
|
||||||
Dictionary<ulong, Operand> constants = new();
|
var constants = new Dictionary<ulong, Operand>();
|
||||||
|
|
||||||
Operand GetConstantCopy(BasicBlock block, Operation operation, Operand source)
|
Operand GetConstantCopy(BasicBlock block, Operation operation, Operand source)
|
||||||
{
|
{
|
||||||
// If the constant has many uses, we also force a new constant mov to be added, in order
|
// If the constant has many uses, we also force a new constant mov to be added, in order
|
||||||
// to avoid overflow of the counts field (that is limited to 16 bits).
|
// to avoid overflow of the counts field (that is limited to 16 bits).
|
||||||
if (!constants.TryGetValue(source.Value, out Operand constant) || constant.UsesCount > MaxConstantUses)
|
if (!constants.TryGetValue(source.Value, out var constant) || constant.UsesCount > MaxConstantUses)
|
||||||
{
|
{
|
||||||
constant = Local(source.Type);
|
constant = Local(source.Type);
|
||||||
|
|
||||||
|
|||||||
@@ -20,21 +20,45 @@ namespace ARMeilleure.Common
|
|||||||
new( 1, 6)
|
new( 1, 6)
|
||||||
];
|
];
|
||||||
|
|
||||||
private static readonly AddressTableLevel[] _levels64BitMono =
|
private static readonly AddressTableLevel[] _levels64BitSparseTiny =
|
||||||
[
|
[
|
||||||
new( 2, 37)
|
new( 11, 28),
|
||||||
|
new( 2, 9)
|
||||||
];
|
];
|
||||||
|
|
||||||
private static readonly AddressTableLevel[] _levels32BitMono =
|
private static readonly AddressTableLevel[] _levels32BitSparseTiny =
|
||||||
[
|
[
|
||||||
new( 1, 31)
|
new( 10, 22),
|
||||||
|
new( 1, 9)
|
||||||
];
|
];
|
||||||
|
|
||||||
public static AddressTableLevel[] GetArmPreset(bool for64Bits, bool mono)
|
private static readonly AddressTableLevel[] _levels64BitSparseGiant =
|
||||||
|
[
|
||||||
|
new( 38, 1),
|
||||||
|
new( 2, 36)
|
||||||
|
];
|
||||||
|
|
||||||
|
private static readonly AddressTableLevel[] _levels32BitSparseGiant =
|
||||||
|
[
|
||||||
|
new( 31, 1),
|
||||||
|
new( 1, 30)
|
||||||
|
];
|
||||||
|
|
||||||
|
//high power will run worse on DDR3 systems and some DDR4 systems due to the higher ram utilization
|
||||||
|
//low power will never run worse than non-sparse, but for most systems it won't be necessary
|
||||||
|
//high power is always used, but I've left low power in here for future reference
|
||||||
|
public static AddressTableLevel[] GetArmPreset(bool for64Bits, bool sparse, bool lowPower = false)
|
||||||
{
|
{
|
||||||
if (mono)
|
if (sparse)
|
||||||
{
|
{
|
||||||
return for64Bits ? _levels64BitMono : _levels32BitMono;
|
if (lowPower)
|
||||||
|
{
|
||||||
|
return for64Bits ? _levels64BitSparseTiny : _levels32BitSparseTiny;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return for64Bits ? _levels64BitSparseGiant : _levels32BitSparseGiant;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
namespace ARMeilleure.Common
|
|
||||||
{
|
|
||||||
public enum AddressTableType
|
|
||||||
{
|
|
||||||
Default,
|
|
||||||
Sparse
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -129,13 +129,13 @@ namespace ARMeilleure.Common
|
|||||||
|
|
||||||
if (count > _count)
|
if (count > _count)
|
||||||
{
|
{
|
||||||
long* oldMask = _masks;
|
var oldMask = _masks;
|
||||||
Span<long> oldSpan = new(_masks, _count);
|
var oldSpan = new Span<long>(_masks, _count);
|
||||||
|
|
||||||
_masks = _allocator.Allocate<long>((uint)count);
|
_masks = _allocator.Allocate<long>((uint)count);
|
||||||
_count = count;
|
_count = count;
|
||||||
|
|
||||||
Span<long> newSpan = new(_masks, _count);
|
var newSpan = new Span<long>(_masks, _count);
|
||||||
|
|
||||||
oldSpan.CopyTo(newSpan);
|
oldSpan.CopyTo(newSpan);
|
||||||
newSpan[oldSpan.Length..].Clear();
|
newSpan[oldSpan.Length..].Clear();
|
||||||
|
|||||||
@@ -53,14 +53,5 @@ namespace ARMeilleure.Common
|
|||||||
{
|
{
|
||||||
return (bits >> shift) | (bits << (size - shift));
|
return (bits >> shift) | (bits << (size - shift));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static T AlignUp<T>(T value, T size) where T : IBinaryInteger<T>
|
|
||||||
=> (value + (size - T.One)) & -size;
|
|
||||||
|
|
||||||
public static T AlignDown<T>(T value, T size) where T : IBinaryInteger<T>
|
|
||||||
=> value & -size;
|
|
||||||
|
|
||||||
public static T DivRoundUp<T>(T value, T dividend) where T : IBinaryInteger<T>
|
|
||||||
=> (value + (dividend - T.One)) / dividend;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ namespace ARMeilleure.Common
|
|||||||
}
|
}
|
||||||
|
|
||||||
int index = _freeHint++;
|
int index = _freeHint++;
|
||||||
Span<TEntry> page = GetPage(index);
|
var page = GetPage(index);
|
||||||
|
|
||||||
_allocated.Set(index);
|
_allocated.Set(index);
|
||||||
|
|
||||||
@@ -111,7 +111,7 @@ namespace ARMeilleure.Common
|
|||||||
throw new ArgumentException("Entry at the specified index was not allocated", nameof(index));
|
throw new ArgumentException("Entry at the specified index was not allocated", nameof(index));
|
||||||
}
|
}
|
||||||
|
|
||||||
Span<TEntry> page = GetPage(index);
|
var page = GetPage(index);
|
||||||
|
|
||||||
return ref GetValue(page, index);
|
return ref GetValue(page, index);
|
||||||
}
|
}
|
||||||
@@ -136,7 +136,7 @@ namespace ARMeilleure.Common
|
|||||||
/// <returns>Page for the specified <see cref="index"/></returns>
|
/// <returns>Page for the specified <see cref="index"/></returns>
|
||||||
private unsafe Span<TEntry> GetPage(int index)
|
private unsafe Span<TEntry> GetPage(int index)
|
||||||
{
|
{
|
||||||
int pageIndex = (int)((uint)(index & ~(_pageCapacity - 1)) >> _pageLogCapacity);
|
var pageIndex = (int)((uint)(index & ~(_pageCapacity - 1)) >> _pageLogCapacity);
|
||||||
|
|
||||||
if (!_pages.TryGetValue(pageIndex, out nint page))
|
if (!_pages.TryGetValue(pageIndex, out nint page))
|
||||||
{
|
{
|
||||||
@@ -168,7 +168,7 @@ namespace ARMeilleure.Common
|
|||||||
{
|
{
|
||||||
_allocated.Dispose();
|
_allocated.Dispose();
|
||||||
|
|
||||||
foreach (IntPtr page in _pages.Values)
|
foreach (var page in _pages.Values)
|
||||||
{
|
{
|
||||||
NativeAllocator.Instance.Free((void*)page);
|
NativeAllocator.Instance.Free((void*)page);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,9 +5,10 @@ namespace ARMeilleure.Common
|
|||||||
public interface IAddressTable<TEntry> : IDisposable where TEntry : unmanaged
|
public interface IAddressTable<TEntry> : IDisposable where TEntry : unmanaged
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the <see cref="AddressTableType"/> of the <see cref="IAddressTable{TEntry}"/> instance.
|
/// True if the address table's bottom level is sparsely mapped.
|
||||||
|
/// This also ensures the second bottom level is filled with a dummy page rather than 0.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
AddressTableType TableType { get; }
|
bool Sparse { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the bits used by the <see cref="Levels"/> of the <see cref="IAddressTable{TEntry}"/> instance.
|
/// Gets the bits used by the <see cref="Levels"/> of the <see cref="IAddressTable{TEntry}"/> instance.
|
||||||
@@ -30,13 +31,6 @@ namespace ARMeilleure.Common
|
|||||||
/// <exception cref="ObjectDisposedException"><see cref="EntryTable{TEntry}"/> instance was disposed</exception>
|
/// <exception cref="ObjectDisposedException"><see cref="EntryTable{TEntry}"/> instance was disposed</exception>
|
||||||
nint Base { get; }
|
nint Base { get; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Signal that the given code range exists.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="address">Guest code range address</param>
|
|
||||||
/// <param name="size">Guest code range size</param>
|
|
||||||
void SignalCodeRange(ulong address, ulong size);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines if the specified <paramref name="address"/> is in the range of the
|
/// Determines if the specified <paramref name="address"/> is in the range of the
|
||||||
/// <see cref="IAddressTable{TEntry}"/>.
|
/// <see cref="IAddressTable{TEntry}"/>.
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ namespace ARMeilleure.Decoders
|
|||||||
|
|
||||||
public OpCode32SimdDupElem(InstDescriptor inst, ulong address, int opCode, bool isThumb) : base(inst, address, opCode, isThumb)
|
public OpCode32SimdDupElem(InstDescriptor inst, ulong address, int opCode, bool isThumb) : base(inst, address, opCode, isThumb)
|
||||||
{
|
{
|
||||||
int opc = (opCode >> 16) & 0xf;
|
var opc = (opCode >> 16) & 0xf;
|
||||||
|
|
||||||
if ((opc & 0b1) == 1)
|
if ((opc & 0b1) == 1)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ namespace ARMeilleure.Decoders
|
|||||||
Op = (opCode >> 20) & 0x1;
|
Op = (opCode >> 20) & 0x1;
|
||||||
U = ((opCode >> 23) & 1) != 0;
|
U = ((opCode >> 23) & 1) != 0;
|
||||||
|
|
||||||
int opc = (((opCode >> 23) & 1) << 4) | (((opCode >> 21) & 0x3) << 2) | ((opCode >> 5) & 0x3);
|
var opc = (((opCode >> 23) & 1) << 4) | (((opCode >> 21) & 0x3) << 2) | ((opCode >> 5) & 0x3);
|
||||||
|
|
||||||
if ((opc & 0b01000) == 0b01000)
|
if ((opc & 0b01000) == 0b01000)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ namespace ARMeilleure.Decoders
|
|||||||
}
|
}
|
||||||
else if (DataOp == DataOp.Logical)
|
else if (DataOp == DataOp.Logical)
|
||||||
{
|
{
|
||||||
DecoderHelper.BitMask bm = DecoderHelper.DecodeBitMask(opCode, true);
|
var bm = DecoderHelper.DecodeBitMask(opCode, true);
|
||||||
|
|
||||||
if (bm.IsUndefined)
|
if (bm.IsUndefined)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ namespace ARMeilleure.Decoders
|
|||||||
|
|
||||||
public OpCodeBfm(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
public OpCodeBfm(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||||
{
|
{
|
||||||
DecoderHelper.BitMask bm = DecoderHelper.DecodeBitMask(opCode, false);
|
var bm = DecoderHelper.DecodeBitMask(opCode, false);
|
||||||
|
|
||||||
if (bm.IsUndefined)
|
if (bm.IsUndefined)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ namespace ARMeilleure.Decoders.Optimizations
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Block> newBlocks = new(blocks.Count);
|
var newBlocks = new List<Block>(blocks.Count);
|
||||||
|
|
||||||
// Finally, rebuild decoded block list, ignoring blocks outside the contiguous range.
|
// Finally, rebuild decoded block list, ignoring blocks outside the contiguous range.
|
||||||
for (int i = 0; i < blocks.Count; i++)
|
for (int i = 0; i < blocks.Count; i++)
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ namespace ARMeilleure.Diagnostics
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case OperandKind.Memory:
|
case OperandKind.Memory:
|
||||||
MemoryOperand memOp = operand.GetMemory();
|
var memOp = operand.GetMemory();
|
||||||
|
|
||||||
_builder.Append('[');
|
_builder.Append('[');
|
||||||
|
|
||||||
@@ -284,7 +284,7 @@ namespace ARMeilleure.Diagnostics
|
|||||||
|
|
||||||
public static string GetDump(ControlFlowGraph cfg)
|
public static string GetDump(ControlFlowGraph cfg)
|
||||||
{
|
{
|
||||||
IRDumper dumper = new(1);
|
var dumper = new IRDumper(1);
|
||||||
|
|
||||||
for (BasicBlock block = cfg.Blocks.First; block != null; block = block.ListNext)
|
for (BasicBlock block = cfg.Blocks.First; block != null; block = block.ListNext)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -415,7 +415,7 @@ namespace ARMeilleure.Instructions
|
|||||||
{
|
{
|
||||||
IOpCode32AluBf op = (IOpCode32AluBf)context.CurrOp;
|
IOpCode32AluBf op = (IOpCode32AluBf)context.CurrOp;
|
||||||
|
|
||||||
int msb = op.Lsb + op.Msb; // For this instruction, the msb is actually a width.
|
var msb = op.Lsb + op.Msb; // For this instruction, the msb is actually a width.
|
||||||
|
|
||||||
Operand n = GetIntA32(context, op.Rn);
|
Operand n = GetIntA32(context, op.Rn);
|
||||||
Operand res = context.ShiftRightSI(context.ShiftLeft(n, Const(31 - msb)), Const(31 - op.Msb));
|
Operand res = context.ShiftRightSI(context.ShiftLeft(n, Const(31 - msb)), Const(31 - op.Msb));
|
||||||
@@ -547,7 +547,7 @@ namespace ARMeilleure.Instructions
|
|||||||
{
|
{
|
||||||
IOpCode32AluBf op = (IOpCode32AluBf)context.CurrOp;
|
IOpCode32AluBf op = (IOpCode32AluBf)context.CurrOp;
|
||||||
|
|
||||||
int msb = op.Lsb + op.Msb; // For this instruction, the msb is actually a width.
|
var msb = op.Lsb + op.Msb; // For this instruction, the msb is actually a width.
|
||||||
|
|
||||||
Operand n = GetIntA32(context, op.Rn);
|
Operand n = GetIntA32(context, op.Rn);
|
||||||
Operand res = context.ShiftRightUI(context.ShiftLeft(n, Const(31 - msb)), Const(31 - op.Msb));
|
Operand res = context.ShiftRightUI(context.ShiftLeft(n, Const(31 - msb)), Const(31 - op.Msb));
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
using ARMeilleure.CodeGen.Linking;
|
using ARMeilleure.CodeGen.Linking;
|
||||||
using ARMeilleure.Common;
|
|
||||||
using ARMeilleure.Decoders;
|
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 ARMeilleure.Translation.PTC;
|
||||||
using System.Linq;
|
|
||||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||||
|
|
||||||
@@ -224,7 +223,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
Operand hostAddress;
|
Operand hostAddress;
|
||||||
|
|
||||||
IAddressTable<ulong> table = context.FunctionTable;
|
var table = context.FunctionTable;
|
||||||
|
|
||||||
// If address is mapped onto the function table, we can skip the table walk. Otherwise we fallback
|
// If address is mapped onto the function table, we can skip the table walk. Otherwise we fallback
|
||||||
// onto the dispatch stub.
|
// onto the dispatch stub.
|
||||||
@@ -236,9 +235,9 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
hostAddress = context.Load(OperandType.I64, hostAddressAddr);
|
hostAddress = context.Load(OperandType.I64, hostAddressAddr);
|
||||||
}
|
}
|
||||||
else if (table.TableType == AddressTableType.Sparse)
|
else if (table.Sparse)
|
||||||
{
|
{
|
||||||
// Inline table lookup. Only enabled when the sparse function table is enabled with 1 level.
|
// Inline table lookup. Only enabled when the sparse function table is enabled with 2 levels.
|
||||||
// Deliberately attempts to avoid branches.
|
// Deliberately attempts to avoid branches.
|
||||||
|
|
||||||
Operand tableBase = !context.HasPtc ?
|
Operand tableBase = !context.HasPtc ?
|
||||||
@@ -247,21 +246,24 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
hostAddress = tableBase;
|
hostAddress = tableBase;
|
||||||
|
|
||||||
AddressTableLevel level = table.Levels.Last();
|
for (int i = 0; i < table.Levels.Length; i++)
|
||||||
int clearBits = 64 - (level.Index + level.Length);
|
{
|
||||||
|
var level = table.Levels[i];
|
||||||
|
int clearBits = 64 - (level.Index + level.Length);
|
||||||
|
|
||||||
Operand index = context.ShiftLeft(
|
Operand index = context.ShiftLeft(
|
||||||
context.ShiftRightUI(context.ShiftLeft(guestAddress, Const(clearBits)), Const(clearBits + level.Index)),
|
context.ShiftRightUI(context.ShiftLeft(guestAddress, Const(clearBits)), Const(clearBits + level.Index)),
|
||||||
Const(3)
|
Const(3)
|
||||||
);
|
);
|
||||||
|
|
||||||
hostAddress = context.Load(OperandType.I64, context.Add(hostAddress, index));
|
hostAddress = context.Load(OperandType.I64, context.Add(hostAddress, index));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
hostAddress = !context.HasPtc ?
|
hostAddress = !context.HasPtc ?
|
||||||
Const(context.Stubs.DispatchStub) :
|
Const((long)context.Stubs.DispatchStub) :
|
||||||
Const(context.Stubs.DispatchStub, Ptc.DispatchStubSymbol);
|
Const((long)context.Stubs.DispatchStub, Ptc.DispatchStubSymbol);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isJump)
|
if (isJump)
|
||||||
|
|||||||
@@ -143,8 +143,8 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
Operand address = context.Copy(GetIntA32(context, op.Rn));
|
Operand address = context.Copy(GetIntA32(context, op.Rn));
|
||||||
|
|
||||||
bool exclusive = (accType & AccessType.Exclusive) != 0;
|
var exclusive = (accType & AccessType.Exclusive) != 0;
|
||||||
bool ordered = (accType & AccessType.Ordered) != 0;
|
var ordered = (accType & AccessType.Ordered) != 0;
|
||||||
|
|
||||||
if ((accType & AccessType.Load) != 0)
|
if ((accType & AccessType.Load) != 0)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -229,7 +229,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
private static Operand ZerosOrOnes(ArmEmitterContext context, Operand fromBool, OperandType baseType)
|
private static Operand ZerosOrOnes(ArmEmitterContext context, Operand fromBool, OperandType baseType)
|
||||||
{
|
{
|
||||||
Operand ones = (baseType == OperandType.I64) ? Const(-1L) : Const(-1);
|
var ones = (baseType == OperandType.I64) ? Const(-1L) : Const(-1);
|
||||||
|
|
||||||
return context.ConditionalSelect(fromBool, ones, Const(baseType, 0L));
|
return context.ConditionalSelect(fromBool, ones, Const(baseType, 0L));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -118,15 +118,15 @@ namespace ARMeilleure.Instructions
|
|||||||
{
|
{
|
||||||
OpCode32SimdCvtFFixed op = (OpCode32SimdCvtFFixed)context.CurrOp;
|
OpCode32SimdCvtFFixed op = (OpCode32SimdCvtFFixed)context.CurrOp;
|
||||||
|
|
||||||
bool toFixed = op.Opc == 1;
|
var toFixed = op.Opc == 1;
|
||||||
int fracBits = op.Fbits;
|
int fracBits = op.Fbits;
|
||||||
bool unsigned = op.U;
|
var unsigned = op.U;
|
||||||
|
|
||||||
if (toFixed) // F32 to S32 or U32 (fixed)
|
if (toFixed) // F32 to S32 or U32 (fixed)
|
||||||
{
|
{
|
||||||
EmitVectorUnaryOpF32(context, (op1) =>
|
EmitVectorUnaryOpF32(context, (op1) =>
|
||||||
{
|
{
|
||||||
Operand scaledValue = context.Multiply(op1, ConstF(MathF.Pow(2f, fracBits)));
|
var scaledValue = context.Multiply(op1, ConstF(MathF.Pow(2f, fracBits)));
|
||||||
MethodInfo info = unsigned ? typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToU32)) : typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToS32));
|
MethodInfo info = unsigned ? typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToU32)) : typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToS32));
|
||||||
|
|
||||||
return context.Call(info, scaledValue);
|
return context.Call(info, scaledValue);
|
||||||
@@ -136,7 +136,7 @@ namespace ARMeilleure.Instructions
|
|||||||
{
|
{
|
||||||
EmitVectorUnaryOpI32(context, (op1) =>
|
EmitVectorUnaryOpI32(context, (op1) =>
|
||||||
{
|
{
|
||||||
Operand floatValue = unsigned ? context.ConvertToFPUI(OperandType.FP32, op1) : context.ConvertToFP(OperandType.FP32, op1);
|
var floatValue = unsigned ? context.ConvertToFPUI(OperandType.FP32, op1) : context.ConvertToFP(OperandType.FP32, op1);
|
||||||
|
|
||||||
return context.Multiply(floatValue, ConstF(1f / MathF.Pow(2f, fracBits)));
|
return context.Multiply(floatValue, ConstF(1f / MathF.Pow(2f, fracBits)));
|
||||||
}, !unsigned);
|
}, !unsigned);
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ namespace ARMeilleure.Instructions
|
|||||||
{
|
{
|
||||||
if (op.Replicate)
|
if (op.Replicate)
|
||||||
{
|
{
|
||||||
int regs = (count > 1) ? 1 : op.Increment;
|
var regs = (count > 1) ? 1 : op.Increment;
|
||||||
for (int reg = 0; reg < regs; reg++)
|
for (int reg = 0; reg < regs; reg++)
|
||||||
{
|
{
|
||||||
int dreg = reg + d;
|
int dreg = reg + d;
|
||||||
|
|||||||
@@ -1538,7 +1538,7 @@ namespace ARMeilleure.Instructions
|
|||||||
}
|
}
|
||||||
else if (MathF.Abs(value) < MathF.Pow(2f, -128))
|
else if (MathF.Abs(value) < MathF.Pow(2f, -128))
|
||||||
{
|
{
|
||||||
bool overflowToInf = fpcr.GetRoundingMode() switch
|
var overflowToInf = fpcr.GetRoundingMode() switch
|
||||||
{
|
{
|
||||||
FPRoundingMode.ToNearest => true,
|
FPRoundingMode.ToNearest => true,
|
||||||
FPRoundingMode.TowardsPlusInfinity => !sign,
|
FPRoundingMode.TowardsPlusInfinity => !sign,
|
||||||
@@ -3073,7 +3073,7 @@ namespace ARMeilleure.Instructions
|
|||||||
}
|
}
|
||||||
else if (Math.Abs(value) < Math.Pow(2d, -1024))
|
else if (Math.Abs(value) < Math.Pow(2d, -1024))
|
||||||
{
|
{
|
||||||
bool overflowToInf = fpcr.GetRoundingMode() switch
|
var overflowToInf = fpcr.GetRoundingMode() switch
|
||||||
{
|
{
|
||||||
FPRoundingMode.ToNearest => true,
|
FPRoundingMode.ToNearest => true,
|
||||||
FPRoundingMode.TowardsPlusInfinity => !sign,
|
FPRoundingMode.TowardsPlusInfinity => !sign,
|
||||||
|
|||||||
@@ -304,7 +304,7 @@ namespace ARMeilleure.IntermediateRepresentation
|
|||||||
ushort newCount = checked((ushort)(count + 1));
|
ushort newCount = checked((ushort)(count + 1));
|
||||||
ushort newCapacity = (ushort)Math.Min(capacity * 2, ushort.MaxValue);
|
ushort newCapacity = (ushort)Math.Min(capacity * 2, ushort.MaxValue);
|
||||||
|
|
||||||
Span<T> oldSpan = new(data, count);
|
var oldSpan = new Span<T>(data, count);
|
||||||
|
|
||||||
capacity = newCapacity;
|
capacity = newCapacity;
|
||||||
data = Allocators.References.Allocate<T>(capacity);
|
data = Allocators.References.Allocate<T>(capacity);
|
||||||
@@ -338,7 +338,7 @@ namespace ARMeilleure.IntermediateRepresentation
|
|||||||
throw new OverflowException();
|
throw new OverflowException();
|
||||||
}
|
}
|
||||||
|
|
||||||
Span<T> oldSpan = new(data, (int)count);
|
var oldSpan = new Span<T>(data, (int)count);
|
||||||
|
|
||||||
capacity = newCapacity;
|
capacity = newCapacity;
|
||||||
data = Allocators.References.Allocate<T>(capacity);
|
data = Allocators.References.Allocate<T>(capacity);
|
||||||
@@ -352,7 +352,7 @@ namespace ARMeilleure.IntermediateRepresentation
|
|||||||
|
|
||||||
private static void Remove<T>(in T item, ref T* data, ref ushort count) where T : unmanaged
|
private static void Remove<T>(in T item, ref T* data, ref ushort count) where T : unmanaged
|
||||||
{
|
{
|
||||||
Span<T> span = new(data, count);
|
var span = new Span<T>(data, count);
|
||||||
|
|
||||||
for (int i = 0; i < span.Length; i++)
|
for (int i = 0; i < span.Length; i++)
|
||||||
{
|
{
|
||||||
@@ -372,7 +372,7 @@ namespace ARMeilleure.IntermediateRepresentation
|
|||||||
|
|
||||||
private static void Remove<T>(in T item, ref T* data, ref uint count) where T : unmanaged
|
private static void Remove<T>(in T item, ref T* data, ref uint count) where T : unmanaged
|
||||||
{
|
{
|
||||||
Span<T> span = new(data, (int)count);
|
var span = new Span<T>(data, (int)count);
|
||||||
|
|
||||||
for (int i = 0; i < span.Length; i++)
|
for (int i = 0; i < span.Length; i++)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ using System;
|
|||||||
|
|
||||||
namespace ARMeilleure.Memory
|
namespace ARMeilleure.Memory
|
||||||
{
|
{
|
||||||
public class ReservedRegion : IDisposable
|
public class ReservedRegion
|
||||||
{
|
{
|
||||||
public const int DefaultGranularity = 65536; // Mapping granularity in Windows.
|
public const int DefaultGranularity = 65536; // Mapping granularity in Windows.
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using ARMeilleure.IntermediateRepresentation;
|
using ARMeilleure.IntermediateRepresentation;
|
||||||
using ARMeilleure.Translation;
|
using ARMeilleure.Translation;
|
||||||
using ARMeilleure.Translation.Cache;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||||
|
|
||||||
@@ -18,11 +17,11 @@ namespace ARMeilleure.Signal
|
|||||||
public delegate int DebugThreadLocalMapGetOrReserve(int threadId, int initialState);
|
public delegate int DebugThreadLocalMapGetOrReserve(int threadId, int initialState);
|
||||||
public delegate void DebugNativeWriteLoop(nint nativeWriteLoopPtr, nint writePtr);
|
public delegate void DebugNativeWriteLoop(nint nativeWriteLoopPtr, nint writePtr);
|
||||||
|
|
||||||
public static DebugPartialUnmap GenerateDebugPartialUnmap(JitCache jitCache)
|
public static DebugPartialUnmap GenerateDebugPartialUnmap()
|
||||||
{
|
{
|
||||||
EmitterContext context = new();
|
EmitterContext context = new();
|
||||||
|
|
||||||
Operand result = WindowsPartialUnmapHandler.EmitRetryFromAccessViolation(context);
|
var result = WindowsPartialUnmapHandler.EmitRetryFromAccessViolation(context);
|
||||||
|
|
||||||
context.Return(result);
|
context.Return(result);
|
||||||
|
|
||||||
@@ -32,14 +31,14 @@ namespace ARMeilleure.Signal
|
|||||||
|
|
||||||
OperandType[] argTypes = [OperandType.I64];
|
OperandType[] argTypes = [OperandType.I64];
|
||||||
|
|
||||||
return Compiler.Compile(cfg, argTypes, OperandType.I32, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<DebugPartialUnmap>(jitCache);
|
return Compiler.Compile(cfg, argTypes, OperandType.I32, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<DebugPartialUnmap>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static DebugThreadLocalMapGetOrReserve GenerateDebugThreadLocalMapGetOrReserve(JitCache jitCache, nint structPtr)
|
public static DebugThreadLocalMapGetOrReserve GenerateDebugThreadLocalMapGetOrReserve(nint structPtr)
|
||||||
{
|
{
|
||||||
EmitterContext context = new();
|
EmitterContext context = new();
|
||||||
|
|
||||||
Operand result = WindowsPartialUnmapHandler.EmitThreadLocalMapIntGetOrReserve(context, structPtr, context.LoadArgument(OperandType.I32, 0), context.LoadArgument(OperandType.I32, 1));
|
var result = WindowsPartialUnmapHandler.EmitThreadLocalMapIntGetOrReserve(context, structPtr, context.LoadArgument(OperandType.I32, 0), context.LoadArgument(OperandType.I32, 1));
|
||||||
|
|
||||||
context.Return(result);
|
context.Return(result);
|
||||||
|
|
||||||
@@ -49,10 +48,10 @@ namespace ARMeilleure.Signal
|
|||||||
|
|
||||||
OperandType[] argTypes = [OperandType.I64];
|
OperandType[] argTypes = [OperandType.I64];
|
||||||
|
|
||||||
return Compiler.Compile(cfg, argTypes, OperandType.I32, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<DebugThreadLocalMapGetOrReserve>(jitCache);
|
return Compiler.Compile(cfg, argTypes, OperandType.I32, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<DebugThreadLocalMapGetOrReserve>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static DebugNativeWriteLoop GenerateDebugNativeWriteLoop(JitCache jitCache)
|
public static DebugNativeWriteLoop GenerateDebugNativeWriteLoop()
|
||||||
{
|
{
|
||||||
EmitterContext context = new();
|
EmitterContext context = new();
|
||||||
|
|
||||||
@@ -78,7 +77,7 @@ namespace ARMeilleure.Signal
|
|||||||
|
|
||||||
OperandType[] argTypes = [OperandType.I64];
|
OperandType[] argTypes = [OperandType.I64];
|
||||||
|
|
||||||
return Compiler.Compile(cfg, argTypes, OperandType.None, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<DebugNativeWriteLoop>(jitCache);
|
return Compiler.Compile(cfg, argTypes, OperandType.None, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<DebugNativeWriteLoop>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ using System.Diagnostics.CodeAnalysis;
|
|||||||
|
|
||||||
namespace ARMeilleure.Translation.Cache
|
namespace ARMeilleure.Translation.Cache
|
||||||
{
|
{
|
||||||
public readonly struct CacheEntry : IComparable<CacheEntry>
|
readonly struct CacheEntry : IComparable<CacheEntry>
|
||||||
{
|
{
|
||||||
public int Offset { get; }
|
public int Offset { get; }
|
||||||
public int Size { get; }
|
public int Size { get; }
|
||||||
|
|||||||
@@ -23,30 +23,11 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly int _regionSize;
|
|
||||||
private int _regionCount;
|
|
||||||
|
|
||||||
private readonly List<MemoryBlock> _blocks = [];
|
private readonly List<MemoryBlock> _blocks = [];
|
||||||
|
|
||||||
public CacheMemoryAllocator(int regionSize, int initialRegionCount = 1)
|
public CacheMemoryAllocator(int capacity)
|
||||||
{
|
{
|
||||||
_regionCount = 0;
|
_blocks.Add(new MemoryBlock(0, capacity));
|
||||||
_regionSize = regionSize;
|
|
||||||
|
|
||||||
for (; initialRegionCount > 0; initialRegionCount--)
|
|
||||||
{
|
|
||||||
_blocks.Add(new MemoryBlock(_regionSize * _regionCount, _regionSize));
|
|
||||||
_regionCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddNewBlocks(int count)
|
|
||||||
{
|
|
||||||
for (; count > 0; count--)
|
|
||||||
{
|
|
||||||
_blocks.Add(new MemoryBlock(_regionSize * _regionCount, _regionSize));
|
|
||||||
_regionCount++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Allocate(int size)
|
public int Allocate(int size)
|
||||||
@@ -85,13 +66,12 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
index = ~index;
|
index = ~index;
|
||||||
}
|
}
|
||||||
|
|
||||||
int endOffs = block.Offset + block.Size;
|
if (index < _blocks.Count)
|
||||||
|
|
||||||
// Don't merge blocks from different allocations
|
|
||||||
if (index < _blocks.Count && endOffs % _regionSize != 0)
|
|
||||||
{
|
{
|
||||||
MemoryBlock next = _blocks[index];
|
MemoryBlock next = _blocks[index];
|
||||||
|
|
||||||
|
int endOffs = block.Offset + block.Size;
|
||||||
|
|
||||||
if (next.Offset == endOffs)
|
if (next.Offset == endOffs)
|
||||||
{
|
{
|
||||||
block = new MemoryBlock(block.Offset, block.Size + next.Size);
|
block = new MemoryBlock(block.Offset, block.Size + next.Size);
|
||||||
@@ -99,8 +79,7 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't merge blocks from different allocations
|
if (index > 0)
|
||||||
if (index > 0 && block.Offset % _regionSize != 0)
|
|
||||||
{
|
{
|
||||||
MemoryBlock prev = _blocks[index - 1];
|
MemoryBlock prev = _blocks[index - 1];
|
||||||
|
|
||||||
|
|||||||
@@ -14,35 +14,62 @@ using System.Threading;
|
|||||||
|
|
||||||
namespace ARMeilleure.Translation.Cache
|
namespace ARMeilleure.Translation.Cache
|
||||||
{
|
{
|
||||||
public partial class JitCache : IDisposable
|
static partial class JitCache
|
||||||
{
|
{
|
||||||
private static readonly int _pageSize = (int)MemoryBlock.GetPageSize();
|
private static readonly int _pageSize = (int)MemoryBlock.GetPageSize();
|
||||||
private static readonly int _pageMask = _pageSize - 1;
|
private static readonly int _pageMask = _pageSize - 1;
|
||||||
|
|
||||||
private const int CodeAlignment = 4; // Bytes.
|
private const int CodeAlignment = 4; // Bytes.
|
||||||
private const uint CacheSize = 256 * 1024 * 1024;
|
private const int CacheSize = 256 * 1024 * 1024;
|
||||||
|
|
||||||
private readonly JitCacheInvalidation _jitCacheInvalidator;
|
private static JitCacheInvalidation _jitCacheInvalidator;
|
||||||
|
|
||||||
private readonly CacheMemoryAllocator _cacheAllocator;
|
private static List<CacheMemoryAllocator> _cacheAllocators = [];
|
||||||
|
|
||||||
private readonly List<CacheEntry> _cacheEntries = [];
|
private static readonly List<CacheEntry> _cacheEntries = [];
|
||||||
|
|
||||||
private readonly Lock _lock = new();
|
private static readonly Lock _lock = new();
|
||||||
|
private static bool _initialized;
|
||||||
|
|
||||||
private readonly List<ReservedRegion> _jitRegions = [];
|
private static readonly List<ReservedRegion> _jitRegions = [];
|
||||||
|
private static int _activeRegionIndex = 0;
|
||||||
|
|
||||||
[SupportedOSPlatform("windows")]
|
[SupportedOSPlatform("windows")]
|
||||||
[LibraryImport("kernel32.dll", SetLastError = true)]
|
[LibraryImport("kernel32.dll", SetLastError = true)]
|
||||||
private static partial nint FlushInstructionCache(nint hProcess, nint lpAddress, nuint dwSize);
|
public static partial nint FlushInstructionCache(nint hProcess, nint lpAddress, nuint dwSize);
|
||||||
|
|
||||||
public JitCache(IJitMemoryAllocator allocator)
|
public static void Initialize(IJitMemoryAllocator allocator)
|
||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
_jitRegions.Add(new(allocator, CacheSize));
|
if (_initialized)
|
||||||
|
{
|
||||||
|
if (OperatingSystem.IsWindows())
|
||||||
|
{
|
||||||
|
JitUnwindWindows.RemoveFunctionTableHandler(
|
||||||
|
_jitRegions[0].Pointer);
|
||||||
|
}
|
||||||
|
|
||||||
_cacheAllocator = new((int)CacheSize);
|
for (int i = 0; i < _jitRegions.Count; i++)
|
||||||
|
{
|
||||||
|
_jitRegions[i].Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
_jitRegions.Clear();
|
||||||
|
_cacheAllocators.Clear();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_activeRegionIndex = 0;
|
||||||
|
|
||||||
|
var firstRegion = new ReservedRegion(allocator, CacheSize);
|
||||||
|
_jitRegions.Add(firstRegion);
|
||||||
|
|
||||||
|
CacheMemoryAllocator firstCacheAllocator = new(CacheSize);
|
||||||
|
_cacheAllocators.Add(firstCacheAllocator);
|
||||||
|
|
||||||
if (!OperatingSystem.IsWindows() && !OperatingSystem.IsMacOS())
|
if (!OperatingSystem.IsWindows() && !OperatingSystem.IsMacOS())
|
||||||
{
|
{
|
||||||
@@ -52,20 +79,23 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
if (OperatingSystem.IsWindows())
|
if (OperatingSystem.IsWindows())
|
||||||
{
|
{
|
||||||
JitUnwindWindows.InstallFunctionTableHandler(
|
JitUnwindWindows.InstallFunctionTableHandler(
|
||||||
this, _jitRegions[0].Pointer, CacheSize, _jitRegions[0].Pointer + Allocate(_pageSize)
|
firstRegion.Pointer, CacheSize, firstRegion.Pointer + Allocate(_pageSize)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public nint Map(CompiledFunction func)
|
public static nint Map(CompiledFunction func)
|
||||||
{
|
{
|
||||||
byte[] code = func.Code;
|
byte[] code = func.Code;
|
||||||
|
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
|
Debug.Assert(_initialized);
|
||||||
|
|
||||||
int funcOffset = Allocate(code.Length);
|
int funcOffset = Allocate(code.Length);
|
||||||
nint funcPtr = GetFunctionPtr(funcOffset);
|
ReservedRegion targetRegion = _jitRegions[_activeRegionIndex];
|
||||||
|
nint funcPtr = targetRegion.Pointer + funcOffset;
|
||||||
|
|
||||||
if (OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
if (OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
||||||
{
|
{
|
||||||
@@ -79,9 +109,9 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ReprotectAsWritable(funcOffset, code.Length);
|
ReprotectAsWritable(targetRegion, funcOffset, code.Length);
|
||||||
Marshal.Copy(code, 0, funcPtr, code.Length);
|
Marshal.Copy(code, 0, funcPtr, code.Length);
|
||||||
ReprotectAsExecutable(funcOffset, code.Length);
|
ReprotectAsExecutable(targetRegion, funcOffset, code.Length);
|
||||||
|
|
||||||
if (OperatingSystem.IsWindows() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
if (OperatingSystem.IsWindows() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
||||||
{
|
{
|
||||||
@@ -99,24 +129,25 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Unmap(nint pointer)
|
public static void Unmap(nint pointer)
|
||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < _jitRegions.Count; i++)
|
Debug.Assert(_initialized);
|
||||||
|
|
||||||
|
foreach (var region in _jitRegions)
|
||||||
{
|
{
|
||||||
ReservedRegion jitRegion = _jitRegions[i];
|
if (pointer.ToInt64() < region.Pointer.ToInt64() ||
|
||||||
if (pointer.ToInt64() < jitRegion.Pointer.ToInt64() ||
|
pointer.ToInt64() >= (region.Pointer + CacheSize).ToInt64())
|
||||||
pointer.ToInt64() >= (jitRegion.Pointer + (nint)CacheSize).ToInt64())
|
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
int funcOffset = (int)(pointer.ToInt64() - jitRegion.Pointer.ToInt64() + i * CacheSize);
|
int funcOffset = (int)(pointer.ToInt64() - region.Pointer.ToInt64());
|
||||||
|
|
||||||
if (TryFind(funcOffset, out CacheEntry entry, out int entryIndex) && entry.Offset == funcOffset)
|
if (TryFind(funcOffset, out CacheEntry entry, out int entryIndex) && entry.Offset == funcOffset)
|
||||||
{
|
{
|
||||||
_cacheAllocator.Free(funcOffset, AlignCodeSize(entry.Size));
|
_cacheAllocators[_activeRegionIndex].Free(funcOffset, AlignCodeSize(entry.Size));
|
||||||
_cacheEntries.RemoveAt(entryIndex);
|
_cacheEntries.RemoveAt(entryIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,66 +156,53 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ReprotectAsWritable(int offset, int size)
|
private static void ReprotectAsWritable(ReservedRegion region, int offset, int size)
|
||||||
{
|
{
|
||||||
int endOffs = offset + size;
|
int endOffs = offset + size;
|
||||||
int regionStart = (offset % (int)CacheSize) & ~_pageMask;
|
int regionStart = offset & ~_pageMask;
|
||||||
int regionEnd = endOffs % (int)CacheSize == 0
|
int regionEnd = (endOffs + _pageMask) & ~_pageMask;
|
||||||
? (((int)CacheSize) + _pageMask) & ~_pageMask
|
|
||||||
: ((endOffs % (int)CacheSize) + _pageMask) & ~_pageMask;
|
region.Block.MapAsRwx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
||||||
GetRegion(offset).Block.MapAsRwx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ReprotectAsExecutable(int offset, int size)
|
private static void ReprotectAsExecutable(ReservedRegion region, int offset, int size)
|
||||||
{
|
{
|
||||||
int endOffs = offset + size;
|
int endOffs = offset + size;
|
||||||
int regionStart = (offset % (int)CacheSize) & ~_pageMask;
|
int regionStart = offset & ~_pageMask;
|
||||||
int regionEnd = endOffs % (int)CacheSize == 0
|
int regionEnd = (endOffs + _pageMask) & ~_pageMask;
|
||||||
? (((int)CacheSize) + _pageMask) & ~_pageMask
|
|
||||||
: ((endOffs % (int)CacheSize) + _pageMask) & ~_pageMask;
|
|
||||||
|
|
||||||
GetRegion(offset).Block.MapAsRx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
region.Block.MapAsRx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
||||||
}
|
}
|
||||||
|
|
||||||
private int Allocate(int codeSize)
|
private static int Allocate(int codeSize)
|
||||||
{
|
{
|
||||||
codeSize = AlignCodeSize(codeSize);
|
codeSize = AlignCodeSize(codeSize);
|
||||||
|
|
||||||
int allocOffset = _cacheAllocator.Allocate(codeSize);
|
int allocOffset = _cacheAllocators[_activeRegionIndex].Allocate(codeSize);
|
||||||
|
|
||||||
if (allocOffset >= 0)
|
if (allocOffset >= 0)
|
||||||
{
|
{
|
||||||
GetRegion(allocOffset).ExpandIfNeeded((ulong)(allocOffset % (int)CacheSize) + (ulong)codeSize);
|
_jitRegions[_activeRegionIndex].ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize);
|
||||||
return allocOffset;
|
return allocOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
_cacheAllocator.AddNewBlocks(1);
|
int exhaustedRegion = _activeRegionIndex;
|
||||||
ReservedRegion newRegion = new(_jitRegions[0].Allocator, CacheSize);
|
var newRegion = new ReservedRegion(_jitRegions[0].Allocator, CacheSize);
|
||||||
|
|
||||||
Logger.Warning?.Print(LogClass.Cpu, $"JIT Cache of size {(_jitRegions.Count * CacheSize).Bytes()} exhausted, creating new Cache Region ({((_jitRegions.Count + 1) * CacheSize).Bytes()} Total Allocation).");
|
|
||||||
|
|
||||||
_jitRegions.Add(newRegion);
|
_jitRegions.Add(newRegion);
|
||||||
|
_activeRegionIndex = _jitRegions.Count - 1;
|
||||||
|
|
||||||
allocOffset = _cacheAllocator.Allocate(codeSize);
|
Logger.Warning?.Print(LogClass.Cpu, $"JIT Cache Region {exhaustedRegion} exhausted, creating new Cache Region {_activeRegionIndex} ({((long)(_activeRegionIndex + 1) * CacheSize).Bytes()} Total Allocation).");
|
||||||
if (allocOffset < 0)
|
|
||||||
|
_cacheAllocators.Add(new CacheMemoryAllocator(CacheSize));
|
||||||
|
|
||||||
|
int allocOffsetNew = _cacheAllocators[_activeRegionIndex].Allocate(codeSize);
|
||||||
|
if (allocOffsetNew < 0)
|
||||||
{
|
{
|
||||||
throw new OutOfMemoryException("Failed to allocate in new Cache Region!");
|
throw new OutOfMemoryException("Failed to allocate in new Cache Region!");
|
||||||
}
|
}
|
||||||
|
|
||||||
GetRegion(allocOffset).ExpandIfNeeded((ulong)(allocOffset % (int)CacheSize) + (ulong)codeSize);
|
newRegion.ExpandIfNeeded((ulong)allocOffsetNew + (ulong)codeSize);
|
||||||
return allocOffset;
|
return allocOffsetNew;
|
||||||
}
|
|
||||||
|
|
||||||
private nint GetFunctionPtr(int offset)
|
|
||||||
{
|
|
||||||
return GetRegion(offset).Pointer + (offset % (int)CacheSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ReservedRegion GetRegion(int offset)
|
|
||||||
{
|
|
||||||
int index = offset / (int)CacheSize;
|
|
||||||
|
|
||||||
return _jitRegions[index];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int AlignCodeSize(int codeSize)
|
private static int AlignCodeSize(int codeSize)
|
||||||
@@ -192,7 +210,7 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
return checked(codeSize + (CodeAlignment - 1)) & ~(CodeAlignment - 1);
|
return checked(codeSize + (CodeAlignment - 1)) & ~(CodeAlignment - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Add(int offset, int size, UnwindInfo unwindInfo)
|
private static void Add(int offset, int size, UnwindInfo unwindInfo)
|
||||||
{
|
{
|
||||||
CacheEntry entry = new(offset, size, unwindInfo);
|
CacheEntry entry = new(offset, size, unwindInfo);
|
||||||
|
|
||||||
@@ -206,22 +224,25 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
_cacheEntries.Insert(index, entry);
|
_cacheEntries.Insert(index, entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryFind(int offset, out CacheEntry entry, out int entryIndex)
|
public static bool TryFind(int offset, out CacheEntry entry, out int entryIndex)
|
||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
int index = _cacheEntries.BinarySearch(new CacheEntry(offset, 0, default));
|
foreach (var region in _jitRegions)
|
||||||
|
|
||||||
if (index < 0)
|
|
||||||
{
|
{
|
||||||
index = ~index - 1;
|
int index = _cacheEntries.BinarySearch(new CacheEntry(offset, 0, default));
|
||||||
}
|
|
||||||
|
|
||||||
if (index >= 0)
|
if (index < 0)
|
||||||
{
|
{
|
||||||
entry = _cacheEntries[index];
|
index = ~index - 1;
|
||||||
entryIndex = index;
|
}
|
||||||
return true;
|
|
||||||
|
if (index >= 0)
|
||||||
|
{
|
||||||
|
entry = _cacheEntries[index];
|
||||||
|
entryIndex = index;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,18 +250,5 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
entryIndex = 0;
|
entryIndex = 0;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (OperatingSystem.IsWindows())
|
|
||||||
{
|
|
||||||
JitUnwindWindows.RemoveFunctionTableHandler(_jitRegions[0].Pointer);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (ReservedRegion jitRegion in _jitRegions)
|
|
||||||
{
|
|
||||||
jitRegion.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,6 @@
|
|||||||
|
|
||||||
using ARMeilleure.CodeGen.Unwinding;
|
using ARMeilleure.CodeGen.Unwinding;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
@@ -29,29 +27,6 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
public unsafe fixed ushort UnwindCodes[MaxUnwindCodesArraySize];
|
public unsafe fixed ushort UnwindCodes[MaxUnwindCodesArraySize];
|
||||||
}
|
}
|
||||||
|
|
||||||
private unsafe struct InternalFunctionHandler
|
|
||||||
{
|
|
||||||
public InternalFunctionHandler(JitCache jitCache, nint workBufferPtr)
|
|
||||||
{
|
|
||||||
_jitCache = jitCache;
|
|
||||||
|
|
||||||
_runtimeFunction = (RuntimeFunction*)workBufferPtr;
|
|
||||||
|
|
||||||
_unwindInfo = (UnwindInfo*)(workBufferPtr + _sizeOfRuntimeFunction);
|
|
||||||
}
|
|
||||||
|
|
||||||
readonly JitCache _jitCache;
|
|
||||||
|
|
||||||
readonly RuntimeFunction* _runtimeFunction;
|
|
||||||
|
|
||||||
readonly UnwindInfo* _unwindInfo;
|
|
||||||
|
|
||||||
public RuntimeFunction* FunctionTableHandler(ulong controlPc, nint context)
|
|
||||||
{
|
|
||||||
return JitUnwindWindows.FunctionTableHandler(_jitCache, _runtimeFunction, _unwindInfo, controlPc, context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum UnwindOp
|
private enum UnwindOp
|
||||||
{
|
{
|
||||||
PushNonvol = 0,
|
PushNonvol = 0,
|
||||||
@@ -84,28 +59,27 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
|
|
||||||
private static GetRuntimeFunctionCallback _getRuntimeFunctionCallback;
|
private static GetRuntimeFunctionCallback _getRuntimeFunctionCallback;
|
||||||
|
|
||||||
private static readonly int _sizeOfRuntimeFunction;
|
private static int _sizeOfRuntimeFunction;
|
||||||
|
|
||||||
private static readonly ConcurrentDictionary<ulong, InternalFunctionHandler> _functionTableHandlers = new();
|
private unsafe static RuntimeFunction* _runtimeFunction;
|
||||||
|
|
||||||
static JitUnwindWindows()
|
private unsafe static UnwindInfo* _unwindInfo;
|
||||||
{
|
|
||||||
_sizeOfRuntimeFunction = Marshal.SizeOf<RuntimeFunction>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void InstallFunctionTableHandler(JitCache jitCache, nint codeCachePointer, uint codeCacheLength, nint workBufferPtr)
|
public static void InstallFunctionTableHandler(nint codeCachePointer, uint codeCacheLength, nint workBufferPtr)
|
||||||
{
|
{
|
||||||
ulong codeCachePtr = (ulong)codeCachePointer.ToInt64();
|
ulong codeCachePtr = (ulong)codeCachePointer.ToInt64();
|
||||||
|
|
||||||
bool result;
|
_sizeOfRuntimeFunction = Marshal.SizeOf<RuntimeFunction>();
|
||||||
|
|
||||||
InternalFunctionHandler handler;
|
bool result;
|
||||||
|
|
||||||
unsafe
|
unsafe
|
||||||
{
|
{
|
||||||
handler = new InternalFunctionHandler(jitCache, workBufferPtr);
|
_runtimeFunction = (RuntimeFunction*)workBufferPtr;
|
||||||
|
|
||||||
_getRuntimeFunctionCallback = handler.FunctionTableHandler;
|
_unwindInfo = (UnwindInfo*)(workBufferPtr + _sizeOfRuntimeFunction);
|
||||||
|
|
||||||
|
_getRuntimeFunctionCallback = new GetRuntimeFunctionCallback(FunctionTableHandler);
|
||||||
|
|
||||||
result = RtlInstallFunctionTableCallback(
|
result = RtlInstallFunctionTableCallback(
|
||||||
codeCachePtr | 3,
|
codeCachePtr | 3,
|
||||||
@@ -120,8 +94,6 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
{
|
{
|
||||||
throw new InvalidOperationException("Failure installing function table callback.");
|
throw new InvalidOperationException("Failure installing function table callback.");
|
||||||
}
|
}
|
||||||
|
|
||||||
_functionTableHandlers.TryAdd(codeCachePtr, handler);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void RemoveFunctionTableHandler(nint codeCachePointer)
|
public static void RemoveFunctionTableHandler(nint codeCachePointer)
|
||||||
@@ -139,26 +111,24 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
{
|
{
|
||||||
throw new InvalidOperationException("Failure removing function table callback.");
|
throw new InvalidOperationException("Failure removing function table callback.");
|
||||||
}
|
}
|
||||||
|
|
||||||
_functionTableHandlers.Remove(codeCachePtr, out _);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static unsafe RuntimeFunction* FunctionTableHandler(JitCache jitCache, RuntimeFunction* runtimeFunction, UnwindInfo* unwindInfo, ulong controlPc, nint context)
|
private static unsafe RuntimeFunction* FunctionTableHandler(ulong controlPc, nint context)
|
||||||
{
|
{
|
||||||
int offset = (int)((long)controlPc - context.ToInt64());
|
int offset = (int)((long)controlPc - context.ToInt64());
|
||||||
|
|
||||||
if (!jitCache.TryFind(offset, out CacheEntry funcEntry, out _))
|
if (!JitCache.TryFind(offset, out CacheEntry funcEntry, out _))
|
||||||
{
|
{
|
||||||
return null; // Not found.
|
return null; // Not found.
|
||||||
}
|
}
|
||||||
|
|
||||||
CodeGen.Unwinding.UnwindInfo funcUnwindInfo = funcEntry.UnwindInfo;
|
var unwindInfo = funcEntry.UnwindInfo;
|
||||||
|
|
||||||
int codeIndex = 0;
|
int codeIndex = 0;
|
||||||
|
|
||||||
for (int index = funcUnwindInfo.PushEntries.Length - 1; index >= 0; index--)
|
for (int index = unwindInfo.PushEntries.Length - 1; index >= 0; index--)
|
||||||
{
|
{
|
||||||
UnwindPushEntry entry = funcUnwindInfo.PushEntries[index];
|
var entry = unwindInfo.PushEntries[index];
|
||||||
|
|
||||||
switch (entry.PseudoOp)
|
switch (entry.PseudoOp)
|
||||||
{
|
{
|
||||||
@@ -170,14 +140,14 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
|
|
||||||
if (stackOffset <= 0xFFFF0)
|
if (stackOffset <= 0xFFFF0)
|
||||||
{
|
{
|
||||||
unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.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(UnwindOp.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);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -191,18 +161,18 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
|
|
||||||
if (allocSize <= 128)
|
if (allocSize <= 128)
|
||||||
{
|
{
|
||||||
unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.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(UnwindOp.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(UnwindOp.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);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -210,7 +180,7 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
|
|
||||||
case UnwindPseudoOp.PushReg:
|
case UnwindPseudoOp.PushReg:
|
||||||
{
|
{
|
||||||
unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.PushNonvol, entry.PrologOffset, entry.RegIndex);
|
_unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.PushNonvol, entry.PrologOffset, entry.RegIndex);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -222,16 +192,16 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
|
|
||||||
Debug.Assert(codeIndex <= MaxUnwindCodesArraySize);
|
Debug.Assert(codeIndex <= MaxUnwindCodesArraySize);
|
||||||
|
|
||||||
unwindInfo->VersionAndFlags = 1; // Flags: The function has no handler.
|
_unwindInfo->VersionAndFlags = 1; // Flags: The function has no handler.
|
||||||
unwindInfo->SizeOfProlog = (byte)funcUnwindInfo.PrologSize;
|
_unwindInfo->SizeOfProlog = (byte)unwindInfo.PrologSize;
|
||||||
unwindInfo->CountOfUnwindCodes = (byte)codeIndex;
|
_unwindInfo->CountOfUnwindCodes = (byte)codeIndex;
|
||||||
unwindInfo->FrameRegister = 0;
|
_unwindInfo->FrameRegister = 0;
|
||||||
|
|
||||||
runtimeFunction->BeginAddress = (uint)funcEntry.Offset;
|
_runtimeFunction->BeginAddress = (uint)funcEntry.Offset;
|
||||||
runtimeFunction->EndAddress = (uint)(funcEntry.Offset + funcEntry.Size);
|
_runtimeFunction->EndAddress = (uint)(funcEntry.Offset + funcEntry.Size);
|
||||||
runtimeFunction->UnwindData = (uint)_sizeOfRuntimeFunction;
|
_runtimeFunction->UnwindData = (uint)_sizeOfRuntimeFunction;
|
||||||
|
|
||||||
return runtimeFunction;
|
return _runtimeFunction;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ushort PackUnwindOp(UnwindOp op, int prologOffset, int opInfo)
|
private static ushort PackUnwindOp(UnwindOp op, int prologOffset, int opInfo)
|
||||||
|
|||||||
@@ -47,8 +47,8 @@ namespace ARMeilleure.Translation
|
|||||||
{
|
{
|
||||||
RemoveUnreachableBlocks(Blocks);
|
RemoveUnreachableBlocks(Blocks);
|
||||||
|
|
||||||
HashSet<BasicBlock> visited = new();
|
var visited = new HashSet<BasicBlock>();
|
||||||
Stack<BasicBlock> blockStack = new();
|
var blockStack = new Stack<BasicBlock>();
|
||||||
|
|
||||||
Array.Resize(ref _postOrderBlocks, Blocks.Count);
|
Array.Resize(ref _postOrderBlocks, Blocks.Count);
|
||||||
Array.Resize(ref _postOrderMap, Blocks.Count);
|
Array.Resize(ref _postOrderMap, Blocks.Count);
|
||||||
@@ -88,8 +88,8 @@ namespace ARMeilleure.Translation
|
|||||||
|
|
||||||
private void RemoveUnreachableBlocks(IntrusiveList<BasicBlock> blocks)
|
private void RemoveUnreachableBlocks(IntrusiveList<BasicBlock> blocks)
|
||||||
{
|
{
|
||||||
HashSet<BasicBlock> visited = new();
|
var visited = new HashSet<BasicBlock>();
|
||||||
Queue<BasicBlock> workQueue = new();
|
var workQueue = new Queue<BasicBlock>();
|
||||||
|
|
||||||
visited.Add(Entry);
|
visited.Add(Entry);
|
||||||
workQueue.Enqueue(Entry);
|
workQueue.Enqueue(Entry);
|
||||||
|
|||||||
@@ -4,14 +4,12 @@ using ARMeilleure.CodeGen.Unwinding;
|
|||||||
using ARMeilleure.Common;
|
using ARMeilleure.Common;
|
||||||
using ARMeilleure.Memory;
|
using ARMeilleure.Memory;
|
||||||
using ARMeilleure.State;
|
using ARMeilleure.State;
|
||||||
using ARMeilleure.Translation.Cache;
|
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Configuration;
|
using Ryujinx.Common.Configuration;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Common.Memory;
|
using Ryujinx.Common.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.Buffers.Binary;
|
using System.Buffers.Binary;
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
@@ -32,11 +30,14 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
private const string OuterHeaderMagicString = "PTCohd\0\0";
|
private const string OuterHeaderMagicString = "PTCohd\0\0";
|
||||||
private const string InnerHeaderMagicString = "PTCihd\0\0";
|
private const string InnerHeaderMagicString = "PTCihd\0\0";
|
||||||
|
|
||||||
private const uint InternalVersion = 7031; //! To be incremented manually for each change to the ARMeilleure project.
|
private const uint InternalVersion = 7010; //! To be incremented manually for each change to the ARMeilleure project.
|
||||||
|
|
||||||
private const string ActualDir = "0";
|
private const string ActualDir = "0";
|
||||||
private const string BackupDir = "1";
|
private const string BackupDir = "1";
|
||||||
|
|
||||||
|
private const string TitleIdTextDefault = "0000000000000000";
|
||||||
|
private const string DisplayVersionDefault = "0";
|
||||||
|
|
||||||
public static readonly Symbol PageTableSymbol = new(SymbolType.Special, 1);
|
public static readonly Symbol PageTableSymbol = new(SymbolType.Special, 1);
|
||||||
public static readonly Symbol CountTableSymbol = new(SymbolType.Special, 2);
|
public static readonly Symbol CountTableSymbol = new(SymbolType.Special, 2);
|
||||||
public static readonly Symbol DispatchStubSymbol = new(SymbolType.Special, 3);
|
public static readonly Symbol DispatchStubSymbol = new(SymbolType.Special, 3);
|
||||||
@@ -47,8 +48,6 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
|
|
||||||
public PtcProfiler Profiler { get; }
|
public PtcProfiler Profiler { get; }
|
||||||
|
|
||||||
private readonly JitCache _jitCache;
|
|
||||||
|
|
||||||
// Carriers.
|
// Carriers.
|
||||||
private MemoryStream _infosStream;
|
private MemoryStream _infosStream;
|
||||||
private List<byte[]> _codesList;
|
private List<byte[]> _codesList;
|
||||||
@@ -64,7 +63,8 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
|
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
|
|
||||||
public PtcCacheInfo CacheInfo { get; private set; }
|
public string TitleIdText { get; private set; }
|
||||||
|
public string DisplayVersion { get; private set; }
|
||||||
|
|
||||||
private MemoryManagerType _memoryMode;
|
private MemoryManagerType _memoryMode;
|
||||||
|
|
||||||
@@ -78,7 +78,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
private volatile int _translateTotalCount;
|
private volatile int _translateTotalCount;
|
||||||
public event Action<PtcLoadingState, int, int> PtcStateChanged;
|
public event Action<PtcLoadingState, int, int> PtcStateChanged;
|
||||||
|
|
||||||
public Ptc(JitCache jitCache)
|
public Ptc()
|
||||||
{
|
{
|
||||||
Profiler = new PtcProfiler(this);
|
Profiler = new PtcProfiler(this);
|
||||||
|
|
||||||
@@ -89,11 +89,10 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
|
|
||||||
_waitEvent = new ManualResetEvent(true);
|
_waitEvent = new ManualResetEvent(true);
|
||||||
|
|
||||||
_jitCache = jitCache;
|
|
||||||
|
|
||||||
_disposed = false;
|
_disposed = false;
|
||||||
|
|
||||||
CacheInfo = new PtcCacheInfo(0, null, null, 0, null, "Unknown", "default");
|
TitleIdText = TitleIdTextDefault;
|
||||||
|
DisplayVersion = DisplayVersionDefault;
|
||||||
|
|
||||||
CachePathActual = string.Empty;
|
CachePathActual = string.Empty;
|
||||||
CachePathBackup = string.Empty;
|
CachePathBackup = string.Empty;
|
||||||
@@ -101,24 +100,20 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
Disable();
|
Disable();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Initialize(PtcCacheInfo cacheInfo, bool enabled, MemoryManagerType memoryMode)
|
public void Initialize(string titleIdText, string displayVersion, bool enabled, MemoryManagerType memoryMode, string cacheSelector)
|
||||||
{
|
{
|
||||||
Wait();
|
Wait();
|
||||||
|
|
||||||
Profiler.Wait();
|
Profiler.Wait();
|
||||||
Profiler.ClearEntries();
|
Profiler.ClearEntries();
|
||||||
|
|
||||||
CacheInfo = cacheInfo;
|
Logger.Info?.Print(LogClass.Ptc, $"Initializing Profiled Persistent Translation Cache v{InternalVersion}\n\t\t (title: {titleIdText}, version: '{displayVersion}', selector: '{cacheSelector}', enabled: {enabled}).");
|
||||||
|
|
||||||
Logger.Info?.Print(
|
if (!enabled || string.IsNullOrEmpty(titleIdText) || titleIdText == TitleIdTextDefault)
|
||||||
LogClass.Ptc,
|
|
||||||
$"Initializing Profiled Persistent Translation Cache v{InternalVersion}\n\t\t " +
|
|
||||||
$"(pid: {cacheInfo.ProcessId}, title: {cacheInfo.TitleIdText}, application: {cacheInfo.ApplicationIdText}, " +
|
|
||||||
$"programIndex: {cacheInfo.ProgramIndex}, version: '{cacheInfo.DisplayVersion}', kind: {cacheInfo.ProcessKind}, " +
|
|
||||||
$"selector: '{cacheInfo.CacheSelector}', key: '{cacheInfo.CacheKey}', enabled: {enabled}).");
|
|
||||||
|
|
||||||
if (!enabled || cacheInfo.TitleIdText == PtcCacheInfo.TitleIdTextDefault)
|
|
||||||
{
|
{
|
||||||
|
TitleIdText = TitleIdTextDefault;
|
||||||
|
DisplayVersion = DisplayVersionDefault;
|
||||||
|
|
||||||
CachePathActual = string.Empty;
|
CachePathActual = string.Empty;
|
||||||
CachePathBackup = string.Empty;
|
CachePathBackup = string.Empty;
|
||||||
|
|
||||||
@@ -127,10 +122,12 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TitleIdText = titleIdText;
|
||||||
|
DisplayVersion = !string.IsNullOrEmpty(displayVersion) ? displayVersion : DisplayVersionDefault;
|
||||||
_memoryMode = memoryMode;
|
_memoryMode = memoryMode;
|
||||||
|
|
||||||
string workPathActual = Path.Combine(AppDataManager.GamesDirPath, CacheInfo.TitleIdText, "cache", "cpu", ActualDir);
|
string workPathActual = Path.Combine(AppDataManager.GamesDirPath, TitleIdText, "cache", "cpu", ActualDir);
|
||||||
string workPathBackup = Path.Combine(AppDataManager.GamesDirPath, CacheInfo.TitleIdText, "cache", "cpu", BackupDir);
|
string workPathBackup = Path.Combine(AppDataManager.GamesDirPath, TitleIdText, "cache", "cpu", BackupDir);
|
||||||
|
|
||||||
if (!Directory.Exists(workPathActual))
|
if (!Directory.Exists(workPathActual))
|
||||||
{
|
{
|
||||||
@@ -142,14 +139,8 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
Directory.CreateDirectory(workPathBackup);
|
Directory.CreateDirectory(workPathBackup);
|
||||||
}
|
}
|
||||||
|
|
||||||
CachePathActual = Path.Combine(workPathActual, CacheInfo.DisplayVersion) + "-" + CacheInfo.CacheSelector;
|
CachePathActual = Path.Combine(workPathActual, DisplayVersion) + "-" + cacheSelector;
|
||||||
CachePathBackup = Path.Combine(workPathBackup, CacheInfo.DisplayVersion) + "-" + CacheInfo.CacheSelector;
|
CachePathBackup = Path.Combine(workPathBackup, DisplayVersion) + "-" + cacheSelector;
|
||||||
|
|
||||||
Logger.Info?.Print(
|
|
||||||
LogClass.Ptc,
|
|
||||||
$"PPTC cache owner selected (pid: {CacheInfo.ProcessId}, title: {CacheInfo.TitleIdText}, application: {CacheInfo.ApplicationIdText}, " +
|
|
||||||
$"version: '{CacheInfo.DisplayVersion}', kind: {CacheInfo.ProcessKind}, selector: '{CacheInfo.CacheSelector}', " +
|
|
||||||
$"key: '{CacheInfo.CacheKey}', path: '{CachePathActual}').");
|
|
||||||
|
|
||||||
PreLoad();
|
PreLoad();
|
||||||
Profiler.PreLoad();
|
Profiler.PreLoad();
|
||||||
@@ -373,12 +364,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
|
|
||||||
long fileSize = new FileInfo(fileName).Length;
|
long fileSize = new FileInfo(fileName).Length;
|
||||||
|
|
||||||
Logger.Info?.Print(
|
Logger.Info?.Print(LogClass.Ptc, $"{(isBackup ? "Loaded Backup Translation Cache" : "Loaded Translation Cache")} (size: {fileSize} bytes, translated functions: {GetEntriesCount()}).");
|
||||||
LogClass.Ptc,
|
|
||||||
$"{(isBackup ? "Loaded Backup Translation Cache" : "Loaded Translation Cache")} " +
|
|
||||||
$"(pid: {CacheInfo.ProcessId}, title: {CacheInfo.TitleIdText}, version: '{CacheInfo.DisplayVersion}', kind: {CacheInfo.ProcessKind}, " +
|
|
||||||
$"selector: '{CacheInfo.CacheSelector}', key: '{CacheInfo.CacheKey}', path: '{fileName}', " +
|
|
||||||
$"size: {fileSize} bytes, translated functions: {GetEntriesCount()}).");
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -558,7 +544,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
|
|
||||||
bool isEntryChanged = infoEntry.Hash != ComputeHash(translator.Memory, infoEntry.Address, infoEntry.GuestSize);
|
bool isEntryChanged = infoEntry.Hash != ComputeHash(translator.Memory, infoEntry.Address, infoEntry.GuestSize);
|
||||||
|
|
||||||
if (isEntryChanged || (!infoEntry.HighCq && Profiler.ProfiledFuncs.TryGetValue(infoEntry.Address, out PtcProfiler.FuncProfile value) && value.HighCq))
|
if (isEntryChanged || (!infoEntry.HighCq && Profiler.ProfiledFuncs.TryGetValue(infoEntry.Address, out var value) && value.HighCq))
|
||||||
{
|
{
|
||||||
infoEntry.Stubbed = true;
|
infoEntry.Stubbed = true;
|
||||||
infoEntry.CodeLength = 0;
|
infoEntry.CodeLength = 0;
|
||||||
@@ -738,15 +724,15 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
return new UnwindInfo(pushEntries, prologueSize);
|
return new UnwindInfo(pushEntries, prologueSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
private TranslatedFunction FastTranslate(
|
private static TranslatedFunction FastTranslate(
|
||||||
byte[] code,
|
byte[] code,
|
||||||
Counter<uint> callCounter,
|
Counter<uint> callCounter,
|
||||||
ulong guestSize,
|
ulong guestSize,
|
||||||
UnwindInfo unwindInfo,
|
UnwindInfo unwindInfo,
|
||||||
bool highCq)
|
bool highCq)
|
||||||
{
|
{
|
||||||
CompiledFunction cFunc = new(code, unwindInfo, RelocInfo.Empty);
|
var cFunc = new CompiledFunction(code, unwindInfo, RelocInfo.Empty);
|
||||||
GuestFunction gFunc = cFunc.MapWithPointer<GuestFunction>(_jitCache, out nint gFuncPointer);
|
var gFunc = cFunc.MapWithPointer<GuestFunction>(out nint gFuncPointer);
|
||||||
|
|
||||||
return new TranslatedFunction(gFunc, gFuncPointer, callCounter, guestSize, highCq);
|
return new TranslatedFunction(gFunc, gFuncPointer, callCounter, guestSize, highCq);
|
||||||
}
|
}
|
||||||
@@ -783,7 +769,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
|
|
||||||
public void MakeAndSaveTranslations(Translator translator)
|
public void MakeAndSaveTranslations(Translator translator)
|
||||||
{
|
{
|
||||||
ConcurrentQueue<(ulong address, PtcProfiler.FuncProfile funcProfile)> profiledFuncsToTranslate = Profiler.GetProfiledFuncsToTranslate(translator.Functions);
|
var profiledFuncsToTranslate = Profiler.GetProfiledFuncsToTranslate(translator.Functions);
|
||||||
|
|
||||||
_translateCount = 0;
|
_translateCount = 0;
|
||||||
_translateTotalCount = profiledFuncsToTranslate.Count;
|
_translateTotalCount = profiledFuncsToTranslate.Count;
|
||||||
@@ -827,7 +813,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
|
|
||||||
void TranslateFuncs()
|
void TranslateFuncs()
|
||||||
{
|
{
|
||||||
while (profiledFuncsToTranslate.TryDequeue(out (ulong address, PtcProfiler.FuncProfile funcProfile) item))
|
while (profiledFuncsToTranslate.TryDequeue(out var item))
|
||||||
{
|
{
|
||||||
ulong address = item.address;
|
ulong address = item.address;
|
||||||
ExecutionMode executionMode = item.funcProfile.Mode;
|
ExecutionMode executionMode = item.funcProfile.Mode;
|
||||||
@@ -872,11 +858,11 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
|
|
||||||
Stopwatch sw = Stopwatch.StartNew();
|
Stopwatch sw = Stopwatch.StartNew();
|
||||||
|
|
||||||
foreach (Thread thread in threads)
|
foreach (var thread in threads)
|
||||||
{
|
{
|
||||||
thread.Start();
|
thread.Start();
|
||||||
}
|
}
|
||||||
foreach (Thread thread in threads)
|
foreach (var thread in threads)
|
||||||
{
|
{
|
||||||
thread.Join();
|
thread.Join();
|
||||||
}
|
}
|
||||||
@@ -956,7 +942,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
WriteCode(code.AsSpan());
|
WriteCode(code.AsSpan());
|
||||||
|
|
||||||
// WriteReloc.
|
// WriteReloc.
|
||||||
using BinaryWriter relocInfoWriter = new(_relocsStream, EncodingCache.UTF8NoBOM, true);
|
using var relocInfoWriter = new BinaryWriter(_relocsStream, EncodingCache.UTF8NoBOM, true);
|
||||||
|
|
||||||
foreach (RelocEntry entry in relocInfo.Entries)
|
foreach (RelocEntry entry in relocInfo.Entries)
|
||||||
{
|
{
|
||||||
@@ -966,7 +952,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
}
|
}
|
||||||
|
|
||||||
// WriteUnwindInfo.
|
// WriteUnwindInfo.
|
||||||
using BinaryWriter unwindInfoWriter = new(_unwindInfosStream, EncodingCache.UTF8NoBOM, true);
|
using var unwindInfoWriter = new BinaryWriter(_unwindInfosStream, EncodingCache.UTF8NoBOM, true);
|
||||||
|
|
||||||
unwindInfoWriter.Write(unwindInfo.PushEntries.Length);
|
unwindInfoWriter.Write(unwindInfo.PushEntries.Length);
|
||||||
|
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
namespace ARMeilleure.Translation.PTC
|
|
||||||
{
|
|
||||||
public readonly struct PtcCacheInfo
|
|
||||||
{
|
|
||||||
public const string TitleIdTextDefault = "0000000000000000";
|
|
||||||
public const string ApplicationIdTextDefault = "0000000000000000";
|
|
||||||
public const string DisplayVersionDefault = "0";
|
|
||||||
|
|
||||||
public ulong ProcessId { get; }
|
|
||||||
public string TitleIdText { get; }
|
|
||||||
public string ApplicationIdText { get; }
|
|
||||||
public byte ProgramIndex { get; }
|
|
||||||
public string DisplayVersion { get; }
|
|
||||||
public string ProcessKind { get; }
|
|
||||||
public string CacheSelector { get; }
|
|
||||||
|
|
||||||
public string CacheKey => $"{DisplayVersion}-{CacheSelector}";
|
|
||||||
|
|
||||||
public PtcCacheInfo(
|
|
||||||
ulong processId,
|
|
||||||
string titleIdText,
|
|
||||||
string applicationIdText,
|
|
||||||
byte programIndex,
|
|
||||||
string displayVersion,
|
|
||||||
string processKind,
|
|
||||||
string cacheSelector)
|
|
||||||
{
|
|
||||||
ProcessId = processId;
|
|
||||||
TitleIdText = !string.IsNullOrEmpty(titleIdText) ? titleIdText : TitleIdTextDefault;
|
|
||||||
ApplicationIdText = !string.IsNullOrEmpty(applicationIdText) ? applicationIdText : ApplicationIdTextDefault;
|
|
||||||
ProgramIndex = programIndex;
|
|
||||||
DisplayVersion = !string.IsNullOrEmpty(displayVersion) ? displayVersion : DisplayVersionDefault;
|
|
||||||
ProcessKind = processKind ?? string.Empty;
|
|
||||||
CacheSelector = string.IsNullOrEmpty(cacheSelector) ? "default" : cacheSelector;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -23,7 +23,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
{
|
{
|
||||||
private const string OuterHeaderMagicString = "Pohd\0\0\0\0";
|
private const string OuterHeaderMagicString = "Pohd\0\0\0\0";
|
||||||
|
|
||||||
private const uint InternalVersion = 7031; //! Not to be incremented manually for each change to the ARMeilleure project.
|
private const uint InternalVersion = 6698; //! Not to be incremented manually for each change to the ARMeilleure project.
|
||||||
|
|
||||||
private static readonly uint[] _migrateInternalVersions =
|
private static readonly uint[] _migrateInternalVersions =
|
||||||
[
|
[
|
||||||
@@ -119,9 +119,9 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
|
|
||||||
public ConcurrentQueue<(ulong address, FuncProfile funcProfile)> GetProfiledFuncsToTranslate(TranslatorCache<TranslatedFunction> funcs)
|
public ConcurrentQueue<(ulong address, FuncProfile funcProfile)> GetProfiledFuncsToTranslate(TranslatorCache<TranslatedFunction> funcs)
|
||||||
{
|
{
|
||||||
ConcurrentQueue<(ulong address, FuncProfile funcProfile)> profiledFuncsToTranslate = new();
|
var profiledFuncsToTranslate = new ConcurrentQueue<(ulong address, FuncProfile funcProfile)>();
|
||||||
|
|
||||||
foreach (KeyValuePair<ulong, FuncProfile> profiledFunc in ProfiledFuncs)
|
foreach (var profiledFunc in ProfiledFuncs)
|
||||||
{
|
{
|
||||||
if (!funcs.ContainsKey(profiledFunc.Key) && !profiledFunc.Value.Blacklist)
|
if (!funcs.ContainsKey(profiledFunc.Key) && !profiledFunc.Value.Blacklist)
|
||||||
{
|
{
|
||||||
@@ -142,7 +142,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
{
|
{
|
||||||
List<ulong> funcs = [];
|
List<ulong> funcs = [];
|
||||||
|
|
||||||
foreach (KeyValuePair<ulong, FuncProfile> profiledFunc in ProfiledFuncs)
|
foreach (var profiledFunc in ProfiledFuncs)
|
||||||
{
|
{
|
||||||
if (profiledFunc.Value.Blacklist)
|
if (profiledFunc.Value.Blacklist)
|
||||||
{
|
{
|
||||||
@@ -254,12 +254,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
|
|
||||||
long fileSize = new FileInfo(fileName).Length;
|
long fileSize = new FileInfo(fileName).Length;
|
||||||
|
|
||||||
Logger.Info?.Print(
|
Logger.Info?.Print(LogClass.Ptc, $"{(isBackup ? "Loaded Backup Profiling Info" : "Loaded Profiling Info")} (size: {fileSize} bytes, profiled functions: {ProfiledFuncs.Count}).");
|
||||||
LogClass.Ptc,
|
|
||||||
$"{(isBackup ? "Loaded Backup Profiling Info" : "Loaded Profiling Info")} " +
|
|
||||||
$"(pid: {_ptc.CacheInfo.ProcessId}, title: {_ptc.CacheInfo.TitleIdText}, version: '{_ptc.CacheInfo.DisplayVersion}', " +
|
|
||||||
$"kind: {_ptc.CacheInfo.ProcessKind}, selector: '{_ptc.CacheInfo.CacheSelector}', key: '{_ptc.CacheInfo.CacheKey}', " +
|
|
||||||
$"path: '{fileName}', size: {fileSize} bytes, profiled functions: {ProfiledFuncs.Count}).");
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -380,11 +375,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
|
|
||||||
if (fileSize != 0L)
|
if (fileSize != 0L)
|
||||||
{
|
{
|
||||||
Logger.Info?.Print(
|
Logger.Info?.Print(LogClass.Ptc, $"Saved Profiling Info (size: {fileSize} bytes, profiled functions: {profiledFuncsCount}).");
|
||||||
LogClass.Ptc,
|
|
||||||
$"Saved Profiling Info (pid: {_ptc.CacheInfo.ProcessId}, title: {_ptc.CacheInfo.TitleIdText}, version: '{_ptc.CacheInfo.DisplayVersion}', " +
|
|
||||||
$"kind: {_ptc.CacheInfo.ProcessKind}, selector: '{_ptc.CacheInfo.CacheSelector}', key: '{_ptc.CacheInfo.CacheKey}', " +
|
|
||||||
$"path: '{fileName}', size: {fileSize} bytes, profiled functions: {profiledFuncsCount}).");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,10 +44,10 @@ namespace ARMeilleure.Translation
|
|||||||
|
|
||||||
public static void Construct(ControlFlowGraph cfg)
|
public static void Construct(ControlFlowGraph cfg)
|
||||||
{
|
{
|
||||||
DefMap[] globalDefs = new DefMap[cfg.Blocks.Count];
|
var globalDefs = new DefMap[cfg.Blocks.Count];
|
||||||
Operand[] localDefs = new Operand[cfg.LocalsCount + RegisterConsts.TotalCount];
|
var localDefs = new Operand[cfg.LocalsCount + RegisterConsts.TotalCount];
|
||||||
|
|
||||||
Queue<BasicBlock> dfPhiBlocks = new();
|
var dfPhiBlocks = new Queue<BasicBlock>();
|
||||||
|
|
||||||
for (BasicBlock block = cfg.Blocks.First; block != null; block = block.ListNext)
|
for (BasicBlock block = cfg.Blocks.First; block != null; block = block.ListNext)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -24,11 +24,10 @@ namespace ARMeilleure.Translation
|
|||||||
private readonly IJitMemoryAllocator _allocator;
|
private readonly IJitMemoryAllocator _allocator;
|
||||||
private readonly ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>> _oldFuncs;
|
private readonly ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>> _oldFuncs;
|
||||||
|
|
||||||
public readonly JitCache JitCache;
|
|
||||||
private readonly Ptc _ptc;
|
private readonly Ptc _ptc;
|
||||||
|
|
||||||
internal TranslatorCache<TranslatedFunction> Functions { get; }
|
internal TranslatorCache<TranslatedFunction> Functions { get; }
|
||||||
public IAddressTable<ulong> FunctionTable { get; }
|
internal IAddressTable<ulong> FunctionTable { get; }
|
||||||
internal EntryTable<uint> CountTable { get; }
|
internal EntryTable<uint> CountTable { get; }
|
||||||
internal TranslatorStubs Stubs { get; }
|
internal TranslatorStubs Stubs { get; }
|
||||||
internal TranslatorQueue Queue { get; }
|
internal TranslatorQueue Queue { get; }
|
||||||
@@ -44,26 +43,23 @@ namespace ARMeilleure.Translation
|
|||||||
|
|
||||||
_oldFuncs = new ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>>();
|
_oldFuncs = new ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>>();
|
||||||
|
|
||||||
JitCache = new JitCache(allocator);
|
_ptc = new Ptc();
|
||||||
|
|
||||||
_ptc = new Ptc(JitCache);
|
|
||||||
|
|
||||||
Queue = new TranslatorQueue();
|
Queue = new TranslatorQueue();
|
||||||
|
|
||||||
|
JitCache.Initialize(allocator);
|
||||||
|
|
||||||
CountTable = new EntryTable<uint>();
|
CountTable = new EntryTable<uint>();
|
||||||
Functions = new TranslatorCache<TranslatedFunction>();
|
Functions = new TranslatorCache<TranslatedFunction>();
|
||||||
|
|
||||||
FunctionTable = functionTable;
|
FunctionTable = functionTable;
|
||||||
|
Stubs = new TranslatorStubs(FunctionTable);
|
||||||
Stubs = new TranslatorStubs(JitCache, FunctionTable);
|
|
||||||
|
|
||||||
FunctionTable.Fill = (ulong)Stubs.SlowDispatchStub;
|
FunctionTable.Fill = (ulong)Stubs.SlowDispatchStub;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IPtcLoadState LoadDiskCache(PtcCacheInfo cacheInfo, bool enabled)
|
public IPtcLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled, string cacheSelector)
|
||||||
{
|
{
|
||||||
_ptc.Initialize(cacheInfo, enabled, Memory.Type);
|
_ptc.Initialize(titleIdText, displayVersion, enabled, Memory.Type, cacheSelector);
|
||||||
return _ptc;
|
return _ptc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,7 +167,6 @@ namespace ARMeilleure.Translation
|
|||||||
}
|
}
|
||||||
|
|
||||||
ClearJitCache();
|
ClearJitCache();
|
||||||
JitCache.Dispose();
|
|
||||||
|
|
||||||
Stubs.Dispose();
|
Stubs.Dispose();
|
||||||
FunctionTable.Dispose();
|
FunctionTable.Dispose();
|
||||||
@@ -247,7 +242,7 @@ namespace ARMeilleure.Translation
|
|||||||
|
|
||||||
internal TranslatedFunction Translate(ulong address, ExecutionMode mode, bool highCq, bool singleStep = false, bool pptcTranslation = false)
|
internal TranslatedFunction Translate(ulong address, ExecutionMode mode, bool highCq, bool singleStep = false, bool pptcTranslation = false)
|
||||||
{
|
{
|
||||||
ArmEmitterContext context = new(
|
var context = new ArmEmitterContext(
|
||||||
Memory,
|
Memory,
|
||||||
CountTable,
|
CountTable,
|
||||||
FunctionTable,
|
FunctionTable,
|
||||||
@@ -291,10 +286,10 @@ namespace ARMeilleure.Translation
|
|||||||
|
|
||||||
Logger.EndPass(PassName.RegisterUsage);
|
Logger.EndPass(PassName.RegisterUsage);
|
||||||
|
|
||||||
OperandType retType = OperandType.I64;
|
var retType = OperandType.I64;
|
||||||
OperandType[] argTypes = new OperandType[] { OperandType.I64 };
|
var argTypes = new OperandType[] { OperandType.I64 };
|
||||||
|
|
||||||
CompilerOptions options = highCq ? CompilerOptions.HighCq : CompilerOptions.None;
|
var options = highCq ? CompilerOptions.HighCq : CompilerOptions.None;
|
||||||
|
|
||||||
if (context.HasPtc && !singleStep)
|
if (context.HasPtc && !singleStep)
|
||||||
{
|
{
|
||||||
@@ -310,7 +305,7 @@ namespace ARMeilleure.Translation
|
|||||||
_ptc.WriteCompiledFunction(address, funcSize, hash, highCq, compiledFunc);
|
_ptc.WriteCompiledFunction(address, funcSize, hash, highCq, compiledFunc);
|
||||||
}
|
}
|
||||||
|
|
||||||
GuestFunction func = compiledFunc.MapWithPointer<GuestFunction>(JitCache, out nint funcPointer);
|
GuestFunction func = compiledFunc.MapWithPointer<GuestFunction>(out nint funcPointer);
|
||||||
|
|
||||||
Allocators.ResetAll();
|
Allocators.ResetAll();
|
||||||
|
|
||||||
@@ -579,7 +574,7 @@ namespace ARMeilleure.Translation
|
|||||||
|
|
||||||
List<TranslatedFunction> functions = Functions.AsList();
|
List<TranslatedFunction> functions = Functions.AsList();
|
||||||
|
|
||||||
foreach (TranslatedFunction func in functions)
|
foreach (var func in functions)
|
||||||
{
|
{
|
||||||
JitCache.Unmap(func.FuncPointer);
|
JitCache.Unmap(func.FuncPointer);
|
||||||
|
|
||||||
@@ -588,7 +583,7 @@ namespace ARMeilleure.Translation
|
|||||||
|
|
||||||
Functions.Clear();
|
Functions.Clear();
|
||||||
|
|
||||||
while (_oldFuncs.TryDequeue(out KeyValuePair<ulong, TranslatedFunction> kv))
|
while (_oldFuncs.TryDequeue(out var kv))
|
||||||
{
|
{
|
||||||
JitCache.Unmap(kv.Value.FuncPointer);
|
JitCache.Unmap(kv.Value.FuncPointer);
|
||||||
|
|
||||||
@@ -609,7 +604,7 @@ namespace ARMeilleure.Translation
|
|||||||
{
|
{
|
||||||
while (Queue.Count > 0 && Queue.TryDequeue(out RejitRequest request))
|
while (Queue.Count > 0 && Queue.TryDequeue(out RejitRequest request))
|
||||||
{
|
{
|
||||||
if (Functions.TryGetValue(request.Address, out TranslatedFunction func) && func.CallCounter != null)
|
if (Functions.TryGetValue(request.Address, out var func) && func.CallCounter != null)
|
||||||
{
|
{
|
||||||
Volatile.Write(ref func.CallCounter.Value, 0);
|
Volatile.Write(ref func.CallCounter.Value, 0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,8 +14,6 @@ namespace ARMeilleure.Translation
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
class TranslatorStubs : IDisposable
|
class TranslatorStubs : IDisposable
|
||||||
{
|
{
|
||||||
private readonly JitCache _jitCache;
|
|
||||||
|
|
||||||
private readonly Lazy<nint> _slowDispatchStub;
|
private readonly Lazy<nint> _slowDispatchStub;
|
||||||
|
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
@@ -85,15 +83,12 @@ namespace ARMeilleure.Translation
|
|||||||
/// Initializes a new instance of the <see cref="TranslatorStubs"/> class with the specified
|
/// Initializes a new instance of the <see cref="TranslatorStubs"/> class with the specified
|
||||||
/// <see cref="Translator"/> instance.
|
/// <see cref="Translator"/> instance.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="jitCache">Jit cache to map functions in</param>
|
|
||||||
/// <param name="functionTable">Function table used to store pointers to the functions that the guest code will call</param>
|
/// <param name="functionTable">Function table used to store pointers to the functions that the guest code will call</param>
|
||||||
/// <exception cref="ArgumentNullException"><paramref name="translator"/> is null</exception>
|
/// <exception cref="ArgumentNullException"><paramref name="translator"/> is null</exception>
|
||||||
public TranslatorStubs(JitCache jitCache, IAddressTable<ulong> functionTable)
|
public TranslatorStubs(IAddressTable<ulong> functionTable)
|
||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(functionTable);
|
ArgumentNullException.ThrowIfNull(functionTable);
|
||||||
|
|
||||||
_jitCache = jitCache;
|
|
||||||
|
|
||||||
_functionTable = functionTable;
|
_functionTable = functionTable;
|
||||||
_slowDispatchStub = new(GenerateSlowDispatchStub, isThreadSafe: true);
|
_slowDispatchStub = new(GenerateSlowDispatchStub, isThreadSafe: true);
|
||||||
_dispatchStub = new(GenerateDispatchStub, isThreadSafe: true);
|
_dispatchStub = new(GenerateDispatchStub, isThreadSafe: true);
|
||||||
@@ -120,12 +115,12 @@ namespace ARMeilleure.Translation
|
|||||||
{
|
{
|
||||||
if (_dispatchStub.IsValueCreated)
|
if (_dispatchStub.IsValueCreated)
|
||||||
{
|
{
|
||||||
_jitCache.Unmap(_dispatchStub.Value);
|
JitCache.Unmap(_dispatchStub.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_dispatchLoop.IsValueCreated)
|
if (_dispatchLoop.IsValueCreated)
|
||||||
{
|
{
|
||||||
_jitCache.Unmap(Marshal.GetFunctionPointerForDelegate(_dispatchLoop.Value));
|
JitCache.Unmap(Marshal.GetFunctionPointerForDelegate(_dispatchLoop.Value));
|
||||||
}
|
}
|
||||||
|
|
||||||
_disposed = true;
|
_disposed = true;
|
||||||
@@ -146,7 +141,7 @@ namespace ARMeilleure.Translation
|
|||||||
/// <returns>Generated <see cref="DispatchStub"/></returns>
|
/// <returns>Generated <see cref="DispatchStub"/></returns>
|
||||||
private nint GenerateDispatchStub()
|
private nint GenerateDispatchStub()
|
||||||
{
|
{
|
||||||
EmitterContext context = new();
|
var context = new EmitterContext();
|
||||||
|
|
||||||
Operand lblFallback = Label();
|
Operand lblFallback = Label();
|
||||||
Operand lblEnd = Label();
|
Operand lblEnd = Label();
|
||||||
@@ -165,7 +160,7 @@ namespace ARMeilleure.Translation
|
|||||||
|
|
||||||
for (int i = 0; i < _functionTable.Levels.Length; i++)
|
for (int i = 0; i < _functionTable.Levels.Length; i++)
|
||||||
{
|
{
|
||||||
ref AddressTableLevel level = ref _functionTable.Levels[i];
|
ref var level = ref _functionTable.Levels[i];
|
||||||
|
|
||||||
// level.Mask is not used directly because it is more often bigger than 32-bits, so it will not
|
// level.Mask is not used directly because it is more often bigger than 32-bits, so it will not
|
||||||
// be encoded as an immediate on x86's bitwise and operation.
|
// be encoded as an immediate on x86's bitwise and operation.
|
||||||
@@ -189,11 +184,11 @@ namespace ARMeilleure.Translation
|
|||||||
hostAddress = context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFunctionAddress)), guestAddress);
|
hostAddress = context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFunctionAddress)), guestAddress);
|
||||||
context.Tailcall(hostAddress, nativeContext);
|
context.Tailcall(hostAddress, nativeContext);
|
||||||
|
|
||||||
ControlFlowGraph cfg = context.GetControlFlowGraph();
|
var cfg = context.GetControlFlowGraph();
|
||||||
OperandType retType = OperandType.I64;
|
var retType = OperandType.I64;
|
||||||
OperandType[] argTypes = new[] { OperandType.I64 };
|
var argTypes = new[] { OperandType.I64 };
|
||||||
|
|
||||||
GuestFunction func = Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<GuestFunction>(_jitCache);
|
var func = Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<GuestFunction>();
|
||||||
|
|
||||||
return Marshal.GetFunctionPointerForDelegate(func);
|
return Marshal.GetFunctionPointerForDelegate(func);
|
||||||
}
|
}
|
||||||
@@ -204,7 +199,7 @@ namespace ARMeilleure.Translation
|
|||||||
/// <returns>Generated <see cref="SlowDispatchStub"/></returns>
|
/// <returns>Generated <see cref="SlowDispatchStub"/></returns>
|
||||||
private nint GenerateSlowDispatchStub()
|
private nint GenerateSlowDispatchStub()
|
||||||
{
|
{
|
||||||
EmitterContext context = new();
|
var context = new EmitterContext();
|
||||||
|
|
||||||
// Load the target guest address from the native context.
|
// Load the target guest address from the native context.
|
||||||
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
|
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
|
||||||
@@ -214,11 +209,11 @@ namespace ARMeilleure.Translation
|
|||||||
Operand hostAddress = context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFunctionAddress)), guestAddress);
|
Operand hostAddress = context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFunctionAddress)), guestAddress);
|
||||||
context.Tailcall(hostAddress, nativeContext);
|
context.Tailcall(hostAddress, nativeContext);
|
||||||
|
|
||||||
ControlFlowGraph cfg = context.GetControlFlowGraph();
|
var cfg = context.GetControlFlowGraph();
|
||||||
OperandType retType = OperandType.I64;
|
var retType = OperandType.I64;
|
||||||
OperandType[] argTypes = new[] { OperandType.I64 };
|
var argTypes = new[] { OperandType.I64 };
|
||||||
|
|
||||||
GuestFunction func = Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<GuestFunction>(_jitCache);
|
var func = Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<GuestFunction>();
|
||||||
|
|
||||||
return Marshal.GetFunctionPointerForDelegate(func);
|
return Marshal.GetFunctionPointerForDelegate(func);
|
||||||
}
|
}
|
||||||
@@ -255,7 +250,7 @@ namespace ARMeilleure.Translation
|
|||||||
/// <returns><see cref="DispatchLoop"/> function</returns>
|
/// <returns><see cref="DispatchLoop"/> function</returns>
|
||||||
private DispatcherFunction GenerateDispatchLoop()
|
private DispatcherFunction GenerateDispatchLoop()
|
||||||
{
|
{
|
||||||
EmitterContext context = new();
|
var context = new EmitterContext();
|
||||||
|
|
||||||
Operand beginLbl = Label();
|
Operand beginLbl = Label();
|
||||||
Operand endLbl = Label();
|
Operand endLbl = Label();
|
||||||
@@ -291,11 +286,11 @@ namespace ARMeilleure.Translation
|
|||||||
|
|
||||||
context.Return();
|
context.Return();
|
||||||
|
|
||||||
ControlFlowGraph cfg = context.GetControlFlowGraph();
|
var cfg = context.GetControlFlowGraph();
|
||||||
OperandType retType = OperandType.None;
|
var retType = OperandType.None;
|
||||||
OperandType[] argTypes = new[] { OperandType.I64, OperandType.I64 };
|
var argTypes = new[] { OperandType.I64, OperandType.I64 };
|
||||||
|
|
||||||
return Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<DispatcherFunction>(_jitCache);
|
return Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<DispatcherFunction>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -304,7 +299,7 @@ namespace ARMeilleure.Translation
|
|||||||
/// <returns><see cref="ContextWrapper"/> function</returns>
|
/// <returns><see cref="ContextWrapper"/> function</returns>
|
||||||
private WrapperFunction GenerateContextWrapper()
|
private WrapperFunction GenerateContextWrapper()
|
||||||
{
|
{
|
||||||
EmitterContext context = new();
|
var context = new EmitterContext();
|
||||||
|
|
||||||
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
|
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
|
||||||
Operand guestMethod = context.LoadArgument(OperandType.I64, 1);
|
Operand guestMethod = context.LoadArgument(OperandType.I64, 1);
|
||||||
@@ -315,11 +310,11 @@ namespace ARMeilleure.Translation
|
|||||||
|
|
||||||
context.Return(returnValue);
|
context.Return(returnValue);
|
||||||
|
|
||||||
ControlFlowGraph cfg = context.GetControlFlowGraph();
|
var cfg = context.GetControlFlowGraph();
|
||||||
OperandType retType = OperandType.I64;
|
var retType = OperandType.I64;
|
||||||
OperandType[] argTypes = new[] { OperandType.I64, OperandType.I64 };
|
var argTypes = new[] { OperandType.I64, OperandType.I64 };
|
||||||
|
|
||||||
return Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<WrapperFunction>(_jitCache);
|
return Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<WrapperFunction>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using ARMeilleure.CodeGen.X86;
|
using ARMeilleure.CodeGen.X86;
|
||||||
using ARMeilleure.IntermediateRepresentation;
|
using ARMeilleure.IntermediateRepresentation;
|
||||||
using ARMeilleure.State;
|
using ARMeilleure.State;
|
||||||
using ARMeilleure.Translation.Cache;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||||
@@ -60,7 +59,7 @@ namespace ARMeilleure.Translation
|
|||||||
return context.VectorExtract(OperandType.I32, vec, 0);
|
return context.VectorExtract(OperandType.I32, vec, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static FpFlagsPInvokeTest GenerateFpFlagsPInvokeTest(JitCache jitCache)
|
public static FpFlagsPInvokeTest GenerateFpFlagsPInvokeTest()
|
||||||
{
|
{
|
||||||
EmitterContext context = new();
|
EmitterContext context = new();
|
||||||
|
|
||||||
@@ -142,7 +141,7 @@ namespace ARMeilleure.Translation
|
|||||||
|
|
||||||
OperandType[] argTypes = [OperandType.I64];
|
OperandType[] argTypes = [OperandType.I64];
|
||||||
|
|
||||||
return Compiler.Compile(cfg, argTypes, OperandType.I32, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<FpFlagsPInvokeTest>(jitCache);
|
return Compiler.Compile(cfg, argTypes, OperandType.I32, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<FpFlagsPInvokeTest>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ namespace Ryujinx.Audio.Backends.Apple
|
|||||||
|
|
||||||
if (result == 0)
|
if (result == 0)
|
||||||
{
|
{
|
||||||
AudioChannelLayout layout = new()
|
AudioChannelLayout layout = new AudioChannelLayout
|
||||||
{
|
{
|
||||||
AudioChannelLayoutTag = kAudioChannelLayoutTag_MPEG_5_1_A,
|
AudioChannelLayoutTag = kAudioChannelLayoutTag_MPEG_5_1_A,
|
||||||
AudioChannelBitmap = 0,
|
AudioChannelBitmap = 0,
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ namespace Ryujinx.Audio.Backends.SDL3
|
|||||||
{
|
{
|
||||||
SDL_AudioSpec desired = GetSDL3Spec(requestedSampleFormat, requestedSampleRate, requestedChannelCount);
|
SDL_AudioSpec desired = GetSDL3Spec(requestedSampleFormat, requestedSampleRate, requestedChannelCount);
|
||||||
SDL_AudioSpec got = desired;
|
SDL_AudioSpec got = desired;
|
||||||
SDL_AudioStreamCallbackPointer pCallback = callback != null ? (SDL_AudioStreamCallbackPointer)Marshal.GetFunctionPointerForDelegate(callback) : null;
|
var pCallback = callback != null ? (SDL_AudioStreamCallbackPointer)Marshal.GetFunctionPointerForDelegate(callback) : null;
|
||||||
|
|
||||||
// From SDL 3 and on, SDL requires us to set this as a hint
|
// From SDL 3 and on, SDL requires us to set this as a hint
|
||||||
SDL_SetHint(SDL_HINT_AUDIO_DEVICE_SAMPLE_FRAMES, $"{sampleCount}");
|
SDL_SetHint(SDL_HINT_AUDIO_DEVICE_SAMPLE_FRAMES, $"{sampleCount}");
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ namespace Ryujinx.Audio.Backends.SoundIo.Native
|
|||||||
get => Marshal.PtrToStringAnsi(GetOutContext().Name);
|
get => Marshal.PtrToStringAnsi(GetOutContext().Name);
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
SoundIoOutStream context = GetOutContext();
|
var context = GetOutContext();
|
||||||
|
|
||||||
if (_nameStored != nint.Zero && context.Name == _nameStored)
|
if (_nameStored != nint.Zero && context.Name == _nameStored)
|
||||||
{
|
{
|
||||||
@@ -129,8 +129,8 @@ namespace Ryujinx.Audio.Backends.SoundIo.Native
|
|||||||
|
|
||||||
unsafe
|
unsafe
|
||||||
{
|
{
|
||||||
int* frameCountPtr = &nativeFrameCount;
|
var frameCountPtr = &nativeFrameCount;
|
||||||
IntPtr* arenasPtr = &arenas;
|
var arenasPtr = &arenas;
|
||||||
CheckError(soundio_outstream_begin_write(_context, (nint)arenasPtr, (nint)frameCountPtr));
|
CheckError(soundio_outstream_begin_write(_context, (nint)arenasPtr, (nint)frameCountPtr));
|
||||||
|
|
||||||
frameCount = *frameCountPtr;
|
frameCount = *frameCountPtr;
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ namespace Ryujinx.Audio.Renderer.Utils
|
|||||||
|
|
||||||
private void UpdateHeader()
|
private void UpdateHeader()
|
||||||
{
|
{
|
||||||
BinaryWriter writer = new(_stream);
|
var writer = new BinaryWriter(_stream);
|
||||||
|
|
||||||
long currentPos = writer.Seek(0, SeekOrigin.Current);
|
long currentPos = writer.Seek(0, SeekOrigin.Current);
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ namespace Ryujinx.Common
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
foreach (T item in _queue.GetConsumingEnumerable(_cts.Token))
|
foreach (var item in _queue.GetConsumingEnumerable(_cts.Token))
|
||||||
{
|
{
|
||||||
_workerAction(item);
|
_workerAction(item);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,10 +16,5 @@ namespace Ryujinx.Common.Configuration.Hid.Controller
|
|||||||
/// Enable Rumble
|
/// Enable Rumble
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool EnableRumble { get; set; }
|
public bool EnableRumble { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Enable HD Rumble support
|
|
||||||
/// </summary
|
|
||||||
public bool UseHDRumble { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
@@ -17,15 +16,15 @@ namespace Ryujinx.Common.Extensions
|
|||||||
/// <param name="fileFullName">The path and name of the file to create and dump to</param>
|
/// <param name="fileFullName">The path and name of the file to create and dump to</param>
|
||||||
public static void DumpToFile(this ref SequenceReader<byte> reader, string fileFullName)
|
public static void DumpToFile(this ref SequenceReader<byte> reader, string fileFullName)
|
||||||
{
|
{
|
||||||
long initialConsumed = reader.Consumed;
|
var initialConsumed = reader.Consumed;
|
||||||
|
|
||||||
reader.Rewind(initialConsumed);
|
reader.Rewind(initialConsumed);
|
||||||
|
|
||||||
using (FileStream fileStream = System.IO.File.Create(fileFullName, 4096, System.IO.FileOptions.None))
|
using (var fileStream = System.IO.File.Create(fileFullName, 4096, System.IO.FileOptions.None))
|
||||||
{
|
{
|
||||||
while (reader.End == false)
|
while (reader.End == false)
|
||||||
{
|
{
|
||||||
ReadOnlySpan<byte> span = reader.CurrentSpan;
|
var span = reader.CurrentSpan;
|
||||||
fileStream.Write(span);
|
fileStream.Write(span);
|
||||||
reader.Advance(span.Length);
|
reader.Advance(span.Length);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ namespace Ryujinx.Common.Logging.Formatters
|
|||||||
|
|
||||||
sb.Append('{');
|
sb.Append('{');
|
||||||
|
|
||||||
foreach (PropertyInfo prop in props)
|
foreach (var prop in props)
|
||||||
{
|
{
|
||||||
sb.Append(prop.Name);
|
sb.Append(prop.Name);
|
||||||
sb.Append(": ");
|
sb.Append(": ");
|
||||||
@@ -52,7 +52,7 @@ namespace Ryujinx.Common.Logging.Formatters
|
|||||||
|
|
||||||
if (array is not null)
|
if (array is not null)
|
||||||
{
|
{
|
||||||
foreach (object? item in array)
|
foreach (var item in array)
|
||||||
{
|
{
|
||||||
sb.Append(item);
|
sb.Append(item);
|
||||||
sb.Append(", ");
|
sb.Append(", ");
|
||||||
|
|||||||
@@ -136,7 +136,11 @@ namespace Ryujinx.Common.Logging
|
|||||||
|
|
||||||
_time = Stopwatch.StartNew();
|
_time = Stopwatch.StartNew();
|
||||||
|
|
||||||
SetConsoleTargetEnabled(true);
|
// Logger should log to console by default
|
||||||
|
AddTarget(new AsyncLogTargetWrapper(
|
||||||
|
new ConsoleLogTarget("console"),
|
||||||
|
1000,
|
||||||
|
AsyncLogTargetOverflowAction.Discard));
|
||||||
|
|
||||||
Notice = new Log(LogLevel.Notice);
|
Notice = new Log(LogLevel.Notice);
|
||||||
|
|
||||||
@@ -169,21 +173,6 @@ namespace Ryujinx.Common.Logging
|
|||||||
Updated += target.Log;
|
Updated += target.Log;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SetConsoleTargetEnabled(bool enabled)
|
|
||||||
{
|
|
||||||
if (enabled)
|
|
||||||
{
|
|
||||||
AddTarget(new AsyncLogTargetWrapper(
|
|
||||||
new ConsoleLogTarget("console"),
|
|
||||||
1000,
|
|
||||||
AsyncLogTargetOverflowAction.Discard));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
RemoveTarget("console");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void RemoveTarget(string target)
|
public static void RemoveTarget(string target)
|
||||||
{
|
{
|
||||||
ILogTarget logTarget = GetTarget(target);
|
ILogTarget logTarget = GetTarget(target);
|
||||||
@@ -215,7 +204,7 @@ namespace Ryujinx.Common.Logging
|
|||||||
|
|
||||||
_stdErrAdapter.Dispose();
|
_stdErrAdapter.Dispose();
|
||||||
|
|
||||||
foreach (ILogTarget target in _logTargets)
|
foreach (var target in _logTargets)
|
||||||
{
|
{
|
||||||
target.Dispose();
|
target.Dispose();
|
||||||
}
|
}
|
||||||
@@ -225,9 +214,9 @@ namespace Ryujinx.Common.Logging
|
|||||||
|
|
||||||
public static IReadOnlyCollection<LogLevel> GetEnabledLevels()
|
public static IReadOnlyCollection<LogLevel> GetEnabledLevels()
|
||||||
{
|
{
|
||||||
Log?[] logs = new[] { Debug, Info, Warning, Error, Guest, AccessLog, Stub, Trace };
|
var logs = new[] { Debug, Info, Warning, Error, Guest, AccessLog, Stub, Trace };
|
||||||
List<LogLevel> levels = new(logs.Length);
|
List<LogLevel> levels = new(logs.Length);
|
||||||
foreach (Log? log in logs)
|
foreach (var log in logs)
|
||||||
{
|
{
|
||||||
if (log.HasValue)
|
if (log.HasValue)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ namespace Ryujinx.Common.Logging.Targets
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
using ManualResetEventSlim signal = new(false);
|
using var signal = new ManualResetEventSlim(false);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_messageQueue.Add(new FlushEventArgs(signal));
|
_messageQueue.Add(new FlushEventArgs(signal));
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ namespace Ryujinx.Common.Logging.Targets
|
|||||||
|
|
||||||
public void Log(object sender, LogEventArgs e)
|
public void Log(object sender, LogEventArgs e)
|
||||||
{
|
{
|
||||||
LogEventArgsJson logEventArgsJson = LogEventArgsJson.FromLogEventArgs(e);
|
var logEventArgsJson = LogEventArgsJson.FromLogEventArgs(e);
|
||||||
JsonHelper.SerializeToStream(_stream, logEventArgsJson, LogEventJsonSerializerContext.Default.LogEventArgsJson);
|
JsonHelper.SerializeToStream(_stream, logEventArgsJson, LogEventJsonSerializerContext.Default.LogEventArgsJson);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,21 +19,21 @@ namespace Ryujinx.Common
|
|||||||
|
|
||||||
public static byte[] Read(string filename)
|
public static byte[] Read(string filename)
|
||||||
{
|
{
|
||||||
(Assembly assembly, string path) = ResolveManifestPath(filename);
|
var (assembly, path) = ResolveManifestPath(filename);
|
||||||
|
|
||||||
return Read(assembly, path);
|
return Read(assembly, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Task<byte[]> ReadAsync(string filename)
|
public static Task<byte[]> ReadAsync(string filename)
|
||||||
{
|
{
|
||||||
(Assembly assembly, string path) = ResolveManifestPath(filename);
|
var (assembly, path) = ResolveManifestPath(filename);
|
||||||
|
|
||||||
return ReadAsync(assembly, path);
|
return ReadAsync(assembly, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] Read(Assembly assembly, string filename)
|
public static byte[] Read(Assembly assembly, string filename)
|
||||||
{
|
{
|
||||||
using Stream stream = GetStream(assembly, filename);
|
using var stream = GetStream(assembly, filename);
|
||||||
if (stream == null)
|
if (stream == null)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
@@ -44,14 +44,14 @@ namespace Ryujinx.Common
|
|||||||
|
|
||||||
public static MemoryOwner<byte> ReadFileToRentedMemory(string filename)
|
public static MemoryOwner<byte> ReadFileToRentedMemory(string filename)
|
||||||
{
|
{
|
||||||
(Assembly assembly, string path) = ResolveManifestPath(filename);
|
var (assembly, path) = ResolveManifestPath(filename);
|
||||||
|
|
||||||
return ReadFileToRentedMemory(assembly, path);
|
return ReadFileToRentedMemory(assembly, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MemoryOwner<byte> ReadFileToRentedMemory(Assembly assembly, string filename)
|
public static MemoryOwner<byte> ReadFileToRentedMemory(Assembly assembly, string filename)
|
||||||
{
|
{
|
||||||
using Stream stream = GetStream(assembly, filename);
|
using var stream = GetStream(assembly, filename);
|
||||||
|
|
||||||
return stream is null
|
return stream is null
|
||||||
? null
|
? null
|
||||||
@@ -60,7 +60,7 @@ namespace Ryujinx.Common
|
|||||||
|
|
||||||
public async static Task<byte[]> ReadAsync(Assembly assembly, string filename)
|
public async static Task<byte[]> ReadAsync(Assembly assembly, string filename)
|
||||||
{
|
{
|
||||||
using Stream stream = GetStream(assembly, filename);
|
using var stream = GetStream(assembly, filename);
|
||||||
if (stream == null)
|
if (stream == null)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
@@ -71,78 +71,73 @@ namespace Ryujinx.Common
|
|||||||
|
|
||||||
public static string ReadAllText(string filename)
|
public static string ReadAllText(string filename)
|
||||||
{
|
{
|
||||||
(Assembly assembly, string path) = ResolveManifestPath(filename);
|
var (assembly, path) = ResolveManifestPath(filename);
|
||||||
|
|
||||||
return ReadAllText(assembly, path);
|
return ReadAllText(assembly, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Task<string> ReadAllTextAsync(string filename)
|
public static Task<string> ReadAllTextAsync(string filename)
|
||||||
{
|
{
|
||||||
(Assembly assembly, string path) = ResolveManifestPath(filename);
|
var (assembly, path) = ResolveManifestPath(filename);
|
||||||
|
|
||||||
return ReadAllTextAsync(assembly, path);
|
return ReadAllTextAsync(assembly, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string ReadAllText(Assembly assembly, string filename)
|
public static string ReadAllText(Assembly assembly, string filename)
|
||||||
{
|
{
|
||||||
using Stream stream = GetStream(assembly, filename);
|
using var stream = GetStream(assembly, filename);
|
||||||
if (stream == null)
|
if (stream == null)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
using StreamReader reader = new(stream);
|
using var reader = new StreamReader(stream);
|
||||||
return reader.ReadToEnd();
|
return reader.ReadToEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async static Task<string> ReadAllTextAsync(Assembly assembly, string filename)
|
public async static Task<string> ReadAllTextAsync(Assembly assembly, string filename)
|
||||||
{
|
{
|
||||||
using Stream stream = GetStream(assembly, filename);
|
using var stream = GetStream(assembly, filename);
|
||||||
if (stream == null)
|
if (stream == null)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
using StreamReader reader = new(stream);
|
using var reader = new StreamReader(stream);
|
||||||
return await reader.ReadToEndAsync();
|
return await reader.ReadToEndAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Stream GetStream(string filename)
|
public static Stream GetStream(string filename)
|
||||||
{
|
{
|
||||||
(Assembly assembly, string path) = ResolveManifestPath(filename);
|
var (assembly, path) = ResolveManifestPath(filename);
|
||||||
|
|
||||||
return GetStream(assembly, path);
|
return GetStream(assembly, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Stream GetStream(Assembly assembly, string filename)
|
public static Stream GetStream(Assembly assembly, string filename)
|
||||||
{
|
{
|
||||||
string @namespace = assembly.GetName().Name;
|
var @namespace = assembly.GetName().Name;
|
||||||
string manifestUri = @namespace + "." + filename.Replace('/', '.');
|
var manifestUri = @namespace + "." + filename.Replace('/', '.');
|
||||||
|
|
||||||
Stream stream = assembly.GetManifestResourceStream(manifestUri);
|
var stream = assembly.GetManifestResourceStream(manifestUri);
|
||||||
|
|
||||||
return stream;
|
return stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string[] GetAllAvailableResources(string path, string ext = "")
|
public static string[] GetAllAvailableResources(string path, string ext = "")
|
||||||
{
|
{
|
||||||
(Assembly assembly, string resourcePath) = ResolveManifestPath(path);
|
return ResolveManifestPath(path).Item1.GetManifestResourceNames()
|
||||||
|
.Where(r => r.EndsWith(ext))
|
||||||
string manifestPath = assembly.GetName().Name + "." + resourcePath.Replace('/', '.');
|
|
||||||
|
|
||||||
return assembly.GetManifestResourceNames()
|
|
||||||
.Where(r => r.StartsWith(manifestPath + ".", StringComparison.Ordinal))
|
|
||||||
.Where(r => r.EndsWith(ext, StringComparison.Ordinal))
|
|
||||||
.ToArray();
|
.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static (Assembly, string) ResolveManifestPath(string filename)
|
private static (Assembly, string) ResolveManifestPath(string filename)
|
||||||
{
|
{
|
||||||
string[] segments = filename.Split('/', 2, StringSplitOptions.RemoveEmptyEntries);
|
var segments = filename.Split('/', 2, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
|
||||||
if (segments.Length >= 2)
|
if (segments.Length >= 2)
|
||||||
{
|
{
|
||||||
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
|
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
|
||||||
{
|
{
|
||||||
if (assembly.GetName().Name == segments[0])
|
if (assembly.GetName().Name == segments[0])
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ namespace Ryujinx.Common.Utilities
|
|||||||
public static void CopyDirectory(string sourceDir, string destinationDir, bool recursive)
|
public static void CopyDirectory(string sourceDir, string destinationDir, bool recursive)
|
||||||
{
|
{
|
||||||
// Get information about the source directory
|
// Get information about the source directory
|
||||||
DirectoryInfo dir = new(sourceDir);
|
var dir = new DirectoryInfo(sourceDir);
|
||||||
|
|
||||||
// Check if the source directory exists
|
// Check if the source directory exists
|
||||||
if (!dir.Exists)
|
if (!dir.Exists)
|
||||||
@@ -49,7 +49,7 @@ namespace Ryujinx.Common.Utilities
|
|||||||
|
|
||||||
public static string SanitizeFileName(string fileName)
|
public static string SanitizeFileName(string fileName)
|
||||||
{
|
{
|
||||||
HashSet<char> reservedChars = new(Path.GetInvalidFileNameChars());
|
var reservedChars = new HashSet<char>(Path.GetInvalidFileNameChars());
|
||||||
return string.Concat(fileName.Select(c => reservedChars.Contains(c) ? '_' : c));
|
return string.Concat(fileName.Select(c => reservedChars.Contains(c) ? '_' : c));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using MsgPack;
|
using MsgPack;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Utilities
|
namespace Ryujinx.Common.Utilities
|
||||||
@@ -19,7 +18,7 @@ namespace Ryujinx.Common.Utilities
|
|||||||
|
|
||||||
public static string Format(MessagePackObject obj)
|
public static string Format(MessagePackObject obj)
|
||||||
{
|
{
|
||||||
IndentedStringBuilder builder = new();
|
var builder = new IndentedStringBuilder();
|
||||||
|
|
||||||
FormatMsgPackObj(obj, builder);
|
FormatMsgPackObj(obj, builder);
|
||||||
|
|
||||||
@@ -42,7 +41,7 @@ namespace Ryujinx.Common.Utilities
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
object literal = obj.ToObject();
|
var literal = obj.ToObject();
|
||||||
|
|
||||||
if (literal is String)
|
if (literal is String)
|
||||||
{
|
{
|
||||||
@@ -89,7 +88,7 @@ namespace Ryujinx.Common.Utilities
|
|||||||
{
|
{
|
||||||
builder.Append("[ ");
|
builder.Append("[ ");
|
||||||
|
|
||||||
foreach (byte b in arr)
|
foreach (var b in arr)
|
||||||
{
|
{
|
||||||
builder.Append("0x");
|
builder.Append("0x");
|
||||||
builder.Append(ToHexChar(b >> 4));
|
builder.Append(ToHexChar(b >> 4));
|
||||||
@@ -112,7 +111,7 @@ namespace Ryujinx.Common.Utilities
|
|||||||
builder.Append("0x");
|
builder.Append("0x");
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (byte b in arr)
|
foreach (var b in arr)
|
||||||
{
|
{
|
||||||
builder.Append(ToHexChar(b >> 4));
|
builder.Append(ToHexChar(b >> 4));
|
||||||
builder.Append(ToHexChar(b & 0xF));
|
builder.Append(ToHexChar(b & 0xF));
|
||||||
@@ -123,7 +122,7 @@ namespace Ryujinx.Common.Utilities
|
|||||||
|
|
||||||
private static void FormatMsgPackMap(MessagePackObject obj, IndentedStringBuilder builder)
|
private static void FormatMsgPackMap(MessagePackObject obj, IndentedStringBuilder builder)
|
||||||
{
|
{
|
||||||
MessagePackObjectDictionary map = obj.AsDictionary();
|
var map = obj.AsDictionary();
|
||||||
|
|
||||||
builder.Append('{');
|
builder.Append('{');
|
||||||
|
|
||||||
@@ -131,7 +130,7 @@ namespace Ryujinx.Common.Utilities
|
|||||||
builder.IncreaseIndent()
|
builder.IncreaseIndent()
|
||||||
.AppendLine();
|
.AppendLine();
|
||||||
|
|
||||||
foreach (KeyValuePair<MessagePackObject, MessagePackObject> item in map)
|
foreach (var item in map)
|
||||||
{
|
{
|
||||||
FormatMsgPackObj(item.Key, builder);
|
FormatMsgPackObj(item.Key, builder);
|
||||||
|
|
||||||
@@ -155,11 +154,11 @@ namespace Ryujinx.Common.Utilities
|
|||||||
|
|
||||||
private static void FormatMsgPackArray(MessagePackObject obj, IndentedStringBuilder builder)
|
private static void FormatMsgPackArray(MessagePackObject obj, IndentedStringBuilder builder)
|
||||||
{
|
{
|
||||||
IList<MessagePackObject> arr = obj.AsList();
|
var arr = obj.AsList();
|
||||||
|
|
||||||
builder.Append("[ ");
|
builder.Append("[ ");
|
||||||
|
|
||||||
foreach (MessagePackObject item in arr)
|
foreach (var item in arr)
|
||||||
{
|
{
|
||||||
FormatMsgPackObj(item, builder);
|
FormatMsgPackObj(item, builder);
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using Microsoft.IO;
|
using Microsoft.IO;
|
||||||
using Ryujinx.Common.Memory;
|
using Ryujinx.Common.Memory;
|
||||||
using System;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@@ -28,7 +27,7 @@ namespace Ryujinx.Common.Utilities
|
|||||||
|
|
||||||
MemoryOwner<byte> ownedMemory = MemoryOwner<byte>.Rent(checked((int)bytesExpected));
|
MemoryOwner<byte> ownedMemory = MemoryOwner<byte>.Rent(checked((int)bytesExpected));
|
||||||
|
|
||||||
Span<byte> destSpan = ownedMemory.Span;
|
var destSpan = ownedMemory.Span;
|
||||||
|
|
||||||
int totalBytesRead = 0;
|
int totalBytesRead = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ namespace Ryujinx.Common.Utilities
|
|||||||
{
|
{
|
||||||
internal static TimeSpan Measure(Action action)
|
internal static TimeSpan Measure(Action action)
|
||||||
{
|
{
|
||||||
Stopwatch sw = new();
|
var sw = new Stopwatch();
|
||||||
sw.Start();
|
sw.Start();
|
||||||
|
|
||||||
try
|
try
|
||||||
@@ -66,7 +66,7 @@ namespace Ryujinx.Common.Utilities
|
|||||||
{
|
{
|
||||||
if (Path.GetExtension(filename).Equals(".XCI", StringComparison.InvariantCultureIgnoreCase))
|
if (Path.GetExtension(filename).Equals(".XCI", StringComparison.InvariantCultureIgnoreCase))
|
||||||
{
|
{
|
||||||
XCIFileTrimmer trimmer = new(filename, log);
|
var trimmer = new XCIFileTrimmer(filename, log);
|
||||||
return trimmer.CanBeTrimmed;
|
return trimmer.CanBeTrimmed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,7 +77,7 @@ namespace Ryujinx.Common.Utilities
|
|||||||
{
|
{
|
||||||
if (Path.GetExtension(filename).Equals(".XCI", StringComparison.InvariantCultureIgnoreCase))
|
if (Path.GetExtension(filename).Equals(".XCI", StringComparison.InvariantCultureIgnoreCase))
|
||||||
{
|
{
|
||||||
XCIFileTrimmer trimmer = new(filename, log);
|
var trimmer = new XCIFileTrimmer(filename, log);
|
||||||
return trimmer.CanBeUntrimmed;
|
return trimmer.CanBeUntrimmed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -221,7 +221,7 @@ namespace Ryujinx.Common.Utilities
|
|||||||
{
|
{
|
||||||
long maxReads = readSizeB / BufferSize;
|
long maxReads = readSizeB / BufferSize;
|
||||||
long read = 0;
|
long read = 0;
|
||||||
byte[] buffer = new byte[BufferSize];
|
var buffer = new byte[BufferSize];
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
@@ -287,7 +287,7 @@ namespace Ryujinx.Common.Utilities
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
FileInfo info = new(Filename);
|
var info = new FileInfo(Filename);
|
||||||
if ((info.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
|
if ((info.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -308,7 +308,7 @@ namespace Ryujinx.Common.Utilities
|
|||||||
return OperationOutcome.FileSizeChanged;
|
return OperationOutcome.FileSizeChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
FileStream outfileStream = new(_filename, FileMode.Open, FileAccess.Write, FileShare.Write);
|
var outfileStream = new FileStream(_filename, FileMode.Open, FileAccess.Write, FileShare.Write);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -347,7 +347,7 @@ namespace Ryujinx.Common.Utilities
|
|||||||
{
|
{
|
||||||
Log?.Write(LogType.Info, "Untrimming...");
|
Log?.Write(LogType.Info, "Untrimming...");
|
||||||
|
|
||||||
FileInfo info = new(Filename);
|
var info = new FileInfo(Filename);
|
||||||
if ((info.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
|
if ((info.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -368,7 +368,7 @@ namespace Ryujinx.Common.Utilities
|
|||||||
return OperationOutcome.FileSizeChanged;
|
return OperationOutcome.FileSizeChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
FileStream outfileStream = new(_filename, FileMode.Append, FileAccess.Write, FileShare.Write);
|
var outfileStream = new FileStream(_filename, FileMode.Append, FileAccess.Write, FileShare.Write);
|
||||||
long bytesToWriteB = UntrimmedFileSizeB - FileSizeB;
|
long bytesToWriteB = UntrimmedFileSizeB - FileSizeB;
|
||||||
|
|
||||||
try
|
try
|
||||||
@@ -413,7 +413,7 @@ namespace Ryujinx.Common.Utilities
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
byte[] buffer = new byte[BufferSize];
|
var buffer = new byte[BufferSize];
|
||||||
Array.Fill<byte>(buffer, PaddingByte);
|
Array.Fill<byte>(buffer, PaddingByte);
|
||||||
|
|
||||||
while (bytesLeftToWriteB > 0)
|
while (bytesLeftToWriteB > 0)
|
||||||
|
|||||||
+295
-34
@@ -1,9 +1,15 @@
|
|||||||
using ARMeilleure.Common;
|
using ARMeilleure.Memory;
|
||||||
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.Cpu.Signal;
|
||||||
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Threading;
|
||||||
|
using static Ryujinx.Cpu.MemoryEhMeilleure;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu
|
namespace ARMeilleure.Common
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a table of guest address to a value.
|
/// Represents a table of guest address to a value.
|
||||||
@@ -11,12 +17,84 @@ namespace Ryujinx.Cpu
|
|||||||
/// <typeparam name="TEntry">Type of the value</typeparam>
|
/// <typeparam name="TEntry">Type of the value</typeparam>
|
||||||
public unsafe class AddressTable<TEntry> : IAddressTable<TEntry> where TEntry : unmanaged
|
public unsafe class AddressTable<TEntry> : IAddressTable<TEntry> where TEntry : unmanaged
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a page of the address table.
|
||||||
|
/// </summary>
|
||||||
|
private readonly struct AddressTablePage
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// True if the allocation belongs to a sparse block, false otherwise.
|
||||||
|
/// </summary>
|
||||||
|
public readonly bool IsSparse;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Base address for the page.
|
||||||
|
/// </summary>
|
||||||
|
public readonly nint Address;
|
||||||
|
|
||||||
|
public AddressTablePage(bool isSparse, nint address)
|
||||||
|
{
|
||||||
|
IsSparse = isSparse;
|
||||||
|
Address = address;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A sparsely mapped block of memory with a signal handler to map pages as they're accessed.
|
||||||
|
/// </summary>
|
||||||
|
private readonly struct TableSparseBlock : IDisposable
|
||||||
|
{
|
||||||
|
public readonly SparseMemoryBlock Block;
|
||||||
|
private readonly TrackingEventDelegate _trackingEvent;
|
||||||
|
|
||||||
|
public TableSparseBlock(ulong size, Action<nint> ensureMapped, PageInitDelegate pageInit)
|
||||||
|
{
|
||||||
|
var block = new SparseMemoryBlock(size, pageInit, null);
|
||||||
|
|
||||||
|
_trackingEvent = (ulong address, ulong _, bool _) =>
|
||||||
|
{
|
||||||
|
ulong pointer = (ulong)block.Block.Pointer + address;
|
||||||
|
ensureMapped((nint)pointer);
|
||||||
|
return pointer;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool added = NativeSignalHandler.AddTrackedRegion(
|
||||||
|
(nuint)block.Block.Pointer,
|
||||||
|
(nuint)(block.Block.Pointer + (nint)block.Block.Size),
|
||||||
|
Marshal.GetFunctionPointerForDelegate(_trackingEvent));
|
||||||
|
|
||||||
|
if (!added)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Number of allowed tracked regions exceeded.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Block = block;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
NativeSignalHandler.RemoveTrackedRegion((nuint)Block.Block.Pointer);
|
||||||
|
|
||||||
|
Block.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
private TEntry** _table;
|
private TEntry** _table;
|
||||||
private readonly List<nint> _pages;
|
private readonly List<AddressTablePage> _pages;
|
||||||
|
private TEntry _fill;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
private readonly MemoryBlock _sparseFill;
|
||||||
public AddressTableType TableType => AddressTableType.Default;
|
private readonly SparseMemoryBlock _fillBottomLevel;
|
||||||
|
private readonly TEntry* _fillBottomLevelPtr;
|
||||||
|
|
||||||
|
private readonly List<TableSparseBlock> _sparseReserved;
|
||||||
|
private readonly ReaderWriterLockSlim _sparseLock;
|
||||||
|
|
||||||
|
private ulong _sparseBlockSize;
|
||||||
|
private ulong _sparseReservedOffset;
|
||||||
|
|
||||||
|
public bool Sparse { get; }
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public ulong Mask { get; }
|
public ulong Mask { get; }
|
||||||
@@ -25,7 +103,17 @@ namespace Ryujinx.Cpu
|
|||||||
public AddressTableLevel[] Levels { get; }
|
public AddressTableLevel[] Levels { get; }
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public TEntry Fill { get; set; }
|
public TEntry Fill
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _fill;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
UpdateFill(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public nint Base
|
public nint Base
|
||||||
@@ -43,24 +131,44 @@ namespace Ryujinx.Cpu
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Constructs a new instance of the <see cref="AddressTable{TEntry}"/> class with the specified list of
|
/// Constructs a new instance of the <see cref="AddressTable{TEntry}"/> class with the specified list of
|
||||||
/// <see cref="AddressTableLevel"/>.
|
/// <see cref="Level"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="levels">Levels for the address table</param>
|
/// <param name="levels">Levels for the address table</param>
|
||||||
|
/// <param name="sparse">True if the bottom page should be sparsely mapped</param>
|
||||||
/// <exception cref="ArgumentNullException"><paramref name="levels"/> is null</exception>
|
/// <exception cref="ArgumentNullException"><paramref name="levels"/> is null</exception>
|
||||||
/// <exception cref="ArgumentException">Length of <paramref name="levels"/> is less than 2</exception>
|
/// <exception cref="ArgumentException">Length of <paramref name="levels"/> is less than 2</exception>
|
||||||
public AddressTable(AddressTableLevel[] levels)
|
public AddressTable(AddressTableLevel[] levels, bool sparse)
|
||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(levels);
|
ArgumentNullException.ThrowIfNull(levels);
|
||||||
|
|
||||||
_pages = new List<nint>(capacity: 16);
|
_pages = new List<AddressTablePage>(capacity: 16);
|
||||||
|
|
||||||
Levels = levels;
|
Levels = levels;
|
||||||
Mask = 0;
|
Mask = 0;
|
||||||
|
|
||||||
foreach (AddressTableLevel level in Levels)
|
foreach (var level in Levels)
|
||||||
{
|
{
|
||||||
Mask |= level.Mask;
|
Mask |= level.Mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Sparse = sparse;
|
||||||
|
|
||||||
|
if (sparse)
|
||||||
|
{
|
||||||
|
// If the address table is sparse, allocate a fill block
|
||||||
|
|
||||||
|
_sparseFill = new MemoryBlock(268435456ul, MemoryAllocationFlags.Mirrorable); //low Power TC uses size: 65536ul
|
||||||
|
|
||||||
|
ulong bottomLevelSize = (1ul << levels.Last().Length) * (ulong)sizeof(TEntry);
|
||||||
|
|
||||||
|
_fillBottomLevel = new SparseMemoryBlock(bottomLevelSize, null, _sparseFill);
|
||||||
|
_fillBottomLevelPtr = (TEntry*)_fillBottomLevel.Block.Pointer;
|
||||||
|
|
||||||
|
_sparseReserved = [];
|
||||||
|
_sparseLock = new ReaderWriterLockSlim();
|
||||||
|
|
||||||
|
_sparseBlockSize = bottomLevelSize;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -68,10 +176,29 @@ namespace Ryujinx.Cpu
|
|||||||
/// Selects the best table structure for A32/A64, taking into account the selected memory manager type.
|
/// Selects the best table structure for A32/A64, taking into account the selected memory manager type.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="for64Bits">True if the guest is A64, false otherwise</param>
|
/// <param name="for64Bits">True if the guest is A64, false otherwise</param>
|
||||||
|
/// <param name="type">Memory manager type</param>
|
||||||
/// <returns>An <see cref="AddressTable{TEntry}"/> for ARM function lookup</returns>
|
/// <returns>An <see cref="AddressTable{TEntry}"/> for ARM function lookup</returns>
|
||||||
public static AddressTable<TEntry> CreateForArm(bool for64Bits)
|
public static AddressTable<TEntry> CreateForArm(bool for64Bits, MemoryManagerType type)
|
||||||
{
|
{
|
||||||
return new AddressTable<TEntry>(AddressTablePresets.GetArmPreset(for64Bits, false));
|
// Assume software memory means that we don't want to use any signal handlers.
|
||||||
|
bool sparse = type != MemoryManagerType.SoftwareMmu && type != MemoryManagerType.SoftwarePageTable;
|
||||||
|
|
||||||
|
return new AddressTable<TEntry>(AddressTablePresets.GetArmPreset(for64Bits, sparse), sparse);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Update the fill value for the bottom level of the table.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="fillValue">New fill value</param>
|
||||||
|
private void UpdateFill(TEntry fillValue)
|
||||||
|
{
|
||||||
|
if (_sparseFill != null)
|
||||||
|
{
|
||||||
|
Span<byte> span = _sparseFill.GetSpan(0, (int)_sparseFill.Size);
|
||||||
|
MemoryMarshal.Cast<byte, TEntry>(span).Fill(fillValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
_fill = fillValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -79,7 +206,17 @@ namespace Ryujinx.Cpu
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="address"></param>
|
/// <param name="address"></param>
|
||||||
/// <param name="size"></param>
|
/// <param name="size"></param>
|
||||||
public void SignalCodeRange(ulong address, ulong size) { }
|
public void SignalCodeRange(ulong address, ulong size)
|
||||||
|
{
|
||||||
|
AddressTableLevel bottom = Levels.Last();
|
||||||
|
ulong bottomLevelEntries = 1ul << bottom.Length;
|
||||||
|
|
||||||
|
ulong entryIndex = address >> bottom.Index;
|
||||||
|
ulong entries = size >> bottom.Index;
|
||||||
|
entries += entryIndex - BitUtils.AlignDown(entryIndex, bottomLevelEntries);
|
||||||
|
|
||||||
|
_sparseBlockSize = Math.Max(_sparseBlockSize, BitUtils.AlignUp(entries, bottomLevelEntries) * (ulong)sizeof(TEntry));
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public bool IsValid(ulong address)
|
public bool IsValid(ulong address)
|
||||||
@@ -103,6 +240,8 @@ namespace Ryujinx.Cpu
|
|||||||
|
|
||||||
long index = Levels[^1].GetValue(address);
|
long index = Levels[^1].GetValue(address);
|
||||||
|
|
||||||
|
EnsureMapped((nint)(page + index));
|
||||||
|
|
||||||
return ref page[index];
|
return ref page[index];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -121,19 +260,19 @@ namespace Ryujinx.Cpu
|
|||||||
ref AddressTableLevel level = ref Levels[i];
|
ref AddressTableLevel level = ref Levels[i];
|
||||||
if (page != null)
|
if (page != null)
|
||||||
{
|
{
|
||||||
ref TEntry* nextPage = ref page![level.GetValue(address)];
|
ref TEntry* nextPage = ref page[level.GetValue(address)];
|
||||||
|
|
||||||
if (nextPage == null)
|
if (nextPage == null || nextPage == _fillBottomLevelPtr)
|
||||||
{
|
{
|
||||||
ref AddressTableLevel nextLevel = ref Levels[i + 1];
|
ref AddressTableLevel nextLevel = ref Levels[i + 1];
|
||||||
|
|
||||||
if (i == Levels.Length - 2)
|
if (i == Levels.Length - 2)
|
||||||
{
|
{
|
||||||
nextPage = (TEntry*)Allocate(1 << nextLevel.Length, Fill);
|
nextPage = (TEntry*)Allocate(1 << nextLevel.Length, Fill, leaf: true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
nextPage = (TEntry*)Allocate(1 << nextLevel.Length, nint.Zero);
|
nextPage = (TEntry*)Allocate(1 << nextLevel.Length, GetFillValue(i), leaf: false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,6 +283,57 @@ namespace Ryujinx.Cpu
|
|||||||
return (TEntry*)page;
|
return (TEntry*)page;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ensure the given pointer is mapped in any overlapping sparse reservations.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ptr">Pointer to be mapped</param>
|
||||||
|
private void EnsureMapped(nint ptr)
|
||||||
|
{
|
||||||
|
if (Sparse)
|
||||||
|
{
|
||||||
|
// Check sparse allocations to see if the pointer is in any of them.
|
||||||
|
// Ensure the page is committed if there's a match.
|
||||||
|
|
||||||
|
_sparseLock.EnterReadLock();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
foreach (TableSparseBlock reserved in _sparseReserved)
|
||||||
|
{
|
||||||
|
SparseMemoryBlock sparse = reserved.Block;
|
||||||
|
|
||||||
|
if (ptr >= sparse.Block.Pointer && ptr < sparse.Block.Pointer + (nint)sparse.Block.Size)
|
||||||
|
{
|
||||||
|
sparse.EnsureMapped((ulong)(ptr - sparse.Block.Pointer));
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_sparseLock.ExitReadLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the fill value for a non-leaf level of the table.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="level">Level to get the fill value for</param>
|
||||||
|
/// <returns>The fill value</returns>
|
||||||
|
private nint GetFillValue(int level)
|
||||||
|
{
|
||||||
|
if (_fillBottomLevel != null && level == Levels.Length - 2)
|
||||||
|
{
|
||||||
|
return (nint)_fillBottomLevelPtr;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return nint.Zero;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Lazily initialize and get the root page of the <see cref="AddressTable{TEntry}"/>.
|
/// Lazily initialize and get the root page of the <see cref="AddressTable{TEntry}"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -153,35 +343,91 @@ namespace Ryujinx.Cpu
|
|||||||
if (_table == null)
|
if (_table == null)
|
||||||
{
|
{
|
||||||
if (Levels.Length == 1)
|
if (Levels.Length == 1)
|
||||||
_table = (TEntry**)Allocate(1 << Levels[0].Length, Fill);
|
_table = (TEntry**)Allocate(1 << Levels[0].Length, Fill, leaf: true);
|
||||||
else
|
else
|
||||||
_table = (TEntry**)Allocate(1 << Levels[0].Length, nint.Zero);
|
_table = (TEntry**)Allocate(1 << Levels[0].Length, GetFillValue(0), leaf: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return _table;
|
return _table;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initialize a leaf page with the fill value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="page">Page to initialize</param>
|
||||||
|
private void InitLeafPage(Span<byte> page)
|
||||||
|
{
|
||||||
|
MemoryMarshal.Cast<byte, TEntry>(page).Fill(_fill);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reserve a new sparse block, and add it to the list.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The new sparse block that was added</returns>
|
||||||
|
private TableSparseBlock ReserveNewSparseBlock()
|
||||||
|
{
|
||||||
|
var block = new TableSparseBlock(_sparseBlockSize, EnsureMapped, InitLeafPage);
|
||||||
|
|
||||||
|
_sparseReserved.Add(block);
|
||||||
|
_sparseReservedOffset = 0;
|
||||||
|
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Allocates a block of memory of the specified type and length.
|
/// Allocates a block of memory of the specified type and length.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">Type of elements</typeparam>
|
/// <typeparam name="T">Type of elements</typeparam>
|
||||||
/// <param name="length">Number of elements</param>
|
/// <param name="length">Number of elements</param>
|
||||||
/// <param name="fill">Fill value</param>
|
/// <param name="fill">Fill value</param>
|
||||||
|
/// <param name="leaf"><see langword="true"/> if leaf; otherwise <see langword="false"/></param>
|
||||||
/// <returns>Allocated block</returns>
|
/// <returns>Allocated block</returns>
|
||||||
private nint Allocate<T>(int length, T fill) where T : unmanaged
|
private nint Allocate<T>(int length, T fill, bool leaf) where T : unmanaged
|
||||||
{
|
{
|
||||||
int size = sizeof(T) * length;
|
var size = sizeof(T) * length;
|
||||||
|
|
||||||
nint address = (nint)NativeAllocator.Instance.Allocate((uint)size);
|
AddressTablePage page;
|
||||||
|
|
||||||
Span<T> span = new((void*)address, length);
|
if (Sparse && leaf)
|
||||||
span.Fill(fill);
|
{
|
||||||
|
_sparseLock.EnterWriteLock();
|
||||||
|
|
||||||
_pages.Add(address);
|
SparseMemoryBlock block;
|
||||||
|
|
||||||
|
if (_sparseReserved.Count == 0)
|
||||||
|
{
|
||||||
|
block = ReserveNewSparseBlock().Block;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
block = _sparseReserved.Last().Block;
|
||||||
|
|
||||||
|
if (_sparseReservedOffset == block.Block.Size)
|
||||||
|
{
|
||||||
|
block = ReserveNewSparseBlock().Block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
page = new AddressTablePage(true, block.Block.Pointer + (nint)_sparseReservedOffset);
|
||||||
|
|
||||||
|
_sparseReservedOffset += (ulong)size;
|
||||||
|
|
||||||
|
_sparseLock.ExitWriteLock();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var address = (nint)NativeAllocator.Instance.Allocate((uint)size);
|
||||||
|
page = new AddressTablePage(false, address);
|
||||||
|
|
||||||
|
var span = new Span<T>((void*)page.Address, length);
|
||||||
|
span.Fill(fill);
|
||||||
|
}
|
||||||
|
|
||||||
|
_pages.Add(page);
|
||||||
|
|
||||||
//TranslatorEventSource.Log.AddressTableAllocated(size, leaf);
|
//TranslatorEventSource.Log.AddressTableAllocated(size, leaf);
|
||||||
|
|
||||||
return address;
|
return page.Address;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -200,17 +446,32 @@ namespace Ryujinx.Cpu
|
|||||||
/// <param name="disposing"><see langword="true"/> to dispose managed resources also; otherwise just unmanaged resouces</param>
|
/// <param name="disposing"><see langword="true"/> to dispose managed resources also; otherwise just unmanaged resouces</param>
|
||||||
protected virtual void Dispose(bool disposing)
|
protected virtual void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
if (_disposed)
|
if (!_disposed)
|
||||||
{
|
{
|
||||||
return;
|
foreach (var page in _pages)
|
||||||
}
|
{
|
||||||
|
if (!page.IsSparse)
|
||||||
|
{
|
||||||
|
Marshal.FreeHGlobal(page.Address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
foreach (nint page in _pages)
|
if (Sparse)
|
||||||
{
|
{
|
||||||
Marshal.FreeHGlobal(page);
|
foreach (TableSparseBlock block in _sparseReserved)
|
||||||
}
|
{
|
||||||
|
block.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
_disposed = true;
|
_sparseReserved.Clear();
|
||||||
|
|
||||||
|
_fillBottomLevel.Dispose();
|
||||||
|
_sparseFill.Dispose();
|
||||||
|
_sparseLock.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
_disposed = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
|
|
||||||
public HvAddressSpace(MemoryBlock backingMemory, ulong asSize)
|
public HvAddressSpace(MemoryBlock backingMemory, ulong asSize)
|
||||||
{
|
{
|
||||||
(_asBase, HvIpaAllocator ipaAllocator) = HvVm.CreateAddressSpace(backingMemory);
|
(_asBase, var ipaAllocator) = HvVm.CreateAddressSpace(backingMemory);
|
||||||
_backingSize = backingMemory.Size;
|
_backingSize = backingMemory.Size;
|
||||||
|
|
||||||
_userRange = new HvAddressSpaceRange(ipaAllocator);
|
_userRange = new HvAddressSpaceRange(ipaAllocator);
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using ARMeilleure.Memory;
|
using ARMeilleure.Memory;
|
||||||
using ARMeilleure.Translation.PTC;
|
|
||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.AppleHv
|
namespace Ryujinx.Cpu.AppleHv
|
||||||
@@ -33,7 +32,7 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public IDiskCacheLoadState LoadDiskCache(PtcCacheInfo cacheInfo, bool enabled)
|
public IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled, string cacheSelector)
|
||||||
{
|
{
|
||||||
return new DummyDiskCacheLoadState();
|
return new DummyDiskCacheLoadState();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using ARMeilleure.State;
|
using ARMeilleure.State;
|
||||||
using Ryujinx.Common.Logging;
|
|
||||||
using Ryujinx.Cpu.AppleHv.Arm;
|
using Ryujinx.Cpu.AppleHv.Arm;
|
||||||
using Ryujinx.Memory.Tracking;
|
using Ryujinx.Memory.Tracking;
|
||||||
using System;
|
using System;
|
||||||
@@ -18,7 +17,9 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
{
|
{
|
||||||
uint currentEl = Pstate & ~((uint)ExceptionLevel.PstateMask);
|
uint currentEl = Pstate & ~((uint)ExceptionLevel.PstateMask);
|
||||||
if (currentEl == (uint)ExceptionLevel.EL1h)
|
if (currentEl == (uint)ExceptionLevel.EL1h)
|
||||||
|
{
|
||||||
return _impl.ElrEl1;
|
return _impl.ElrEl1;
|
||||||
|
}
|
||||||
return _impl.Pc;
|
return _impl.Pc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -68,7 +69,9 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (value)
|
if (value)
|
||||||
|
{
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,12 +82,10 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
private readonly IHvExecutionContext _shadowContext;
|
private readonly IHvExecutionContext _shadowContext;
|
||||||
private IHvExecutionContext _impl;
|
private IHvExecutionContext _impl;
|
||||||
private int _shouldStep;
|
private int _shouldStep;
|
||||||
private readonly ExceptionCallbacks _exceptionCallbacks;
|
|
||||||
private int _interruptRequested;
|
|
||||||
|
|
||||||
// GPU Sync control
|
private readonly ExceptionCallbacks _exceptionCallbacks;
|
||||||
private int _syncCounter;
|
|
||||||
private int _strongSyncCounter;
|
private int _interruptRequested;
|
||||||
|
|
||||||
public HvExecutionContext(ICounter counter, ExceptionCallbacks exceptionCallbacks)
|
public HvExecutionContext(ICounter counter, ExceptionCallbacks exceptionCallbacks)
|
||||||
{
|
{
|
||||||
@@ -107,17 +108,38 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void SetV(int index, V128 value) => _impl.SetV(index, value);
|
public void SetV(int index, V128 value) => _impl.SetV(index, value);
|
||||||
|
|
||||||
private void InterruptHandler() => _exceptionCallbacks.InterruptCallback?.Invoke(this);
|
private void InterruptHandler()
|
||||||
private void BreakHandler(ulong address, int imm) => _exceptionCallbacks.BreakCallback?.Invoke(this, address, imm);
|
{
|
||||||
private void StepHandler() => _exceptionCallbacks.StepCallback?.Invoke(this);
|
_exceptionCallbacks.InterruptCallback?.Invoke(this);
|
||||||
private void SupervisorCallHandler(ulong address, int imm) => _exceptionCallbacks.SupervisorCallback?.Invoke(this, address, imm);
|
}
|
||||||
private void UndefinedHandler(ulong address, int opCode) => _exceptionCallbacks.UndefinedCallback?.Invoke(this, address, opCode);
|
|
||||||
|
private void BreakHandler(ulong address, int imm)
|
||||||
|
{
|
||||||
|
_exceptionCallbacks.BreakCallback?.Invoke(this, address, imm);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void StepHandler()
|
||||||
|
{
|
||||||
|
_exceptionCallbacks.StepCallback?.Invoke(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SupervisorCallHandler(ulong address, int imm)
|
||||||
|
{
|
||||||
|
_exceptionCallbacks.SupervisorCallback?.Invoke(this, address, imm);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UndefinedHandler(ulong address, int opCode)
|
||||||
|
{
|
||||||
|
_exceptionCallbacks.UndefinedCallback?.Invoke(this, address, opCode);
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void RequestInterrupt()
|
public void RequestInterrupt()
|
||||||
{
|
{
|
||||||
if (Interlocked.Exchange(ref _interruptRequested, 1) == 0 && _impl is HvExecutionContextVcpu impl)
|
if (Interlocked.Exchange(ref _interruptRequested, 1) == 0 && _impl is HvExecutionContextVcpu impl)
|
||||||
|
{
|
||||||
impl.RequestInterrupt();
|
impl.RequestInterrupt();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool GetAndClearInterruptRequested()
|
private bool GetAndClearInterruptRequested()
|
||||||
@@ -139,9 +161,13 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
{
|
{
|
||||||
uint currentEl = Pstate & ~((uint)ExceptionLevel.PstateMask);
|
uint currentEl = Pstate & ~((uint)ExceptionLevel.PstateMask);
|
||||||
if (currentEl == (uint)ExceptionLevel.EL1h)
|
if (currentEl == (uint)ExceptionLevel.EL1h)
|
||||||
|
{
|
||||||
_impl.ElrEl1 = value;
|
_impl.ElrEl1 = value;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
_impl.Pc = value;
|
_impl.Pc = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,10 +181,8 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
public unsafe void Execute(HvMemoryManager memoryManager, ulong address)
|
public unsafe void Execute(HvMemoryManager memoryManager, ulong address)
|
||||||
{
|
{
|
||||||
HvVcpu vcpu = HvVcpuPool.Instance.Create(memoryManager.AddressSpace, _shadowContext, SwapContext);
|
HvVcpu vcpu = HvVcpuPool.Instance.Create(memoryManager.AddressSpace, _shadowContext, SwapContext);
|
||||||
HvApi.hv_vcpu_set_reg(vcpu.Handle, HvReg.PC, address).ThrowOnError();
|
|
||||||
|
|
||||||
_syncCounter = 0;
|
HvApi.hv_vcpu_set_reg(vcpu.Handle, HvReg.PC, address).ThrowOnError();
|
||||||
_strongSyncCounter = 0;
|
|
||||||
|
|
||||||
while (Running)
|
while (Running)
|
||||||
{
|
{
|
||||||
@@ -168,23 +192,16 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
if (currentEl == (uint)ExceptionLevel.EL1h)
|
if (currentEl == (uint)ExceptionLevel.EL1h)
|
||||||
{
|
{
|
||||||
HvApi.hv_vcpu_get_sys_reg(vcpu.Handle, HvSysReg.SPSR_EL1, out ulong spsr).ThrowOnError();
|
HvApi.hv_vcpu_get_sys_reg(vcpu.Handle, HvSysReg.SPSR_EL1, out ulong spsr).ThrowOnError();
|
||||||
spsr |= (1U << 21);
|
spsr |= (1 << 21);
|
||||||
HvApi.hv_vcpu_set_sys_reg(vcpu.Handle, HvSysReg.SPSR_EL1, spsr);
|
HvApi.hv_vcpu_set_sys_reg(vcpu.Handle, HvSysReg.SPSR_EL1, spsr);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Pstate |= (1U << 21);
|
Pstate |= (1 << 21);
|
||||||
}
|
}
|
||||||
HvApi.hv_vcpu_set_sys_reg(vcpu.Handle, HvSysReg.MDSCR_EL1, 1);
|
HvApi.hv_vcpu_set_sys_reg(vcpu.Handle, HvSysReg.MDSCR_EL1, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adaptive GPU synchronization to prevent 0 FPS
|
|
||||||
if (++_syncCounter % 12 == 0)
|
|
||||||
{
|
|
||||||
TryGpuSync();
|
|
||||||
_syncCounter = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
HvApi.hv_vcpu_run(vcpu.Handle).ThrowOnError();
|
HvApi.hv_vcpu_run(vcpu.Handle).ThrowOnError();
|
||||||
|
|
||||||
HvExitReason reason = vcpu.ExitInfo->Reason;
|
HvExitReason reason = vcpu.ExitInfo->Reason;
|
||||||
@@ -195,7 +212,9 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
ExceptionClass hvEc = (ExceptionClass)(hvEsr >> 26);
|
ExceptionClass hvEc = (ExceptionClass)(hvEsr >> 26);
|
||||||
|
|
||||||
if (hvEc != ExceptionClass.HvcAarch64)
|
if (hvEc != ExceptionClass.HvcAarch64)
|
||||||
|
{
|
||||||
throw new Exception($"Unhandled exception from guest kernel with ESR 0x{hvEsr:X} ({hvEc}).");
|
throw new Exception($"Unhandled exception from guest kernel with ESR 0x{hvEsr:X} ({hvEc}).");
|
||||||
|
}
|
||||||
|
|
||||||
address = SynchronousException(memoryManager, ref vcpu);
|
address = SynchronousException(memoryManager, ref vcpu);
|
||||||
HvApi.hv_vcpu_set_reg(vcpu.Handle, HvReg.PC, address).ThrowOnError();
|
HvApi.hv_vcpu_set_reg(vcpu.Handle, HvReg.PC, address).ThrowOnError();
|
||||||
@@ -226,31 +245,10 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
HvVcpuPool.Instance.Destroy(vcpu, SwapContext);
|
HvVcpuPool.Instance.Destroy(vcpu, SwapContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TryGpuSync() is called periodically in the main Execute() loop. The "syncing" value can be tuned based on gameplay results.
|
|
||||||
// This feature it to be followed-up and further completed in a future PR.
|
|
||||||
private void TryGpuSync()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Thread.Yield();
|
|
||||||
|
|
||||||
if (++_strongSyncCounter % 6 == 0)
|
|
||||||
{
|
|
||||||
Thread.Yield();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
if (_strongSyncCounter % 100 == 0)
|
|
||||||
{
|
|
||||||
Logger.Warning?.Print(LogClass.Gpu, $"[AppleHv] GPU sync issue: {ex.Message}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private ulong SynchronousException(HvMemoryManager memoryManager, ref HvVcpu vcpu)
|
private ulong SynchronousException(HvMemoryManager memoryManager, ref HvVcpu vcpu)
|
||||||
{
|
{
|
||||||
ulong vcpuHandle = vcpu.Handle;
|
ulong vcpuHandle = vcpu.Handle;
|
||||||
|
|
||||||
HvApi.hv_vcpu_get_sys_reg(vcpuHandle, HvSysReg.ELR_EL1, out ulong elr).ThrowOnError();
|
HvApi.hv_vcpu_get_sys_reg(vcpuHandle, HvSysReg.ELR_EL1, out ulong elr).ThrowOnError();
|
||||||
HvApi.hv_vcpu_get_sys_reg(vcpuHandle, HvSysReg.ESR_EL1, out ulong esr).ThrowOnError();
|
HvApi.hv_vcpu_get_sys_reg(vcpuHandle, HvSysReg.ESR_EL1, out ulong esr).ThrowOnError();
|
||||||
|
|
||||||
@@ -261,20 +259,16 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
case ExceptionClass.DataAbortLowerEl:
|
case ExceptionClass.DataAbortLowerEl:
|
||||||
DataAbort(memoryManager.Tracking, vcpuHandle, (uint)esr);
|
DataAbort(memoryManager.Tracking, vcpuHandle, (uint)esr);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ExceptionClass.TrappedMsrMrsSystem:
|
case ExceptionClass.TrappedMsrMrsSystem:
|
||||||
InstructionTrap((uint)esr);
|
InstructionTrap((uint)esr);
|
||||||
HvApi.hv_vcpu_set_sys_reg(vcpuHandle, HvSysReg.ELR_EL1, elr + 4UL).ThrowOnError();
|
HvApi.hv_vcpu_set_sys_reg(vcpuHandle, HvSysReg.ELR_EL1, elr + 4UL).ThrowOnError();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ExceptionClass.SvcAarch64:
|
case ExceptionClass.SvcAarch64:
|
||||||
ReturnToPool(vcpu);
|
ReturnToPool(vcpu);
|
||||||
ushort id = (ushort)esr;
|
ushort id = (ushort)esr;
|
||||||
SupervisorCallHandler(elr - 4UL, id);
|
SupervisorCallHandler(elr - 4UL, id);
|
||||||
Thread.Yield(); // MoltenVK causes extremely frequent SVC exits, and HVF handles them in a busy loop. Hypervisor.Framework accelerates the guest CPU, and without periodic yielding/flushing, MoltenVK's presentation queue can starve, causing permanent 0 FPS deadlock.
|
|
||||||
vcpu = RentFromPool(memoryManager.AddressSpace, vcpu);
|
vcpu = RentFromPool(memoryManager.AddressSpace, vcpu);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ExceptionClass.SoftwareStepLowerEl:
|
case ExceptionClass.SoftwareStepLowerEl:
|
||||||
HvApi.hv_vcpu_get_sys_reg(vcpuHandle, HvSysReg.SPSR_EL1, out ulong spsr).ThrowOnError();
|
HvApi.hv_vcpu_get_sys_reg(vcpuHandle, HvSysReg.SPSR_EL1, out ulong spsr).ThrowOnError();
|
||||||
spsr &= ~((ulong)(1 << 21));
|
spsr &= ~((ulong)(1 << 21));
|
||||||
@@ -284,23 +278,21 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
StepHandler();
|
StepHandler();
|
||||||
vcpu = RentFromPool(memoryManager.AddressSpace, vcpu);
|
vcpu = RentFromPool(memoryManager.AddressSpace, vcpu);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ExceptionClass.BrkAarch64:
|
case ExceptionClass.BrkAarch64:
|
||||||
ReturnToPool(vcpu);
|
ReturnToPool(vcpu);
|
||||||
BreakHandler(elr, (ushort)esr);
|
BreakHandler(elr, (ushort)esr);
|
||||||
vcpu = RentFromPool(memoryManager.AddressSpace, vcpu);
|
vcpu = RentFromPool(memoryManager.AddressSpace, vcpu);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new Exception($"Unhandled guest exception {ec}.");
|
throw new Exception($"Unhandled guest exception {ec}.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure we will continue running at EL0.
|
// Make sure we will continue running at EL0.
|
||||||
if (memoryManager.AddressSpace.GetAndClearUserTlbInvalidationPending())
|
if (memoryManager.AddressSpace.GetAndClearUserTlbInvalidationPending())
|
||||||
|
{
|
||||||
// TODO: Invalidate only the range that was modified?
|
// TODO: Invalidate only the range that was modified?
|
||||||
return HvAddressSpace.KernelRegionTlbiEretAddress;
|
return HvAddressSpace.KernelRegionTlbiEretAddress;
|
||||||
|
}
|
||||||
return HvAddressSpace.KernelRegionEretAddress;
|
return HvAddressSpace.KernelRegionEretAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -313,6 +305,7 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
if (farValid)
|
if (farValid)
|
||||||
{
|
{
|
||||||
HvApi.hv_vcpu_get_sys_reg(vcpu, HvSysReg.FAR_EL1, out ulong far).ThrowOnError();
|
HvApi.hv_vcpu_get_sys_reg(vcpu, HvSysReg.FAR_EL1, out ulong far).ThrowOnError();
|
||||||
|
|
||||||
ulong size = 1UL << accessSizeLog2;
|
ulong size = 1UL << accessSizeLog2;
|
||||||
|
|
||||||
if (!tracking.VirtualMemoryEvent(far, size, write))
|
if (!tracking.VirtualMemoryEvent(far, size, write))
|
||||||
@@ -356,7 +349,9 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
private void WriteRt(uint rt, ulong value)
|
private void WriteRt(uint rt, ulong value)
|
||||||
{
|
{
|
||||||
if (rt < 31)
|
if (rt < 31)
|
||||||
|
{
|
||||||
SetX((int)rt, value);
|
SetX((int)rt, value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ReturnToPool(HvVcpu vcpu)
|
private void ReturnToPool(HvVcpu vcpu)
|
||||||
@@ -374,6 +369,8 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
_impl = newContext;
|
_impl = newContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose() { }
|
public void Dispose()
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
using ARMeilleure.State;
|
using ARMeilleure.State;
|
||||||
using Ryujinx.Common.Logging;
|
|
||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
@@ -16,31 +14,8 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
private static readonly SetSimdFpReg _setSimdFpReg;
|
private static readonly SetSimdFpReg _setSimdFpReg;
|
||||||
private static readonly nint _setSimdFpRegNativePtr;
|
private static readonly nint _setSimdFpRegNativePtr;
|
||||||
|
|
||||||
public static bool AggressiveMode { get; set; } = false;
|
|
||||||
private bool _earlyBootPhase = true;
|
|
||||||
|
|
||||||
public ulong ThreadUid { get; set; }
|
public ulong ThreadUid { get; set; }
|
||||||
|
|
||||||
private readonly ulong[] _x = new ulong[32];
|
|
||||||
private readonly V128[] _v = new V128[32];
|
|
||||||
|
|
||||||
private ulong _pc;
|
|
||||||
private ulong _elrEl1;
|
|
||||||
private ulong _esrEl1;
|
|
||||||
private ulong _tpidrEl0;
|
|
||||||
private ulong _tpidrroEl0;
|
|
||||||
private ulong _fpcr;
|
|
||||||
private ulong _fpsr;
|
|
||||||
private ulong _pstateRaw;
|
|
||||||
|
|
||||||
private long _fallbackCount;
|
|
||||||
private long _lastWarningTicks;
|
|
||||||
private const long WarningCooldownTicks = 500_000_000; // 0.5 seconds
|
|
||||||
|
|
||||||
private readonly ulong _vcpu;
|
|
||||||
private int _interruptRequested;
|
|
||||||
private readonly object _registerLock = new object();
|
|
||||||
|
|
||||||
static HvExecutionContextVcpu()
|
static HvExecutionContextVcpu()
|
||||||
{
|
{
|
||||||
// .NET does not support passing vectors by value, so we need to pass a pointer and use a native
|
// .NET does not support passing vectors by value, so we need to pass a pointer and use a native
|
||||||
@@ -58,293 +33,155 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public HvExecutionContextVcpu(ulong vcpu)
|
|
||||||
{
|
|
||||||
_vcpu = vcpu;
|
|
||||||
Reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Reset()
|
|
||||||
{
|
|
||||||
lock (_registerLock)
|
|
||||||
{
|
|
||||||
_pstateRaw = 0x80000000UL;
|
|
||||||
_pc = 0;
|
|
||||||
_elrEl1 = 0;
|
|
||||||
_esrEl1 = 0;
|
|
||||||
_tpidrEl0 = 0;
|
|
||||||
_tpidrroEl0 = 0;
|
|
||||||
_fpcr = 0;
|
|
||||||
_fpsr = 0;
|
|
||||||
|
|
||||||
Array.Clear(_x, 0, _x.Length);
|
|
||||||
Array.Clear(_v, 0, _v.Length);
|
|
||||||
|
|
||||||
_fallbackCount = 0;
|
|
||||||
_lastWarningTicks = 0;
|
|
||||||
_interruptRequested = 0;
|
|
||||||
_earlyBootPhase = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LogHvWarning(string operation, string regName, string extra = "")
|
|
||||||
{
|
|
||||||
if (AggressiveMode) return;
|
|
||||||
|
|
||||||
long now = DateTime.UtcNow.Ticks;
|
|
||||||
if (now - _lastWarningTicks <= WarningCooldownTicks) return;
|
|
||||||
|
|
||||||
string msg = $"[AppleHv] BadArgument on {operation} {regName} | PC=0x{_pc:X16}";
|
|
||||||
if (!string.IsNullOrEmpty(extra)) msg += $" | {extra}";
|
|
||||||
msg += $" | Total: {Interlocked.Read(ref _fallbackCount)}";
|
|
||||||
|
|
||||||
Logger.Warning?.Print(LogClass.Cpu, msg);
|
|
||||||
_lastWarningTicks = now;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ulong Pc
|
public ulong Pc
|
||||||
{
|
{
|
||||||
get { lock (_registerLock) return GetRegCached(HvReg.PC, ref _pc, "PC"); }
|
get
|
||||||
set { lock (_registerLock) SetRegCached(HvReg.PC, value, ref _pc, "PC"); }
|
{
|
||||||
|
HvApi.hv_vcpu_get_reg(_vcpu, HvReg.PC, out ulong pc).ThrowOnError();
|
||||||
|
return pc;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
HvApi.hv_vcpu_set_reg(_vcpu, HvReg.PC, value).ThrowOnError();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ulong ElrEl1
|
public ulong ElrEl1
|
||||||
{
|
{
|
||||||
get { lock (_registerLock) return GetSysRegCached(HvSysReg.ELR_EL1, ref _elrEl1, "ELR_EL1"); }
|
get
|
||||||
set { lock (_registerLock) SetSysRegCached(HvSysReg.ELR_EL1, value, ref _elrEl1, "ELR_EL1"); }
|
{
|
||||||
|
HvApi.hv_vcpu_get_sys_reg(_vcpu, HvSysReg.ELR_EL1, out ulong elr).ThrowOnError();
|
||||||
|
return elr;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
HvApi.hv_vcpu_set_sys_reg(_vcpu, HvSysReg.ELR_EL1, value).ThrowOnError();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ulong EsrEl1
|
public ulong EsrEl1
|
||||||
{
|
{
|
||||||
get { lock (_registerLock) return GetSysRegCached(HvSysReg.ESR_EL1, ref _esrEl1, "ESR_EL1"); }
|
get
|
||||||
set { lock (_registerLock) SetSysRegCached(HvSysReg.ESR_EL1, value, ref _esrEl1, "ESR_EL1"); }
|
{
|
||||||
|
HvApi.hv_vcpu_get_sys_reg(_vcpu, HvSysReg.ESR_EL1, out ulong esr).ThrowOnError();
|
||||||
|
return esr;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
HvApi.hv_vcpu_set_sys_reg(_vcpu, HvSysReg.ESR_EL1, value).ThrowOnError();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public long TpidrEl0
|
public long TpidrEl0
|
||||||
{
|
{
|
||||||
get { lock (_registerLock) return (long)GetSysRegCached(HvSysReg.TPIDR_EL0, ref _tpidrEl0, "TPIDR_EL0"); }
|
get
|
||||||
set { lock (_registerLock) SetSysRegCached(HvSysReg.TPIDR_EL0, (ulong)value, ref _tpidrEl0, "TPIDR_EL0"); }
|
{
|
||||||
|
HvApi.hv_vcpu_get_sys_reg(_vcpu, HvSysReg.TPIDR_EL0, out ulong tpidrEl0).ThrowOnError();
|
||||||
|
return (long)tpidrEl0;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
HvApi.hv_vcpu_set_sys_reg(_vcpu, HvSysReg.TPIDR_EL0, (ulong)value).ThrowOnError();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public long TpidrroEl0
|
public long TpidrroEl0
|
||||||
{
|
{
|
||||||
get { lock (_registerLock) return (long)GetSysRegCached(HvSysReg.TPIDRRO_EL0, ref _tpidrroEl0, "TPIDRRO_EL0"); }
|
get
|
||||||
set { lock (_registerLock) SetSysRegCached(HvSysReg.TPIDRRO_EL0, (ulong)value, ref _tpidrroEl0, "TPIDRRO_EL0"); }
|
{
|
||||||
|
HvApi.hv_vcpu_get_sys_reg(_vcpu, HvSysReg.TPIDRRO_EL0, out ulong tpidrroEl0).ThrowOnError();
|
||||||
|
return (long)tpidrroEl0;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
HvApi.hv_vcpu_set_sys_reg(_vcpu, HvSysReg.TPIDRRO_EL0, (ulong)value).ThrowOnError();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public uint Pstate
|
public uint Pstate
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
lock (_registerLock)
|
HvApi.hv_vcpu_get_reg(_vcpu, HvReg.CPSR, out ulong cpsr).ThrowOnError();
|
||||||
{
|
return (uint)cpsr;
|
||||||
HvResult res = HvApi.hv_vcpu_get_reg(_vcpu, HvReg.CPSR, out ulong val);
|
|
||||||
if (res == HvResult.BadArgument)
|
|
||||||
{
|
|
||||||
Interlocked.Increment(ref _fallbackCount);
|
|
||||||
LogHvWarning("Get", "CPSR (Pstate)");
|
|
||||||
return (uint)_pstateRaw;
|
|
||||||
}
|
|
||||||
res.ThrowOnError();
|
|
||||||
_pstateRaw = val;
|
|
||||||
return (uint)val;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
lock (_registerLock)
|
HvApi.hv_vcpu_set_reg(_vcpu, HvReg.CPSR, (ulong)value).ThrowOnError();
|
||||||
{
|
|
||||||
HvResult res = HvApi.hv_vcpu_set_reg(_vcpu, HvReg.CPSR, value);
|
|
||||||
if (res == HvResult.BadArgument)
|
|
||||||
{
|
|
||||||
Interlocked.Increment(ref _fallbackCount);
|
|
||||||
LogHvWarning("Set", "CPSR (Pstate)", $"value=0x{value:X}");
|
|
||||||
}
|
|
||||||
else res.ThrowOnError();
|
|
||||||
_pstateRaw = value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public uint Fpcr
|
public uint Fpcr
|
||||||
{
|
{
|
||||||
get { lock (_registerLock) return (uint)GetRegCached(HvReg.FPCR, ref _fpcr, "FPCR"); }
|
get
|
||||||
set { lock (_registerLock) SetRegCached(HvReg.FPCR, value, ref _fpcr, "FPCR"); }
|
{
|
||||||
|
HvApi.hv_vcpu_get_reg(_vcpu, HvReg.FPCR, out ulong fpcr).ThrowOnError();
|
||||||
|
return (uint)fpcr;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
HvApi.hv_vcpu_set_reg(_vcpu, HvReg.FPCR, (ulong)value).ThrowOnError();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public uint Fpsr
|
public uint Fpsr
|
||||||
{
|
{
|
||||||
get { lock (_registerLock) return (uint)GetRegCached(HvReg.FPSR, ref _fpsr, "FPSR"); }
|
get
|
||||||
set { lock (_registerLock) SetRegCached(HvReg.FPSR, value, ref _fpsr, "FPSR"); }
|
{
|
||||||
|
HvApi.hv_vcpu_get_reg(_vcpu, HvReg.FPSR, out ulong fpsr).ThrowOnError();
|
||||||
|
return (uint)fpsr;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
HvApi.hv_vcpu_set_reg(_vcpu, HvReg.FPSR, (ulong)value).ThrowOnError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly ulong _vcpu;
|
||||||
|
private int _interruptRequested;
|
||||||
|
|
||||||
|
public HvExecutionContextVcpu(ulong vcpu)
|
||||||
|
{
|
||||||
|
_vcpu = vcpu;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ulong GetX(int index)
|
public ulong GetX(int index)
|
||||||
{
|
{
|
||||||
lock (_registerLock)
|
if (index == 31)
|
||||||
{
|
{
|
||||||
ulong value;
|
HvApi.hv_vcpu_get_sys_reg(_vcpu, HvSysReg.SP_EL0, out ulong value).ThrowOnError();
|
||||||
string regName = index == 31 ? "SP_EL0" : $"X{index}";
|
return value;
|
||||||
|
}
|
||||||
if (index == 31)
|
else
|
||||||
{
|
{
|
||||||
HvResult res = HvApi.hv_vcpu_get_sys_reg(_vcpu, HvSysReg.SP_EL0, out value);
|
HvApi.hv_vcpu_get_reg(_vcpu, HvReg.X0 + (uint)index, out ulong value).ThrowOnError();
|
||||||
if (res == HvResult.BadArgument)
|
return value;
|
||||||
{
|
|
||||||
Interlocked.Increment(ref _fallbackCount);
|
|
||||||
LogHvWarning("GetX", regName);
|
|
||||||
return _x[31];
|
|
||||||
}
|
|
||||||
res.ThrowOnError();
|
|
||||||
return _x[31] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((uint)index > 30) return 0;
|
|
||||||
|
|
||||||
if (index == 0 && _earlyBootPhase && _pc == 0)
|
|
||||||
{
|
|
||||||
return _x[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
HvResult resX = HvApi.hv_vcpu_get_reg(_vcpu, HvReg.X0 + (uint)index, out value);
|
|
||||||
if (resX == HvResult.BadArgument)
|
|
||||||
{
|
|
||||||
Interlocked.Increment(ref _fallbackCount);
|
|
||||||
LogHvWarning("GetX", regName);
|
|
||||||
return _x[index];
|
|
||||||
}
|
|
||||||
resX.ThrowOnError();
|
|
||||||
return _x[index] = value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetX(int index, ulong value)
|
public void SetX(int index, ulong value)
|
||||||
{
|
{
|
||||||
lock (_registerLock)
|
if (index == 31)
|
||||||
{
|
{
|
||||||
string regName = index == 31 ? "SP_EL0" : $"X{index}";
|
HvApi.hv_vcpu_set_sys_reg(_vcpu, HvSysReg.SP_EL0, value).ThrowOnError();
|
||||||
|
}
|
||||||
if (index == 31)
|
else
|
||||||
{
|
{
|
||||||
HvResult res = HvApi.hv_vcpu_set_sys_reg(_vcpu, HvSysReg.SP_EL0, value);
|
HvApi.hv_vcpu_set_reg(_vcpu, HvReg.X0 + (uint)index, value).ThrowOnError();
|
||||||
if (res == HvResult.BadArgument)
|
|
||||||
{
|
|
||||||
Interlocked.Increment(ref _fallbackCount);
|
|
||||||
LogHvWarning("SetX", regName, $"value=0x{value:X16}");
|
|
||||||
_x[31] = value;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
res.ThrowOnError();
|
|
||||||
_x[31] = value;
|
|
||||||
}
|
|
||||||
else if ((uint)index <= 30)
|
|
||||||
{
|
|
||||||
HvResult res = HvApi.hv_vcpu_set_reg(_vcpu, HvReg.X0 + (uint)index, value);
|
|
||||||
if (res == HvResult.BadArgument)
|
|
||||||
{
|
|
||||||
Interlocked.Increment(ref _fallbackCount);
|
|
||||||
LogHvWarning("SetX", regName, $"value=0x{value:X16}");
|
|
||||||
_x[index] = value;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
res.ThrowOnError();
|
|
||||||
_x[index] = value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public V128 GetV(int index)
|
public V128 GetV(int index)
|
||||||
{
|
{
|
||||||
lock (_registerLock)
|
HvApi.hv_vcpu_get_simd_fp_reg(_vcpu, HvSimdFPReg.Q0 + (uint)index, out HvSimdFPUchar16 value).ThrowOnError();
|
||||||
{
|
return new V128(value.Low, value.High);
|
||||||
if ((uint)index > 31) return default;
|
|
||||||
|
|
||||||
HvResult res = HvApi.hv_vcpu_get_simd_fp_reg(_vcpu, HvSimdFPReg.Q0 + (uint)index, out HvSimdFPUchar16 val);
|
|
||||||
if (res == HvResult.BadArgument)
|
|
||||||
{
|
|
||||||
Interlocked.Increment(ref _fallbackCount);
|
|
||||||
LogHvWarning("GetV", $"Q{index}");
|
|
||||||
return _v[index];
|
|
||||||
}
|
|
||||||
res.ThrowOnError();
|
|
||||||
return _v[index] = new V128(val.Low, val.High);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetV(int index, V128 value)
|
public void SetV(int index, V128 value)
|
||||||
{
|
{
|
||||||
lock (_registerLock)
|
_setSimdFpReg(_vcpu, HvSimdFPReg.Q0 + (uint)index, value, _setSimdFpRegNativePtr).ThrowOnError();
|
||||||
{
|
|
||||||
if ((uint)index > 31) return;
|
|
||||||
|
|
||||||
HvResult res = _setSimdFpReg(_vcpu, HvSimdFPReg.Q0 + (uint)index, value, _setSimdFpRegNativePtr);
|
|
||||||
if (res == HvResult.BadArgument)
|
|
||||||
{
|
|
||||||
Interlocked.Increment(ref _fallbackCount);
|
|
||||||
LogHvWarning("SetV", $"Q{index}");
|
|
||||||
_v[index] = value;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
res.ThrowOnError();
|
|
||||||
_v[index] = value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ulong GetRegCached(HvReg reg, ref ulong cached, string name)
|
|
||||||
{
|
|
||||||
HvResult res = HvApi.hv_vcpu_get_reg(_vcpu, reg, out ulong val);
|
|
||||||
if (res == HvResult.BadArgument)
|
|
||||||
{
|
|
||||||
Interlocked.Increment(ref _fallbackCount);
|
|
||||||
LogHvWarning("GetReg", name);
|
|
||||||
return cached;
|
|
||||||
}
|
|
||||||
res.ThrowOnError();
|
|
||||||
return cached = val;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetRegCached(HvReg reg, ulong value, ref ulong cached, string name)
|
|
||||||
{
|
|
||||||
HvResult res = HvApi.hv_vcpu_set_reg(_vcpu, reg, value);
|
|
||||||
if (res == HvResult.BadArgument)
|
|
||||||
{
|
|
||||||
Interlocked.Increment(ref _fallbackCount);
|
|
||||||
LogHvWarning("SetReg", name, $"value=0x{value:X16}");
|
|
||||||
cached = value;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
res.ThrowOnError();
|
|
||||||
cached = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ulong GetSysRegCached(HvSysReg reg, ref ulong cached, string name)
|
|
||||||
{
|
|
||||||
HvResult res = HvApi.hv_vcpu_get_sys_reg(_vcpu, reg, out ulong val);
|
|
||||||
if (res == HvResult.BadArgument)
|
|
||||||
{
|
|
||||||
Interlocked.Increment(ref _fallbackCount);
|
|
||||||
LogHvWarning("GetSysReg", name);
|
|
||||||
return cached;
|
|
||||||
}
|
|
||||||
res.ThrowOnError();
|
|
||||||
return cached = val;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetSysRegCached(HvSysReg reg, ulong value, ref ulong cached, string name)
|
|
||||||
{
|
|
||||||
HvResult res = HvApi.hv_vcpu_set_sys_reg(_vcpu, reg, value);
|
|
||||||
if (res == HvResult.BadArgument)
|
|
||||||
{
|
|
||||||
Interlocked.Increment(ref _fallbackCount);
|
|
||||||
LogHvWarning("SetSysReg", name, $"value=0x{value:X16}");
|
|
||||||
cached = value;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
res.ThrowOnError();
|
|
||||||
cached = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long GetFallbackCount() => Interlocked.Read(ref _fallbackCount);
|
|
||||||
|
|
||||||
public void RequestInterrupt()
|
public void RequestInterrupt()
|
||||||
{
|
{
|
||||||
if (Interlocked.Exchange(ref _interruptRequested, 1) == 0)
|
if (Interlocked.Exchange(ref _interruptRequested, 1) == 0)
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
|
|
||||||
public HvMemoryBlockAllocation Allocate(ulong size, ulong alignment)
|
public HvMemoryBlockAllocation Allocate(ulong size, ulong alignment)
|
||||||
{
|
{
|
||||||
Allocation allocation = Allocate(size, alignment, CreateBlock);
|
var allocation = Allocate(size, alignment, CreateBlock);
|
||||||
|
|
||||||
return new HvMemoryBlockAllocation(this, allocation.Block, allocation.Offset, allocation.Size);
|
return new HvMemoryBlockAllocation(this, allocation.Block, allocation.Offset, allocation.Size);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -237,17 +237,17 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
List<MemoryRange> guestRegions = GetPhysicalRegionsImpl(va, size);
|
var guestRegions = GetPhysicalRegionsImpl(va, size);
|
||||||
if (guestRegions == null)
|
if (guestRegions == null)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
HostMemoryRange[] regions = new HostMemoryRange[guestRegions.Count];
|
var regions = new HostMemoryRange[guestRegions.Count];
|
||||||
|
|
||||||
for (int i = 0; i < regions.Length; i++)
|
for (int i = 0; i < regions.Length; i++)
|
||||||
{
|
{
|
||||||
MemoryRange guestRegion = guestRegions[i];
|
var guestRegion = guestRegions[i];
|
||||||
nint pointer = _backingMemory.GetPointer(guestRegion.Address, guestRegion.Size);
|
nint pointer = _backingMemory.GetPointer(guestRegion.Address, guestRegion.Size);
|
||||||
regions[i] = new HostMemoryRange((nuint)(ulong)pointer, guestRegion.Size);
|
regions[i] = new HostMemoryRange((nuint)(ulong)pointer, guestRegion.Size);
|
||||||
}
|
}
|
||||||
@@ -275,7 +275,7 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
|
|
||||||
int pages = GetPagesCount(va, (uint)size, out va);
|
int pages = GetPagesCount(va, (uint)size, out va);
|
||||||
|
|
||||||
List<MemoryRange> regions = new();
|
var regions = new List<MemoryRange>();
|
||||||
|
|
||||||
ulong regionStart = GetPhysicalAddressInternal(va);
|
ulong regionStart = GetPhysicalAddressInternal(va);
|
||||||
ulong regionSize = PageSize;
|
ulong regionSize = PageSize;
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
{
|
{
|
||||||
// Calculate our time delta in ticks based on the current clock frequency.
|
// Calculate our time delta in ticks based on the current clock frequency.
|
||||||
|
|
||||||
int result = TimeApi.mach_timebase_info(out MachTimebaseInfo timeBaseInfo);
|
int result = TimeApi.mach_timebase_info(out var timeBaseInfo);
|
||||||
|
|
||||||
Debug.Assert(result == 0);
|
Debug.Assert(result == 0);
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
baseAddress = ipaAllocator.Allocate(block.Size, AsIpaAlignment);
|
baseAddress = ipaAllocator.Allocate(block.Size, AsIpaAlignment);
|
||||||
}
|
}
|
||||||
|
|
||||||
HvMemoryFlags rwx = HvMemoryFlags.Read | HvMemoryFlags.Write | HvMemoryFlags.Exec;
|
var rwx = HvMemoryFlags.Read | HvMemoryFlags.Write | HvMemoryFlags.Exec;
|
||||||
|
|
||||||
HvApi.hv_vm_map((ulong)block.Pointer, baseAddress, block.Size, rwx).ThrowOnError();
|
HvApi.hv_vm_map((ulong)block.Pointer, baseAddress, block.Size, rwx).ThrowOnError();
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using ARMeilleure.Translation.PTC;
|
|
||||||
|
|
||||||
namespace Ryujinx.Cpu
|
namespace Ryujinx.Cpu
|
||||||
{
|
{
|
||||||
@@ -45,10 +44,11 @@ namespace Ryujinx.Cpu
|
|||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// If the execution engine is recompiling guest code, this can be used to load cached code from disk.
|
/// If the execution engine is recompiling guest code, this can be used to load cached code from disk.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <param name="cacheInfo">Identity and selector for the process-owned disk cache</param>
|
/// <param name="titleIdText">Title ID of the application in padded hex form</param>
|
||||||
|
/// <param name="displayVersion">Version of the application</param>
|
||||||
/// <param name="enabled">True if the cache should be loaded from disk if it exists, false otherwise</param>
|
/// <param name="enabled">True if the cache should be loaded from disk if it exists, false otherwise</param>
|
||||||
/// <returns>Disk cache load progress reporter and manager</returns>
|
/// <returns>Disk cache load progress reporter and manager</returns>
|
||||||
IDiskCacheLoadState LoadDiskCache(PtcCacheInfo cacheInfo, bool enabled);
|
IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled, string cacheSelector);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates that code has been loaded into guest memory, and that it might be executed in the future.
|
/// Indicates that code has been loaded into guest memory, and that it might be executed in the future.
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ namespace Ryujinx.Cpu.Jit.HostTracked
|
|||||||
Debug.Assert(leftSize > 0);
|
Debug.Assert(leftSize > 0);
|
||||||
Debug.Assert(rightSize > 0);
|
Debug.Assert(rightSize > 0);
|
||||||
|
|
||||||
(PrivateMemoryAllocation leftAllocation, PrivateAllocation) = PrivateAllocation.Split(leftSize);
|
(var leftAllocation, PrivateAllocation) = PrivateAllocation.Split(leftSize);
|
||||||
|
|
||||||
PrivateMapping left = new(Address, leftSize, leftAllocation);
|
PrivateMapping left = new(Address, leftSize, leftAllocation);
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using ARMeilleure.Common;
|
using ARMeilleure.Common;
|
||||||
using ARMeilleure.Memory;
|
using ARMeilleure.Memory;
|
||||||
using ARMeilleure.Translation;
|
using ARMeilleure.Translation;
|
||||||
using ARMeilleure.Translation.PTC;
|
|
||||||
using Ryujinx.Cpu.Signal;
|
using Ryujinx.Cpu.Signal;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.Jit
|
namespace Ryujinx.Cpu.Jit
|
||||||
@@ -10,15 +9,13 @@ namespace Ryujinx.Cpu.Jit
|
|||||||
{
|
{
|
||||||
private readonly ITickSource _tickSource;
|
private readonly ITickSource _tickSource;
|
||||||
private readonly Translator _translator;
|
private readonly Translator _translator;
|
||||||
|
private readonly AddressTable<ulong> _functionTable;
|
||||||
|
|
||||||
public JitCpuContext(ITickSource tickSource, IMemoryManager memory, bool for64Bit)
|
public JitCpuContext(ITickSource tickSource, IMemoryManager memory, bool for64Bit)
|
||||||
{
|
{
|
||||||
_tickSource = tickSource;
|
_tickSource = tickSource;
|
||||||
|
_functionTable = AddressTable<ulong>.CreateForArm(for64Bit, memory.Type);
|
||||||
bool sparse = memory.Type is not MemoryManagerType.SoftwareMmu and not MemoryManagerType.SoftwarePageTable;
|
_translator = new Translator(new JitMemoryAllocator(forJit: true), memory, _functionTable);
|
||||||
IAddressTable<ulong> functionTable = sparse ? SparseAddressTable<ulong>.CreateForArm(for64Bit) : AddressTable<ulong>.CreateForArm(for64Bit);
|
|
||||||
|
|
||||||
_translator = new Translator(new JitMemoryAllocator(forJit: true), memory, functionTable);
|
|
||||||
|
|
||||||
if (memory.Type.IsHostMappedOrTracked())
|
if (memory.Type.IsHostMappedOrTracked())
|
||||||
{
|
{
|
||||||
@@ -52,15 +49,15 @@ namespace Ryujinx.Cpu.Jit
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public IDiskCacheLoadState LoadDiskCache(PtcCacheInfo cacheInfo, bool enabled)
|
public IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled, string cacheSelector)
|
||||||
{
|
{
|
||||||
return new JitDiskCacheLoadState(_translator.LoadDiskCache(cacheInfo, enabled));
|
return new JitDiskCacheLoadState(_translator.LoadDiskCache(titleIdText, displayVersion, enabled, cacheSelector));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void PrepareCodeRange(ulong address, ulong size)
|
public void PrepareCodeRange(ulong address, ulong size)
|
||||||
{
|
{
|
||||||
_translator.FunctionTable.SignalCodeRange(address, size);
|
_functionTable.SignalCodeRange(address, size);
|
||||||
_translator.PrepareCodeRange(address, size);
|
_translator.PrepareCodeRange(address, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -257,17 +257,17 @@ namespace Ryujinx.Cpu.Jit
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
List<MemoryRange> guestRegions = GetPhysicalRegionsImpl(va, size);
|
var guestRegions = GetPhysicalRegionsImpl(va, size);
|
||||||
if (guestRegions == null)
|
if (guestRegions == null)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
HostMemoryRange[] regions = new HostMemoryRange[guestRegions.Count];
|
var regions = new HostMemoryRange[guestRegions.Count];
|
||||||
|
|
||||||
for (int i = 0; i < regions.Length; i++)
|
for (int i = 0; i < regions.Length; i++)
|
||||||
{
|
{
|
||||||
MemoryRange guestRegion = guestRegions[i];
|
var guestRegion = guestRegions[i];
|
||||||
nint pointer = _backingMemory.GetPointer(guestRegion.Address, guestRegion.Size);
|
nint pointer = _backingMemory.GetPointer(guestRegion.Address, guestRegion.Size);
|
||||||
regions[i] = new HostMemoryRange((nuint)(ulong)pointer, guestRegion.Size);
|
regions[i] = new HostMemoryRange((nuint)(ulong)pointer, guestRegion.Size);
|
||||||
}
|
}
|
||||||
@@ -295,7 +295,7 @@ namespace Ryujinx.Cpu.Jit
|
|||||||
|
|
||||||
int pages = GetPagesCount(va, (uint)size, out va);
|
int pages = GetPagesCount(va, (uint)size, out va);
|
||||||
|
|
||||||
List<MemoryRange> regions = new();
|
var regions = new List<MemoryRange>();
|
||||||
|
|
||||||
ulong regionStart = GetPhysicalAddressInternal(va);
|
ulong regionStart = GetPhysicalAddressInternal(va);
|
||||||
ulong regionSize = PageSize;
|
ulong regionSize = PageSize;
|
||||||
|
|||||||
@@ -345,7 +345,7 @@ namespace Ryujinx.Cpu.Jit
|
|||||||
{
|
{
|
||||||
int pages = GetPagesCount(va, (uint)size, out va);
|
int pages = GetPagesCount(va, (uint)size, out va);
|
||||||
|
|
||||||
List<MemoryRange> regions = new();
|
var regions = new List<MemoryRange>();
|
||||||
|
|
||||||
ulong regionStart = GetPhysicalAddressChecked(va);
|
ulong regionStart = GetPhysicalAddressChecked(va);
|
||||||
ulong regionSize = PageSize;
|
ulong regionSize = PageSize;
|
||||||
|
|||||||
@@ -249,7 +249,7 @@ namespace Ryujinx.Cpu.Jit
|
|||||||
|
|
||||||
if (TryGetVirtualContiguous(va, data.Length, out MemoryBlock memoryBlock, out ulong offset))
|
if (TryGetVirtualContiguous(va, data.Length, out MemoryBlock memoryBlock, out ulong offset))
|
||||||
{
|
{
|
||||||
Span<byte> target = memoryBlock.GetSpan(offset, data.Length);
|
var target = memoryBlock.GetSpan(offset, data.Length);
|
||||||
|
|
||||||
bool changed = !data.SequenceEqual(target);
|
bool changed = !data.SequenceEqual(target);
|
||||||
|
|
||||||
@@ -448,7 +448,7 @@ namespace Ryujinx.Cpu.Jit
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<HostMemoryRange> regions = new();
|
var regions = new List<HostMemoryRange>();
|
||||||
ulong endVa = va + size;
|
ulong endVa = va + size;
|
||||||
|
|
||||||
try
|
try
|
||||||
@@ -489,7 +489,7 @@ namespace Ryujinx.Cpu.Jit
|
|||||||
|
|
||||||
int pages = GetPagesCount(va, (uint)size, out va);
|
int pages = GetPagesCount(va, (uint)size, out va);
|
||||||
|
|
||||||
List<MemoryRange> regions = new();
|
var regions = new List<MemoryRange>();
|
||||||
|
|
||||||
ulong regionStart = GetPhysicalAddressInternal(va);
|
ulong regionStart = GetPhysicalAddressInternal(va);
|
||||||
ulong regionSize = PageSize;
|
ulong regionSize = PageSize;
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ namespace Ryujinx.Cpu.LightningJit
|
|||||||
CpuPreset cpuPreset,
|
CpuPreset cpuPreset,
|
||||||
IMemoryManager memoryManager,
|
IMemoryManager memoryManager,
|
||||||
ulong address,
|
ulong address,
|
||||||
IAddressTable<ulong> funcTable,
|
AddressTable<ulong> funcTable,
|
||||||
nint dispatchStubPtr,
|
nint dispatchStubPtr,
|
||||||
ExecutionMode executionMode,
|
ExecutionMode executionMode,
|
||||||
Architecture targetArch)
|
Architecture targetArch)
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32
|
|||||||
CpuPreset cpuPreset,
|
CpuPreset cpuPreset,
|
||||||
IMemoryManager memoryManager,
|
IMemoryManager memoryManager,
|
||||||
ulong address,
|
ulong address,
|
||||||
IAddressTable<ulong> funcTable,
|
AddressTable<ulong> funcTable,
|
||||||
nint dispatchStubPtr,
|
nint dispatchStubPtr,
|
||||||
bool isThumb,
|
bool isThumb,
|
||||||
Architecture targetArch)
|
Architecture targetArch)
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
|||||||
public readonly RegisterAllocator RegisterAllocator;
|
public readonly RegisterAllocator RegisterAllocator;
|
||||||
public readonly MemoryManagerType MemoryManagerType;
|
public readonly MemoryManagerType MemoryManagerType;
|
||||||
public readonly TailMerger TailMerger;
|
public readonly TailMerger TailMerger;
|
||||||
public readonly IAddressTable<ulong> FuncTable;
|
public readonly AddressTable<ulong> FuncTable;
|
||||||
public readonly nint DispatchStubPointer;
|
public readonly nint DispatchStubPointer;
|
||||||
|
|
||||||
private readonly RegisterSaveRestore _registerSaveRestore;
|
private readonly RegisterSaveRestore _registerSaveRestore;
|
||||||
@@ -33,7 +33,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
|||||||
RegisterAllocator registerAllocator,
|
RegisterAllocator registerAllocator,
|
||||||
MemoryManagerType mmType,
|
MemoryManagerType mmType,
|
||||||
TailMerger tailMerger,
|
TailMerger tailMerger,
|
||||||
IAddressTable<ulong> funcTable,
|
AddressTable<ulong> funcTable,
|
||||||
RegisterSaveRestore registerSaveRestore,
|
RegisterSaveRestore registerSaveRestore,
|
||||||
nint dispatchStubPointer,
|
nint dispatchStubPointer,
|
||||||
nint pageTablePointer)
|
nint pageTablePointer)
|
||||||
@@ -225,7 +225,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CompiledFunction Compile(CpuPreset cpuPreset, IMemoryManager memoryManager, ulong address, IAddressTable<ulong> funcTable, nint dispatchStubPtr, bool isThumb)
|
public static CompiledFunction Compile(CpuPreset cpuPreset, IMemoryManager memoryManager, ulong address, AddressTable<ulong> funcTable, nint dispatchStubPtr, bool isThumb)
|
||||||
{
|
{
|
||||||
MultiBlock multiBlock = Decoder<InstEmit>.DecodeMulti(cpuPreset, memoryManager, address, isThumb);
|
MultiBlock multiBlock = Decoder<InstEmit>.DecodeMulti(cpuPreset, memoryManager, address, isThumb);
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ using Ryujinx.Cpu.LightningJit.CodeGen;
|
|||||||
using Ryujinx.Cpu.LightningJit.CodeGen.Arm64;
|
using Ryujinx.Cpu.LightningJit.CodeGen.Arm64;
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
@@ -133,7 +132,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
|||||||
RegisterAllocator regAlloc,
|
RegisterAllocator regAlloc,
|
||||||
TailMerger tailMerger,
|
TailMerger tailMerger,
|
||||||
Action writeEpilogue,
|
Action writeEpilogue,
|
||||||
IAddressTable<ulong> funcTable,
|
AddressTable<ulong> funcTable,
|
||||||
nint funcPtr,
|
nint funcPtr,
|
||||||
int spillBaseOffset,
|
int spillBaseOffset,
|
||||||
uint nextAddress,
|
uint nextAddress,
|
||||||
@@ -144,7 +143,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
|||||||
int tempGuestAddress = -1;
|
int tempGuestAddress = -1;
|
||||||
|
|
||||||
bool inlineLookup = guestAddress.Kind != OperandKind.Constant &&
|
bool inlineLookup = guestAddress.Kind != OperandKind.Constant &&
|
||||||
funcTable.TableType == AddressTableType.Sparse;
|
funcTable is { Sparse: true };
|
||||||
|
|
||||||
if (guestAddress.Kind == OperandKind.Constant)
|
if (guestAddress.Kind == OperandKind.Constant)
|
||||||
{
|
{
|
||||||
@@ -190,7 +189,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
|||||||
}
|
}
|
||||||
else if (inlineLookup)
|
else if (inlineLookup)
|
||||||
{
|
{
|
||||||
// Inline table lookup. Only enabled when the sparse function table is enabled with 1 level.
|
// Inline table lookup. Only enabled when the sparse function table is enabled with 2 levels.
|
||||||
|
|
||||||
Operand indexReg = Register(NextFreeRegister(tempRegister + 1, tempGuestAddress));
|
Operand indexReg = Register(NextFreeRegister(tempRegister + 1, tempGuestAddress));
|
||||||
|
|
||||||
@@ -204,15 +203,18 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
|||||||
// Index into the table.
|
// Index into the table.
|
||||||
asm.Mov(rn, tableBase);
|
asm.Mov(rn, tableBase);
|
||||||
|
|
||||||
AddressTableLevel level = funcTable.Levels.Last();
|
for (int i = 0; i < funcTable.Levels.Length; i++)
|
||||||
asm.Ubfx(indexReg, guestAddress, level.Index, level.Length);
|
{
|
||||||
asm.Lsl(indexReg, indexReg, Const(3));
|
var level = funcTable.Levels[i];
|
||||||
|
asm.Ubfx(indexReg, guestAddress, level.Index, level.Length);
|
||||||
|
asm.Lsl(indexReg, indexReg, Const(3));
|
||||||
|
|
||||||
// Index into the page.
|
// Index into the page.
|
||||||
asm.Add(rn, rn, indexReg);
|
asm.Add(rn, rn, indexReg);
|
||||||
|
|
||||||
// Load the page address.
|
// Load the page address.
|
||||||
asm.LdrRiUn(rn, rn, 0);
|
asm.LdrRiUn(rn, rn, 0);
|
||||||
|
}
|
||||||
|
|
||||||
if (tempGuestAddress != -1)
|
if (tempGuestAddress != -1)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64
|
|||||||
CpuPreset cpuPreset,
|
CpuPreset cpuPreset,
|
||||||
IMemoryManager memoryManager,
|
IMemoryManager memoryManager,
|
||||||
ulong address,
|
ulong address,
|
||||||
IAddressTable<ulong> funcTable,
|
AddressTable<ulong> funcTable,
|
||||||
nint dispatchStubPtr,
|
nint dispatchStubPtr,
|
||||||
Architecture targetArch)
|
Architecture targetArch)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
|
|||||||
public readonly CodeWriter Writer;
|
public readonly CodeWriter Writer;
|
||||||
public readonly RegisterAllocator RegisterAllocator;
|
public readonly RegisterAllocator RegisterAllocator;
|
||||||
public readonly TailMerger TailMerger;
|
public readonly TailMerger TailMerger;
|
||||||
public readonly IAddressTable<ulong> FuncTable;
|
public readonly AddressTable<ulong> FuncTable;
|
||||||
public readonly nint DispatchStubPointer;
|
public readonly nint DispatchStubPointer;
|
||||||
|
|
||||||
private readonly MultiBlock _multiBlock;
|
private readonly MultiBlock _multiBlock;
|
||||||
@@ -31,7 +31,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
|
|||||||
TailMerger tailMerger,
|
TailMerger tailMerger,
|
||||||
RegisterSaveRestore registerSaveRestore,
|
RegisterSaveRestore registerSaveRestore,
|
||||||
MultiBlock multiBlock,
|
MultiBlock multiBlock,
|
||||||
IAddressTable<ulong> funcTable,
|
AddressTable<ulong> funcTable,
|
||||||
nint dispatchStubPointer,
|
nint dispatchStubPointer,
|
||||||
nint pageTablePointer)
|
nint pageTablePointer)
|
||||||
{
|
{
|
||||||
@@ -303,7 +303,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CompiledFunction Compile(CpuPreset cpuPreset, IMemoryManager memoryManager, ulong address, IAddressTable<ulong> funcTable, nint dispatchStubPtr)
|
public static CompiledFunction Compile(CpuPreset cpuPreset, IMemoryManager memoryManager, ulong address, AddressTable<ulong> funcTable, nint dispatchStubPtr)
|
||||||
{
|
{
|
||||||
MultiBlock multiBlock = Decoder.DecodeMulti(cpuPreset, memoryManager, address);
|
MultiBlock multiBlock = Decoder.DecodeMulti(cpuPreset, memoryManager, address);
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ using Ryujinx.Cpu.LightningJit.CodeGen;
|
|||||||
using Ryujinx.Cpu.LightningJit.CodeGen.Arm64;
|
using Ryujinx.Cpu.LightningJit.CodeGen.Arm64;
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
@@ -215,7 +214,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
|
|||||||
RegisterAllocator regAlloc,
|
RegisterAllocator regAlloc,
|
||||||
TailMerger tailMerger,
|
TailMerger tailMerger,
|
||||||
Action writeEpilogue,
|
Action writeEpilogue,
|
||||||
IAddressTable<ulong> funcTable,
|
AddressTable<ulong> funcTable,
|
||||||
nint dispatchStubPtr,
|
nint dispatchStubPtr,
|
||||||
InstName name,
|
InstName name,
|
||||||
ulong pc,
|
ulong pc,
|
||||||
@@ -298,7 +297,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
|
|||||||
RegisterAllocator regAlloc,
|
RegisterAllocator regAlloc,
|
||||||
TailMerger tailMerger,
|
TailMerger tailMerger,
|
||||||
Action writeEpilogue,
|
Action writeEpilogue,
|
||||||
IAddressTable<ulong> funcTable,
|
AddressTable<ulong> funcTable,
|
||||||
nint funcPtr,
|
nint funcPtr,
|
||||||
int spillBaseOffset,
|
int spillBaseOffset,
|
||||||
ulong pc,
|
ulong pc,
|
||||||
@@ -309,7 +308,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
|
|||||||
int tempGuestAddress = -1;
|
int tempGuestAddress = -1;
|
||||||
|
|
||||||
bool inlineLookup = guestAddress.Kind != OperandKind.Constant &&
|
bool inlineLookup = guestAddress.Kind != OperandKind.Constant &&
|
||||||
funcTable.TableType == AddressTableType.Sparse;
|
funcTable is { Sparse: true };
|
||||||
|
|
||||||
if (guestAddress.Kind == OperandKind.Constant)
|
if (guestAddress.Kind == OperandKind.Constant)
|
||||||
{
|
{
|
||||||
@@ -355,7 +354,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
|
|||||||
}
|
}
|
||||||
else if (inlineLookup)
|
else if (inlineLookup)
|
||||||
{
|
{
|
||||||
// Inline table lookup. Only enabled when the sparse function table is enabled with 1 level.
|
// Inline table lookup. Only enabled when the sparse function table is enabled with 2 levels.
|
||||||
|
|
||||||
Operand indexReg = Register(NextFreeRegister(tempRegister + 1, tempGuestAddress));
|
Operand indexReg = Register(NextFreeRegister(tempRegister + 1, tempGuestAddress));
|
||||||
|
|
||||||
@@ -369,15 +368,18 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
|
|||||||
// Index into the table.
|
// Index into the table.
|
||||||
asm.Mov(rn, tableBase);
|
asm.Mov(rn, tableBase);
|
||||||
|
|
||||||
AddressTableLevel level = funcTable.Levels.Last();
|
for (int i = 0; i < funcTable.Levels.Length; i++)
|
||||||
asm.Ubfx(indexReg, guestAddress, level.Index, level.Length);
|
{
|
||||||
asm.Lsl(indexReg, indexReg, Const(3));
|
var level = funcTable.Levels[i];
|
||||||
|
asm.Ubfx(indexReg, guestAddress, level.Index, level.Length);
|
||||||
|
asm.Lsl(indexReg, indexReg, Const(3));
|
||||||
|
|
||||||
// Index into the page.
|
// Index into the page.
|
||||||
asm.Add(rn, rn, indexReg);
|
asm.Add(rn, rn, indexReg);
|
||||||
|
|
||||||
// Load the page address.
|
// Load the page address.
|
||||||
asm.LdrRiUn(rn, rn, 0);
|
asm.LdrRiUn(rn, rn, 0);
|
||||||
|
}
|
||||||
|
|
||||||
if (tempGuestAddress != -1)
|
if (tempGuestAddress != -1)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -24,30 +24,11 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly int _regionSize;
|
|
||||||
private int _regionCount;
|
|
||||||
|
|
||||||
private readonly List<MemoryBlock> _blocks = [];
|
private readonly List<MemoryBlock> _blocks = [];
|
||||||
|
|
||||||
public CacheMemoryAllocator(int regionSize, int initialRegionCount = 1)
|
public CacheMemoryAllocator(int capacity)
|
||||||
{
|
{
|
||||||
_regionCount = 0;
|
_blocks.Add(new MemoryBlock(0, capacity));
|
||||||
_regionSize = regionSize;
|
|
||||||
|
|
||||||
for (; initialRegionCount > 0; initialRegionCount--)
|
|
||||||
{
|
|
||||||
_blocks.Add(new MemoryBlock(_regionSize * _regionCount, _regionSize));
|
|
||||||
_regionCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddNewBlocks(int count)
|
|
||||||
{
|
|
||||||
for (; count > 0; count--)
|
|
||||||
{
|
|
||||||
_blocks.Add(new MemoryBlock(_regionSize * _regionCount, _regionSize));
|
|
||||||
_regionCount++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Allocate(int size)
|
public int Allocate(int size)
|
||||||
@@ -120,13 +101,12 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
index = ~index;
|
index = ~index;
|
||||||
}
|
}
|
||||||
|
|
||||||
int endOffs = block.Offset + block.Size;
|
if (index < _blocks.Count)
|
||||||
|
|
||||||
// Don't merge blocks from different allocations
|
|
||||||
if (index < _blocks.Count && endOffs % _regionSize != 0)
|
|
||||||
{
|
{
|
||||||
MemoryBlock next = _blocks[index];
|
MemoryBlock next = _blocks[index];
|
||||||
|
|
||||||
|
int endOffs = block.Offset + block.Size;
|
||||||
|
|
||||||
if (next.Offset == endOffs)
|
if (next.Offset == endOffs)
|
||||||
{
|
{
|
||||||
block = new MemoryBlock(block.Offset, block.Size + next.Size);
|
block = new MemoryBlock(block.Offset, block.Size + next.Size);
|
||||||
@@ -134,8 +114,7 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't merge blocks from different allocations
|
if (index > 0)
|
||||||
if (index > 0 && block.Offset % _regionSize != 0)
|
|
||||||
{
|
{
|
||||||
MemoryBlock prev = _blocks[index - 1];
|
MemoryBlock prev = _blocks[index - 1];
|
||||||
|
|
||||||
|
|||||||
@@ -4,50 +4,83 @@ using Ryujinx.Common.Logging;
|
|||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.LightningJit.Cache
|
namespace Ryujinx.Cpu.LightningJit.Cache
|
||||||
{
|
{
|
||||||
partial class JitCache : IDisposable
|
static partial class JitCache
|
||||||
{
|
{
|
||||||
private static readonly int _pageSize = (int)MemoryBlock.GetPageSize();
|
private static readonly int _pageSize = (int)MemoryBlock.GetPageSize();
|
||||||
private static readonly int _pageMask = _pageSize - 1;
|
private static readonly int _pageMask = _pageSize - 1;
|
||||||
|
|
||||||
private const int CodeAlignment = 4; // Bytes.
|
private const int CodeAlignment = 4; // Bytes.
|
||||||
private const uint CacheSize = 256 * 1024 * 1024; // Megabytes * Size of Megabytes (since its in bytes).
|
// TODO: JIT Cache size should be application dependent, not global.
|
||||||
|
private const int CacheSize = 1024 * (1024 * 1024); // Megabytes * Size of Megabytes (since its in bytes).
|
||||||
|
|
||||||
private readonly JitCacheInvalidation _jitCacheInvalidator;
|
private static JitCacheInvalidation _jitCacheInvalidator;
|
||||||
|
|
||||||
private readonly CacheMemoryAllocator _cacheAllocator;
|
private static CacheMemoryAllocator _cacheAllocator;
|
||||||
|
|
||||||
private readonly List<CacheEntry> _cacheEntries = [];
|
private static readonly List<CacheEntry> _cacheEntries = [];
|
||||||
|
|
||||||
private readonly Lock _lock = new();
|
private static readonly Lock _lock = new();
|
||||||
|
private static bool _initialized;
|
||||||
|
private static readonly List<ReservedRegion> _jitRegions = [];
|
||||||
|
private static int _activeRegionIndex = 0;
|
||||||
|
|
||||||
private readonly List<ReservedRegion> _jitRegions = [];
|
[SupportedOSPlatform("windows")]
|
||||||
|
[LibraryImport("kernel32.dll", SetLastError = true)]
|
||||||
|
public static partial nint FlushInstructionCache(nint hProcess, nint lpAddress, nuint dwSize);
|
||||||
|
|
||||||
public JitCache(IJitMemoryAllocator allocator)
|
[SupportedOSPlatform("macos")]
|
||||||
|
[LibraryImport("libSystem.dylib", EntryPoint = "sys_icache_invalidate")]
|
||||||
|
internal static partial void SysICacheInvalidate(nint start, nuint len);
|
||||||
|
|
||||||
|
[SupportedOSPlatform("linux")]
|
||||||
|
[LibraryImport("libgcc_s.so.1", EntryPoint = "__clear_cache")]
|
||||||
|
internal static partial void ClearCache(nint begin, nint end);
|
||||||
|
|
||||||
|
public static void Initialize(IJitMemoryAllocator allocator)
|
||||||
{
|
{
|
||||||
|
if (_initialized)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
_jitRegions.Add(new(allocator, CacheSize));
|
if (_initialized)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_cacheAllocator = new CacheMemoryAllocator((int)CacheSize);
|
var firstRegion = new ReservedRegion(allocator, CacheSize);
|
||||||
|
_jitRegions.Add(firstRegion);
|
||||||
|
_activeRegionIndex = 0;
|
||||||
|
|
||||||
if (!OperatingSystem.IsWindows() && !OperatingSystem.IsMacOS())
|
if (!OperatingSystem.IsWindows() && !OperatingSystem.IsMacOS())
|
||||||
{
|
{
|
||||||
_jitCacheInvalidator = new JitCacheInvalidation(allocator);
|
_jitCacheInvalidator = new JitCacheInvalidation(allocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_cacheAllocator = new CacheMemoryAllocator(CacheSize);
|
||||||
|
|
||||||
|
_initialized = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe nint Map(ReadOnlySpan<byte> code)
|
public unsafe static nint Map(ReadOnlySpan<byte> code)
|
||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
|
Debug.Assert(_initialized);
|
||||||
|
|
||||||
int funcOffset = Allocate(code.Length);
|
int funcOffset = Allocate(code.Length);
|
||||||
nint funcPtr = GetFunctionPtr(funcOffset);
|
ReservedRegion targetRegion = _jitRegions[_activeRegionIndex];
|
||||||
|
nint funcPtr = targetRegion.Pointer + funcOffset;
|
||||||
|
|
||||||
if (OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
if (OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
||||||
{
|
{
|
||||||
@@ -61,11 +94,18 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ReprotectAsWritable(funcOffset, code.Length);
|
ReprotectAsWritable(targetRegion, funcOffset, code.Length);
|
||||||
code.CopyTo(new Span<byte>((void*)funcPtr, code.Length));
|
code.CopyTo(new Span<byte>((void*)funcPtr, code.Length));
|
||||||
ReprotectAsExecutable(funcOffset, code.Length);
|
ReprotectAsExecutable(targetRegion, funcOffset, code.Length);
|
||||||
|
|
||||||
_jitCacheInvalidator?.Invalidate(funcPtr, (ulong)code.Length);
|
if (OperatingSystem.IsWindows() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
||||||
|
{
|
||||||
|
FlushInstructionCache(Process.GetCurrentProcess().Handle, funcPtr, (nuint)code.Length);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_jitCacheInvalidator?.Invalidate(funcPtr, (ulong)code.Length);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Add(funcOffset, code.Length);
|
Add(funcOffset, code.Length);
|
||||||
@@ -74,20 +114,21 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Unmap(nint pointer)
|
public static void Unmap(nint pointer)
|
||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < _jitRegions.Count; i++)
|
Debug.Assert(_initialized);
|
||||||
|
|
||||||
|
foreach (var region in _jitRegions)
|
||||||
{
|
{
|
||||||
ReservedRegion region = _jitRegions[i];
|
|
||||||
if (pointer.ToInt64() < region.Pointer.ToInt64() ||
|
if (pointer.ToInt64() < region.Pointer.ToInt64() ||
|
||||||
pointer.ToInt64() >= (region.Pointer + (nint)CacheSize).ToInt64())
|
pointer.ToInt64() >= (region.Pointer + CacheSize).ToInt64())
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
int funcOffset = (int)(pointer.ToInt64() - region.Pointer.ToInt64() + i * CacheSize);
|
int funcOffset = (int)(pointer.ToInt64() - region.Pointer.ToInt64());
|
||||||
|
|
||||||
if (TryFind(funcOffset, out CacheEntry entry, out int entryIndex) && entry.Offset == funcOffset)
|
if (TryFind(funcOffset, out CacheEntry entry, out int entryIndex) && entry.Offset == funcOffset)
|
||||||
{
|
{
|
||||||
@@ -100,67 +141,59 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ReprotectAsWritable(int offset, int size)
|
private static void ReprotectAsWritable(ReservedRegion region, int offset, int size)
|
||||||
{
|
{
|
||||||
int endOffs = offset + size;
|
int endOffs = offset + size;
|
||||||
int regionStart = (offset % (int)CacheSize) & ~_pageMask;
|
int regionStart = offset & ~_pageMask;
|
||||||
int regionEnd = endOffs % (int)CacheSize == 0
|
int regionEnd = (endOffs + _pageMask) & ~_pageMask;
|
||||||
? (((int)CacheSize) + _pageMask) & ~_pageMask
|
|
||||||
: ((endOffs % (int)CacheSize) + _pageMask) & ~_pageMask;
|
|
||||||
|
|
||||||
GetRegion(offset).Block.MapAsRwx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
region.Block.MapAsRwx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ReprotectAsExecutable(int offset, int size)
|
private static void ReprotectAsExecutable(ReservedRegion region, int offset, int size)
|
||||||
{
|
{
|
||||||
int endOffs = offset + size;
|
int endOffs = offset + size;
|
||||||
int regionStart = (offset % (int)CacheSize) & ~_pageMask;
|
int regionStart = offset & ~_pageMask;
|
||||||
int regionEnd = endOffs % (int)CacheSize == 0
|
int regionEnd = (endOffs + _pageMask) & ~_pageMask;
|
||||||
? (((int)CacheSize) + _pageMask) & ~_pageMask
|
|
||||||
: ((endOffs % (int)CacheSize) + _pageMask) & ~_pageMask;
|
|
||||||
|
|
||||||
GetRegion(offset).Block.MapAsRx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
region.Block.MapAsRx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
||||||
}
|
}
|
||||||
|
|
||||||
private int Allocate(int codeSize)
|
private static int Allocate(int codeSize)
|
||||||
{
|
{
|
||||||
codeSize = AlignCodeSize(codeSize);
|
codeSize = AlignCodeSize(codeSize);
|
||||||
|
|
||||||
int allocOffset = _cacheAllocator.Allocate(codeSize);
|
for (int i = _activeRegionIndex; i < _jitRegions.Count; i++)
|
||||||
|
|
||||||
if (allocOffset >= 0)
|
|
||||||
{
|
{
|
||||||
GetRegion(allocOffset).ExpandIfNeeded((ulong)(allocOffset % (int)CacheSize) + (ulong)codeSize);
|
int allocOffset = _cacheAllocator.Allocate(codeSize);
|
||||||
return allocOffset;
|
|
||||||
|
if (allocOffset >= 0)
|
||||||
|
{
|
||||||
|
_jitRegions[i].ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize);
|
||||||
|
_activeRegionIndex = i;
|
||||||
|
return allocOffset;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_cacheAllocator.AddNewBlocks(1);
|
int exhaustedRegion = _activeRegionIndex;
|
||||||
ReservedRegion newRegion = new(_jitRegions[0].Allocator, CacheSize);
|
var newRegion = new ReservedRegion(_jitRegions[0].Allocator, CacheSize);
|
||||||
|
|
||||||
Logger.Warning?.Print(LogClass.Cpu, $"JIT Cache of size {(_jitRegions.Count * CacheSize).Bytes()} exhausted, creating new Cache Region ({((_jitRegions.Count + 1) * CacheSize).Bytes()} Total Allocation).");
|
|
||||||
|
|
||||||
_jitRegions.Add(newRegion);
|
_jitRegions.Add(newRegion);
|
||||||
|
_activeRegionIndex = _jitRegions.Count - 1;
|
||||||
|
|
||||||
allocOffset = _cacheAllocator.Allocate(codeSize);
|
int newRegionNumber = _activeRegionIndex;
|
||||||
if (allocOffset < 0)
|
|
||||||
|
Logger.Warning?.Print(LogClass.Cpu, $"JIT Cache Region {exhaustedRegion} exhausted, creating new Cache Region {newRegionNumber} ({((long)(newRegionNumber + 1) * CacheSize).Bytes()} Total Allocation).");
|
||||||
|
|
||||||
|
_cacheAllocator = new CacheMemoryAllocator(CacheSize);
|
||||||
|
|
||||||
|
int allocOffsetNew = _cacheAllocator.Allocate(codeSize);
|
||||||
|
if (allocOffsetNew < 0)
|
||||||
{
|
{
|
||||||
throw new OutOfMemoryException("Failed to allocate in new Cache Region!");
|
throw new OutOfMemoryException("Failed to allocate in new Cache Region!");
|
||||||
}
|
}
|
||||||
|
|
||||||
GetRegion(allocOffset).ExpandIfNeeded((ulong)(allocOffset % (int)CacheSize) + (ulong)codeSize);
|
newRegion.ExpandIfNeeded((ulong)allocOffsetNew + (ulong)codeSize);
|
||||||
return allocOffset;
|
return allocOffsetNew;
|
||||||
}
|
|
||||||
|
|
||||||
private nint GetFunctionPtr(int offset)
|
|
||||||
{
|
|
||||||
return GetRegion(offset).Pointer + (offset % (int)CacheSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ReservedRegion GetRegion(int offset)
|
|
||||||
{
|
|
||||||
int index = offset / (int)CacheSize;
|
|
||||||
|
|
||||||
return _jitRegions[index];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int AlignCodeSize(int codeSize)
|
private static int AlignCodeSize(int codeSize)
|
||||||
@@ -168,7 +201,7 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
return checked(codeSize + (CodeAlignment - 1)) & ~(CodeAlignment - 1);
|
return checked(codeSize + (CodeAlignment - 1)) & ~(CodeAlignment - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Add(int offset, int size)
|
private static void Add(int offset, int size)
|
||||||
{
|
{
|
||||||
CacheEntry entry = new(offset, size);
|
CacheEntry entry = new(offset, size);
|
||||||
|
|
||||||
@@ -182,7 +215,7 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
_cacheEntries.Insert(index, entry);
|
_cacheEntries.Insert(index, entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryFind(int offset, out CacheEntry entry, out int entryIndex)
|
public static bool TryFind(int offset, out CacheEntry entry, out int entryIndex)
|
||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
@@ -205,13 +238,5 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
entryIndex = 0;
|
entryIndex = 0;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
foreach (ReservedRegion jitRegion in _jitRegions)
|
|
||||||
{
|
|
||||||
jitRegion.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -190,7 +190,7 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
|
|
||||||
private bool TryGetThreadLocalFunction(ulong guestAddress, out nint funcPtr)
|
private bool TryGetThreadLocalFunction(ulong guestAddress, out nint funcPtr)
|
||||||
{
|
{
|
||||||
if ((_threadLocalCache ??= new()).TryGetValue(guestAddress, out ThreadLocalCacheEntry entry))
|
if ((_threadLocalCache ??= new()).TryGetValue(guestAddress, out var entry))
|
||||||
{
|
{
|
||||||
if (entry.IncrementUseCount() >= MinCallsForPad)
|
if (entry.IncrementUseCount() >= MinCallsForPad)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ namespace Ryujinx.Cpu.LightningJit.CodeGen.Arm64
|
|||||||
{
|
{
|
||||||
int targetIndex = _code.Count;
|
int targetIndex = _code.Count;
|
||||||
|
|
||||||
LabelState state = _labels[label.AsInt32()];
|
var state = _labels[label.AsInt32()];
|
||||||
|
|
||||||
state.TargetIndex = targetIndex;
|
state.TargetIndex = targetIndex;
|
||||||
state.HasTarget = true;
|
state.HasTarget = true;
|
||||||
@@ -68,7 +68,7 @@ namespace Ryujinx.Cpu.LightningJit.CodeGen.Arm64
|
|||||||
{
|
{
|
||||||
int branchIndex = _code.Count;
|
int branchIndex = _code.Count;
|
||||||
|
|
||||||
LabelState state = _labels[label.AsInt32()];
|
var state = _labels[label.AsInt32()];
|
||||||
|
|
||||||
state.BranchIndex = branchIndex;
|
state.BranchIndex = branchIndex;
|
||||||
state.HasBranch = true;
|
state.HasBranch = true;
|
||||||
@@ -94,7 +94,7 @@ namespace Ryujinx.Cpu.LightningJit.CodeGen.Arm64
|
|||||||
{
|
{
|
||||||
int branchIndex = _code.Count;
|
int branchIndex = _code.Count;
|
||||||
|
|
||||||
LabelState state = _labels[label.AsInt32()];
|
var state = _labels[label.AsInt32()];
|
||||||
|
|
||||||
state.BranchIndex = branchIndex;
|
state.BranchIndex = branchIndex;
|
||||||
state.HasBranch = true;
|
state.HasBranch = true;
|
||||||
@@ -113,7 +113,7 @@ namespace Ryujinx.Cpu.LightningJit.CodeGen.Arm64
|
|||||||
{
|
{
|
||||||
int branchIndex = _code.Count;
|
int branchIndex = _code.Count;
|
||||||
|
|
||||||
LabelState state = _labels[label.AsInt32()];
|
var state = _labels[label.AsInt32()];
|
||||||
|
|
||||||
state.BranchIndex = branchIndex;
|
state.BranchIndex = branchIndex;
|
||||||
state.HasBranch = true;
|
state.HasBranch = true;
|
||||||
@@ -342,7 +342,7 @@ namespace Ryujinx.Cpu.LightningJit.CodeGen.Arm64
|
|||||||
|
|
||||||
public readonly void Cset(Operand rd, ArmCondition condition)
|
public readonly void Cset(Operand rd, ArmCondition condition)
|
||||||
{
|
{
|
||||||
Operand zr = new(ZrRegister, RegisterType.Integer, rd.Type);
|
var zr = new Operand(ZrRegister, RegisterType.Integer, rd.Type);
|
||||||
Csinc(rd, zr, zr, (ArmCondition)((int)condition ^ 1));
|
Csinc(rd, zr, zr, (ArmCondition)((int)condition ^ 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using ARMeilleure.Common;
|
using ARMeilleure.Common;
|
||||||
using ARMeilleure.Memory;
|
using ARMeilleure.Memory;
|
||||||
using ARMeilleure.Translation.PTC;
|
|
||||||
using Ryujinx.Cpu.Jit;
|
using Ryujinx.Cpu.Jit;
|
||||||
using Ryujinx.Cpu.LightningJit.State;
|
using Ryujinx.Cpu.LightningJit.State;
|
||||||
|
|
||||||
@@ -10,15 +9,15 @@ namespace Ryujinx.Cpu.LightningJit
|
|||||||
{
|
{
|
||||||
private readonly ITickSource _tickSource;
|
private readonly ITickSource _tickSource;
|
||||||
private readonly Translator _translator;
|
private readonly Translator _translator;
|
||||||
|
private readonly AddressTable<ulong> _functionTable;
|
||||||
|
|
||||||
public LightningJitCpuContext(ITickSource tickSource, IMemoryManager memory, bool for64Bit)
|
public LightningJitCpuContext(ITickSource tickSource, IMemoryManager memory, bool for64Bit)
|
||||||
{
|
{
|
||||||
_tickSource = tickSource;
|
_tickSource = tickSource;
|
||||||
|
|
||||||
bool sparse = memory.Type is not MemoryManagerType.SoftwareMmu and not MemoryManagerType.SoftwarePageTable;
|
_functionTable = AddressTable<ulong>.CreateForArm(for64Bit, memory.Type);
|
||||||
IAddressTable<ulong> functionTable = sparse ? SparseAddressTable<ulong>.CreateForArm(for64Bit) : AddressTable<ulong>.CreateForArm(for64Bit);
|
|
||||||
|
|
||||||
_translator = new Translator(memory, functionTable);
|
_translator = new Translator(memory, _functionTable);
|
||||||
|
|
||||||
memory.UnmapEvent += UnmapHandler;
|
memory.UnmapEvent += UnmapHandler;
|
||||||
}
|
}
|
||||||
@@ -47,7 +46,7 @@ namespace Ryujinx.Cpu.LightningJit
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public IDiskCacheLoadState LoadDiskCache(PtcCacheInfo cacheInfo, bool enabled)
|
public IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled, string cacheSelector)
|
||||||
{
|
{
|
||||||
return new DummyDiskCacheLoadState();
|
return new DummyDiskCacheLoadState();
|
||||||
}
|
}
|
||||||
@@ -55,7 +54,7 @@ namespace Ryujinx.Cpu.LightningJit
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void PrepareCodeRange(ulong address, ulong size)
|
public void PrepareCodeRange(ulong address, ulong size)
|
||||||
{
|
{
|
||||||
_translator.FunctionTable.SignalCodeRange(address, size);
|
_functionTable.SignalCodeRange(address, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user