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

Some files were not shown because too many files have changed in this diff Show More