mirror of
https://git.ryujinx.app/projects/Kenji-NX.git
synced 2026-10-04 20:41:04 +02:00
Compare commits
69
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2e6e4076bf | ||
|
|
ab7176e6fc | ||
|
|
20868546b0 | ||
|
|
892fcd9444 | ||
|
|
70cc617e31 | ||
|
|
9bf15801fd | ||
|
|
e3150d20bb | ||
|
|
50538d601f | ||
|
|
5d3e392082 | ||
|
|
c57e91cc59 | ||
|
|
0381c7157b | ||
|
|
93b4c53c8a | ||
|
|
ef14467d1f | ||
|
|
e07f333a31 | ||
|
|
fc4c45ef1d | ||
|
|
52c6a1890e | ||
|
|
2288084b53 | ||
|
|
dd519a32b6 | ||
|
|
7be8829b0a | ||
|
|
6225e142fc | ||
|
|
9e3c03a606 | ||
|
|
8e9bcb2a8e | ||
|
|
7101f52c01 | ||
|
|
e80fc00462 | ||
|
|
33cbd29c23 | ||
|
|
cfc7c6039f | ||
|
|
ac6db0fe76 | ||
|
|
bed294bb92 | ||
|
|
ed70f7921e | ||
|
|
1a1fe53535 | ||
|
|
47e55acf21 | ||
|
|
0e5fead0f9 | ||
|
|
d1fb89f746 | ||
|
|
cc1861f9bb | ||
|
|
733bd0951b | ||
|
|
087e711e44 | ||
|
|
9c46452388 | ||
|
|
0657b00622 | ||
|
|
87cf0b0cbb | ||
|
|
954332aa2a | ||
|
|
e650d3fb04 | ||
|
|
d578c5e2a9 | ||
|
|
521a18f5a7 | ||
|
|
4b255a98db | ||
|
|
8b34270a22 | ||
|
|
9580f20caa | ||
|
|
152460929d | ||
|
|
7a5554a17f | ||
|
|
d5e4dfae56 | ||
|
|
28268ef7ef | ||
|
|
6c47892b94 | ||
|
|
b362f837c5 | ||
|
|
a82a721df6 | ||
|
|
297df259c3 | ||
|
|
3ace5deadf | ||
|
|
04aacb1385 | ||
|
|
ea91841e9b | ||
|
|
5734ad9090 | ||
|
|
1486d2db40 | ||
|
|
c54ca28843 | ||
|
|
a53e691185 | ||
|
|
bf8c902d13 | ||
|
|
f4dc400a47 | ||
|
|
a36882ab51 | ||
|
|
76a123b81d | ||
|
|
9d3fecf334 | ||
|
|
31d0bc5915 | ||
|
|
30dba1ed36 | ||
|
|
0d6c0aeeab |
+18
-19
@@ -3,25 +3,25 @@
|
|||||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageVersion Include="Avalonia" Version="11.3.16" />
|
<PackageVersion Include="Avalonia" Version="12.1.2" />
|
||||||
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.13" />
|
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="12.1.2" />
|
||||||
<PackageVersion Include="Avalonia.Desktop" Version="11.3.16" />
|
<PackageVersion Include="Avalonia.Desktop" Version="12.1.2" />
|
||||||
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.16" />
|
<PackageVersion Include="AvaloniaUI.DiagnosticsSupport" Version="2.2.3" />
|
||||||
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.3.16" />
|
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="12.1.2" />
|
||||||
<PackageVersion Include="Svg.Controls.Avalonia" Version="11.3.9.5" />
|
<PackageVersion Include="Svg.Controls.Avalonia" Version="12.0.0.17" />
|
||||||
<PackageVersion Include="Svg.Controls.Skia.Avalonia" Version="11.3.9.5" />
|
<PackageVersion Include="Svg.Controls.Skia.Avalonia" Version="12.0.0.17" />
|
||||||
<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.31" />
|
<PackageVersion Include="DynamicData" Version="9.4.33" />
|
||||||
<PackageVersion Include="FluentAvaloniaUI" Version="2.5.1" />
|
<PackageVersion Include="FluentAvaloniaUI" Version="3.1.0" />
|
||||||
<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.18.0" />
|
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.22.0" />
|
||||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.5.1" />
|
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.10.0" />
|
||||||
<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,25 +31,24 @@
|
|||||||
<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.1" />
|
<PackageVersion Include="Ryujinx.Audio.OpenAL" Version="1.25.2" />
|
||||||
<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.2-ryujinx.3" />
|
<PackageVersion Include="Ryujinx.Graphics.Vulkan.MoltenVK" Version="1.4.3-ryujinx.1" />
|
||||||
<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.501.0" />
|
<PackageVersion Include="Ryujinx.SDL3-CS" Version="2026.707.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.2" />
|
<PackageVersion Include="SkiaSharp" Version="3.119.4" />
|
||||||
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="3.119.2" />
|
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="3.119.4" />
|
||||||
<PackageVersion Include="SPB" Version="0.0.4-build32" />
|
<PackageVersion Include="SPB" Version="0.0.4-build32" />
|
||||||
<PackageVersion Include="System.IO.Hashing" Version="10.0.8" />
|
<PackageVersion Include="System.IO.Hashing" Version="10.0.12" />
|
||||||
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.1.4-a40db6c" />
|
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.1.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
@@ -21,6 +21,7 @@
|
|||||||
<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)
|
||||||
{
|
{
|
||||||
var constants = new Dictionary<ulong, Operand>();
|
Dictionary<ulong, Operand> constants = new();
|
||||||
|
|
||||||
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 var constant) || constant.UsesCount > MaxConstantUses)
|
if (!constants.TryGetValue(source.Value, out Operand 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)
|
||||||
{
|
{
|
||||||
var zr = Factory.Register(ZrRegister, RegisterType.Integer, rd.Type);
|
Operand 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 var list))
|
if (_pendingBranches.TryGetValue(block, out List<(ArmCondition Condition, long BranchPos)> 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 var list))
|
if (!_pendingBranches.TryGetValue(target, out List<(ArmCondition Condition, long BranchPos)> 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);
|
||||||
|
|
||||||
var cond = ((Comparison)comp.AsInt32()).ToArmCondition();
|
ArmCondition 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);
|
||||||
|
|
||||||
var cond = ((Comparison)comp.AsInt32()).ToArmCondition();
|
ArmCondition 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);
|
||||||
|
|
||||||
var compType = (Comparison)comp.AsInt32();
|
Comparison 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>
|
||||||
readonly struct CompiledFunction
|
public readonly struct CompiledFunction
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the machine code of the <see cref="CompiledFunction"/>.
|
/// Gets the machine code of the <see cref="CompiledFunction"/>.
|
||||||
@@ -44,10 +44,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>
|
||||||
/// <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>()
|
public T Map<T>(JitCache jitCache)
|
||||||
{
|
{
|
||||||
return MapWithPointer<T>(out _);
|
return MapWithPointer<T>(jitCache, out _);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -55,11 +56,12 @@ 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>(out nint codePointer)
|
public T MapWithPointer<T>(JitCache jitCache, 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>
|
||||||
readonly struct RelocEntry
|
public 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>
|
||||||
readonly struct RelocInfo
|
public 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>
|
||||||
readonly struct Symbol
|
public 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>
|
||||||
enum SymbolType : byte
|
public 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);
|
||||||
|
|
||||||
var context = new AllocationContext(stackAlloc, regMasks, _intervals.Count);
|
AllocationContext context = new(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)
|
||||||
{
|
{
|
||||||
var oldSpan = Span;
|
Span<LiveInterval> 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);
|
||||||
|
|
||||||
var newSpan = Span;
|
Span<LiveInterval> newSpan = Span;
|
||||||
|
|
||||||
oldSpan.CopyTo(newSpan);
|
oldSpan.CopyTo(newSpan);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,12 +16,12 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
|||||||
{
|
{
|
||||||
if (Count + 1 > _capacity)
|
if (Count + 1 > _capacity)
|
||||||
{
|
{
|
||||||
var oldSpan = Span;
|
Span<int> 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);
|
||||||
|
|
||||||
var newSpan = Span;
|
Span<int> newSpan = Span;
|
||||||
|
|
||||||
oldSpan.CopyTo(newSpan);
|
oldSpan.CopyTo(newSpan);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
namespace ARMeilleure.CodeGen.Unwinding
|
namespace ARMeilleure.CodeGen.Unwinding
|
||||||
{
|
{
|
||||||
struct UnwindInfo
|
public 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
|
||||||
{
|
{
|
||||||
enum UnwindPseudoOp
|
public enum UnwindPseudoOp
|
||||||
{
|
{
|
||||||
PushReg = 0,
|
PushReg = 0,
|
||||||
SetFrame = 1,
|
SetFrame = 1,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
namespace ARMeilleure.CodeGen.Unwinding
|
namespace ARMeilleure.CodeGen.Unwinding
|
||||||
{
|
{
|
||||||
struct UnwindPushEntry
|
public struct UnwindPushEntry
|
||||||
{
|
{
|
||||||
public const int Stride = 16; // Bytes.
|
public const int Stride = 16; // Bytes.
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
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;
|
||||||
@@ -1324,8 +1325,8 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
|
|
||||||
public (byte[], RelocInfo) GetCode()
|
public (byte[], RelocInfo) GetCode()
|
||||||
{
|
{
|
||||||
var jumps = CollectionsMarshal.AsSpan(_jumps);
|
Span<Jump> jumps = CollectionsMarshal.AsSpan(_jumps);
|
||||||
var relocs = CollectionsMarshal.AsSpan(_relocs);
|
Span<Reloc> relocs = CollectionsMarshal.AsSpan(_relocs);
|
||||||
|
|
||||||
// Write jump relative offsets.
|
// Write jump relative offsets.
|
||||||
bool modified;
|
bool modified;
|
||||||
@@ -1410,13 +1411,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 var codeStream = MemoryStreamManager.Shared.GetStream();
|
using RecyclableMemoryStream codeStream = MemoryStreamManager.Shared.GetStream();
|
||||||
var assembler = new Assembler(codeStream, HasRelocs);
|
Assembler assembler = new(codeStream, HasRelocs);
|
||||||
|
|
||||||
bool hasRelocs = HasRelocs;
|
bool hasRelocs = HasRelocs;
|
||||||
int relocIndex = 0;
|
int relocIndex = 0;
|
||||||
int relocOffset = 0;
|
int relocOffset = 0;
|
||||||
var relocEntries = hasRelocs
|
RelocEntry[] relocEntries = hasRelocs
|
||||||
? new RelocEntry[relocs.Length]
|
? new RelocEntry[relocs.Length]
|
||||||
: Array.Empty<RelocEntry>();
|
: Array.Empty<RelocEntry>();
|
||||||
|
|
||||||
@@ -1469,8 +1470,8 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
|
|
||||||
_stream.CopyTo(codeStream);
|
_stream.CopyTo(codeStream);
|
||||||
|
|
||||||
var code = codeStream.ToArray();
|
byte[] code = codeStream.ToArray();
|
||||||
var relocInfo = new RelocInfo(relocEntries);
|
RelocInfo relocInfo = new(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);
|
||||||
|
|
||||||
var cond = ((Comparison)comp.AsInt32()).ToX86Condition();
|
X86Condition 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);
|
||||||
|
|
||||||
var cond = ((Comparison)comp.AsInt32()).ToX86Condition();
|
X86Condition 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);
|
||||||
|
|
||||||
var fGetXcr0 = Marshal.GetDelegateForFunctionPointer<GetXcr0>(memGetXcr0.Pointer);
|
GetXcr0 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);
|
||||||
|
|
||||||
var compType = (Comparison)comp.AsInt32();
|
Comparison 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)
|
||||||
{
|
{
|
||||||
var constants = new Dictionary<ulong, Operand>();
|
Dictionary<ulong, Operand> constants = new();
|
||||||
|
|
||||||
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 var constant) || constant.UsesCount > MaxConstantUses)
|
if (!constants.TryGetValue(source.Value, out Operand constant) || constant.UsesCount > MaxConstantUses)
|
||||||
{
|
{
|
||||||
constant = Local(source.Type);
|
constant = Local(source.Type);
|
||||||
|
|
||||||
|
|||||||
@@ -20,45 +20,21 @@ namespace ARMeilleure.Common
|
|||||||
new( 1, 6)
|
new( 1, 6)
|
||||||
];
|
];
|
||||||
|
|
||||||
private static readonly AddressTableLevel[] _levels64BitSparseTiny =
|
private static readonly AddressTableLevel[] _levels64BitMono =
|
||||||
[
|
[
|
||||||
new( 11, 28),
|
new( 2, 37)
|
||||||
new( 2, 9)
|
|
||||||
];
|
];
|
||||||
|
|
||||||
private static readonly AddressTableLevel[] _levels32BitSparseTiny =
|
private static readonly AddressTableLevel[] _levels32BitMono =
|
||||||
[
|
[
|
||||||
new( 10, 22),
|
new( 1, 31)
|
||||||
new( 1, 9)
|
|
||||||
];
|
];
|
||||||
|
|
||||||
private static readonly AddressTableLevel[] _levels64BitSparseGiant =
|
public static AddressTableLevel[] GetArmPreset(bool for64Bits, bool mono)
|
||||||
[
|
|
||||||
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 (sparse)
|
if (mono)
|
||||||
{
|
{
|
||||||
if (lowPower)
|
return for64Bits ? _levels64BitMono : _levels32BitMono;
|
||||||
{
|
|
||||||
return for64Bits ? _levels64BitSparseTiny : _levels32BitSparseTiny;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return for64Bits ? _levels64BitSparseGiant : _levels32BitSparseGiant;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
namespace ARMeilleure.Common
|
||||||
|
{
|
||||||
|
public enum AddressTableType
|
||||||
|
{
|
||||||
|
Default,
|
||||||
|
Sparse
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -129,13 +129,13 @@ namespace ARMeilleure.Common
|
|||||||
|
|
||||||
if (count > _count)
|
if (count > _count)
|
||||||
{
|
{
|
||||||
var oldMask = _masks;
|
long* oldMask = _masks;
|
||||||
var oldSpan = new Span<long>(_masks, _count);
|
Span<long> oldSpan = new(_masks, _count);
|
||||||
|
|
||||||
_masks = _allocator.Allocate<long>((uint)count);
|
_masks = _allocator.Allocate<long>((uint)count);
|
||||||
_count = count;
|
_count = count;
|
||||||
|
|
||||||
var newSpan = new Span<long>(_masks, _count);
|
Span<long> newSpan = new(_masks, _count);
|
||||||
|
|
||||||
oldSpan.CopyTo(newSpan);
|
oldSpan.CopyTo(newSpan);
|
||||||
newSpan[oldSpan.Length..].Clear();
|
newSpan[oldSpan.Length..].Clear();
|
||||||
|
|||||||
@@ -53,5 +53,14 @@ 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++;
|
||||||
var page = GetPage(index);
|
Span<TEntry> 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));
|
||||||
}
|
}
|
||||||
|
|
||||||
var page = GetPage(index);
|
Span<TEntry> 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)
|
||||||
{
|
{
|
||||||
var pageIndex = (int)((uint)(index & ~(_pageCapacity - 1)) >> _pageLogCapacity);
|
int 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 (var page in _pages.Values)
|
foreach (IntPtr page in _pages.Values)
|
||||||
{
|
{
|
||||||
NativeAllocator.Instance.Free((void*)page);
|
NativeAllocator.Instance.Free((void*)page);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,10 +5,9 @@ namespace ARMeilleure.Common
|
|||||||
public interface IAddressTable<TEntry> : IDisposable where TEntry : unmanaged
|
public interface IAddressTable<TEntry> : IDisposable where TEntry : unmanaged
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// True if the address table's bottom level is sparsely mapped.
|
/// Gets the <see cref="AddressTableType"/> of the <see cref="IAddressTable{TEntry}"/> instance.
|
||||||
/// This also ensures the second bottom level is filled with a dummy page rather than 0.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
bool Sparse { get; }
|
AddressTableType TableType { 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.
|
||||||
@@ -31,6 +30,13 @@ 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)
|
||||||
{
|
{
|
||||||
var opc = (opCode >> 16) & 0xf;
|
int 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;
|
||||||
|
|
||||||
var opc = (((opCode >> 23) & 1) << 4) | (((opCode >> 21) & 0x3) << 2) | ((opCode >> 5) & 0x3);
|
int 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)
|
||||||
{
|
{
|
||||||
var bm = DecoderHelper.DecodeBitMask(opCode, true);
|
DecoderHelper.BitMask 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)
|
||||||
{
|
{
|
||||||
var bm = DecoderHelper.DecodeBitMask(opCode, false);
|
DecoderHelper.BitMask bm = DecoderHelper.DecodeBitMask(opCode, false);
|
||||||
|
|
||||||
if (bm.IsUndefined)
|
if (bm.IsUndefined)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ namespace ARMeilleure.Decoders.Optimizations
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var newBlocks = new List<Block>(blocks.Count);
|
List<Block> newBlocks = new(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:
|
||||||
var memOp = operand.GetMemory();
|
MemoryOperand 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)
|
||||||
{
|
{
|
||||||
var dumper = new IRDumper(1);
|
IRDumper dumper = new(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;
|
||||||
|
|
||||||
var msb = op.Lsb + op.Msb; // For this instruction, the msb is actually a width.
|
int 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;
|
||||||
|
|
||||||
var msb = op.Lsb + op.Msb; // For this instruction, the msb is actually a width.
|
int 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,10 +1,11 @@
|
|||||||
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;
|
||||||
|
|
||||||
@@ -223,7 +224,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
Operand hostAddress;
|
Operand hostAddress;
|
||||||
|
|
||||||
var table = context.FunctionTable;
|
IAddressTable<ulong> 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.
|
||||||
@@ -235,9 +236,9 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
hostAddress = context.Load(OperandType.I64, hostAddressAddr);
|
hostAddress = context.Load(OperandType.I64, hostAddressAddr);
|
||||||
}
|
}
|
||||||
else if (table.Sparse)
|
else if (table.TableType == AddressTableType.Sparse)
|
||||||
{
|
{
|
||||||
// Inline table lookup. Only enabled when the sparse function table is enabled with 2 levels.
|
// Inline table lookup. Only enabled when the sparse function table is enabled with 1 level.
|
||||||
// Deliberately attempts to avoid branches.
|
// Deliberately attempts to avoid branches.
|
||||||
|
|
||||||
Operand tableBase = !context.HasPtc ?
|
Operand tableBase = !context.HasPtc ?
|
||||||
@@ -246,24 +247,21 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
hostAddress = tableBase;
|
hostAddress = tableBase;
|
||||||
|
|
||||||
for (int i = 0; i < table.Levels.Length; i++)
|
AddressTableLevel level = table.Levels.Last();
|
||||||
{
|
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((long)context.Stubs.DispatchStub) :
|
Const(context.Stubs.DispatchStub) :
|
||||||
Const((long)context.Stubs.DispatchStub, Ptc.DispatchStubSymbol);
|
Const(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));
|
||||||
|
|
||||||
var exclusive = (accType & AccessType.Exclusive) != 0;
|
bool exclusive = (accType & AccessType.Exclusive) != 0;
|
||||||
var ordered = (accType & AccessType.Ordered) != 0;
|
bool 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)
|
||||||
{
|
{
|
||||||
var ones = (baseType == OperandType.I64) ? Const(-1L) : Const(-1);
|
Operand 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;
|
||||||
|
|
||||||
var toFixed = op.Opc == 1;
|
bool toFixed = op.Opc == 1;
|
||||||
int fracBits = op.Fbits;
|
int fracBits = op.Fbits;
|
||||||
var unsigned = op.U;
|
bool 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) =>
|
||||||
{
|
{
|
||||||
var scaledValue = context.Multiply(op1, ConstF(MathF.Pow(2f, fracBits)));
|
Operand 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) =>
|
||||||
{
|
{
|
||||||
var floatValue = unsigned ? context.ConvertToFPUI(OperandType.FP32, op1) : context.ConvertToFP(OperandType.FP32, op1);
|
Operand 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)
|
||||||
{
|
{
|
||||||
var regs = (count > 1) ? 1 : op.Increment;
|
int 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))
|
||||||
{
|
{
|
||||||
var overflowToInf = fpcr.GetRoundingMode() switch
|
bool 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))
|
||||||
{
|
{
|
||||||
var overflowToInf = fpcr.GetRoundingMode() switch
|
bool 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);
|
||||||
|
|
||||||
var oldSpan = new Span<T>(data, count);
|
Span<T> oldSpan = new(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();
|
||||||
}
|
}
|
||||||
|
|
||||||
var oldSpan = new Span<T>(data, (int)count);
|
Span<T> oldSpan = new(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
|
||||||
{
|
{
|
||||||
var span = new Span<T>(data, count);
|
Span<T> span = new(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
|
||||||
{
|
{
|
||||||
var span = new Span<T>(data, (int)count);
|
Span<T> span = new(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
|
public class ReservedRegion : IDisposable
|
||||||
{
|
{
|
||||||
public const int DefaultGranularity = 65536; // Mapping granularity in Windows.
|
public const int DefaultGranularity = 65536; // Mapping granularity in Windows.
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
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;
|
||||||
|
|
||||||
@@ -17,11 +18,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()
|
public static DebugPartialUnmap GenerateDebugPartialUnmap(JitCache jitCache)
|
||||||
{
|
{
|
||||||
EmitterContext context = new();
|
EmitterContext context = new();
|
||||||
|
|
||||||
var result = WindowsPartialUnmapHandler.EmitRetryFromAccessViolation(context);
|
Operand result = WindowsPartialUnmapHandler.EmitRetryFromAccessViolation(context);
|
||||||
|
|
||||||
context.Return(result);
|
context.Return(result);
|
||||||
|
|
||||||
@@ -31,14 +32,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>();
|
return Compiler.Compile(cfg, argTypes, OperandType.I32, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<DebugPartialUnmap>(jitCache);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static DebugThreadLocalMapGetOrReserve GenerateDebugThreadLocalMapGetOrReserve(nint structPtr)
|
public static DebugThreadLocalMapGetOrReserve GenerateDebugThreadLocalMapGetOrReserve(JitCache jitCache, nint structPtr)
|
||||||
{
|
{
|
||||||
EmitterContext context = new();
|
EmitterContext context = new();
|
||||||
|
|
||||||
var result = WindowsPartialUnmapHandler.EmitThreadLocalMapIntGetOrReserve(context, structPtr, context.LoadArgument(OperandType.I32, 0), context.LoadArgument(OperandType.I32, 1));
|
Operand result = WindowsPartialUnmapHandler.EmitThreadLocalMapIntGetOrReserve(context, structPtr, context.LoadArgument(OperandType.I32, 0), context.LoadArgument(OperandType.I32, 1));
|
||||||
|
|
||||||
context.Return(result);
|
context.Return(result);
|
||||||
|
|
||||||
@@ -48,10 +49,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>();
|
return Compiler.Compile(cfg, argTypes, OperandType.I32, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<DebugThreadLocalMapGetOrReserve>(jitCache);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static DebugNativeWriteLoop GenerateDebugNativeWriteLoop()
|
public static DebugNativeWriteLoop GenerateDebugNativeWriteLoop(JitCache jitCache)
|
||||||
{
|
{
|
||||||
EmitterContext context = new();
|
EmitterContext context = new();
|
||||||
|
|
||||||
@@ -77,7 +78,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>();
|
return Compiler.Compile(cfg, argTypes, OperandType.None, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<DebugNativeWriteLoop>(jitCache);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ using System.Diagnostics.CodeAnalysis;
|
|||||||
|
|
||||||
namespace ARMeilleure.Translation.Cache
|
namespace ARMeilleure.Translation.Cache
|
||||||
{
|
{
|
||||||
readonly struct CacheEntry : IComparable<CacheEntry>
|
public readonly struct CacheEntry : IComparable<CacheEntry>
|
||||||
{
|
{
|
||||||
public int Offset { get; }
|
public int Offset { get; }
|
||||||
public int Size { get; }
|
public int Size { get; }
|
||||||
|
|||||||
@@ -23,11 +23,30 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private readonly int _regionSize;
|
||||||
|
private int _regionCount;
|
||||||
|
|
||||||
private readonly List<MemoryBlock> _blocks = [];
|
private readonly List<MemoryBlock> _blocks = [];
|
||||||
|
|
||||||
public CacheMemoryAllocator(int capacity)
|
public CacheMemoryAllocator(int regionSize, int initialRegionCount = 1)
|
||||||
{
|
{
|
||||||
_blocks.Add(new MemoryBlock(0, capacity));
|
_regionCount = 0;
|
||||||
|
_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)
|
||||||
@@ -66,12 +85,13 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
index = ~index;
|
index = ~index;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (index < _blocks.Count)
|
int endOffs = block.Offset + block.Size;
|
||||||
|
|
||||||
|
// 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);
|
||||||
@@ -79,7 +99,8 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (index > 0)
|
// Don't merge blocks from different allocations
|
||||||
|
if (index > 0 && block.Offset % _regionSize != 0)
|
||||||
{
|
{
|
||||||
MemoryBlock prev = _blocks[index - 1];
|
MemoryBlock prev = _blocks[index - 1];
|
||||||
|
|
||||||
|
|||||||
@@ -14,62 +14,35 @@ using System.Threading;
|
|||||||
|
|
||||||
namespace ARMeilleure.Translation.Cache
|
namespace ARMeilleure.Translation.Cache
|
||||||
{
|
{
|
||||||
static partial class JitCache
|
public partial class JitCache : IDisposable
|
||||||
{
|
{
|
||||||
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 int CacheSize = 256 * 1024 * 1024;
|
private const uint CacheSize = 256 * 1024 * 1024;
|
||||||
|
|
||||||
private static JitCacheInvalidation _jitCacheInvalidator;
|
private readonly JitCacheInvalidation _jitCacheInvalidator;
|
||||||
|
|
||||||
private static List<CacheMemoryAllocator> _cacheAllocators = [];
|
private readonly CacheMemoryAllocator _cacheAllocator;
|
||||||
|
|
||||||
private static readonly List<CacheEntry> _cacheEntries = [];
|
private readonly List<CacheEntry> _cacheEntries = [];
|
||||||
|
|
||||||
private static readonly Lock _lock = new();
|
private readonly Lock _lock = new();
|
||||||
private static bool _initialized;
|
|
||||||
|
|
||||||
private static readonly List<ReservedRegion> _jitRegions = [];
|
private readonly List<ReservedRegion> _jitRegions = [];
|
||||||
private static int _activeRegionIndex = 0;
|
|
||||||
|
|
||||||
[SupportedOSPlatform("windows")]
|
[SupportedOSPlatform("windows")]
|
||||||
[LibraryImport("kernel32.dll", SetLastError = true)]
|
[LibraryImport("kernel32.dll", SetLastError = true)]
|
||||||
public static partial nint FlushInstructionCache(nint hProcess, nint lpAddress, nuint dwSize);
|
private static partial nint FlushInstructionCache(nint hProcess, nint lpAddress, nuint dwSize);
|
||||||
|
|
||||||
public static void Initialize(IJitMemoryAllocator allocator)
|
public JitCache(IJitMemoryAllocator allocator)
|
||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
if (_initialized)
|
_jitRegions.Add(new(allocator, CacheSize));
|
||||||
{
|
|
||||||
if (OperatingSystem.IsWindows())
|
|
||||||
{
|
|
||||||
JitUnwindWindows.RemoveFunctionTableHandler(
|
|
||||||
_jitRegions[0].Pointer);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < _jitRegions.Count; i++)
|
_cacheAllocator = new((int)CacheSize);
|
||||||
{
|
|
||||||
_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())
|
||||||
{
|
{
|
||||||
@@ -79,23 +52,20 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
if (OperatingSystem.IsWindows())
|
if (OperatingSystem.IsWindows())
|
||||||
{
|
{
|
||||||
JitUnwindWindows.InstallFunctionTableHandler(
|
JitUnwindWindows.InstallFunctionTableHandler(
|
||||||
firstRegion.Pointer, CacheSize, firstRegion.Pointer + Allocate(_pageSize)
|
this, _jitRegions[0].Pointer, CacheSize, _jitRegions[0].Pointer + Allocate(_pageSize)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static nint Map(CompiledFunction func)
|
public 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);
|
||||||
ReservedRegion targetRegion = _jitRegions[_activeRegionIndex];
|
nint funcPtr = GetFunctionPtr(funcOffset);
|
||||||
nint funcPtr = targetRegion.Pointer + funcOffset;
|
|
||||||
|
|
||||||
if (OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
if (OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
||||||
{
|
{
|
||||||
@@ -109,9 +79,9 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ReprotectAsWritable(targetRegion, funcOffset, code.Length);
|
ReprotectAsWritable(funcOffset, code.Length);
|
||||||
Marshal.Copy(code, 0, funcPtr, code.Length);
|
Marshal.Copy(code, 0, funcPtr, code.Length);
|
||||||
ReprotectAsExecutable(targetRegion, funcOffset, code.Length);
|
ReprotectAsExecutable(funcOffset, code.Length);
|
||||||
|
|
||||||
if (OperatingSystem.IsWindows() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
if (OperatingSystem.IsWindows() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
||||||
{
|
{
|
||||||
@@ -129,25 +99,24 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Unmap(nint pointer)
|
public void Unmap(nint pointer)
|
||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
Debug.Assert(_initialized);
|
for (int i = 0; i < _jitRegions.Count; i++)
|
||||||
|
|
||||||
foreach (var region in _jitRegions)
|
|
||||||
{
|
{
|
||||||
if (pointer.ToInt64() < region.Pointer.ToInt64() ||
|
ReservedRegion jitRegion = _jitRegions[i];
|
||||||
pointer.ToInt64() >= (region.Pointer + CacheSize).ToInt64())
|
if (pointer.ToInt64() < jitRegion.Pointer.ToInt64() ||
|
||||||
|
pointer.ToInt64() >= (jitRegion.Pointer + (nint)CacheSize).ToInt64())
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
int funcOffset = (int)(pointer.ToInt64() - region.Pointer.ToInt64());
|
int funcOffset = (int)(pointer.ToInt64() - jitRegion.Pointer.ToInt64() + i * CacheSize);
|
||||||
|
|
||||||
if (TryFind(funcOffset, out CacheEntry entry, out int entryIndex) && entry.Offset == funcOffset)
|
if (TryFind(funcOffset, out CacheEntry entry, out int entryIndex) && entry.Offset == funcOffset)
|
||||||
{
|
{
|
||||||
_cacheAllocators[_activeRegionIndex].Free(funcOffset, AlignCodeSize(entry.Size));
|
_cacheAllocator.Free(funcOffset, AlignCodeSize(entry.Size));
|
||||||
_cacheEntries.RemoveAt(entryIndex);
|
_cacheEntries.RemoveAt(entryIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,53 +125,66 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ReprotectAsWritable(ReservedRegion region, int offset, int size)
|
private void ReprotectAsWritable(int offset, int size)
|
||||||
{
|
{
|
||||||
int endOffs = offset + size;
|
int endOffs = offset + size;
|
||||||
int regionStart = offset & ~_pageMask;
|
int regionStart = (offset % (int)CacheSize) & ~_pageMask;
|
||||||
int regionEnd = (endOffs + _pageMask) & ~_pageMask;
|
int regionEnd = endOffs % (int)CacheSize == 0
|
||||||
|
? (((int)CacheSize) + _pageMask) & ~_pageMask
|
||||||
region.Block.MapAsRwx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
: ((endOffs % (int)CacheSize) + _pageMask) & ~_pageMask;
|
||||||
|
GetRegion(offset).Block.MapAsRwx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ReprotectAsExecutable(ReservedRegion region, int offset, int size)
|
private void ReprotectAsExecutable(int offset, int size)
|
||||||
{
|
{
|
||||||
int endOffs = offset + size;
|
int endOffs = offset + size;
|
||||||
int regionStart = offset & ~_pageMask;
|
int regionStart = (offset % (int)CacheSize) & ~_pageMask;
|
||||||
int regionEnd = (endOffs + _pageMask) & ~_pageMask;
|
int regionEnd = endOffs % (int)CacheSize == 0
|
||||||
|
? (((int)CacheSize) + _pageMask) & ~_pageMask
|
||||||
|
: ((endOffs % (int)CacheSize) + _pageMask) & ~_pageMask;
|
||||||
|
|
||||||
region.Block.MapAsRx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
GetRegion(offset).Block.MapAsRx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int Allocate(int codeSize)
|
private int Allocate(int codeSize)
|
||||||
{
|
{
|
||||||
codeSize = AlignCodeSize(codeSize);
|
codeSize = AlignCodeSize(codeSize);
|
||||||
|
|
||||||
int allocOffset = _cacheAllocators[_activeRegionIndex].Allocate(codeSize);
|
int allocOffset = _cacheAllocator.Allocate(codeSize);
|
||||||
|
|
||||||
if (allocOffset >= 0)
|
if (allocOffset >= 0)
|
||||||
{
|
{
|
||||||
_jitRegions[_activeRegionIndex].ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize);
|
GetRegion(allocOffset).ExpandIfNeeded((ulong)(allocOffset % (int)CacheSize) + (ulong)codeSize);
|
||||||
return allocOffset;
|
return allocOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
int exhaustedRegion = _activeRegionIndex;
|
_cacheAllocator.AddNewBlocks(1);
|
||||||
var newRegion = new ReservedRegion(_jitRegions[0].Allocator, CacheSize);
|
ReservedRegion newRegion = new(_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;
|
|
||||||
|
|
||||||
Logger.Warning?.Print(LogClass.Cpu, $"JIT Cache Region {exhaustedRegion} exhausted, creating new Cache Region {_activeRegionIndex} ({((long)(_activeRegionIndex + 1) * CacheSize).Bytes()} Total Allocation).");
|
allocOffset = _cacheAllocator.Allocate(codeSize);
|
||||||
|
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!");
|
||||||
}
|
}
|
||||||
|
|
||||||
newRegion.ExpandIfNeeded((ulong)allocOffsetNew + (ulong)codeSize);
|
GetRegion(allocOffset).ExpandIfNeeded((ulong)(allocOffset % (int)CacheSize) + (ulong)codeSize);
|
||||||
return allocOffsetNew;
|
return allocOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
@@ -210,7 +192,7 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
return checked(codeSize + (CodeAlignment - 1)) & ~(CodeAlignment - 1);
|
return checked(codeSize + (CodeAlignment - 1)) & ~(CodeAlignment - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void Add(int offset, int size, UnwindInfo unwindInfo)
|
private void Add(int offset, int size, UnwindInfo unwindInfo)
|
||||||
{
|
{
|
||||||
CacheEntry entry = new(offset, size, unwindInfo);
|
CacheEntry entry = new(offset, size, unwindInfo);
|
||||||
|
|
||||||
@@ -224,25 +206,22 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
_cacheEntries.Insert(index, entry);
|
_cacheEntries.Insert(index, entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool TryFind(int offset, out CacheEntry entry, out int entryIndex)
|
public bool TryFind(int offset, out CacheEntry entry, out int entryIndex)
|
||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
foreach (var region in _jitRegions)
|
int index = _cacheEntries.BinarySearch(new CacheEntry(offset, 0, default));
|
||||||
|
|
||||||
|
if (index < 0)
|
||||||
{
|
{
|
||||||
int index = _cacheEntries.BinarySearch(new CacheEntry(offset, 0, default));
|
index = ~index - 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (index < 0)
|
if (index >= 0)
|
||||||
{
|
{
|
||||||
index = ~index - 1;
|
entry = _cacheEntries[index];
|
||||||
}
|
entryIndex = index;
|
||||||
|
return true;
|
||||||
if (index >= 0)
|
|
||||||
{
|
|
||||||
entry = _cacheEntries[index];
|
|
||||||
entryIndex = index;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -250,5 +229,18 @@ 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,6 +2,8 @@
|
|||||||
|
|
||||||
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;
|
||||||
|
|
||||||
@@ -27,6 +29,29 @@ 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,
|
||||||
@@ -59,27 +84,28 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
|
|
||||||
private static GetRuntimeFunctionCallback _getRuntimeFunctionCallback;
|
private static GetRuntimeFunctionCallback _getRuntimeFunctionCallback;
|
||||||
|
|
||||||
private static int _sizeOfRuntimeFunction;
|
private static readonly int _sizeOfRuntimeFunction;
|
||||||
|
|
||||||
private unsafe static RuntimeFunction* _runtimeFunction;
|
private static readonly ConcurrentDictionary<ulong, InternalFunctionHandler> _functionTableHandlers = new();
|
||||||
|
|
||||||
private unsafe static UnwindInfo* _unwindInfo;
|
static JitUnwindWindows()
|
||||||
|
{
|
||||||
|
_sizeOfRuntimeFunction = Marshal.SizeOf<RuntimeFunction>();
|
||||||
|
}
|
||||||
|
|
||||||
public static void InstallFunctionTableHandler(nint codeCachePointer, uint codeCacheLength, nint workBufferPtr)
|
public static void InstallFunctionTableHandler(JitCache jitCache, nint codeCachePointer, uint codeCacheLength, nint workBufferPtr)
|
||||||
{
|
{
|
||||||
ulong codeCachePtr = (ulong)codeCachePointer.ToInt64();
|
ulong codeCachePtr = (ulong)codeCachePointer.ToInt64();
|
||||||
|
|
||||||
_sizeOfRuntimeFunction = Marshal.SizeOf<RuntimeFunction>();
|
|
||||||
|
|
||||||
bool result;
|
bool result;
|
||||||
|
|
||||||
|
InternalFunctionHandler handler;
|
||||||
|
|
||||||
unsafe
|
unsafe
|
||||||
{
|
{
|
||||||
_runtimeFunction = (RuntimeFunction*)workBufferPtr;
|
handler = new InternalFunctionHandler(jitCache, workBufferPtr);
|
||||||
|
|
||||||
_unwindInfo = (UnwindInfo*)(workBufferPtr + _sizeOfRuntimeFunction);
|
_getRuntimeFunctionCallback = handler.FunctionTableHandler;
|
||||||
|
|
||||||
_getRuntimeFunctionCallback = new GetRuntimeFunctionCallback(FunctionTableHandler);
|
|
||||||
|
|
||||||
result = RtlInstallFunctionTableCallback(
|
result = RtlInstallFunctionTableCallback(
|
||||||
codeCachePtr | 3,
|
codeCachePtr | 3,
|
||||||
@@ -94,6 +120,8 @@ 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)
|
||||||
@@ -111,24 +139,26 @@ 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(ulong controlPc, nint context)
|
private static unsafe RuntimeFunction* FunctionTableHandler(JitCache jitCache, RuntimeFunction* runtimeFunction, UnwindInfo* unwindInfo, 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.
|
||||||
}
|
}
|
||||||
|
|
||||||
var unwindInfo = funcEntry.UnwindInfo;
|
CodeGen.Unwinding.UnwindInfo funcUnwindInfo = funcEntry.UnwindInfo;
|
||||||
|
|
||||||
int codeIndex = 0;
|
int codeIndex = 0;
|
||||||
|
|
||||||
for (int index = unwindInfo.PushEntries.Length - 1; index >= 0; index--)
|
for (int index = funcUnwindInfo.PushEntries.Length - 1; index >= 0; index--)
|
||||||
{
|
{
|
||||||
var entry = unwindInfo.PushEntries[index];
|
UnwindPushEntry entry = funcUnwindInfo.PushEntries[index];
|
||||||
|
|
||||||
switch (entry.PseudoOp)
|
switch (entry.PseudoOp)
|
||||||
{
|
{
|
||||||
@@ -140,14 +170,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;
|
||||||
@@ -161,18 +191,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;
|
||||||
@@ -180,7 +210,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;
|
||||||
}
|
}
|
||||||
@@ -192,16 +222,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)unwindInfo.PrologSize;
|
unwindInfo->SizeOfProlog = (byte)funcUnwindInfo.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);
|
||||||
|
|
||||||
var visited = new HashSet<BasicBlock>();
|
HashSet<BasicBlock> visited = new();
|
||||||
var blockStack = new Stack<BasicBlock>();
|
Stack<BasicBlock> blockStack = new();
|
||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
var visited = new HashSet<BasicBlock>();
|
HashSet<BasicBlock> visited = new();
|
||||||
var workQueue = new Queue<BasicBlock>();
|
Queue<BasicBlock> workQueue = new();
|
||||||
|
|
||||||
visited.Add(Entry);
|
visited.Add(Entry);
|
||||||
workQueue.Enqueue(Entry);
|
workQueue.Enqueue(Entry);
|
||||||
|
|||||||
@@ -4,12 +4,14 @@ 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;
|
||||||
@@ -30,14 +32,11 @@ 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 = 7010; //! To be incremented manually for each change to the ARMeilleure project.
|
private const uint InternalVersion = 7031; //! 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);
|
||||||
@@ -48,6 +47,8 @@ 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;
|
||||||
@@ -63,8 +64,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
|
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
|
|
||||||
public string TitleIdText { get; private set; }
|
public PtcCacheInfo CacheInfo { 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()
|
public Ptc(JitCache jitCache)
|
||||||
{
|
{
|
||||||
Profiler = new PtcProfiler(this);
|
Profiler = new PtcProfiler(this);
|
||||||
|
|
||||||
@@ -89,10 +89,11 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
|
|
||||||
_waitEvent = new ManualResetEvent(true);
|
_waitEvent = new ManualResetEvent(true);
|
||||||
|
|
||||||
|
_jitCache = jitCache;
|
||||||
|
|
||||||
_disposed = false;
|
_disposed = false;
|
||||||
|
|
||||||
TitleIdText = TitleIdTextDefault;
|
CacheInfo = new PtcCacheInfo(0, null, null, 0, null, "Unknown", "default");
|
||||||
DisplayVersion = DisplayVersionDefault;
|
|
||||||
|
|
||||||
CachePathActual = string.Empty;
|
CachePathActual = string.Empty;
|
||||||
CachePathBackup = string.Empty;
|
CachePathBackup = string.Empty;
|
||||||
@@ -100,20 +101,24 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
Disable();
|
Disable();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Initialize(string titleIdText, string displayVersion, bool enabled, MemoryManagerType memoryMode, string cacheSelector)
|
public void Initialize(PtcCacheInfo cacheInfo, bool enabled, MemoryManagerType memoryMode)
|
||||||
{
|
{
|
||||||
Wait();
|
Wait();
|
||||||
|
|
||||||
Profiler.Wait();
|
Profiler.Wait();
|
||||||
Profiler.ClearEntries();
|
Profiler.ClearEntries();
|
||||||
|
|
||||||
Logger.Info?.Print(LogClass.Ptc, $"Initializing Profiled Persistent Translation Cache v{InternalVersion}\n\t\t (title: {titleIdText}, version: '{displayVersion}', selector: '{cacheSelector}', enabled: {enabled}).");
|
CacheInfo = cacheInfo;
|
||||||
|
|
||||||
if (!enabled || string.IsNullOrEmpty(titleIdText) || titleIdText == TitleIdTextDefault)
|
Logger.Info?.Print(
|
||||||
|
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;
|
||||||
|
|
||||||
@@ -122,12 +127,10 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
TitleIdText = titleIdText;
|
|
||||||
DisplayVersion = !string.IsNullOrEmpty(displayVersion) ? displayVersion : DisplayVersionDefault;
|
|
||||||
_memoryMode = memoryMode;
|
_memoryMode = memoryMode;
|
||||||
|
|
||||||
string workPathActual = Path.Combine(AppDataManager.GamesDirPath, TitleIdText, "cache", "cpu", ActualDir);
|
string workPathActual = Path.Combine(AppDataManager.GamesDirPath, CacheInfo.TitleIdText, "cache", "cpu", ActualDir);
|
||||||
string workPathBackup = Path.Combine(AppDataManager.GamesDirPath, TitleIdText, "cache", "cpu", BackupDir);
|
string workPathBackup = Path.Combine(AppDataManager.GamesDirPath, CacheInfo.TitleIdText, "cache", "cpu", BackupDir);
|
||||||
|
|
||||||
if (!Directory.Exists(workPathActual))
|
if (!Directory.Exists(workPathActual))
|
||||||
{
|
{
|
||||||
@@ -139,8 +142,14 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
Directory.CreateDirectory(workPathBackup);
|
Directory.CreateDirectory(workPathBackup);
|
||||||
}
|
}
|
||||||
|
|
||||||
CachePathActual = Path.Combine(workPathActual, DisplayVersion) + "-" + cacheSelector;
|
CachePathActual = Path.Combine(workPathActual, CacheInfo.DisplayVersion) + "-" + CacheInfo.CacheSelector;
|
||||||
CachePathBackup = Path.Combine(workPathBackup, DisplayVersion) + "-" + cacheSelector;
|
CachePathBackup = Path.Combine(workPathBackup, CacheInfo.DisplayVersion) + "-" + CacheInfo.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();
|
||||||
@@ -364,7 +373,12 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
|
|
||||||
long fileSize = new FileInfo(fileName).Length;
|
long fileSize = new FileInfo(fileName).Length;
|
||||||
|
|
||||||
Logger.Info?.Print(LogClass.Ptc, $"{(isBackup ? "Loaded Backup Translation Cache" : "Loaded Translation Cache")} (size: {fileSize} bytes, translated functions: {GetEntriesCount()}).");
|
Logger.Info?.Print(
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
@@ -544,7 +558,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 var value) && value.HighCq))
|
if (isEntryChanged || (!infoEntry.HighCq && Profiler.ProfiledFuncs.TryGetValue(infoEntry.Address, out PtcProfiler.FuncProfile value) && value.HighCq))
|
||||||
{
|
{
|
||||||
infoEntry.Stubbed = true;
|
infoEntry.Stubbed = true;
|
||||||
infoEntry.CodeLength = 0;
|
infoEntry.CodeLength = 0;
|
||||||
@@ -724,15 +738,15 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
return new UnwindInfo(pushEntries, prologueSize);
|
return new UnwindInfo(pushEntries, prologueSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static TranslatedFunction FastTranslate(
|
private TranslatedFunction FastTranslate(
|
||||||
byte[] code,
|
byte[] code,
|
||||||
Counter<uint> callCounter,
|
Counter<uint> callCounter,
|
||||||
ulong guestSize,
|
ulong guestSize,
|
||||||
UnwindInfo unwindInfo,
|
UnwindInfo unwindInfo,
|
||||||
bool highCq)
|
bool highCq)
|
||||||
{
|
{
|
||||||
var cFunc = new CompiledFunction(code, unwindInfo, RelocInfo.Empty);
|
CompiledFunction cFunc = new(code, unwindInfo, RelocInfo.Empty);
|
||||||
var gFunc = cFunc.MapWithPointer<GuestFunction>(out nint gFuncPointer);
|
GuestFunction gFunc = cFunc.MapWithPointer<GuestFunction>(_jitCache, out nint gFuncPointer);
|
||||||
|
|
||||||
return new TranslatedFunction(gFunc, gFuncPointer, callCounter, guestSize, highCq);
|
return new TranslatedFunction(gFunc, gFuncPointer, callCounter, guestSize, highCq);
|
||||||
}
|
}
|
||||||
@@ -769,7 +783,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
|
|
||||||
public void MakeAndSaveTranslations(Translator translator)
|
public void MakeAndSaveTranslations(Translator translator)
|
||||||
{
|
{
|
||||||
var profiledFuncsToTranslate = Profiler.GetProfiledFuncsToTranslate(translator.Functions);
|
ConcurrentQueue<(ulong address, PtcProfiler.FuncProfile funcProfile)> profiledFuncsToTranslate = Profiler.GetProfiledFuncsToTranslate(translator.Functions);
|
||||||
|
|
||||||
_translateCount = 0;
|
_translateCount = 0;
|
||||||
_translateTotalCount = profiledFuncsToTranslate.Count;
|
_translateTotalCount = profiledFuncsToTranslate.Count;
|
||||||
@@ -813,7 +827,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
|
|
||||||
void TranslateFuncs()
|
void TranslateFuncs()
|
||||||
{
|
{
|
||||||
while (profiledFuncsToTranslate.TryDequeue(out var item))
|
while (profiledFuncsToTranslate.TryDequeue(out (ulong address, PtcProfiler.FuncProfile funcProfile) item))
|
||||||
{
|
{
|
||||||
ulong address = item.address;
|
ulong address = item.address;
|
||||||
ExecutionMode executionMode = item.funcProfile.Mode;
|
ExecutionMode executionMode = item.funcProfile.Mode;
|
||||||
@@ -858,11 +872,11 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
|
|
||||||
Stopwatch sw = Stopwatch.StartNew();
|
Stopwatch sw = Stopwatch.StartNew();
|
||||||
|
|
||||||
foreach (var thread in threads)
|
foreach (Thread thread in threads)
|
||||||
{
|
{
|
||||||
thread.Start();
|
thread.Start();
|
||||||
}
|
}
|
||||||
foreach (var thread in threads)
|
foreach (Thread thread in threads)
|
||||||
{
|
{
|
||||||
thread.Join();
|
thread.Join();
|
||||||
}
|
}
|
||||||
@@ -942,7 +956,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
WriteCode(code.AsSpan());
|
WriteCode(code.AsSpan());
|
||||||
|
|
||||||
// WriteReloc.
|
// WriteReloc.
|
||||||
using var relocInfoWriter = new BinaryWriter(_relocsStream, EncodingCache.UTF8NoBOM, true);
|
using BinaryWriter relocInfoWriter = new(_relocsStream, EncodingCache.UTF8NoBOM, true);
|
||||||
|
|
||||||
foreach (RelocEntry entry in relocInfo.Entries)
|
foreach (RelocEntry entry in relocInfo.Entries)
|
||||||
{
|
{
|
||||||
@@ -952,7 +966,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
}
|
}
|
||||||
|
|
||||||
// WriteUnwindInfo.
|
// WriteUnwindInfo.
|
||||||
using var unwindInfoWriter = new BinaryWriter(_unwindInfosStream, EncodingCache.UTF8NoBOM, true);
|
using BinaryWriter unwindInfoWriter = new(_unwindInfosStream, EncodingCache.UTF8NoBOM, true);
|
||||||
|
|
||||||
unwindInfoWriter.Write(unwindInfo.PushEntries.Length);
|
unwindInfoWriter.Write(unwindInfo.PushEntries.Length);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
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 = 6698; //! Not to be incremented manually for each change to the ARMeilleure project.
|
private const uint InternalVersion = 7031; //! 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)
|
||||||
{
|
{
|
||||||
var profiledFuncsToTranslate = new ConcurrentQueue<(ulong address, FuncProfile funcProfile)>();
|
ConcurrentQueue<(ulong address, FuncProfile funcProfile)> profiledFuncsToTranslate = new();
|
||||||
|
|
||||||
foreach (var profiledFunc in ProfiledFuncs)
|
foreach (KeyValuePair<ulong, FuncProfile> 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 (var profiledFunc in ProfiledFuncs)
|
foreach (KeyValuePair<ulong, FuncProfile> profiledFunc in ProfiledFuncs)
|
||||||
{
|
{
|
||||||
if (profiledFunc.Value.Blacklist)
|
if (profiledFunc.Value.Blacklist)
|
||||||
{
|
{
|
||||||
@@ -254,7 +254,12 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
|
|
||||||
long fileSize = new FileInfo(fileName).Length;
|
long fileSize = new FileInfo(fileName).Length;
|
||||||
|
|
||||||
Logger.Info?.Print(LogClass.Ptc, $"{(isBackup ? "Loaded Backup Profiling Info" : "Loaded Profiling Info")} (size: {fileSize} bytes, profiled functions: {ProfiledFuncs.Count}).");
|
Logger.Info?.Print(
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
@@ -375,7 +380,11 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
|
|
||||||
if (fileSize != 0L)
|
if (fileSize != 0L)
|
||||||
{
|
{
|
||||||
Logger.Info?.Print(LogClass.Ptc, $"Saved Profiling Info (size: {fileSize} bytes, profiled functions: {profiledFuncsCount}).");
|
Logger.Info?.Print(
|
||||||
|
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)
|
||||||
{
|
{
|
||||||
var globalDefs = new DefMap[cfg.Blocks.Count];
|
DefMap[] globalDefs = new DefMap[cfg.Blocks.Count];
|
||||||
var localDefs = new Operand[cfg.LocalsCount + RegisterConsts.TotalCount];
|
Operand[] localDefs = new Operand[cfg.LocalsCount + RegisterConsts.TotalCount];
|
||||||
|
|
||||||
var dfPhiBlocks = new Queue<BasicBlock>();
|
Queue<BasicBlock> dfPhiBlocks = new();
|
||||||
|
|
||||||
for (BasicBlock block = cfg.Blocks.First; block != null; block = block.ListNext)
|
for (BasicBlock block = cfg.Blocks.First; block != null; block = block.ListNext)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -24,10 +24,11 @@ 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; }
|
||||||
internal IAddressTable<ulong> FunctionTable { get; }
|
public 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; }
|
||||||
@@ -43,23 +44,26 @@ namespace ARMeilleure.Translation
|
|||||||
|
|
||||||
_oldFuncs = new ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>>();
|
_oldFuncs = new ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>>();
|
||||||
|
|
||||||
_ptc = new Ptc();
|
JitCache = new JitCache(allocator);
|
||||||
|
|
||||||
|
_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(string titleIdText, string displayVersion, bool enabled, string cacheSelector)
|
public IPtcLoadState LoadDiskCache(PtcCacheInfo cacheInfo, bool enabled)
|
||||||
{
|
{
|
||||||
_ptc.Initialize(titleIdText, displayVersion, enabled, Memory.Type, cacheSelector);
|
_ptc.Initialize(cacheInfo, enabled, Memory.Type);
|
||||||
return _ptc;
|
return _ptc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,6 +171,7 @@ namespace ARMeilleure.Translation
|
|||||||
}
|
}
|
||||||
|
|
||||||
ClearJitCache();
|
ClearJitCache();
|
||||||
|
JitCache.Dispose();
|
||||||
|
|
||||||
Stubs.Dispose();
|
Stubs.Dispose();
|
||||||
FunctionTable.Dispose();
|
FunctionTable.Dispose();
|
||||||
@@ -242,7 +247,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)
|
||||||
{
|
{
|
||||||
var context = new ArmEmitterContext(
|
ArmEmitterContext context = new(
|
||||||
Memory,
|
Memory,
|
||||||
CountTable,
|
CountTable,
|
||||||
FunctionTable,
|
FunctionTable,
|
||||||
@@ -286,10 +291,10 @@ namespace ARMeilleure.Translation
|
|||||||
|
|
||||||
Logger.EndPass(PassName.RegisterUsage);
|
Logger.EndPass(PassName.RegisterUsage);
|
||||||
|
|
||||||
var retType = OperandType.I64;
|
OperandType retType = OperandType.I64;
|
||||||
var argTypes = new OperandType[] { OperandType.I64 };
|
OperandType[] argTypes = new OperandType[] { OperandType.I64 };
|
||||||
|
|
||||||
var options = highCq ? CompilerOptions.HighCq : CompilerOptions.None;
|
CompilerOptions options = highCq ? CompilerOptions.HighCq : CompilerOptions.None;
|
||||||
|
|
||||||
if (context.HasPtc && !singleStep)
|
if (context.HasPtc && !singleStep)
|
||||||
{
|
{
|
||||||
@@ -305,7 +310,7 @@ namespace ARMeilleure.Translation
|
|||||||
_ptc.WriteCompiledFunction(address, funcSize, hash, highCq, compiledFunc);
|
_ptc.WriteCompiledFunction(address, funcSize, hash, highCq, compiledFunc);
|
||||||
}
|
}
|
||||||
|
|
||||||
GuestFunction func = compiledFunc.MapWithPointer<GuestFunction>(out nint funcPointer);
|
GuestFunction func = compiledFunc.MapWithPointer<GuestFunction>(JitCache, out nint funcPointer);
|
||||||
|
|
||||||
Allocators.ResetAll();
|
Allocators.ResetAll();
|
||||||
|
|
||||||
@@ -574,7 +579,7 @@ namespace ARMeilleure.Translation
|
|||||||
|
|
||||||
List<TranslatedFunction> functions = Functions.AsList();
|
List<TranslatedFunction> functions = Functions.AsList();
|
||||||
|
|
||||||
foreach (var func in functions)
|
foreach (TranslatedFunction func in functions)
|
||||||
{
|
{
|
||||||
JitCache.Unmap(func.FuncPointer);
|
JitCache.Unmap(func.FuncPointer);
|
||||||
|
|
||||||
@@ -583,7 +588,7 @@ namespace ARMeilleure.Translation
|
|||||||
|
|
||||||
Functions.Clear();
|
Functions.Clear();
|
||||||
|
|
||||||
while (_oldFuncs.TryDequeue(out var kv))
|
while (_oldFuncs.TryDequeue(out KeyValuePair<ulong, TranslatedFunction> kv))
|
||||||
{
|
{
|
||||||
JitCache.Unmap(kv.Value.FuncPointer);
|
JitCache.Unmap(kv.Value.FuncPointer);
|
||||||
|
|
||||||
@@ -604,7 +609,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 var func) && func.CallCounter != null)
|
if (Functions.TryGetValue(request.Address, out TranslatedFunction func) && func.CallCounter != null)
|
||||||
{
|
{
|
||||||
Volatile.Write(ref func.CallCounter.Value, 0);
|
Volatile.Write(ref func.CallCounter.Value, 0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ 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;
|
||||||
@@ -83,12 +85,15 @@ 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(IAddressTable<ulong> functionTable)
|
public TranslatorStubs(JitCache jitCache, 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);
|
||||||
@@ -115,12 +120,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;
|
||||||
@@ -141,7 +146,7 @@ namespace ARMeilleure.Translation
|
|||||||
/// <returns>Generated <see cref="DispatchStub"/></returns>
|
/// <returns>Generated <see cref="DispatchStub"/></returns>
|
||||||
private nint GenerateDispatchStub()
|
private nint GenerateDispatchStub()
|
||||||
{
|
{
|
||||||
var context = new EmitterContext();
|
EmitterContext context = new();
|
||||||
|
|
||||||
Operand lblFallback = Label();
|
Operand lblFallback = Label();
|
||||||
Operand lblEnd = Label();
|
Operand lblEnd = Label();
|
||||||
@@ -160,7 +165,7 @@ namespace ARMeilleure.Translation
|
|||||||
|
|
||||||
for (int i = 0; i < _functionTable.Levels.Length; i++)
|
for (int i = 0; i < _functionTable.Levels.Length; i++)
|
||||||
{
|
{
|
||||||
ref var level = ref _functionTable.Levels[i];
|
ref AddressTableLevel 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.
|
||||||
@@ -184,11 +189,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);
|
||||||
|
|
||||||
var cfg = context.GetControlFlowGraph();
|
ControlFlowGraph cfg = context.GetControlFlowGraph();
|
||||||
var retType = OperandType.I64;
|
OperandType retType = OperandType.I64;
|
||||||
var argTypes = new[] { OperandType.I64 };
|
OperandType[] argTypes = new[] { OperandType.I64 };
|
||||||
|
|
||||||
var func = Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<GuestFunction>();
|
GuestFunction func = Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<GuestFunction>(_jitCache);
|
||||||
|
|
||||||
return Marshal.GetFunctionPointerForDelegate(func);
|
return Marshal.GetFunctionPointerForDelegate(func);
|
||||||
}
|
}
|
||||||
@@ -199,7 +204,7 @@ namespace ARMeilleure.Translation
|
|||||||
/// <returns>Generated <see cref="SlowDispatchStub"/></returns>
|
/// <returns>Generated <see cref="SlowDispatchStub"/></returns>
|
||||||
private nint GenerateSlowDispatchStub()
|
private nint GenerateSlowDispatchStub()
|
||||||
{
|
{
|
||||||
var context = new EmitterContext();
|
EmitterContext context = new();
|
||||||
|
|
||||||
// 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);
|
||||||
@@ -209,11 +214,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);
|
||||||
|
|
||||||
var cfg = context.GetControlFlowGraph();
|
ControlFlowGraph cfg = context.GetControlFlowGraph();
|
||||||
var retType = OperandType.I64;
|
OperandType retType = OperandType.I64;
|
||||||
var argTypes = new[] { OperandType.I64 };
|
OperandType[] argTypes = new[] { OperandType.I64 };
|
||||||
|
|
||||||
var func = Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<GuestFunction>();
|
GuestFunction func = Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<GuestFunction>(_jitCache);
|
||||||
|
|
||||||
return Marshal.GetFunctionPointerForDelegate(func);
|
return Marshal.GetFunctionPointerForDelegate(func);
|
||||||
}
|
}
|
||||||
@@ -250,7 +255,7 @@ namespace ARMeilleure.Translation
|
|||||||
/// <returns><see cref="DispatchLoop"/> function</returns>
|
/// <returns><see cref="DispatchLoop"/> function</returns>
|
||||||
private DispatcherFunction GenerateDispatchLoop()
|
private DispatcherFunction GenerateDispatchLoop()
|
||||||
{
|
{
|
||||||
var context = new EmitterContext();
|
EmitterContext context = new();
|
||||||
|
|
||||||
Operand beginLbl = Label();
|
Operand beginLbl = Label();
|
||||||
Operand endLbl = Label();
|
Operand endLbl = Label();
|
||||||
@@ -286,11 +291,11 @@ namespace ARMeilleure.Translation
|
|||||||
|
|
||||||
context.Return();
|
context.Return();
|
||||||
|
|
||||||
var cfg = context.GetControlFlowGraph();
|
ControlFlowGraph cfg = context.GetControlFlowGraph();
|
||||||
var retType = OperandType.None;
|
OperandType retType = OperandType.None;
|
||||||
var argTypes = new[] { OperandType.I64, OperandType.I64 };
|
OperandType[] argTypes = new[] { OperandType.I64, OperandType.I64 };
|
||||||
|
|
||||||
return Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<DispatcherFunction>();
|
return Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<DispatcherFunction>(_jitCache);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -299,7 +304,7 @@ namespace ARMeilleure.Translation
|
|||||||
/// <returns><see cref="ContextWrapper"/> function</returns>
|
/// <returns><see cref="ContextWrapper"/> function</returns>
|
||||||
private WrapperFunction GenerateContextWrapper()
|
private WrapperFunction GenerateContextWrapper()
|
||||||
{
|
{
|
||||||
var context = new EmitterContext();
|
EmitterContext context = new();
|
||||||
|
|
||||||
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);
|
||||||
@@ -310,11 +315,11 @@ namespace ARMeilleure.Translation
|
|||||||
|
|
||||||
context.Return(returnValue);
|
context.Return(returnValue);
|
||||||
|
|
||||||
var cfg = context.GetControlFlowGraph();
|
ControlFlowGraph cfg = context.GetControlFlowGraph();
|
||||||
var retType = OperandType.I64;
|
OperandType retType = OperandType.I64;
|
||||||
var argTypes = new[] { OperandType.I64, OperandType.I64 };
|
OperandType[] argTypes = new[] { OperandType.I64, OperandType.I64 };
|
||||||
|
|
||||||
return Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<WrapperFunction>();
|
return Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<WrapperFunction>(_jitCache);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
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;
|
||||||
@@ -59,7 +60,7 @@ namespace ARMeilleure.Translation
|
|||||||
return context.VectorExtract(OperandType.I32, vec, 0);
|
return context.VectorExtract(OperandType.I32, vec, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static FpFlagsPInvokeTest GenerateFpFlagsPInvokeTest()
|
public static FpFlagsPInvokeTest GenerateFpFlagsPInvokeTest(JitCache jitCache)
|
||||||
{
|
{
|
||||||
EmitterContext context = new();
|
EmitterContext context = new();
|
||||||
|
|
||||||
@@ -141,7 +142,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>();
|
return Compiler.Compile(cfg, argTypes, OperandType.I32, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<FpFlagsPInvokeTest>(jitCache);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ namespace Ryujinx.Audio.Backends.Apple
|
|||||||
|
|
||||||
if (result == 0)
|
if (result == 0)
|
||||||
{
|
{
|
||||||
AudioChannelLayout layout = new AudioChannelLayout
|
AudioChannelLayout layout = new()
|
||||||
{
|
{
|
||||||
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;
|
||||||
var pCallback = callback != null ? (SDL_AudioStreamCallbackPointer)Marshal.GetFunctionPointerForDelegate(callback) : null;
|
SDL_AudioStreamCallbackPointer 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
|
||||||
{
|
{
|
||||||
var context = GetOutContext();
|
SoundIoOutStream 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
|
||||||
{
|
{
|
||||||
var frameCountPtr = &nativeFrameCount;
|
int* frameCountPtr = &nativeFrameCount;
|
||||||
var arenasPtr = &arenas;
|
IntPtr* 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()
|
||||||
{
|
{
|
||||||
var writer = new BinaryWriter(_stream);
|
BinaryWriter writer = new(_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 (var item in _queue.GetConsumingEnumerable(_cts.Token))
|
foreach (T item in _queue.GetConsumingEnumerable(_cts.Token))
|
||||||
{
|
{
|
||||||
_workerAction(item);
|
_workerAction(item);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,5 +16,10 @@ 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,6 +1,7 @@
|
|||||||
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;
|
||||||
|
|
||||||
@@ -16,15 +17,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)
|
||||||
{
|
{
|
||||||
var initialConsumed = reader.Consumed;
|
long initialConsumed = reader.Consumed;
|
||||||
|
|
||||||
reader.Rewind(initialConsumed);
|
reader.Rewind(initialConsumed);
|
||||||
|
|
||||||
using (var fileStream = System.IO.File.Create(fileFullName, 4096, System.IO.FileOptions.None))
|
using (FileStream fileStream = System.IO.File.Create(fileFullName, 4096, System.IO.FileOptions.None))
|
||||||
{
|
{
|
||||||
while (reader.End == false)
|
while (reader.End == false)
|
||||||
{
|
{
|
||||||
var span = reader.CurrentSpan;
|
ReadOnlySpan<byte> 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 (var prop in props)
|
foreach (PropertyInfo 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 (var item in array)
|
foreach (object? item in array)
|
||||||
{
|
{
|
||||||
sb.Append(item);
|
sb.Append(item);
|
||||||
sb.Append(", ");
|
sb.Append(", ");
|
||||||
|
|||||||
@@ -136,11 +136,7 @@ namespace Ryujinx.Common.Logging
|
|||||||
|
|
||||||
_time = Stopwatch.StartNew();
|
_time = Stopwatch.StartNew();
|
||||||
|
|
||||||
// Logger should log to console by default
|
SetConsoleTargetEnabled(true);
|
||||||
AddTarget(new AsyncLogTargetWrapper(
|
|
||||||
new ConsoleLogTarget("console"),
|
|
||||||
1000,
|
|
||||||
AsyncLogTargetOverflowAction.Discard));
|
|
||||||
|
|
||||||
Notice = new Log(LogLevel.Notice);
|
Notice = new Log(LogLevel.Notice);
|
||||||
|
|
||||||
@@ -173,6 +169,21 @@ 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);
|
||||||
@@ -204,7 +215,7 @@ namespace Ryujinx.Common.Logging
|
|||||||
|
|
||||||
_stdErrAdapter.Dispose();
|
_stdErrAdapter.Dispose();
|
||||||
|
|
||||||
foreach (var target in _logTargets)
|
foreach (ILogTarget target in _logTargets)
|
||||||
{
|
{
|
||||||
target.Dispose();
|
target.Dispose();
|
||||||
}
|
}
|
||||||
@@ -214,9 +225,9 @@ namespace Ryujinx.Common.Logging
|
|||||||
|
|
||||||
public static IReadOnlyCollection<LogLevel> GetEnabledLevels()
|
public static IReadOnlyCollection<LogLevel> GetEnabledLevels()
|
||||||
{
|
{
|
||||||
var logs = new[] { Debug, Info, Warning, Error, Guest, AccessLog, Stub, Trace };
|
Log?[] logs = new[] { Debug, Info, Warning, Error, Guest, AccessLog, Stub, Trace };
|
||||||
List<LogLevel> levels = new(logs.Length);
|
List<LogLevel> levels = new(logs.Length);
|
||||||
foreach (var log in logs)
|
foreach (Log? log in logs)
|
||||||
{
|
{
|
||||||
if (log.HasValue)
|
if (log.HasValue)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ namespace Ryujinx.Common.Logging.Targets
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
using var signal = new ManualResetEventSlim(false);
|
using ManualResetEventSlim signal = new(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)
|
||||||
{
|
{
|
||||||
var logEventArgsJson = LogEventArgsJson.FromLogEventArgs(e);
|
LogEventArgsJson 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)
|
||||||
{
|
{
|
||||||
var (assembly, path) = ResolveManifestPath(filename);
|
(Assembly assembly, string 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)
|
||||||
{
|
{
|
||||||
var (assembly, path) = ResolveManifestPath(filename);
|
(Assembly assembly, string 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 var stream = GetStream(assembly, filename);
|
using Stream 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)
|
||||||
{
|
{
|
||||||
var (assembly, path) = ResolveManifestPath(filename);
|
(Assembly assembly, string 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 var stream = GetStream(assembly, filename);
|
using Stream 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 var stream = GetStream(assembly, filename);
|
using Stream stream = GetStream(assembly, filename);
|
||||||
if (stream == null)
|
if (stream == null)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
@@ -71,73 +71,78 @@ namespace Ryujinx.Common
|
|||||||
|
|
||||||
public static string ReadAllText(string filename)
|
public static string ReadAllText(string filename)
|
||||||
{
|
{
|
||||||
var (assembly, path) = ResolveManifestPath(filename);
|
(Assembly assembly, string 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)
|
||||||
{
|
{
|
||||||
var (assembly, path) = ResolveManifestPath(filename);
|
(Assembly assembly, string 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 var stream = GetStream(assembly, filename);
|
using Stream stream = GetStream(assembly, filename);
|
||||||
if (stream == null)
|
if (stream == null)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
using var reader = new StreamReader(stream);
|
using StreamReader reader = new(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 var stream = GetStream(assembly, filename);
|
using Stream stream = GetStream(assembly, filename);
|
||||||
if (stream == null)
|
if (stream == null)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
using var reader = new StreamReader(stream);
|
using StreamReader reader = new(stream);
|
||||||
return await reader.ReadToEndAsync();
|
return await reader.ReadToEndAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Stream GetStream(string filename)
|
public static Stream GetStream(string filename)
|
||||||
{
|
{
|
||||||
var (assembly, path) = ResolveManifestPath(filename);
|
(Assembly assembly, string 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)
|
||||||
{
|
{
|
||||||
var @namespace = assembly.GetName().Name;
|
string @namespace = assembly.GetName().Name;
|
||||||
var manifestUri = @namespace + "." + filename.Replace('/', '.');
|
string manifestUri = @namespace + "." + filename.Replace('/', '.');
|
||||||
|
|
||||||
var stream = assembly.GetManifestResourceStream(manifestUri);
|
Stream stream = assembly.GetManifestResourceStream(manifestUri);
|
||||||
|
|
||||||
return stream;
|
return stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string[] GetAllAvailableResources(string path, string ext = "")
|
public static string[] GetAllAvailableResources(string path, string ext = "")
|
||||||
{
|
{
|
||||||
return ResolveManifestPath(path).Item1.GetManifestResourceNames()
|
(Assembly assembly, string resourcePath) = ResolveManifestPath(path);
|
||||||
.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)
|
||||||
{
|
{
|
||||||
var segments = filename.Split('/', 2, StringSplitOptions.RemoveEmptyEntries);
|
string[] segments = filename.Split('/', 2, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
|
||||||
if (segments.Length >= 2)
|
if (segments.Length >= 2)
|
||||||
{
|
{
|
||||||
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
|
foreach (Assembly 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
|
||||||
var dir = new DirectoryInfo(sourceDir);
|
DirectoryInfo dir = new(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)
|
||||||
{
|
{
|
||||||
var reservedChars = new HashSet<char>(Path.GetInvalidFileNameChars());
|
HashSet<char> reservedChars = new(Path.GetInvalidFileNameChars());
|
||||||
return string.Concat(fileName.Select(c => reservedChars.Contains(c) ? '_' : c));
|
return string.Concat(fileName.Select(c => reservedChars.Contains(c) ? '_' : c));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
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
|
||||||
@@ -18,7 +19,7 @@ namespace Ryujinx.Common.Utilities
|
|||||||
|
|
||||||
public static string Format(MessagePackObject obj)
|
public static string Format(MessagePackObject obj)
|
||||||
{
|
{
|
||||||
var builder = new IndentedStringBuilder();
|
IndentedStringBuilder builder = new();
|
||||||
|
|
||||||
FormatMsgPackObj(obj, builder);
|
FormatMsgPackObj(obj, builder);
|
||||||
|
|
||||||
@@ -41,7 +42,7 @@ namespace Ryujinx.Common.Utilities
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var literal = obj.ToObject();
|
object literal = obj.ToObject();
|
||||||
|
|
||||||
if (literal is String)
|
if (literal is String)
|
||||||
{
|
{
|
||||||
@@ -88,7 +89,7 @@ namespace Ryujinx.Common.Utilities
|
|||||||
{
|
{
|
||||||
builder.Append("[ ");
|
builder.Append("[ ");
|
||||||
|
|
||||||
foreach (var b in arr)
|
foreach (byte b in arr)
|
||||||
{
|
{
|
||||||
builder.Append("0x");
|
builder.Append("0x");
|
||||||
builder.Append(ToHexChar(b >> 4));
|
builder.Append(ToHexChar(b >> 4));
|
||||||
@@ -111,7 +112,7 @@ namespace Ryujinx.Common.Utilities
|
|||||||
builder.Append("0x");
|
builder.Append("0x");
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var b in arr)
|
foreach (byte b in arr)
|
||||||
{
|
{
|
||||||
builder.Append(ToHexChar(b >> 4));
|
builder.Append(ToHexChar(b >> 4));
|
||||||
builder.Append(ToHexChar(b & 0xF));
|
builder.Append(ToHexChar(b & 0xF));
|
||||||
@@ -122,7 +123,7 @@ namespace Ryujinx.Common.Utilities
|
|||||||
|
|
||||||
private static void FormatMsgPackMap(MessagePackObject obj, IndentedStringBuilder builder)
|
private static void FormatMsgPackMap(MessagePackObject obj, IndentedStringBuilder builder)
|
||||||
{
|
{
|
||||||
var map = obj.AsDictionary();
|
MessagePackObjectDictionary map = obj.AsDictionary();
|
||||||
|
|
||||||
builder.Append('{');
|
builder.Append('{');
|
||||||
|
|
||||||
@@ -130,7 +131,7 @@ namespace Ryujinx.Common.Utilities
|
|||||||
builder.IncreaseIndent()
|
builder.IncreaseIndent()
|
||||||
.AppendLine();
|
.AppendLine();
|
||||||
|
|
||||||
foreach (var item in map)
|
foreach (KeyValuePair<MessagePackObject, MessagePackObject> item in map)
|
||||||
{
|
{
|
||||||
FormatMsgPackObj(item.Key, builder);
|
FormatMsgPackObj(item.Key, builder);
|
||||||
|
|
||||||
@@ -154,11 +155,11 @@ namespace Ryujinx.Common.Utilities
|
|||||||
|
|
||||||
private static void FormatMsgPackArray(MessagePackObject obj, IndentedStringBuilder builder)
|
private static void FormatMsgPackArray(MessagePackObject obj, IndentedStringBuilder builder)
|
||||||
{
|
{
|
||||||
var arr = obj.AsList();
|
IList<MessagePackObject> arr = obj.AsList();
|
||||||
|
|
||||||
builder.Append("[ ");
|
builder.Append("[ ");
|
||||||
|
|
||||||
foreach (var item in arr)
|
foreach (MessagePackObject item in arr)
|
||||||
{
|
{
|
||||||
FormatMsgPackObj(item, builder);
|
FormatMsgPackObj(item, builder);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
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;
|
||||||
@@ -27,7 +28,7 @@ namespace Ryujinx.Common.Utilities
|
|||||||
|
|
||||||
MemoryOwner<byte> ownedMemory = MemoryOwner<byte>.Rent(checked((int)bytesExpected));
|
MemoryOwner<byte> ownedMemory = MemoryOwner<byte>.Rent(checked((int)bytesExpected));
|
||||||
|
|
||||||
var destSpan = ownedMemory.Span;
|
Span<byte> 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)
|
||||||
{
|
{
|
||||||
var sw = new Stopwatch();
|
Stopwatch sw = new();
|
||||||
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))
|
||||||
{
|
{
|
||||||
var trimmer = new XCIFileTrimmer(filename, log);
|
XCIFileTrimmer trimmer = new(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))
|
||||||
{
|
{
|
||||||
var trimmer = new XCIFileTrimmer(filename, log);
|
XCIFileTrimmer trimmer = new(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;
|
||||||
var buffer = new byte[BufferSize];
|
byte[] buffer = new byte[BufferSize];
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
@@ -287,7 +287,7 @@ namespace Ryujinx.Common.Utilities
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var info = new FileInfo(Filename);
|
FileInfo info = new(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;
|
||||||
}
|
}
|
||||||
|
|
||||||
var outfileStream = new FileStream(_filename, FileMode.Open, FileAccess.Write, FileShare.Write);
|
FileStream outfileStream = new(_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...");
|
||||||
|
|
||||||
var info = new FileInfo(Filename);
|
FileInfo info = new(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;
|
||||||
}
|
}
|
||||||
|
|
||||||
var outfileStream = new FileStream(_filename, FileMode.Append, FileAccess.Write, FileShare.Write);
|
FileStream outfileStream = new(_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
|
||||||
{
|
{
|
||||||
var buffer = new byte[BufferSize];
|
byte[] buffer = new byte[BufferSize];
|
||||||
Array.Fill<byte>(buffer, PaddingByte);
|
Array.Fill<byte>(buffer, PaddingByte);
|
||||||
|
|
||||||
while (bytesLeftToWriteB > 0)
|
while (bytesLeftToWriteB > 0)
|
||||||
|
|||||||
+35
-296
@@ -1,15 +1,9 @@
|
|||||||
using ARMeilleure.Memory;
|
using ARMeilleure.Common;
|
||||||
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 ARMeilleure.Common
|
namespace Ryujinx.Cpu
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a table of guest address to a value.
|
/// Represents a table of guest address to a value.
|
||||||
@@ -17,84 +11,12 @@ namespace ARMeilleure.Common
|
|||||||
/// <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<AddressTablePage> _pages;
|
private readonly List<nint> _pages;
|
||||||
private TEntry _fill;
|
|
||||||
|
|
||||||
private readonly MemoryBlock _sparseFill;
|
/// <inheritdoc/>
|
||||||
private readonly SparseMemoryBlock _fillBottomLevel;
|
public AddressTableType TableType => AddressTableType.Default;
|
||||||
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; }
|
||||||
@@ -103,17 +25,7 @@ namespace ARMeilleure.Common
|
|||||||
public AddressTableLevel[] Levels { get; }
|
public AddressTableLevel[] Levels { get; }
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public TEntry Fill
|
public TEntry Fill { get; set; }
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return _fill;
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
UpdateFill(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public nint Base
|
public nint Base
|
||||||
@@ -131,44 +43,24 @@ namespace ARMeilleure.Common
|
|||||||
|
|
||||||
/// <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="Level"/>.
|
/// <see cref="AddressTableLevel"/>.
|
||||||
/// </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, bool sparse)
|
public AddressTable(AddressTableLevel[] levels)
|
||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(levels);
|
ArgumentNullException.ThrowIfNull(levels);
|
||||||
|
|
||||||
_pages = new List<AddressTablePage>(capacity: 16);
|
_pages = new List<nint>(capacity: 16);
|
||||||
|
|
||||||
Levels = levels;
|
Levels = levels;
|
||||||
Mask = 0;
|
Mask = 0;
|
||||||
|
|
||||||
foreach (var level in Levels)
|
foreach (AddressTableLevel 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>
|
||||||
@@ -176,29 +68,10 @@ namespace ARMeilleure.Common
|
|||||||
/// 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, MemoryManagerType type)
|
public static AddressTable<TEntry> CreateForArm(bool for64Bits)
|
||||||
{
|
{
|
||||||
// Assume software memory means that we don't want to use any signal handlers.
|
return new AddressTable<TEntry>(AddressTablePresets.GetArmPreset(for64Bits, false));
|
||||||
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>
|
||||||
@@ -206,17 +79,7 @@ namespace ARMeilleure.Common
|
|||||||
/// </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)
|
||||||
@@ -240,8 +103,6 @@ namespace ARMeilleure.Common
|
|||||||
|
|
||||||
long index = Levels[^1].GetValue(address);
|
long index = Levels[^1].GetValue(address);
|
||||||
|
|
||||||
EnsureMapped((nint)(page + index));
|
|
||||||
|
|
||||||
return ref page[index];
|
return ref page[index];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -260,19 +121,19 @@ namespace ARMeilleure.Common
|
|||||||
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 || nextPage == _fillBottomLevelPtr)
|
if (nextPage == null)
|
||||||
{
|
{
|
||||||
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, leaf: true);
|
nextPage = (TEntry*)Allocate(1 << nextLevel.Length, Fill);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
nextPage = (TEntry*)Allocate(1 << nextLevel.Length, GetFillValue(i), leaf: false);
|
nextPage = (TEntry*)Allocate(1 << nextLevel.Length, nint.Zero);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -283,57 +144,6 @@ namespace ARMeilleure.Common
|
|||||||
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>
|
||||||
@@ -343,91 +153,35 @@ namespace ARMeilleure.Common
|
|||||||
if (_table == null)
|
if (_table == null)
|
||||||
{
|
{
|
||||||
if (Levels.Length == 1)
|
if (Levels.Length == 1)
|
||||||
_table = (TEntry**)Allocate(1 << Levels[0].Length, Fill, leaf: true);
|
_table = (TEntry**)Allocate(1 << Levels[0].Length, Fill);
|
||||||
else
|
else
|
||||||
_table = (TEntry**)Allocate(1 << Levels[0].Length, GetFillValue(0), leaf: false);
|
_table = (TEntry**)Allocate(1 << Levels[0].Length, nint.Zero);
|
||||||
}
|
}
|
||||||
|
|
||||||
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, bool leaf) where T : unmanaged
|
private nint Allocate<T>(int length, T fill) where T : unmanaged
|
||||||
{
|
{
|
||||||
var size = sizeof(T) * length;
|
int size = sizeof(T) * length;
|
||||||
|
|
||||||
AddressTablePage page;
|
nint address = (nint)NativeAllocator.Instance.Allocate((uint)size);
|
||||||
|
|
||||||
if (Sparse && leaf)
|
Span<T> span = new((void*)address, length);
|
||||||
{
|
span.Fill(fill);
|
||||||
_sparseLock.EnterWriteLock();
|
|
||||||
|
|
||||||
SparseMemoryBlock block;
|
_pages.Add(address);
|
||||||
|
|
||||||
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 page.Address;
|
return address;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -446,32 +200,17 @@ namespace ARMeilleure.Common
|
|||||||
/// <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)
|
||||||
{
|
{
|
||||||
foreach (var page in _pages)
|
return;
|
||||||
{
|
|
||||||
if (!page.IsSparse)
|
|
||||||
{
|
|
||||||
Marshal.FreeHGlobal(page.Address);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Sparse)
|
|
||||||
{
|
|
||||||
foreach (TableSparseBlock block in _sparseReserved)
|
|
||||||
{
|
|
||||||
block.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
_sparseReserved.Clear();
|
|
||||||
|
|
||||||
_fillBottomLevel.Dispose();
|
|
||||||
_sparseFill.Dispose();
|
|
||||||
_sparseLock.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
_disposed = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach (nint page in _pages)
|
||||||
|
{
|
||||||
|
Marshal.FreeHGlobal(page);
|
||||||
|
}
|
||||||
|
|
||||||
|
_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, var ipaAllocator) = HvVm.CreateAddressSpace(backingMemory);
|
(_asBase, HvIpaAllocator ipaAllocator) = HvVm.CreateAddressSpace(backingMemory);
|
||||||
_backingSize = backingMemory.Size;
|
_backingSize = backingMemory.Size;
|
||||||
|
|
||||||
_userRange = new HvAddressSpaceRange(ipaAllocator);
|
_userRange = new HvAddressSpaceRange(ipaAllocator);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
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
|
||||||
@@ -32,7 +33,7 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled, string cacheSelector)
|
public IDiskCacheLoadState LoadDiskCache(PtcCacheInfo cacheInfo, bool enabled)
|
||||||
{
|
{
|
||||||
return new DummyDiskCacheLoadState();
|
return new DummyDiskCacheLoadState();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
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;
|
||||||
@@ -17,9 +18,7 @@ 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -69,9 +68,7 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (value)
|
if (value)
|
||||||
{
|
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,11 +79,13 @@ 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 readonly ExceptionCallbacks _exceptionCallbacks;
|
||||||
|
|
||||||
private int _interruptRequested;
|
private int _interruptRequested;
|
||||||
|
|
||||||
|
// GPU Sync control
|
||||||
|
private int _syncCounter;
|
||||||
|
private int _strongSyncCounter;
|
||||||
|
|
||||||
public HvExecutionContext(ICounter counter, ExceptionCallbacks exceptionCallbacks)
|
public HvExecutionContext(ICounter counter, ExceptionCallbacks exceptionCallbacks)
|
||||||
{
|
{
|
||||||
_counter = counter;
|
_counter = counter;
|
||||||
@@ -108,38 +107,17 @@ 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()
|
private void InterruptHandler() => _exceptionCallbacks.InterruptCallback?.Invoke(this);
|
||||||
{
|
private void BreakHandler(ulong address, int imm) => _exceptionCallbacks.BreakCallback?.Invoke(this, address, imm);
|
||||||
_exceptionCallbacks.InterruptCallback?.Invoke(this);
|
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);
|
||||||
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()
|
||||||
@@ -161,13 +139,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)
|
||||||
{
|
|
||||||
_impl.ElrEl1 = value;
|
_impl.ElrEl1 = value;
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
|
||||||
_impl.Pc = value;
|
_impl.Pc = value;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,9 +155,11 @@ 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();
|
HvApi.hv_vcpu_set_reg(vcpu.Handle, HvReg.PC, address).ThrowOnError();
|
||||||
|
|
||||||
|
_syncCounter = 0;
|
||||||
|
_strongSyncCounter = 0;
|
||||||
|
|
||||||
while (Running)
|
while (Running)
|
||||||
{
|
{
|
||||||
if (Interlocked.CompareExchange(ref _shouldStep, 0, 1) == 1)
|
if (Interlocked.CompareExchange(ref _shouldStep, 0, 1) == 1)
|
||||||
@@ -192,16 +168,23 @@ 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 |= (1 << 21);
|
spsr |= (1U << 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 |= (1 << 21);
|
Pstate |= (1U << 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;
|
||||||
@@ -212,9 +195,7 @@ 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();
|
||||||
@@ -245,10 +226,31 @@ 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();
|
||||||
|
|
||||||
@@ -259,16 +261,20 @@ 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));
|
||||||
@@ -278,21 +284,23 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -305,7 +313,6 @@ 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))
|
||||||
@@ -349,9 +356,7 @@ 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)
|
||||||
@@ -369,8 +374,6 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
_impl = newContext;
|
_impl = newContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose() { }
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
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;
|
||||||
@@ -14,8 +16,31 @@ 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
|
||||||
@@ -33,155 +58,293 @@ 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
|
get { lock (_registerLock) return GetRegCached(HvReg.PC, ref _pc, "PC"); }
|
||||||
{
|
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
|
get { lock (_registerLock) return GetSysRegCached(HvSysReg.ELR_EL1, ref _elrEl1, "ELR_EL1"); }
|
||||||
{
|
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
|
get { lock (_registerLock) return GetSysRegCached(HvSysReg.ESR_EL1, ref _esrEl1, "ESR_EL1"); }
|
||||||
{
|
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
|
get { lock (_registerLock) return (long)GetSysRegCached(HvSysReg.TPIDR_EL0, ref _tpidrEl0, "TPIDR_EL0"); }
|
||||||
{
|
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
|
get { lock (_registerLock) return (long)GetSysRegCached(HvSysReg.TPIDRRO_EL0, ref _tpidrroEl0, "TPIDRRO_EL0"); }
|
||||||
{
|
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
|
||||||
{
|
{
|
||||||
HvApi.hv_vcpu_get_reg(_vcpu, HvReg.CPSR, out ulong cpsr).ThrowOnError();
|
lock (_registerLock)
|
||||||
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
|
||||||
{
|
{
|
||||||
HvApi.hv_vcpu_set_reg(_vcpu, HvReg.CPSR, (ulong)value).ThrowOnError();
|
lock (_registerLock)
|
||||||
|
{
|
||||||
|
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
|
get { lock (_registerLock) return (uint)GetRegCached(HvReg.FPCR, ref _fpcr, "FPCR"); }
|
||||||
{
|
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
|
get { lock (_registerLock) return (uint)GetRegCached(HvReg.FPSR, ref _fpsr, "FPSR"); }
|
||||||
{
|
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)
|
||||||
{
|
{
|
||||||
if (index == 31)
|
lock (_registerLock)
|
||||||
{
|
{
|
||||||
HvApi.hv_vcpu_get_sys_reg(_vcpu, HvSysReg.SP_EL0, out ulong value).ThrowOnError();
|
ulong value;
|
||||||
return value;
|
string regName = index == 31 ? "SP_EL0" : $"X{index}";
|
||||||
}
|
|
||||||
else
|
if (index == 31)
|
||||||
{
|
{
|
||||||
HvApi.hv_vcpu_get_reg(_vcpu, HvReg.X0 + (uint)index, out ulong value).ThrowOnError();
|
HvResult res = HvApi.hv_vcpu_get_sys_reg(_vcpu, HvSysReg.SP_EL0, out value);
|
||||||
return value;
|
if (res == HvResult.BadArgument)
|
||||||
|
{
|
||||||
|
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)
|
||||||
{
|
{
|
||||||
if (index == 31)
|
lock (_registerLock)
|
||||||
{
|
{
|
||||||
HvApi.hv_vcpu_set_sys_reg(_vcpu, HvSysReg.SP_EL0, value).ThrowOnError();
|
string regName = index == 31 ? "SP_EL0" : $"X{index}";
|
||||||
}
|
|
||||||
else
|
if (index == 31)
|
||||||
{
|
{
|
||||||
HvApi.hv_vcpu_set_reg(_vcpu, HvReg.X0 + (uint)index, value).ThrowOnError();
|
HvResult res = HvApi.hv_vcpu_set_sys_reg(_vcpu, HvSysReg.SP_EL0, value);
|
||||||
|
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)
|
||||||
{
|
{
|
||||||
HvApi.hv_vcpu_get_simd_fp_reg(_vcpu, HvSimdFPReg.Q0 + (uint)index, out HvSimdFPUchar16 value).ThrowOnError();
|
lock (_registerLock)
|
||||||
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)
|
||||||
{
|
{
|
||||||
_setSimdFpReg(_vcpu, HvSimdFPReg.Q0 + (uint)index, value, _setSimdFpRegNativePtr).ThrowOnError();
|
lock (_registerLock)
|
||||||
|
{
|
||||||
|
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)
|
||||||
{
|
{
|
||||||
var allocation = Allocate(size, alignment, CreateBlock);
|
Allocation 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 [];
|
||||||
}
|
}
|
||||||
|
|
||||||
var guestRegions = GetPhysicalRegionsImpl(va, size);
|
List<MemoryRange> guestRegions = GetPhysicalRegionsImpl(va, size);
|
||||||
if (guestRegions == null)
|
if (guestRegions == null)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var regions = new HostMemoryRange[guestRegions.Count];
|
HostMemoryRange[] regions = new HostMemoryRange[guestRegions.Count];
|
||||||
|
|
||||||
for (int i = 0; i < regions.Length; i++)
|
for (int i = 0; i < regions.Length; i++)
|
||||||
{
|
{
|
||||||
var guestRegion = guestRegions[i];
|
MemoryRange 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);
|
||||||
|
|
||||||
var regions = new List<MemoryRange>();
|
List<MemoryRange> regions = new();
|
||||||
|
|
||||||
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 var timeBaseInfo);
|
int result = TimeApi.mach_timebase_info(out MachTimebaseInfo 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
var rwx = HvMemoryFlags.Read | HvMemoryFlags.Write | HvMemoryFlags.Exec;
|
HvMemoryFlags 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,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using ARMeilleure.Translation.PTC;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu
|
namespace Ryujinx.Cpu
|
||||||
{
|
{
|
||||||
@@ -44,11 +45,10 @@ 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="titleIdText">Title ID of the application in padded hex form</param>
|
/// <param name="cacheInfo">Identity and selector for the process-owned disk cache</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(string titleIdText, string displayVersion, bool enabled, string cacheSelector);
|
IDiskCacheLoadState LoadDiskCache(PtcCacheInfo cacheInfo, bool enabled);
|
||||||
|
|
||||||
/// <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);
|
||||||
|
|
||||||
(var leftAllocation, PrivateAllocation) = PrivateAllocation.Split(leftSize);
|
(PrivateMemoryAllocation leftAllocation, PrivateAllocation) = PrivateAllocation.Split(leftSize);
|
||||||
|
|
||||||
PrivateMapping left = new(Address, leftSize, leftAllocation);
|
PrivateMapping left = new(Address, leftSize, leftAllocation);
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
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
|
||||||
@@ -9,13 +10,15 @@ 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);
|
|
||||||
_translator = new Translator(new JitMemoryAllocator(forJit: true), memory, _functionTable);
|
bool sparse = memory.Type is not MemoryManagerType.SoftwareMmu and not MemoryManagerType.SoftwarePageTable;
|
||||||
|
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())
|
||||||
{
|
{
|
||||||
@@ -49,15 +52,15 @@ namespace Ryujinx.Cpu.Jit
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled, string cacheSelector)
|
public IDiskCacheLoadState LoadDiskCache(PtcCacheInfo cacheInfo, bool enabled)
|
||||||
{
|
{
|
||||||
return new JitDiskCacheLoadState(_translator.LoadDiskCache(titleIdText, displayVersion, enabled, cacheSelector));
|
return new JitDiskCacheLoadState(_translator.LoadDiskCache(cacheInfo, enabled));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void PrepareCodeRange(ulong address, ulong size)
|
public void PrepareCodeRange(ulong address, ulong size)
|
||||||
{
|
{
|
||||||
_functionTable.SignalCodeRange(address, size);
|
_translator.FunctionTable.SignalCodeRange(address, size);
|
||||||
_translator.PrepareCodeRange(address, size);
|
_translator.PrepareCodeRange(address, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -257,17 +257,17 @@ namespace Ryujinx.Cpu.Jit
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
var guestRegions = GetPhysicalRegionsImpl(va, size);
|
List<MemoryRange> guestRegions = GetPhysicalRegionsImpl(va, size);
|
||||||
if (guestRegions == null)
|
if (guestRegions == null)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var regions = new HostMemoryRange[guestRegions.Count];
|
HostMemoryRange[] regions = new HostMemoryRange[guestRegions.Count];
|
||||||
|
|
||||||
for (int i = 0; i < regions.Length; i++)
|
for (int i = 0; i < regions.Length; i++)
|
||||||
{
|
{
|
||||||
var guestRegion = guestRegions[i];
|
MemoryRange 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);
|
||||||
|
|
||||||
var regions = new List<MemoryRange>();
|
List<MemoryRange> regions = new();
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
var regions = new List<MemoryRange>();
|
List<MemoryRange> regions = new();
|
||||||
|
|
||||||
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))
|
||||||
{
|
{
|
||||||
var target = memoryBlock.GetSpan(offset, data.Length);
|
Span<byte> 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
var regions = new List<HostMemoryRange>();
|
List<HostMemoryRange> regions = new();
|
||||||
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);
|
||||||
|
|
||||||
var regions = new List<MemoryRange>();
|
List<MemoryRange> regions = new();
|
||||||
|
|
||||||
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,
|
||||||
AddressTable<ulong> funcTable,
|
IAddressTable<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,
|
||||||
AddressTable<ulong> funcTable,
|
IAddressTable<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 AddressTable<ulong> FuncTable;
|
public readonly IAddressTable<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,
|
||||||
AddressTable<ulong> funcTable,
|
IAddressTable<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, AddressTable<ulong> funcTable, nint dispatchStubPtr, bool isThumb)
|
public static CompiledFunction Compile(CpuPreset cpuPreset, IMemoryManager memoryManager, ulong address, IAddressTable<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,6 +3,7 @@ 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;
|
||||||
|
|
||||||
@@ -132,7 +133,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
|||||||
RegisterAllocator regAlloc,
|
RegisterAllocator regAlloc,
|
||||||
TailMerger tailMerger,
|
TailMerger tailMerger,
|
||||||
Action writeEpilogue,
|
Action writeEpilogue,
|
||||||
AddressTable<ulong> funcTable,
|
IAddressTable<ulong> funcTable,
|
||||||
nint funcPtr,
|
nint funcPtr,
|
||||||
int spillBaseOffset,
|
int spillBaseOffset,
|
||||||
uint nextAddress,
|
uint nextAddress,
|
||||||
@@ -143,7 +144,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 is { Sparse: true };
|
funcTable.TableType == AddressTableType.Sparse;
|
||||||
|
|
||||||
if (guestAddress.Kind == OperandKind.Constant)
|
if (guestAddress.Kind == OperandKind.Constant)
|
||||||
{
|
{
|
||||||
@@ -189,7 +190,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 2 levels.
|
// Inline table lookup. Only enabled when the sparse function table is enabled with 1 level.
|
||||||
|
|
||||||
Operand indexReg = Register(NextFreeRegister(tempRegister + 1, tempGuestAddress));
|
Operand indexReg = Register(NextFreeRegister(tempRegister + 1, tempGuestAddress));
|
||||||
|
|
||||||
@@ -203,18 +204,15 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
|||||||
// Index into the table.
|
// Index into the table.
|
||||||
asm.Mov(rn, tableBase);
|
asm.Mov(rn, tableBase);
|
||||||
|
|
||||||
for (int i = 0; i < funcTable.Levels.Length; i++)
|
AddressTableLevel level = funcTable.Levels.Last();
|
||||||
{
|
asm.Ubfx(indexReg, guestAddress, level.Index, level.Length);
|
||||||
var level = funcTable.Levels[i];
|
asm.Lsl(indexReg, indexReg, Const(3));
|
||||||
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,
|
||||||
AddressTable<ulong> funcTable,
|
IAddressTable<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 AddressTable<ulong> FuncTable;
|
public readonly IAddressTable<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,
|
||||||
AddressTable<ulong> funcTable,
|
IAddressTable<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, AddressTable<ulong> funcTable, nint dispatchStubPtr)
|
public static CompiledFunction Compile(CpuPreset cpuPreset, IMemoryManager memoryManager, ulong address, IAddressTable<ulong> funcTable, nint dispatchStubPtr)
|
||||||
{
|
{
|
||||||
MultiBlock multiBlock = Decoder.DecodeMulti(cpuPreset, memoryManager, address);
|
MultiBlock multiBlock = Decoder.DecodeMulti(cpuPreset, memoryManager, address);
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ 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;
|
||||||
@@ -214,7 +215,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
|
|||||||
RegisterAllocator regAlloc,
|
RegisterAllocator regAlloc,
|
||||||
TailMerger tailMerger,
|
TailMerger tailMerger,
|
||||||
Action writeEpilogue,
|
Action writeEpilogue,
|
||||||
AddressTable<ulong> funcTable,
|
IAddressTable<ulong> funcTable,
|
||||||
nint dispatchStubPtr,
|
nint dispatchStubPtr,
|
||||||
InstName name,
|
InstName name,
|
||||||
ulong pc,
|
ulong pc,
|
||||||
@@ -297,7 +298,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
|
|||||||
RegisterAllocator regAlloc,
|
RegisterAllocator regAlloc,
|
||||||
TailMerger tailMerger,
|
TailMerger tailMerger,
|
||||||
Action writeEpilogue,
|
Action writeEpilogue,
|
||||||
AddressTable<ulong> funcTable,
|
IAddressTable<ulong> funcTable,
|
||||||
nint funcPtr,
|
nint funcPtr,
|
||||||
int spillBaseOffset,
|
int spillBaseOffset,
|
||||||
ulong pc,
|
ulong pc,
|
||||||
@@ -308,7 +309,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 is { Sparse: true };
|
funcTable.TableType == AddressTableType.Sparse;
|
||||||
|
|
||||||
if (guestAddress.Kind == OperandKind.Constant)
|
if (guestAddress.Kind == OperandKind.Constant)
|
||||||
{
|
{
|
||||||
@@ -354,7 +355,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 2 levels.
|
// Inline table lookup. Only enabled when the sparse function table is enabled with 1 level.
|
||||||
|
|
||||||
Operand indexReg = Register(NextFreeRegister(tempRegister + 1, tempGuestAddress));
|
Operand indexReg = Register(NextFreeRegister(tempRegister + 1, tempGuestAddress));
|
||||||
|
|
||||||
@@ -368,18 +369,15 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
|
|||||||
// Index into the table.
|
// Index into the table.
|
||||||
asm.Mov(rn, tableBase);
|
asm.Mov(rn, tableBase);
|
||||||
|
|
||||||
for (int i = 0; i < funcTable.Levels.Length; i++)
|
AddressTableLevel level = funcTable.Levels.Last();
|
||||||
{
|
asm.Ubfx(indexReg, guestAddress, level.Index, level.Length);
|
||||||
var level = funcTable.Levels[i];
|
asm.Lsl(indexReg, indexReg, Const(3));
|
||||||
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,11 +24,30 @@ 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 capacity)
|
public CacheMemoryAllocator(int regionSize, int initialRegionCount = 1)
|
||||||
{
|
{
|
||||||
_blocks.Add(new MemoryBlock(0, capacity));
|
_regionCount = 0;
|
||||||
|
_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)
|
||||||
@@ -101,12 +120,13 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
index = ~index;
|
index = ~index;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (index < _blocks.Count)
|
int endOffs = block.Offset + block.Size;
|
||||||
|
|
||||||
|
// 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);
|
||||||
@@ -114,7 +134,8 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (index > 0)
|
// Don't merge blocks from different allocations
|
||||||
|
if (index > 0 && block.Offset % _regionSize != 0)
|
||||||
{
|
{
|
||||||
MemoryBlock prev = _blocks[index - 1];
|
MemoryBlock prev = _blocks[index - 1];
|
||||||
|
|
||||||
|
|||||||
@@ -4,83 +4,50 @@ 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
|
||||||
{
|
{
|
||||||
static partial class JitCache
|
partial class JitCache : IDisposable
|
||||||
{
|
{
|
||||||
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.
|
||||||
// TODO: JIT Cache size should be application dependent, not global.
|
private const uint CacheSize = 256 * 1024 * 1024; // Megabytes * Size of Megabytes (since its in bytes).
|
||||||
private const int CacheSize = 1024 * (1024 * 1024); // Megabytes * Size of Megabytes (since its in bytes).
|
|
||||||
|
|
||||||
private static JitCacheInvalidation _jitCacheInvalidator;
|
private readonly JitCacheInvalidation _jitCacheInvalidator;
|
||||||
|
|
||||||
private static CacheMemoryAllocator _cacheAllocator;
|
private readonly CacheMemoryAllocator _cacheAllocator;
|
||||||
|
|
||||||
private static readonly List<CacheEntry> _cacheEntries = [];
|
private readonly List<CacheEntry> _cacheEntries = [];
|
||||||
|
|
||||||
private static readonly Lock _lock = new();
|
private readonly Lock _lock = new();
|
||||||
private static bool _initialized;
|
|
||||||
private static readonly List<ReservedRegion> _jitRegions = [];
|
|
||||||
private static int _activeRegionIndex = 0;
|
|
||||||
|
|
||||||
[SupportedOSPlatform("windows")]
|
private readonly List<ReservedRegion> _jitRegions = [];
|
||||||
[LibraryImport("kernel32.dll", SetLastError = true)]
|
|
||||||
public static partial nint FlushInstructionCache(nint hProcess, nint lpAddress, nuint dwSize);
|
|
||||||
|
|
||||||
[SupportedOSPlatform("macos")]
|
public JitCache(IJitMemoryAllocator allocator)
|
||||||
[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)
|
||||||
{
|
{
|
||||||
if (_initialized)
|
_jitRegions.Add(new(allocator, CacheSize));
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var firstRegion = new ReservedRegion(allocator, CacheSize);
|
_cacheAllocator = new CacheMemoryAllocator((int)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 static nint Map(ReadOnlySpan<byte> code)
|
public unsafe nint Map(ReadOnlySpan<byte> code)
|
||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
Debug.Assert(_initialized);
|
|
||||||
|
|
||||||
int funcOffset = Allocate(code.Length);
|
int funcOffset = Allocate(code.Length);
|
||||||
ReservedRegion targetRegion = _jitRegions[_activeRegionIndex];
|
nint funcPtr = GetFunctionPtr(funcOffset);
|
||||||
nint funcPtr = targetRegion.Pointer + funcOffset;
|
|
||||||
|
|
||||||
if (OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
if (OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
||||||
{
|
{
|
||||||
@@ -94,18 +61,11 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ReprotectAsWritable(targetRegion, funcOffset, code.Length);
|
ReprotectAsWritable(funcOffset, code.Length);
|
||||||
code.CopyTo(new Span<byte>((void*)funcPtr, code.Length));
|
code.CopyTo(new Span<byte>((void*)funcPtr, code.Length));
|
||||||
ReprotectAsExecutable(targetRegion, funcOffset, code.Length);
|
ReprotectAsExecutable(funcOffset, code.Length);
|
||||||
|
|
||||||
if (OperatingSystem.IsWindows() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
_jitCacheInvalidator?.Invalidate(funcPtr, (ulong)code.Length);
|
||||||
{
|
|
||||||
FlushInstructionCache(Process.GetCurrentProcess().Handle, funcPtr, (nuint)code.Length);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_jitCacheInvalidator?.Invalidate(funcPtr, (ulong)code.Length);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Add(funcOffset, code.Length);
|
Add(funcOffset, code.Length);
|
||||||
@@ -114,21 +74,20 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Unmap(nint pointer)
|
public void Unmap(nint pointer)
|
||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
Debug.Assert(_initialized);
|
for (int i = 0; i < _jitRegions.Count; i++)
|
||||||
|
|
||||||
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 + CacheSize).ToInt64())
|
pointer.ToInt64() >= (region.Pointer + (nint)CacheSize).ToInt64())
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
int funcOffset = (int)(pointer.ToInt64() - region.Pointer.ToInt64());
|
int funcOffset = (int)(pointer.ToInt64() - region.Pointer.ToInt64() + i * CacheSize);
|
||||||
|
|
||||||
if (TryFind(funcOffset, out CacheEntry entry, out int entryIndex) && entry.Offset == funcOffset)
|
if (TryFind(funcOffset, out CacheEntry entry, out int entryIndex) && entry.Offset == funcOffset)
|
||||||
{
|
{
|
||||||
@@ -141,59 +100,67 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ReprotectAsWritable(ReservedRegion region, int offset, int size)
|
private void ReprotectAsWritable(int offset, int size)
|
||||||
{
|
{
|
||||||
int endOffs = offset + size;
|
int endOffs = offset + size;
|
||||||
int regionStart = offset & ~_pageMask;
|
int regionStart = (offset % (int)CacheSize) & ~_pageMask;
|
||||||
int regionEnd = (endOffs + _pageMask) & ~_pageMask;
|
int regionEnd = endOffs % (int)CacheSize == 0
|
||||||
|
? (((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 static void ReprotectAsExecutable(ReservedRegion region, int offset, int size)
|
private void ReprotectAsExecutable(int offset, int size)
|
||||||
{
|
{
|
||||||
int endOffs = offset + size;
|
int endOffs = offset + size;
|
||||||
int regionStart = offset & ~_pageMask;
|
int regionStart = (offset % (int)CacheSize) & ~_pageMask;
|
||||||
int regionEnd = (endOffs + _pageMask) & ~_pageMask;
|
int regionEnd = endOffs % (int)CacheSize == 0
|
||||||
|
? (((int)CacheSize) + _pageMask) & ~_pageMask
|
||||||
|
: ((endOffs % (int)CacheSize) + _pageMask) & ~_pageMask;
|
||||||
|
|
||||||
region.Block.MapAsRx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
GetRegion(offset).Block.MapAsRx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int Allocate(int codeSize)
|
private int Allocate(int codeSize)
|
||||||
{
|
{
|
||||||
codeSize = AlignCodeSize(codeSize);
|
codeSize = AlignCodeSize(codeSize);
|
||||||
|
|
||||||
for (int i = _activeRegionIndex; i < _jitRegions.Count; i++)
|
int allocOffset = _cacheAllocator.Allocate(codeSize);
|
||||||
{
|
|
||||||
int allocOffset = _cacheAllocator.Allocate(codeSize);
|
|
||||||
|
|
||||||
if (allocOffset >= 0)
|
if (allocOffset >= 0)
|
||||||
{
|
{
|
||||||
_jitRegions[i].ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize);
|
GetRegion(allocOffset).ExpandIfNeeded((ulong)(allocOffset % (int)CacheSize) + (ulong)codeSize);
|
||||||
_activeRegionIndex = i;
|
return allocOffset;
|
||||||
return allocOffset;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int exhaustedRegion = _activeRegionIndex;
|
_cacheAllocator.AddNewBlocks(1);
|
||||||
var newRegion = new ReservedRegion(_jitRegions[0].Allocator, CacheSize);
|
ReservedRegion newRegion = new(_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;
|
|
||||||
|
|
||||||
int newRegionNumber = _activeRegionIndex;
|
allocOffset = _cacheAllocator.Allocate(codeSize);
|
||||||
|
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!");
|
||||||
}
|
}
|
||||||
|
|
||||||
newRegion.ExpandIfNeeded((ulong)allocOffsetNew + (ulong)codeSize);
|
GetRegion(allocOffset).ExpandIfNeeded((ulong)(allocOffset % (int)CacheSize) + (ulong)codeSize);
|
||||||
return allocOffsetNew;
|
return allocOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
@@ -201,7 +168,7 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
return checked(codeSize + (CodeAlignment - 1)) & ~(CodeAlignment - 1);
|
return checked(codeSize + (CodeAlignment - 1)) & ~(CodeAlignment - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void Add(int offset, int size)
|
private void Add(int offset, int size)
|
||||||
{
|
{
|
||||||
CacheEntry entry = new(offset, size);
|
CacheEntry entry = new(offset, size);
|
||||||
|
|
||||||
@@ -215,7 +182,7 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
_cacheEntries.Insert(index, entry);
|
_cacheEntries.Insert(index, entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool TryFind(int offset, out CacheEntry entry, out int entryIndex)
|
public bool TryFind(int offset, out CacheEntry entry, out int entryIndex)
|
||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
@@ -238,5 +205,13 @@ 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 var entry))
|
if ((_threadLocalCache ??= new()).TryGetValue(guestAddress, out ThreadLocalCacheEntry 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;
|
||||||
|
|
||||||
var state = _labels[label.AsInt32()];
|
LabelState 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;
|
||||||
|
|
||||||
var state = _labels[label.AsInt32()];
|
LabelState 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;
|
||||||
|
|
||||||
var state = _labels[label.AsInt32()];
|
LabelState 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;
|
||||||
|
|
||||||
var state = _labels[label.AsInt32()];
|
LabelState 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)
|
||||||
{
|
{
|
||||||
var zr = new Operand(ZrRegister, RegisterType.Integer, rd.Type);
|
Operand zr = new(ZrRegister, RegisterType.Integer, rd.Type);
|
||||||
Csinc(rd, zr, zr, (ArmCondition)((int)condition ^ 1));
|
Csinc(rd, zr, zr, (ArmCondition)((int)condition ^ 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
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;
|
||||||
|
|
||||||
@@ -9,15 +10,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;
|
||||||
|
|
||||||
_functionTable = AddressTable<ulong>.CreateForArm(for64Bit, memory.Type);
|
bool sparse = memory.Type is not MemoryManagerType.SoftwareMmu and not MemoryManagerType.SoftwarePageTable;
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
@@ -46,7 +47,7 @@ namespace Ryujinx.Cpu.LightningJit
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled, string cacheSelector)
|
public IDiskCacheLoadState LoadDiskCache(PtcCacheInfo cacheInfo, bool enabled)
|
||||||
{
|
{
|
||||||
return new DummyDiskCacheLoadState();
|
return new DummyDiskCacheLoadState();
|
||||||
}
|
}
|
||||||
@@ -54,7 +55,7 @@ namespace Ryujinx.Cpu.LightningJit
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void PrepareCodeRange(ulong address, ulong size)
|
public void PrepareCodeRange(ulong address, ulong size)
|
||||||
{
|
{
|
||||||
_functionTable.SignalCodeRange(address, size);
|
_translator.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