mirror of
https://git.ryujinx.app/projects/Kenji-NX.git
synced 2026-09-27 16:37:53 +02:00
Compare commits
19
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
39544e323c | ||
|
|
24932b7e1b | ||
|
|
2aaf34937d | ||
|
|
bce3426d26 | ||
|
|
51ca73cb10 | ||
|
|
cd3221abdc | ||
|
|
19dd23c288 | ||
|
|
20f1cef9b2 | ||
|
|
2f400491d5 | ||
|
|
f6351e3fff | ||
|
|
f15ddcf9c4 | ||
|
|
88d904acde | ||
|
|
fbcf57bef5 | ||
|
|
8bfb670e65 | ||
|
|
e00bece65a | ||
|
|
2a03f7b1d4 | ||
|
|
6c7b7d6fc4 | ||
|
|
c43db7d5f6 | ||
|
|
cc92af12a4 |
@@ -173,6 +173,3 @@ PublishProfiles/
|
|||||||
|
|
||||||
# Glade backup files
|
# Glade backup files
|
||||||
*.glade~
|
*.glade~
|
||||||
|
|
||||||
# Log files
|
|
||||||
/logs/
|
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
[submodule "src/KenjinxAndroid/app/src/main/cpp/libraries/adrenotools"]
|
|
||||||
path = src/KenjinxAndroid/app/src/main/cpp/libraries/adrenotools
|
|
||||||
url = https://git.ryujinx.app/kenji-nx/libadrenotools.git
|
|
||||||
[submodule "src/KenjinxAndroid/app/src/main/cpp/libraries/openal"]
|
|
||||||
path = src/KenjinxAndroid/app/src/main/cpp/libraries/openal
|
|
||||||
url = https://git.ryujinx.app/kenji-nx/openal-soft.git
|
|
||||||
[submodule "src/KenjinxAndroid/app/src/main/cpp/libraries/adrenotools/lib/linkernsbypass"]
|
|
||||||
path = src/KenjinxAndroid/app/src/main/cpp/libraries/adrenotools/lib/linkernsbypass
|
|
||||||
url = https://git.ryujinx.app/kenji-nx/liblinkernsbypass.git
|
|
||||||
+33
-35
@@ -3,54 +3,52 @@
|
|||||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageVersion Include="Avalonia" Version="11.3.13" />
|
<PackageVersion Include="Avalonia" Version="11.0.13" />
|
||||||
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.13" />
|
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.0.13" />
|
||||||
<PackageVersion Include="Avalonia.Desktop" Version="11.3.13" />
|
<PackageVersion Include="Avalonia.Desktop" Version="11.0.13" />
|
||||||
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.13" />
|
<PackageVersion Include="Avalonia.Diagnostics" Version="11.0.13" />
|
||||||
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.3.13" />
|
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.0.13" />
|
||||||
<PackageVersion Include="Svg.Controls.Avalonia" Version="11.3.9.5" />
|
<PackageVersion Include="Avalonia.Svg" Version="11.0.0.19" />
|
||||||
<PackageVersion Include="Svg.Controls.Skia.Avalonia" Version="11.3.9.5" />
|
<PackageVersion Include="Avalonia.Svg.Skia" Version="11.0.0.19" />
|
||||||
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
|
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
|
||||||
<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.2.1.24" />
|
||||||
<PackageVersion Include="DynamicData" Version="9.4.31" />
|
<PackageVersion Include="DynamicData" Version="9.0.4" />
|
||||||
<PackageVersion Include="FluentAvaloniaUI" Version="2.4.1" />
|
<PackageVersion Include="FluentAvaloniaUI" Version="2.0.5" />
|
||||||
<PackageVersion Include="Gommon" Version="2.8.1.1" />
|
<PackageVersion Include="Gommon" Version="2.7.1.1" />
|
||||||
<PackageVersion Include="Humanizer" Version="3.0.10" />
|
<PackageVersion Include="Humanizer" Version="2.14.1" />
|
||||||
<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.9.2" />
|
||||||
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.17.0" />
|
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.9.0" />
|
||||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.3.0" />
|
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
|
||||||
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.1" />
|
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.1" />
|
||||||
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
|
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
|
||||||
<PackageVersion Include="NetCoreServer" Version="8.0.7" />
|
<PackageVersion Include="NetCoreServer" Version="8.0.7" />
|
||||||
<PackageVersion Include="Newtonsoft.Json" Version="13.0.4" />
|
<PackageVersion Include="NUnit" Version="3.13.3" />
|
||||||
<PackageVersion Include="NUnit" Version="4.5.1" />
|
<PackageVersion Include="NUnit3TestAdapter" Version="4.1.0" />
|
||||||
<PackageVersion Include="NUnit3TestAdapter" Version="6.2.0" />
|
<PackageVersion Include="OpenTK.Core" Version="4.8.2" />
|
||||||
<PackageVersion Include="OpenTK.Core" Version="4.9.4" />
|
<PackageVersion Include="OpenTK.Graphics" Version="4.8.2" />
|
||||||
<PackageVersion Include="OpenTK.Graphics" Version="4.9.4" />
|
<PackageVersion Include="OpenTK.Audio.OpenAL" Version="4.8.2" />
|
||||||
<PackageVersion Include="OpenTK.Audio.OpenAL" Version="4.9.4" />
|
<PackageVersion Include="OpenTK.Windowing.GraphicsLibraryFramework" Version="4.8.2" />
|
||||||
<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.Dependencies" Version="1.21.0.1" />
|
||||||
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies.Linux" Version="6.1.3-build5" />
|
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies.Linux" Version="6.1.2-build4" />
|
||||||
<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.3-build5" />
|
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies.Windows" Version="6.1.2-build4" />
|
||||||
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
|
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
|
||||||
<PackageVersion Include="Ryujinx.LibHac" Version="0.21.0-alpha.128" />
|
<PackageVersion Include="Ryujinx.LibHac" Version="0.20.0-alpha.103" />
|
||||||
<PackageVersion Include="Ryujinx.SDL3-CS" Version="2026.707.0" />
|
<PackageVersion Include="Ryujinx.SDL2-CS-Redux" Version="2.32.6" />
|
||||||
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
|
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
|
||||||
|
<PackageVersion Include="shaderc.net" 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.Vulkan" Version="2.22.0" />
|
||||||
<PackageVersion Include="Silk.NET.Vulkan" Version="2.23.0" />
|
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.22.0" />
|
||||||
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.23.0" />
|
<PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.22.0" />
|
||||||
<PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.23.0" />
|
|
||||||
<PackageVersion Include="SkiaSharp" Version="2.88.9" />
|
<PackageVersion Include="SkiaSharp" Version="2.88.9" />
|
||||||
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.9" />
|
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.9" />
|
||||||
<PackageVersion Include="SPB" Version="0.0.4-build32" />
|
<PackageVersion Include="SPB" Version="0.0.4-build32" />
|
||||||
<PackageVersion Include="System.IO.Hashing" Version="9.0.12" />
|
<PackageVersion Include="System.IO.Hashing" Version="9.0.5" />
|
||||||
<PackageVersion Include="System.Management" Version="9.0.12" />
|
<PackageVersion Include="System.Management" Version="9.0.5" />
|
||||||
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.1.0" />
|
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
|
||||||
<PackageVersion Include="Rxmxnx.PInvoke.Extensions" Version="2.9.0" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -1,26 +1,31 @@
|
|||||||
<h1 align="center">
|
<h1 align="center">
|
||||||
<br>
|
<br>
|
||||||
<img src="https://git.ryujinx.app/projects/Kenji-NX/raw/branch/master/distribution/misc/Logo.png" alt="Kenji-NX">
|
<img src="https://git.ryujinx.app/kenji-nx/ryujinx/-/raw/master/distribution/misc/Logo.png" alt="Kenji-NX">
|
||||||
<br>
|
<br>
|
||||||
<b>Kenji-NX</b>
|
<b>Kenji-NX</b>
|
||||||
<br>
|
<br>
|
||||||
|
<a href="https://github.com/Kenji-NX/Releases/releases/latest">
|
||||||
[](https://git.ryujinx.app/projects/Kenji-NX/releases/latest)
|
<img src="https://img.shields.io/github/v/release/Kenji-NX/Releases"
|
||||||
[](https://git.ryujinx.app/Kenji-NX/android/releases/latest)
|
alt="Latest Release">
|
||||||
<br>
|
</a>
|
||||||
[](https://discord.gg/zBSAuZfKqe)
|
|
||||||
|
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
Kenji-NX is an open-source Nintendo Switch emulator, originally created by gdkchan, written in C#.
|
<p>
|
||||||
This emulator aims at providing excellent accuracy and performance, a user-friendly interface and consistent builds.
|
Kenji-NX is an open-source Nintendo Switch emulator, originally created by gdkchan, written in C#.
|
||||||
It was written from scratch and development on the project began in September 2017.
|
This emulator aims at providing excellent accuracy and performance, a user-friendly interface and consistent builds.
|
||||||
Kenji-NX is available on GitHub under the [MIT license](LICENSE.txt).
|
It was written from scratch and development on the project began in September 2017.
|
||||||
|
Kenji-NX is available on GitHub under the <a href="https://git.ryujinx.app/kenji-nx/ryujinx/-/raw/master/LICENSE.txt" target="_blank">MIT license</a>.
|
||||||
|
<br><br>
|
||||||
|
On October 1st 2024, Ryujinx was discontinued as the creator was forced to abandon the project.
|
||||||
|
<br><br>
|
||||||
|
This fork is not a Ryujinx revival project; it aims to be a middle ground between GreemDev's <a href="https://git.ryujinx.app/ryubing/ryujinx">Ryujinx</a> fork and the more preservative <a href="https://git.ryujinx.app/archive/ryujinx-mirror">ryujinx-mirror</a> fork.
|
||||||
|
It brings over many of the front-facing features from the aforementioned forks with <i>additional</i> contributions from KeatonTheBot and others.
|
||||||
|
<br>
|
||||||
|
</p>
|
||||||
|
|
||||||
On October 1st 2024, Ryujinx was discontinued as the creator was forced to abandon the project.
|
<p>
|
||||||
|
<img src="docs/shell.png">
|
||||||
This fork is not a Ryujinx revival project; it aims to be a middle ground between GreemDev's [Ryujinx](https://git.ryujinx.app/projects/Ryubing) fork and the more preservative [ryujinx-mirror](https://git.ryujinx.app/archive/ryujinx-mirror) fork.
|
</p>
|
||||||
It brings over many of the front-facing features from the aforementioned forks with *additional* contributions from KeatonTheBot and others.
|
|
||||||
|
|
||||||
## Compatibility
|
## Compatibility
|
||||||
|
|
||||||
@@ -53,12 +58,12 @@ If you wish to build the emulator yourself, follow these steps:
|
|||||||
|
|
||||||
### Step 1
|
### Step 1
|
||||||
|
|
||||||
Install the [.NET 10.0 (or higher) SDK](https://dotnet.microsoft.com/download/dotnet/10.0).
|
Install the [.NET 9.0 (or higher) SDK](https://dotnet.microsoft.com/download/dotnet/9.0).
|
||||||
Make sure your SDK version is higher or equal to the required version specified in [global.json](global.json).
|
Make sure your SDK version is higher or equal to the required version specified in [global.json](global.json).
|
||||||
|
|
||||||
### Step 2
|
### Step 2
|
||||||
|
|
||||||
Either use `git clone https://git.ryujinx.app/projects/Kenji-NX` on the command line to clone the repository or use Code --> Download zip button to get the files.
|
Either use `git clone https://git.ryujinx.app/kenji-nx/ryujinx` on the command line to clone the repository or use Code --> Download zip button to get the files.
|
||||||
|
|
||||||
### Step 3
|
### Step 3
|
||||||
|
|
||||||
@@ -120,7 +125,7 @@ See [LICENSE.txt](LICENSE.txt) and [THIRDPARTY.md](distribution/legal/THIRDPARTY
|
|||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
|
|
||||||
- [LibHac](https://git.ryujinx.app/projects/LibHac) is used for our file-system.
|
- [LibHac](https://github.com/Thealexbarney/LibHac) is used for our file-system.
|
||||||
- [AmiiboAPI](https://www.amiiboapi.com) is used in our Amiibo emulation.
|
- [AmiiboAPI](https://www.amiiboapi.com) is used in our Amiibo emulation.
|
||||||
- [ldn_mitm](https://github.com/spacemeowx2/ldn_mitm) is used for one of our available multiplayer modes.
|
- [ldn_mitm](https://github.com/spacemeowx2/ldn_mitm) is used for one of our available multiplayer modes.
|
||||||
- [ShellLink](https://github.com/securifybv/ShellLink) is used for Windows shortcut generation.
|
- [ShellLink](https://github.com/securifybv/ShellLink) is used for Windows shortcut generation.
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ VisualStudioVersion = 17.1.32228.430
|
|||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Tests", "src\Ryujinx.Tests\Ryujinx.Tests.csproj", "{EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Tests", "src\Ryujinx.Tests\Ryujinx.Tests.csproj", "{EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibKenjinx", "src\LibKenjinx\LibKenjinx.csproj", "{AF58C1D5-DE16-429B-B155-7CE83B4E8FA6}"
|
|
||||||
EndProject
|
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Tests.Unicorn", "src\Ryujinx.Tests.Unicorn\Ryujinx.Tests.Unicorn.csproj", "{D8F72938-78EF-4E8C-BAFE-531C9C3C8F15}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Tests.Unicorn", "src\Ryujinx.Tests.Unicorn\Ryujinx.Tests.Unicorn.csproj", "{D8F72938-78EF-4E8C-BAFE-531C9C3C8F15}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.HLE", "src\Ryujinx.HLE\Ryujinx.HLE.csproj", "{CB92CFF9-1D62-4D4F-9E88-8130EF61E351}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.HLE", "src\Ryujinx.HLE\Ryujinx.HLE.csproj", "{CB92CFF9-1D62-4D4F-9E88-8130EF61E351}"
|
||||||
@@ -34,9 +32,7 @@ EndProject
|
|||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{36F870C1-3E5F-485F-B426-F0645AF78751}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{36F870C1-3E5F-485F-B426-F0645AF78751}"
|
||||||
ProjectSection(SolutionItems) = preProject
|
ProjectSection(SolutionItems) = preProject
|
||||||
.editorconfig = .editorconfig
|
.editorconfig = .editorconfig
|
||||||
Directory.Build.props = Directory.Build.props
|
|
||||||
Directory.Packages.props = Directory.Packages.props
|
Directory.Packages.props = Directory.Packages.props
|
||||||
nuget.config = nuget.config
|
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Memory", "src\Ryujinx.Memory\Ryujinx.Memory.csproj", "{A5E6C691-9E22-4263-8F40-42F002CE66BE}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Memory", "src\Ryujinx.Memory\Ryujinx.Memory.csproj", "{A5E6C691-9E22-4263-8F40-42F002CE66BE}"
|
||||||
@@ -99,10 +95,6 @@ Global
|
|||||||
{EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}.Release|Any CPU.Build.0 = Release|Any CPU
|
{EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{AF58C1D5-DE16-429B-B155-7CE83B4E8FA6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{AF58C1D5-DE16-429B-B155-7CE83B4E8FA6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{AF58C1D5-DE16-429B-B155-7CE83B4E8FA6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{AF58C1D5-DE16-429B-B155-7CE83B4E8FA6}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{D8F72938-78EF-4E8C-BAFE-531C9C3C8F15}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{D8F72938-78EF-4E8C-BAFE-531C9C3C8F15}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{D8F72938-78EF-4E8C-BAFE-531C9C3C8F15}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{D8F72938-78EF-4E8C-BAFE-531C9C3C8F15}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{D8F72938-78EF-4E8C-BAFE-531C9C3C8F15}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{D8F72938-78EF-4E8C-BAFE-531C9C3C8F15}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
|||||||
@@ -1,47 +0,0 @@
|
|||||||
<Solution>
|
|
||||||
<Folder Name="/Solution Items/">
|
|
||||||
<File Path=".editorconfig" />
|
|
||||||
<File Path="Directory.Build.props" />
|
|
||||||
<File Path="Directory.Packages.props" />
|
|
||||||
<File Path="nuget.config" />
|
|
||||||
</Folder>
|
|
||||||
<Project Path="src/ARMeilleure/ARMeilleure.csproj" />
|
|
||||||
<Project Path="src/LibKenjinx/LibKenjinx.csproj" />
|
|
||||||
<Project Path="src/Ryujinx.Audio.Backends.OpenAL/Ryujinx.Audio.Backends.OpenAL.csproj" />
|
|
||||||
<Project Path="src/Ryujinx.Audio.Backends.SDL3/Ryujinx.Audio.Backends.SDL3.csproj" />
|
|
||||||
<Project Path="src/Ryujinx.Audio.Backends.SoundIo/Ryujinx.Audio.Backends.SoundIo.csproj" />
|
|
||||||
<Project Path="src/Ryujinx.Audio/Ryujinx.Audio.csproj" />
|
|
||||||
<Project Path="src/Ryujinx.Common/Ryujinx.Common.csproj" />
|
|
||||||
<Project Path="src/Ryujinx.Cpu/Ryujinx.Cpu.csproj" />
|
|
||||||
<Project Path="src/Ryujinx.Graphics.Device/Ryujinx.Graphics.Device.csproj" />
|
|
||||||
<Project Path="src/Ryujinx.Graphics.GAL/Ryujinx.Graphics.GAL.csproj" />
|
|
||||||
<Project Path="src/Ryujinx.Graphics.Gpu/Ryujinx.Graphics.Gpu.csproj" />
|
|
||||||
<Project Path="src/Ryujinx.Graphics.Host1x/Ryujinx.Graphics.Host1x.csproj" />
|
|
||||||
<Project Path="src/Ryujinx.Graphics.Nvdec.FFmpeg/Ryujinx.Graphics.Nvdec.FFmpeg.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.OpenGL/Ryujinx.Graphics.OpenGL.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.Vic/Ryujinx.Graphics.Vic.csproj" />
|
|
||||||
<Project Path="src/Ryujinx.Graphics.Video/Ryujinx.Graphics.Video.csproj" />
|
|
||||||
<Project Path="src/Ryujinx.Graphics.Vulkan/Ryujinx.Graphics.Vulkan.csproj" />
|
|
||||||
<Project Path="src/Ryujinx.HLE.Generators/Ryujinx.HLE.Generators.csproj" />
|
|
||||||
<Project Path="src/Ryujinx.HLE/Ryujinx.HLE.csproj" />
|
|
||||||
<Project Path="src/Ryujinx.Horizon.Common/Ryujinx.Horizon.Common.csproj" />
|
|
||||||
<Project Path="src/Ryujinx.Horizon.Generators/Ryujinx.Horizon.Generators.csproj" />
|
|
||||||
<Project Path="src/Ryujinx.Horizon.Kernel.Generators/Ryujinx.Horizon.Kernel.Generators.csproj" />
|
|
||||||
<Project Path="src/Ryujinx.Horizon/Ryujinx.Horizon.csproj" />
|
|
||||||
<Project Path="src/Ryujinx.Input.SDL3/Ryujinx.Input.SDL3.csproj" />
|
|
||||||
<Project Path="src/Ryujinx.Input/Ryujinx.Input.csproj" />
|
|
||||||
<Project Path="src/Ryujinx.Memory/Ryujinx.Memory.csproj" />
|
|
||||||
<Project Path="src/Ryujinx.SDL3.Common/Ryujinx.SDL3.Common.csproj" />
|
|
||||||
<Project Path="src/Ryujinx.ShaderTools/Ryujinx.ShaderTools.csproj" />
|
|
||||||
<Project Path="src/Ryujinx.Tests.Memory/Ryujinx.Tests.Memory.csproj" />
|
|
||||||
<Project Path="src/Ryujinx.Tests.Unicorn/Ryujinx.Tests.Unicorn.csproj" />
|
|
||||||
<Project Path="src/Ryujinx.Tests/Ryujinx.Tests.csproj" />
|
|
||||||
<Project Path="src/Ryujinx.UI.Common/Ryujinx.UI.Common.csproj" />
|
|
||||||
<Project Path="src/Ryujinx.UI.LocaleGenerator/Ryujinx.UI.LocaleGenerator.csproj" />
|
|
||||||
<Project Path="src/Ryujinx/Ryujinx.csproj" />
|
|
||||||
<Project Path="src/Spv.Generator/Spv.Generator.csproj" />
|
|
||||||
</Solution>
|
|
||||||
+1
-1
@@ -8,7 +8,7 @@ Intro to Kenji-NX
|
|||||||
Kenji-NX is an open-source Nintendo Switch emulator written in C#. It is based on Ryujinx, which was originally created by gdkchan.
|
Kenji-NX is an open-source Nintendo Switch emulator written in C#. It is based on Ryujinx, which was originally created by gdkchan.
|
||||||
* The CPU emulator, ARMeilleure, emulates an ARMv8 CPU and currently has support for most 64-bit ARMv8 and some of the ARMv7 (and older) instructions.
|
* The CPU emulator, ARMeilleure, emulates an ARMv8 CPU and currently has support for most 64-bit ARMv8 and some of the ARMv7 (and older) instructions.
|
||||||
* The GPU emulator emulates the Switch's Maxwell GPU using either the OpenGL (version 4.5 minimum), Vulkan, or Metal (via MoltenVK) APIs through a custom build of OpenTK or Silk.NET respectively.
|
* The GPU emulator emulates the Switch's Maxwell GPU using either the OpenGL (version 4.5 minimum), Vulkan, or Metal (via MoltenVK) APIs through a custom build of OpenTK or Silk.NET respectively.
|
||||||
* Audio output is entirely supported via C# wrappers for SDL3, with OpenAL & libsoundio as fallbacks.
|
* Audio output is entirely supported via C# wrappers for SDL2, with OpenAL & libsoundio as fallbacks.
|
||||||
|
|
||||||
Getting Started
|
Getting Started
|
||||||
===============
|
===============
|
||||||
|
|||||||
+5
-7
@@ -3,21 +3,19 @@
|
|||||||
<packageSources>
|
<packageSources>
|
||||||
<clear />
|
<clear />
|
||||||
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
|
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
|
||||||
<!-- Only needed when using pre-release versions of Ryujinx.LibHac. -->
|
<add key="LibHacAlpha" value="https://git.ryujinx.app/api/v4/projects/17/packages/nuget/index.json" />
|
||||||
<add key="LibHacAlpha" value="https://git.ryujinx.app/api/packages/projects/nuget/index.json" />
|
|
||||||
<!--<add key="Silk.NET" value="https://gitlab.com/api/v4/projects/51457475/packages/nuget/index.json" />-->
|
|
||||||
</packageSources>
|
</packageSources>
|
||||||
|
|
||||||
|
<!-- Define mappings by adding package patterns beneath the target source. -->
|
||||||
|
<!-- Ryujinx.LibHac packages will be restored from LibHacAlpha,
|
||||||
|
everything else from nuget.org. -->
|
||||||
<packageSourceMapping>
|
<packageSourceMapping>
|
||||||
<!-- key value for <packageSource> should match key values from <packageSources> element -->
|
<!-- key value for <packageSource> should match key values from <packageSources> element -->
|
||||||
<!-- These are defined and .NET still yells about multiple package sources with no mappings. Not sure what to do, this is in the docs lol -->
|
|
||||||
<packageSource key="nuget.org">
|
<packageSource key="nuget.org">
|
||||||
<package pattern="*" />
|
<package pattern="*" />
|
||||||
</packageSource>
|
</packageSource>
|
||||||
<packageSource key="LibHacAlpha">
|
<packageSource key="LibHacAlpha">
|
||||||
<package pattern="Ryujinx.LibHac" />
|
<package pattern="Ryujinx.LibHac" />
|
||||||
</packageSource>
|
</packageSource>
|
||||||
<!--<packageSource key="Silk.NET">
|
|
||||||
<package pattern="Silk.*" />
|
|
||||||
</packageSource>-->
|
|
||||||
</packageSourceMapping>
|
</packageSourceMapping>
|
||||||
</configuration>
|
</configuration>
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
@@ -254,7 +254,7 @@ namespace ARMeilleure.CodeGen.Arm64
|
|||||||
|
|
||||||
private static bool IsMemoryLoadOrStore(Instruction inst)
|
private static bool IsMemoryLoadOrStore(Instruction inst)
|
||||||
{
|
{
|
||||||
return inst is Instruction.Load or Instruction.Store;
|
return inst == Instruction.Load || inst == Instruction.Store;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool ConstTooLong(Operand constOp, OperandType accessType)
|
private static bool ConstTooLong(Operand constOp, OperandType accessType)
|
||||||
|
|||||||
@@ -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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ namespace ARMeilleure.CodeGen.Arm64
|
|||||||
// Any value AND all ones will be equal itself, so it's effectively a no-op.
|
// Any value AND all ones will be equal itself, so it's effectively a no-op.
|
||||||
// Any value OR all ones will be equal all ones, so one can just use MOV.
|
// Any value OR all ones will be equal all ones, so one can just use MOV.
|
||||||
// Any value XOR all ones will be equal its inverse, so one can just use MVN.
|
// Any value XOR all ones will be equal its inverse, so one can just use MVN.
|
||||||
if (value is 0 or ulong.MaxValue)
|
if (value == 0 || value == ulong.MaxValue)
|
||||||
{
|
{
|
||||||
immN = 0;
|
immN = 0;
|
||||||
immS = 0;
|
immS = 0;
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -189,7 +189,8 @@ namespace ARMeilleure.CodeGen.Arm64
|
|||||||
// The only blocks which can have 0 successors are exit blocks.
|
// The only blocks which can have 0 successors are exit blocks.
|
||||||
Operation last = block.Operations.Last;
|
Operation last = block.Operations.Last;
|
||||||
|
|
||||||
Debug.Assert(last.Instruction is Instruction.Tailcall or Instruction.Return);
|
Debug.Assert(last.Instruction == Instruction.Tailcall ||
|
||||||
|
last.Instruction == Instruction.Return);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -321,7 +322,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 +354,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);
|
||||||
|
|
||||||
@@ -463,7 +464,7 @@ namespace ARMeilleure.CodeGen.Arm64
|
|||||||
Operand dest = operation.Destination;
|
Operand dest = operation.Destination;
|
||||||
Operand source = operation.GetSource(0);
|
Operand source = operation.GetSource(0);
|
||||||
|
|
||||||
Debug.Assert(dest.Type is OperandType.FP32 or OperandType.FP64);
|
Debug.Assert(dest.Type == OperandType.FP32 || dest.Type == OperandType.FP64);
|
||||||
Debug.Assert(dest.Type != source.Type);
|
Debug.Assert(dest.Type != source.Type);
|
||||||
Debug.Assert(source.Type != OperandType.V128);
|
Debug.Assert(source.Type != OperandType.V128);
|
||||||
|
|
||||||
@@ -482,7 +483,7 @@ namespace ARMeilleure.CodeGen.Arm64
|
|||||||
Operand dest = operation.Destination;
|
Operand dest = operation.Destination;
|
||||||
Operand source = operation.GetSource(0);
|
Operand source = operation.GetSource(0);
|
||||||
|
|
||||||
Debug.Assert(dest.Type is OperandType.FP32 or OperandType.FP64);
|
Debug.Assert(dest.Type == OperandType.FP32 || dest.Type == OperandType.FP64);
|
||||||
Debug.Assert(dest.Type != source.Type);
|
Debug.Assert(dest.Type != source.Type);
|
||||||
Debug.Assert(source.Type.IsInteger());
|
Debug.Assert(source.Type.IsInteger());
|
||||||
|
|
||||||
@@ -1462,7 +1463,7 @@ namespace ARMeilleure.CodeGen.Arm64
|
|||||||
|
|
||||||
private static bool IsLoadOrStore(Operation operation)
|
private static bool IsLoadOrStore(Operation operation)
|
||||||
{
|
{
|
||||||
return operation.Instruction is Instruction.Load or Instruction.Store;
|
return operation.Instruction == Instruction.Load || operation.Instruction == Instruction.Store;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static OperandType GetMemOpValueType(Operation operation)
|
private static OperandType GetMemOpValueType(Operation operation)
|
||||||
@@ -1552,7 +1553,7 @@ namespace ARMeilleure.CodeGen.Arm64
|
|||||||
|
|
||||||
private static void EnsureSameReg(Operand op1, Operand op2)
|
private static void EnsureSameReg(Operand op1, Operand op2)
|
||||||
{
|
{
|
||||||
Debug.Assert(op1.Kind is OperandKind.Register or OperandKind.Memory);
|
Debug.Assert(op1.Kind == OperandKind.Register || op1.Kind == OperandKind.Memory);
|
||||||
Debug.Assert(op1.Kind == op2.Kind);
|
Debug.Assert(op1.Kind == op2.Kind);
|
||||||
Debug.Assert(op1.Value == op2.Value);
|
Debug.Assert(op1.Value == op2.Value);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -127,13 +127,13 @@ namespace ARMeilleure.CodeGen.Arm64
|
|||||||
#region macOS
|
#region macOS
|
||||||
|
|
||||||
[LibraryImport("libSystem.dylib", SetLastError = true)]
|
[LibraryImport("libSystem.dylib", SetLastError = true)]
|
||||||
private static unsafe partial int sysctlbyname([MarshalAs(UnmanagedType.LPStr)] string name, out int oldValue, ref ulong oldSize, nint newValue, ulong newValueSize);
|
private static unsafe partial int sysctlbyname([MarshalAs(UnmanagedType.LPStr)] string name, out int oldValue, ref ulong oldSize, IntPtr newValue, ulong newValueSize);
|
||||||
|
|
||||||
[SupportedOSPlatform("macos")]
|
[SupportedOSPlatform("macos")]
|
||||||
private static bool CheckSysctlName(string name)
|
private static bool CheckSysctlName(string name)
|
||||||
{
|
{
|
||||||
ulong size = sizeof(int);
|
ulong size = sizeof(int);
|
||||||
if (sysctlbyname(name, out int val, ref size, nint.Zero, 0) == 0 && size == sizeof(int))
|
if (sysctlbyname(name, out int val, ref size, IntPtr.Zero, 0) == 0 && size == sizeof(int))
|
||||||
{
|
{
|
||||||
return val != 0;
|
return val != 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -736,19 +736,19 @@ namespace ARMeilleure.CodeGen.Arm64
|
|||||||
{
|
{
|
||||||
IntrinsicInfo info = IntrinsicTable.GetInfo(intrinsic & ~(Intrinsic.Arm64VTypeMask | Intrinsic.Arm64VSizeMask));
|
IntrinsicInfo info = IntrinsicTable.GetInfo(intrinsic & ~(Intrinsic.Arm64VTypeMask | Intrinsic.Arm64VSizeMask));
|
||||||
|
|
||||||
return info.Type is IntrinsicType.ScalarBinaryRd
|
return info.Type == IntrinsicType.ScalarBinaryRd ||
|
||||||
or IntrinsicType.ScalarTernaryFPRdByElem
|
info.Type == IntrinsicType.ScalarTernaryFPRdByElem ||
|
||||||
or IntrinsicType.ScalarTernaryShlRd
|
info.Type == IntrinsicType.ScalarTernaryShlRd ||
|
||||||
or IntrinsicType.ScalarTernaryShrRd
|
info.Type == IntrinsicType.ScalarTernaryShrRd ||
|
||||||
or IntrinsicType.Vector128BinaryRd
|
info.Type == IntrinsicType.Vector128BinaryRd ||
|
||||||
or IntrinsicType.VectorBinaryRd
|
info.Type == IntrinsicType.VectorBinaryRd ||
|
||||||
or IntrinsicType.VectorInsertByElem
|
info.Type == IntrinsicType.VectorInsertByElem ||
|
||||||
or IntrinsicType.VectorTernaryRd
|
info.Type == IntrinsicType.VectorTernaryRd ||
|
||||||
or IntrinsicType.VectorTernaryRdBitwise
|
info.Type == IntrinsicType.VectorTernaryRdBitwise ||
|
||||||
or IntrinsicType.VectorTernaryFPRdByElem
|
info.Type == IntrinsicType.VectorTernaryFPRdByElem ||
|
||||||
or IntrinsicType.VectorTernaryRdByElem
|
info.Type == IntrinsicType.VectorTernaryRdByElem ||
|
||||||
or IntrinsicType.VectorTernaryShlRd
|
info.Type == IntrinsicType.VectorTernaryShlRd ||
|
||||||
or IntrinsicType.VectorTernaryShrRd;
|
info.Type == IntrinsicType.VectorTernaryShrRd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool HasConstSrc1(Operation node, ulong value)
|
private static bool HasConstSrc1(Operation node, ulong value)
|
||||||
@@ -847,9 +847,9 @@ 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 == Comparison.Equal || compType == Comparison.NotEqual;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using ARMeilleure.CodeGen.Linking;
|
using ARMeilleure.CodeGen.Linking;
|
||||||
using ARMeilleure.CodeGen.Unwinding;
|
using ARMeilleure.CodeGen.Unwinding;
|
||||||
using ARMeilleure.Translation.Cache;
|
using ARMeilleure.Translation.Cache;
|
||||||
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace ARMeilleure.CodeGen
|
namespace ARMeilleure.CodeGen
|
||||||
@@ -57,7 +58,7 @@ namespace ARMeilleure.CodeGen
|
|||||||
/// <typeparam name="T">Type of delegate</typeparam>
|
/// <typeparam name="T">Type of delegate</typeparam>
|
||||||
/// <param name="codePointer">Pointer to the function code in memory</param>
|
/// <param name="codePointer">Pointer to the function code in memory</param>
|
||||||
/// <returns>A delegate of type <typeparamref name="T"/> pointing to the mapped function</returns>
|
/// <returns>A delegate of type <typeparamref name="T"/> pointing to the mapped function</returns>
|
||||||
public T MapWithPointer<T>(out nint codePointer)
|
public T MapWithPointer<T>(out IntPtr codePointer)
|
||||||
{
|
{
|
||||||
codePointer = JitCache.Map(this);
|
codePointer = JitCache.Map(this);
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ namespace ARMeilleure.CodeGen.Linking
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets an empty <see cref="RelocInfo"/>.
|
/// Gets an empty <see cref="RelocInfo"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static RelocInfo Empty { get; } = new(null);
|
public static RelocInfo Empty { get; } = new RelocInfo(null);
|
||||||
|
|
||||||
private readonly RelocEntry[] _entries;
|
private readonly RelocEntry[] _entries;
|
||||||
|
|
||||||
|
|||||||
@@ -227,11 +227,11 @@ namespace ARMeilleure.CodeGen.Optimizations
|
|||||||
|
|
||||||
private static bool HasSideEffects(Operation node)
|
private static bool HasSideEffects(Operation node)
|
||||||
{
|
{
|
||||||
return node.Instruction is Instruction.Call
|
return node.Instruction == Instruction.Call
|
||||||
or Instruction.Tailcall
|
|| node.Instruction == Instruction.Tailcall
|
||||||
or Instruction.CompareAndSwap
|
|| node.Instruction == Instruction.CompareAndSwap
|
||||||
or Instruction.CompareAndSwap16
|
|| node.Instruction == Instruction.CompareAndSwap16
|
||||||
or Instruction.CompareAndSwap8;
|
|| node.Instruction == Instruction.CompareAndSwap8;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsPropagableCompare(Operation operation)
|
private static bool IsPropagableCompare(Operation operation)
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
@@ -839,7 +839,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
|||||||
{
|
{
|
||||||
dest.NumberLocal(_intervals.Count);
|
dest.NumberLocal(_intervals.Count);
|
||||||
|
|
||||||
LiveInterval interval = new(dest);
|
LiveInterval interval = new LiveInterval(dest);
|
||||||
_intervals.Add(interval);
|
_intervals.Add(interval);
|
||||||
|
|
||||||
SetVisited(dest);
|
SetVisited(dest);
|
||||||
@@ -847,7 +847,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
|||||||
// If this is a copy (or copy-like operation), set the copy source interval as well.
|
// If this is a copy (or copy-like operation), set the copy source interval as well.
|
||||||
// This is used for register preferencing later on, which allows the copy to be eliminated
|
// This is used for register preferencing later on, which allows the copy to be eliminated
|
||||||
// in some cases.
|
// in some cases.
|
||||||
if (node.Instruction is Instruction.Copy or Instruction.ZeroExtend32)
|
if (node.Instruction == Instruction.Copy || node.Instruction == Instruction.ZeroExtend32)
|
||||||
{
|
{
|
||||||
Operand source = node.GetSource(0);
|
Operand source = node.GetSource(0);
|
||||||
|
|
||||||
@@ -1120,7 +1120,8 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
|||||||
|
|
||||||
private static bool IsLocalOrRegister(OperandKind kind)
|
private static bool IsLocalOrRegister(OperandKind kind)
|
||||||
{
|
{
|
||||||
return kind is OperandKind.LocalVariable or OperandKind.Register;
|
return kind == OperandKind.LocalVariable ||
|
||||||
|
kind == OperandKind.Register;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -387,7 +387,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
|||||||
|
|
||||||
public override int GetHashCode()
|
public override int GetHashCode()
|
||||||
{
|
{
|
||||||
return HashCode.Combine((nint)_data);
|
return HashCode.Combine((IntPtr)_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
|||||||
|
|
||||||
public override int GetHashCode()
|
public override int GetHashCode()
|
||||||
{
|
{
|
||||||
return HashCode.Combine((nint)_data);
|
return HashCode.Combine((IntPtr)_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
|
|||||||
@@ -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,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;
|
||||||
@@ -1107,17 +1106,17 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if ((flags & InstructionFlags.Prefix66) != 0)
|
if (flags.HasFlag(InstructionFlags.Prefix66))
|
||||||
{
|
{
|
||||||
WriteByte(0x66);
|
WriteByte(0x66);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((flags & InstructionFlags.PrefixF2) != 0f)
|
if (flags.HasFlag(InstructionFlags.PrefixF2))
|
||||||
{
|
{
|
||||||
WriteByte(0xf2);
|
WriteByte(0xf2);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((flags & InstructionFlags.PrefixF3) != 0f)
|
if (flags.HasFlag(InstructionFlags.PrefixF3))
|
||||||
{
|
{
|
||||||
WriteByte(0xf3);
|
WriteByte(0xf3);
|
||||||
}
|
}
|
||||||
@@ -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,15 +1469,15 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool Is64Bits(OperandType type)
|
private static bool Is64Bits(OperandType type)
|
||||||
{
|
{
|
||||||
return type is OperandType.I64 or OperandType.FP64;
|
return type == OperandType.I64 || type == OperandType.FP64;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsImm8(ulong immediate, OperandType type)
|
private static bool IsImm8(ulong immediate, OperandType type)
|
||||||
|
|||||||
@@ -175,7 +175,8 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
// The only blocks which can have 0 successors are exit blocks.
|
// The only blocks which can have 0 successors are exit blocks.
|
||||||
Operation last = block.Operations.Last;
|
Operation last = block.Operations.Last;
|
||||||
|
|
||||||
Debug.Assert(last.Instruction is Instruction.Tailcall or Instruction.Return);
|
Debug.Assert(last.Instruction == Instruction.Tailcall ||
|
||||||
|
last.Instruction == Instruction.Return);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -477,7 +478,7 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
Debug.Assert(HardwareCapabilities.SupportsVexEncoding);
|
Debug.Assert(HardwareCapabilities.SupportsVexEncoding);
|
||||||
|
|
||||||
Debug.Assert(dest.Kind == OperandKind.Register && src1.Kind == OperandKind.Register && src2.Kind == OperandKind.Register);
|
Debug.Assert(dest.Kind == OperandKind.Register && src1.Kind == OperandKind.Register && src2.Kind == OperandKind.Register);
|
||||||
Debug.Assert(src3.Kind is OperandKind.Register or OperandKind.Memory);
|
Debug.Assert(src3.Kind == OperandKind.Register || src3.Kind == OperandKind.Memory);
|
||||||
|
|
||||||
EnsureSameType(dest, src1, src2, src3);
|
EnsureSameType(dest, src1, src2, src3);
|
||||||
Debug.Assert(dest.Type == OperandType.V128);
|
Debug.Assert(dest.Type == OperandType.V128);
|
||||||
@@ -622,7 +623,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 +661,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);
|
||||||
|
|
||||||
@@ -787,7 +788,7 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
Operand dest = operation.Destination;
|
Operand dest = operation.Destination;
|
||||||
Operand source = operation.GetSource(0);
|
Operand source = operation.GetSource(0);
|
||||||
|
|
||||||
Debug.Assert(dest.Type is OperandType.FP32 or OperandType.FP64);
|
Debug.Assert(dest.Type == OperandType.FP32 || dest.Type == OperandType.FP64);
|
||||||
|
|
||||||
if (dest.Type == OperandType.FP32)
|
if (dest.Type == OperandType.FP32)
|
||||||
{
|
{
|
||||||
@@ -1722,7 +1723,7 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug.Assert(op1.Kind is OperandKind.Register or OperandKind.Memory);
|
Debug.Assert(op1.Kind == OperandKind.Register || op1.Kind == OperandKind.Memory);
|
||||||
Debug.Assert(op1.Kind == op2.Kind);
|
Debug.Assert(op1.Kind == op2.Kind);
|
||||||
Debug.Assert(op1.Value == op2.Value);
|
Debug.Assert(op1.Value == op2.Value);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -312,9 +312,9 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
|
|
||||||
case Instruction.Extended:
|
case Instruction.Extended:
|
||||||
{
|
{
|
||||||
bool isBlend = node.Intrinsic is Intrinsic.X86Blendvpd
|
bool isBlend = node.Intrinsic == Intrinsic.X86Blendvpd ||
|
||||||
or Intrinsic.X86Blendvps
|
node.Intrinsic == Intrinsic.X86Blendvps ||
|
||||||
or Intrinsic.X86Pblendvb;
|
node.Intrinsic == Intrinsic.X86Pblendvb;
|
||||||
|
|
||||||
// BLENDVPD, BLENDVPS, PBLENDVB last operand is always implied to be XMM0 when VEX is not supported.
|
// BLENDVPD, BLENDVPS, PBLENDVB last operand is always implied to be XMM0 when VEX is not supported.
|
||||||
// SHA256RNDS2 always has an implied XMM0 as a last operand.
|
// SHA256RNDS2 always has an implied XMM0 as a last operand.
|
||||||
@@ -513,8 +513,8 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
Operand dest = node.Destination;
|
Operand dest = node.Destination;
|
||||||
Operand source = node.GetSource(0);
|
Operand source = node.GetSource(0);
|
||||||
|
|
||||||
Debug.Assert(dest.Type is OperandType.FP32 or OperandType.FP64,
|
Debug.Assert(dest.Type == OperandType.FP32 ||
|
||||||
$"Invalid destination type \"{dest.Type}\".");
|
dest.Type == OperandType.FP64, $"Invalid destination type \"{dest.Type}\".");
|
||||||
|
|
||||||
Operation currentNode = node;
|
Operation currentNode = node;
|
||||||
|
|
||||||
@@ -759,9 +759,9 @@ 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 == Comparison.Equal || compType == Comparison.NotEqual;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,13 +13,13 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
|
|
||||||
public static void RunPass(ControlFlowGraph cfg)
|
public static void RunPass(ControlFlowGraph cfg)
|
||||||
{
|
{
|
||||||
Dictionary<ulong, Operand> constants = new();
|
var constants = new Dictionary<ulong, Operand>();
|
||||||
|
|
||||||
Operand GetConstantCopy(BasicBlock block, Operation operation, Operand source)
|
Operand GetConstantCopy(BasicBlock block, Operation operation, Operand source)
|
||||||
{
|
{
|
||||||
// If the constant has many uses, we also force a new constant mov to be added, in order
|
// If the constant has many uses, we also force a new constant mov to be added, in order
|
||||||
// to avoid overflow of the counts field (that is limited to 16 bits).
|
// to avoid overflow of the counts field (that is limited to 16 bits).
|
||||||
if (!constants.TryGetValue(source.Value, out Operand constant) || constant.UsesCount > MaxConstantUses)
|
if (!constants.TryGetValue(source.Value, out var constant) || constant.UsesCount > MaxConstantUses)
|
||||||
{
|
{
|
||||||
constant = Local(source.Type);
|
constant = Local(source.Type);
|
||||||
|
|
||||||
@@ -248,12 +248,12 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
|
|
||||||
private static bool IsMemoryLoadOrStore(Instruction inst)
|
private static bool IsMemoryLoadOrStore(Instruction inst)
|
||||||
{
|
{
|
||||||
return inst is Instruction.Load
|
return inst == Instruction.Load ||
|
||||||
or Instruction.Load16
|
inst == Instruction.Load16 ||
|
||||||
or Instruction.Load8
|
inst == Instruction.Load8 ||
|
||||||
or Instruction.Store
|
inst == Instruction.Store ||
|
||||||
or Instruction.Store16
|
inst == Instruction.Store16 ||
|
||||||
or Instruction.Store8;
|
inst == Instruction.Store8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ namespace ARMeilleure.Common
|
|||||||
private List<PageInfo> _pages;
|
private List<PageInfo> _pages;
|
||||||
private readonly ulong _pageSize;
|
private readonly ulong _pageSize;
|
||||||
private readonly uint _pageCount;
|
private readonly uint _pageCount;
|
||||||
private readonly List<nint> _extras;
|
private readonly List<IntPtr> _extras;
|
||||||
|
|
||||||
public ArenaAllocator(uint pageSize, uint pageCount)
|
public ArenaAllocator(uint pageSize, uint pageCount)
|
||||||
{
|
{
|
||||||
@@ -64,7 +64,7 @@ namespace ARMeilleure.Common
|
|||||||
{
|
{
|
||||||
void* extra = NativeAllocator.Instance.Allocate(size);
|
void* extra = NativeAllocator.Instance.Allocate(size);
|
||||||
|
|
||||||
_extras.Add((nint)extra);
|
_extras.Add((IntPtr)extra);
|
||||||
|
|
||||||
return extra;
|
return extra;
|
||||||
}
|
}
|
||||||
@@ -114,7 +114,7 @@ namespace ARMeilleure.Common
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Free extra blocks that are not page-sized
|
// Free extra blocks that are not page-sized
|
||||||
foreach (nint ptr in _extras)
|
foreach (IntPtr ptr in _extras)
|
||||||
{
|
{
|
||||||
NativeAllocator.Instance.Free((void*)ptr);
|
NativeAllocator.Instance.Free((void*)ptr);
|
||||||
}
|
}
|
||||||
@@ -173,7 +173,7 @@ namespace ARMeilleure.Common
|
|||||||
NativeAllocator.Instance.Free(info.Pointer);
|
NativeAllocator.Instance.Free(info.Pointer);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (nint ptr in _extras)
|
foreach (IntPtr ptr in _extras)
|
||||||
{
|
{
|
||||||
NativeAllocator.Instance.Free((void*)ptr);
|
NativeAllocator.Instance.Free((void*)ptr);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ namespace ARMeilleure.Common
|
|||||||
private int _freeHint;
|
private int _freeHint;
|
||||||
private readonly int _pageCapacity; // Number of entries per page.
|
private readonly int _pageCapacity; // Number of entries per page.
|
||||||
private readonly int _pageLogCapacity;
|
private readonly int _pageLogCapacity;
|
||||||
private readonly Dictionary<int, nint> _pages;
|
private readonly Dictionary<int, IntPtr> _pages;
|
||||||
private readonly BitMap _allocated;
|
private readonly BitMap _allocated;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -41,7 +41,7 @@ namespace ARMeilleure.Common
|
|||||||
}
|
}
|
||||||
|
|
||||||
_allocated = new BitMap(NativeAllocator.Instance);
|
_allocated = new BitMap(NativeAllocator.Instance);
|
||||||
_pages = new Dictionary<int, nint>();
|
_pages = new Dictionary<int, IntPtr>();
|
||||||
_pageLogCapacity = BitOperations.Log2((uint)(pageSize / sizeof(TEntry)));
|
_pageLogCapacity = BitOperations.Log2((uint)(pageSize / sizeof(TEntry)));
|
||||||
_pageCapacity = 1 << _pageLogCapacity;
|
_pageCapacity = 1 << _pageLogCapacity;
|
||||||
}
|
}
|
||||||
@@ -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,11 +136,11 @@ 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 IntPtr page))
|
||||||
{
|
{
|
||||||
page = (nint)NativeAllocator.Instance.Allocate((uint)sizeof(TEntry) * (uint)_pageCapacity);
|
page = (IntPtr)NativeAllocator.Instance.Allocate((uint)sizeof(TEntry) * (uint)_pageCapacity);
|
||||||
|
|
||||||
_pages.Add(pageIndex, page);
|
_pages.Add(pageIndex, 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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ namespace ARMeilleure.Common
|
|||||||
|
|
||||||
public override void* Allocate(ulong size)
|
public override void* Allocate(ulong size)
|
||||||
{
|
{
|
||||||
void* result = (void*)Marshal.AllocHGlobal((nint)size);
|
void* result = (void*)Marshal.AllocHGlobal((IntPtr)size);
|
||||||
|
|
||||||
if (result == null)
|
if (result == null)
|
||||||
{
|
{
|
||||||
@@ -21,7 +21,7 @@ namespace ARMeilleure.Common
|
|||||||
|
|
||||||
public override void Free(void* block)
|
public override void Free(void* block)
|
||||||
{
|
{
|
||||||
Marshal.FreeHGlobal((nint)block);
|
Marshal.FreeHGlobal((IntPtr)block);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -254,7 +254,8 @@ namespace ARMeilleure.Decoders
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Compare and branch instructions are always conditional.
|
// Compare and branch instructions are always conditional.
|
||||||
if (opCode.Instruction.Name is InstName.Cbz or InstName.Cbnz)
|
if (opCode.Instruction.Name == InstName.Cbz ||
|
||||||
|
opCode.Instruction.Name == InstName.Cbnz)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -283,7 +284,7 @@ namespace ARMeilleure.Decoders
|
|||||||
// register (Rt == 15 or (mask & (1 << 15)) != 0), and cases where there is
|
// register (Rt == 15 or (mask & (1 << 15)) != 0), and cases where there is
|
||||||
// a write back to PC (wback == true && Rn == 15), however the later may
|
// a write back to PC (wback == true && Rn == 15), however the later may
|
||||||
// be "undefined" depending on the CPU, so compilers should not produce that.
|
// be "undefined" depending on the CPU, so compilers should not produce that.
|
||||||
if (opCode is IOpCode32Mem or IOpCode32MemMult)
|
if (opCode is IOpCode32Mem || opCode is IOpCode32MemMult)
|
||||||
{
|
{
|
||||||
int rt, rn;
|
int rt, rn;
|
||||||
|
|
||||||
@@ -325,12 +326,15 @@ namespace ARMeilleure.Decoders
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Explicit branch instructions.
|
// Explicit branch instructions.
|
||||||
return opCode is IOpCode32BImm or IOpCode32BReg;
|
return opCode is IOpCode32BImm ||
|
||||||
|
opCode is IOpCode32BReg;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsCall(OpCode opCode)
|
private static bool IsCall(OpCode opCode)
|
||||||
{
|
{
|
||||||
return opCode.Instruction.Name is InstName.Bl or InstName.Blr or InstName.Blx;
|
return opCode.Instruction.Name == InstName.Bl ||
|
||||||
|
opCode.Instruction.Name == InstName.Blr ||
|
||||||
|
opCode.Instruction.Name == InstName.Blx;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsException(OpCode opCode)
|
private static bool IsException(OpCode opCode)
|
||||||
@@ -340,7 +344,9 @@ namespace ARMeilleure.Decoders
|
|||||||
|
|
||||||
private static bool IsTrap(OpCode opCode)
|
private static bool IsTrap(OpCode opCode)
|
||||||
{
|
{
|
||||||
return opCode.Instruction.Name is InstName.Brk or InstName.Trap or InstName.Und;
|
return opCode.Instruction.Name == InstName.Brk ||
|
||||||
|
opCode.Instruction.Name == InstName.Trap ||
|
||||||
|
opCode.Instruction.Name == InstName.Und;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static OpCode DecodeOpCode(IMemoryManager memory, ulong address, ExecutionMode mode)
|
public static OpCode DecodeOpCode(IMemoryManager memory, ulong address, ExecutionMode mode)
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ namespace ARMeilleure.Decoders
|
|||||||
|
|
||||||
public OpCode32SimdDupElem(InstDescriptor inst, ulong address, int opCode, bool isThumb) : base(inst, address, opCode, isThumb)
|
public OpCode32SimdDupElem(InstDescriptor inst, ulong address, int opCode, bool isThumb) : base(inst, address, opCode, isThumb)
|
||||||
{
|
{
|
||||||
int opc = (opCode >> 16) & 0xf;
|
var opc = (opCode >> 16) & 0xf;
|
||||||
|
|
||||||
if ((opc & 0b1) == 1)
|
if ((opc & 0b1) == 1)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ namespace ARMeilleure.Decoders
|
|||||||
Op = (opCode >> 20) & 0x1;
|
Op = (opCode >> 20) & 0x1;
|
||||||
U = ((opCode >> 23) & 1) != 0;
|
U = ((opCode >> 23) & 1) != 0;
|
||||||
|
|
||||||
int opc = (((opCode >> 23) & 1) << 4) | (((opCode >> 21) & 0x3) << 2) | ((opCode >> 5) & 0x3);
|
var opc = (((opCode >> 23) & 1) << 4) | (((opCode >> 21) & 0x3) << 2) | ((opCode >> 5) & 0x3);
|
||||||
|
|
||||||
if ((opc & 0b01000) == 0b01000)
|
if ((opc & 0b01000) == 0b01000)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ namespace ARMeilleure.Decoders
|
|||||||
}
|
}
|
||||||
else if (DataOp == DataOp.Logical)
|
else if (DataOp == DataOp.Logical)
|
||||||
{
|
{
|
||||||
DecoderHelper.BitMask bm = DecoderHelper.DecodeBitMask(opCode, true);
|
var bm = DecoderHelper.DecodeBitMask(opCode, true);
|
||||||
|
|
||||||
if (bm.IsUndefined)
|
if (bm.IsUndefined)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ namespace ARMeilleure.Decoders
|
|||||||
|
|
||||||
public OpCodeBfm(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
public OpCodeBfm(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||||
{
|
{
|
||||||
DecoderHelper.BitMask bm = DecoderHelper.DecodeBitMask(opCode, false);
|
var bm = DecoderHelper.DecodeBitMask(opCode, false);
|
||||||
|
|
||||||
if (bm.IsUndefined)
|
if (bm.IsUndefined)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -28,7 +28,8 @@ namespace ARMeilleure.Decoders
|
|||||||
MemOp type = WBack ? (MemOp)((opCode >> 10) & 3) : MemOp.Unsigned;
|
MemOp type = WBack ? (MemOp)((opCode >> 10) & 3) : MemOp.Unsigned;
|
||||||
|
|
||||||
PostIdx = type == MemOp.PostIndexed;
|
PostIdx = type == MemOp.PostIndexed;
|
||||||
Unscaled = type is MemOp.Unscaled or MemOp.Unprivileged;
|
Unscaled = type == MemOp.Unscaled ||
|
||||||
|
type == MemOp.Unprivileged;
|
||||||
|
|
||||||
// Unscaled and Unprivileged doesn't write back,
|
// Unscaled and Unprivileged doesn't write back,
|
||||||
// but they do use the 9-bits Signed Immediate.
|
// but they do use the 9-bits Signed Immediate.
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ namespace ARMeilleure.Decoders.Optimizations
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Block> newBlocks = new(blocks.Count);
|
var newBlocks = new List<Block>(blocks.Count);
|
||||||
|
|
||||||
// Finally, rebuild decoded block list, ignoring blocks outside the contiguous range.
|
// Finally, rebuild decoded block list, ignoring blocks outside the contiguous range.
|
||||||
for (int i = 0; i < blocks.Count; i++)
|
for (int i = 0; i < blocks.Count; i++)
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ namespace ARMeilleure.Diagnostics
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case OperandKind.Memory:
|
case OperandKind.Memory:
|
||||||
MemoryOperand memOp = operand.GetMemory();
|
var memOp = operand.GetMemory();
|
||||||
|
|
||||||
_builder.Append('[');
|
_builder.Append('[');
|
||||||
|
|
||||||
@@ -235,7 +235,8 @@ namespace ARMeilleure.Diagnostics
|
|||||||
{
|
{
|
||||||
_builder.Append('.').Append(operation.Intrinsic);
|
_builder.Append('.').Append(operation.Intrinsic);
|
||||||
}
|
}
|
||||||
else if (operation.Instruction is Instruction.BranchIf or Instruction.Compare)
|
else if (operation.Instruction == Instruction.BranchIf ||
|
||||||
|
operation.Instruction == Instruction.Compare)
|
||||||
{
|
{
|
||||||
comparison = true;
|
comparison = true;
|
||||||
}
|
}
|
||||||
@@ -284,7 +285,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));
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
context.LoadFromContext();
|
context.LoadFromContext();
|
||||||
|
|
||||||
InstEmitFlowHelper.EmitReturn(context, Const(op.Address));
|
context.Return(Const(op.Address));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Svc(ArmEmitterContext context)
|
public static void Svc(ArmEmitterContext context)
|
||||||
@@ -49,7 +49,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
context.LoadFromContext();
|
context.LoadFromContext();
|
||||||
|
|
||||||
InstEmitFlowHelper.EmitReturn(context, Const(op.Address));
|
context.Return(Const(op.Address));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
context.LoadFromContext();
|
context.LoadFromContext();
|
||||||
|
|
||||||
InstEmitFlowHelper.EmitReturn(context, Const(context.CurrOp.Address));
|
context.Return(Const(context.CurrOp.Address));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ namespace ARMeilleure.Instructions
|
|||||||
{
|
{
|
||||||
OpCodeBReg op = (OpCodeBReg)context.CurrOp;
|
OpCodeBReg op = (OpCodeBReg)context.CurrOp;
|
||||||
|
|
||||||
EmitReturn(context, GetIntOrZR(context, op.Rn));
|
context.Return(GetIntOrZR(context, op.Rn));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Tbnz(ArmEmitterContext context) => EmitTb(context, onNotZero: true);
|
public static void Tbnz(ArmEmitterContext context) => EmitTb(context, onNotZero: true);
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
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;
|
||||||
@@ -13,10 +12,6 @@ namespace ARMeilleure.Instructions
|
|||||||
{
|
{
|
||||||
static class InstEmitFlowHelper
|
static class InstEmitFlowHelper
|
||||||
{
|
{
|
||||||
// How many calls we can have in our call stack before we give up and return to the dispatcher.
|
|
||||||
// This prevents stack overflows caused by deep recursive calls.
|
|
||||||
private const int MaxCallDepth = 200;
|
|
||||||
|
|
||||||
public static void EmitCondBranch(ArmEmitterContext context, Operand target, Condition cond)
|
public static void EmitCondBranch(ArmEmitterContext context, Operand target, Condition cond)
|
||||||
{
|
{
|
||||||
if (cond != Condition.Al)
|
if (cond != Condition.Al)
|
||||||
@@ -168,7 +163,12 @@ namespace ARMeilleure.Instructions
|
|||||||
{
|
{
|
||||||
if (isReturn)
|
if (isReturn)
|
||||||
{
|
{
|
||||||
EmitReturn(context, target);
|
if (target.Type == OperandType.I32)
|
||||||
|
{
|
||||||
|
target = context.ZeroExtend32(OperandType.I64, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
context.Return(target);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -176,19 +176,6 @@ namespace ARMeilleure.Instructions
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void EmitReturn(ArmEmitterContext context, Operand target)
|
|
||||||
{
|
|
||||||
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
|
|
||||||
DecreaseCallDepth(context, nativeContext);
|
|
||||||
|
|
||||||
if (target.Type == OperandType.I32)
|
|
||||||
{
|
|
||||||
target = context.ZeroExtend32(OperandType.I64, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
context.Return(target);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void EmitTableBranch(ArmEmitterContext context, Operand guestAddress, bool isJump)
|
private static void EmitTableBranch(ArmEmitterContext context, Operand guestAddress, bool isJump)
|
||||||
{
|
{
|
||||||
context.StoreToContext();
|
context.StoreToContext();
|
||||||
@@ -206,7 +193,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.
|
||||||
@@ -231,7 +218,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
for (int i = 0; i < table.Levels.Length; i++)
|
for (int i = 0; i < table.Levels.Length; i++)
|
||||||
{
|
{
|
||||||
AddressTableLevel level = table.Levels[i];
|
var level = table.Levels[i];
|
||||||
int clearBits = 64 - (level.Index + level.Length);
|
int clearBits = 64 - (level.Index + level.Length);
|
||||||
|
|
||||||
Operand index = context.ShiftLeft(
|
Operand index = context.ShiftLeft(
|
||||||
@@ -251,8 +238,6 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
if (isJump)
|
if (isJump)
|
||||||
{
|
{
|
||||||
DecreaseCallDepth(context, nativeContext);
|
|
||||||
|
|
||||||
context.Tailcall(hostAddress, nativeContext);
|
context.Tailcall(hostAddress, nativeContext);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -274,42 +259,8 @@ namespace ARMeilleure.Instructions
|
|||||||
Operand lblContinue = context.GetLabel(nextAddr.Value);
|
Operand lblContinue = context.GetLabel(nextAddr.Value);
|
||||||
context.BranchIf(lblContinue, returnAddress, nextAddr, Comparison.Equal, BasicBlockFrequency.Cold);
|
context.BranchIf(lblContinue, returnAddress, nextAddr, Comparison.Equal, BasicBlockFrequency.Cold);
|
||||||
|
|
||||||
DecreaseCallDepth(context, nativeContext);
|
|
||||||
|
|
||||||
context.Return(returnAddress);
|
context.Return(returnAddress);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void EmitCallDepthCheckAndIncrement(EmitterContext context, Operand guestAddress)
|
|
||||||
{
|
|
||||||
if (!Optimizations.EnableDeepCallRecursionProtection)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
|
|
||||||
Operand callDepthAddr = context.Add(nativeContext, Const((ulong)NativeContext.GetCallDepthOffset()));
|
|
||||||
Operand currentCallDepth = context.Load(OperandType.I32, callDepthAddr);
|
|
||||||
Operand lblDoCall = Label();
|
|
||||||
|
|
||||||
context.BranchIf(lblDoCall, currentCallDepth, Const(MaxCallDepth), Comparison.LessUI);
|
|
||||||
context.Store(callDepthAddr, context.Subtract(currentCallDepth, Const(1)));
|
|
||||||
context.Return(guestAddress);
|
|
||||||
|
|
||||||
context.MarkLabel(lblDoCall);
|
|
||||||
context.Store(callDepthAddr, context.Add(currentCallDepth, Const(1)));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void DecreaseCallDepth(EmitterContext context, Operand nativeContext)
|
|
||||||
{
|
|
||||||
if (!Optimizations.EnableDeepCallRecursionProtection)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Operand callDepthAddr = context.Add(nativeContext, Const((ulong)NativeContext.GetCallDepthOffset()));
|
|
||||||
Operand currentCallDepth = context.Load(OperandType.I32, callDepthAddr);
|
|
||||||
context.Store(callDepthAddr, context.Subtract(currentCallDepth, Const(1)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
if (pair)
|
if (pair)
|
||||||
{
|
{
|
||||||
Debug.Assert(op.Size is 2 or 3, "Invalid size for pairwise store.");
|
Debug.Assert(op.Size == 2 || op.Size == 3, "Invalid size for pairwise store.");
|
||||||
|
|
||||||
Operand t2 = GetIntOrZR(context, op.Rt2);
|
Operand t2 = GetIntOrZR(context, op.Rt2);
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ namespace ARMeilleure.Instructions
|
|||||||
{
|
{
|
||||||
Operand value = GetInt(context, rt);
|
Operand value = GetInt(context, rt);
|
||||||
|
|
||||||
if (ext is Extension.Sx32 or Extension.Sx64)
|
if (ext == Extension.Sx32 || ext == Extension.Sx64)
|
||||||
{
|
{
|
||||||
OperandType destType = ext == Extension.Sx64 ? OperandType.I64 : OperandType.I32;
|
OperandType destType = ext == Extension.Sx64 ? OperandType.I64 : OperandType.I32;
|
||||||
|
|
||||||
@@ -124,7 +124,8 @@ namespace ARMeilleure.Instructions
|
|||||||
private static bool IsSimd(ArmEmitterContext context)
|
private static bool IsSimd(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
return context.CurrOp is IOpCodeSimd &&
|
return context.CurrOp is IOpCodeSimd &&
|
||||||
!(context.CurrOp is OpCodeSimdMemMs or OpCodeSimdMemSs);
|
!(context.CurrOp is OpCodeSimdMemMs ||
|
||||||
|
context.CurrOp is OpCodeSimdMemSs);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Operand EmitReadInt(ArmEmitterContext context, Operand address, int size)
|
public static Operand EmitReadInt(ArmEmitterContext context, Operand address, int size)
|
||||||
|
|||||||
@@ -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));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1119,7 +1119,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
private static Operand EmitFPConvert(ArmEmitterContext context, Operand value, int size, bool signed)
|
private static Operand EmitFPConvert(ArmEmitterContext context, Operand value, int size, bool signed)
|
||||||
{
|
{
|
||||||
Debug.Assert(value.Type is OperandType.I32 or OperandType.I64);
|
Debug.Assert(value.Type == OperandType.I32 || value.Type == OperandType.I64);
|
||||||
Debug.Assert((uint)size < 2);
|
Debug.Assert((uint)size < 2);
|
||||||
|
|
||||||
OperandType type = size == 0 ? OperandType.FP32 : OperandType.FP64;
|
OperandType type = size == 0 ? OperandType.FP32 : OperandType.FP64;
|
||||||
@@ -1136,7 +1136,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
private static Operand EmitScalarFcvts(ArmEmitterContext context, Operand value, int fBits)
|
private static Operand EmitScalarFcvts(ArmEmitterContext context, Operand value, int fBits)
|
||||||
{
|
{
|
||||||
Debug.Assert(value.Type is OperandType.FP32 or OperandType.FP64);
|
Debug.Assert(value.Type == OperandType.FP32 || value.Type == OperandType.FP64);
|
||||||
|
|
||||||
value = EmitF2iFBitsMul(context, value, fBits);
|
value = EmitF2iFBitsMul(context, value, fBits);
|
||||||
|
|
||||||
@@ -1160,7 +1160,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
private static Operand EmitScalarFcvtu(ArmEmitterContext context, Operand value, int fBits)
|
private static Operand EmitScalarFcvtu(ArmEmitterContext context, Operand value, int fBits)
|
||||||
{
|
{
|
||||||
Debug.Assert(value.Type is OperandType.FP32 or OperandType.FP64);
|
Debug.Assert(value.Type == OperandType.FP32 || value.Type == OperandType.FP64);
|
||||||
|
|
||||||
value = EmitF2iFBitsMul(context, value, fBits);
|
value = EmitF2iFBitsMul(context, value, fBits);
|
||||||
|
|
||||||
@@ -1184,7 +1184,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
private static Operand EmitF2iFBitsMul(ArmEmitterContext context, Operand value, int fBits)
|
private static Operand EmitF2iFBitsMul(ArmEmitterContext context, Operand value, int fBits)
|
||||||
{
|
{
|
||||||
Debug.Assert(value.Type is OperandType.FP32 or OperandType.FP64);
|
Debug.Assert(value.Type == OperandType.FP32 || value.Type == OperandType.FP64);
|
||||||
|
|
||||||
if (fBits == 0)
|
if (fBits == 0)
|
||||||
{
|
{
|
||||||
@@ -1203,7 +1203,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
private static Operand EmitI2fFBitsMul(ArmEmitterContext context, Operand value, int fBits)
|
private static Operand EmitI2fFBitsMul(ArmEmitterContext context, Operand value, int fBits)
|
||||||
{
|
{
|
||||||
Debug.Assert(value.Type is OperandType.FP32 or OperandType.FP64);
|
Debug.Assert(value.Type == OperandType.FP32 || value.Type == OperandType.FP64);
|
||||||
|
|
||||||
if (fBits == 0)
|
if (fBits == 0)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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);
|
||||||
@@ -635,7 +635,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
private static Operand EmitFPConvert(ArmEmitterContext context, Operand value, OperandType type, bool signed)
|
private static Operand EmitFPConvert(ArmEmitterContext context, Operand value, OperandType type, bool signed)
|
||||||
{
|
{
|
||||||
Debug.Assert(value.Type is OperandType.I32 or OperandType.I64);
|
Debug.Assert(value.Type == OperandType.I32 || value.Type == OperandType.I64);
|
||||||
|
|
||||||
if (signed)
|
if (signed)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -363,7 +363,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
public static Operand EmitCountSetBits8(ArmEmitterContext context, Operand op) // "size" is 8 (SIMD&FP Inst.).
|
public static Operand EmitCountSetBits8(ArmEmitterContext context, Operand op) // "size" is 8 (SIMD&FP Inst.).
|
||||||
{
|
{
|
||||||
Debug.Assert(op.Type is OperandType.I32 or OperandType.I64);
|
Debug.Assert(op.Type == OperandType.I32 || op.Type == OperandType.I64);
|
||||||
|
|
||||||
Operand op0 = context.Subtract(op, context.BitwiseAnd(context.ShiftRightUI(op, Const(1)), Const(op.Type, 0x55L)));
|
Operand op0 = context.Subtract(op, context.BitwiseAnd(context.ShiftRightUI(op, Const(1)), Const(op.Type, 0x55L)));
|
||||||
|
|
||||||
@@ -489,7 +489,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
public static Operand EmitRoundByRMode(ArmEmitterContext context, Operand op)
|
public static Operand EmitRoundByRMode(ArmEmitterContext context, Operand op)
|
||||||
{
|
{
|
||||||
Debug.Assert(op.Type is OperandType.FP32 or OperandType.FP64);
|
Debug.Assert(op.Type == OperandType.FP32 || op.Type == OperandType.FP64);
|
||||||
|
|
||||||
Operand lbl1 = Label();
|
Operand lbl1 = Label();
|
||||||
Operand lbl2 = Label();
|
Operand lbl2 = Label();
|
||||||
@@ -1676,7 +1676,7 @@ namespace ARMeilleure.Instructions
|
|||||||
int eSize = 8 << size;
|
int eSize = 8 << size;
|
||||||
|
|
||||||
Debug.Assert(op.Type == OperandType.I64);
|
Debug.Assert(op.Type == OperandType.I64);
|
||||||
Debug.Assert(eSize is 8 or 16 or 32 or 64);
|
Debug.Assert(eSize == 8 || eSize == 16 || eSize == 32 || eSize == 64);
|
||||||
|
|
||||||
Operand lbl1 = Label();
|
Operand lbl1 = Label();
|
||||||
Operand lblEnd = Label();
|
Operand lblEnd = Label();
|
||||||
@@ -1709,7 +1709,7 @@ namespace ARMeilleure.Instructions
|
|||||||
int eSize = 8 << size;
|
int eSize = 8 << size;
|
||||||
|
|
||||||
Debug.Assert(op.Type == OperandType.I64);
|
Debug.Assert(op.Type == OperandType.I64);
|
||||||
Debug.Assert(eSize is 8 or 16 or 32 or 64);
|
Debug.Assert(eSize == 8 || eSize == 16 || eSize == 32 || eSize == 64);
|
||||||
|
|
||||||
Operand lblEnd = Label();
|
Operand lblEnd = Label();
|
||||||
|
|
||||||
@@ -1735,7 +1735,7 @@ namespace ARMeilleure.Instructions
|
|||||||
int eSizeDst = 8 << sizeDst;
|
int eSizeDst = 8 << sizeDst;
|
||||||
|
|
||||||
Debug.Assert(op.Type == OperandType.I64);
|
Debug.Assert(op.Type == OperandType.I64);
|
||||||
Debug.Assert(eSizeDst is 8 or 16 or 32);
|
Debug.Assert(eSizeDst == 8 || eSizeDst == 16 || eSizeDst == 32);
|
||||||
|
|
||||||
Operand lbl1 = Label();
|
Operand lbl1 = Label();
|
||||||
Operand lblEnd = Label();
|
Operand lblEnd = Label();
|
||||||
@@ -1768,7 +1768,7 @@ namespace ARMeilleure.Instructions
|
|||||||
int eSizeDst = 8 << sizeDst;
|
int eSizeDst = 8 << sizeDst;
|
||||||
|
|
||||||
Debug.Assert(op.Type == OperandType.I64);
|
Debug.Assert(op.Type == OperandType.I64);
|
||||||
Debug.Assert(eSizeDst is 8 or 16 or 32);
|
Debug.Assert(eSizeDst == 8 || eSizeDst == 16 || eSizeDst == 32);
|
||||||
|
|
||||||
Operand lblEnd = Label();
|
Operand lblEnd = Label();
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ namespace ARMeilleure.Instructions
|
|||||||
{
|
{
|
||||||
Debug.Assert(type != OperandType.V128);
|
Debug.Assert(type != OperandType.V128);
|
||||||
|
|
||||||
if (type is OperandType.FP64 or OperandType.I64)
|
if (type == OperandType.FP64 || type == OperandType.I64)
|
||||||
{
|
{
|
||||||
// From dreg.
|
// From dreg.
|
||||||
return context.VectorExtract(type, GetVecA32(reg >> 1), reg & 1);
|
return context.VectorExtract(type, GetVecA32(reg >> 1), reg & 1);
|
||||||
@@ -48,7 +48,7 @@ namespace ARMeilleure.Instructions
|
|||||||
Debug.Assert(value.Type != OperandType.V128);
|
Debug.Assert(value.Type != OperandType.V128);
|
||||||
|
|
||||||
Operand vec, insert;
|
Operand vec, insert;
|
||||||
if (value.Type is OperandType.FP64 or OperandType.I64)
|
if (value.Type == OperandType.FP64 || value.Type == OperandType.I64)
|
||||||
{
|
{
|
||||||
// From dreg.
|
// From dreg.
|
||||||
vec = GetVecA32(reg >> 1);
|
vec = GetVecA32(reg >> 1);
|
||||||
@@ -71,7 +71,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
public static void InsertScalar16(ArmEmitterContext context, int reg, bool top, Operand value)
|
public static void InsertScalar16(ArmEmitterContext context, int reg, bool top, Operand value)
|
||||||
{
|
{
|
||||||
Debug.Assert(value.Type is OperandType.FP32 or OperandType.I32);
|
Debug.Assert(value.Type == OperandType.FP32 || value.Type == OperandType.I32);
|
||||||
|
|
||||||
Operand vec, insert;
|
Operand vec, insert;
|
||||||
vec = GetVecA32(reg >> 2);
|
vec = GetVecA32(reg >> 2);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -1634,7 +1634,7 @@ namespace ARMeilleure.Instructions
|
|||||||
int eSize = 8 << size;
|
int eSize = 8 << size;
|
||||||
|
|
||||||
Debug.Assert(op.Type == OperandType.I64);
|
Debug.Assert(op.Type == OperandType.I64);
|
||||||
Debug.Assert(eSize is 8 or 16 or 32 or 64);
|
Debug.Assert(eSize == 8 || eSize == 16 || eSize == 32 || eSize == 64);
|
||||||
|
|
||||||
Operand res = context.AllocateLocal(OperandType.I64);
|
Operand res = context.AllocateLocal(OperandType.I64);
|
||||||
|
|
||||||
@@ -1657,7 +1657,7 @@ namespace ARMeilleure.Instructions
|
|||||||
int eSize = 8 << size;
|
int eSize = 8 << size;
|
||||||
|
|
||||||
Debug.Assert(op.Type == OperandType.I64);
|
Debug.Assert(op.Type == OperandType.I64);
|
||||||
Debug.Assert(eSize is 8 or 16 or 32 or 64);
|
Debug.Assert(eSize == 8 || eSize == 16 || eSize == 32 || eSize == 64);
|
||||||
|
|
||||||
Operand lblEnd = Label();
|
Operand lblEnd = Label();
|
||||||
|
|
||||||
@@ -1732,7 +1732,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
Debug.Assert(op.Type == OperandType.I64);
|
Debug.Assert(op.Type == OperandType.I64);
|
||||||
Debug.Assert(shiftLsB.Type == OperandType.I32);
|
Debug.Assert(shiftLsB.Type == OperandType.I32);
|
||||||
Debug.Assert(eSize is 8 or 16 or 32 or 64);
|
Debug.Assert(eSize == 8 || eSize == 16 || eSize == 32 || eSize == 64);
|
||||||
|
|
||||||
Operand lbl1 = Label();
|
Operand lbl1 = Label();
|
||||||
Operand lblEnd = Label();
|
Operand lblEnd = Label();
|
||||||
@@ -1769,7 +1769,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
Debug.Assert(op.Type == OperandType.I64);
|
Debug.Assert(op.Type == OperandType.I64);
|
||||||
Debug.Assert(shiftLsB.Type == OperandType.I32);
|
Debug.Assert(shiftLsB.Type == OperandType.I32);
|
||||||
Debug.Assert(eSize is 8 or 16 or 32 or 64);
|
Debug.Assert(eSize == 8 || eSize == 16 || eSize == 32 || eSize == 64);
|
||||||
|
|
||||||
Operand lbl1 = Label();
|
Operand lbl1 = Label();
|
||||||
Operand lbl2 = Label();
|
Operand lbl2 = Label();
|
||||||
|
|||||||
@@ -330,7 +330,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
float result;
|
float result;
|
||||||
|
|
||||||
if (type is FPType.SNaN or FPType.QNaN)
|
if (type == FPType.SNaN || type == FPType.QNaN)
|
||||||
{
|
{
|
||||||
if ((context.Fpcr & FPCR.Dn) != 0)
|
if ((context.Fpcr & FPCR.Dn) != 0)
|
||||||
{
|
{
|
||||||
@@ -510,7 +510,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
double result;
|
double result;
|
||||||
|
|
||||||
if (type is FPType.SNaN or FPType.QNaN)
|
if (type == FPType.SNaN || type == FPType.QNaN)
|
||||||
{
|
{
|
||||||
if ((context.Fpcr & FPCR.Dn) != 0)
|
if ((context.Fpcr & FPCR.Dn) != 0)
|
||||||
{
|
{
|
||||||
@@ -692,7 +692,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
ushort resultBits;
|
ushort resultBits;
|
||||||
|
|
||||||
if (type is FPType.SNaN or FPType.QNaN)
|
if (type == FPType.SNaN || type == FPType.QNaN)
|
||||||
{
|
{
|
||||||
if (altHp)
|
if (altHp)
|
||||||
{
|
{
|
||||||
@@ -1670,7 +1670,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
float result;
|
float result;
|
||||||
|
|
||||||
if (type is FPType.SNaN or FPType.QNaN)
|
if (type == FPType.SNaN || type == FPType.QNaN)
|
||||||
{
|
{
|
||||||
result = FPProcessNaN(type, op, context, fpcr);
|
result = FPProcessNaN(type, op, context, fpcr);
|
||||||
}
|
}
|
||||||
@@ -1686,7 +1686,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,
|
||||||
@@ -1849,7 +1849,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
float result;
|
float result;
|
||||||
|
|
||||||
if (type is FPType.SNaN or FPType.QNaN)
|
if (type == FPType.SNaN || type == FPType.QNaN)
|
||||||
{
|
{
|
||||||
result = FPProcessNaN(type, op, context, fpcr);
|
result = FPProcessNaN(type, op, context, fpcr);
|
||||||
}
|
}
|
||||||
@@ -1894,7 +1894,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
float result;
|
float result;
|
||||||
|
|
||||||
if (type is FPType.SNaN or FPType.QNaN)
|
if (type == FPType.SNaN || type == FPType.QNaN)
|
||||||
{
|
{
|
||||||
result = FPProcessNaN(type, op, context, fpcr);
|
result = FPProcessNaN(type, op, context, fpcr);
|
||||||
}
|
}
|
||||||
@@ -2100,7 +2100,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
float result;
|
float result;
|
||||||
|
|
||||||
if (type is FPType.SNaN or FPType.QNaN)
|
if (type == FPType.SNaN || type == FPType.QNaN)
|
||||||
{
|
{
|
||||||
result = FPProcessNaN(type, op, context, fpcr);
|
result = FPProcessNaN(type, op, context, fpcr);
|
||||||
}
|
}
|
||||||
@@ -2399,7 +2399,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
ushort resultBits;
|
ushort resultBits;
|
||||||
|
|
||||||
if (type is FPType.SNaN or FPType.QNaN)
|
if (type == FPType.SNaN || type == FPType.QNaN)
|
||||||
{
|
{
|
||||||
if (altHp)
|
if (altHp)
|
||||||
{
|
{
|
||||||
@@ -3377,7 +3377,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
double result;
|
double result;
|
||||||
|
|
||||||
if (type is FPType.SNaN or FPType.QNaN)
|
if (type == FPType.SNaN || type == FPType.QNaN)
|
||||||
{
|
{
|
||||||
result = FPProcessNaN(type, op, context, fpcr);
|
result = FPProcessNaN(type, op, context, fpcr);
|
||||||
}
|
}
|
||||||
@@ -3393,7 +3393,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,
|
||||||
@@ -3556,7 +3556,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
double result;
|
double result;
|
||||||
|
|
||||||
if (type is FPType.SNaN or FPType.QNaN)
|
if (type == FPType.SNaN || type == FPType.QNaN)
|
||||||
{
|
{
|
||||||
result = FPProcessNaN(type, op, context, fpcr);
|
result = FPProcessNaN(type, op, context, fpcr);
|
||||||
}
|
}
|
||||||
@@ -3601,7 +3601,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
double result;
|
double result;
|
||||||
|
|
||||||
if (type is FPType.SNaN or FPType.QNaN)
|
if (type == FPType.SNaN || type == FPType.QNaN)
|
||||||
{
|
{
|
||||||
result = FPProcessNaN(type, op, context, fpcr);
|
result = FPProcessNaN(type, op, context, fpcr);
|
||||||
}
|
}
|
||||||
@@ -3807,7 +3807,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
double result;
|
double result;
|
||||||
|
|
||||||
if (type is FPType.SNaN or FPType.QNaN)
|
if (type == FPType.SNaN || type == FPType.QNaN)
|
||||||
{
|
{
|
||||||
result = FPProcessNaN(type, op, context, fpcr);
|
result = FPProcessNaN(type, op, context, fpcr);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ namespace ARMeilleure.IntermediateRepresentation
|
|||||||
/// <exception cref="ArgumentException"><typeparamref name="T"/> is not pointer sized.</exception>
|
/// <exception cref="ArgumentException"><typeparamref name="T"/> is not pointer sized.</exception>
|
||||||
public IntrusiveList()
|
public IntrusiveList()
|
||||||
{
|
{
|
||||||
if (Unsafe.SizeOf<T>() != nint.Size)
|
if (Unsafe.SizeOf<T>() != IntPtr.Size)
|
||||||
{
|
{
|
||||||
throw new ArgumentException("T must be a reference type or a pointer sized struct.");
|
throw new ArgumentException("T must be a reference type or a pointer sized struct.");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
@@ -23,7 +24,7 @@ namespace ARMeilleure.IntermediateRepresentation
|
|||||||
{
|
{
|
||||||
Debug.Assert(operand.Kind == OperandKind.Memory);
|
Debug.Assert(operand.Kind == OperandKind.Memory);
|
||||||
|
|
||||||
_data = (Data*)Unsafe.As<Operand, nint>(ref operand);
|
_data = (Data*)Unsafe.As<Operand, IntPtr>(ref operand);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Operand BaseAddress
|
public Operand BaseAddress
|
||||||
|
|||||||
@@ -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++)
|
||||||
{
|
{
|
||||||
@@ -446,7 +446,7 @@ namespace ARMeilleure.IntermediateRepresentation
|
|||||||
Data* data = null;
|
Data* data = null;
|
||||||
|
|
||||||
// If constant or register, then try to look up in the intern table before allocating.
|
// If constant or register, then try to look up in the intern table before allocating.
|
||||||
if (kind is OperandKind.Constant or OperandKind.Register)
|
if (kind == OperandKind.Constant || kind == OperandKind.Register)
|
||||||
{
|
{
|
||||||
uint hash = (uint)HashCode.Combine(kind, type, value);
|
uint hash = (uint)HashCode.Combine(kind, type, value);
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,8 @@ namespace ARMeilleure.IntermediateRepresentation
|
|||||||
{
|
{
|
||||||
public static bool IsInteger(this OperandType type)
|
public static bool IsInteger(this OperandType type)
|
||||||
{
|
{
|
||||||
return type is OperandType.I32 or OperandType.I64;
|
return type == OperandType.I32 ||
|
||||||
|
type == OperandType.I64;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static RegisterType ToRegisterType(this OperandType type)
|
public static RegisterType ToRegisterType(this OperandType type)
|
||||||
|
|||||||
@@ -228,7 +228,7 @@ namespace ARMeilleure.IntermediateRepresentation
|
|||||||
|
|
||||||
public readonly override int GetHashCode()
|
public readonly override int GetHashCode()
|
||||||
{
|
{
|
||||||
return HashCode.Combine((nint)_data);
|
return HashCode.Combine((IntPtr)_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool operator ==(Operation a, Operation b)
|
public static bool operator ==(Operation a, Operation b)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ namespace ARMeilleure.Memory
|
|||||||
{
|
{
|
||||||
public interface IJitMemoryBlock : IDisposable
|
public interface IJitMemoryBlock : IDisposable
|
||||||
{
|
{
|
||||||
nint Pointer { get; }
|
IntPtr Pointer { get; }
|
||||||
|
|
||||||
void Commit(ulong offset, ulong size);
|
void Commit(ulong offset, ulong size);
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ namespace ARMeilleure.Memory
|
|||||||
{
|
{
|
||||||
int AddressSpaceBits { get; }
|
int AddressSpaceBits { get; }
|
||||||
|
|
||||||
nint PageTablePointer { get; }
|
IntPtr PageTablePointer { get; }
|
||||||
|
|
||||||
MemoryManagerType Type { get; }
|
MemoryManagerType Type { get; }
|
||||||
|
|
||||||
|
|||||||
@@ -47,12 +47,12 @@ namespace ARMeilleure.Memory
|
|||||||
{
|
{
|
||||||
public static bool IsHostMapped(this MemoryManagerType type)
|
public static bool IsHostMapped(this MemoryManagerType type)
|
||||||
{
|
{
|
||||||
return type is MemoryManagerType.HostMapped or MemoryManagerType.HostMappedUnsafe;
|
return type == MemoryManagerType.HostMapped || type == MemoryManagerType.HostMappedUnsafe;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsHostTracked(this MemoryManagerType type)
|
public static bool IsHostTracked(this MemoryManagerType type)
|
||||||
{
|
{
|
||||||
return type is MemoryManagerType.HostTracked or MemoryManagerType.HostTrackedUnsafe;
|
return type == MemoryManagerType.HostTracked || type == MemoryManagerType.HostTrackedUnsafe;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsHostMappedOrTracked(this MemoryManagerType type)
|
public static bool IsHostMappedOrTracked(this MemoryManagerType type)
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ namespace ARMeilleure.Memory
|
|||||||
public IJitMemoryBlock Block { get; }
|
public IJitMemoryBlock Block { get; }
|
||||||
public IJitMemoryAllocator Allocator { get; }
|
public IJitMemoryAllocator Allocator { get; }
|
||||||
|
|
||||||
public nint Pointer => Block.Pointer;
|
public IntPtr Pointer => Block.Pointer;
|
||||||
|
|
||||||
private readonly ulong _maxSize;
|
private readonly ulong _maxSize;
|
||||||
private readonly ulong _sizeGranularity;
|
private readonly ulong _sizeGranularity;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
@@ -7,6 +8,6 @@ namespace ARMeilleure.Native
|
|||||||
static partial class JitSupportDarwin
|
static partial class JitSupportDarwin
|
||||||
{
|
{
|
||||||
[LibraryImport("libarmeilleure-jitsupport", EntryPoint = "armeilleure_jit_memcpy")]
|
[LibraryImport("libarmeilleure-jitsupport", EntryPoint = "armeilleure_jit_memcpy")]
|
||||||
public static partial void Copy(nint dst, nint src, ulong n);
|
public static partial void Copy(IntPtr dst, IntPtr src, ulong n);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,14 +8,10 @@ namespace ARMeilleure
|
|||||||
// low-core count PPTC
|
// low-core count PPTC
|
||||||
public static bool EcoFriendly { get; set; } = false;
|
public static bool EcoFriendly { get; set; } = false;
|
||||||
|
|
||||||
// Jit cache eviction
|
|
||||||
public static bool CacheEviction { get; set; } = false;
|
|
||||||
|
|
||||||
public static bool FastFP { get; set; } = true;
|
public static bool FastFP { get; set; } = true;
|
||||||
|
|
||||||
public static bool AllowLcqInFunctionTable { get; set; } = true;
|
public static bool AllowLcqInFunctionTable { get; set; } = true;
|
||||||
public static bool UseUnmanagedDispatchLoop { get; set; } = true;
|
public static bool UseUnmanagedDispatchLoop { get; set; } = true;
|
||||||
public static bool EnableDeepCallRecursionProtection { get; set; } = true;
|
|
||||||
|
|
||||||
public static bool UseAdvSimdIfAvailable { get; set; } = true;
|
public static bool UseAdvSimdIfAvailable { get; set; } = true;
|
||||||
public static bool UseArm64AesIfAvailable { get; set; } = true;
|
public static bool UseArm64AesIfAvailable { get; set; } = true;
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ namespace ARMeilleure.Signal
|
|||||||
{
|
{
|
||||||
public static class NativeSignalHandlerGenerator
|
public static class NativeSignalHandlerGenerator
|
||||||
{
|
{
|
||||||
public const int MaxTrackedRanges = 8;
|
public const int MaxTrackedRanges = 16;
|
||||||
|
|
||||||
private const int StructAddressOffset = 0;
|
private const int StructAddressOffset = 0;
|
||||||
private const int StructWriteOffset = 4;
|
private const int StructWriteOffset = 4;
|
||||||
@@ -21,7 +21,7 @@ namespace ARMeilleure.Signal
|
|||||||
|
|
||||||
private const uint EXCEPTION_ACCESS_VIOLATION = 0xc0000005;
|
private const uint EXCEPTION_ACCESS_VIOLATION = 0xc0000005;
|
||||||
|
|
||||||
private static Operand EmitGenericRegionCheck(EmitterContext context, nint signalStructPtr, Operand faultAddress, Operand isWrite, int rangeStructSize)
|
private static Operand EmitGenericRegionCheck(EmitterContext context, IntPtr signalStructPtr, Operand faultAddress, Operand isWrite, int rangeStructSize)
|
||||||
{
|
{
|
||||||
Operand inRegionLocal = context.AllocateLocal(OperandType.I32);
|
Operand inRegionLocal = context.AllocateLocal(OperandType.I32);
|
||||||
context.Copy(inRegionLocal, Const(0));
|
context.Copy(inRegionLocal, Const(0));
|
||||||
@@ -155,7 +155,7 @@ namespace ARMeilleure.Signal
|
|||||||
throw new PlatformNotSupportedException();
|
throw new PlatformNotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] GenerateUnixSignalHandler(nint signalStructPtr, int rangeStructSize)
|
public static byte[] GenerateUnixSignalHandler(IntPtr signalStructPtr, int rangeStructSize)
|
||||||
{
|
{
|
||||||
EmitterContext context = new();
|
EmitterContext context = new();
|
||||||
|
|
||||||
@@ -203,7 +203,7 @@ namespace ARMeilleure.Signal
|
|||||||
return Compiler.Compile(cfg, argTypes, OperandType.None, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Code;
|
return Compiler.Compile(cfg, argTypes, OperandType.None, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Code;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] GenerateWindowsSignalHandler(nint signalStructPtr, int rangeStructSize)
|
public static byte[] GenerateWindowsSignalHandler(IntPtr signalStructPtr, int rangeStructSize)
|
||||||
{
|
{
|
||||||
EmitterContext context = new();
|
EmitterContext context = new();
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using ARMeilleure.IntermediateRepresentation;
|
using ARMeilleure.IntermediateRepresentation;
|
||||||
using ARMeilleure.Translation;
|
using ARMeilleure.Translation;
|
||||||
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||||
|
|
||||||
@@ -15,13 +16,13 @@ namespace ARMeilleure.Signal
|
|||||||
{
|
{
|
||||||
public delegate bool DebugPartialUnmap();
|
public delegate bool DebugPartialUnmap();
|
||||||
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(IntPtr nativeWriteLoopPtr, IntPtr writePtr);
|
||||||
|
|
||||||
public static DebugPartialUnmap GenerateDebugPartialUnmap()
|
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);
|
||||||
|
|
||||||
@@ -34,11 +35,11 @@ namespace ARMeilleure.Signal
|
|||||||
return Compiler.Compile(cfg, argTypes, OperandType.I32, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<DebugPartialUnmap>();
|
return Compiler.Compile(cfg, argTypes, OperandType.I32, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<DebugPartialUnmap>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static DebugThreadLocalMapGetOrReserve GenerateDebugThreadLocalMapGetOrReserve(nint structPtr)
|
public static DebugThreadLocalMapGetOrReserve GenerateDebugThreadLocalMapGetOrReserve(IntPtr 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);
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using ARMeilleure.IntermediateRepresentation;
|
using ARMeilleure.IntermediateRepresentation;
|
||||||
using ARMeilleure.Translation;
|
using ARMeilleure.Translation;
|
||||||
using Ryujinx.Common.Memory.PartialUnmaps;
|
using Ryujinx.Common.Memory.PartialUnmaps;
|
||||||
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||||
|
|
||||||
@@ -12,18 +13,18 @@ namespace ARMeilleure.Signal
|
|||||||
internal static partial class WindowsPartialUnmapHandler
|
internal static partial class WindowsPartialUnmapHandler
|
||||||
{
|
{
|
||||||
[LibraryImport("kernel32.dll", SetLastError = true, EntryPoint = "LoadLibraryA")]
|
[LibraryImport("kernel32.dll", SetLastError = true, EntryPoint = "LoadLibraryA")]
|
||||||
private static partial nint LoadLibrary([MarshalAs(UnmanagedType.LPStr)] string lpFileName);
|
private static partial IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)] string lpFileName);
|
||||||
|
|
||||||
[LibraryImport("kernel32.dll", SetLastError = true)]
|
[LibraryImport("kernel32.dll", SetLastError = true)]
|
||||||
private static partial nint GetProcAddress(nint hModule, [MarshalAs(UnmanagedType.LPStr)] string procName);
|
private static partial IntPtr GetProcAddress(IntPtr hModule, [MarshalAs(UnmanagedType.LPStr)] string procName);
|
||||||
|
|
||||||
private static nint _getCurrentThreadIdPtr;
|
private static IntPtr _getCurrentThreadIdPtr;
|
||||||
|
|
||||||
public static nint GetCurrentThreadIdFunc()
|
public static IntPtr GetCurrentThreadIdFunc()
|
||||||
{
|
{
|
||||||
if (_getCurrentThreadIdPtr == nint.Zero)
|
if (_getCurrentThreadIdPtr == IntPtr.Zero)
|
||||||
{
|
{
|
||||||
nint handle = LoadLibrary("kernel32.dll");
|
IntPtr handle = LoadLibrary("kernel32.dll");
|
||||||
|
|
||||||
_getCurrentThreadIdPtr = GetProcAddress(handle, "GetCurrentThreadId");
|
_getCurrentThreadIdPtr = GetProcAddress(handle, "GetCurrentThreadId");
|
||||||
}
|
}
|
||||||
@@ -33,13 +34,13 @@ namespace ARMeilleure.Signal
|
|||||||
|
|
||||||
public static Operand EmitRetryFromAccessViolation(EmitterContext context)
|
public static Operand EmitRetryFromAccessViolation(EmitterContext context)
|
||||||
{
|
{
|
||||||
nint partialRemapStatePtr = PartialUnmapState.GlobalState;
|
IntPtr partialRemapStatePtr = PartialUnmapState.GlobalState;
|
||||||
nint localCountsPtr = nint.Add(partialRemapStatePtr, PartialUnmapState.LocalCountsOffset);
|
IntPtr localCountsPtr = IntPtr.Add(partialRemapStatePtr, PartialUnmapState.LocalCountsOffset);
|
||||||
|
|
||||||
// Get the lock first.
|
// Get the lock first.
|
||||||
EmitNativeReaderLockAcquire(context, nint.Add(partialRemapStatePtr, PartialUnmapState.PartialUnmapLockOffset));
|
EmitNativeReaderLockAcquire(context, IntPtr.Add(partialRemapStatePtr, PartialUnmapState.PartialUnmapLockOffset));
|
||||||
|
|
||||||
nint getCurrentThreadId = GetCurrentThreadIdFunc();
|
IntPtr getCurrentThreadId = GetCurrentThreadIdFunc();
|
||||||
Operand threadId = context.Call(Const((ulong)getCurrentThreadId), OperandType.I32);
|
Operand threadId = context.Call(Const((ulong)getCurrentThreadId), OperandType.I32);
|
||||||
Operand threadIndex = EmitThreadLocalMapIntGetOrReserve(context, localCountsPtr, threadId, Const(0));
|
Operand threadIndex = EmitThreadLocalMapIntGetOrReserve(context, localCountsPtr, threadId, Const(0));
|
||||||
|
|
||||||
@@ -57,7 +58,7 @@ namespace ARMeilleure.Signal
|
|||||||
|
|
||||||
Operand threadLocalPartialUnmapsPtr = EmitThreadLocalMapIntGetValuePtr(context, localCountsPtr, threadIndex);
|
Operand threadLocalPartialUnmapsPtr = EmitThreadLocalMapIntGetValuePtr(context, localCountsPtr, threadIndex);
|
||||||
Operand threadLocalPartialUnmaps = context.Load(OperandType.I32, threadLocalPartialUnmapsPtr);
|
Operand threadLocalPartialUnmaps = context.Load(OperandType.I32, threadLocalPartialUnmapsPtr);
|
||||||
Operand partialUnmapsCount = context.Load(OperandType.I32, Const((ulong)nint.Add(partialRemapStatePtr, PartialUnmapState.PartialUnmapsCountOffset)));
|
Operand partialUnmapsCount = context.Load(OperandType.I32, Const((ulong)IntPtr.Add(partialRemapStatePtr, PartialUnmapState.PartialUnmapsCountOffset)));
|
||||||
|
|
||||||
context.Copy(retry, context.ICompareNotEqual(threadLocalPartialUnmaps, partialUnmapsCount));
|
context.Copy(retry, context.ICompareNotEqual(threadLocalPartialUnmaps, partialUnmapsCount));
|
||||||
|
|
||||||
@@ -78,14 +79,14 @@ namespace ARMeilleure.Signal
|
|||||||
context.MarkLabel(endLabel);
|
context.MarkLabel(endLabel);
|
||||||
|
|
||||||
// Finally, release the lock and return the retry value.
|
// Finally, release the lock and return the retry value.
|
||||||
EmitNativeReaderLockRelease(context, nint.Add(partialRemapStatePtr, PartialUnmapState.PartialUnmapLockOffset));
|
EmitNativeReaderLockRelease(context, IntPtr.Add(partialRemapStatePtr, PartialUnmapState.PartialUnmapLockOffset));
|
||||||
|
|
||||||
return retry;
|
return retry;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Operand EmitThreadLocalMapIntGetOrReserve(EmitterContext context, nint threadLocalMapPtr, Operand threadId, Operand initialState)
|
public static Operand EmitThreadLocalMapIntGetOrReserve(EmitterContext context, IntPtr threadLocalMapPtr, Operand threadId, Operand initialState)
|
||||||
{
|
{
|
||||||
Operand idsPtr = Const((ulong)nint.Add(threadLocalMapPtr, ThreadLocalMap<int>.ThreadIdsOffset));
|
Operand idsPtr = Const((ulong)IntPtr.Add(threadLocalMapPtr, ThreadLocalMap<int>.ThreadIdsOffset));
|
||||||
|
|
||||||
Operand i = context.AllocateLocal(OperandType.I32);
|
Operand i = context.AllocateLocal(OperandType.I32);
|
||||||
|
|
||||||
@@ -129,7 +130,7 @@ namespace ARMeilleure.Signal
|
|||||||
// If it was 0, then we need to initialize the struct entry and return i.
|
// If it was 0, then we need to initialize the struct entry and return i.
|
||||||
context.BranchIfFalse(idNot0Label, context.ICompareEqual(existingId2, Const(0)));
|
context.BranchIfFalse(idNot0Label, context.ICompareEqual(existingId2, Const(0)));
|
||||||
|
|
||||||
Operand structsPtr = Const((ulong)nint.Add(threadLocalMapPtr, ThreadLocalMap<int>.StructsOffset));
|
Operand structsPtr = Const((ulong)IntPtr.Add(threadLocalMapPtr, ThreadLocalMap<int>.StructsOffset));
|
||||||
Operand structPtr = context.Add(structsPtr, context.SignExtend32(OperandType.I64, offset2));
|
Operand structPtr = context.Add(structsPtr, context.SignExtend32(OperandType.I64, offset2));
|
||||||
context.Store(structPtr, initialState);
|
context.Store(structPtr, initialState);
|
||||||
|
|
||||||
@@ -148,10 +149,10 @@ namespace ARMeilleure.Signal
|
|||||||
return context.Copy(i);
|
return context.Copy(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Operand EmitThreadLocalMapIntGetValuePtr(EmitterContext context, nint threadLocalMapPtr, Operand index)
|
private static Operand EmitThreadLocalMapIntGetValuePtr(EmitterContext context, IntPtr threadLocalMapPtr, Operand index)
|
||||||
{
|
{
|
||||||
Operand offset = context.Multiply(index, Const(sizeof(int)));
|
Operand offset = context.Multiply(index, Const(sizeof(int)));
|
||||||
Operand structsPtr = Const((ulong)nint.Add(threadLocalMapPtr, ThreadLocalMap<int>.StructsOffset));
|
Operand structsPtr = Const((ulong)IntPtr.Add(threadLocalMapPtr, ThreadLocalMap<int>.StructsOffset));
|
||||||
|
|
||||||
return context.Add(structsPtr, context.SignExtend32(OperandType.I64, offset));
|
return context.Add(structsPtr, context.SignExtend32(OperandType.I64, offset));
|
||||||
}
|
}
|
||||||
@@ -169,9 +170,9 @@ namespace ARMeilleure.Signal
|
|||||||
context.BranchIfFalse(loop, context.ICompareEqual(initial, replaced));
|
context.BranchIfFalse(loop, context.ICompareEqual(initial, replaced));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void EmitNativeReaderLockAcquire(EmitterContext context, nint nativeReaderLockPtr)
|
private static void EmitNativeReaderLockAcquire(EmitterContext context, IntPtr nativeReaderLockPtr)
|
||||||
{
|
{
|
||||||
Operand writeLockPtr = Const((ulong)nint.Add(nativeReaderLockPtr, NativeReaderWriterLock.WriteLockOffset));
|
Operand writeLockPtr = Const((ulong)IntPtr.Add(nativeReaderLockPtr, NativeReaderWriterLock.WriteLockOffset));
|
||||||
|
|
||||||
// Spin until we can acquire the write lock.
|
// Spin until we can acquire the write lock.
|
||||||
Operand spinLabel = Label();
|
Operand spinLabel = Label();
|
||||||
@@ -181,16 +182,16 @@ namespace ARMeilleure.Signal
|
|||||||
context.BranchIfTrue(spinLabel, context.CompareAndSwap(writeLockPtr, Const(0), Const(1)));
|
context.BranchIfTrue(spinLabel, context.CompareAndSwap(writeLockPtr, Const(0), Const(1)));
|
||||||
|
|
||||||
// Increment reader count.
|
// Increment reader count.
|
||||||
EmitAtomicAddI32(context, Const((ulong)nint.Add(nativeReaderLockPtr, NativeReaderWriterLock.ReaderCountOffset)), Const(1));
|
EmitAtomicAddI32(context, Const((ulong)IntPtr.Add(nativeReaderLockPtr, NativeReaderWriterLock.ReaderCountOffset)), Const(1));
|
||||||
|
|
||||||
// Release write lock.
|
// Release write lock.
|
||||||
context.CompareAndSwap(writeLockPtr, Const(1), Const(0));
|
context.CompareAndSwap(writeLockPtr, Const(1), Const(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void EmitNativeReaderLockRelease(EmitterContext context, nint nativeReaderLockPtr)
|
private static void EmitNativeReaderLockRelease(EmitterContext context, IntPtr nativeReaderLockPtr)
|
||||||
{
|
{
|
||||||
// Decrement reader count.
|
// Decrement reader count.
|
||||||
EmitAtomicAddI32(context, Const((ulong)nint.Add(nativeReaderLockPtr, NativeReaderWriterLock.ReaderCountOffset)), Const(-1));
|
EmitAtomicAddI32(context, Const((ulong)IntPtr.Add(nativeReaderLockPtr, NativeReaderWriterLock.ReaderCountOffset)), Const(-1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using ARMeilleure.Memory;
|
using ARMeilleure.Memory;
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace ARMeilleure.State
|
namespace ARMeilleure.State
|
||||||
{
|
{
|
||||||
@@ -8,7 +9,7 @@ namespace ARMeilleure.State
|
|||||||
|
|
||||||
private readonly NativeContext _nativeContext;
|
private readonly NativeContext _nativeContext;
|
||||||
|
|
||||||
internal nint NativeContextPtr => _nativeContext.BasePtr;
|
internal IntPtr NativeContextPtr => _nativeContext.BasePtr;
|
||||||
|
|
||||||
private bool _interrupted;
|
private bool _interrupted;
|
||||||
|
|
||||||
@@ -127,11 +128,6 @@ namespace ARMeilleure.State
|
|||||||
public bool GetFPstateFlag(FPState flag) => _nativeContext.GetFPStateFlag(flag);
|
public bool GetFPstateFlag(FPState flag) => _nativeContext.GetFPStateFlag(flag);
|
||||||
public void SetFPstateFlag(FPState flag, bool value) => _nativeContext.SetFPStateFlag(flag, value);
|
public void SetFPstateFlag(FPState flag, bool value) => _nativeContext.SetFPStateFlag(flag, value);
|
||||||
|
|
||||||
internal void ResetCallDepth()
|
|
||||||
{
|
|
||||||
_nativeContext.ResetCallDepth();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void CheckInterrupt()
|
internal void CheckInterrupt()
|
||||||
{
|
{
|
||||||
if (_interrupted)
|
if (_interrupted)
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ namespace ARMeilleure.State
|
|||||||
public ulong ExclusiveValueLow;
|
public ulong ExclusiveValueLow;
|
||||||
public ulong ExclusiveValueHigh;
|
public ulong ExclusiveValueHigh;
|
||||||
public int Running;
|
public int Running;
|
||||||
public int CallDepth;
|
|
||||||
public long Tpidr2El0;
|
public long Tpidr2El0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,7 +28,7 @@ namespace ARMeilleure.State
|
|||||||
|
|
||||||
private readonly IJitMemoryBlock _block;
|
private readonly IJitMemoryBlock _block;
|
||||||
|
|
||||||
public nint BasePtr => _block.Pointer;
|
public IntPtr BasePtr => _block.Pointer;
|
||||||
|
|
||||||
public NativeContext(IJitMemoryAllocator allocator)
|
public NativeContext(IJitMemoryAllocator allocator)
|
||||||
{
|
{
|
||||||
@@ -187,8 +186,6 @@ namespace ARMeilleure.State
|
|||||||
public bool GetRunning() => GetStorage().Running != 0;
|
public bool GetRunning() => GetStorage().Running != 0;
|
||||||
public void SetRunning(bool value) => GetStorage().Running = value ? 1 : 0;
|
public void SetRunning(bool value) => GetStorage().Running = value ? 1 : 0;
|
||||||
|
|
||||||
public void ResetCallDepth() => GetStorage().CallDepth = 0;
|
|
||||||
|
|
||||||
public unsafe static int GetRegisterOffset(Register reg)
|
public unsafe static int GetRegisterOffset(Register reg)
|
||||||
{
|
{
|
||||||
if (reg.Type == RegisterType.Integer)
|
if (reg.Type == RegisterType.Integer)
|
||||||
@@ -269,11 +266,6 @@ namespace ARMeilleure.State
|
|||||||
return StorageOffset(ref _dummyStorage, ref _dummyStorage.Running);
|
return StorageOffset(ref _dummyStorage, ref _dummyStorage.Running);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int GetCallDepthOffset()
|
|
||||||
{
|
|
||||||
return StorageOffset(ref _dummyStorage, ref _dummyStorage.CallDepth);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int StorageOffset<T>(ref NativeCtxStorage storage, ref T target)
|
private static int StorageOffset<T>(ref NativeCtxStorage storage, ref T target)
|
||||||
{
|
{
|
||||||
return (int)Unsafe.ByteOffset(ref Unsafe.As<NativeCtxStorage, T>(ref storage), ref target);
|
return (int)Unsafe.ByteOffset(ref Unsafe.As<NativeCtxStorage, T>(ref storage), ref target);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using ARMeilleure.Instructions;
|
|||||||
using ARMeilleure.IntermediateRepresentation;
|
using ARMeilleure.IntermediateRepresentation;
|
||||||
using ARMeilleure.Memory;
|
using ARMeilleure.Memory;
|
||||||
using ARMeilleure.State;
|
using ARMeilleure.State;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||||
@@ -91,7 +92,7 @@ namespace ARMeilleure.Translation
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
int index = Delegates.GetDelegateIndex(info);
|
int index = Delegates.GetDelegateIndex(info);
|
||||||
nint funcPtr = Delegates.GetDelegateFuncPtrByIndex(index);
|
IntPtr funcPtr = Delegates.GetDelegateFuncPtrByIndex(index);
|
||||||
|
|
||||||
OperandType returnType = GetOperandType(info.ReturnType);
|
OperandType returnType = GetOperandType(info.ReturnType);
|
||||||
|
|
||||||
|
|||||||
@@ -2,12 +2,12 @@ using ARMeilleure.CodeGen;
|
|||||||
using ARMeilleure.CodeGen.Unwinding;
|
using ARMeilleure.CodeGen.Unwinding;
|
||||||
using ARMeilleure.Memory;
|
using ARMeilleure.Memory;
|
||||||
using ARMeilleure.Native;
|
using ARMeilleure.Native;
|
||||||
|
using Humanizer;
|
||||||
using Ryujinx.Common.Logging;
|
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.Diagnostics;
|
||||||
using System.Linq;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
@@ -19,123 +19,83 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
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;
|
private const int CodeAlignment = 4; // Bytes.
|
||||||
private const int FullCacheSize = 2047 * 1024 * 1024;
|
private const int CacheSize = 256 * 1024 * 1024;
|
||||||
private const int ReducedCacheSize = FullCacheSize / 8;
|
|
||||||
|
|
||||||
private const float EvictionTargetPercentage = 0.20f;
|
|
||||||
private const int MaxEntriesToEvictAtOnce = 100;
|
|
||||||
|
|
||||||
// Simple logging configuration
|
|
||||||
private const int LogInterval = 5000; // Log every 5000 allocations
|
|
||||||
|
|
||||||
private static ReservedRegion _jitRegion;
|
|
||||||
private static JitCacheInvalidation _jitCacheInvalidator;
|
private static JitCacheInvalidation _jitCacheInvalidator;
|
||||||
|
|
||||||
private static CacheMemoryAllocator _cacheAllocator;
|
private static List<CacheMemoryAllocator> _cacheAllocators = [];
|
||||||
|
|
||||||
private static readonly List<CacheEntry> _cacheEntries = [];
|
private static readonly List<CacheEntry> _cacheEntries = [];
|
||||||
private static readonly Dictionary<int, EntryUsageStats> _entryUsageStats = [];
|
|
||||||
|
|
||||||
private static readonly Lock _lock = new();
|
private static readonly Lock _lock = new();
|
||||||
private static bool _initialized;
|
private static bool _initialized;
|
||||||
private static int _cacheSize;
|
|
||||||
|
|
||||||
// Basic statistics
|
private static readonly List<ReservedRegion> _jitRegions = [];
|
||||||
private static int _totalAllocations = 0;
|
private static int _activeRegionIndex = 0;
|
||||||
private static int _totalEvictions = 0;
|
|
||||||
|
|
||||||
private class EntryUsageStats
|
|
||||||
{
|
|
||||||
public long LastAccessTime { get; private set; }
|
|
||||||
public int UsageCount { get; private set; }
|
|
||||||
|
|
||||||
public EntryUsageStats()
|
|
||||||
{
|
|
||||||
LastAccessTime = DateTime.UtcNow.Ticks;
|
|
||||||
UsageCount = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateUsage()
|
|
||||||
{
|
|
||||||
LastAccessTime = DateTime.UtcNow.Ticks;
|
|
||||||
UsageCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[SupportedOSPlatform("windows")]
|
[SupportedOSPlatform("windows")]
|
||||||
[LibraryImport("kernel32.dll", SetLastError = true)]
|
[LibraryImport("kernel32.dll", SetLastError = true)]
|
||||||
public static partial nint FlushInstructionCache(nint hProcess, nint lpAddress, nuint dwSize);
|
public static partial IntPtr FlushInstructionCache(IntPtr hProcess, IntPtr lpAddress, UIntPtr dwSize);
|
||||||
|
|
||||||
public static void Initialize(IJitMemoryAllocator allocator)
|
public static void Initialize(IJitMemoryAllocator allocator)
|
||||||
{
|
{
|
||||||
if (_initialized)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
if (_initialized)
|
if (_initialized)
|
||||||
{
|
{
|
||||||
return;
|
if (OperatingSystem.IsWindows())
|
||||||
|
{
|
||||||
|
JitUnwindWindows.RemoveFunctionTableHandler(
|
||||||
|
_jitRegions[0].Pointer);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < _jitRegions.Count; i++)
|
||||||
|
{
|
||||||
|
_jitRegions[i].Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
_jitRegions.Clear();
|
||||||
|
_cacheAllocators.Clear();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
_cacheSize = Optimizations.CacheEviction ? ReducedCacheSize : FullCacheSize;
|
_activeRegionIndex = 0;
|
||||||
_jitRegion = new ReservedRegion(allocator, (ulong)_cacheSize);
|
|
||||||
|
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())
|
||||||
{
|
{
|
||||||
_jitCacheInvalidator = new JitCacheInvalidation(allocator);
|
_jitCacheInvalidator = new JitCacheInvalidation(allocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
_cacheAllocator = new CacheMemoryAllocator(_cacheSize);
|
|
||||||
|
|
||||||
if (OperatingSystem.IsWindows())
|
if (OperatingSystem.IsWindows())
|
||||||
{
|
{
|
||||||
JitUnwindWindows.InstallFunctionTableHandler(_jitRegion.Pointer, (uint)_cacheSize, _jitRegion.Pointer + Allocate(_pageSize));
|
JitUnwindWindows.InstallFunctionTableHandler(
|
||||||
|
firstRegion.Pointer, CacheSize, firstRegion.Pointer + Allocate(_pageSize)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.Info?.Print(LogClass.Cpu, $"JIT Cache initialized: Size={_cacheSize / (1024 * 1024)} MB, Eviction={Optimizations.CacheEviction}");
|
|
||||||
_initialized = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static nint Map(CompiledFunction func)
|
public static IntPtr Map(CompiledFunction func)
|
||||||
{
|
{
|
||||||
byte[] code = func.Code;
|
byte[] code = func.Code;
|
||||||
|
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
Debug.Assert(_initialized);
|
Debug.Assert(_initialized);
|
||||||
_totalAllocations++;
|
|
||||||
|
|
||||||
int funcOffset;
|
int funcOffset = Allocate(code.Length);
|
||||||
|
ReservedRegion targetRegion = _jitRegions[_activeRegionIndex];
|
||||||
if (Optimizations.CacheEviction)
|
IntPtr funcPtr = targetRegion.Pointer + funcOffset;
|
||||||
{
|
|
||||||
int codeSize = AlignCodeSize(code.Length);
|
|
||||||
funcOffset = _cacheAllocator.Allocate(codeSize);
|
|
||||||
|
|
||||||
if (funcOffset < 0)
|
|
||||||
{
|
|
||||||
EvictEntries(codeSize);
|
|
||||||
funcOffset = _cacheAllocator.Allocate(codeSize);
|
|
||||||
|
|
||||||
if (funcOffset < 0)
|
|
||||||
{
|
|
||||||
throw new OutOfMemoryException("JIT Cache exhausted even after eviction.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_jitRegion.ExpandIfNeeded((ulong)funcOffset + (ulong)codeSize);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
funcOffset = Allocate(code.Length);
|
|
||||||
}
|
|
||||||
|
|
||||||
nint funcPtr = _jitRegion.Pointer + funcOffset;
|
|
||||||
|
|
||||||
if (OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
if (OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
||||||
{
|
{
|
||||||
@@ -143,19 +103,19 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
{
|
{
|
||||||
fixed (byte* codePtr = code)
|
fixed (byte* codePtr = code)
|
||||||
{
|
{
|
||||||
JitSupportDarwin.Copy(funcPtr, (nint)codePtr, (ulong)code.Length);
|
JitSupportDarwin.Copy(funcPtr, (IntPtr)codePtr, (ulong)code.Length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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)
|
||||||
{
|
{
|
||||||
FlushInstructionCache(Process.GetCurrentProcess().Handle, funcPtr, (nuint)code.Length);
|
FlushInstructionCache(Process.GetCurrentProcess().Handle, funcPtr, (UIntPtr)code.Length);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -165,71 +125,84 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
|
|
||||||
Add(funcOffset, code.Length, func.UnwindInfo);
|
Add(funcOffset, code.Length, func.UnwindInfo);
|
||||||
|
|
||||||
// Simple periodic logging
|
|
||||||
if (_totalAllocations % LogInterval == 0)
|
|
||||||
{
|
|
||||||
LogCacheStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
return funcPtr;
|
return funcPtr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Unmap(nint pointer)
|
public static void Unmap(IntPtr pointer)
|
||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
Debug.Assert(_initialized);
|
Debug.Assert(_initialized);
|
||||||
|
|
||||||
int funcOffset = (int)(pointer.ToInt64() - _jitRegion.Pointer.ToInt64());
|
foreach (var region in _jitRegions)
|
||||||
|
|
||||||
if (TryFind(funcOffset, out CacheEntry entry, out int entryIndex) && entry.Offset == funcOffset)
|
|
||||||
{
|
{
|
||||||
_cacheAllocator.Free(funcOffset, AlignCodeSize(entry.Size));
|
if (pointer.ToInt64() < region.Pointer.ToInt64() ||
|
||||||
_cacheEntries.RemoveAt(entryIndex);
|
pointer.ToInt64() >= (region.Pointer + CacheSize).ToInt64())
|
||||||
|
|
||||||
if (Optimizations.CacheEviction)
|
|
||||||
{
|
{
|
||||||
_entryUsageStats.Remove(funcOffset);
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int funcOffset = (int)(pointer.ToInt64() - region.Pointer.ToInt64());
|
||||||
|
|
||||||
|
if (TryFind(funcOffset, out CacheEntry entry, out int entryIndex) && entry.Offset == funcOffset)
|
||||||
|
{
|
||||||
|
_cacheAllocators[_activeRegionIndex].Free(funcOffset, AlignCodeSize(entry.Size));
|
||||||
|
_cacheEntries.RemoveAt(entryIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static 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 & ~_pageMask;
|
int regionStart = offset & ~_pageMask;
|
||||||
int regionEnd = (endOffs + _pageMask) & ~_pageMask;
|
int regionEnd = (endOffs + _pageMask) & ~_pageMask;
|
||||||
|
|
||||||
_jitRegion.Block.MapAsRwx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
region.Block.MapAsRwx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static 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 & ~_pageMask;
|
int regionStart = offset & ~_pageMask;
|
||||||
int regionEnd = (endOffs + _pageMask) & ~_pageMask;
|
int regionEnd = (endOffs + _pageMask) & ~_pageMask;
|
||||||
|
|
||||||
_jitRegion.Block.MapAsRx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
region.Block.MapAsRx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static 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)
|
||||||
{
|
{
|
||||||
throw new OutOfMemoryException("JIT Cache exhausted.");
|
_jitRegions[_activeRegionIndex].ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize);
|
||||||
|
return allocOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
_jitRegion.ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize);
|
int exhaustedRegion = _activeRegionIndex;
|
||||||
|
var newRegion = new ReservedRegion(_jitRegions[0].Allocator, CacheSize);
|
||||||
|
_jitRegions.Add(newRegion);
|
||||||
|
_activeRegionIndex = _jitRegions.Count - 1;
|
||||||
|
|
||||||
return allocOffset;
|
Logger.Warning?.Print(LogClass.Cpu, $"JIT Cache Region {exhaustedRegion} exhausted, creating new Cache Region {_activeRegionIndex} ({((long)(_activeRegionIndex + 1) * CacheSize).Bytes()} Total Allocation).");
|
||||||
|
|
||||||
|
_cacheAllocators.Add(new CacheMemoryAllocator(CacheSize));
|
||||||
|
|
||||||
|
int allocOffsetNew = _cacheAllocators[_activeRegionIndex].Allocate(codeSize);
|
||||||
|
if (allocOffsetNew < 0)
|
||||||
|
{
|
||||||
|
throw new OutOfMemoryException("Failed to allocate in new Cache Region!");
|
||||||
|
}
|
||||||
|
|
||||||
|
newRegion.ExpandIfNeeded((ulong)allocOffsetNew + (ulong)codeSize);
|
||||||
|
return allocOffsetNew;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int AlignCodeSize(int codeSize)
|
private static int AlignCodeSize(int codeSize)
|
||||||
@@ -249,35 +222,27 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
}
|
}
|
||||||
|
|
||||||
_cacheEntries.Insert(index, entry);
|
_cacheEntries.Insert(index, entry);
|
||||||
|
|
||||||
if (Optimizations.CacheEviction)
|
|
||||||
{
|
|
||||||
_entryUsageStats[offset] = new EntryUsageStats();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static 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];
|
|
||||||
|
|
||||||
if (Optimizations.CacheEviction && _entryUsageStats.TryGetValue(offset, out EntryUsageStats stats))
|
|
||||||
{
|
{
|
||||||
stats.UpdateUsage();
|
index = ~index - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
entryIndex = index;
|
if (index >= 0)
|
||||||
return true;
|
{
|
||||||
|
entry = _cacheEntries[index];
|
||||||
|
entryIndex = index;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -285,83 +250,5 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
entryIndex = 0;
|
entryIndex = 0;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void EvictEntries(int requiredSize)
|
|
||||||
{
|
|
||||||
if (!Optimizations.CacheEviction)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (_lock)
|
|
||||||
{
|
|
||||||
int targetSpace = Math.Max(requiredSize, (int)(_cacheSize * EvictionTargetPercentage));
|
|
||||||
int freedSpace = 0;
|
|
||||||
int evictedCount = 0;
|
|
||||||
|
|
||||||
var entriesWithStats = _cacheEntries
|
|
||||||
.Where(e => _entryUsageStats.ContainsKey(e.Offset))
|
|
||||||
.Select(e => new {
|
|
||||||
Entry = e,
|
|
||||||
Stats = _entryUsageStats[e.Offset],
|
|
||||||
Score = CalculateEvictionScore(_entryUsageStats[e.Offset])
|
|
||||||
})
|
|
||||||
.OrderBy(x => x.Score)
|
|
||||||
.Take(MaxEntriesToEvictAtOnce)
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
foreach (var item in entriesWithStats)
|
|
||||||
{
|
|
||||||
int entrySize = AlignCodeSize(item.Entry.Size);
|
|
||||||
|
|
||||||
int entryIndex = _cacheEntries.BinarySearch(item.Entry);
|
|
||||||
if (entryIndex >= 0)
|
|
||||||
{
|
|
||||||
_cacheAllocator.Free(item.Entry.Offset, entrySize);
|
|
||||||
_cacheEntries.RemoveAt(entryIndex);
|
|
||||||
_entryUsageStats.Remove(item.Entry.Offset);
|
|
||||||
|
|
||||||
freedSpace += entrySize;
|
|
||||||
evictedCount++;
|
|
||||||
|
|
||||||
if (freedSpace >= targetSpace)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_totalEvictions += evictedCount;
|
|
||||||
|
|
||||||
Logger.Info?.Print(LogClass.Cpu, $"JIT Cache: Evicted {evictedCount} entries, freed {freedSpace / (1024 * 1024.0):F2} MB");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static double CalculateEvictionScore(EntryUsageStats stats)
|
|
||||||
{
|
|
||||||
long currentTime = DateTime.UtcNow.Ticks;
|
|
||||||
long ageInTicks = currentTime - stats.LastAccessTime;
|
|
||||||
|
|
||||||
double ageInSeconds = ageInTicks / 10_000_000.0;
|
|
||||||
|
|
||||||
const double usageWeight = 1.0;
|
|
||||||
const double ageWeight = 2.0;
|
|
||||||
|
|
||||||
double usageScore = Math.Log10(stats.UsageCount + 1) * usageWeight;
|
|
||||||
double ageScore = (10.0 / (ageInSeconds + 1.0)) * ageWeight;
|
|
||||||
|
|
||||||
return usageScore + ageScore;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void LogCacheStatus()
|
|
||||||
{
|
|
||||||
int estimatedUsedSize = _cacheEntries.Sum(e => AlignCodeSize(e.Size));
|
|
||||||
double usagePercentage = 100.0 * estimatedUsedSize / _cacheSize;
|
|
||||||
|
|
||||||
Logger.Info?.Print(LogClass.Cpu,
|
|
||||||
$"JIT Cache status: entries={_cacheEntries.Count}, " +
|
|
||||||
$"est. used={estimatedUsedSize / (1024 * 1024.0):F2} MB ({usagePercentage:F1}%), " +
|
|
||||||
$"evictions={_totalEvictions}, allocations={_totalAllocations}");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using ARMeilleure.Memory;
|
using ARMeilleure.Memory;
|
||||||
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace ARMeilleure.Translation.Cache
|
namespace ARMeilleure.Translation.Cache
|
||||||
@@ -67,7 +68,7 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Invalidate(nint basePointer, ulong size)
|
public void Invalidate(IntPtr basePointer, ulong size)
|
||||||
{
|
{
|
||||||
if (_needsInvalidation)
|
if (_needsInvalidation)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
PushMachframe = 10,
|
PushMachframe = 10,
|
||||||
}
|
}
|
||||||
|
|
||||||
private unsafe delegate RuntimeFunction* GetRuntimeFunctionCallback(ulong controlPc, nint context);
|
private unsafe delegate RuntimeFunction* GetRuntimeFunctionCallback(ulong controlPc, IntPtr context);
|
||||||
|
|
||||||
[LibraryImport("kernel32.dll")]
|
[LibraryImport("kernel32.dll")]
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
@@ -49,7 +49,7 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
ulong baseAddress,
|
ulong baseAddress,
|
||||||
uint length,
|
uint length,
|
||||||
GetRuntimeFunctionCallback callback,
|
GetRuntimeFunctionCallback callback,
|
||||||
nint context,
|
IntPtr context,
|
||||||
[MarshalAs(UnmanagedType.LPWStr)] string outOfProcessCallbackDll);
|
[MarshalAs(UnmanagedType.LPWStr)] string outOfProcessCallbackDll);
|
||||||
|
|
||||||
[LibraryImport("kernel32.dll")]
|
[LibraryImport("kernel32.dll")]
|
||||||
@@ -65,7 +65,7 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
|
|
||||||
private unsafe static UnwindInfo* _unwindInfo;
|
private unsafe static UnwindInfo* _unwindInfo;
|
||||||
|
|
||||||
public static void InstallFunctionTableHandler(nint codeCachePointer, uint codeCacheLength, nint workBufferPtr)
|
public static void InstallFunctionTableHandler(IntPtr codeCachePointer, uint codeCacheLength, IntPtr workBufferPtr)
|
||||||
{
|
{
|
||||||
ulong codeCachePtr = (ulong)codeCachePointer.ToInt64();
|
ulong codeCachePtr = (ulong)codeCachePointer.ToInt64();
|
||||||
|
|
||||||
@@ -96,7 +96,7 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void RemoveFunctionTableHandler(nint codeCachePointer)
|
public static void RemoveFunctionTableHandler(IntPtr codeCachePointer)
|
||||||
{
|
{
|
||||||
ulong codeCachePtr = (ulong)codeCachePointer.ToInt64();
|
ulong codeCachePtr = (ulong)codeCachePointer.ToInt64();
|
||||||
|
|
||||||
@@ -113,7 +113,7 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static unsafe RuntimeFunction* FunctionTableHandler(ulong controlPc, nint context)
|
private static unsafe RuntimeFunction* FunctionTableHandler(ulong controlPc, IntPtr context)
|
||||||
{
|
{
|
||||||
int offset = (int)((long)controlPc - context.ToInt64());
|
int offset = (int)((long)controlPc - context.ToInt64());
|
||||||
|
|
||||||
@@ -122,13 +122,13 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
return null; // Not found.
|
return null; // Not found.
|
||||||
}
|
}
|
||||||
|
|
||||||
CodeGen.Unwinding.UnwindInfo unwindInfo = funcEntry.UnwindInfo;
|
var unwindInfo = funcEntry.UnwindInfo;
|
||||||
|
|
||||||
int codeIndex = 0;
|
int codeIndex = 0;
|
||||||
|
|
||||||
for (int index = unwindInfo.PushEntries.Length - 1; index >= 0; index--)
|
for (int index = unwindInfo.PushEntries.Length - 1; index >= 0; index--)
|
||||||
{
|
{
|
||||||
UnwindPushEntry entry = unwindInfo.PushEntries[index];
|
var entry = unwindInfo.PushEntries[index];
|
||||||
|
|
||||||
switch (entry.PseudoOp)
|
switch (entry.PseudoOp)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
namespace ARMeilleure.Translation
|
namespace ARMeilleure.Translation
|
||||||
{
|
{
|
||||||
public class DelegateInfo
|
public class DelegateInfo
|
||||||
{
|
{
|
||||||
public nint FuncPtr { get; }
|
public IntPtr FuncPtr { get; }
|
||||||
|
|
||||||
public DelegateInfo(nint funcPtr)
|
public DelegateInfo(IntPtr funcPtr)
|
||||||
{
|
{
|
||||||
FuncPtr = funcPtr;
|
FuncPtr = funcPtr;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -354,7 +354,7 @@ namespace ARMeilleure.Translation
|
|||||||
|
|
||||||
private static readonly Dictionary<string, Delegate> _androidDelegates;
|
private static readonly Dictionary<string, Delegate> _androidDelegates;
|
||||||
#endif
|
#endif
|
||||||
public static bool TryGetDelegateFuncPtrByIndex(int index, out nint funcPtr)
|
public static bool TryGetDelegateFuncPtrByIndex(int index, out IntPtr funcPtr)
|
||||||
{
|
{
|
||||||
if (index >= 0 && index < _delegates.Count)
|
if (index >= 0 && index < _delegates.Count)
|
||||||
{
|
{
|
||||||
@@ -370,7 +370,7 @@ namespace ARMeilleure.Translation
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static nint GetDelegateFuncPtrByIndex(int index)
|
public static IntPtr GetDelegateFuncPtrByIndex(int index)
|
||||||
{
|
{
|
||||||
if (index < 0 || index >= _delegates.Count)
|
if (index < 0 || index >= _delegates.Count)
|
||||||
{
|
{
|
||||||
@@ -403,7 +403,7 @@ namespace ARMeilleure.Translation
|
|||||||
#if ANDROID
|
#if ANDROID
|
||||||
if (_androidDelegates.TryGetValue(key, out Delegate del))
|
if (_androidDelegates.TryGetValue(key, out Delegate del))
|
||||||
{
|
{
|
||||||
nint funcPtr = Marshal.GetFunctionPointerForDelegate(del);
|
IntPtr funcPtr = Marshal.GetFunctionPointerForDelegate(del);
|
||||||
_delegates.Add(key, new DelegateInfo(funcPtr));
|
_delegates.Add(key, new DelegateInfo(funcPtr));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
namespace ARMeilleure.Translation
|
namespace ARMeilleure.Translation
|
||||||
{
|
{
|
||||||
delegate void DispatcherFunction(nint nativeContext, ulong startAddress);
|
delegate void DispatcherFunction(IntPtr nativeContext, ulong startAddress);
|
||||||
delegate ulong WrapperFunction(nint nativeContext, ulong startAddress);
|
delegate ulong WrapperFunction(IntPtr nativeContext, ulong startAddress);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,10 +100,10 @@ namespace ARMeilleure.Translation
|
|||||||
#if ANDROID
|
#if ANDROID
|
||||||
// For Android, use the Delegates class to get the function pointer
|
// For Android, use the Delegates class to get the function pointer
|
||||||
int index = Delegates.GetDelegateIndex(info);
|
int index = Delegates.GetDelegateIndex(info);
|
||||||
nint funcPtr = Delegates.GetDelegateFuncPtrByIndex(index);
|
IntPtr funcPtr = Delegates.GetDelegateFuncPtrByIndex(index);
|
||||||
#else
|
#else
|
||||||
// For other platforms, use direct method handle approach
|
// For other platforms, use direct method handle approach
|
||||||
nint funcPtr = info.MethodHandle.GetFunctionPointer();
|
IntPtr funcPtr = info.MethodHandle.GetFunctionPointer();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
OperandType returnType = GetOperandType(info.ReturnType);
|
OperandType returnType = GetOperandType(info.ReturnType);
|
||||||
@@ -675,7 +675,8 @@ namespace ARMeilleure.Translation
|
|||||||
Operation last = block.Operations.Last;
|
Operation last = block.Operations.Last;
|
||||||
|
|
||||||
return last != default &&
|
return last != default &&
|
||||||
last.Instruction is Instruction.Return or Instruction.Tailcall;
|
(last.Instruction == Instruction.Return ||
|
||||||
|
last.Instruction == Instruction.Tailcall);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ControlFlowGraph GetControlFlowGraph()
|
public ControlFlowGraph GetControlFlowGraph()
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
namespace ARMeilleure.Translation
|
namespace ARMeilleure.Translation
|
||||||
{
|
{
|
||||||
delegate ulong GuestFunction(nint nativeContextPtr);
|
delegate ulong GuestFunction(IntPtr nativeContextPtr);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -316,7 +316,7 @@ namespace ARMeilleure.Translation
|
|||||||
{
|
{
|
||||||
_root = newNode;
|
_root = newNode;
|
||||||
}
|
}
|
||||||
else if (start.CompareTo(parent!.Start) < 0)
|
else if (start.CompareTo(parent.Start) < 0)
|
||||||
{
|
{
|
||||||
parent.Left = newNode;
|
parent.Left = newNode;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ 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;
|
||||||
@@ -31,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 = 7008; //! 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);
|
||||||
@@ -61,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;
|
||||||
|
|
||||||
@@ -88,7 +91,8 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
|
|
||||||
_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;
|
||||||
@@ -96,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 (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;
|
||||||
|
|
||||||
@@ -122,10 +122,14 @@ 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);
|
Logger.Info?.Print(LogClass.Ptc, $"PPTC (v{InternalVersion}) Profile: {DisplayVersion}-{cacheSelector}");
|
||||||
string workPathBackup = Path.Combine(AppDataManager.GamesDirPath, CacheInfo.TitleIdText, "cache", "cpu", BackupDir);
|
|
||||||
|
string workPathActual = Path.Combine(AppDataManager.GamesDirPath, TitleIdText, "cache", "cpu", ActualDir);
|
||||||
|
string workPathBackup = Path.Combine(AppDataManager.GamesDirPath, TitleIdText, "cache", "cpu", BackupDir);
|
||||||
|
|
||||||
if (!Directory.Exists(workPathActual))
|
if (!Directory.Exists(workPathActual))
|
||||||
{
|
{
|
||||||
@@ -137,14 +141,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();
|
||||||
@@ -192,7 +190,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
_infosStream.Seek(0L, SeekOrigin.Begin);
|
_infosStream.Seek(0L, SeekOrigin.Begin);
|
||||||
bool foundBadFunction = false;
|
bool foundBadFunction = false;
|
||||||
|
|
||||||
for (int index = 0; index < _infosStream.Length / Unsafe.SizeOf<InfoEntry>(); index++)
|
for (int index = 0; index < GetEntriesCount(); index++)
|
||||||
{
|
{
|
||||||
InfoEntry infoEntry = DeserializeStructure<InfoEntry>(_infosStream);
|
InfoEntry infoEntry = DeserializeStructure<InfoEntry>(_infosStream);
|
||||||
foreach (ulong address in blacklist)
|
foreach (ulong address in blacklist)
|
||||||
@@ -246,25 +244,67 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
{
|
{
|
||||||
OuterHeader outerHeader = DeserializeStructure<OuterHeader>(compressedStream);
|
OuterHeader outerHeader = DeserializeStructure<OuterHeader>(compressedStream);
|
||||||
|
|
||||||
if (!outerHeader.IsHeaderValid() ||
|
if (!outerHeader.IsHeaderValid())
|
||||||
outerHeader.Magic != _outerHeaderMagic ||
|
|
||||||
outerHeader.CacheFileVersion != InternalVersion ||
|
|
||||||
outerHeader.Endianness != GetEndianness() ||
|
|
||||||
outerHeader.FeatureInfo != GetFeatureInfo() ||
|
|
||||||
outerHeader.MemoryManagerMode != GetMemoryManagerMode() ||
|
|
||||||
outerHeader.OSPlatform != GetOSPlatform() ||
|
|
||||||
outerHeader.Architecture != (uint)RuntimeInformation.ProcessArchitecture)
|
|
||||||
{
|
{
|
||||||
InvalidateCompressedStream(compressedStream);
|
InvalidateCompressedStream(compressedStream);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
nint intPtr = nint.Zero;
|
if (outerHeader.Magic != _outerHeaderMagic)
|
||||||
|
{
|
||||||
|
InvalidateCompressedStream(compressedStream);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outerHeader.CacheFileVersion != InternalVersion)
|
||||||
|
{
|
||||||
|
InvalidateCompressedStream(compressedStream);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outerHeader.Endianness != GetEndianness())
|
||||||
|
{
|
||||||
|
InvalidateCompressedStream(compressedStream);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outerHeader.FeatureInfo != GetFeatureInfo())
|
||||||
|
{
|
||||||
|
InvalidateCompressedStream(compressedStream);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outerHeader.MemoryManagerMode != GetMemoryManagerMode())
|
||||||
|
{
|
||||||
|
InvalidateCompressedStream(compressedStream);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outerHeader.OSPlatform != GetOSPlatform())
|
||||||
|
{
|
||||||
|
InvalidateCompressedStream(compressedStream);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outerHeader.Architecture != (uint)RuntimeInformation.ProcessArchitecture)
|
||||||
|
{
|
||||||
|
InvalidateCompressedStream(compressedStream);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
IntPtr intPtr = IntPtr.Zero;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
intPtr = Marshal.AllocHGlobal(new nint(outerHeader.UncompressedStreamSize));
|
intPtr = Marshal.AllocHGlobal(new IntPtr(outerHeader.UncompressedStreamSize));
|
||||||
|
|
||||||
using UnmanagedMemoryStream stream = new((byte*)intPtr.ToPointer(), outerHeader.UncompressedStreamSize, outerHeader.UncompressedStreamSize, FileAccess.ReadWrite);
|
using UnmanagedMemoryStream stream = new((byte*)intPtr.ToPointer(), outerHeader.UncompressedStreamSize, outerHeader.UncompressedStreamSize, FileAccess.ReadWrite);
|
||||||
try
|
try
|
||||||
@@ -284,7 +324,14 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
|
|
||||||
InnerHeader innerHeader = DeserializeStructure<InnerHeader>(stream);
|
InnerHeader innerHeader = DeserializeStructure<InnerHeader>(stream);
|
||||||
|
|
||||||
if (!innerHeader.IsHeaderValid() || innerHeader.Magic != _innerHeaderMagic)
|
if (!innerHeader.IsHeaderValid())
|
||||||
|
{
|
||||||
|
InvalidateCompressedStream(compressedStream);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (innerHeader.Magic != _innerHeaderMagic)
|
||||||
{
|
{
|
||||||
InvalidateCompressedStream(compressedStream);
|
InvalidateCompressedStream(compressedStream);
|
||||||
|
|
||||||
@@ -358,7 +405,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
if (intPtr != nint.Zero)
|
if (intPtr != IntPtr.Zero)
|
||||||
{
|
{
|
||||||
Marshal.FreeHGlobal(intPtr);
|
Marshal.FreeHGlobal(intPtr);
|
||||||
}
|
}
|
||||||
@@ -367,12 +414,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;
|
||||||
}
|
}
|
||||||
@@ -445,11 +487,11 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
|
|
||||||
outerHeader.SetHeaderHash();
|
outerHeader.SetHeaderHash();
|
||||||
|
|
||||||
nint intPtr = nint.Zero;
|
IntPtr intPtr = IntPtr.Zero;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
intPtr = Marshal.AllocHGlobal(new nint(outerHeader.UncompressedStreamSize));
|
intPtr = Marshal.AllocHGlobal(new IntPtr(outerHeader.UncompressedStreamSize));
|
||||||
|
|
||||||
using UnmanagedMemoryStream stream = new((byte*)intPtr.ToPointer(), outerHeader.UncompressedStreamSize, outerHeader.UncompressedStreamSize, FileAccess.ReadWrite);
|
using UnmanagedMemoryStream stream = new((byte*)intPtr.ToPointer(), outerHeader.UncompressedStreamSize, outerHeader.UncompressedStreamSize, FileAccess.ReadWrite);
|
||||||
stream.Seek((long)Unsafe.SizeOf<InnerHeader>(), SeekOrigin.Begin);
|
stream.Seek((long)Unsafe.SizeOf<InnerHeader>(), SeekOrigin.Begin);
|
||||||
@@ -503,7 +545,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
if (intPtr != nint.Zero)
|
if (intPtr != IntPtr.Zero)
|
||||||
{
|
{
|
||||||
Marshal.FreeHGlobal(intPtr);
|
Marshal.FreeHGlobal(intPtr);
|
||||||
}
|
}
|
||||||
@@ -551,7 +593,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;
|
||||||
@@ -655,7 +697,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
|
|
||||||
foreach (RelocEntry relocEntry in relocEntries)
|
foreach (RelocEntry relocEntry in relocEntries)
|
||||||
{
|
{
|
||||||
nint? imm = null;
|
IntPtr? imm = null;
|
||||||
Symbol symbol = relocEntry.Symbol;
|
Symbol symbol = relocEntry.Symbol;
|
||||||
|
|
||||||
if (symbol.Type == SymbolType.FunctionTable)
|
if (symbol.Type == SymbolType.FunctionTable)
|
||||||
@@ -666,7 +708,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
{
|
{
|
||||||
unsafe
|
unsafe
|
||||||
{
|
{
|
||||||
imm = (nint)Unsafe.AsPointer(ref translator.FunctionTable.GetValue(guestAddress));
|
imm = (IntPtr)Unsafe.AsPointer(ref translator.FunctionTable.GetValue(guestAddress));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -674,7 +716,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
{
|
{
|
||||||
int index = (int)symbol.Value;
|
int index = (int)symbol.Value;
|
||||||
|
|
||||||
if (Delegates.TryGetDelegateFuncPtrByIndex(index, out nint funcPtr))
|
if (Delegates.TryGetDelegateFuncPtrByIndex(index, out IntPtr funcPtr))
|
||||||
{
|
{
|
||||||
imm = funcPtr;
|
imm = funcPtr;
|
||||||
}
|
}
|
||||||
@@ -689,7 +731,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
|
|
||||||
unsafe
|
unsafe
|
||||||
{
|
{
|
||||||
imm = (nint)Unsafe.AsPointer(ref callCounter.Value);
|
imm = (IntPtr)Unsafe.AsPointer(ref callCounter.Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (symbol == DispatchStubSymbol)
|
else if (symbol == DispatchStubSymbol)
|
||||||
@@ -738,8 +780,8 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
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>(out nint gFuncPointer);
|
var gFunc = cFunc.MapWithPointer<GuestFunction>(out IntPtr gFuncPointer);
|
||||||
|
|
||||||
return new TranslatedFunction(gFunc, gFuncPointer, callCounter, guestSize, highCq);
|
return new TranslatedFunction(gFunc, gFuncPointer, callCounter, guestSize, highCq);
|
||||||
}
|
}
|
||||||
@@ -776,7 +818,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;
|
||||||
@@ -820,7 +862,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;
|
||||||
@@ -865,11 +907,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();
|
||||||
}
|
}
|
||||||
@@ -949,7 +991,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)
|
||||||
{
|
{
|
||||||
@@ -959,7 +1001,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);
|
||||||
|
|
||||||
@@ -1124,7 +1166,8 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
|
|
||||||
public void Close()
|
public void Close()
|
||||||
{
|
{
|
||||||
if (State is PtcState.Enabled or PtcState.Continuing)
|
if (State == PtcState.Enabled ||
|
||||||
|
State == PtcState.Continuing)
|
||||||
{
|
{
|
||||||
State = PtcState.Closing;
|
State = PtcState.Closing;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -9,13 +9,13 @@ using System.Collections.Generic;
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.IO.Compression;
|
using System.IO.Compression;
|
||||||
|
using System.Linq;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Timers;
|
using System.Timers;
|
||||||
using static ARMeilleure.Translation.PTC.PtcFormatter;
|
using static ARMeilleure.Translation.PTC.PtcFormatter;
|
||||||
using Timer = System.Timers.Timer;
|
using Timer = System.Timers.Timer;
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace ARMeilleure.Translation.PTC
|
namespace ARMeilleure.Translation.PTC
|
||||||
{
|
{
|
||||||
@@ -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)
|
||||||
{
|
{
|
||||||
@@ -189,10 +189,28 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
{
|
{
|
||||||
OuterHeader outerHeader = DeserializeStructure<OuterHeader>(compressedStream);
|
OuterHeader outerHeader = DeserializeStructure<OuterHeader>(compressedStream);
|
||||||
|
|
||||||
if (!outerHeader.IsHeaderValid() ||
|
if (!outerHeader.IsHeaderValid())
|
||||||
outerHeader.Magic != _outerHeaderMagic ||
|
{
|
||||||
outerHeader.InfoFileVersion != InternalVersion && !_migrateInternalVersions.Contains(outerHeader.InfoFileVersion) ||
|
InvalidateCompressedStream(compressedStream);
|
||||||
outerHeader.Endianness != Ptc.GetEndianness())
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outerHeader.Magic != _outerHeaderMagic)
|
||||||
|
{
|
||||||
|
InvalidateCompressedStream(compressedStream);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outerHeader.InfoFileVersion != InternalVersion && !_migrateInternalVersions.Contains(outerHeader.InfoFileVersion))
|
||||||
|
{
|
||||||
|
InvalidateCompressedStream(compressedStream);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outerHeader.Endianness != Ptc.GetEndianness())
|
||||||
{
|
{
|
||||||
InvalidateCompressedStream(compressedStream);
|
InvalidateCompressedStream(compressedStream);
|
||||||
|
|
||||||
@@ -254,12 +272,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 +393,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}).");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -456,7 +465,8 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
|
|
||||||
public void Start()
|
public void Start()
|
||||||
{
|
{
|
||||||
if (_ptc.State is PtcState.Enabled or PtcState.Continuing)
|
if (_ptc.State == PtcState.Enabled ||
|
||||||
|
_ptc.State == PtcState.Continuing)
|
||||||
{
|
{
|
||||||
Enabled = true;
|
Enabled = true;
|
||||||
|
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ namespace ARMeilleure.Translation
|
|||||||
// This is required because we have a implicit context load at the start of the function,
|
// This is required because we have a implicit context load at the start of the function,
|
||||||
// but if there is a jump to the start of the function, the context load would trash the modified values.
|
// but if there is a jump to the start of the function, the context load would trash the modified values.
|
||||||
// Here we insert a new entry block that will jump to the existing entry block.
|
// Here we insert a new entry block that will jump to the existing entry block.
|
||||||
BasicBlock newEntry = new(cfg.Blocks.Count);
|
BasicBlock newEntry = new BasicBlock(cfg.Blocks.Count);
|
||||||
|
|
||||||
cfg.UpdateEntry(newEntry);
|
cfg.UpdateEntry(newEntry);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using ARMeilleure.Common;
|
using ARMeilleure.Common;
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace ARMeilleure.Translation
|
namespace ARMeilleure.Translation
|
||||||
{
|
{
|
||||||
@@ -6,12 +7,12 @@ namespace ARMeilleure.Translation
|
|||||||
{
|
{
|
||||||
private readonly GuestFunction _func; // Ensure that this delegate will not be garbage collected.
|
private readonly GuestFunction _func; // Ensure that this delegate will not be garbage collected.
|
||||||
|
|
||||||
public nint FuncPointer { get; }
|
public IntPtr FuncPointer { get; }
|
||||||
public Counter<uint> CallCounter { get; }
|
public Counter<uint> CallCounter { get; }
|
||||||
public ulong GuestSize { get; }
|
public ulong GuestSize { get; }
|
||||||
public bool HighCq { get; }
|
public bool HighCq { get; }
|
||||||
|
|
||||||
public TranslatedFunction(GuestFunction func, nint funcPointer, Counter<uint> callCounter, ulong guestSize, bool highCq)
|
public TranslatedFunction(GuestFunction func, IntPtr funcPointer, Counter<uint> callCounter, ulong guestSize, bool highCq)
|
||||||
{
|
{
|
||||||
_func = func;
|
_func = func;
|
||||||
FuncPointer = funcPointer;
|
FuncPointer = funcPointer;
|
||||||
|
|||||||
@@ -57,9 +57,9 @@ namespace ARMeilleure.Translation
|
|||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,7 +168,6 @@ namespace ARMeilleure.Translation
|
|||||||
|
|
||||||
Statistics.StartTimer();
|
Statistics.StartTimer();
|
||||||
|
|
||||||
context.ResetCallDepth();
|
|
||||||
ulong nextAddr = func.Execute(Stubs.ContextWrapper, context);
|
ulong nextAddr = func.Execute(Stubs.ContextWrapper, context);
|
||||||
|
|
||||||
Statistics.StopTimer(address);
|
Statistics.StopTimer(address);
|
||||||
@@ -222,7 +221,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,
|
||||||
@@ -240,7 +239,6 @@ namespace ARMeilleure.Translation
|
|||||||
|
|
||||||
Logger.StartPass(PassName.Translation);
|
Logger.StartPass(PassName.Translation);
|
||||||
|
|
||||||
InstEmitFlowHelper.EmitCallDepthCheckAndIncrement(context, Const(address));
|
|
||||||
EmitSynchronization(context);
|
EmitSynchronization(context);
|
||||||
|
|
||||||
if (blocks[0].Address != address)
|
if (blocks[0].Address != address)
|
||||||
@@ -265,10 +263,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)
|
||||||
{
|
{
|
||||||
@@ -284,7 +282,7 @@ namespace ARMeilleure.Translation
|
|||||||
_ptc.WriteCompiledFunction(address, funcSize, hash, highCq, compiledFunc);
|
_ptc.WriteCompiledFunction(address, funcSize, hash, highCq, compiledFunc);
|
||||||
}
|
}
|
||||||
|
|
||||||
GuestFunction func = compiledFunc.MapWithPointer<GuestFunction>(out nint funcPointer);
|
GuestFunction func = compiledFunc.MapWithPointer<GuestFunction>(out IntPtr funcPointer);
|
||||||
|
|
||||||
Allocators.ResetAll();
|
Allocators.ResetAll();
|
||||||
|
|
||||||
@@ -536,7 +534,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);
|
||||||
|
|
||||||
@@ -545,7 +543,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);
|
||||||
|
|
||||||
@@ -566,7 +564,7 @@ namespace ARMeilleure.Translation
|
|||||||
{
|
{
|
||||||
while (Queue.Count > 0 && Queue.TryDequeue(out RejitRequest request))
|
while (Queue.Count > 0 && Queue.TryDequeue(out RejitRequest request))
|
||||||
{
|
{
|
||||||
if (Functions.TryGetValue(request.Address, out TranslatedFunction func) && func.CallCounter != null)
|
if (Functions.TryGetValue(request.Address, out var func) && func.CallCounter != null)
|
||||||
{
|
{
|
||||||
Volatile.Write(ref func.CallCounter.Value, 0);
|
Volatile.Write(ref func.CallCounter.Value, 0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ namespace ARMeilleure.Translation
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
class TranslatorStubs : IDisposable
|
class TranslatorStubs : IDisposable
|
||||||
{
|
{
|
||||||
private readonly Lazy<nint> _slowDispatchStub;
|
private readonly Lazy<IntPtr> _slowDispatchStub;
|
||||||
|
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
|
|
||||||
@@ -27,7 +27,7 @@ namespace ARMeilleure.Translation
|
|||||||
/// Gets the dispatch stub.
|
/// Gets the dispatch stub.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <exception cref="ObjectDisposedException"><see cref="TranslatorStubs"/> instance was disposed</exception>
|
/// <exception cref="ObjectDisposedException"><see cref="TranslatorStubs"/> instance was disposed</exception>
|
||||||
public nint DispatchStub
|
public IntPtr DispatchStub
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
@@ -41,7 +41,7 @@ namespace ARMeilleure.Translation
|
|||||||
/// Gets the slow dispatch stub.
|
/// Gets the slow dispatch stub.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <exception cref="ObjectDisposedException"><see cref="TranslatorStubs"/> instance was disposed</exception>
|
/// <exception cref="ObjectDisposedException"><see cref="TranslatorStubs"/> instance was disposed</exception>
|
||||||
public nint SlowDispatchStub
|
public IntPtr SlowDispatchStub
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
@@ -139,9 +139,9 @@ namespace ARMeilleure.Translation
|
|||||||
/// Generates a <see cref="DispatchStub"/>.
|
/// Generates a <see cref="DispatchStub"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>Generated <see cref="DispatchStub"/></returns>
|
/// <returns>Generated <see cref="DispatchStub"/></returns>
|
||||||
private nint GenerateDispatchStub()
|
private IntPtr GenerateDispatchStub()
|
||||||
{
|
{
|
||||||
EmitterContext context = new();
|
var context = new EmitterContext();
|
||||||
|
|
||||||
Operand lblFallback = Label();
|
Operand lblFallback = Label();
|
||||||
Operand lblEnd = Label();
|
Operand lblEnd = Label();
|
||||||
@@ -160,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.
|
||||||
@@ -184,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>();
|
var func = Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<GuestFunction>();
|
||||||
|
|
||||||
return Marshal.GetFunctionPointerForDelegate(func);
|
return Marshal.GetFunctionPointerForDelegate(func);
|
||||||
}
|
}
|
||||||
@@ -197,9 +197,9 @@ namespace ARMeilleure.Translation
|
|||||||
/// Generates a <see cref="SlowDispatchStub"/>.
|
/// Generates a <see cref="SlowDispatchStub"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>Generated <see cref="SlowDispatchStub"/></returns>
|
/// <returns>Generated <see cref="SlowDispatchStub"/></returns>
|
||||||
private nint GenerateSlowDispatchStub()
|
private IntPtr 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);
|
||||||
@@ -209,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>();
|
var func = Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<GuestFunction>();
|
||||||
|
|
||||||
return Marshal.GetFunctionPointerForDelegate(func);
|
return Marshal.GetFunctionPointerForDelegate(func);
|
||||||
}
|
}
|
||||||
@@ -250,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();
|
||||||
@@ -262,18 +262,10 @@ namespace ARMeilleure.Translation
|
|||||||
|
|
||||||
Operand runningAddress = context.Add(nativeContext, Const((ulong)NativeContext.GetRunningOffset()));
|
Operand runningAddress = context.Add(nativeContext, Const((ulong)NativeContext.GetRunningOffset()));
|
||||||
Operand dispatchAddress = context.Add(nativeContext, Const((ulong)NativeContext.GetDispatchAddressOffset()));
|
Operand dispatchAddress = context.Add(nativeContext, Const((ulong)NativeContext.GetDispatchAddressOffset()));
|
||||||
Operand callDepthAddress = context.Add(nativeContext, Const((ulong)NativeContext.GetCallDepthOffset()));
|
|
||||||
|
|
||||||
EmitSyncFpContext(context, nativeContext, true);
|
EmitSyncFpContext(context, nativeContext, true);
|
||||||
|
|
||||||
context.MarkLabel(beginLbl);
|
context.MarkLabel(beginLbl);
|
||||||
|
|
||||||
if (Optimizations.EnableDeepCallRecursionProtection)
|
|
||||||
{
|
|
||||||
// Reset the call depth counter, since this is our first guest function call.
|
|
||||||
context.Store(callDepthAddress, Const(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
context.Store(dispatchAddress, guestAddress);
|
context.Store(dispatchAddress, guestAddress);
|
||||||
context.Copy(guestAddress, context.Call(Const((ulong)DispatchStub), OperandType.I64, nativeContext));
|
context.Copy(guestAddress, context.Call(Const((ulong)DispatchStub), OperandType.I64, nativeContext));
|
||||||
context.BranchIfFalse(endLbl, guestAddress);
|
context.BranchIfFalse(endLbl, guestAddress);
|
||||||
@@ -286,9 +278,9 @@ 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>();
|
return Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<DispatcherFunction>();
|
||||||
}
|
}
|
||||||
@@ -299,7 +291,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);
|
||||||
@@ -310,9 +302,9 @@ 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>();
|
return Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<WrapperFunction>();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ namespace ARMeilleure.Translation
|
|||||||
{
|
{
|
||||||
public static class TranslatorTestMethods
|
public static class TranslatorTestMethods
|
||||||
{
|
{
|
||||||
public delegate int FpFlagsPInvokeTest(nint managedMethod);
|
public delegate int FpFlagsPInvokeTest(IntPtr managedMethod);
|
||||||
|
|
||||||
private static bool SetPlatformFtz(EmitterContext context, bool ftz)
|
private static bool SetPlatformFtz(EmitterContext context, bool ftz)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
.idea/
|
|
||||||
*.iml
|
|
||||||
.gradle
|
|
||||||
local.properties
|
|
||||||
.DS_Store
|
|
||||||
build/
|
|
||||||
captures
|
|
||||||
.externalNativeBuild
|
|
||||||
.cxx/
|
|
||||||
|
|
||||||
app/src/main/jniLibs/arm64-v8a/**
|
|
||||||
!app/src/main/jniLibs/arm64-v8a/.gitkeep
|
|
||||||
@@ -1,175 +0,0 @@
|
|||||||
plugins {
|
|
||||||
id 'com.android.application'
|
|
||||||
id 'org.jetbrains.kotlin.plugin.compose'
|
|
||||||
}
|
|
||||||
|
|
||||||
android {
|
|
||||||
namespace = 'org.kenjinx.android'
|
|
||||||
compileSdk 37
|
|
||||||
|
|
||||||
defaultConfig {
|
|
||||||
applicationId "org.kenjinx.android"
|
|
||||||
minSdk 29
|
|
||||||
targetSdk 37
|
|
||||||
versionCode 20100
|
|
||||||
versionName '2.1.0-pr.2'
|
|
||||||
|
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
|
||||||
|
|
||||||
ndk {
|
|
||||||
//noinspection ChromeOsAbiSupport
|
|
||||||
abiFilters 'arm64-v8a'
|
|
||||||
}
|
|
||||||
|
|
||||||
externalNativeBuild {
|
|
||||||
cmake {
|
|
||||||
cppFlags "-std=c++20 -O3",
|
|
||||||
"-fno-math-errno -fno-trapping-math -fno-signed-zeros -ffinite-math-only"
|
|
||||||
arguments "-DANDROID_STL=c++_shared",
|
|
||||||
"-DCMAKE_BUILD_TYPE=Release",
|
|
||||||
"-DANDROID_ARM_NEON=TRUE",
|
|
||||||
"-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buildTypes {
|
|
||||||
release {
|
|
||||||
// minifyEnabled false
|
|
||||||
// proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
|
||||||
signingConfig = signingConfigs.debug
|
|
||||||
debuggable false
|
|
||||||
jniDebuggable false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def deviceFlavors = [
|
|
||||||
armv8 : "-march=armv8-a",
|
|
||||||
armv82 : "-march=armv8.2-a",
|
|
||||||
armv87 : "-march=armv8.7-a",
|
|
||||||
sd8eg5 : "-march=armv8.7-a+sve+sve2",
|
|
||||||
]
|
|
||||||
|
|
||||||
flavorDimensions "appid", "device"
|
|
||||||
productFlavors {
|
|
||||||
mainline {
|
|
||||||
dimension "appid"
|
|
||||||
applicationId "org.kenjinx.android"
|
|
||||||
manifestPlaceholders = [applicationLabel: "@string/app_name"]
|
|
||||||
}
|
|
||||||
optimized {
|
|
||||||
dimension "appid"
|
|
||||||
applicationId "com.garena.game.codm"
|
|
||||||
manifestPlaceholders = [applicationLabel: "@string/app_name_optimized"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
deviceFlavors.each { name, flags ->
|
|
||||||
productFlavors.create(name) {
|
|
||||||
dimension "device"
|
|
||||||
externalNativeBuild {
|
|
||||||
cmake {
|
|
||||||
cppFlags flags
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
androidComponents {
|
|
||||||
onVariants(selector().withBuildType("debug")) {
|
|
||||||
packaging.dex.useLegacyPackaging.set(false)
|
|
||||||
}
|
|
||||||
beforeVariants(selector().all()) { variant ->
|
|
||||||
def flavors = variant.productFlavors.collect { it.second }.toSet()
|
|
||||||
|
|
||||||
def invalidCombos = [
|
|
||||||
["optimized", "armv8"],
|
|
||||||
["optimized", "armv87"],
|
|
||||||
["optimized", "sd8eg5"]
|
|
||||||
]
|
|
||||||
|
|
||||||
if (invalidCombos.any { combo -> flavors.containsAll(combo) }) {
|
|
||||||
enable = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
compileOptions {
|
|
||||||
sourceCompatibility JavaVersion.VERSION_21
|
|
||||||
targetCompatibility JavaVersion.VERSION_21
|
|
||||||
}
|
|
||||||
|
|
||||||
buildFeatures {
|
|
||||||
compose = true
|
|
||||||
prefab = true
|
|
||||||
buildConfig = true
|
|
||||||
}
|
|
||||||
|
|
||||||
packagingOptions {
|
|
||||||
jniLibs {
|
|
||||||
keepDebugSymbols += '**/libkenjinx.so'
|
|
||||||
useLegacyPackaging true
|
|
||||||
}
|
|
||||||
dex {
|
|
||||||
useLegacyPackaging true
|
|
||||||
}
|
|
||||||
resources {
|
|
||||||
excludes += '/META-INF/{AL2.0,LGPL2.1}'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
externalNativeBuild {
|
|
||||||
cmake {
|
|
||||||
path file('src/main/cpp/CMakeLists.txt')
|
|
||||||
version = '4.1.2'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sourceSets {
|
|
||||||
main {
|
|
||||||
jniLibs.srcDirs = ['src/main/extLibs', 'src/main/jniLibs']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.named("preBuild") {
|
|
||||||
dependsOn ':libkenjinx:assemble'
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
runtimeOnly project(":libkenjinx")
|
|
||||||
implementation 'androidx.activity:activity-compose:1.13.0'
|
|
||||||
implementation 'androidx.appcompat:appcompat:1.8.0'
|
|
||||||
implementation 'androidx.compose.material3:material3'
|
|
||||||
implementation 'androidx.compose.material:material-icons-extended:1.7.8'
|
|
||||||
implementation 'androidx.compose.ui:ui'
|
|
||||||
implementation 'androidx.compose.ui:ui-graphics'
|
|
||||||
implementation 'androidx.compose.ui:ui-tooling-preview'
|
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:2.2.2'
|
|
||||||
implementation 'androidx.core:core-ktx:1.19.0'
|
|
||||||
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.11.0'
|
|
||||||
implementation 'androidx.navigation:navigation-compose:2.9.8'
|
|
||||||
implementation 'androidx.preference:preference-ktx:1.2.1'
|
|
||||||
implementation 'br.com.devsrsouza.compose.icons:css-gg:1.1.1'
|
|
||||||
implementation 'com.anggrayudi:storage:2.2.0'
|
|
||||||
implementation 'com.github.swordfish90:radialgamepad:2.0.0'
|
|
||||||
implementation 'com.google.android.material:material:1.14.0'
|
|
||||||
implementation 'com.google.code.gson:gson:2.14.0'
|
|
||||||
implementation 'com.halilibo.compose-richtext:richtext-commonmark:0.20.0'
|
|
||||||
implementation 'com.halilibo.compose-richtext:richtext-ui-material3:0.20.0'
|
|
||||||
implementation 'com.halilibo.compose-richtext:richtext-ui:0.20.0'
|
|
||||||
implementation 'io.coil-kt:coil-compose:2.7.0'
|
|
||||||
implementation 'net.java.dev.jna:jna:5.19.1@aar'
|
|
||||||
implementation 'net.lingala.zip4j:zip4j:2.11.6'
|
|
||||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.11.0'
|
|
||||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.11.0'
|
|
||||||
implementation platform('androidx.compose:compose-bom:2026.08.00')
|
|
||||||
implementation platform('org.jetbrains.kotlin:kotlin-bom:2.3.10')
|
|
||||||
androidTestImplementation 'androidx.compose.ui:ui-test-junit4'
|
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.7.0'
|
|
||||||
androidTestImplementation 'androidx.test.ext:junit:1.3.0'
|
|
||||||
androidTestImplementation platform('androidx.compose:compose-bom:2026.08.00')
|
|
||||||
testImplementation 'junit:junit:4.13.2'
|
|
||||||
debugImplementation 'androidx.compose.ui:ui-test-manifest'
|
|
||||||
debugImplementation 'androidx.compose.ui:ui-tooling'
|
|
||||||
}
|
|
||||||
-37
@@ -1,37 +0,0 @@
|
|||||||
-optimizationpasses 5
|
|
||||||
-optimizations !code/simplification/arithmetic
|
|
||||||
-optimizations !code/simplification/cast
|
|
||||||
-optimizations !field/*
|
|
||||||
-optimizations !class/merging/*
|
|
||||||
-optimizations !method/inlining/short
|
|
||||||
-optimizations !method/inlining/unique
|
|
||||||
-optimizations method/inlining/tailrecursion
|
|
||||||
-optimizations method/removal/parameter
|
|
||||||
-optimizations code/merging
|
|
||||||
-optimizations code/simplification/variable
|
|
||||||
-allowaccessmodification
|
|
||||||
-repackageclasses ''
|
|
||||||
-keepattributes Exceptions,InnerClasses,Signature,*Annotation*
|
|
||||||
-dontpreverify
|
|
||||||
-keep public class * extends android.app.Activity
|
|
||||||
-keep public class * extends android.app.Application
|
|
||||||
-keep public class * extends android.app.Service
|
|
||||||
-keep public class * extends android.content.BroadcastReceiver
|
|
||||||
-keep public class * extends android.content.ContentProvider
|
|
||||||
-keep public class * extends androidx.fragment.app.Fragment
|
|
||||||
-dontwarn java.awt.Component
|
|
||||||
-dontwarn java.awt.GraphicsEnvironment
|
|
||||||
-dontwarn java.awt.HeadlessException
|
|
||||||
-dontwarn java.awt.Window
|
|
||||||
-dontwarn javax.lang.model.element.Modifier
|
|
||||||
-assumenosideeffects class java.lang.Math {
|
|
||||||
public static double random();
|
|
||||||
public static double sin(...);
|
|
||||||
public static double cos(...);
|
|
||||||
public static double sqrt(...);
|
|
||||||
}
|
|
||||||
-assumenosideeffects public class ** {
|
|
||||||
public boolean is*();
|
|
||||||
public boolean get*();
|
|
||||||
public boolean has*();
|
|
||||||
}
|
|
||||||
@@ -1,116 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
|
||||||
|
|
||||||
<!-- Android 11+ Package Visibility: What this app is allowed to see/access -->
|
|
||||||
<queries>
|
|
||||||
<!-- If other flavors/starters are involved, make them visible here -->
|
|
||||||
<package android:name="org.kenjinx.android"/>
|
|
||||||
|
|
||||||
<!-- General: Make LAUNCHER activities and ACTION_VIEW files resolvable -->
|
|
||||||
<intent>
|
|
||||||
<action android:name="android.intent.action.MAIN" />
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
|
||||||
</intent>
|
|
||||||
</queries>
|
|
||||||
|
|
||||||
<uses-feature
|
|
||||||
android:name="android.hardware.audio.output"
|
|
||||||
android:required="true" />
|
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
|
||||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
|
||||||
<uses-permission
|
|
||||||
android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
|
|
||||||
tools:ignore="ScopedStorage" />
|
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
|
||||||
<!-- Notifications + special-use Foreground Service (Android 14+) -->
|
|
||||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
|
||||||
<!-- Required for startForeground() -->
|
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK"/>
|
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
|
||||||
|
|
||||||
<application
|
|
||||||
android:name=".KenjinxApplication"
|
|
||||||
android:allowBackup="true"
|
|
||||||
android:appCategory="game"
|
|
||||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
|
||||||
android:fullBackupContent="@xml/backup_rules"
|
|
||||||
android:icon="@mipmap/ic_launcher"
|
|
||||||
android:isGame="true"
|
|
||||||
android:label="${applicationLabel}"
|
|
||||||
android:supportsRtl="true"
|
|
||||||
android:theme="@style/Theme.KenjinxAndroid"
|
|
||||||
android:largeHeap="true"
|
|
||||||
android:requestLegacyExternalStorage="true"
|
|
||||||
tools:targetApi="31">
|
|
||||||
<activity
|
|
||||||
android:name=".MainActivity"
|
|
||||||
android:launchMode="singleTop"
|
|
||||||
android:exported="true"
|
|
||||||
android:screenOrientation="unspecified"
|
|
||||||
android:resizeableActivity="true"
|
|
||||||
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|uiMode"
|
|
||||||
android:hardwareAccelerated="true"
|
|
||||||
android:theme="@style/Theme.KenjinxAndroid">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.MAIN" />
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
|
||||||
</intent-filter>
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.VIEW" />
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
|
||||||
</intent-filter>
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="org.kenjinx.android.LAUNCH_GAME" />
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
|
||||||
</intent-filter>
|
|
||||||
</activity>
|
|
||||||
<!--
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="org.kenji.android.action.CREATE_SHORTCUT" />
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
|
||||||
</intent-filter>
|
|
||||||
-->
|
|
||||||
|
|
||||||
<!-- Shortcut Wizard Activity (transparent, freely rotatable) -->
|
|
||||||
<activity
|
|
||||||
android:name=".ShortcutWizardActivity"
|
|
||||||
android:exported="false"
|
|
||||||
android:theme="@style/Theme.KenjinxAndroid.Transparent"
|
|
||||||
android:screenOrientation="unspecified"
|
|
||||||
android:configChanges="orientation|screenSize|keyboardHidden|uiMode" />
|
|
||||||
|
|
||||||
<provider
|
|
||||||
android:name="androidx.core.content.FileProvider"
|
|
||||||
android:authorities="${applicationId}.fileprovider"
|
|
||||||
android:exported="false"
|
|
||||||
android:grantUriPermissions="true">
|
|
||||||
<meta-data
|
|
||||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
|
||||||
android:resource="@xml/provider_paths" />
|
|
||||||
</provider>
|
|
||||||
<provider
|
|
||||||
android:name=".providers.DocumentProvider"
|
|
||||||
android:authorities="${applicationId}.providers"
|
|
||||||
android:exported="true"
|
|
||||||
android:grantUriPermissions="true"
|
|
||||||
android:permission="android.permission.MANAGE_DOCUMENTS">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.content.action.DOCUMENTS_PROVIDER" />
|
|
||||||
</intent-filter>
|
|
||||||
</provider>
|
|
||||||
<service
|
|
||||||
android:name=".service.EmulationService"
|
|
||||||
android:exported="false"
|
|
||||||
android:stopWithTask="true"
|
|
||||||
android:foregroundServiceType="mediaPlayback" />
|
|
||||||
|
|
||||||
|
|
||||||
</application>
|
|
||||||
|
|
||||||
</manifest>
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
include(FetchContent)
|
|
||||||
|
|
||||||
# For more information about using CMake with Android Studio, read the
|
|
||||||
# documentation: https://d.android.com/studio/projects/add-native-code.html
|
|
||||||
|
|
||||||
# Sets the minimum version of CMake required to build the native library.
|
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.31.5)
|
|
||||||
|
|
||||||
# Declares and names the project.
|
|
||||||
|
|
||||||
project("kenjinxjni")
|
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 20)
|
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
|
|
||||||
|
|
||||||
FetchContent_Declare(
|
|
||||||
adrenotools
|
|
||||||
GIT_REPOSITORY https://github.com/bylaws/libadrenotools.git
|
|
||||||
GIT_TAG 8fae8ce254dfc1344527e05301e43f37dea2df80
|
|
||||||
)
|
|
||||||
|
|
||||||
FetchContent_MakeAvailable(adrenotools)
|
|
||||||
|
|
||||||
# Creates and names a library, sets it as either STATIC
|
|
||||||
# or SHARED, and provides the relative paths to its source code.
|
|
||||||
# You can define multiple libraries, and CMake builds them for you.
|
|
||||||
# Gradle automatically packages shared libraries with your APK.
|
|
||||||
|
|
||||||
add_library( # Sets the name of the library.
|
|
||||||
kenjinxjni
|
|
||||||
|
|
||||||
# Sets the library as a shared library.
|
|
||||||
SHARED
|
|
||||||
|
|
||||||
# Provides a relative path to your source file(s).
|
|
||||||
vulkan_wrapper.cpp
|
|
||||||
kenjinx.cpp)
|
|
||||||
|
|
||||||
# Searches for a specified prebuilt library and stores the path as a
|
|
||||||
# variable. Because CMake includes system libraries in the search path by
|
|
||||||
# default, you only need to specify the name of the public NDK library
|
|
||||||
# you want to add. CMake verifies that the library exists before
|
|
||||||
# completing its build.
|
|
||||||
|
|
||||||
find_library( # Sets the name of the path variable.
|
|
||||||
log-lib
|
|
||||||
|
|
||||||
# Specifies the name of the NDK library that
|
|
||||||
# you want CMake to locate.
|
|
||||||
log )
|
|
||||||
|
|
||||||
# Specifies libraries CMake should link to your target library. You
|
|
||||||
# can link multiple libraries, such as libraries you define in this
|
|
||||||
# build script, prebuilt third-party libraries, or system libraries.
|
|
||||||
|
|
||||||
target_link_libraries( # Specifies the target library.
|
|
||||||
kenjinxjni
|
|
||||||
# Links the target library to the log library
|
|
||||||
# included in the NDK.
|
|
||||||
${log-lib}
|
|
||||||
-lvulkan
|
|
||||||
-landroid
|
|
||||||
adrenotools
|
|
||||||
)
|
|
||||||
|
|
||||||
# Build external libraries if prebuilt files don't exist
|
|
||||||
set(JNI_PATH ../jniLibs/${CMAKE_ANDROID_ARCH_ABI})
|
|
||||||
cmake_path(ABSOLUTE_PATH JNI_PATH NORMALIZE)
|
|
||||||
|
|
||||||
cmake_path(APPEND JNI_PATH libcrypto.so OUTPUT_VARIABLE LIBCRYPTO_JNI_PATH)
|
|
||||||
cmake_path(APPEND JNI_PATH libssl.so OUTPUT_VARIABLE LIBSSL_JNI_PATH)
|
|
||||||
|
|
||||||
# Add OpenAL
|
|
||||||
add_subdirectory(libraries/openal)
|
|
||||||
set_target_properties(OpenAL PROPERTIES
|
|
||||||
LIBRARY_OUTPUT_DIRECTORY ${JNI_PATH}
|
|
||||||
ARCHIVE_OUTPUT_DIRECTORY ${JNI_PATH}
|
|
||||||
RUNTIME_OUTPUT_DIRECTORY ${JNI_PATH}
|
|
||||||
)
|
|
||||||
@@ -1,254 +0,0 @@
|
|||||||
// Write C++ code here.
|
|
||||||
//
|
|
||||||
// Do not forget to dynamically load the C++ library into your application.
|
|
||||||
//
|
|
||||||
// For instance,
|
|
||||||
//
|
|
||||||
// In MainActivity.java:
|
|
||||||
// static {
|
|
||||||
// System.loadLibrary("kenjinxjni");
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Or, in MainActivity.kt:
|
|
||||||
// companion object {
|
|
||||||
// init {
|
|
||||||
// System.loadLibrary("kenjinxjni")
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
#include "kenjinx.h"
|
|
||||||
#include "pthread.h"
|
|
||||||
#include <chrono>
|
|
||||||
#include <csignal>
|
|
||||||
|
|
||||||
|
|
||||||
std::chrono::time_point<std::chrono::steady_clock, std::chrono::nanoseconds> _currentTimePoint;
|
|
||||||
|
|
||||||
extern "C"
|
|
||||||
{
|
|
||||||
JNIEXPORT jlong JNICALL
|
|
||||||
Java_org_kenjinx_android_NativeHelpers_getNativeWindow(
|
|
||||||
JNIEnv *env,
|
|
||||||
jobject instance,
|
|
||||||
jobject surface) {
|
|
||||||
auto nativeWindow = ANativeWindow_fromSurface(env, surface);
|
|
||||||
return nativeWindow == nullptr ? -1 : (jlong) nativeWindow;
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT void JNICALL
|
|
||||||
Java_org_kenjinx_android_NativeHelpers_releaseNativeWindow(
|
|
||||||
JNIEnv *env,
|
|
||||||
jobject instance,
|
|
||||||
jlong window) {
|
|
||||||
auto nativeWindow = (ANativeWindow *) window;
|
|
||||||
|
|
||||||
if (nativeWindow != nullptr)
|
|
||||||
ANativeWindow_release(nativeWindow);
|
|
||||||
}
|
|
||||||
|
|
||||||
long createSurface(long native_surface, long instance) {
|
|
||||||
auto nativeWindow = (ANativeWindow *) native_surface;
|
|
||||||
VkSurfaceKHR surface;
|
|
||||||
auto vkInstance = (VkInstance) instance;
|
|
||||||
auto fpCreateAndroidSurfaceKHR =
|
|
||||||
reinterpret_cast<PFN_vkCreateAndroidSurfaceKHR>(vkGetInstanceProcAddr(vkInstance,
|
|
||||||
"vkCreateAndroidSurfaceKHR"));
|
|
||||||
if (fpCreateAndroidSurfaceKHR == nullptr)
|
|
||||||
LOGE("Could not get function pointer to CreateAndroidSurfaceKHR");
|
|
||||||
|
|
||||||
VkAndroidSurfaceCreateInfoKHR info;
|
|
||||||
info.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR;
|
|
||||||
info.pNext = nullptr;
|
|
||||||
info.flags = 0;
|
|
||||||
info.window = nativeWindow;
|
|
||||||
VK_CHECK(fpCreateAndroidSurfaceKHR(vkInstance, &info, nullptr, &surface));
|
|
||||||
return (long) surface;
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT jlong JNICALL
|
|
||||||
Java_org_kenjinx_android_NativeHelpers_getCreateSurfacePtr(
|
|
||||||
JNIEnv *env,
|
|
||||||
jobject instance) {
|
|
||||||
return (jlong) createSurface;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *getStringPointer(
|
|
||||||
JNIEnv *env,
|
|
||||||
jstring jS) {
|
|
||||||
const char *cparam = env->GetStringUTFChars(jS, nullptr);
|
|
||||||
auto len = env->GetStringUTFLength(jS);
|
|
||||||
char *s = new char[len + 1]; //null terminator
|
|
||||||
strcpy(s, cparam);
|
|
||||||
env->ReleaseStringUTFChars(jS, cparam);
|
|
||||||
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
jstring createString(
|
|
||||||
JNIEnv *env,
|
|
||||||
char *ch) {
|
|
||||||
auto str = env->NewStringUTF(ch);
|
|
||||||
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
jstring createStringFromStdString(
|
|
||||||
JNIEnv *env,
|
|
||||||
std::string s) {
|
|
||||||
auto str = env->NewStringUTF(s.c_str());
|
|
||||||
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
extern "C"
|
|
||||||
void setRenderingThread() {
|
|
||||||
auto currentId = pthread_self();
|
|
||||||
|
|
||||||
_renderingThreadId = currentId;
|
|
||||||
|
|
||||||
_currentTimePoint = std::chrono::high_resolution_clock::now();
|
|
||||||
}
|
|
||||||
extern "C"
|
|
||||||
JNIEXPORT void JNICALL
|
|
||||||
Java_org_kenjinx_android_MainActivity_initVm(JNIEnv *env, jobject thiz) {
|
|
||||||
JavaVM *vm = nullptr;
|
|
||||||
env->GetJavaVM(&vm);
|
|
||||||
_vm = vm;
|
|
||||||
_mainActivity = thiz;
|
|
||||||
_mainActivityClass = env->GetObjectClass(thiz);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isInitialOrientationFlipped = true;
|
|
||||||
|
|
||||||
extern "C"
|
|
||||||
void setCurrentTransform(long native_window, int transform) {
|
|
||||||
if (native_window == 0 || native_window == -1)
|
|
||||||
return;
|
|
||||||
auto nativeWindow = (ANativeWindow *) native_window;
|
|
||||||
|
|
||||||
auto nativeTransform = ANativeWindowTransform::ANATIVEWINDOW_TRANSFORM_IDENTITY;
|
|
||||||
|
|
||||||
transform = transform >> 1;
|
|
||||||
|
|
||||||
// transform is a valid VkSurfaceTransformFlagBitsKHR
|
|
||||||
switch (transform) {
|
|
||||||
case 0x1:
|
|
||||||
nativeTransform = ANativeWindowTransform::ANATIVEWINDOW_TRANSFORM_IDENTITY;
|
|
||||||
break;
|
|
||||||
case 0x2:
|
|
||||||
nativeTransform = ANativeWindowTransform::ANATIVEWINDOW_TRANSFORM_ROTATE_90;
|
|
||||||
break;
|
|
||||||
case 0x4:
|
|
||||||
nativeTransform = isInitialOrientationFlipped
|
|
||||||
? ANativeWindowTransform::ANATIVEWINDOW_TRANSFORM_IDENTITY
|
|
||||||
: ANativeWindowTransform::ANATIVEWINDOW_TRANSFORM_ROTATE_180;
|
|
||||||
break;
|
|
||||||
case 0x8:
|
|
||||||
nativeTransform = ANativeWindowTransform::ANATIVEWINDOW_TRANSFORM_ROTATE_270;
|
|
||||||
break;
|
|
||||||
case 0x10:
|
|
||||||
nativeTransform = ANativeWindowTransform::ANATIVEWINDOW_TRANSFORM_MIRROR_HORIZONTAL;
|
|
||||||
break;
|
|
||||||
case 0x20:
|
|
||||||
nativeTransform = static_cast<ANativeWindowTransform>(
|
|
||||||
ANativeWindowTransform::ANATIVEWINDOW_TRANSFORM_MIRROR_HORIZONTAL |
|
|
||||||
ANATIVEWINDOW_TRANSFORM_ROTATE_90);
|
|
||||||
break;
|
|
||||||
case 0x40:
|
|
||||||
nativeTransform = ANativeWindowTransform::ANATIVEWINDOW_TRANSFORM_MIRROR_VERTICAL;
|
|
||||||
break;
|
|
||||||
case 0x80:
|
|
||||||
nativeTransform = static_cast<ANativeWindowTransform>(
|
|
||||||
ANativeWindowTransform::ANATIVEWINDOW_TRANSFORM_MIRROR_VERTICAL |
|
|
||||||
ANATIVEWINDOW_TRANSFORM_ROTATE_90);
|
|
||||||
break;
|
|
||||||
case 0x100:
|
|
||||||
nativeTransform = ANativeWindowTransform::ANATIVEWINDOW_TRANSFORM_IDENTITY;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
nativeWindow->perform(nativeWindow, NATIVE_WINDOW_SET_BUFFERS_TRANSFORM,
|
|
||||||
static_cast<int32_t>(nativeTransform));
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C"
|
|
||||||
JNIEXPORT jlong JNICALL
|
|
||||||
Java_org_kenjinx_android_NativeHelpers_loadDriver(JNIEnv *env, jobject thiz,
|
|
||||||
jstring native_lib_path,
|
|
||||||
jstring private_apps_path,
|
|
||||||
jstring driver_name) {
|
|
||||||
auto libPath = getStringPointer(env, native_lib_path);
|
|
||||||
auto privateAppsPath = getStringPointer(env, private_apps_path);
|
|
||||||
auto driverName = getStringPointer(env, driver_name);
|
|
||||||
|
|
||||||
auto handle = adrenotools_open_libvulkan(
|
|
||||||
RTLD_NOW,
|
|
||||||
ADRENOTOOLS_DRIVER_CUSTOM,
|
|
||||||
nullptr,
|
|
||||||
libPath,
|
|
||||||
privateAppsPath,
|
|
||||||
driverName,
|
|
||||||
nullptr,
|
|
||||||
nullptr
|
|
||||||
);
|
|
||||||
|
|
||||||
delete libPath;
|
|
||||||
delete privateAppsPath;
|
|
||||||
delete driverName;
|
|
||||||
|
|
||||||
return (jlong) handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C"
|
|
||||||
void debug_break(int code) {
|
|
||||||
if (code >= 3)
|
|
||||||
int r = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C"
|
|
||||||
JNIEXPORT void JNICALL
|
|
||||||
Java_org_kenjinx_android_NativeHelpers_setTurboMode(JNIEnv *env, jobject thiz, jboolean enable) {
|
|
||||||
adrenotools_set_turbo(enable);
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C"
|
|
||||||
JNIEXPORT jint JNICALL
|
|
||||||
Java_org_kenjinx_android_NativeHelpers_getMaxSwapInterval(JNIEnv *env, jobject thiz,
|
|
||||||
jlong native_window) {
|
|
||||||
auto nativeWindow = (ANativeWindow *) native_window;
|
|
||||||
|
|
||||||
return nativeWindow->maxSwapInterval;
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C"
|
|
||||||
JNIEXPORT jint JNICALL
|
|
||||||
Java_org_kenjinx_android_NativeHelpers_getMinSwapInterval(JNIEnv *env, jobject thiz,
|
|
||||||
jlong native_window) {
|
|
||||||
auto nativeWindow = (ANativeWindow *) native_window;
|
|
||||||
|
|
||||||
return nativeWindow->minSwapInterval;
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C"
|
|
||||||
JNIEXPORT jint JNICALL
|
|
||||||
Java_org_kenjinx_android_NativeHelpers_setSwapInterval(JNIEnv *env, jobject thiz,
|
|
||||||
jlong native_window, jint swap_interval) {
|
|
||||||
auto nativeWindow = (ANativeWindow *) native_window;
|
|
||||||
|
|
||||||
return nativeWindow->setSwapInterval(nativeWindow, swap_interval);
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C"
|
|
||||||
JNIEXPORT jstring JNICALL
|
|
||||||
Java_org_kenjinx_android_NativeHelpers_getStringJava(JNIEnv *env, jobject thiz, jlong ptr) {
|
|
||||||
return createString(env, (char*)ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C"
|
|
||||||
JNIEXPORT void JNICALL
|
|
||||||
Java_org_kenjinx_android_NativeHelpers_setIsInitialOrientationFlipped(JNIEnv *env, jobject thiz,
|
|
||||||
jboolean is_flipped) {
|
|
||||||
isInitialOrientationFlipped = is_flipped;
|
|
||||||
}
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by Emmanuel Hansen on 6/19/2023.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef KENJINXNATIVE_KENJINX_H
|
|
||||||
#define KENJINXNATIVE_KENJINX_H
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <cstring>
|
|
||||||
#include <dlfcn.h>
|
|
||||||
#include <exception>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <jni.h>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include <android/log.h>
|
|
||||||
#include <android/native_window.h>
|
|
||||||
#include <android/native_window_jni.h>
|
|
||||||
|
|
||||||
#include "vulkan_wrapper.h"
|
|
||||||
#include <vulkan/vulkan_android.h>
|
|
||||||
#include "adrenotools/driver.h"
|
|
||||||
#include "native_window.h"
|
|
||||||
|
|
||||||
// Android log function wrappers
|
|
||||||
static const char* TAG = "Kenjinx";
|
|
||||||
#define LOGI(...) \
|
|
||||||
((void)__android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__))
|
|
||||||
#define LOGW(...) \
|
|
||||||
((void)__android_log_print(ANDROID_LOG_WARN, TAG, __VA_ARGS__))
|
|
||||||
#define LOGE(...) \
|
|
||||||
((void)__android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__))
|
|
||||||
|
|
||||||
// A macro to pass call to Vulkan and check for return value for success
|
|
||||||
#define CALL_VK(func) \
|
|
||||||
if (VK_SUCCESS != (func)) { \
|
|
||||||
__android_log_print(ANDROID_LOG_ERROR, "Tutorial ", \
|
|
||||||
"Vulkan error. File[%s], line[%d]", __FILE__, \
|
|
||||||
__LINE__); \
|
|
||||||
assert(false); \
|
|
||||||
}
|
|
||||||
|
|
||||||
// A macro to check value is VK_SUCCESS
|
|
||||||
// Used also for non-vulkan functions but return VK_SUCCESS
|
|
||||||
#define VK_CHECK(x) CALL_VK(x)
|
|
||||||
|
|
||||||
#define LoadLib(a) dlopen(a, RTLD_NOW)
|
|
||||||
|
|
||||||
void *_kenjinxNative = nullptr;
|
|
||||||
|
|
||||||
// Kenjinx imported functions
|
|
||||||
bool (*initialize)(char *) = nullptr;
|
|
||||||
|
|
||||||
long _renderingThreadId = 0;
|
|
||||||
JavaVM *_vm = nullptr;
|
|
||||||
jobject _mainActivity = nullptr;
|
|
||||||
jclass _mainActivityClass = nullptr;
|
|
||||||
|
|
||||||
#endif //KENJINXNATIVE_KENJINX_H
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user