Author SHA1 Message Date
KeatonTheBot 39544e323c Update Kenji-NX to 2.0.4 2025-05-29 22:01:40 -05:00
KeatonTheBot 24932b7e1b infra: Remove Ryujinx.Common references from project files that indirectly reference the same file 2025-05-29 11:10:57 -05:00
KeatonTheBot 2aaf34937d infra: Fix missing libarmeilleure-jitsupport.dylib on macOS (arm64) builds 2025-05-29 10:28:00 -05:00
KeatonTheBot bce3426d26 Linux: Fix games not launching (from Bionic code) 2025-05-28 03:37:23 -05:00
MrKev 51ca73cb10 Fix JWT Claims and Socket Flag Handling to Improve Just Dance® Server Connection 2025-05-27 18:28:15 -05:00
KeatonTheBot cd3221abdc Android: NCE support 2025-05-26 16:02:20 -05:00
KeatonTheBot 19dd23c288 Android: Numerous fixes
* GetSerialNumber in ISystemSettingsServer

* Bionic qualifiers

* Revert application pool size to 3285 MB for 4 GB DRAM

* Fix PPTC recompilation if mod or patch is applied
2025-05-26 16:01:23 -05:00
GreemDev 20f1cef9b2 fix: Super Mario Party Jamboree audio renderer crashing 2025-05-26 10:22:13 -05:00
KeatonTheBot 2f400491d5 ffmpeg: Fix green screen issues on Linux 2025-05-25 22:34:06 -05:00
ChromJ f6351e3fff Add Ctrl+Comma shortcut for settings
See merge request kenji-nx/ryujinx!2
2025-05-24 23:47:13 -05:00
KeatonTheBot f15ddcf9c4 UI: Fix GitLab logos not showing on git server in Ryujinx.UI.Common\Resources folder due to capitalization error 2025-05-24 22:23:44 -05:00
Evan Husted 88d904acde Headless in Avalonia v2
Launch the Ryujinx.exe, first argument --no-gui or nogui, and the rest of the arguments should be your normal headless script. You can include the new option --use-main-config which will provide any arguments that you don't, filled in from your main config made by the UI.
2025-05-24 19:32:45 -05:00
KeatonTheBot fbcf57bef5 UI: RPC: Add LUNAR Remastered Collection 2025-05-22 15:29:39 -05:00
KeatonTheBot 8bfb670e65 infra: Update SDL2 to 2.32.6 2025-05-21 15:50:27 -05:00
KeatonTheBot e00bece65a nuget: Remove GtkSharp packages 2025-05-20 02:26:44 -05:00
GreemDev 2a03f7b1d4 infra: Update to Ryujinx.LibHac 0.20.0.
This time it's pulled in via GitLab package registry.
2025-05-20 00:28:49 -05:00
KeatonTheBot 6c7b7d6fc4 UI: Change the GitHub button in the About window to GitLab 2025-05-19 23:03:37 -05:00
KeatonTheBot c43db7d5f6 Update README.md and other documents 2025-05-17 13:35:37 -05:00
KeatonTheBot cc92af12a4 infra: Fix clean installation crash 2025-05-16 17:23:10 -05:00
1272 changed files with 12921 additions and 45989 deletions
-3
View File
@@ -173,6 +173,3 @@ PublishProfiles/
# Glade backup files
*.glade~
# Log files
/logs/
-9
View File
@@ -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
View File
@@ -3,54 +3,52 @@
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Avalonia" Version="11.3.13" />
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.13" />
<PackageVersion Include="Avalonia.Desktop" Version="11.3.13" />
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.13" />
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.3.13" />
<PackageVersion Include="Svg.Controls.Avalonia" Version="11.3.9.5" />
<PackageVersion Include="Svg.Controls.Skia.Avalonia" Version="11.3.9.5" />
<PackageVersion Include="Avalonia" Version="11.0.13" />
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.0.13" />
<PackageVersion Include="Avalonia.Desktop" Version="11.0.13" />
<PackageVersion Include="Avalonia.Diagnostics" Version="11.0.13" />
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.0.13" />
<PackageVersion Include="Avalonia.Svg" Version="11.0.0.19" />
<PackageVersion Include="Avalonia.Svg.Skia" Version="11.0.0.19" />
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
<PackageVersion Include="Concentus" Version="2.2.2" />
<PackageVersion Include="DiscordRichPresence" Version="1.6.1.70" />
<PackageVersion Include="DynamicData" Version="9.4.31" />
<PackageVersion Include="FluentAvaloniaUI" Version="2.4.1" />
<PackageVersion Include="Gommon" Version="2.8.1.1" />
<PackageVersion Include="Humanizer" Version="3.0.10" />
<PackageVersion Include="DiscordRichPresence" Version="1.2.1.24" />
<PackageVersion Include="DynamicData" Version="9.0.4" />
<PackageVersion Include="FluentAvaloniaUI" Version="2.0.5" />
<PackageVersion Include="Gommon" Version="2.7.1.1" />
<PackageVersion Include="Humanizer" Version="2.14.1" />
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.12.0" />
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.17.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.3.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2" />
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.9.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.1" />
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
<PackageVersion Include="NetCoreServer" Version="8.0.7" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.4" />
<PackageVersion Include="NUnit" Version="4.5.1" />
<PackageVersion Include="NUnit3TestAdapter" Version="6.2.0" />
<PackageVersion Include="OpenTK.Core" Version="4.9.4" />
<PackageVersion Include="OpenTK.Graphics" Version="4.9.4" />
<PackageVersion Include="OpenTK.Audio.OpenAL" Version="4.9.4" />
<PackageVersion Include="OpenTK.Windowing.GraphicsLibraryFramework" Version="4.9.4" />
<PackageVersion Include="NUnit" Version="3.13.3" />
<PackageVersion Include="NUnit3TestAdapter" Version="4.1.0" />
<PackageVersion Include="OpenTK.Core" Version="4.8.2" />
<PackageVersion Include="OpenTK.Graphics" Version="4.8.2" />
<PackageVersion Include="OpenTK.Audio.OpenAL" Version="4.8.2" />
<PackageVersion Include="OpenTK.Windowing.GraphicsLibraryFramework" Version="4.8.2" />
<PackageVersion Include="Open.NAT.Core" Version="2.1.0.5" />
<PackageVersion Include="Ryujinx.Audio.OpenAL" Version="1.25.2" />
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies.Linux" Version="6.1.3-build5" />
<PackageVersion Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" />
<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.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.LibHac" Version="0.21.0-alpha.128" />
<PackageVersion Include="Ryujinx.SDL3-CS" Version="2026.707.0" />
<PackageVersion Include="Ryujinx.LibHac" Version="0.20.0-alpha.103" />
<PackageVersion Include="Ryujinx.SDL2-CS-Redux" Version="2.32.6" />
<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="Silk.NET.Shaderc" Version="2.23.0" />
<PackageVersion Include="Silk.NET.Vulkan" Version="2.23.0" />
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.23.0" />
<PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.23.0" />
<PackageVersion Include="Silk.NET.Vulkan" Version="2.22.0" />
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.22.0" />
<PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.22.0" />
<PackageVersion Include="SkiaSharp" Version="2.88.9" />
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.9" />
<PackageVersion Include="SPB" Version="0.0.4-build32" />
<PackageVersion Include="System.IO.Hashing" Version="9.0.12" />
<PackageVersion Include="System.Management" Version="9.0.12" />
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.1.0" />
<PackageVersion Include="Rxmxnx.PInvoke.Extensions" Version="2.9.0" />
<PackageVersion Include="System.IO.Hashing" Version="9.0.5" />
<PackageVersion Include="System.Management" Version="9.0.5" />
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
</ItemGroup>
</Project>
+23 -18
View File
@@ -1,26 +1,31 @@
<h1 align="center">
<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>
<b>Kenji-NX</b>
<br>
[![Latest Desktop Release](https://git.ryujinx.app/projects/Kenji-NX/badges/release.svg?label=desktop)](https://git.ryujinx.app/projects/Kenji-NX/releases/latest)
[![Latest Android Release](https://git.ryujinx.app/Kenji-NX/android/badges/release.svg?label=android)](https://git.ryujinx.app/Kenji-NX/android/releases/latest)
<br>
[![Discord](https://img.shields.io/discord/1294443224030511104?color=5865F2&label=Kenji-NX&logo=discord&logoColor=white)](https://discord.gg/zBSAuZfKqe)
<a href="https://github.com/Kenji-NX/Releases/releases/latest">
<img src="https://img.shields.io/github/v/release/Kenji-NX/Releases"
alt="Latest Release">
</a>
</h1>
Kenji-NX is an open-source Nintendo Switch emulator, originally created by gdkchan, written in C#.
This emulator aims at providing excellent accuracy and performance, a user-friendly interface and consistent builds.
It was written from scratch and development on the project began in September 2017.
Kenji-NX is available on GitHub under the [MIT license](LICENSE.txt).
<p>
Kenji-NX is an open-source Nintendo Switch emulator, originally created by gdkchan, written in C#.
This emulator aims at providing excellent accuracy and performance, a user-friendly interface and consistent builds.
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.
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.
It brings over many of the front-facing features from the aforementioned forks with *additional* contributions from KeatonTheBot and others.
<p>
<img src="docs/shell.png">
</p>
## Compatibility
@@ -53,12 +58,12 @@ If you wish to build the emulator yourself, follow these steps:
### 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).
### 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
@@ -120,7 +125,7 @@ See [LICENSE.txt](LICENSE.txt) and [THIRDPARTY.md](distribution/legal/THIRDPARTY
## 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.
- [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.
-8
View File
@@ -5,8 +5,6 @@ VisualStudioVersion = 17.1.32228.430
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Tests", "src\Ryujinx.Tests\Ryujinx.Tests.csproj", "{EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}"
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}"
EndProject
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}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
Directory.Build.props = Directory.Build.props
Directory.Packages.props = Directory.Packages.props
nuget.config = nuget.config
EndProjectSection
EndProject
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}.Release|Any CPU.ActiveCfg = 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.Build.0 = Debug|Any CPU
{D8F72938-78EF-4E8C-BAFE-531C9C3C8F15}.Release|Any CPU.ActiveCfg = Release|Any CPU
-47
View File
@@ -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
View File
@@ -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.
* 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.
* 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
===============
+5 -7
View File
@@ -3,21 +3,19 @@
<packageSources>
<clear />
<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/packages/projects/nuget/index.json" />
<!--<add key="Silk.NET" value="https://gitlab.com/api/v4/projects/51457475/packages/nuget/index.json" />-->
<add key="LibHacAlpha" value="https://git.ryujinx.app/api/v4/projects/17/packages/nuget/index.json" />
</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>
<!-- 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">
<package pattern="*" />
</packageSource>
<packageSource key="LibHacAlpha">
<package pattern="Ryujinx.LibHac" />
</packageSource>
<!--<packageSource key="Silk.NET">
<package pattern="Silk.*" />
</packageSource>-->
</packageSourceMapping>
</configuration>
@@ -13,13 +13,13 @@ namespace ARMeilleure.CodeGen.Arm64
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)
{
// 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).
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);
@@ -254,7 +254,7 @@ namespace ARMeilleure.CodeGen.Arm64
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)
+1 -1
View File
@@ -123,7 +123,7 @@ namespace ARMeilleure.CodeGen.Arm64
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));
}
@@ -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 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.
if (value is 0 or ulong.MaxValue)
if (value == 0 || value == ulong.MaxValue)
{
immN = 0;
immS = 0;
@@ -91,7 +91,7 @@ namespace ARMeilleure.CodeGen.Arm64
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)
{
@@ -119,7 +119,7 @@ namespace ARMeilleure.CodeGen.Arm64
}
else
{
if (!_pendingBranches.TryGetValue(target, out List<(ArmCondition Condition, long BranchPos)> list))
if (!_pendingBranches.TryGetValue(target, out var list))
{
list = new List<(ArmCondition, long)>();
_pendingBranches.Add(target, list);
@@ -189,7 +189,8 @@ namespace ARMeilleure.CodeGen.Arm64
// The only blocks which can have 0 successors are exit blocks.
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
{
@@ -321,7 +322,7 @@ namespace ARMeilleure.CodeGen.Arm64
Debug.Assert(comp.Kind == OperandKind.Constant);
ArmCondition cond = ((Comparison)comp.AsInt32()).ToArmCondition();
var cond = ((Comparison)comp.AsInt32()).ToArmCondition();
GenerateCompareCommon(context, operation);
@@ -353,7 +354,7 @@ namespace ARMeilleure.CodeGen.Arm64
Debug.Assert(dest.Type == OperandType.I32);
Debug.Assert(comp.Kind == OperandKind.Constant);
ArmCondition cond = ((Comparison)comp.AsInt32()).ToArmCondition();
var cond = ((Comparison)comp.AsInt32()).ToArmCondition();
GenerateCompareCommon(context, operation);
@@ -463,7 +464,7 @@ namespace ARMeilleure.CodeGen.Arm64
Operand dest = operation.Destination;
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(source.Type != OperandType.V128);
@@ -482,7 +483,7 @@ namespace ARMeilleure.CodeGen.Arm64
Operand dest = operation.Destination;
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(source.Type.IsInteger());
@@ -1462,7 +1463,7 @@ namespace ARMeilleure.CodeGen.Arm64
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)
@@ -1552,7 +1553,7 @@ namespace ARMeilleure.CodeGen.Arm64
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.Value == op2.Value);
}
@@ -127,13 +127,13 @@ namespace ARMeilleure.CodeGen.Arm64
#region macOS
[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")]
private static bool CheckSysctlName(string name)
{
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;
}
+15 -15
View File
@@ -736,19 +736,19 @@ namespace ARMeilleure.CodeGen.Arm64
{
IntrinsicInfo info = IntrinsicTable.GetInfo(intrinsic & ~(Intrinsic.Arm64VTypeMask | Intrinsic.Arm64VSizeMask));
return info.Type is IntrinsicType.ScalarBinaryRd
or IntrinsicType.ScalarTernaryFPRdByElem
or IntrinsicType.ScalarTernaryShlRd
or IntrinsicType.ScalarTernaryShrRd
or IntrinsicType.Vector128BinaryRd
or IntrinsicType.VectorBinaryRd
or IntrinsicType.VectorInsertByElem
or IntrinsicType.VectorTernaryRd
or IntrinsicType.VectorTernaryRdBitwise
or IntrinsicType.VectorTernaryFPRdByElem
or IntrinsicType.VectorTernaryRdByElem
or IntrinsicType.VectorTernaryShlRd
or IntrinsicType.VectorTernaryShrRd;
return info.Type == IntrinsicType.ScalarBinaryRd ||
info.Type == IntrinsicType.ScalarTernaryFPRdByElem ||
info.Type == IntrinsicType.ScalarTernaryShlRd ||
info.Type == IntrinsicType.ScalarTernaryShrRd ||
info.Type == IntrinsicType.Vector128BinaryRd ||
info.Type == IntrinsicType.VectorBinaryRd ||
info.Type == IntrinsicType.VectorInsertByElem ||
info.Type == IntrinsicType.VectorTernaryRd ||
info.Type == IntrinsicType.VectorTernaryRdBitwise ||
info.Type == IntrinsicType.VectorTernaryFPRdByElem ||
info.Type == IntrinsicType.VectorTernaryRdByElem ||
info.Type == IntrinsicType.VectorTernaryShlRd ||
info.Type == IntrinsicType.VectorTernaryShrRd;
}
private static bool HasConstSrc1(Operation node, ulong value)
@@ -847,9 +847,9 @@ namespace ARMeilleure.CodeGen.Arm64
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;
}
}
+2 -1
View File
@@ -1,6 +1,7 @@
using ARMeilleure.CodeGen.Linking;
using ARMeilleure.CodeGen.Unwinding;
using ARMeilleure.Translation.Cache;
using System;
using System.Runtime.InteropServices;
namespace ARMeilleure.CodeGen
@@ -57,7 +58,7 @@ namespace ARMeilleure.CodeGen
/// <typeparam name="T">Type of delegate</typeparam>
/// <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>
public T MapWithPointer<T>(out nint codePointer)
public T MapWithPointer<T>(out IntPtr codePointer)
{
codePointer = JitCache.Map(this);
+1 -1
View File
@@ -10,7 +10,7 @@ namespace ARMeilleure.CodeGen.Linking
/// <summary>
/// Gets an empty <see cref="RelocInfo"/>.
/// </summary>
public static RelocInfo Empty { get; } = new(null);
public static RelocInfo Empty { get; } = new RelocInfo(null);
private readonly RelocEntry[] _entries;
@@ -227,11 +227,11 @@ namespace ARMeilleure.CodeGen.Optimizations
private static bool HasSideEffects(Operation node)
{
return node.Instruction is Instruction.Call
or Instruction.Tailcall
or Instruction.CompareAndSwap
or Instruction.CompareAndSwap16
or Instruction.CompareAndSwap8;
return node.Instruction == Instruction.Call
|| node.Instruction == Instruction.Tailcall
|| node.Instruction == Instruction.CompareAndSwap
|| node.Instruction == Instruction.CompareAndSwap16
|| node.Instruction == Instruction.CompareAndSwap8;
}
private static bool IsPropagableCompare(Operation operation)
@@ -115,7 +115,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
{
NumberLocals(cfg, regMasks.RegistersCount);
AllocationContext context = new(stackAlloc, regMasks, _intervals.Count);
var context = new AllocationContext(stackAlloc, regMasks, _intervals.Count);
BuildIntervals(cfg, context);
@@ -839,7 +839,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
{
dest.NumberLocal(_intervals.Count);
LiveInterval interval = new(dest);
LiveInterval interval = new LiveInterval(dest);
_intervals.Add(interval);
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.
// This is used for register preferencing later on, which allows the copy to be eliminated
// 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);
@@ -1120,7 +1120,8 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
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()
{
return HashCode.Combine((nint)_data);
return HashCode.Combine((IntPtr)_data);
}
public override string ToString()
@@ -15,12 +15,12 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
{
if (_count + 1 > _capacity)
{
Span<LiveInterval> oldSpan = Span;
var oldSpan = Span;
_capacity = Math.Max(4, _capacity * 2);
_items = Allocators.References.Allocate<LiveInterval>((uint)_capacity);
Span<LiveInterval> newSpan = Span;
var newSpan = Span;
oldSpan.CopyTo(newSpan);
}
@@ -63,7 +63,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
public override int GetHashCode()
{
return HashCode.Combine((nint)_data);
return HashCode.Combine((IntPtr)_data);
}
public override string ToString()
@@ -16,12 +16,12 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
{
if (Count + 1 > _capacity)
{
Span<int> oldSpan = Span;
var oldSpan = Span;
_capacity = Math.Max(4, _capacity * 2);
_items = Allocators.Default.Allocate<int>((uint)_capacity);
Span<int> newSpan = Span;
var newSpan = Span;
oldSpan.CopyTo(newSpan);
}
+11 -12
View File
@@ -1,6 +1,5 @@
using ARMeilleure.CodeGen.Linking;
using ARMeilleure.IntermediateRepresentation;
using Microsoft.IO;
using Ryujinx.Common.Memory;
using System;
using System.Collections.Generic;
@@ -1107,17 +1106,17 @@ namespace ARMeilleure.CodeGen.X86
}
else
{
if ((flags & InstructionFlags.Prefix66) != 0)
if (flags.HasFlag(InstructionFlags.Prefix66))
{
WriteByte(0x66);
}
if ((flags & InstructionFlags.PrefixF2) != 0f)
if (flags.HasFlag(InstructionFlags.PrefixF2))
{
WriteByte(0xf2);
}
if ((flags & InstructionFlags.PrefixF3) != 0f)
if (flags.HasFlag(InstructionFlags.PrefixF3))
{
WriteByte(0xf3);
}
@@ -1325,8 +1324,8 @@ namespace ARMeilleure.CodeGen.X86
public (byte[], RelocInfo) GetCode()
{
Span<Jump> jumps = CollectionsMarshal.AsSpan(_jumps);
Span<Reloc> relocs = CollectionsMarshal.AsSpan(_relocs);
var jumps = CollectionsMarshal.AsSpan(_jumps);
var relocs = CollectionsMarshal.AsSpan(_relocs);
// Write jump relative offsets.
bool modified;
@@ -1411,13 +1410,13 @@ namespace ARMeilleure.CodeGen.X86
// Write the code, ignoring the dummy bytes after jumps, into a new stream.
_stream.Seek(0, SeekOrigin.Begin);
using RecyclableMemoryStream codeStream = MemoryStreamManager.Shared.GetStream();
Assembler assembler = new(codeStream, HasRelocs);
using var codeStream = MemoryStreamManager.Shared.GetStream();
var assembler = new Assembler(codeStream, HasRelocs);
bool hasRelocs = HasRelocs;
int relocIndex = 0;
int relocOffset = 0;
RelocEntry[] relocEntries = hasRelocs
var relocEntries = hasRelocs
? new RelocEntry[relocs.Length]
: Array.Empty<RelocEntry>();
@@ -1470,15 +1469,15 @@ namespace ARMeilleure.CodeGen.X86
_stream.CopyTo(codeStream);
byte[] code = codeStream.ToArray();
RelocInfo relocInfo = new(relocEntries);
var code = codeStream.ToArray();
var relocInfo = new RelocInfo(relocEntries);
return (code, relocInfo);
}
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)
+7 -6
View File
@@ -175,7 +175,8 @@ namespace ARMeilleure.CodeGen.X86
// The only blocks which can have 0 successors are exit blocks.
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
{
@@ -477,7 +478,7 @@ namespace ARMeilleure.CodeGen.X86
Debug.Assert(HardwareCapabilities.SupportsVexEncoding);
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);
Debug.Assert(dest.Type == OperandType.V128);
@@ -622,7 +623,7 @@ namespace ARMeilleure.CodeGen.X86
Debug.Assert(comp.Kind == OperandKind.Constant);
X86Condition cond = ((Comparison)comp.AsInt32()).ToX86Condition();
var cond = ((Comparison)comp.AsInt32()).ToX86Condition();
GenerateCompareCommon(context, operation);
@@ -660,7 +661,7 @@ namespace ARMeilleure.CodeGen.X86
Debug.Assert(dest.Type == OperandType.I32);
Debug.Assert(comp.Kind == OperandKind.Constant);
X86Condition cond = ((Comparison)comp.AsInt32()).ToX86Condition();
var cond = ((Comparison)comp.AsInt32()).ToX86Condition();
GenerateCompareCommon(context, operation);
@@ -787,7 +788,7 @@ namespace ARMeilleure.CodeGen.X86
Operand dest = operation.Destination;
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)
{
@@ -1722,7 +1723,7 @@ namespace ARMeilleure.CodeGen.X86
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.Value == op2.Value);
}
@@ -53,7 +53,7 @@ namespace ARMeilleure.CodeGen.X86
memGetXcr0.Reprotect(0, (ulong)asmGetXcr0.Length, MemoryPermission.ReadAndExecute);
GetXcr0 fGetXcr0 = Marshal.GetDelegateForFunctionPointer<GetXcr0>(memGetXcr0.Pointer);
var fGetXcr0 = Marshal.GetDelegateForFunctionPointer<GetXcr0>(memGetXcr0.Pointer);
return fGetXcr0();
}
+7 -7
View File
@@ -312,9 +312,9 @@ namespace ARMeilleure.CodeGen.X86
case Instruction.Extended:
{
bool isBlend = node.Intrinsic is Intrinsic.X86Blendvpd
or Intrinsic.X86Blendvps
or Intrinsic.X86Pblendvb;
bool isBlend = node.Intrinsic == Intrinsic.X86Blendvpd ||
node.Intrinsic == Intrinsic.X86Blendvps ||
node.Intrinsic == Intrinsic.X86Pblendvb;
// 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.
@@ -513,8 +513,8 @@ namespace ARMeilleure.CodeGen.X86
Operand dest = node.Destination;
Operand source = node.GetSource(0);
Debug.Assert(dest.Type is OperandType.FP32 or OperandType.FP64,
$"Invalid destination type \"{dest.Type}\".");
Debug.Assert(dest.Type == OperandType.FP32 ||
dest.Type == OperandType.FP64, $"Invalid destination type \"{dest.Type}\".");
Operation currentNode = node;
@@ -759,9 +759,9 @@ namespace ARMeilleure.CodeGen.X86
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;
}
}
+8 -8
View File
@@ -13,13 +13,13 @@ namespace ARMeilleure.CodeGen.X86
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)
{
// 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).
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);
@@ -248,12 +248,12 @@ namespace ARMeilleure.CodeGen.X86
private static bool IsMemoryLoadOrStore(Instruction inst)
{
return inst is Instruction.Load
or Instruction.Load16
or Instruction.Load8
or Instruction.Store
or Instruction.Store16
or Instruction.Store8;
return inst == Instruction.Load ||
inst == Instruction.Load16 ||
inst == Instruction.Load8 ||
inst == Instruction.Store ||
inst == Instruction.Store16 ||
inst == Instruction.Store8;
}
}
}
+4 -4
View File
@@ -20,7 +20,7 @@ namespace ARMeilleure.Common
private List<PageInfo> _pages;
private readonly ulong _pageSize;
private readonly uint _pageCount;
private readonly List<nint> _extras;
private readonly List<IntPtr> _extras;
public ArenaAllocator(uint pageSize, uint pageCount)
{
@@ -64,7 +64,7 @@ namespace ARMeilleure.Common
{
void* extra = NativeAllocator.Instance.Allocate(size);
_extras.Add((nint)extra);
_extras.Add((IntPtr)extra);
return extra;
}
@@ -114,7 +114,7 @@ namespace ARMeilleure.Common
}
// Free extra blocks that are not page-sized
foreach (nint ptr in _extras)
foreach (IntPtr ptr in _extras)
{
NativeAllocator.Instance.Free((void*)ptr);
}
@@ -173,7 +173,7 @@ namespace ARMeilleure.Common
NativeAllocator.Instance.Free(info.Pointer);
}
foreach (nint ptr in _extras)
foreach (IntPtr ptr in _extras)
{
NativeAllocator.Instance.Free((void*)ptr);
}
+3 -3
View File
@@ -129,13 +129,13 @@ namespace ARMeilleure.Common
if (count > _count)
{
long* oldMask = _masks;
Span<long> oldSpan = new(_masks, _count);
var oldMask = _masks;
var oldSpan = new Span<long>(_masks, _count);
_masks = _allocator.Allocate<long>((uint)count);
_count = count;
Span<long> newSpan = new(_masks, _count);
var newSpan = new Span<long>(_masks, _count);
oldSpan.CopyTo(newSpan);
newSpan[oldSpan.Length..].Clear();
+8 -8
View File
@@ -15,7 +15,7 @@ namespace ARMeilleure.Common
private int _freeHint;
private readonly int _pageCapacity; // Number of entries per page.
private readonly int _pageLogCapacity;
private readonly Dictionary<int, nint> _pages;
private readonly Dictionary<int, IntPtr> _pages;
private readonly BitMap _allocated;
/// <summary>
@@ -41,7 +41,7 @@ namespace ARMeilleure.Common
}
_allocated = new BitMap(NativeAllocator.Instance);
_pages = new Dictionary<int, nint>();
_pages = new Dictionary<int, IntPtr>();
_pageLogCapacity = BitOperations.Log2((uint)(pageSize / sizeof(TEntry)));
_pageCapacity = 1 << _pageLogCapacity;
}
@@ -63,7 +63,7 @@ namespace ARMeilleure.Common
}
int index = _freeHint++;
Span<TEntry> page = GetPage(index);
var page = GetPage(index);
_allocated.Set(index);
@@ -111,7 +111,7 @@ namespace ARMeilleure.Common
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);
}
@@ -136,11 +136,11 @@ namespace ARMeilleure.Common
/// <returns>Page for the specified <see cref="index"/></returns>
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);
}
@@ -168,7 +168,7 @@ namespace ARMeilleure.Common
{
_allocated.Dispose();
foreach (IntPtr page in _pages.Values)
foreach (var page in _pages.Values)
{
NativeAllocator.Instance.Free((void*)page);
}
+2 -2
View File
@@ -9,7 +9,7 @@ namespace ARMeilleure.Common
public override void* Allocate(ulong size)
{
void* result = (void*)Marshal.AllocHGlobal((nint)size);
void* result = (void*)Marshal.AllocHGlobal((IntPtr)size);
if (result == null)
{
@@ -21,7 +21,7 @@ namespace ARMeilleure.Common
public override void Free(void* block)
{
Marshal.FreeHGlobal((nint)block);
Marshal.FreeHGlobal((IntPtr)block);
}
}
}
+11 -5
View File
@@ -254,7 +254,8 @@ namespace ARMeilleure.Decoders
}
// 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;
}
@@ -283,7 +284,7 @@ namespace ARMeilleure.Decoders
// 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
// 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;
@@ -325,12 +326,15 @@ namespace ARMeilleure.Decoders
}
// Explicit branch instructions.
return opCode is IOpCode32BImm or IOpCode32BReg;
return opCode is IOpCode32BImm ||
opCode is IOpCode32BReg;
}
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)
@@ -340,7 +344,9 @@ namespace ARMeilleure.Decoders
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)
@@ -9,7 +9,7 @@ namespace ARMeilleure.Decoders
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)
{
@@ -21,7 +21,7 @@ namespace ARMeilleure.Decoders
Op = (opCode >> 20) & 0x1;
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)
{
+1 -1
View File
@@ -20,7 +20,7 @@ namespace ARMeilleure.Decoders
}
else if (DataOp == DataOp.Logical)
{
DecoderHelper.BitMask bm = DecoderHelper.DecodeBitMask(opCode, true);
var bm = DecoderHelper.DecodeBitMask(opCode, true);
if (bm.IsUndefined)
{
+1 -1
View File
@@ -11,7 +11,7 @@ namespace ARMeilleure.Decoders
public OpCodeBfm(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
DecoderHelper.BitMask bm = DecoderHelper.DecodeBitMask(opCode, false);
var bm = DecoderHelper.DecodeBitMask(opCode, false);
if (bm.IsUndefined)
{
+2 -1
View File
@@ -28,7 +28,8 @@ namespace ARMeilleure.Decoders
MemOp type = WBack ? (MemOp)((opCode >> 10) & 3) : MemOp.Unsigned;
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,
// 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.
for (int i = 0; i < blocks.Count; i++)
+4 -3
View File
@@ -141,7 +141,7 @@ namespace ARMeilleure.Diagnostics
break;
case OperandKind.Memory:
MemoryOperand memOp = operand.GetMemory();
var memOp = operand.GetMemory();
_builder.Append('[');
@@ -235,7 +235,8 @@ namespace ARMeilleure.Diagnostics
{
_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;
}
@@ -284,7 +285,7 @@ namespace ARMeilleure.Diagnostics
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)
{
@@ -415,7 +415,7 @@ namespace ARMeilleure.Instructions
{
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 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;
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 res = context.ShiftRightUI(context.ShiftLeft(n, Const(31 - msb)), Const(31 - op.Msb));
@@ -19,7 +19,7 @@ namespace ARMeilleure.Instructions
context.LoadFromContext();
InstEmitFlowHelper.EmitReturn(context, Const(op.Address));
context.Return(Const(op.Address));
}
public static void Svc(ArmEmitterContext context)
@@ -49,7 +49,7 @@ namespace ARMeilleure.Instructions
context.LoadFromContext();
InstEmitFlowHelper.EmitReturn(context, Const(op.Address));
context.Return(Const(op.Address));
}
}
}
@@ -33,7 +33,7 @@ namespace ARMeilleure.Instructions
context.LoadFromContext();
InstEmitFlowHelper.EmitReturn(context, Const(context.CurrOp.Address));
context.Return(Const(context.CurrOp.Address));
}
}
}
+1 -1
View File
@@ -66,7 +66,7 @@ namespace ARMeilleure.Instructions
{
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);
@@ -1,5 +1,4 @@
using ARMeilleure.CodeGen.Linking;
using ARMeilleure.Common;
using ARMeilleure.Decoders;
using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.State;
@@ -13,10 +12,6 @@ namespace ARMeilleure.Instructions
{
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)
{
if (cond != Condition.Al)
@@ -168,7 +163,12 @@ namespace ARMeilleure.Instructions
{
if (isReturn)
{
EmitReturn(context, target);
if (target.Type == OperandType.I32)
{
target = context.ZeroExtend32(OperandType.I64, target);
}
context.Return(target);
}
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)
{
context.StoreToContext();
@@ -206,7 +193,7 @@ namespace ARMeilleure.Instructions
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
// onto the dispatch stub.
@@ -231,7 +218,7 @@ namespace ARMeilleure.Instructions
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);
Operand index = context.ShiftLeft(
@@ -251,8 +238,6 @@ namespace ARMeilleure.Instructions
if (isJump)
{
DecreaseCallDepth(context, nativeContext);
context.Tailcall(hostAddress, nativeContext);
}
else
@@ -274,42 +259,8 @@ namespace ARMeilleure.Instructions
Operand lblContinue = context.GetLabel(nextAddr.Value);
context.BranchIf(lblContinue, returnAddress, nextAddr, Comparison.Equal, BasicBlockFrequency.Cold);
DecreaseCallDepth(context, nativeContext);
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)
{
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);
@@ -143,8 +143,8 @@ namespace ARMeilleure.Instructions
Operand address = context.Copy(GetIntA32(context, op.Rn));
bool exclusive = (accType & AccessType.Exclusive) != 0;
bool ordered = (accType & AccessType.Ordered) != 0;
var exclusive = (accType & AccessType.Exclusive) != 0;
var ordered = (accType & AccessType.Ordered) != 0;
if ((accType & AccessType.Load) != 0)
{
@@ -59,7 +59,7 @@ namespace ARMeilleure.Instructions
{
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;
@@ -124,7 +124,8 @@ namespace ARMeilleure.Instructions
private static bool IsSimd(ArmEmitterContext context)
{
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)
@@ -229,7 +229,7 @@ namespace ARMeilleure.Instructions
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));
}
@@ -1119,7 +1119,7 @@ namespace ARMeilleure.Instructions
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);
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)
{
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);
@@ -1160,7 +1160,7 @@ namespace ARMeilleure.Instructions
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);
@@ -1184,7 +1184,7 @@ namespace ARMeilleure.Instructions
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)
{
@@ -1203,7 +1203,7 @@ namespace ARMeilleure.Instructions
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)
{
@@ -118,15 +118,15 @@ namespace ARMeilleure.Instructions
{
OpCode32SimdCvtFFixed op = (OpCode32SimdCvtFFixed)context.CurrOp;
bool toFixed = op.Opc == 1;
var toFixed = op.Opc == 1;
int fracBits = op.Fbits;
bool unsigned = op.U;
var unsigned = op.U;
if (toFixed) // F32 to S32 or U32 (fixed)
{
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));
return context.Call(info, scaledValue);
@@ -136,7 +136,7 @@ namespace ARMeilleure.Instructions
{
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)));
}, !unsigned);
@@ -635,7 +635,7 @@ namespace ARMeilleure.Instructions
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)
{
@@ -363,7 +363,7 @@ namespace ARMeilleure.Instructions
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)));
@@ -489,7 +489,7 @@ namespace ARMeilleure.Instructions
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 lbl2 = Label();
@@ -1676,7 +1676,7 @@ namespace ARMeilleure.Instructions
int eSize = 8 << size;
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 lblEnd = Label();
@@ -1709,7 +1709,7 @@ namespace ARMeilleure.Instructions
int eSize = 8 << size;
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();
@@ -1735,7 +1735,7 @@ namespace ARMeilleure.Instructions
int eSizeDst = 8 << sizeDst;
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 lblEnd = Label();
@@ -1768,7 +1768,7 @@ namespace ARMeilleure.Instructions
int eSizeDst = 8 << sizeDst;
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();
@@ -31,7 +31,7 @@ namespace ARMeilleure.Instructions
{
Debug.Assert(type != OperandType.V128);
if (type is OperandType.FP64 or OperandType.I64)
if (type == OperandType.FP64 || type == OperandType.I64)
{
// From dreg.
return context.VectorExtract(type, GetVecA32(reg >> 1), reg & 1);
@@ -48,7 +48,7 @@ namespace ARMeilleure.Instructions
Debug.Assert(value.Type != OperandType.V128);
Operand vec, insert;
if (value.Type is OperandType.FP64 or OperandType.I64)
if (value.Type == OperandType.FP64 || value.Type == OperandType.I64)
{
// From dreg.
vec = GetVecA32(reg >> 1);
@@ -71,7 +71,7 @@ namespace ARMeilleure.Instructions
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;
vec = GetVecA32(reg >> 2);
@@ -87,7 +87,7 @@ namespace ARMeilleure.Instructions
{
if (op.Replicate)
{
int regs = (count > 1) ? 1 : op.Increment;
var regs = (count > 1) ? 1 : op.Increment;
for (int reg = 0; reg < regs; reg++)
{
int dreg = reg + d;
@@ -1634,7 +1634,7 @@ namespace ARMeilleure.Instructions
int eSize = 8 << size;
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);
@@ -1657,7 +1657,7 @@ namespace ARMeilleure.Instructions
int eSize = 8 << size;
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();
@@ -1732,7 +1732,7 @@ namespace ARMeilleure.Instructions
Debug.Assert(op.Type == OperandType.I64);
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 lblEnd = Label();
@@ -1769,7 +1769,7 @@ namespace ARMeilleure.Instructions
Debug.Assert(op.Type == OperandType.I64);
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 lbl2 = Label();
+14 -14
View File
@@ -330,7 +330,7 @@ namespace ARMeilleure.Instructions
float result;
if (type is FPType.SNaN or FPType.QNaN)
if (type == FPType.SNaN || type == FPType.QNaN)
{
if ((context.Fpcr & FPCR.Dn) != 0)
{
@@ -510,7 +510,7 @@ namespace ARMeilleure.Instructions
double result;
if (type is FPType.SNaN or FPType.QNaN)
if (type == FPType.SNaN || type == FPType.QNaN)
{
if ((context.Fpcr & FPCR.Dn) != 0)
{
@@ -692,7 +692,7 @@ namespace ARMeilleure.Instructions
ushort resultBits;
if (type is FPType.SNaN or FPType.QNaN)
if (type == FPType.SNaN || type == FPType.QNaN)
{
if (altHp)
{
@@ -1670,7 +1670,7 @@ namespace ARMeilleure.Instructions
float result;
if (type is FPType.SNaN or FPType.QNaN)
if (type == FPType.SNaN || type == FPType.QNaN)
{
result = FPProcessNaN(type, op, context, fpcr);
}
@@ -1686,7 +1686,7 @@ namespace ARMeilleure.Instructions
}
else if (MathF.Abs(value) < MathF.Pow(2f, -128))
{
bool overflowToInf = fpcr.GetRoundingMode() switch
var overflowToInf = fpcr.GetRoundingMode() switch
{
FPRoundingMode.ToNearest => true,
FPRoundingMode.TowardsPlusInfinity => !sign,
@@ -1849,7 +1849,7 @@ namespace ARMeilleure.Instructions
float result;
if (type is FPType.SNaN or FPType.QNaN)
if (type == FPType.SNaN || type == FPType.QNaN)
{
result = FPProcessNaN(type, op, context, fpcr);
}
@@ -1894,7 +1894,7 @@ namespace ARMeilleure.Instructions
float result;
if (type is FPType.SNaN or FPType.QNaN)
if (type == FPType.SNaN || type == FPType.QNaN)
{
result = FPProcessNaN(type, op, context, fpcr);
}
@@ -2100,7 +2100,7 @@ namespace ARMeilleure.Instructions
float result;
if (type is FPType.SNaN or FPType.QNaN)
if (type == FPType.SNaN || type == FPType.QNaN)
{
result = FPProcessNaN(type, op, context, fpcr);
}
@@ -2399,7 +2399,7 @@ namespace ARMeilleure.Instructions
ushort resultBits;
if (type is FPType.SNaN or FPType.QNaN)
if (type == FPType.SNaN || type == FPType.QNaN)
{
if (altHp)
{
@@ -3377,7 +3377,7 @@ namespace ARMeilleure.Instructions
double result;
if (type is FPType.SNaN or FPType.QNaN)
if (type == FPType.SNaN || type == FPType.QNaN)
{
result = FPProcessNaN(type, op, context, fpcr);
}
@@ -3393,7 +3393,7 @@ namespace ARMeilleure.Instructions
}
else if (Math.Abs(value) < Math.Pow(2d, -1024))
{
bool overflowToInf = fpcr.GetRoundingMode() switch
var overflowToInf = fpcr.GetRoundingMode() switch
{
FPRoundingMode.ToNearest => true,
FPRoundingMode.TowardsPlusInfinity => !sign,
@@ -3556,7 +3556,7 @@ namespace ARMeilleure.Instructions
double result;
if (type is FPType.SNaN or FPType.QNaN)
if (type == FPType.SNaN || type == FPType.QNaN)
{
result = FPProcessNaN(type, op, context, fpcr);
}
@@ -3601,7 +3601,7 @@ namespace ARMeilleure.Instructions
double result;
if (type is FPType.SNaN or FPType.QNaN)
if (type == FPType.SNaN || type == FPType.QNaN)
{
result = FPProcessNaN(type, op, context, fpcr);
}
@@ -3807,7 +3807,7 @@ namespace ARMeilleure.Instructions
double result;
if (type is FPType.SNaN or FPType.QNaN)
if (type == FPType.SNaN || type == FPType.QNaN)
{
result = FPProcessNaN(type, op, context, fpcr);
}
@@ -32,7 +32,7 @@ namespace ARMeilleure.IntermediateRepresentation
/// <exception cref="ArgumentException"><typeparamref name="T"/> is not pointer sized.</exception>
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.");
}
@@ -1,3 +1,4 @@
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
@@ -23,7 +24,7 @@ namespace ARMeilleure.IntermediateRepresentation
{
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
@@ -304,7 +304,7 @@ namespace ARMeilleure.IntermediateRepresentation
ushort newCount = checked((ushort)(count + 1));
ushort newCapacity = (ushort)Math.Min(capacity * 2, ushort.MaxValue);
Span<T> oldSpan = new(data, count);
var oldSpan = new Span<T>(data, count);
capacity = newCapacity;
data = Allocators.References.Allocate<T>(capacity);
@@ -338,7 +338,7 @@ namespace ARMeilleure.IntermediateRepresentation
throw new OverflowException();
}
Span<T> oldSpan = new(data, (int)count);
var oldSpan = new Span<T>(data, (int)count);
capacity = newCapacity;
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
{
Span<T> span = new(data, count);
var span = new Span<T>(data, count);
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
{
Span<T> span = new(data, (int)count);
var span = new Span<T>(data, (int)count);
for (int i = 0; i < span.Length; i++)
{
@@ -446,7 +446,7 @@ namespace ARMeilleure.IntermediateRepresentation
Data* data = null;
// 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);
@@ -16,7 +16,8 @@ namespace ARMeilleure.IntermediateRepresentation
{
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)
@@ -228,7 +228,7 @@ namespace ARMeilleure.IntermediateRepresentation
public readonly override int GetHashCode()
{
return HashCode.Combine((nint)_data);
return HashCode.Combine((IntPtr)_data);
}
public static bool operator ==(Operation a, Operation b)
+1 -1
View File
@@ -4,7 +4,7 @@ namespace ARMeilleure.Memory
{
public interface IJitMemoryBlock : IDisposable
{
nint Pointer { get; }
IntPtr Pointer { get; }
void Commit(ulong offset, ulong size);
+1 -1
View File
@@ -6,7 +6,7 @@ namespace ARMeilleure.Memory
{
int AddressSpaceBits { get; }
nint PageTablePointer { get; }
IntPtr PageTablePointer { get; }
MemoryManagerType Type { get; }
+2 -2
View File
@@ -47,12 +47,12 @@ namespace ARMeilleure.Memory
{
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)
{
return type is MemoryManagerType.HostTracked or MemoryManagerType.HostTrackedUnsafe;
return type == MemoryManagerType.HostTracked || type == MemoryManagerType.HostTrackedUnsafe;
}
public static bool IsHostMappedOrTracked(this MemoryManagerType type)
+1 -1
View File
@@ -9,7 +9,7 @@ namespace ARMeilleure.Memory
public IJitMemoryBlock Block { get; }
public IJitMemoryAllocator Allocator { get; }
public nint Pointer => Block.Pointer;
public IntPtr Pointer => Block.Pointer;
private readonly ulong _maxSize;
private readonly ulong _sizeGranularity;
+2 -1
View File
@@ -1,3 +1,4 @@
using System;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
@@ -7,6 +8,6 @@ namespace ARMeilleure.Native
static partial class JitSupportDarwin
{
[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);
}
}
-4
View File
@@ -8,14 +8,10 @@ namespace ARMeilleure
// low-core count PPTC
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 AllowLcqInFunctionTable { 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 UseArm64AesIfAvailable { get; set; } = true;
@@ -8,7 +8,7 @@ namespace ARMeilleure.Signal
{
public static class NativeSignalHandlerGenerator
{
public const int MaxTrackedRanges = 8;
public const int MaxTrackedRanges = 16;
private const int StructAddressOffset = 0;
private const int StructWriteOffset = 4;
@@ -21,7 +21,7 @@ namespace ARMeilleure.Signal
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);
context.Copy(inRegionLocal, Const(0));
@@ -155,7 +155,7 @@ namespace ARMeilleure.Signal
throw new PlatformNotSupportedException();
}
public static byte[] GenerateUnixSignalHandler(nint signalStructPtr, int rangeStructSize)
public static byte[] GenerateUnixSignalHandler(IntPtr signalStructPtr, int rangeStructSize)
{
EmitterContext context = new();
@@ -203,7 +203,7 @@ namespace ARMeilleure.Signal
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();
+5 -4
View File
@@ -1,5 +1,6 @@
using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.Translation;
using System;
using System.Runtime.InteropServices;
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
@@ -15,13 +16,13 @@ namespace ARMeilleure.Signal
{
public delegate bool DebugPartialUnmap();
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()
{
EmitterContext context = new();
Operand result = WindowsPartialUnmapHandler.EmitRetryFromAccessViolation(context);
var result = WindowsPartialUnmapHandler.EmitRetryFromAccessViolation(context);
context.Return(result);
@@ -34,11 +35,11 @@ namespace ARMeilleure.Signal
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();
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);
@@ -1,6 +1,7 @@
using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.Translation;
using Ryujinx.Common.Memory.PartialUnmaps;
using System;
using System.Runtime.InteropServices;
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
@@ -12,18 +13,18 @@ namespace ARMeilleure.Signal
internal static partial class WindowsPartialUnmapHandler
{
[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)]
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");
}
@@ -33,13 +34,13 @@ namespace ARMeilleure.Signal
public static Operand EmitRetryFromAccessViolation(EmitterContext context)
{
nint partialRemapStatePtr = PartialUnmapState.GlobalState;
nint localCountsPtr = nint.Add(partialRemapStatePtr, PartialUnmapState.LocalCountsOffset);
IntPtr partialRemapStatePtr = PartialUnmapState.GlobalState;
IntPtr localCountsPtr = IntPtr.Add(partialRemapStatePtr, PartialUnmapState.LocalCountsOffset);
// 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 threadIndex = EmitThreadLocalMapIntGetOrReserve(context, localCountsPtr, threadId, Const(0));
@@ -57,7 +58,7 @@ namespace ARMeilleure.Signal
Operand threadLocalPartialUnmapsPtr = EmitThreadLocalMapIntGetValuePtr(context, localCountsPtr, threadIndex);
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));
@@ -78,14 +79,14 @@ namespace ARMeilleure.Signal
context.MarkLabel(endLabel);
// 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;
}
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);
@@ -129,7 +130,7 @@ namespace ARMeilleure.Signal
// If it was 0, then we need to initialize the struct entry and return i.
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));
context.Store(structPtr, initialState);
@@ -148,10 +149,10 @@ namespace ARMeilleure.Signal
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 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));
}
@@ -169,9 +170,9 @@ namespace ARMeilleure.Signal
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.
Operand spinLabel = Label();
@@ -181,16 +182,16 @@ namespace ARMeilleure.Signal
context.BranchIfTrue(spinLabel, context.CompareAndSwap(writeLockPtr, Const(0), Const(1)));
// 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.
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.
EmitAtomicAddI32(context, Const((ulong)nint.Add(nativeReaderLockPtr, NativeReaderWriterLock.ReaderCountOffset)), Const(-1));
EmitAtomicAddI32(context, Const((ulong)IntPtr.Add(nativeReaderLockPtr, NativeReaderWriterLock.ReaderCountOffset)), Const(-1));
}
}
}
+2 -6
View File
@@ -1,4 +1,5 @@
using ARMeilleure.Memory;
using System;
namespace ARMeilleure.State
{
@@ -8,7 +9,7 @@ namespace ARMeilleure.State
private readonly NativeContext _nativeContext;
internal nint NativeContextPtr => _nativeContext.BasePtr;
internal IntPtr NativeContextPtr => _nativeContext.BasePtr;
private bool _interrupted;
@@ -127,11 +128,6 @@ namespace ARMeilleure.State
public bool GetFPstateFlag(FPState flag) => _nativeContext.GetFPStateFlag(flag);
public void SetFPstateFlag(FPState flag, bool value) => _nativeContext.SetFPStateFlag(flag, value);
internal void ResetCallDepth()
{
_nativeContext.ResetCallDepth();
}
internal void CheckInterrupt()
{
if (_interrupted)
+1 -9
View File
@@ -21,7 +21,6 @@ namespace ARMeilleure.State
public ulong ExclusiveValueLow;
public ulong ExclusiveValueHigh;
public int Running;
public int CallDepth;
public long Tpidr2El0;
}
@@ -29,7 +28,7 @@ namespace ARMeilleure.State
private readonly IJitMemoryBlock _block;
public nint BasePtr => _block.Pointer;
public IntPtr BasePtr => _block.Pointer;
public NativeContext(IJitMemoryAllocator allocator)
{
@@ -187,8 +186,6 @@ namespace ARMeilleure.State
public bool GetRunning() => GetStorage().Running != 0;
public void SetRunning(bool value) => GetStorage().Running = value ? 1 : 0;
public void ResetCallDepth() => GetStorage().CallDepth = 0;
public unsafe static int GetRegisterOffset(Register reg)
{
if (reg.Type == RegisterType.Integer)
@@ -269,11 +266,6 @@ namespace ARMeilleure.State
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)
{
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.Memory;
using ARMeilleure.State;
using System;
using System.Collections.Generic;
using System.Reflection;
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
@@ -91,7 +92,7 @@ namespace ARMeilleure.Translation
else
{
int index = Delegates.GetDelegateIndex(info);
nint funcPtr = Delegates.GetDelegateFuncPtrByIndex(index);
IntPtr funcPtr = Delegates.GetDelegateFuncPtrByIndex(index);
OperandType returnType = GetOperandType(info.ReturnType);
+91 -204
View File
@@ -2,12 +2,12 @@ using ARMeilleure.CodeGen;
using ARMeilleure.CodeGen.Unwinding;
using ARMeilleure.Memory;
using ARMeilleure.Native;
using Humanizer;
using Ryujinx.Common.Logging;
using Ryujinx.Memory;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Threading;
@@ -19,123 +19,83 @@ namespace ARMeilleure.Translation.Cache
private static readonly int _pageSize = (int)MemoryBlock.GetPageSize();
private static readonly int _pageMask = _pageSize - 1;
private const int CodeAlignment = 4;
private const int FullCacheSize = 2047 * 1024 * 1024;
private const int ReducedCacheSize = FullCacheSize / 8;
private const int CodeAlignment = 4; // Bytes.
private const int CacheSize = 256 * 1024 * 1024;
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 CacheMemoryAllocator _cacheAllocator;
private static List<CacheMemoryAllocator> _cacheAllocators = [];
private static readonly List<CacheEntry> _cacheEntries = [];
private static readonly Dictionary<int, EntryUsageStats> _entryUsageStats = [];
private static readonly Lock _lock = new();
private static bool _initialized;
private static int _cacheSize;
// Basic statistics
private static int _totalAllocations = 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++;
}
}
private static readonly List<ReservedRegion> _jitRegions = [];
private static int _activeRegionIndex = 0;
[SupportedOSPlatform("windows")]
[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)
{
if (_initialized)
{
return;
}
lock (_lock)
{
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;
_jitRegion = new ReservedRegion(allocator, (ulong)_cacheSize);
_activeRegionIndex = 0;
var firstRegion = new ReservedRegion(allocator, CacheSize);
_jitRegions.Add(firstRegion);
CacheMemoryAllocator firstCacheAllocator = new(CacheSize);
_cacheAllocators.Add(firstCacheAllocator);
if (!OperatingSystem.IsWindows() && !OperatingSystem.IsMacOS())
{
_jitCacheInvalidator = new JitCacheInvalidation(allocator);
}
_cacheAllocator = new CacheMemoryAllocator(_cacheSize);
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;
lock (_lock)
{
Debug.Assert(_initialized);
_totalAllocations++;
int funcOffset;
if (Optimizations.CacheEviction)
{
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;
int funcOffset = Allocate(code.Length);
ReservedRegion targetRegion = _jitRegions[_activeRegionIndex];
IntPtr funcPtr = targetRegion.Pointer + funcOffset;
if (OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
{
@@ -143,19 +103,19 @@ namespace ARMeilleure.Translation.Cache
{
fixed (byte* codePtr = code)
{
JitSupportDarwin.Copy(funcPtr, (nint)codePtr, (ulong)code.Length);
JitSupportDarwin.Copy(funcPtr, (IntPtr)codePtr, (ulong)code.Length);
}
}
}
else
{
ReprotectAsWritable(funcOffset, code.Length);
ReprotectAsWritable(targetRegion, funcOffset, 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)
{
FlushInstructionCache(Process.GetCurrentProcess().Handle, funcPtr, (nuint)code.Length);
FlushInstructionCache(Process.GetCurrentProcess().Handle, funcPtr, (UIntPtr)code.Length);
}
else
{
@@ -165,71 +125,84 @@ namespace ARMeilleure.Translation.Cache
Add(funcOffset, code.Length, func.UnwindInfo);
// Simple periodic logging
if (_totalAllocations % LogInterval == 0)
{
LogCacheStatus();
}
return funcPtr;
}
}
public static void Unmap(nint pointer)
public static void Unmap(IntPtr pointer)
{
lock (_lock)
{
Debug.Assert(_initialized);
int funcOffset = (int)(pointer.ToInt64() - _jitRegion.Pointer.ToInt64());
if (TryFind(funcOffset, out CacheEntry entry, out int entryIndex) && entry.Offset == funcOffset)
foreach (var region in _jitRegions)
{
_cacheAllocator.Free(funcOffset, AlignCodeSize(entry.Size));
_cacheEntries.RemoveAt(entryIndex);
if (Optimizations.CacheEviction)
if (pointer.ToInt64() < region.Pointer.ToInt64() ||
pointer.ToInt64() >= (region.Pointer + CacheSize).ToInt64())
{
_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 regionStart = offset & ~_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 regionStart = offset & ~_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)
{
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)
@@ -249,35 +222,27 @@ namespace ARMeilleure.Translation.Cache
}
_cacheEntries.Insert(index, entry);
if (Optimizations.CacheEviction)
{
_entryUsageStats[offset] = new EntryUsageStats();
}
}
public static bool TryFind(int offset, out CacheEntry entry, out int entryIndex)
{
lock (_lock)
{
int index = _cacheEntries.BinarySearch(new CacheEntry(offset, 0, default));
if (index < 0)
foreach (var region in _jitRegions)
{
index = ~index - 1;
}
int index = _cacheEntries.BinarySearch(new CacheEntry(offset, 0, default));
if (index >= 0)
{
entry = _cacheEntries[index];
if (Optimizations.CacheEviction && _entryUsageStats.TryGetValue(offset, out EntryUsageStats stats))
if (index < 0)
{
stats.UpdateUsage();
index = ~index - 1;
}
entryIndex = index;
return true;
if (index >= 0)
{
entry = _cacheEntries[index];
entryIndex = index;
return true;
}
}
}
@@ -285,83 +250,5 @@ namespace ARMeilleure.Translation.Cache
entryIndex = 0;
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 System;
using System.Runtime.InteropServices;
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)
{
@@ -40,7 +40,7 @@ namespace ARMeilleure.Translation.Cache
PushMachframe = 10,
}
private unsafe delegate RuntimeFunction* GetRuntimeFunctionCallback(ulong controlPc, nint context);
private unsafe delegate RuntimeFunction* GetRuntimeFunctionCallback(ulong controlPc, IntPtr context);
[LibraryImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
@@ -49,7 +49,7 @@ namespace ARMeilleure.Translation.Cache
ulong baseAddress,
uint length,
GetRuntimeFunctionCallback callback,
nint context,
IntPtr context,
[MarshalAs(UnmanagedType.LPWStr)] string outOfProcessCallbackDll);
[LibraryImport("kernel32.dll")]
@@ -65,7 +65,7 @@ namespace ARMeilleure.Translation.Cache
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();
@@ -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();
@@ -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());
@@ -122,13 +122,13 @@ namespace ARMeilleure.Translation.Cache
return null; // Not found.
}
CodeGen.Unwinding.UnwindInfo unwindInfo = funcEntry.UnwindInfo;
var unwindInfo = funcEntry.UnwindInfo;
int codeIndex = 0;
for (int index = unwindInfo.PushEntries.Length - 1; index >= 0; index--)
{
UnwindPushEntry entry = unwindInfo.PushEntries[index];
var entry = unwindInfo.PushEntries[index];
switch (entry.PseudoOp)
{
@@ -47,8 +47,8 @@ namespace ARMeilleure.Translation
{
RemoveUnreachableBlocks(Blocks);
HashSet<BasicBlock> visited = new();
Stack<BasicBlock> blockStack = new();
var visited = new HashSet<BasicBlock>();
var blockStack = new Stack<BasicBlock>();
Array.Resize(ref _postOrderBlocks, Blocks.Count);
Array.Resize(ref _postOrderMap, Blocks.Count);
@@ -88,8 +88,8 @@ namespace ARMeilleure.Translation
private void RemoveUnreachableBlocks(IntrusiveList<BasicBlock> blocks)
{
HashSet<BasicBlock> visited = new();
Queue<BasicBlock> workQueue = new();
var visited = new HashSet<BasicBlock>();
var workQueue = new Queue<BasicBlock>();
visited.Add(Entry);
workQueue.Enqueue(Entry);
+4 -2
View File
@@ -1,10 +1,12 @@
using System;
namespace ARMeilleure.Translation
{
public class DelegateInfo
{
public nint FuncPtr { get; }
public IntPtr FuncPtr { get; }
public DelegateInfo(nint funcPtr)
public DelegateInfo(IntPtr funcPtr)
{
FuncPtr = funcPtr;
}
+3 -3
View File
@@ -354,7 +354,7 @@ namespace ARMeilleure.Translation
private static readonly Dictionary<string, Delegate> _androidDelegates;
#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)
{
@@ -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)
{
@@ -403,7 +403,7 @@ namespace ARMeilleure.Translation
#if ANDROID
if (_androidDelegates.TryGetValue(key, out Delegate del))
{
nint funcPtr = Marshal.GetFunctionPointerForDelegate(del);
IntPtr funcPtr = Marshal.GetFunctionPointerForDelegate(del);
_delegates.Add(key, new DelegateInfo(funcPtr));
return;
}
@@ -1,5 +1,7 @@
using System;
namespace ARMeilleure.Translation
{
delegate void DispatcherFunction(nint nativeContext, ulong startAddress);
delegate ulong WrapperFunction(nint nativeContext, ulong startAddress);
delegate void DispatcherFunction(IntPtr nativeContext, ulong startAddress);
delegate ulong WrapperFunction(IntPtr nativeContext, ulong startAddress);
}
@@ -100,10 +100,10 @@ namespace ARMeilleure.Translation
#if ANDROID
// For Android, use the Delegates class to get the function pointer
int index = Delegates.GetDelegateIndex(info);
nint funcPtr = Delegates.GetDelegateFuncPtrByIndex(index);
IntPtr funcPtr = Delegates.GetDelegateFuncPtrByIndex(index);
#else
// For other platforms, use direct method handle approach
nint funcPtr = info.MethodHandle.GetFunctionPointer();
IntPtr funcPtr = info.MethodHandle.GetFunctionPointer();
#endif
OperandType returnType = GetOperandType(info.ReturnType);
@@ -675,7 +675,8 @@ namespace ARMeilleure.Translation
Operation last = block.Operations.Last;
return last != default &&
last.Instruction is Instruction.Return or Instruction.Tailcall;
(last.Instruction == Instruction.Return ||
last.Instruction == Instruction.Tailcall);
}
public ControlFlowGraph GetControlFlowGraph()
+3 -1
View File
@@ -1,4 +1,6 @@
using System;
namespace ARMeilleure.Translation
{
delegate ulong GuestFunction(nint nativeContextPtr);
delegate ulong GuestFunction(IntPtr nativeContextPtr);
}
+1 -1
View File
@@ -316,7 +316,7 @@ namespace ARMeilleure.Translation
{
_root = newNode;
}
else if (start.CompareTo(parent!.Start) < 0)
else if (start.CompareTo(parent.Start) < 0)
{
parent.Left = newNode;
}
+103 -60
View File
@@ -10,7 +10,6 @@ using Ryujinx.Common.Logging;
using Ryujinx.Common.Memory;
using System;
using System.Buffers.Binary;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
@@ -31,11 +30,14 @@ namespace ARMeilleure.Translation.PTC
private const string OuterHeaderMagicString = "PTCohd\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 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 CountTableSymbol = new(SymbolType.Special, 2);
public static readonly Symbol DispatchStubSymbol = new(SymbolType.Special, 3);
@@ -61,7 +63,8 @@ namespace ARMeilleure.Translation.PTC
private bool _disposed;
public PtcCacheInfo CacheInfo { get; private set; }
public string TitleIdText { get; private set; }
public string DisplayVersion { get; private set; }
private MemoryManagerType _memoryMode;
@@ -88,7 +91,8 @@ namespace ARMeilleure.Translation.PTC
_disposed = false;
CacheInfo = new PtcCacheInfo(0, null, null, 0, null, "Unknown", "default");
TitleIdText = TitleIdTextDefault;
DisplayVersion = DisplayVersionDefault;
CachePathActual = string.Empty;
CachePathBackup = string.Empty;
@@ -96,24 +100,20 @@ namespace ARMeilleure.Translation.PTC
Disable();
}
public void Initialize(PtcCacheInfo cacheInfo, bool enabled, MemoryManagerType memoryMode)
public void Initialize(string titleIdText, string displayVersion, bool enabled, MemoryManagerType memoryMode, string cacheSelector)
{
Wait();
Profiler.Wait();
Profiler.ClearEntries();
CacheInfo = cacheInfo;
Logger.Info?.Print(LogClass.Ptc, $"Initializing Profiled Persistent Translation Cache (enabled: {enabled}).");
Logger.Info?.Print(
LogClass.Ptc,
$"Initializing Profiled Persistent Translation Cache v{InternalVersion}\n\t\t " +
$"(pid: {cacheInfo.ProcessId}, title: {cacheInfo.TitleIdText}, application: {cacheInfo.ApplicationIdText}, " +
$"programIndex: {cacheInfo.ProgramIndex}, version: '{cacheInfo.DisplayVersion}', kind: {cacheInfo.ProcessKind}, " +
$"selector: '{cacheInfo.CacheSelector}', key: '{cacheInfo.CacheKey}', enabled: {enabled}).");
if (!enabled || cacheInfo.TitleIdText == PtcCacheInfo.TitleIdTextDefault)
if (!enabled || string.IsNullOrEmpty(titleIdText) || titleIdText == TitleIdTextDefault)
{
TitleIdText = TitleIdTextDefault;
DisplayVersion = DisplayVersionDefault;
CachePathActual = string.Empty;
CachePathBackup = string.Empty;
@@ -122,10 +122,14 @@ namespace ARMeilleure.Translation.PTC
return;
}
TitleIdText = titleIdText;
DisplayVersion = !string.IsNullOrEmpty(displayVersion) ? displayVersion : DisplayVersionDefault;
_memoryMode = memoryMode;
string workPathActual = Path.Combine(AppDataManager.GamesDirPath, CacheInfo.TitleIdText, "cache", "cpu", ActualDir);
string workPathBackup = Path.Combine(AppDataManager.GamesDirPath, CacheInfo.TitleIdText, "cache", "cpu", BackupDir);
Logger.Info?.Print(LogClass.Ptc, $"PPTC (v{InternalVersion}) Profile: {DisplayVersion}-{cacheSelector}");
string workPathActual = Path.Combine(AppDataManager.GamesDirPath, TitleIdText, "cache", "cpu", ActualDir);
string workPathBackup = Path.Combine(AppDataManager.GamesDirPath, TitleIdText, "cache", "cpu", BackupDir);
if (!Directory.Exists(workPathActual))
{
@@ -137,14 +141,8 @@ namespace ARMeilleure.Translation.PTC
Directory.CreateDirectory(workPathBackup);
}
CachePathActual = Path.Combine(workPathActual, CacheInfo.DisplayVersion) + "-" + CacheInfo.CacheSelector;
CachePathBackup = Path.Combine(workPathBackup, CacheInfo.DisplayVersion) + "-" + CacheInfo.CacheSelector;
Logger.Info?.Print(
LogClass.Ptc,
$"PPTC cache owner selected (pid: {CacheInfo.ProcessId}, title: {CacheInfo.TitleIdText}, application: {CacheInfo.ApplicationIdText}, " +
$"version: '{CacheInfo.DisplayVersion}', kind: {CacheInfo.ProcessKind}, selector: '{CacheInfo.CacheSelector}', " +
$"key: '{CacheInfo.CacheKey}', path: '{CachePathActual}').");
CachePathActual = Path.Combine(workPathActual, DisplayVersion) + "-" + cacheSelector;
CachePathBackup = Path.Combine(workPathBackup, DisplayVersion) + "-" + cacheSelector;
PreLoad();
Profiler.PreLoad();
@@ -192,7 +190,7 @@ namespace ARMeilleure.Translation.PTC
_infosStream.Seek(0L, SeekOrigin.Begin);
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);
foreach (ulong address in blacklist)
@@ -246,25 +244,67 @@ namespace ARMeilleure.Translation.PTC
{
OuterHeader outerHeader = DeserializeStructure<OuterHeader>(compressedStream);
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)
if (!outerHeader.IsHeaderValid())
{
InvalidateCompressedStream(compressedStream);
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
{
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);
try
@@ -284,7 +324,14 @@ namespace ARMeilleure.Translation.PTC
InnerHeader innerHeader = DeserializeStructure<InnerHeader>(stream);
if (!innerHeader.IsHeaderValid() || innerHeader.Magic != _innerHeaderMagic)
if (!innerHeader.IsHeaderValid())
{
InvalidateCompressedStream(compressedStream);
return false;
}
if (innerHeader.Magic != _innerHeaderMagic)
{
InvalidateCompressedStream(compressedStream);
@@ -358,7 +405,7 @@ namespace ARMeilleure.Translation.PTC
}
finally
{
if (intPtr != nint.Zero)
if (intPtr != IntPtr.Zero)
{
Marshal.FreeHGlobal(intPtr);
}
@@ -367,12 +414,7 @@ namespace ARMeilleure.Translation.PTC
long fileSize = new FileInfo(fileName).Length;
Logger.Info?.Print(
LogClass.Ptc,
$"{(isBackup ? "Loaded Backup Translation Cache" : "Loaded Translation Cache")} " +
$"(pid: {CacheInfo.ProcessId}, title: {CacheInfo.TitleIdText}, version: '{CacheInfo.DisplayVersion}', kind: {CacheInfo.ProcessKind}, " +
$"selector: '{CacheInfo.CacheSelector}', key: '{CacheInfo.CacheKey}', path: '{fileName}', " +
$"size: {fileSize} bytes, translated functions: {GetEntriesCount()}).");
Logger.Info?.Print(LogClass.Ptc, $"{(isBackup ? "Loaded Backup Translation Cache" : "Loaded Translation Cache")} (size: {fileSize} bytes, translated functions: {GetEntriesCount()}).");
return true;
}
@@ -445,11 +487,11 @@ namespace ARMeilleure.Translation.PTC
outerHeader.SetHeaderHash();
nint intPtr = nint.Zero;
IntPtr intPtr = IntPtr.Zero;
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);
stream.Seek((long)Unsafe.SizeOf<InnerHeader>(), SeekOrigin.Begin);
@@ -503,7 +545,7 @@ namespace ARMeilleure.Translation.PTC
}
finally
{
if (intPtr != nint.Zero)
if (intPtr != IntPtr.Zero)
{
Marshal.FreeHGlobal(intPtr);
}
@@ -551,7 +593,7 @@ namespace ARMeilleure.Translation.PTC
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.CodeLength = 0;
@@ -655,7 +697,7 @@ namespace ARMeilleure.Translation.PTC
foreach (RelocEntry relocEntry in relocEntries)
{
nint? imm = null;
IntPtr? imm = null;
Symbol symbol = relocEntry.Symbol;
if (symbol.Type == SymbolType.FunctionTable)
@@ -666,7 +708,7 @@ namespace ARMeilleure.Translation.PTC
{
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;
if (Delegates.TryGetDelegateFuncPtrByIndex(index, out nint funcPtr))
if (Delegates.TryGetDelegateFuncPtrByIndex(index, out IntPtr funcPtr))
{
imm = funcPtr;
}
@@ -689,7 +731,7 @@ namespace ARMeilleure.Translation.PTC
unsafe
{
imm = (nint)Unsafe.AsPointer(ref callCounter.Value);
imm = (IntPtr)Unsafe.AsPointer(ref callCounter.Value);
}
}
else if (symbol == DispatchStubSymbol)
@@ -738,8 +780,8 @@ namespace ARMeilleure.Translation.PTC
UnwindInfo unwindInfo,
bool highCq)
{
CompiledFunction cFunc = new(code, unwindInfo, RelocInfo.Empty);
GuestFunction gFunc = cFunc.MapWithPointer<GuestFunction>(out nint gFuncPointer);
var cFunc = new CompiledFunction(code, unwindInfo, RelocInfo.Empty);
var gFunc = cFunc.MapWithPointer<GuestFunction>(out IntPtr gFuncPointer);
return new TranslatedFunction(gFunc, gFuncPointer, callCounter, guestSize, highCq);
}
@@ -776,7 +818,7 @@ namespace ARMeilleure.Translation.PTC
public void MakeAndSaveTranslations(Translator translator)
{
ConcurrentQueue<(ulong address, PtcProfiler.FuncProfile funcProfile)> profiledFuncsToTranslate = Profiler.GetProfiledFuncsToTranslate(translator.Functions);
var profiledFuncsToTranslate = Profiler.GetProfiledFuncsToTranslate(translator.Functions);
_translateCount = 0;
_translateTotalCount = profiledFuncsToTranslate.Count;
@@ -820,7 +862,7 @@ namespace ARMeilleure.Translation.PTC
void TranslateFuncs()
{
while (profiledFuncsToTranslate.TryDequeue(out (ulong address, PtcProfiler.FuncProfile funcProfile) item))
while (profiledFuncsToTranslate.TryDequeue(out var item))
{
ulong address = item.address;
ExecutionMode executionMode = item.funcProfile.Mode;
@@ -865,11 +907,11 @@ namespace ARMeilleure.Translation.PTC
Stopwatch sw = Stopwatch.StartNew();
foreach (Thread thread in threads)
foreach (var thread in threads)
{
thread.Start();
}
foreach (Thread thread in threads)
foreach (var thread in threads)
{
thread.Join();
}
@@ -949,7 +991,7 @@ namespace ARMeilleure.Translation.PTC
WriteCode(code.AsSpan());
// WriteReloc.
using BinaryWriter relocInfoWriter = new(_relocsStream, EncodingCache.UTF8NoBOM, true);
using var relocInfoWriter = new BinaryWriter(_relocsStream, EncodingCache.UTF8NoBOM, true);
foreach (RelocEntry entry in relocInfo.Entries)
{
@@ -959,7 +1001,7 @@ namespace ARMeilleure.Translation.PTC
}
// WriteUnwindInfo.
using BinaryWriter unwindInfoWriter = new(_unwindInfosStream, EncodingCache.UTF8NoBOM, true);
using var unwindInfoWriter = new BinaryWriter(_unwindInfosStream, EncodingCache.UTF8NoBOM, true);
unwindInfoWriter.Write(unwindInfo.PushEntries.Length);
@@ -1124,7 +1166,8 @@ namespace ARMeilleure.Translation.PTC
public void Close()
{
if (State is PtcState.Enabled or PtcState.Continuing)
if (State == PtcState.Enabled ||
State == PtcState.Continuing)
{
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;
}
}
}
+31 -21
View File
@@ -9,13 +9,13 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
using System.Timers;
using static ARMeilleure.Translation.PTC.PtcFormatter;
using Timer = System.Timers.Timer;
using System.Linq;
namespace ARMeilleure.Translation.PTC
{
@@ -23,7 +23,7 @@ namespace ARMeilleure.Translation.PTC
{
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 =
[
@@ -119,9 +119,9 @@ namespace ARMeilleure.Translation.PTC
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)
{
@@ -142,7 +142,7 @@ namespace ARMeilleure.Translation.PTC
{
List<ulong> funcs = [];
foreach (KeyValuePair<ulong, FuncProfile> profiledFunc in ProfiledFuncs)
foreach (var profiledFunc in ProfiledFuncs)
{
if (profiledFunc.Value.Blacklist)
{
@@ -189,10 +189,28 @@ namespace ARMeilleure.Translation.PTC
{
OuterHeader outerHeader = DeserializeStructure<OuterHeader>(compressedStream);
if (!outerHeader.IsHeaderValid() ||
outerHeader.Magic != _outerHeaderMagic ||
outerHeader.InfoFileVersion != InternalVersion && !_migrateInternalVersions.Contains(outerHeader.InfoFileVersion) ||
outerHeader.Endianness != Ptc.GetEndianness())
if (!outerHeader.IsHeaderValid())
{
InvalidateCompressedStream(compressedStream);
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);
@@ -254,12 +272,7 @@ namespace ARMeilleure.Translation.PTC
long fileSize = new FileInfo(fileName).Length;
Logger.Info?.Print(
LogClass.Ptc,
$"{(isBackup ? "Loaded Backup Profiling Info" : "Loaded Profiling Info")} " +
$"(pid: {_ptc.CacheInfo.ProcessId}, title: {_ptc.CacheInfo.TitleIdText}, version: '{_ptc.CacheInfo.DisplayVersion}', " +
$"kind: {_ptc.CacheInfo.ProcessKind}, selector: '{_ptc.CacheInfo.CacheSelector}', key: '{_ptc.CacheInfo.CacheKey}', " +
$"path: '{fileName}', size: {fileSize} bytes, profiled functions: {ProfiledFuncs.Count}).");
Logger.Info?.Print(LogClass.Ptc, $"{(isBackup ? "Loaded Backup Profiling Info" : "Loaded Profiling Info")} (size: {fileSize} bytes, profiled functions: {ProfiledFuncs.Count}).");
return true;
}
@@ -380,11 +393,7 @@ namespace ARMeilleure.Translation.PTC
if (fileSize != 0L)
{
Logger.Info?.Print(
LogClass.Ptc,
$"Saved Profiling Info (pid: {_ptc.CacheInfo.ProcessId}, title: {_ptc.CacheInfo.TitleIdText}, version: '{_ptc.CacheInfo.DisplayVersion}', " +
$"kind: {_ptc.CacheInfo.ProcessKind}, selector: '{_ptc.CacheInfo.CacheSelector}', key: '{_ptc.CacheInfo.CacheKey}', " +
$"path: '{fileName}', size: {fileSize} bytes, profiled functions: {profiledFuncsCount}).");
Logger.Info?.Print(LogClass.Ptc, $"Saved Profiling Info (size: {fileSize} bytes, profiled functions: {profiledFuncsCount}).");
}
}
@@ -456,7 +465,8 @@ namespace ARMeilleure.Translation.PTC
public void Start()
{
if (_ptc.State is PtcState.Enabled or PtcState.Continuing)
if (_ptc.State == PtcState.Enabled ||
_ptc.State == PtcState.Continuing)
{
Enabled = true;
+1 -1
View File
@@ -95,7 +95,7 @@ namespace ARMeilleure.Translation
// 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.
// 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);
}
@@ -44,10 +44,10 @@ namespace ARMeilleure.Translation
public static void Construct(ControlFlowGraph cfg)
{
DefMap[] globalDefs = new DefMap[cfg.Blocks.Count];
Operand[] localDefs = new Operand[cfg.LocalsCount + RegisterConsts.TotalCount];
var globalDefs = new DefMap[cfg.Blocks.Count];
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)
{
@@ -1,4 +1,5 @@
using ARMeilleure.Common;
using System;
namespace ARMeilleure.Translation
{
@@ -6,12 +7,12 @@ namespace ARMeilleure.Translation
{
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 ulong GuestSize { 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;
FuncPointer = funcPointer;
+10 -12
View File
@@ -57,9 +57,9 @@ namespace ARMeilleure.Translation
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;
}
@@ -168,7 +168,6 @@ namespace ARMeilleure.Translation
Statistics.StartTimer();
context.ResetCallDepth();
ulong nextAddr = func.Execute(Stubs.ContextWrapper, context);
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)
{
ArmEmitterContext context = new(
var context = new ArmEmitterContext(
Memory,
CountTable,
FunctionTable,
@@ -240,7 +239,6 @@ namespace ARMeilleure.Translation
Logger.StartPass(PassName.Translation);
InstEmitFlowHelper.EmitCallDepthCheckAndIncrement(context, Const(address));
EmitSynchronization(context);
if (blocks[0].Address != address)
@@ -265,10 +263,10 @@ namespace ARMeilleure.Translation
Logger.EndPass(PassName.RegisterUsage);
OperandType retType = OperandType.I64;
OperandType[] argTypes = new OperandType[] { OperandType.I64 };
var retType = 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)
{
@@ -284,7 +282,7 @@ namespace ARMeilleure.Translation
_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();
@@ -536,7 +534,7 @@ namespace ARMeilleure.Translation
List<TranslatedFunction> functions = Functions.AsList();
foreach (TranslatedFunction func in functions)
foreach (var func in functions)
{
JitCache.Unmap(func.FuncPointer);
@@ -545,7 +543,7 @@ namespace ARMeilleure.Translation
Functions.Clear();
while (_oldFuncs.TryDequeue(out KeyValuePair<ulong, TranslatedFunction> kv))
while (_oldFuncs.TryDequeue(out var kv))
{
JitCache.Unmap(kv.Value.FuncPointer);
@@ -566,7 +564,7 @@ namespace ARMeilleure.Translation
{
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);
}
+24 -32
View File
@@ -14,7 +14,7 @@ namespace ARMeilleure.Translation
/// </summary>
class TranslatorStubs : IDisposable
{
private readonly Lazy<nint> _slowDispatchStub;
private readonly Lazy<IntPtr> _slowDispatchStub;
private bool _disposed;
@@ -27,7 +27,7 @@ namespace ARMeilleure.Translation
/// Gets the dispatch stub.
/// </summary>
/// <exception cref="ObjectDisposedException"><see cref="TranslatorStubs"/> instance was disposed</exception>
public nint DispatchStub
public IntPtr DispatchStub
{
get
{
@@ -41,7 +41,7 @@ namespace ARMeilleure.Translation
/// Gets the slow dispatch stub.
/// </summary>
/// <exception cref="ObjectDisposedException"><see cref="TranslatorStubs"/> instance was disposed</exception>
public nint SlowDispatchStub
public IntPtr SlowDispatchStub
{
get
{
@@ -139,9 +139,9 @@ namespace ARMeilleure.Translation
/// Generates a <see cref="DispatchStub"/>.
/// </summary>
/// <returns>Generated <see cref="DispatchStub"/></returns>
private nint GenerateDispatchStub()
private IntPtr GenerateDispatchStub()
{
EmitterContext context = new();
var context = new EmitterContext();
Operand lblFallback = Label();
Operand lblEnd = Label();
@@ -160,7 +160,7 @@ namespace ARMeilleure.Translation
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
// 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);
context.Tailcall(hostAddress, nativeContext);
ControlFlowGraph cfg = context.GetControlFlowGraph();
OperandType retType = OperandType.I64;
OperandType[] argTypes = new[] { OperandType.I64 };
var cfg = context.GetControlFlowGraph();
var retType = 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);
}
@@ -197,9 +197,9 @@ namespace ARMeilleure.Translation
/// Generates a <see cref="SlowDispatchStub"/>.
/// </summary>
/// <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.
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);
context.Tailcall(hostAddress, nativeContext);
ControlFlowGraph cfg = context.GetControlFlowGraph();
OperandType retType = OperandType.I64;
OperandType[] argTypes = new[] { OperandType.I64 };
var cfg = context.GetControlFlowGraph();
var retType = 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);
}
@@ -250,7 +250,7 @@ namespace ARMeilleure.Translation
/// <returns><see cref="DispatchLoop"/> function</returns>
private DispatcherFunction GenerateDispatchLoop()
{
EmitterContext context = new();
var context = new EmitterContext();
Operand beginLbl = Label();
Operand endLbl = Label();
@@ -262,18 +262,10 @@ namespace ARMeilleure.Translation
Operand runningAddress = context.Add(nativeContext, Const((ulong)NativeContext.GetRunningOffset()));
Operand dispatchAddress = context.Add(nativeContext, Const((ulong)NativeContext.GetDispatchAddressOffset()));
Operand callDepthAddress = context.Add(nativeContext, Const((ulong)NativeContext.GetCallDepthOffset()));
EmitSyncFpContext(context, nativeContext, true);
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.Copy(guestAddress, context.Call(Const((ulong)DispatchStub), OperandType.I64, nativeContext));
context.BranchIfFalse(endLbl, guestAddress);
@@ -286,9 +278,9 @@ namespace ARMeilleure.Translation
context.Return();
ControlFlowGraph cfg = context.GetControlFlowGraph();
OperandType retType = OperandType.None;
OperandType[] argTypes = new[] { OperandType.I64, OperandType.I64 };
var cfg = context.GetControlFlowGraph();
var retType = OperandType.None;
var argTypes = new[] { OperandType.I64, OperandType.I64 };
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>
private WrapperFunction GenerateContextWrapper()
{
EmitterContext context = new();
var context = new EmitterContext();
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
Operand guestMethod = context.LoadArgument(OperandType.I64, 1);
@@ -310,9 +302,9 @@ namespace ARMeilleure.Translation
context.Return(returnValue);
ControlFlowGraph cfg = context.GetControlFlowGraph();
OperandType retType = OperandType.I64;
OperandType[] argTypes = new[] { OperandType.I64, OperandType.I64 };
var cfg = context.GetControlFlowGraph();
var retType = OperandType.I64;
var argTypes = new[] { OperandType.I64, OperandType.I64 };
return Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<WrapperFunction>();
}
@@ -9,7 +9,7 @@ namespace ARMeilleure.Translation
{
public static class TranslatorTestMethods
{
public delegate int FpFlagsPInvokeTest(nint managedMethod);
public delegate int FpFlagsPInvokeTest(IntPtr managedMethod);
private static bool SetPlatformFtz(EmitterContext context, bool ftz)
{
-12
View File
@@ -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
-175
View File
@@ -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
View File
@@ -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