mirror of
https://git.ryujinx.app/projects/Kenji-NX.git
synced 2026-09-27 15:07:54 +02:00
Compare commits
279
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
280c427c55 | ||
|
|
25b4892981 | ||
|
|
d16d67f7c4 | ||
|
|
e7cb0632c5 | ||
|
|
4ea8c0b280 | ||
|
|
aef309800c | ||
|
|
a008cd20f1 | ||
|
|
ec6ce46357 | ||
|
|
a7a3a17b48 | ||
|
|
94cafdd1f9 | ||
|
|
1b57022694 | ||
|
|
2bb07a4364 | ||
|
|
5e67569a6e | ||
|
|
97f0336aa2 | ||
|
|
53d32a761e | ||
|
|
02d8eeb18f | ||
|
|
54e2f79c16 | ||
|
|
77277442a9 | ||
|
|
b707c73f83 | ||
|
|
235fb5f8c2 | ||
|
|
c2cbd53235 | ||
|
|
4f3c3a7b5d | ||
|
|
2dc1894e1e | ||
|
|
db4799e7ab | ||
|
|
06f049b874 | ||
|
|
58f9b6106f | ||
|
|
5422aa35b9 | ||
|
|
8ccf83d9a3 | ||
|
|
2eecc18dc7 | ||
|
|
3ceca9c381 | ||
|
|
a4412346a7 | ||
|
|
b0eb271b5b | ||
|
|
88cef7e853 | ||
|
|
69c088004a | ||
|
|
d3bd888885 | ||
|
|
b615f50701 | ||
|
|
d5524be27a | ||
|
|
43b4f3bf12 | ||
|
|
8d7d8f344c | ||
|
|
047dbf3db7 | ||
|
|
d0f0007421 | ||
|
|
60e553bffa | ||
|
|
a8a23b6b2c | ||
|
|
e0f6e207e8 | ||
|
|
30ab86cb72 | ||
|
|
2901f9b795 | ||
|
|
b08b9eabe6 | ||
|
|
83c65c2c06 | ||
|
|
3a6ce7cd1e | ||
|
|
94cfe6930c | ||
|
|
9a4bf8f94d | ||
|
|
d69d9c3b31 | ||
|
|
d9da38c10b | ||
|
|
ac699e430b | ||
|
|
89d7a5c45e | ||
|
|
93d1eea58d | ||
|
|
ad8ab179d2 | ||
|
|
18c6ec439b | ||
|
|
f2ac50360e | ||
|
|
8c42f306dd | ||
|
|
d765d6daa6 | ||
|
|
e7c720fd2c | ||
|
|
f3a87af51d | ||
|
|
1b9615983f | ||
|
|
d040492b7a | ||
|
|
05f7878699 | ||
|
|
0d8bd2653b | ||
|
|
b7ffcb3be8 | ||
|
|
46c55ee79d | ||
|
|
6c1c05dc5d | ||
|
|
637c8130a3 | ||
|
|
7781911baa | ||
|
|
bd3d4d0566 | ||
|
|
63c7d2a0ee | ||
|
|
5f40a22b0d | ||
|
|
72955d1aa4 | ||
|
|
ce2f2a14af | ||
|
|
b4d0f10568 | ||
|
|
c2dfc93fa6 | ||
|
|
29a828c4ef | ||
|
|
9995e26ec6 | ||
|
|
59c277bc6c | ||
|
|
491fb3ccfb | ||
|
|
e96823c9a0 | ||
|
|
ad0e2c0dea | ||
|
|
59b1989b97 | ||
|
|
40c32c7a20 | ||
|
|
bfe7973d90 | ||
|
|
14c432a416 | ||
|
|
e35ac3fed0 | ||
|
|
e3e46315f0 | ||
|
|
a6cb810a92 | ||
|
|
21b2b2bc03 | ||
|
|
d3a6d02ca1 | ||
|
|
1b2d471039 | ||
|
|
890d5a6e2c | ||
|
|
79ab37f282 | ||
|
|
487ef4c163 | ||
|
|
69908d7bf1 | ||
|
|
395081ae87 | ||
|
|
1a7be99263 | ||
|
|
d7770e2582 | ||
|
|
6a59302104 | ||
|
|
e49ef8d60e | ||
|
|
6840ae0c5c | ||
|
|
6f5b351fa0 | ||
|
|
6d36f471dd | ||
|
|
2c1b7bc667 | ||
|
|
dc4a51a462 | ||
|
|
ea7d680d11 | ||
|
|
fcf1ba6b9e | ||
|
|
99435c8708 | ||
|
|
74011bbb45 | ||
|
|
066afe9407 | ||
|
|
8aa08bc64b | ||
|
|
940d78a1ad | ||
|
|
d0e82aafb1 | ||
|
|
fe7b601826 | ||
|
|
9063b4b8db | ||
|
|
e036837211 | ||
|
|
c6deb3800f | ||
|
|
75220096e7 | ||
|
|
7f805f59fd | ||
|
|
ab571e81b3 | ||
|
|
f6adcd2f65 | ||
|
|
85e05bc7b2 | ||
|
|
bb0658bba9 | ||
|
|
50b00e3827 | ||
|
|
99c2e672c3 | ||
|
|
9ea27c3318 | ||
|
|
203ac3a175 | ||
|
|
bc0c483041 | ||
|
|
dbaf317f15 | ||
|
|
9357bb1524 | ||
|
|
734aecd711 | ||
|
|
1af7ebafc8 | ||
|
|
c8a6958ef3 | ||
|
|
377805579b | ||
|
|
35bced8527 | ||
|
|
c1e24961f9 | ||
|
|
2d720fea03 | ||
|
|
65c740ec4a | ||
|
|
0b8d1d5816 | ||
|
|
78db4c365f | ||
|
|
1bb1c7bba8 | ||
|
|
e1b6cb71f8 | ||
|
|
f34ab9f043 | ||
|
|
ebdc2a9e81 | ||
|
|
ebeb6e146c | ||
|
|
28b4f45645 | ||
|
|
f3931a789a | ||
|
|
42369a22a3 | ||
|
|
7b801bb9d3 | ||
|
|
457b7e77de | ||
|
|
169712a9e8 | ||
|
|
a187ec09d7 | ||
|
|
6d9e7fea75 | ||
|
|
ea89fc28da | ||
|
|
7242cb2449 | ||
|
|
4f101170f0 | ||
|
|
fc7932ad27 | ||
|
|
0c41346400 | ||
|
|
c6230fc02e | ||
|
|
1ef09717d2 | ||
|
|
41eb6f5dc4 | ||
|
|
f47be3342f | ||
|
|
4d1620c330 | ||
|
|
7a5b5dee6a | ||
|
|
fa5b5b127e | ||
|
|
327416c967 | ||
|
|
c42663115b | ||
|
|
09bc10b15d | ||
|
|
75058d850b | ||
|
|
ab6ccb3d0b | ||
|
|
07ae239130 | ||
|
|
548ec2c175 | ||
|
|
925ffc3976 | ||
|
|
cf12e5ab82 | ||
|
|
1f283b366b | ||
|
|
e4d2dea201 | ||
|
|
0cf2b1fbda | ||
|
|
8418025b82 | ||
|
|
316dc3a9b3 | ||
|
|
d73d82fbc0 | ||
|
|
7cc28d0242 | ||
|
|
92c3b72b17 | ||
|
|
8707fd5232 | ||
|
|
c125d08636 | ||
|
|
eee4a6272c | ||
|
|
be176fd367 | ||
|
|
48ba1cc9ed | ||
|
|
a6f3f0718d | ||
|
|
9cdd9f6dbf | ||
|
|
0928851966 | ||
|
|
d773bd60f4 | ||
|
|
dfb6164ba5 | ||
|
|
5a9bc0d703 | ||
|
|
2699bcf03e | ||
|
|
f45c1e6dcb | ||
|
|
fd75b35496 | ||
|
|
3e8d562182 | ||
|
|
65caa1e3f2 | ||
|
|
e3ef1e1fde | ||
|
|
3deddbd491 | ||
|
|
ff0daa9f35 | ||
|
|
faa8fb6642 | ||
|
|
6e7d013903 | ||
|
|
5db4793c7b | ||
|
|
db8b61106d | ||
|
|
55ea9046a2 | ||
|
|
a84dd77dc2 | ||
|
|
743eef7ff9 | ||
|
|
39066b846d | ||
|
|
307d048d25 | ||
|
|
7f89429b9f | ||
|
|
6b08ba47c4 | ||
|
|
f63cb962ad | ||
|
|
2c2b37678a | ||
|
|
6fed0795b9 | ||
|
|
c5540f9541 | ||
|
|
4c7a5a7261 | ||
|
|
925610723c | ||
|
|
bdc573ab17 | ||
|
|
f61e340a58 | ||
|
|
0149b71ac9 | ||
|
|
b4eaa5f262 | ||
|
|
2f7406aaca | ||
|
|
a9954c23cc | ||
|
|
9955191651 | ||
|
|
2e0bb4ec56 | ||
|
|
c417740beb | ||
|
|
0d51f4fcb5 | ||
|
|
4626aae70e | ||
|
|
d0abbc6da3 | ||
|
|
9b6b9146e2 | ||
|
|
4605c8bbd3 | ||
|
|
18743ed661 | ||
|
|
3487c4546e | ||
|
|
c457a2470f | ||
|
|
9fb338395a | ||
|
|
ab7e27a4e9 | ||
|
|
44b6f162c1 | ||
|
|
775c7b766c | ||
|
|
3faa22b85a | ||
|
|
05c3f2a250 | ||
|
|
8611e42a2e | ||
|
|
620666909f | ||
|
|
ef6f43825d | ||
|
|
d7b797bf02 | ||
|
|
0e4f71b2f1 | ||
|
|
1c23a55937 | ||
|
|
84d7f2d113 | ||
|
|
78b00e530c | ||
|
|
b22d771cce | ||
|
|
81aa3b769c | ||
|
|
fda90239bc | ||
|
|
3af88ad2e6 | ||
|
|
d73be3b927 | ||
|
|
cc0b704d8e | ||
|
|
cdd4557343 | ||
|
|
e172bdf2eb | ||
|
|
6e7808a65a | ||
|
|
5a86fb9826 | ||
|
|
45166424fc | ||
|
|
965c18225b | ||
|
|
2ea7d32b30 | ||
|
|
40cbd19bc3 | ||
|
|
0255c1f463 | ||
|
|
3657e55c70 | ||
|
|
af3aa8f786 | ||
|
|
81728f62bb | ||
|
|
a4856f1885 | ||
|
|
8fcf430ea8 | ||
|
|
fc2f1588d5 | ||
|
|
6f13e04a24 | ||
|
|
4e669ada26 | ||
|
|
575cc233c9 | ||
|
|
7a4017d164 | ||
|
|
e8e4c5aa61 |
@@ -173,3 +173,6 @@ PublishProfiles/
|
||||
|
||||
# Glade backup files
|
||||
*.glade~
|
||||
|
||||
# Log files
|
||||
/logs/
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
[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
|
||||
+35
-33
@@ -3,52 +3,54 @@
|
||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<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="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="CommandLineParser" Version="2.9.1" />
|
||||
<PackageVersion Include="Concentus" Version="2.2.2" />
|
||||
<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="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="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
|
||||
<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.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.IO.RecyclableMemoryStream" Version="3.0.1" />
|
||||
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
|
||||
<PackageVersion Include="NetCoreServer" Version="8.0.7" />
|
||||
<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="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="Open.NAT.Core" Version="2.1.0.5" />
|
||||
<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.Audio.OpenAL" Version="1.25.2" />
|
||||
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies.Linux" Version="6.1.3-build5" />
|
||||
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies.macOS" Version="5.0.3-build14" />
|
||||
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies.Windows" Version="6.1.2-build4" />
|
||||
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies.Windows" Version="6.1.3-build5" />
|
||||
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
|
||||
<PackageVersion Include="Ryujinx.LibHac" Version="0.20.0-alpha.103" />
|
||||
<PackageVersion Include="Ryujinx.SDL2-CS-Redux" Version="2.32.6" />
|
||||
<PackageVersion Include="Ryujinx.LibHac" Version="0.21.0-alpha.128" />
|
||||
<PackageVersion Include="Ryujinx.SDL3-CS" Version="2026.707.0" />
|
||||
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
|
||||
<PackageVersion Include="shaderc.net" Version="0.1.0" />
|
||||
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
|
||||
<PackageVersion Include="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="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="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.5" />
|
||||
<PackageVersion Include="System.Management" Version="9.0.5" />
|
||||
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
|
||||
<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" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -1,31 +1,26 @@
|
||||
<h1 align="center">
|
||||
<br>
|
||||
<img src="https://git.ryujinx.app/kenji-nx/ryujinx/-/raw/master/distribution/misc/Logo.png" alt="Kenji-NX">
|
||||
<img src="https://git.ryujinx.app/projects/Kenji-NX/raw/branch/master/distribution/misc/Logo.png" alt="Kenji-NX">
|
||||
<br>
|
||||
<b>Kenji-NX</b>
|
||||
<br>
|
||||
<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>
|
||||
|
||||
[](https://git.ryujinx.app/projects/Kenji-NX/releases/latest)
|
||||
[](https://git.ryujinx.app/Kenji-NX/android/releases/latest)
|
||||
<br>
|
||||
[](https://discord.gg/zBSAuZfKqe)
|
||||
|
||||
</h1>
|
||||
|
||||
<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>
|
||||
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>
|
||||
<img src="docs/shell.png">
|
||||
</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.
|
||||
|
||||
## Compatibility
|
||||
|
||||
@@ -58,12 +53,12 @@ If you wish to build the emulator yourself, follow these steps:
|
||||
|
||||
### Step 1
|
||||
|
||||
Install the [.NET 9.0 (or higher) SDK](https://dotnet.microsoft.com/download/dotnet/9.0).
|
||||
Install the [.NET 10.0 (or higher) SDK](https://dotnet.microsoft.com/download/dotnet/10.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/kenji-nx/ryujinx` 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/projects/Kenji-NX` on the command line to clone the repository or use Code --> Download zip button to get the files.
|
||||
|
||||
### Step 3
|
||||
|
||||
@@ -125,7 +120,7 @@ See [LICENSE.txt](LICENSE.txt) and [THIRDPARTY.md](distribution/legal/THIRDPARTY
|
||||
|
||||
## Credits
|
||||
|
||||
- [LibHac](https://github.com/Thealexbarney/LibHac) is used for our file-system.
|
||||
- [LibHac](https://git.ryujinx.app/projects/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.
|
||||
|
||||
@@ -5,6 +5,8 @@ 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}"
|
||||
@@ -32,7 +34,9 @@ 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}"
|
||||
@@ -95,6 +99,10 @@ 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
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
<Solution>
|
||||
<Folder Name="/Solution Items/">
|
||||
<File Path=".editorconfig" />
|
||||
<File Path="Directory.Build.props" />
|
||||
<File Path="Directory.Packages.props" />
|
||||
<File Path="nuget.config" />
|
||||
</Folder>
|
||||
<Project Path="src/ARMeilleure/ARMeilleure.csproj" />
|
||||
<Project Path="src/LibKenjinx/LibKenjinx.csproj" />
|
||||
<Project Path="src/Ryujinx.Audio.Backends.OpenAL/Ryujinx.Audio.Backends.OpenAL.csproj" />
|
||||
<Project Path="src/Ryujinx.Audio.Backends.SDL3/Ryujinx.Audio.Backends.SDL3.csproj" />
|
||||
<Project Path="src/Ryujinx.Audio.Backends.SoundIo/Ryujinx.Audio.Backends.SoundIo.csproj" />
|
||||
<Project Path="src/Ryujinx.Audio/Ryujinx.Audio.csproj" />
|
||||
<Project Path="src/Ryujinx.Common/Ryujinx.Common.csproj" />
|
||||
<Project Path="src/Ryujinx.Cpu/Ryujinx.Cpu.csproj" />
|
||||
<Project Path="src/Ryujinx.Graphics.Device/Ryujinx.Graphics.Device.csproj" />
|
||||
<Project Path="src/Ryujinx.Graphics.GAL/Ryujinx.Graphics.GAL.csproj" />
|
||||
<Project Path="src/Ryujinx.Graphics.Gpu/Ryujinx.Graphics.Gpu.csproj" />
|
||||
<Project Path="src/Ryujinx.Graphics.Host1x/Ryujinx.Graphics.Host1x.csproj" />
|
||||
<Project Path="src/Ryujinx.Graphics.Nvdec.FFmpeg/Ryujinx.Graphics.Nvdec.FFmpeg.csproj" />
|
||||
<Project Path="src/Ryujinx.Graphics.Nvdec.Vp9/Ryujinx.Graphics.Nvdec.Vp9.csproj" />
|
||||
<Project Path="src/Ryujinx.Graphics.Nvdec/Ryujinx.Graphics.Nvdec.csproj" />
|
||||
<Project Path="src/Ryujinx.Graphics.OpenGL/Ryujinx.Graphics.OpenGL.csproj" />
|
||||
<Project Path="src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj" />
|
||||
<Project Path="src/Ryujinx.Graphics.Texture/Ryujinx.Graphics.Texture.csproj" />
|
||||
<Project Path="src/Ryujinx.Graphics.Vic/Ryujinx.Graphics.Vic.csproj" />
|
||||
<Project Path="src/Ryujinx.Graphics.Video/Ryujinx.Graphics.Video.csproj" />
|
||||
<Project Path="src/Ryujinx.Graphics.Vulkan/Ryujinx.Graphics.Vulkan.csproj" />
|
||||
<Project Path="src/Ryujinx.HLE.Generators/Ryujinx.HLE.Generators.csproj" />
|
||||
<Project Path="src/Ryujinx.HLE/Ryujinx.HLE.csproj" />
|
||||
<Project Path="src/Ryujinx.Horizon.Common/Ryujinx.Horizon.Common.csproj" />
|
||||
<Project Path="src/Ryujinx.Horizon.Generators/Ryujinx.Horizon.Generators.csproj" />
|
||||
<Project Path="src/Ryujinx.Horizon.Kernel.Generators/Ryujinx.Horizon.Kernel.Generators.csproj" />
|
||||
<Project Path="src/Ryujinx.Horizon/Ryujinx.Horizon.csproj" />
|
||||
<Project Path="src/Ryujinx.Input.SDL3/Ryujinx.Input.SDL3.csproj" />
|
||||
<Project Path="src/Ryujinx.Input/Ryujinx.Input.csproj" />
|
||||
<Project Path="src/Ryujinx.Memory/Ryujinx.Memory.csproj" />
|
||||
<Project Path="src/Ryujinx.SDL3.Common/Ryujinx.SDL3.Common.csproj" />
|
||||
<Project Path="src/Ryujinx.ShaderTools/Ryujinx.ShaderTools.csproj" />
|
||||
<Project Path="src/Ryujinx.Tests.Memory/Ryujinx.Tests.Memory.csproj" />
|
||||
<Project Path="src/Ryujinx.Tests.Unicorn/Ryujinx.Tests.Unicorn.csproj" />
|
||||
<Project Path="src/Ryujinx.Tests/Ryujinx.Tests.csproj" />
|
||||
<Project Path="src/Ryujinx.UI.Common/Ryujinx.UI.Common.csproj" />
|
||||
<Project Path="src/Ryujinx.UI.LocaleGenerator/Ryujinx.UI.LocaleGenerator.csproj" />
|
||||
<Project Path="src/Ryujinx/Ryujinx.csproj" />
|
||||
<Project Path="src/Spv.Generator/Spv.Generator.csproj" />
|
||||
</Solution>
|
||||
+1
-1
@@ -8,7 +8,7 @@ Intro to Kenji-NX
|
||||
Kenji-NX is an open-source Nintendo Switch emulator written in C#. It is based on Ryujinx, which was originally created by gdkchan.
|
||||
* 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 SDL2, with OpenAL & libsoundio as fallbacks.
|
||||
* Audio output is entirely supported via C# wrappers for SDL3, with OpenAL & libsoundio as fallbacks.
|
||||
|
||||
Getting Started
|
||||
===============
|
||||
|
||||
+7
-5
@@ -3,19 +3,21 @@
|
||||
<packageSources>
|
||||
<clear />
|
||||
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
|
||||
<add key="LibHacAlpha" value="https://git.ryujinx.app/api/v4/projects/17/packages/nuget/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" />-->
|
||||
</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)
|
||||
{
|
||||
var constants = new Dictionary<ulong, Operand>();
|
||||
Dictionary<ulong, Operand> constants = new();
|
||||
|
||||
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 var constant) || constant.UsesCount > MaxConstantUses)
|
||||
if (!constants.TryGetValue(source.Value, out Operand constant) || constant.UsesCount > MaxConstantUses)
|
||||
{
|
||||
constant = Local(source.Type);
|
||||
|
||||
@@ -254,7 +254,7 @@ namespace ARMeilleure.CodeGen.Arm64
|
||||
|
||||
private static bool IsMemoryLoadOrStore(Instruction inst)
|
||||
{
|
||||
return inst == Instruction.Load || inst == Instruction.Store;
|
||||
return inst is Instruction.Load or Instruction.Store;
|
||||
}
|
||||
|
||||
private static bool ConstTooLong(Operand constOp, OperandType accessType)
|
||||
|
||||
@@ -123,7 +123,7 @@ namespace ARMeilleure.CodeGen.Arm64
|
||||
|
||||
public void Cset(Operand rd, ArmCondition condition)
|
||||
{
|
||||
var zr = Factory.Register(ZrRegister, RegisterType.Integer, rd.Type);
|
||||
Operand zr = Factory.Register(ZrRegister, RegisterType.Integer, rd.Type);
|
||||
Csinc(rd, zr, zr, (ArmCondition)((int)condition ^ 1));
|
||||
}
|
||||
|
||||
|
||||
@@ -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 == 0 || value == ulong.MaxValue)
|
||||
if (value is 0 or ulong.MaxValue)
|
||||
{
|
||||
immN = 0;
|
||||
immS = 0;
|
||||
|
||||
@@ -91,7 +91,7 @@ namespace ARMeilleure.CodeGen.Arm64
|
||||
|
||||
long target = _stream.Position;
|
||||
|
||||
if (_pendingBranches.TryGetValue(block, out var list))
|
||||
if (_pendingBranches.TryGetValue(block, out List<(ArmCondition Condition, long BranchPos)> list))
|
||||
{
|
||||
foreach ((ArmCondition condition, long branchPos) in list)
|
||||
{
|
||||
@@ -119,7 +119,7 @@ namespace ARMeilleure.CodeGen.Arm64
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!_pendingBranches.TryGetValue(target, out var list))
|
||||
if (!_pendingBranches.TryGetValue(target, out List<(ArmCondition Condition, long BranchPos)> list))
|
||||
{
|
||||
list = new List<(ArmCondition, long)>();
|
||||
_pendingBranches.Add(target, list);
|
||||
|
||||
@@ -189,8 +189,7 @@ namespace ARMeilleure.CodeGen.Arm64
|
||||
// The only blocks which can have 0 successors are exit blocks.
|
||||
Operation last = block.Operations.Last;
|
||||
|
||||
Debug.Assert(last.Instruction == Instruction.Tailcall ||
|
||||
last.Instruction == Instruction.Return);
|
||||
Debug.Assert(last.Instruction is Instruction.Tailcall or Instruction.Return);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -322,7 +321,7 @@ namespace ARMeilleure.CodeGen.Arm64
|
||||
|
||||
Debug.Assert(comp.Kind == OperandKind.Constant);
|
||||
|
||||
var cond = ((Comparison)comp.AsInt32()).ToArmCondition();
|
||||
ArmCondition cond = ((Comparison)comp.AsInt32()).ToArmCondition();
|
||||
|
||||
GenerateCompareCommon(context, operation);
|
||||
|
||||
@@ -354,7 +353,7 @@ namespace ARMeilleure.CodeGen.Arm64
|
||||
Debug.Assert(dest.Type == OperandType.I32);
|
||||
Debug.Assert(comp.Kind == OperandKind.Constant);
|
||||
|
||||
var cond = ((Comparison)comp.AsInt32()).ToArmCondition();
|
||||
ArmCondition cond = ((Comparison)comp.AsInt32()).ToArmCondition();
|
||||
|
||||
GenerateCompareCommon(context, operation);
|
||||
|
||||
@@ -464,7 +463,7 @@ namespace ARMeilleure.CodeGen.Arm64
|
||||
Operand dest = operation.Destination;
|
||||
Operand source = operation.GetSource(0);
|
||||
|
||||
Debug.Assert(dest.Type == OperandType.FP32 || dest.Type == OperandType.FP64);
|
||||
Debug.Assert(dest.Type is OperandType.FP32 or OperandType.FP64);
|
||||
Debug.Assert(dest.Type != source.Type);
|
||||
Debug.Assert(source.Type != OperandType.V128);
|
||||
|
||||
@@ -483,7 +482,7 @@ namespace ARMeilleure.CodeGen.Arm64
|
||||
Operand dest = operation.Destination;
|
||||
Operand source = operation.GetSource(0);
|
||||
|
||||
Debug.Assert(dest.Type == OperandType.FP32 || dest.Type == OperandType.FP64);
|
||||
Debug.Assert(dest.Type is OperandType.FP32 or OperandType.FP64);
|
||||
Debug.Assert(dest.Type != source.Type);
|
||||
Debug.Assert(source.Type.IsInteger());
|
||||
|
||||
@@ -1463,7 +1462,7 @@ namespace ARMeilleure.CodeGen.Arm64
|
||||
|
||||
private static bool IsLoadOrStore(Operation operation)
|
||||
{
|
||||
return operation.Instruction == Instruction.Load || operation.Instruction == Instruction.Store;
|
||||
return operation.Instruction is Instruction.Load or Instruction.Store;
|
||||
}
|
||||
|
||||
private static OperandType GetMemOpValueType(Operation operation)
|
||||
@@ -1553,7 +1552,7 @@ namespace ARMeilleure.CodeGen.Arm64
|
||||
|
||||
private static void EnsureSameReg(Operand op1, Operand op2)
|
||||
{
|
||||
Debug.Assert(op1.Kind == OperandKind.Register || op1.Kind == OperandKind.Memory);
|
||||
Debug.Assert(op1.Kind is OperandKind.Register or 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, IntPtr newValue, ulong newValueSize);
|
||||
private static unsafe partial int sysctlbyname([MarshalAs(UnmanagedType.LPStr)] string name, out int oldValue, ref ulong oldSize, nint newValue, ulong newValueSize);
|
||||
|
||||
[SupportedOSPlatform("macos")]
|
||||
private static bool CheckSysctlName(string name)
|
||||
{
|
||||
ulong size = sizeof(int);
|
||||
if (sysctlbyname(name, out int val, ref size, IntPtr.Zero, 0) == 0 && size == sizeof(int))
|
||||
if (sysctlbyname(name, out int val, ref size, nint.Zero, 0) == 0 && size == sizeof(int))
|
||||
{
|
||||
return val != 0;
|
||||
}
|
||||
|
||||
@@ -736,19 +736,19 @@ namespace ARMeilleure.CodeGen.Arm64
|
||||
{
|
||||
IntrinsicInfo info = IntrinsicTable.GetInfo(intrinsic & ~(Intrinsic.Arm64VTypeMask | Intrinsic.Arm64VSizeMask));
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
private static bool HasConstSrc1(Operation node, ulong value)
|
||||
@@ -847,9 +847,9 @@ namespace ARMeilleure.CodeGen.Arm64
|
||||
|
||||
Debug.Assert(comp.Kind == OperandKind.Constant);
|
||||
|
||||
var compType = (Comparison)comp.AsInt32();
|
||||
Comparison compType = (Comparison)comp.AsInt32();
|
||||
|
||||
return compType == Comparison.Equal || compType == Comparison.NotEqual;
|
||||
return compType is Comparison.Equal or Comparison.NotEqual;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using ARMeilleure.CodeGen.Linking;
|
||||
using ARMeilleure.CodeGen.Unwinding;
|
||||
using ARMeilleure.Translation.Cache;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace ARMeilleure.CodeGen
|
||||
@@ -58,7 +57,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 IntPtr codePointer)
|
||||
public T MapWithPointer<T>(out nint codePointer)
|
||||
{
|
||||
codePointer = JitCache.Map(this);
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace ARMeilleure.CodeGen.Linking
|
||||
/// <summary>
|
||||
/// Gets an empty <see cref="RelocInfo"/>.
|
||||
/// </summary>
|
||||
public static RelocInfo Empty { get; } = new RelocInfo(null);
|
||||
public static RelocInfo Empty { get; } = new(null);
|
||||
|
||||
private readonly RelocEntry[] _entries;
|
||||
|
||||
|
||||
@@ -227,11 +227,11 @@ namespace ARMeilleure.CodeGen.Optimizations
|
||||
|
||||
private static bool HasSideEffects(Operation node)
|
||||
{
|
||||
return node.Instruction == Instruction.Call
|
||||
|| node.Instruction == Instruction.Tailcall
|
||||
|| node.Instruction == Instruction.CompareAndSwap
|
||||
|| node.Instruction == Instruction.CompareAndSwap16
|
||||
|| node.Instruction == Instruction.CompareAndSwap8;
|
||||
return node.Instruction is Instruction.Call
|
||||
or Instruction.Tailcall
|
||||
or Instruction.CompareAndSwap
|
||||
or Instruction.CompareAndSwap16
|
||||
or Instruction.CompareAndSwap8;
|
||||
}
|
||||
|
||||
private static bool IsPropagableCompare(Operation operation)
|
||||
|
||||
@@ -115,7 +115,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
||||
{
|
||||
NumberLocals(cfg, regMasks.RegistersCount);
|
||||
|
||||
var context = new AllocationContext(stackAlloc, regMasks, _intervals.Count);
|
||||
AllocationContext context = new(stackAlloc, regMasks, _intervals.Count);
|
||||
|
||||
BuildIntervals(cfg, context);
|
||||
|
||||
@@ -839,7 +839,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
||||
{
|
||||
dest.NumberLocal(_intervals.Count);
|
||||
|
||||
LiveInterval interval = new LiveInterval(dest);
|
||||
LiveInterval interval = new(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 == Instruction.Copy || node.Instruction == Instruction.ZeroExtend32)
|
||||
if (node.Instruction is Instruction.Copy or Instruction.ZeroExtend32)
|
||||
{
|
||||
Operand source = node.GetSource(0);
|
||||
|
||||
@@ -1120,8 +1120,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
||||
|
||||
private static bool IsLocalOrRegister(OperandKind kind)
|
||||
{
|
||||
return kind == OperandKind.LocalVariable ||
|
||||
kind == OperandKind.Register;
|
||||
return kind is OperandKind.LocalVariable or OperandKind.Register;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -387,7 +387,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine((IntPtr)_data);
|
||||
return HashCode.Combine((nint)_data);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
|
||||
@@ -15,12 +15,12 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
||||
{
|
||||
if (_count + 1 > _capacity)
|
||||
{
|
||||
var oldSpan = Span;
|
||||
Span<LiveInterval> oldSpan = Span;
|
||||
|
||||
_capacity = Math.Max(4, _capacity * 2);
|
||||
_items = Allocators.References.Allocate<LiveInterval>((uint)_capacity);
|
||||
|
||||
var newSpan = Span;
|
||||
Span<LiveInterval> newSpan = Span;
|
||||
|
||||
oldSpan.CopyTo(newSpan);
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine((IntPtr)_data);
|
||||
return HashCode.Combine((nint)_data);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
|
||||
@@ -16,12 +16,12 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
||||
{
|
||||
if (Count + 1 > _capacity)
|
||||
{
|
||||
var oldSpan = Span;
|
||||
Span<int> oldSpan = Span;
|
||||
|
||||
_capacity = Math.Max(4, _capacity * 2);
|
||||
_items = Allocators.Default.Allocate<int>((uint)_capacity);
|
||||
|
||||
var newSpan = Span;
|
||||
Span<int> newSpan = Span;
|
||||
|
||||
oldSpan.CopyTo(newSpan);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using ARMeilleure.CodeGen.Linking;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using Microsoft.IO;
|
||||
using Ryujinx.Common.Memory;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -1106,17 +1107,17 @@ namespace ARMeilleure.CodeGen.X86
|
||||
}
|
||||
else
|
||||
{
|
||||
if (flags.HasFlag(InstructionFlags.Prefix66))
|
||||
if ((flags & InstructionFlags.Prefix66) != 0)
|
||||
{
|
||||
WriteByte(0x66);
|
||||
}
|
||||
|
||||
if (flags.HasFlag(InstructionFlags.PrefixF2))
|
||||
if ((flags & InstructionFlags.PrefixF2) != 0f)
|
||||
{
|
||||
WriteByte(0xf2);
|
||||
}
|
||||
|
||||
if (flags.HasFlag(InstructionFlags.PrefixF3))
|
||||
if ((flags & InstructionFlags.PrefixF3) != 0f)
|
||||
{
|
||||
WriteByte(0xf3);
|
||||
}
|
||||
@@ -1324,8 +1325,8 @@ namespace ARMeilleure.CodeGen.X86
|
||||
|
||||
public (byte[], RelocInfo) GetCode()
|
||||
{
|
||||
var jumps = CollectionsMarshal.AsSpan(_jumps);
|
||||
var relocs = CollectionsMarshal.AsSpan(_relocs);
|
||||
Span<Jump> jumps = CollectionsMarshal.AsSpan(_jumps);
|
||||
Span<Reloc> relocs = CollectionsMarshal.AsSpan(_relocs);
|
||||
|
||||
// Write jump relative offsets.
|
||||
bool modified;
|
||||
@@ -1410,13 +1411,13 @@ namespace ARMeilleure.CodeGen.X86
|
||||
// Write the code, ignoring the dummy bytes after jumps, into a new stream.
|
||||
_stream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
using var codeStream = MemoryStreamManager.Shared.GetStream();
|
||||
var assembler = new Assembler(codeStream, HasRelocs);
|
||||
using RecyclableMemoryStream codeStream = MemoryStreamManager.Shared.GetStream();
|
||||
Assembler assembler = new(codeStream, HasRelocs);
|
||||
|
||||
bool hasRelocs = HasRelocs;
|
||||
int relocIndex = 0;
|
||||
int relocOffset = 0;
|
||||
var relocEntries = hasRelocs
|
||||
RelocEntry[] relocEntries = hasRelocs
|
||||
? new RelocEntry[relocs.Length]
|
||||
: Array.Empty<RelocEntry>();
|
||||
|
||||
@@ -1469,15 +1470,15 @@ namespace ARMeilleure.CodeGen.X86
|
||||
|
||||
_stream.CopyTo(codeStream);
|
||||
|
||||
var code = codeStream.ToArray();
|
||||
var relocInfo = new RelocInfo(relocEntries);
|
||||
byte[] code = codeStream.ToArray();
|
||||
RelocInfo relocInfo = new(relocEntries);
|
||||
|
||||
return (code, relocInfo);
|
||||
}
|
||||
|
||||
private static bool Is64Bits(OperandType type)
|
||||
{
|
||||
return type == OperandType.I64 || type == OperandType.FP64;
|
||||
return type is OperandType.I64 or OperandType.FP64;
|
||||
}
|
||||
|
||||
private static bool IsImm8(ulong immediate, OperandType type)
|
||||
|
||||
@@ -175,8 +175,7 @@ namespace ARMeilleure.CodeGen.X86
|
||||
// The only blocks which can have 0 successors are exit blocks.
|
||||
Operation last = block.Operations.Last;
|
||||
|
||||
Debug.Assert(last.Instruction == Instruction.Tailcall ||
|
||||
last.Instruction == Instruction.Return);
|
||||
Debug.Assert(last.Instruction is Instruction.Tailcall or Instruction.Return);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -478,7 +477,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 == OperandKind.Register || src3.Kind == OperandKind.Memory);
|
||||
Debug.Assert(src3.Kind is OperandKind.Register or OperandKind.Memory);
|
||||
|
||||
EnsureSameType(dest, src1, src2, src3);
|
||||
Debug.Assert(dest.Type == OperandType.V128);
|
||||
@@ -623,7 +622,7 @@ namespace ARMeilleure.CodeGen.X86
|
||||
|
||||
Debug.Assert(comp.Kind == OperandKind.Constant);
|
||||
|
||||
var cond = ((Comparison)comp.AsInt32()).ToX86Condition();
|
||||
X86Condition cond = ((Comparison)comp.AsInt32()).ToX86Condition();
|
||||
|
||||
GenerateCompareCommon(context, operation);
|
||||
|
||||
@@ -661,7 +660,7 @@ namespace ARMeilleure.CodeGen.X86
|
||||
Debug.Assert(dest.Type == OperandType.I32);
|
||||
Debug.Assert(comp.Kind == OperandKind.Constant);
|
||||
|
||||
var cond = ((Comparison)comp.AsInt32()).ToX86Condition();
|
||||
X86Condition cond = ((Comparison)comp.AsInt32()).ToX86Condition();
|
||||
|
||||
GenerateCompareCommon(context, operation);
|
||||
|
||||
@@ -788,7 +787,7 @@ namespace ARMeilleure.CodeGen.X86
|
||||
Operand dest = operation.Destination;
|
||||
Operand source = operation.GetSource(0);
|
||||
|
||||
Debug.Assert(dest.Type == OperandType.FP32 || dest.Type == OperandType.FP64);
|
||||
Debug.Assert(dest.Type is OperandType.FP32 or OperandType.FP64);
|
||||
|
||||
if (dest.Type == OperandType.FP32)
|
||||
{
|
||||
@@ -1723,7 +1722,7 @@ namespace ARMeilleure.CodeGen.X86
|
||||
return;
|
||||
}
|
||||
|
||||
Debug.Assert(op1.Kind == OperandKind.Register || op1.Kind == OperandKind.Memory);
|
||||
Debug.Assert(op1.Kind is OperandKind.Register or 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);
|
||||
|
||||
var fGetXcr0 = Marshal.GetDelegateForFunctionPointer<GetXcr0>(memGetXcr0.Pointer);
|
||||
GetXcr0 fGetXcr0 = Marshal.GetDelegateForFunctionPointer<GetXcr0>(memGetXcr0.Pointer);
|
||||
|
||||
return fGetXcr0();
|
||||
}
|
||||
|
||||
@@ -312,9 +312,9 @@ namespace ARMeilleure.CodeGen.X86
|
||||
|
||||
case Instruction.Extended:
|
||||
{
|
||||
bool isBlend = node.Intrinsic == Intrinsic.X86Blendvpd ||
|
||||
node.Intrinsic == Intrinsic.X86Blendvps ||
|
||||
node.Intrinsic == Intrinsic.X86Pblendvb;
|
||||
bool isBlend = node.Intrinsic is Intrinsic.X86Blendvpd
|
||||
or Intrinsic.X86Blendvps
|
||||
or 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 == OperandType.FP32 ||
|
||||
dest.Type == OperandType.FP64, $"Invalid destination type \"{dest.Type}\".");
|
||||
Debug.Assert(dest.Type is OperandType.FP32 or OperandType.FP64,
|
||||
$"Invalid destination type \"{dest.Type}\".");
|
||||
|
||||
Operation currentNode = node;
|
||||
|
||||
@@ -759,9 +759,9 @@ namespace ARMeilleure.CodeGen.X86
|
||||
|
||||
Debug.Assert(comp.Kind == OperandKind.Constant);
|
||||
|
||||
var compType = (Comparison)comp.AsInt32();
|
||||
Comparison compType = (Comparison)comp.AsInt32();
|
||||
|
||||
return compType == Comparison.Equal || compType == Comparison.NotEqual;
|
||||
return compType is Comparison.Equal or Comparison.NotEqual;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,13 +13,13 @@ namespace ARMeilleure.CodeGen.X86
|
||||
|
||||
public static void RunPass(ControlFlowGraph cfg)
|
||||
{
|
||||
var constants = new Dictionary<ulong, Operand>();
|
||||
Dictionary<ulong, Operand> constants = new();
|
||||
|
||||
Operand GetConstantCopy(BasicBlock block, Operation operation, Operand source)
|
||||
{
|
||||
// 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 var constant) || constant.UsesCount > MaxConstantUses)
|
||||
if (!constants.TryGetValue(source.Value, out Operand constant) || constant.UsesCount > MaxConstantUses)
|
||||
{
|
||||
constant = Local(source.Type);
|
||||
|
||||
@@ -248,12 +248,12 @@ namespace ARMeilleure.CodeGen.X86
|
||||
|
||||
private static bool IsMemoryLoadOrStore(Instruction inst)
|
||||
{
|
||||
return inst == Instruction.Load ||
|
||||
inst == Instruction.Load16 ||
|
||||
inst == Instruction.Load8 ||
|
||||
inst == Instruction.Store ||
|
||||
inst == Instruction.Store16 ||
|
||||
inst == Instruction.Store8;
|
||||
return inst is Instruction.Load
|
||||
or Instruction.Load16
|
||||
or Instruction.Load8
|
||||
or Instruction.Store
|
||||
or Instruction.Store16
|
||||
or Instruction.Store8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace ARMeilleure.Common
|
||||
private List<PageInfo> _pages;
|
||||
private readonly ulong _pageSize;
|
||||
private readonly uint _pageCount;
|
||||
private readonly List<IntPtr> _extras;
|
||||
private readonly List<nint> _extras;
|
||||
|
||||
public ArenaAllocator(uint pageSize, uint pageCount)
|
||||
{
|
||||
@@ -64,7 +64,7 @@ namespace ARMeilleure.Common
|
||||
{
|
||||
void* extra = NativeAllocator.Instance.Allocate(size);
|
||||
|
||||
_extras.Add((IntPtr)extra);
|
||||
_extras.Add((nint)extra);
|
||||
|
||||
return extra;
|
||||
}
|
||||
@@ -114,7 +114,7 @@ namespace ARMeilleure.Common
|
||||
}
|
||||
|
||||
// Free extra blocks that are not page-sized
|
||||
foreach (IntPtr ptr in _extras)
|
||||
foreach (nint ptr in _extras)
|
||||
{
|
||||
NativeAllocator.Instance.Free((void*)ptr);
|
||||
}
|
||||
@@ -173,7 +173,7 @@ namespace ARMeilleure.Common
|
||||
NativeAllocator.Instance.Free(info.Pointer);
|
||||
}
|
||||
|
||||
foreach (IntPtr ptr in _extras)
|
||||
foreach (nint ptr in _extras)
|
||||
{
|
||||
NativeAllocator.Instance.Free((void*)ptr);
|
||||
}
|
||||
|
||||
@@ -129,13 +129,13 @@ namespace ARMeilleure.Common
|
||||
|
||||
if (count > _count)
|
||||
{
|
||||
var oldMask = _masks;
|
||||
var oldSpan = new Span<long>(_masks, _count);
|
||||
long* oldMask = _masks;
|
||||
Span<long> oldSpan = new(_masks, _count);
|
||||
|
||||
_masks = _allocator.Allocate<long>((uint)count);
|
||||
_count = count;
|
||||
|
||||
var newSpan = new Span<long>(_masks, _count);
|
||||
Span<long> newSpan = new(_masks, _count);
|
||||
|
||||
oldSpan.CopyTo(newSpan);
|
||||
newSpan[oldSpan.Length..].Clear();
|
||||
|
||||
@@ -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, IntPtr> _pages;
|
||||
private readonly Dictionary<int, nint> _pages;
|
||||
private readonly BitMap _allocated;
|
||||
|
||||
/// <summary>
|
||||
@@ -41,7 +41,7 @@ namespace ARMeilleure.Common
|
||||
}
|
||||
|
||||
_allocated = new BitMap(NativeAllocator.Instance);
|
||||
_pages = new Dictionary<int, IntPtr>();
|
||||
_pages = new Dictionary<int, nint>();
|
||||
_pageLogCapacity = BitOperations.Log2((uint)(pageSize / sizeof(TEntry)));
|
||||
_pageCapacity = 1 << _pageLogCapacity;
|
||||
}
|
||||
@@ -63,7 +63,7 @@ namespace ARMeilleure.Common
|
||||
}
|
||||
|
||||
int index = _freeHint++;
|
||||
var page = GetPage(index);
|
||||
Span<TEntry> 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));
|
||||
}
|
||||
|
||||
var page = GetPage(index);
|
||||
Span<TEntry> 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)
|
||||
{
|
||||
var pageIndex = (int)((uint)(index & ~(_pageCapacity - 1)) >> _pageLogCapacity);
|
||||
int pageIndex = (int)((uint)(index & ~(_pageCapacity - 1)) >> _pageLogCapacity);
|
||||
|
||||
if (!_pages.TryGetValue(pageIndex, out IntPtr page))
|
||||
if (!_pages.TryGetValue(pageIndex, out nint page))
|
||||
{
|
||||
page = (IntPtr)NativeAllocator.Instance.Allocate((uint)sizeof(TEntry) * (uint)_pageCapacity);
|
||||
page = (nint)NativeAllocator.Instance.Allocate((uint)sizeof(TEntry) * (uint)_pageCapacity);
|
||||
|
||||
_pages.Add(pageIndex, page);
|
||||
}
|
||||
@@ -168,7 +168,7 @@ namespace ARMeilleure.Common
|
||||
{
|
||||
_allocated.Dispose();
|
||||
|
||||
foreach (var page in _pages.Values)
|
||||
foreach (IntPtr page in _pages.Values)
|
||||
{
|
||||
NativeAllocator.Instance.Free((void*)page);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace ARMeilleure.Common
|
||||
|
||||
public override void* Allocate(ulong size)
|
||||
{
|
||||
void* result = (void*)Marshal.AllocHGlobal((IntPtr)size);
|
||||
void* result = (void*)Marshal.AllocHGlobal((nint)size);
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
@@ -21,7 +21,7 @@ namespace ARMeilleure.Common
|
||||
|
||||
public override void Free(void* block)
|
||||
{
|
||||
Marshal.FreeHGlobal((IntPtr)block);
|
||||
Marshal.FreeHGlobal((nint)block);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -254,8 +254,7 @@ namespace ARMeilleure.Decoders
|
||||
}
|
||||
|
||||
// Compare and branch instructions are always conditional.
|
||||
if (opCode.Instruction.Name == InstName.Cbz ||
|
||||
opCode.Instruction.Name == InstName.Cbnz)
|
||||
if (opCode.Instruction.Name is InstName.Cbz or InstName.Cbnz)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -284,7 +283,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 || opCode is IOpCode32MemMult)
|
||||
if (opCode is IOpCode32Mem or IOpCode32MemMult)
|
||||
{
|
||||
int rt, rn;
|
||||
|
||||
@@ -326,15 +325,12 @@ namespace ARMeilleure.Decoders
|
||||
}
|
||||
|
||||
// Explicit branch instructions.
|
||||
return opCode is IOpCode32BImm ||
|
||||
opCode is IOpCode32BReg;
|
||||
return opCode is IOpCode32BImm or IOpCode32BReg;
|
||||
}
|
||||
|
||||
private static bool IsCall(OpCode opCode)
|
||||
{
|
||||
return opCode.Instruction.Name == InstName.Bl ||
|
||||
opCode.Instruction.Name == InstName.Blr ||
|
||||
opCode.Instruction.Name == InstName.Blx;
|
||||
return opCode.Instruction.Name is InstName.Bl or InstName.Blr or InstName.Blx;
|
||||
}
|
||||
|
||||
private static bool IsException(OpCode opCode)
|
||||
@@ -344,9 +340,7 @@ namespace ARMeilleure.Decoders
|
||||
|
||||
private static bool IsTrap(OpCode opCode)
|
||||
{
|
||||
return opCode.Instruction.Name == InstName.Brk ||
|
||||
opCode.Instruction.Name == InstName.Trap ||
|
||||
opCode.Instruction.Name == InstName.Und;
|
||||
return opCode.Instruction.Name is InstName.Brk or InstName.Trap or 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)
|
||||
{
|
||||
var opc = (opCode >> 16) & 0xf;
|
||||
int opc = (opCode >> 16) & 0xf;
|
||||
|
||||
if ((opc & 0b1) == 1)
|
||||
{
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace ARMeilleure.Decoders
|
||||
Op = (opCode >> 20) & 0x1;
|
||||
U = ((opCode >> 23) & 1) != 0;
|
||||
|
||||
var opc = (((opCode >> 23) & 1) << 4) | (((opCode >> 21) & 0x3) << 2) | ((opCode >> 5) & 0x3);
|
||||
int opc = (((opCode >> 23) & 1) << 4) | (((opCode >> 21) & 0x3) << 2) | ((opCode >> 5) & 0x3);
|
||||
|
||||
if ((opc & 0b01000) == 0b01000)
|
||||
{
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace ARMeilleure.Decoders
|
||||
}
|
||||
else if (DataOp == DataOp.Logical)
|
||||
{
|
||||
var bm = DecoderHelper.DecodeBitMask(opCode, true);
|
||||
DecoderHelper.BitMask bm = DecoderHelper.DecodeBitMask(opCode, true);
|
||||
|
||||
if (bm.IsUndefined)
|
||||
{
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace ARMeilleure.Decoders
|
||||
|
||||
public OpCodeBfm(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
var bm = DecoderHelper.DecodeBitMask(opCode, false);
|
||||
DecoderHelper.BitMask bm = DecoderHelper.DecodeBitMask(opCode, false);
|
||||
|
||||
if (bm.IsUndefined)
|
||||
{
|
||||
|
||||
@@ -28,8 +28,7 @@ namespace ARMeilleure.Decoders
|
||||
MemOp type = WBack ? (MemOp)((opCode >> 10) & 3) : MemOp.Unsigned;
|
||||
|
||||
PostIdx = type == MemOp.PostIndexed;
|
||||
Unscaled = type == MemOp.Unscaled ||
|
||||
type == MemOp.Unprivileged;
|
||||
Unscaled = type is MemOp.Unscaled or 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
|
||||
}
|
||||
}
|
||||
|
||||
var newBlocks = new List<Block>(blocks.Count);
|
||||
List<Block> newBlocks = new(blocks.Count);
|
||||
|
||||
// Finally, rebuild decoded block list, ignoring blocks outside the contiguous range.
|
||||
for (int i = 0; i < blocks.Count; i++)
|
||||
|
||||
@@ -141,7 +141,7 @@ namespace ARMeilleure.Diagnostics
|
||||
break;
|
||||
|
||||
case OperandKind.Memory:
|
||||
var memOp = operand.GetMemory();
|
||||
MemoryOperand memOp = operand.GetMemory();
|
||||
|
||||
_builder.Append('[');
|
||||
|
||||
@@ -235,8 +235,7 @@ namespace ARMeilleure.Diagnostics
|
||||
{
|
||||
_builder.Append('.').Append(operation.Intrinsic);
|
||||
}
|
||||
else if (operation.Instruction == Instruction.BranchIf ||
|
||||
operation.Instruction == Instruction.Compare)
|
||||
else if (operation.Instruction is Instruction.BranchIf or Instruction.Compare)
|
||||
{
|
||||
comparison = true;
|
||||
}
|
||||
@@ -285,7 +284,7 @@ namespace ARMeilleure.Diagnostics
|
||||
|
||||
public static string GetDump(ControlFlowGraph cfg)
|
||||
{
|
||||
var dumper = new IRDumper(1);
|
||||
IRDumper dumper = new(1);
|
||||
|
||||
for (BasicBlock block = cfg.Blocks.First; block != null; block = block.ListNext)
|
||||
{
|
||||
|
||||
@@ -415,7 +415,7 @@ namespace ARMeilleure.Instructions
|
||||
{
|
||||
IOpCode32AluBf op = (IOpCode32AluBf)context.CurrOp;
|
||||
|
||||
var msb = op.Lsb + op.Msb; // For this instruction, the msb is actually a width.
|
||||
int msb = op.Lsb + op.Msb; // For this instruction, the msb is actually a width.
|
||||
|
||||
Operand n = GetIntA32(context, op.Rn);
|
||||
Operand 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;
|
||||
|
||||
var msb = op.Lsb + op.Msb; // For this instruction, the msb is actually a width.
|
||||
int msb = op.Lsb + op.Msb; // For this instruction, the msb is actually a width.
|
||||
|
||||
Operand n = GetIntA32(context, op.Rn);
|
||||
Operand res = context.ShiftRightUI(context.ShiftLeft(n, Const(31 - msb)), Const(31 - op.Msb));
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
context.LoadFromContext();
|
||||
|
||||
context.Return(Const(op.Address));
|
||||
InstEmitFlowHelper.EmitReturn(context, Const(op.Address));
|
||||
}
|
||||
|
||||
public static void Svc(ArmEmitterContext context)
|
||||
@@ -49,7 +49,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
context.LoadFromContext();
|
||||
|
||||
context.Return(Const(op.Address));
|
||||
InstEmitFlowHelper.EmitReturn(context, Const(op.Address));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
context.LoadFromContext();
|
||||
|
||||
context.Return(Const(context.CurrOp.Address));
|
||||
InstEmitFlowHelper.EmitReturn(context, Const(context.CurrOp.Address));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ namespace ARMeilleure.Instructions
|
||||
{
|
||||
OpCodeBReg op = (OpCodeBReg)context.CurrOp;
|
||||
|
||||
context.Return(GetIntOrZR(context, op.Rn));
|
||||
EmitReturn(context, GetIntOrZR(context, op.Rn));
|
||||
}
|
||||
|
||||
public static void Tbnz(ArmEmitterContext context) => EmitTb(context, onNotZero: true);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using ARMeilleure.CodeGen.Linking;
|
||||
using ARMeilleure.Common;
|
||||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.State;
|
||||
@@ -12,6 +13,10 @@ 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)
|
||||
@@ -163,12 +168,7 @@ namespace ARMeilleure.Instructions
|
||||
{
|
||||
if (isReturn)
|
||||
{
|
||||
if (target.Type == OperandType.I32)
|
||||
{
|
||||
target = context.ZeroExtend32(OperandType.I64, target);
|
||||
}
|
||||
|
||||
context.Return(target);
|
||||
EmitReturn(context, target);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -176,6 +176,19 @@ 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();
|
||||
@@ -193,7 +206,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
Operand hostAddress;
|
||||
|
||||
var table = context.FunctionTable;
|
||||
IAddressTable<ulong> table = context.FunctionTable;
|
||||
|
||||
// If address is mapped onto the function table, we can skip the table walk. Otherwise we fallback
|
||||
// onto the dispatch stub.
|
||||
@@ -218,7 +231,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
for (int i = 0; i < table.Levels.Length; i++)
|
||||
{
|
||||
var level = table.Levels[i];
|
||||
AddressTableLevel level = table.Levels[i];
|
||||
int clearBits = 64 - (level.Index + level.Length);
|
||||
|
||||
Operand index = context.ShiftLeft(
|
||||
@@ -238,6 +251,8 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
if (isJump)
|
||||
{
|
||||
DecreaseCallDepth(context, nativeContext);
|
||||
|
||||
context.Tailcall(hostAddress, nativeContext);
|
||||
}
|
||||
else
|
||||
@@ -259,8 +274,42 @@ 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 == 2 || op.Size == 3, "Invalid size for pairwise store.");
|
||||
Debug.Assert(op.Size is 2 or 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));
|
||||
|
||||
var exclusive = (accType & AccessType.Exclusive) != 0;
|
||||
var ordered = (accType & AccessType.Ordered) != 0;
|
||||
bool exclusive = (accType & AccessType.Exclusive) != 0;
|
||||
bool ordered = (accType & AccessType.Ordered) != 0;
|
||||
|
||||
if ((accType & AccessType.Load) != 0)
|
||||
{
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace ARMeilleure.Instructions
|
||||
{
|
||||
Operand value = GetInt(context, rt);
|
||||
|
||||
if (ext == Extension.Sx32 || ext == Extension.Sx64)
|
||||
if (ext is Extension.Sx32 or Extension.Sx64)
|
||||
{
|
||||
OperandType destType = ext == Extension.Sx64 ? OperandType.I64 : OperandType.I32;
|
||||
|
||||
@@ -124,8 +124,7 @@ namespace ARMeilleure.Instructions
|
||||
private static bool IsSimd(ArmEmitterContext context)
|
||||
{
|
||||
return context.CurrOp is IOpCodeSimd &&
|
||||
!(context.CurrOp is OpCodeSimdMemMs ||
|
||||
context.CurrOp is OpCodeSimdMemSs);
|
||||
!(context.CurrOp is OpCodeSimdMemMs or 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)
|
||||
{
|
||||
var ones = (baseType == OperandType.I64) ? Const(-1L) : Const(-1);
|
||||
Operand ones = (baseType == OperandType.I64) ? Const(-1L) : Const(-1);
|
||||
|
||||
return context.ConditionalSelect(fromBool, ones, Const(baseType, 0L));
|
||||
}
|
||||
|
||||
@@ -1119,7 +1119,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
private static Operand EmitFPConvert(ArmEmitterContext context, Operand value, int size, bool signed)
|
||||
{
|
||||
Debug.Assert(value.Type == OperandType.I32 || value.Type == OperandType.I64);
|
||||
Debug.Assert(value.Type is OperandType.I32 or 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 == OperandType.FP32 || value.Type == OperandType.FP64);
|
||||
Debug.Assert(value.Type is OperandType.FP32 or 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 == OperandType.FP32 || value.Type == OperandType.FP64);
|
||||
Debug.Assert(value.Type is OperandType.FP32 or 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 == OperandType.FP32 || value.Type == OperandType.FP64);
|
||||
Debug.Assert(value.Type is OperandType.FP32 or 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 == OperandType.FP32 || value.Type == OperandType.FP64);
|
||||
Debug.Assert(value.Type is OperandType.FP32 or OperandType.FP64);
|
||||
|
||||
if (fBits == 0)
|
||||
{
|
||||
|
||||
@@ -118,15 +118,15 @@ namespace ARMeilleure.Instructions
|
||||
{
|
||||
OpCode32SimdCvtFFixed op = (OpCode32SimdCvtFFixed)context.CurrOp;
|
||||
|
||||
var toFixed = op.Opc == 1;
|
||||
bool toFixed = op.Opc == 1;
|
||||
int fracBits = op.Fbits;
|
||||
var unsigned = op.U;
|
||||
bool unsigned = op.U;
|
||||
|
||||
if (toFixed) // F32 to S32 or U32 (fixed)
|
||||
{
|
||||
EmitVectorUnaryOpF32(context, (op1) =>
|
||||
{
|
||||
var scaledValue = context.Multiply(op1, ConstF(MathF.Pow(2f, fracBits)));
|
||||
Operand scaledValue = context.Multiply(op1, ConstF(MathF.Pow(2f, fracBits)));
|
||||
MethodInfo info = unsigned ? typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToU32)) : typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToS32));
|
||||
|
||||
return context.Call(info, scaledValue);
|
||||
@@ -136,7 +136,7 @@ namespace ARMeilleure.Instructions
|
||||
{
|
||||
EmitVectorUnaryOpI32(context, (op1) =>
|
||||
{
|
||||
var floatValue = unsigned ? context.ConvertToFPUI(OperandType.FP32, op1) : context.ConvertToFP(OperandType.FP32, op1);
|
||||
Operand floatValue = unsigned ? context.ConvertToFPUI(OperandType.FP32, op1) : context.ConvertToFP(OperandType.FP32, op1);
|
||||
|
||||
return context.Multiply(floatValue, ConstF(1f / MathF.Pow(2f, fracBits)));
|
||||
}, !unsigned);
|
||||
@@ -635,7 +635,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
private static Operand EmitFPConvert(ArmEmitterContext context, Operand value, OperandType type, bool signed)
|
||||
{
|
||||
Debug.Assert(value.Type == OperandType.I32 || value.Type == OperandType.I64);
|
||||
Debug.Assert(value.Type is OperandType.I32 or 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 == OperandType.I32 || op.Type == OperandType.I64);
|
||||
Debug.Assert(op.Type is OperandType.I32 or 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 == OperandType.FP32 || op.Type == OperandType.FP64);
|
||||
Debug.Assert(op.Type is OperandType.FP32 or 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 == 8 || eSize == 16 || eSize == 32 || eSize == 64);
|
||||
Debug.Assert(eSize is 8 or 16 or 32 or 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 == 8 || eSize == 16 || eSize == 32 || eSize == 64);
|
||||
Debug.Assert(eSize is 8 or 16 or 32 or 64);
|
||||
|
||||
Operand lblEnd = Label();
|
||||
|
||||
@@ -1735,7 +1735,7 @@ namespace ARMeilleure.Instructions
|
||||
int eSizeDst = 8 << sizeDst;
|
||||
|
||||
Debug.Assert(op.Type == OperandType.I64);
|
||||
Debug.Assert(eSizeDst == 8 || eSizeDst == 16 || eSizeDst == 32);
|
||||
Debug.Assert(eSizeDst is 8 or 16 or 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 == 8 || eSizeDst == 16 || eSizeDst == 32);
|
||||
Debug.Assert(eSizeDst is 8 or 16 or 32);
|
||||
|
||||
Operand lblEnd = Label();
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace ARMeilleure.Instructions
|
||||
{
|
||||
Debug.Assert(type != OperandType.V128);
|
||||
|
||||
if (type == OperandType.FP64 || type == OperandType.I64)
|
||||
if (type is OperandType.FP64 or 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 == OperandType.FP64 || value.Type == OperandType.I64)
|
||||
if (value.Type is OperandType.FP64 or 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 == OperandType.FP32 || value.Type == OperandType.I32);
|
||||
Debug.Assert(value.Type is OperandType.FP32 or OperandType.I32);
|
||||
|
||||
Operand vec, insert;
|
||||
vec = GetVecA32(reg >> 2);
|
||||
|
||||
@@ -87,7 +87,7 @@ namespace ARMeilleure.Instructions
|
||||
{
|
||||
if (op.Replicate)
|
||||
{
|
||||
var regs = (count > 1) ? 1 : op.Increment;
|
||||
int 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 == 8 || eSize == 16 || eSize == 32 || eSize == 64);
|
||||
Debug.Assert(eSize is 8 or 16 or 32 or 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 == 8 || eSize == 16 || eSize == 32 || eSize == 64);
|
||||
Debug.Assert(eSize is 8 or 16 or 32 or 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 == 8 || eSize == 16 || eSize == 32 || eSize == 64);
|
||||
Debug.Assert(eSize is 8 or 16 or 32 or 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 == 8 || eSize == 16 || eSize == 32 || eSize == 64);
|
||||
Debug.Assert(eSize is 8 or 16 or 32 or 64);
|
||||
|
||||
Operand lbl1 = Label();
|
||||
Operand lbl2 = Label();
|
||||
|
||||
@@ -330,7 +330,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
float result;
|
||||
|
||||
if (type == FPType.SNaN || type == FPType.QNaN)
|
||||
if (type is FPType.SNaN or FPType.QNaN)
|
||||
{
|
||||
if ((context.Fpcr & FPCR.Dn) != 0)
|
||||
{
|
||||
@@ -510,7 +510,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
double result;
|
||||
|
||||
if (type == FPType.SNaN || type == FPType.QNaN)
|
||||
if (type is FPType.SNaN or FPType.QNaN)
|
||||
{
|
||||
if ((context.Fpcr & FPCR.Dn) != 0)
|
||||
{
|
||||
@@ -692,7 +692,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
ushort resultBits;
|
||||
|
||||
if (type == FPType.SNaN || type == FPType.QNaN)
|
||||
if (type is FPType.SNaN or FPType.QNaN)
|
||||
{
|
||||
if (altHp)
|
||||
{
|
||||
@@ -1670,7 +1670,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
float result;
|
||||
|
||||
if (type == FPType.SNaN || type == FPType.QNaN)
|
||||
if (type is FPType.SNaN or FPType.QNaN)
|
||||
{
|
||||
result = FPProcessNaN(type, op, context, fpcr);
|
||||
}
|
||||
@@ -1686,7 +1686,7 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
else if (MathF.Abs(value) < MathF.Pow(2f, -128))
|
||||
{
|
||||
var overflowToInf = fpcr.GetRoundingMode() switch
|
||||
bool overflowToInf = fpcr.GetRoundingMode() switch
|
||||
{
|
||||
FPRoundingMode.ToNearest => true,
|
||||
FPRoundingMode.TowardsPlusInfinity => !sign,
|
||||
@@ -1849,7 +1849,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
float result;
|
||||
|
||||
if (type == FPType.SNaN || type == FPType.QNaN)
|
||||
if (type is FPType.SNaN or FPType.QNaN)
|
||||
{
|
||||
result = FPProcessNaN(type, op, context, fpcr);
|
||||
}
|
||||
@@ -1894,7 +1894,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
float result;
|
||||
|
||||
if (type == FPType.SNaN || type == FPType.QNaN)
|
||||
if (type is FPType.SNaN or FPType.QNaN)
|
||||
{
|
||||
result = FPProcessNaN(type, op, context, fpcr);
|
||||
}
|
||||
@@ -2100,7 +2100,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
float result;
|
||||
|
||||
if (type == FPType.SNaN || type == FPType.QNaN)
|
||||
if (type is FPType.SNaN or FPType.QNaN)
|
||||
{
|
||||
result = FPProcessNaN(type, op, context, fpcr);
|
||||
}
|
||||
@@ -2399,7 +2399,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
ushort resultBits;
|
||||
|
||||
if (type == FPType.SNaN || type == FPType.QNaN)
|
||||
if (type is FPType.SNaN or FPType.QNaN)
|
||||
{
|
||||
if (altHp)
|
||||
{
|
||||
@@ -3377,7 +3377,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
double result;
|
||||
|
||||
if (type == FPType.SNaN || type == FPType.QNaN)
|
||||
if (type is FPType.SNaN or FPType.QNaN)
|
||||
{
|
||||
result = FPProcessNaN(type, op, context, fpcr);
|
||||
}
|
||||
@@ -3393,7 +3393,7 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
else if (Math.Abs(value) < Math.Pow(2d, -1024))
|
||||
{
|
||||
var overflowToInf = fpcr.GetRoundingMode() switch
|
||||
bool overflowToInf = fpcr.GetRoundingMode() switch
|
||||
{
|
||||
FPRoundingMode.ToNearest => true,
|
||||
FPRoundingMode.TowardsPlusInfinity => !sign,
|
||||
@@ -3556,7 +3556,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
double result;
|
||||
|
||||
if (type == FPType.SNaN || type == FPType.QNaN)
|
||||
if (type is FPType.SNaN or FPType.QNaN)
|
||||
{
|
||||
result = FPProcessNaN(type, op, context, fpcr);
|
||||
}
|
||||
@@ -3601,7 +3601,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
double result;
|
||||
|
||||
if (type == FPType.SNaN || type == FPType.QNaN)
|
||||
if (type is FPType.SNaN or FPType.QNaN)
|
||||
{
|
||||
result = FPProcessNaN(type, op, context, fpcr);
|
||||
}
|
||||
@@ -3807,7 +3807,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
double result;
|
||||
|
||||
if (type == FPType.SNaN || type == FPType.QNaN)
|
||||
if (type is FPType.SNaN or 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>() != IntPtr.Size)
|
||||
if (Unsafe.SizeOf<T>() != nint.Size)
|
||||
{
|
||||
throw new ArgumentException("T must be a reference type or a pointer sized struct.");
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
@@ -24,7 +23,7 @@ namespace ARMeilleure.IntermediateRepresentation
|
||||
{
|
||||
Debug.Assert(operand.Kind == OperandKind.Memory);
|
||||
|
||||
_data = (Data*)Unsafe.As<Operand, IntPtr>(ref operand);
|
||||
_data = (Data*)Unsafe.As<Operand, nint>(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);
|
||||
|
||||
var oldSpan = new Span<T>(data, count);
|
||||
Span<T> oldSpan = new(data, count);
|
||||
|
||||
capacity = newCapacity;
|
||||
data = Allocators.References.Allocate<T>(capacity);
|
||||
@@ -338,7 +338,7 @@ namespace ARMeilleure.IntermediateRepresentation
|
||||
throw new OverflowException();
|
||||
}
|
||||
|
||||
var oldSpan = new Span<T>(data, (int)count);
|
||||
Span<T> oldSpan = new(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
|
||||
{
|
||||
var span = new Span<T>(data, count);
|
||||
Span<T> span = new(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
|
||||
{
|
||||
var span = new Span<T>(data, (int)count);
|
||||
Span<T> span = new(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 == OperandKind.Constant || kind == OperandKind.Register)
|
||||
if (kind is OperandKind.Constant or OperandKind.Register)
|
||||
{
|
||||
uint hash = (uint)HashCode.Combine(kind, type, value);
|
||||
|
||||
|
||||
@@ -16,8 +16,7 @@ namespace ARMeilleure.IntermediateRepresentation
|
||||
{
|
||||
public static bool IsInteger(this OperandType type)
|
||||
{
|
||||
return type == OperandType.I32 ||
|
||||
type == OperandType.I64;
|
||||
return type is OperandType.I32 or OperandType.I64;
|
||||
}
|
||||
|
||||
public static RegisterType ToRegisterType(this OperandType type)
|
||||
|
||||
@@ -228,7 +228,7 @@ namespace ARMeilleure.IntermediateRepresentation
|
||||
|
||||
public readonly override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine((IntPtr)_data);
|
||||
return HashCode.Combine((nint)_data);
|
||||
}
|
||||
|
||||
public static bool operator ==(Operation a, Operation b)
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace ARMeilleure.Memory
|
||||
{
|
||||
public interface IJitMemoryBlock : IDisposable
|
||||
{
|
||||
IntPtr Pointer { get; }
|
||||
nint Pointer { get; }
|
||||
|
||||
void Commit(ulong offset, ulong size);
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace ARMeilleure.Memory
|
||||
{
|
||||
int AddressSpaceBits { get; }
|
||||
|
||||
IntPtr PageTablePointer { get; }
|
||||
nint PageTablePointer { get; }
|
||||
|
||||
MemoryManagerType Type { get; }
|
||||
|
||||
|
||||
@@ -47,12 +47,12 @@ namespace ARMeilleure.Memory
|
||||
{
|
||||
public static bool IsHostMapped(this MemoryManagerType type)
|
||||
{
|
||||
return type == MemoryManagerType.HostMapped || type == MemoryManagerType.HostMappedUnsafe;
|
||||
return type is MemoryManagerType.HostMapped or MemoryManagerType.HostMappedUnsafe;
|
||||
}
|
||||
|
||||
public static bool IsHostTracked(this MemoryManagerType type)
|
||||
{
|
||||
return type == MemoryManagerType.HostTracked || type == MemoryManagerType.HostTrackedUnsafe;
|
||||
return type is MemoryManagerType.HostTracked or MemoryManagerType.HostTrackedUnsafe;
|
||||
}
|
||||
|
||||
public static bool IsHostMappedOrTracked(this MemoryManagerType type)
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace ARMeilleure.Memory
|
||||
public IJitMemoryBlock Block { get; }
|
||||
public IJitMemoryAllocator Allocator { get; }
|
||||
|
||||
public IntPtr Pointer => Block.Pointer;
|
||||
public nint Pointer => Block.Pointer;
|
||||
|
||||
private readonly ulong _maxSize;
|
||||
private readonly ulong _sizeGranularity;
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
@@ -8,6 +7,6 @@ namespace ARMeilleure.Native
|
||||
static partial class JitSupportDarwin
|
||||
{
|
||||
[LibraryImport("libarmeilleure-jitsupport", EntryPoint = "armeilleure_jit_memcpy")]
|
||||
public static partial void Copy(IntPtr dst, IntPtr src, ulong n);
|
||||
public static partial void Copy(nint dst, nint src, ulong n);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,10 +8,14 @@ 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 = 16;
|
||||
public const int MaxTrackedRanges = 8;
|
||||
|
||||
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, IntPtr signalStructPtr, Operand faultAddress, Operand isWrite, int rangeStructSize)
|
||||
private static Operand EmitGenericRegionCheck(EmitterContext context, nint 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(IntPtr signalStructPtr, int rangeStructSize)
|
||||
public static byte[] GenerateUnixSignalHandler(nint 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(IntPtr signalStructPtr, int rangeStructSize)
|
||||
public static byte[] GenerateWindowsSignalHandler(nint signalStructPtr, int rangeStructSize)
|
||||
{
|
||||
EmitterContext context = new();
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.Translation;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||
|
||||
@@ -16,13 +15,13 @@ namespace ARMeilleure.Signal
|
||||
{
|
||||
public delegate bool DebugPartialUnmap();
|
||||
public delegate int DebugThreadLocalMapGetOrReserve(int threadId, int initialState);
|
||||
public delegate void DebugNativeWriteLoop(IntPtr nativeWriteLoopPtr, IntPtr writePtr);
|
||||
public delegate void DebugNativeWriteLoop(nint nativeWriteLoopPtr, nint writePtr);
|
||||
|
||||
public static DebugPartialUnmap GenerateDebugPartialUnmap()
|
||||
{
|
||||
EmitterContext context = new();
|
||||
|
||||
var result = WindowsPartialUnmapHandler.EmitRetryFromAccessViolation(context);
|
||||
Operand result = WindowsPartialUnmapHandler.EmitRetryFromAccessViolation(context);
|
||||
|
||||
context.Return(result);
|
||||
|
||||
@@ -35,11 +34,11 @@ namespace ARMeilleure.Signal
|
||||
return Compiler.Compile(cfg, argTypes, OperandType.I32, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<DebugPartialUnmap>();
|
||||
}
|
||||
|
||||
public static DebugThreadLocalMapGetOrReserve GenerateDebugThreadLocalMapGetOrReserve(IntPtr structPtr)
|
||||
public static DebugThreadLocalMapGetOrReserve GenerateDebugThreadLocalMapGetOrReserve(nint structPtr)
|
||||
{
|
||||
EmitterContext context = new();
|
||||
|
||||
var result = WindowsPartialUnmapHandler.EmitThreadLocalMapIntGetOrReserve(context, structPtr, context.LoadArgument(OperandType.I32, 0), context.LoadArgument(OperandType.I32, 1));
|
||||
Operand result = WindowsPartialUnmapHandler.EmitThreadLocalMapIntGetOrReserve(context, structPtr, context.LoadArgument(OperandType.I32, 0), context.LoadArgument(OperandType.I32, 1));
|
||||
|
||||
context.Return(result);
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.Translation;
|
||||
using Ryujinx.Common.Memory.PartialUnmaps;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||
|
||||
@@ -13,18 +12,18 @@ namespace ARMeilleure.Signal
|
||||
internal static partial class WindowsPartialUnmapHandler
|
||||
{
|
||||
[LibraryImport("kernel32.dll", SetLastError = true, EntryPoint = "LoadLibraryA")]
|
||||
private static partial IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)] string lpFileName);
|
||||
private static partial nint LoadLibrary([MarshalAs(UnmanagedType.LPStr)] string lpFileName);
|
||||
|
||||
[LibraryImport("kernel32.dll", SetLastError = true)]
|
||||
private static partial IntPtr GetProcAddress(IntPtr hModule, [MarshalAs(UnmanagedType.LPStr)] string procName);
|
||||
private static partial nint GetProcAddress(nint hModule, [MarshalAs(UnmanagedType.LPStr)] string procName);
|
||||
|
||||
private static IntPtr _getCurrentThreadIdPtr;
|
||||
private static nint _getCurrentThreadIdPtr;
|
||||
|
||||
public static IntPtr GetCurrentThreadIdFunc()
|
||||
public static nint GetCurrentThreadIdFunc()
|
||||
{
|
||||
if (_getCurrentThreadIdPtr == IntPtr.Zero)
|
||||
if (_getCurrentThreadIdPtr == nint.Zero)
|
||||
{
|
||||
IntPtr handle = LoadLibrary("kernel32.dll");
|
||||
nint handle = LoadLibrary("kernel32.dll");
|
||||
|
||||
_getCurrentThreadIdPtr = GetProcAddress(handle, "GetCurrentThreadId");
|
||||
}
|
||||
@@ -34,13 +33,13 @@ namespace ARMeilleure.Signal
|
||||
|
||||
public static Operand EmitRetryFromAccessViolation(EmitterContext context)
|
||||
{
|
||||
IntPtr partialRemapStatePtr = PartialUnmapState.GlobalState;
|
||||
IntPtr localCountsPtr = IntPtr.Add(partialRemapStatePtr, PartialUnmapState.LocalCountsOffset);
|
||||
nint partialRemapStatePtr = PartialUnmapState.GlobalState;
|
||||
nint localCountsPtr = nint.Add(partialRemapStatePtr, PartialUnmapState.LocalCountsOffset);
|
||||
|
||||
// Get the lock first.
|
||||
EmitNativeReaderLockAcquire(context, IntPtr.Add(partialRemapStatePtr, PartialUnmapState.PartialUnmapLockOffset));
|
||||
EmitNativeReaderLockAcquire(context, nint.Add(partialRemapStatePtr, PartialUnmapState.PartialUnmapLockOffset));
|
||||
|
||||
IntPtr getCurrentThreadId = GetCurrentThreadIdFunc();
|
||||
nint getCurrentThreadId = GetCurrentThreadIdFunc();
|
||||
Operand threadId = context.Call(Const((ulong)getCurrentThreadId), OperandType.I32);
|
||||
Operand threadIndex = EmitThreadLocalMapIntGetOrReserve(context, localCountsPtr, threadId, Const(0));
|
||||
|
||||
@@ -58,7 +57,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)IntPtr.Add(partialRemapStatePtr, PartialUnmapState.PartialUnmapsCountOffset)));
|
||||
Operand partialUnmapsCount = context.Load(OperandType.I32, Const((ulong)nint.Add(partialRemapStatePtr, PartialUnmapState.PartialUnmapsCountOffset)));
|
||||
|
||||
context.Copy(retry, context.ICompareNotEqual(threadLocalPartialUnmaps, partialUnmapsCount));
|
||||
|
||||
@@ -79,14 +78,14 @@ namespace ARMeilleure.Signal
|
||||
context.MarkLabel(endLabel);
|
||||
|
||||
// Finally, release the lock and return the retry value.
|
||||
EmitNativeReaderLockRelease(context, IntPtr.Add(partialRemapStatePtr, PartialUnmapState.PartialUnmapLockOffset));
|
||||
EmitNativeReaderLockRelease(context, nint.Add(partialRemapStatePtr, PartialUnmapState.PartialUnmapLockOffset));
|
||||
|
||||
return retry;
|
||||
}
|
||||
|
||||
public static Operand EmitThreadLocalMapIntGetOrReserve(EmitterContext context, IntPtr threadLocalMapPtr, Operand threadId, Operand initialState)
|
||||
public static Operand EmitThreadLocalMapIntGetOrReserve(EmitterContext context, nint threadLocalMapPtr, Operand threadId, Operand initialState)
|
||||
{
|
||||
Operand idsPtr = Const((ulong)IntPtr.Add(threadLocalMapPtr, ThreadLocalMap<int>.ThreadIdsOffset));
|
||||
Operand idsPtr = Const((ulong)nint.Add(threadLocalMapPtr, ThreadLocalMap<int>.ThreadIdsOffset));
|
||||
|
||||
Operand i = context.AllocateLocal(OperandType.I32);
|
||||
|
||||
@@ -130,7 +129,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)IntPtr.Add(threadLocalMapPtr, ThreadLocalMap<int>.StructsOffset));
|
||||
Operand structsPtr = Const((ulong)nint.Add(threadLocalMapPtr, ThreadLocalMap<int>.StructsOffset));
|
||||
Operand structPtr = context.Add(structsPtr, context.SignExtend32(OperandType.I64, offset2));
|
||||
context.Store(structPtr, initialState);
|
||||
|
||||
@@ -149,10 +148,10 @@ namespace ARMeilleure.Signal
|
||||
return context.Copy(i);
|
||||
}
|
||||
|
||||
private static Operand EmitThreadLocalMapIntGetValuePtr(EmitterContext context, IntPtr threadLocalMapPtr, Operand index)
|
||||
private static Operand EmitThreadLocalMapIntGetValuePtr(EmitterContext context, nint threadLocalMapPtr, Operand index)
|
||||
{
|
||||
Operand offset = context.Multiply(index, Const(sizeof(int)));
|
||||
Operand structsPtr = Const((ulong)IntPtr.Add(threadLocalMapPtr, ThreadLocalMap<int>.StructsOffset));
|
||||
Operand structsPtr = Const((ulong)nint.Add(threadLocalMapPtr, ThreadLocalMap<int>.StructsOffset));
|
||||
|
||||
return context.Add(structsPtr, context.SignExtend32(OperandType.I64, offset));
|
||||
}
|
||||
@@ -170,9 +169,9 @@ namespace ARMeilleure.Signal
|
||||
context.BranchIfFalse(loop, context.ICompareEqual(initial, replaced));
|
||||
}
|
||||
|
||||
private static void EmitNativeReaderLockAcquire(EmitterContext context, IntPtr nativeReaderLockPtr)
|
||||
private static void EmitNativeReaderLockAcquire(EmitterContext context, nint nativeReaderLockPtr)
|
||||
{
|
||||
Operand writeLockPtr = Const((ulong)IntPtr.Add(nativeReaderLockPtr, NativeReaderWriterLock.WriteLockOffset));
|
||||
Operand writeLockPtr = Const((ulong)nint.Add(nativeReaderLockPtr, NativeReaderWriterLock.WriteLockOffset));
|
||||
|
||||
// Spin until we can acquire the write lock.
|
||||
Operand spinLabel = Label();
|
||||
@@ -182,16 +181,16 @@ namespace ARMeilleure.Signal
|
||||
context.BranchIfTrue(spinLabel, context.CompareAndSwap(writeLockPtr, Const(0), Const(1)));
|
||||
|
||||
// Increment reader count.
|
||||
EmitAtomicAddI32(context, Const((ulong)IntPtr.Add(nativeReaderLockPtr, NativeReaderWriterLock.ReaderCountOffset)), Const(1));
|
||||
EmitAtomicAddI32(context, Const((ulong)nint.Add(nativeReaderLockPtr, NativeReaderWriterLock.ReaderCountOffset)), Const(1));
|
||||
|
||||
// Release write lock.
|
||||
context.CompareAndSwap(writeLockPtr, Const(1), Const(0));
|
||||
}
|
||||
|
||||
private static void EmitNativeReaderLockRelease(EmitterContext context, IntPtr nativeReaderLockPtr)
|
||||
private static void EmitNativeReaderLockRelease(EmitterContext context, nint nativeReaderLockPtr)
|
||||
{
|
||||
// Decrement reader count.
|
||||
EmitAtomicAddI32(context, Const((ulong)IntPtr.Add(nativeReaderLockPtr, NativeReaderWriterLock.ReaderCountOffset)), Const(-1));
|
||||
EmitAtomicAddI32(context, Const((ulong)nint.Add(nativeReaderLockPtr, NativeReaderWriterLock.ReaderCountOffset)), Const(-1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using ARMeilleure.Memory;
|
||||
using System;
|
||||
|
||||
namespace ARMeilleure.State
|
||||
{
|
||||
@@ -9,7 +8,7 @@ namespace ARMeilleure.State
|
||||
|
||||
private readonly NativeContext _nativeContext;
|
||||
|
||||
internal IntPtr NativeContextPtr => _nativeContext.BasePtr;
|
||||
internal nint NativeContextPtr => _nativeContext.BasePtr;
|
||||
|
||||
private bool _interrupted;
|
||||
|
||||
@@ -128,6 +127,11 @@ 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)
|
||||
|
||||
@@ -21,6 +21,7 @@ namespace ARMeilleure.State
|
||||
public ulong ExclusiveValueLow;
|
||||
public ulong ExclusiveValueHigh;
|
||||
public int Running;
|
||||
public int CallDepth;
|
||||
public long Tpidr2El0;
|
||||
}
|
||||
|
||||
@@ -28,7 +29,7 @@ namespace ARMeilleure.State
|
||||
|
||||
private readonly IJitMemoryBlock _block;
|
||||
|
||||
public IntPtr BasePtr => _block.Pointer;
|
||||
public nint BasePtr => _block.Pointer;
|
||||
|
||||
public NativeContext(IJitMemoryAllocator allocator)
|
||||
{
|
||||
@@ -186,6 +187,8 @@ 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)
|
||||
@@ -266,6 +269,11 @@ 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,7 +6,6 @@ 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;
|
||||
@@ -92,7 +91,7 @@ namespace ARMeilleure.Translation
|
||||
else
|
||||
{
|
||||
int index = Delegates.GetDelegateIndex(info);
|
||||
IntPtr funcPtr = Delegates.GetDelegateFuncPtrByIndex(index);
|
||||
nint funcPtr = Delegates.GetDelegateFuncPtrByIndex(index);
|
||||
|
||||
OperandType returnType = GetOperandType(info.ReturnType);
|
||||
|
||||
|
||||
@@ -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,83 +19,123 @@ namespace ARMeilleure.Translation.Cache
|
||||
private static readonly int _pageSize = (int)MemoryBlock.GetPageSize();
|
||||
private static readonly int _pageMask = _pageSize - 1;
|
||||
|
||||
private const int CodeAlignment = 4; // Bytes.
|
||||
private const int CacheSize = 256 * 1024 * 1024;
|
||||
private const int CodeAlignment = 4;
|
||||
private const int FullCacheSize = 2047 * 1024 * 1024;
|
||||
private const int ReducedCacheSize = FullCacheSize / 8;
|
||||
|
||||
private const float EvictionTargetPercentage = 0.20f;
|
||||
private const int MaxEntriesToEvictAtOnce = 100;
|
||||
|
||||
// Simple logging configuration
|
||||
private const int LogInterval = 5000; // Log every 5000 allocations
|
||||
|
||||
private static ReservedRegion _jitRegion;
|
||||
private static JitCacheInvalidation _jitCacheInvalidator;
|
||||
|
||||
private static List<CacheMemoryAllocator> _cacheAllocators = [];
|
||||
private static CacheMemoryAllocator _cacheAllocator;
|
||||
|
||||
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;
|
||||
|
||||
private static readonly List<ReservedRegion> _jitRegions = [];
|
||||
private static int _activeRegionIndex = 0;
|
||||
// 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++;
|
||||
}
|
||||
}
|
||||
|
||||
[SupportedOSPlatform("windows")]
|
||||
[LibraryImport("kernel32.dll", SetLastError = true)]
|
||||
public static partial IntPtr FlushInstructionCache(IntPtr hProcess, IntPtr lpAddress, UIntPtr dwSize);
|
||||
public static partial nint FlushInstructionCache(nint hProcess, nint lpAddress, nuint dwSize);
|
||||
|
||||
public static void Initialize(IJitMemoryAllocator allocator)
|
||||
{
|
||||
if (_initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
if (_initialized)
|
||||
{
|
||||
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;
|
||||
return;
|
||||
}
|
||||
|
||||
_activeRegionIndex = 0;
|
||||
|
||||
var firstRegion = new ReservedRegion(allocator, CacheSize);
|
||||
_jitRegions.Add(firstRegion);
|
||||
|
||||
CacheMemoryAllocator firstCacheAllocator = new(CacheSize);
|
||||
_cacheAllocators.Add(firstCacheAllocator);
|
||||
_cacheSize = Optimizations.CacheEviction ? ReducedCacheSize : FullCacheSize;
|
||||
_jitRegion = new ReservedRegion(allocator, (ulong)_cacheSize);
|
||||
|
||||
if (!OperatingSystem.IsWindows() && !OperatingSystem.IsMacOS())
|
||||
{
|
||||
_jitCacheInvalidator = new JitCacheInvalidation(allocator);
|
||||
}
|
||||
|
||||
_cacheAllocator = new CacheMemoryAllocator(_cacheSize);
|
||||
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
JitUnwindWindows.InstallFunctionTableHandler(
|
||||
firstRegion.Pointer, CacheSize, firstRegion.Pointer + Allocate(_pageSize)
|
||||
);
|
||||
JitUnwindWindows.InstallFunctionTableHandler(_jitRegion.Pointer, (uint)_cacheSize, _jitRegion.Pointer + Allocate(_pageSize));
|
||||
}
|
||||
|
||||
Logger.Info?.Print(LogClass.Cpu, $"JIT Cache initialized: Size={_cacheSize / (1024 * 1024)} MB, Eviction={Optimizations.CacheEviction}");
|
||||
_initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
public static IntPtr Map(CompiledFunction func)
|
||||
public static nint Map(CompiledFunction func)
|
||||
{
|
||||
byte[] code = func.Code;
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
Debug.Assert(_initialized);
|
||||
_totalAllocations++;
|
||||
|
||||
int funcOffset = Allocate(code.Length);
|
||||
ReservedRegion targetRegion = _jitRegions[_activeRegionIndex];
|
||||
IntPtr funcPtr = targetRegion.Pointer + funcOffset;
|
||||
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;
|
||||
|
||||
if (OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
||||
{
|
||||
@@ -103,19 +143,19 @@ namespace ARMeilleure.Translation.Cache
|
||||
{
|
||||
fixed (byte* codePtr = code)
|
||||
{
|
||||
JitSupportDarwin.Copy(funcPtr, (IntPtr)codePtr, (ulong)code.Length);
|
||||
JitSupportDarwin.Copy(funcPtr, (nint)codePtr, (ulong)code.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ReprotectAsWritable(targetRegion, funcOffset, code.Length);
|
||||
ReprotectAsWritable(funcOffset, code.Length);
|
||||
Marshal.Copy(code, 0, funcPtr, code.Length);
|
||||
ReprotectAsExecutable(targetRegion, funcOffset, code.Length);
|
||||
ReprotectAsExecutable(funcOffset, code.Length);
|
||||
|
||||
if (OperatingSystem.IsWindows() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
||||
{
|
||||
FlushInstructionCache(Process.GetCurrentProcess().Handle, funcPtr, (UIntPtr)code.Length);
|
||||
FlushInstructionCache(Process.GetCurrentProcess().Handle, funcPtr, (nuint)code.Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -125,84 +165,71 @@ namespace ARMeilleure.Translation.Cache
|
||||
|
||||
Add(funcOffset, code.Length, func.UnwindInfo);
|
||||
|
||||
// Simple periodic logging
|
||||
if (_totalAllocations % LogInterval == 0)
|
||||
{
|
||||
LogCacheStatus();
|
||||
}
|
||||
|
||||
return funcPtr;
|
||||
}
|
||||
}
|
||||
|
||||
public static void Unmap(IntPtr pointer)
|
||||
public static void Unmap(nint pointer)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
Debug.Assert(_initialized);
|
||||
|
||||
foreach (var region in _jitRegions)
|
||||
int funcOffset = (int)(pointer.ToInt64() - _jitRegion.Pointer.ToInt64());
|
||||
|
||||
if (TryFind(funcOffset, out CacheEntry entry, out int entryIndex) && entry.Offset == funcOffset)
|
||||
{
|
||||
if (pointer.ToInt64() < region.Pointer.ToInt64() ||
|
||||
pointer.ToInt64() >= (region.Pointer + CacheSize).ToInt64())
|
||||
_cacheAllocator.Free(funcOffset, AlignCodeSize(entry.Size));
|
||||
_cacheEntries.RemoveAt(entryIndex);
|
||||
|
||||
if (Optimizations.CacheEviction)
|
||||
{
|
||||
continue;
|
||||
_entryUsageStats.Remove(funcOffset);
|
||||
}
|
||||
|
||||
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(ReservedRegion region, int offset, int size)
|
||||
private static void ReprotectAsWritable(int offset, int size)
|
||||
{
|
||||
int endOffs = offset + size;
|
||||
|
||||
int regionStart = offset & ~_pageMask;
|
||||
int regionEnd = (endOffs + _pageMask) & ~_pageMask;
|
||||
|
||||
region.Block.MapAsRwx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
||||
_jitRegion.Block.MapAsRwx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
||||
}
|
||||
|
||||
private static void ReprotectAsExecutable(ReservedRegion region, int offset, int size)
|
||||
private static void ReprotectAsExecutable(int offset, int size)
|
||||
{
|
||||
int endOffs = offset + size;
|
||||
|
||||
int regionStart = offset & ~_pageMask;
|
||||
int regionEnd = (endOffs + _pageMask) & ~_pageMask;
|
||||
|
||||
region.Block.MapAsRx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
||||
_jitRegion.Block.MapAsRx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
||||
}
|
||||
|
||||
private static int Allocate(int codeSize)
|
||||
{
|
||||
codeSize = AlignCodeSize(codeSize);
|
||||
|
||||
int allocOffset = _cacheAllocators[_activeRegionIndex].Allocate(codeSize);
|
||||
int allocOffset = _cacheAllocator.Allocate(codeSize);
|
||||
|
||||
if (allocOffset >= 0)
|
||||
if (allocOffset < 0)
|
||||
{
|
||||
_jitRegions[_activeRegionIndex].ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize);
|
||||
return allocOffset;
|
||||
throw new OutOfMemoryException("JIT Cache exhausted.");
|
||||
}
|
||||
|
||||
int exhaustedRegion = _activeRegionIndex;
|
||||
var newRegion = new ReservedRegion(_jitRegions[0].Allocator, CacheSize);
|
||||
_jitRegions.Add(newRegion);
|
||||
_activeRegionIndex = _jitRegions.Count - 1;
|
||||
_jitRegion.ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize);
|
||||
|
||||
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;
|
||||
return allocOffset;
|
||||
}
|
||||
|
||||
private static int AlignCodeSize(int codeSize)
|
||||
@@ -222,27 +249,35 @@ 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)
|
||||
{
|
||||
foreach (var region in _jitRegions)
|
||||
int index = _cacheEntries.BinarySearch(new CacheEntry(offset, 0, default));
|
||||
|
||||
if (index < 0)
|
||||
{
|
||||
int index = _cacheEntries.BinarySearch(new CacheEntry(offset, 0, default));
|
||||
index = ~index - 1;
|
||||
}
|
||||
|
||||
if (index < 0)
|
||||
if (index >= 0)
|
||||
{
|
||||
entry = _cacheEntries[index];
|
||||
|
||||
if (Optimizations.CacheEviction && _entryUsageStats.TryGetValue(offset, out EntryUsageStats stats))
|
||||
{
|
||||
index = ~index - 1;
|
||||
stats.UpdateUsage();
|
||||
}
|
||||
|
||||
if (index >= 0)
|
||||
{
|
||||
entry = _cacheEntries[index];
|
||||
entryIndex = index;
|
||||
return true;
|
||||
}
|
||||
entryIndex = index;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -250,5 +285,83 @@ 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,5 +1,4 @@
|
||||
using ARMeilleure.Memory;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace ARMeilleure.Translation.Cache
|
||||
@@ -68,7 +67,7 @@ namespace ARMeilleure.Translation.Cache
|
||||
}
|
||||
}
|
||||
|
||||
public void Invalidate(IntPtr basePointer, ulong size)
|
||||
public void Invalidate(nint basePointer, ulong size)
|
||||
{
|
||||
if (_needsInvalidation)
|
||||
{
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace ARMeilleure.Translation.Cache
|
||||
PushMachframe = 10,
|
||||
}
|
||||
|
||||
private unsafe delegate RuntimeFunction* GetRuntimeFunctionCallback(ulong controlPc, IntPtr context);
|
||||
private unsafe delegate RuntimeFunction* GetRuntimeFunctionCallback(ulong controlPc, nint context);
|
||||
|
||||
[LibraryImport("kernel32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
@@ -49,7 +49,7 @@ namespace ARMeilleure.Translation.Cache
|
||||
ulong baseAddress,
|
||||
uint length,
|
||||
GetRuntimeFunctionCallback callback,
|
||||
IntPtr context,
|
||||
nint 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(IntPtr codeCachePointer, uint codeCacheLength, IntPtr workBufferPtr)
|
||||
public static void InstallFunctionTableHandler(nint codeCachePointer, uint codeCacheLength, nint workBufferPtr)
|
||||
{
|
||||
ulong codeCachePtr = (ulong)codeCachePointer.ToInt64();
|
||||
|
||||
@@ -96,7 +96,7 @@ namespace ARMeilleure.Translation.Cache
|
||||
}
|
||||
}
|
||||
|
||||
public static void RemoveFunctionTableHandler(IntPtr codeCachePointer)
|
||||
public static void RemoveFunctionTableHandler(nint codeCachePointer)
|
||||
{
|
||||
ulong codeCachePtr = (ulong)codeCachePointer.ToInt64();
|
||||
|
||||
@@ -113,7 +113,7 @@ namespace ARMeilleure.Translation.Cache
|
||||
}
|
||||
}
|
||||
|
||||
private static unsafe RuntimeFunction* FunctionTableHandler(ulong controlPc, IntPtr context)
|
||||
private static unsafe RuntimeFunction* FunctionTableHandler(ulong controlPc, nint context)
|
||||
{
|
||||
int offset = (int)((long)controlPc - context.ToInt64());
|
||||
|
||||
@@ -122,13 +122,13 @@ namespace ARMeilleure.Translation.Cache
|
||||
return null; // Not found.
|
||||
}
|
||||
|
||||
var unwindInfo = funcEntry.UnwindInfo;
|
||||
CodeGen.Unwinding.UnwindInfo unwindInfo = funcEntry.UnwindInfo;
|
||||
|
||||
int codeIndex = 0;
|
||||
|
||||
for (int index = unwindInfo.PushEntries.Length - 1; index >= 0; index--)
|
||||
{
|
||||
var entry = unwindInfo.PushEntries[index];
|
||||
UnwindPushEntry entry = unwindInfo.PushEntries[index];
|
||||
|
||||
switch (entry.PseudoOp)
|
||||
{
|
||||
|
||||
@@ -47,8 +47,8 @@ namespace ARMeilleure.Translation
|
||||
{
|
||||
RemoveUnreachableBlocks(Blocks);
|
||||
|
||||
var visited = new HashSet<BasicBlock>();
|
||||
var blockStack = new Stack<BasicBlock>();
|
||||
HashSet<BasicBlock> visited = new();
|
||||
Stack<BasicBlock> blockStack = new();
|
||||
|
||||
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)
|
||||
{
|
||||
var visited = new HashSet<BasicBlock>();
|
||||
var workQueue = new Queue<BasicBlock>();
|
||||
HashSet<BasicBlock> visited = new();
|
||||
Queue<BasicBlock> workQueue = new();
|
||||
|
||||
visited.Add(Entry);
|
||||
workQueue.Enqueue(Entry);
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
using System;
|
||||
|
||||
namespace ARMeilleure.Translation
|
||||
{
|
||||
public class DelegateInfo
|
||||
{
|
||||
public IntPtr FuncPtr { get; }
|
||||
public nint FuncPtr { get; }
|
||||
|
||||
public DelegateInfo(IntPtr funcPtr)
|
||||
public DelegateInfo(nint funcPtr)
|
||||
{
|
||||
FuncPtr = funcPtr;
|
||||
}
|
||||
|
||||
@@ -354,7 +354,7 @@ namespace ARMeilleure.Translation
|
||||
|
||||
private static readonly Dictionary<string, Delegate> _androidDelegates;
|
||||
#endif
|
||||
public static bool TryGetDelegateFuncPtrByIndex(int index, out IntPtr funcPtr)
|
||||
public static bool TryGetDelegateFuncPtrByIndex(int index, out nint funcPtr)
|
||||
{
|
||||
if (index >= 0 && index < _delegates.Count)
|
||||
{
|
||||
@@ -370,7 +370,7 @@ namespace ARMeilleure.Translation
|
||||
}
|
||||
}
|
||||
|
||||
public static IntPtr GetDelegateFuncPtrByIndex(int index)
|
||||
public static nint 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))
|
||||
{
|
||||
IntPtr funcPtr = Marshal.GetFunctionPointerForDelegate(del);
|
||||
nint funcPtr = Marshal.GetFunctionPointerForDelegate(del);
|
||||
_delegates.Add(key, new DelegateInfo(funcPtr));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
using System;
|
||||
|
||||
namespace ARMeilleure.Translation
|
||||
{
|
||||
delegate void DispatcherFunction(IntPtr nativeContext, ulong startAddress);
|
||||
delegate ulong WrapperFunction(IntPtr nativeContext, ulong startAddress);
|
||||
delegate void DispatcherFunction(nint nativeContext, ulong startAddress);
|
||||
delegate ulong WrapperFunction(nint 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);
|
||||
IntPtr funcPtr = Delegates.GetDelegateFuncPtrByIndex(index);
|
||||
nint funcPtr = Delegates.GetDelegateFuncPtrByIndex(index);
|
||||
#else
|
||||
// For other platforms, use direct method handle approach
|
||||
IntPtr funcPtr = info.MethodHandle.GetFunctionPointer();
|
||||
nint funcPtr = info.MethodHandle.GetFunctionPointer();
|
||||
#endif
|
||||
|
||||
OperandType returnType = GetOperandType(info.ReturnType);
|
||||
@@ -675,8 +675,7 @@ namespace ARMeilleure.Translation
|
||||
Operation last = block.Operations.Last;
|
||||
|
||||
return last != default &&
|
||||
(last.Instruction == Instruction.Return ||
|
||||
last.Instruction == Instruction.Tailcall);
|
||||
last.Instruction is Instruction.Return or Instruction.Tailcall;
|
||||
}
|
||||
|
||||
public ControlFlowGraph GetControlFlowGraph()
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using System;
|
||||
|
||||
namespace ARMeilleure.Translation
|
||||
{
|
||||
delegate ulong GuestFunction(IntPtr nativeContextPtr);
|
||||
delegate ulong GuestFunction(nint nativeContextPtr);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ 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;
|
||||
@@ -30,14 +31,11 @@ namespace ARMeilleure.Translation.PTC
|
||||
private const string OuterHeaderMagicString = "PTCohd\0\0";
|
||||
private const string InnerHeaderMagicString = "PTCihd\0\0";
|
||||
|
||||
private const uint InternalVersion = 7008; //! To be incremented manually for each change to the ARMeilleure project.
|
||||
private const uint InternalVersion = 7031; //! To be incremented manually for each change to the ARMeilleure project.
|
||||
|
||||
private const string ActualDir = "0";
|
||||
private const string 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);
|
||||
@@ -63,8 +61,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
public string TitleIdText { get; private set; }
|
||||
public string DisplayVersion { get; private set; }
|
||||
public PtcCacheInfo CacheInfo { get; private set; }
|
||||
|
||||
private MemoryManagerType _memoryMode;
|
||||
|
||||
@@ -91,8 +88,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
|
||||
_disposed = false;
|
||||
|
||||
TitleIdText = TitleIdTextDefault;
|
||||
DisplayVersion = DisplayVersionDefault;
|
||||
CacheInfo = new PtcCacheInfo(0, null, null, 0, null, "Unknown", "default");
|
||||
|
||||
CachePathActual = string.Empty;
|
||||
CachePathBackup = string.Empty;
|
||||
@@ -100,20 +96,24 @@ namespace ARMeilleure.Translation.PTC
|
||||
Disable();
|
||||
}
|
||||
|
||||
public void Initialize(string titleIdText, string displayVersion, bool enabled, MemoryManagerType memoryMode, string cacheSelector)
|
||||
public void Initialize(PtcCacheInfo cacheInfo, bool enabled, MemoryManagerType memoryMode)
|
||||
{
|
||||
Wait();
|
||||
|
||||
Profiler.Wait();
|
||||
Profiler.ClearEntries();
|
||||
|
||||
Logger.Info?.Print(LogClass.Ptc, $"Initializing Profiled Persistent Translation Cache (enabled: {enabled}).");
|
||||
CacheInfo = cacheInfo;
|
||||
|
||||
if (!enabled || string.IsNullOrEmpty(titleIdText) || titleIdText == TitleIdTextDefault)
|
||||
Logger.Info?.Print(
|
||||
LogClass.Ptc,
|
||||
$"Initializing Profiled Persistent Translation Cache v{InternalVersion}\n\t\t " +
|
||||
$"(pid: {cacheInfo.ProcessId}, title: {cacheInfo.TitleIdText}, application: {cacheInfo.ApplicationIdText}, " +
|
||||
$"programIndex: {cacheInfo.ProgramIndex}, version: '{cacheInfo.DisplayVersion}', kind: {cacheInfo.ProcessKind}, " +
|
||||
$"selector: '{cacheInfo.CacheSelector}', key: '{cacheInfo.CacheKey}', enabled: {enabled}).");
|
||||
|
||||
if (!enabled || cacheInfo.TitleIdText == PtcCacheInfo.TitleIdTextDefault)
|
||||
{
|
||||
TitleIdText = TitleIdTextDefault;
|
||||
DisplayVersion = DisplayVersionDefault;
|
||||
|
||||
CachePathActual = string.Empty;
|
||||
CachePathBackup = string.Empty;
|
||||
|
||||
@@ -122,14 +122,10 @@ namespace ARMeilleure.Translation.PTC
|
||||
return;
|
||||
}
|
||||
|
||||
TitleIdText = titleIdText;
|
||||
DisplayVersion = !string.IsNullOrEmpty(displayVersion) ? displayVersion : DisplayVersionDefault;
|
||||
_memoryMode = memoryMode;
|
||||
|
||||
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);
|
||||
string workPathActual = Path.Combine(AppDataManager.GamesDirPath, CacheInfo.TitleIdText, "cache", "cpu", ActualDir);
|
||||
string workPathBackup = Path.Combine(AppDataManager.GamesDirPath, CacheInfo.TitleIdText, "cache", "cpu", BackupDir);
|
||||
|
||||
if (!Directory.Exists(workPathActual))
|
||||
{
|
||||
@@ -141,8 +137,14 @@ namespace ARMeilleure.Translation.PTC
|
||||
Directory.CreateDirectory(workPathBackup);
|
||||
}
|
||||
|
||||
CachePathActual = Path.Combine(workPathActual, DisplayVersion) + "-" + cacheSelector;
|
||||
CachePathBackup = Path.Combine(workPathBackup, DisplayVersion) + "-" + cacheSelector;
|
||||
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}').");
|
||||
|
||||
PreLoad();
|
||||
Profiler.PreLoad();
|
||||
@@ -190,7 +192,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
_infosStream.Seek(0L, SeekOrigin.Begin);
|
||||
bool foundBadFunction = false;
|
||||
|
||||
for (int index = 0; index < GetEntriesCount(); index++)
|
||||
for (int index = 0; index < _infosStream.Length / Unsafe.SizeOf<InfoEntry>(); index++)
|
||||
{
|
||||
InfoEntry infoEntry = DeserializeStructure<InfoEntry>(_infosStream);
|
||||
foreach (ulong address in blacklist)
|
||||
@@ -244,67 +246,25 @@ namespace ARMeilleure.Translation.PTC
|
||||
{
|
||||
OuterHeader outerHeader = DeserializeStructure<OuterHeader>(compressedStream);
|
||||
|
||||
if (!outerHeader.IsHeaderValid())
|
||||
if (!outerHeader.IsHeaderValid() ||
|
||||
outerHeader.Magic != _outerHeaderMagic ||
|
||||
outerHeader.CacheFileVersion != InternalVersion ||
|
||||
outerHeader.Endianness != GetEndianness() ||
|
||||
outerHeader.FeatureInfo != GetFeatureInfo() ||
|
||||
outerHeader.MemoryManagerMode != GetMemoryManagerMode() ||
|
||||
outerHeader.OSPlatform != GetOSPlatform() ||
|
||||
outerHeader.Architecture != (uint)RuntimeInformation.ProcessArchitecture)
|
||||
{
|
||||
InvalidateCompressedStream(compressedStream);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
nint intPtr = nint.Zero;
|
||||
|
||||
try
|
||||
{
|
||||
intPtr = Marshal.AllocHGlobal(new IntPtr(outerHeader.UncompressedStreamSize));
|
||||
intPtr = Marshal.AllocHGlobal(new nint(outerHeader.UncompressedStreamSize));
|
||||
|
||||
using UnmanagedMemoryStream stream = new((byte*)intPtr.ToPointer(), outerHeader.UncompressedStreamSize, outerHeader.UncompressedStreamSize, FileAccess.ReadWrite);
|
||||
try
|
||||
@@ -324,14 +284,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
|
||||
InnerHeader innerHeader = DeserializeStructure<InnerHeader>(stream);
|
||||
|
||||
if (!innerHeader.IsHeaderValid())
|
||||
{
|
||||
InvalidateCompressedStream(compressedStream);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (innerHeader.Magic != _innerHeaderMagic)
|
||||
if (!innerHeader.IsHeaderValid() || innerHeader.Magic != _innerHeaderMagic)
|
||||
{
|
||||
InvalidateCompressedStream(compressedStream);
|
||||
|
||||
@@ -405,7 +358,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (intPtr != IntPtr.Zero)
|
||||
if (intPtr != nint.Zero)
|
||||
{
|
||||
Marshal.FreeHGlobal(intPtr);
|
||||
}
|
||||
@@ -414,7 +367,12 @@ namespace ARMeilleure.Translation.PTC
|
||||
|
||||
long fileSize = new FileInfo(fileName).Length;
|
||||
|
||||
Logger.Info?.Print(LogClass.Ptc, $"{(isBackup ? "Loaded Backup Translation Cache" : "Loaded Translation Cache")} (size: {fileSize} bytes, translated functions: {GetEntriesCount()}).");
|
||||
Logger.Info?.Print(
|
||||
LogClass.Ptc,
|
||||
$"{(isBackup ? "Loaded Backup Translation Cache" : "Loaded Translation Cache")} " +
|
||||
$"(pid: {CacheInfo.ProcessId}, title: {CacheInfo.TitleIdText}, version: '{CacheInfo.DisplayVersion}', kind: {CacheInfo.ProcessKind}, " +
|
||||
$"selector: '{CacheInfo.CacheSelector}', key: '{CacheInfo.CacheKey}', path: '{fileName}', " +
|
||||
$"size: {fileSize} bytes, translated functions: {GetEntriesCount()}).");
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -487,11 +445,11 @@ namespace ARMeilleure.Translation.PTC
|
||||
|
||||
outerHeader.SetHeaderHash();
|
||||
|
||||
IntPtr intPtr = IntPtr.Zero;
|
||||
nint intPtr = nint.Zero;
|
||||
|
||||
try
|
||||
{
|
||||
intPtr = Marshal.AllocHGlobal(new IntPtr(outerHeader.UncompressedStreamSize));
|
||||
intPtr = Marshal.AllocHGlobal(new nint(outerHeader.UncompressedStreamSize));
|
||||
|
||||
using UnmanagedMemoryStream stream = new((byte*)intPtr.ToPointer(), outerHeader.UncompressedStreamSize, outerHeader.UncompressedStreamSize, FileAccess.ReadWrite);
|
||||
stream.Seek((long)Unsafe.SizeOf<InnerHeader>(), SeekOrigin.Begin);
|
||||
@@ -545,7 +503,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (intPtr != IntPtr.Zero)
|
||||
if (intPtr != nint.Zero)
|
||||
{
|
||||
Marshal.FreeHGlobal(intPtr);
|
||||
}
|
||||
@@ -593,7 +551,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 var value) && value.HighCq))
|
||||
if (isEntryChanged || (!infoEntry.HighCq && Profiler.ProfiledFuncs.TryGetValue(infoEntry.Address, out PtcProfiler.FuncProfile value) && value.HighCq))
|
||||
{
|
||||
infoEntry.Stubbed = true;
|
||||
infoEntry.CodeLength = 0;
|
||||
@@ -697,7 +655,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
|
||||
foreach (RelocEntry relocEntry in relocEntries)
|
||||
{
|
||||
IntPtr? imm = null;
|
||||
nint? imm = null;
|
||||
Symbol symbol = relocEntry.Symbol;
|
||||
|
||||
if (symbol.Type == SymbolType.FunctionTable)
|
||||
@@ -708,7 +666,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
imm = (IntPtr)Unsafe.AsPointer(ref translator.FunctionTable.GetValue(guestAddress));
|
||||
imm = (nint)Unsafe.AsPointer(ref translator.FunctionTable.GetValue(guestAddress));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -716,7 +674,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
{
|
||||
int index = (int)symbol.Value;
|
||||
|
||||
if (Delegates.TryGetDelegateFuncPtrByIndex(index, out IntPtr funcPtr))
|
||||
if (Delegates.TryGetDelegateFuncPtrByIndex(index, out nint funcPtr))
|
||||
{
|
||||
imm = funcPtr;
|
||||
}
|
||||
@@ -731,7 +689,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
|
||||
unsafe
|
||||
{
|
||||
imm = (IntPtr)Unsafe.AsPointer(ref callCounter.Value);
|
||||
imm = (nint)Unsafe.AsPointer(ref callCounter.Value);
|
||||
}
|
||||
}
|
||||
else if (symbol == DispatchStubSymbol)
|
||||
@@ -780,8 +738,8 @@ namespace ARMeilleure.Translation.PTC
|
||||
UnwindInfo unwindInfo,
|
||||
bool highCq)
|
||||
{
|
||||
var cFunc = new CompiledFunction(code, unwindInfo, RelocInfo.Empty);
|
||||
var gFunc = cFunc.MapWithPointer<GuestFunction>(out IntPtr gFuncPointer);
|
||||
CompiledFunction cFunc = new(code, unwindInfo, RelocInfo.Empty);
|
||||
GuestFunction gFunc = cFunc.MapWithPointer<GuestFunction>(out nint gFuncPointer);
|
||||
|
||||
return new TranslatedFunction(gFunc, gFuncPointer, callCounter, guestSize, highCq);
|
||||
}
|
||||
@@ -818,7 +776,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
|
||||
public void MakeAndSaveTranslations(Translator translator)
|
||||
{
|
||||
var profiledFuncsToTranslate = Profiler.GetProfiledFuncsToTranslate(translator.Functions);
|
||||
ConcurrentQueue<(ulong address, PtcProfiler.FuncProfile funcProfile)> profiledFuncsToTranslate = Profiler.GetProfiledFuncsToTranslate(translator.Functions);
|
||||
|
||||
_translateCount = 0;
|
||||
_translateTotalCount = profiledFuncsToTranslate.Count;
|
||||
@@ -862,7 +820,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
|
||||
void TranslateFuncs()
|
||||
{
|
||||
while (profiledFuncsToTranslate.TryDequeue(out var item))
|
||||
while (profiledFuncsToTranslate.TryDequeue(out (ulong address, PtcProfiler.FuncProfile funcProfile) item))
|
||||
{
|
||||
ulong address = item.address;
|
||||
ExecutionMode executionMode = item.funcProfile.Mode;
|
||||
@@ -907,11 +865,11 @@ namespace ARMeilleure.Translation.PTC
|
||||
|
||||
Stopwatch sw = Stopwatch.StartNew();
|
||||
|
||||
foreach (var thread in threads)
|
||||
foreach (Thread thread in threads)
|
||||
{
|
||||
thread.Start();
|
||||
}
|
||||
foreach (var thread in threads)
|
||||
foreach (Thread thread in threads)
|
||||
{
|
||||
thread.Join();
|
||||
}
|
||||
@@ -991,7 +949,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
WriteCode(code.AsSpan());
|
||||
|
||||
// WriteReloc.
|
||||
using var relocInfoWriter = new BinaryWriter(_relocsStream, EncodingCache.UTF8NoBOM, true);
|
||||
using BinaryWriter relocInfoWriter = new(_relocsStream, EncodingCache.UTF8NoBOM, true);
|
||||
|
||||
foreach (RelocEntry entry in relocInfo.Entries)
|
||||
{
|
||||
@@ -1001,7 +959,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
}
|
||||
|
||||
// WriteUnwindInfo.
|
||||
using var unwindInfoWriter = new BinaryWriter(_unwindInfosStream, EncodingCache.UTF8NoBOM, true);
|
||||
using BinaryWriter unwindInfoWriter = new(_unwindInfosStream, EncodingCache.UTF8NoBOM, true);
|
||||
|
||||
unwindInfoWriter.Write(unwindInfo.PushEntries.Length);
|
||||
|
||||
@@ -1166,8 +1124,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
|
||||
public void Close()
|
||||
{
|
||||
if (State == PtcState.Enabled ||
|
||||
State == PtcState.Continuing)
|
||||
if (State is PtcState.Enabled or PtcState.Continuing)
|
||||
{
|
||||
State = PtcState.Closing;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
namespace ARMeilleure.Translation.PTC
|
||||
{
|
||||
public readonly struct PtcCacheInfo
|
||||
{
|
||||
public const string TitleIdTextDefault = "0000000000000000";
|
||||
public const string ApplicationIdTextDefault = "0000000000000000";
|
||||
public const string DisplayVersionDefault = "0";
|
||||
|
||||
public ulong ProcessId { get; }
|
||||
public string TitleIdText { get; }
|
||||
public string ApplicationIdText { get; }
|
||||
public byte ProgramIndex { get; }
|
||||
public string DisplayVersion { get; }
|
||||
public string ProcessKind { get; }
|
||||
public string CacheSelector { get; }
|
||||
|
||||
public string CacheKey => $"{DisplayVersion}-{CacheSelector}";
|
||||
|
||||
public PtcCacheInfo(
|
||||
ulong processId,
|
||||
string titleIdText,
|
||||
string applicationIdText,
|
||||
byte programIndex,
|
||||
string displayVersion,
|
||||
string processKind,
|
||||
string cacheSelector)
|
||||
{
|
||||
ProcessId = processId;
|
||||
TitleIdText = !string.IsNullOrEmpty(titleIdText) ? titleIdText : TitleIdTextDefault;
|
||||
ApplicationIdText = !string.IsNullOrEmpty(applicationIdText) ? applicationIdText : ApplicationIdTextDefault;
|
||||
ProgramIndex = programIndex;
|
||||
DisplayVersion = !string.IsNullOrEmpty(displayVersion) ? displayVersion : DisplayVersionDefault;
|
||||
ProcessKind = processKind ?? string.Empty;
|
||||
CacheSelector = string.IsNullOrEmpty(cacheSelector) ? "default" : cacheSelector;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 = 6698; //! Not to be incremented manually for each change to the ARMeilleure project.
|
||||
private const uint InternalVersion = 7031; //! Not to be incremented manually for each change to the ARMeilleure project.
|
||||
|
||||
private static readonly uint[] _migrateInternalVersions =
|
||||
[
|
||||
@@ -119,9 +119,9 @@ namespace ARMeilleure.Translation.PTC
|
||||
|
||||
public ConcurrentQueue<(ulong address, FuncProfile funcProfile)> GetProfiledFuncsToTranslate(TranslatorCache<TranslatedFunction> funcs)
|
||||
{
|
||||
var profiledFuncsToTranslate = new ConcurrentQueue<(ulong address, FuncProfile funcProfile)>();
|
||||
ConcurrentQueue<(ulong address, FuncProfile funcProfile)> profiledFuncsToTranslate = new();
|
||||
|
||||
foreach (var profiledFunc in ProfiledFuncs)
|
||||
foreach (KeyValuePair<ulong, FuncProfile> profiledFunc in ProfiledFuncs)
|
||||
{
|
||||
if (!funcs.ContainsKey(profiledFunc.Key) && !profiledFunc.Value.Blacklist)
|
||||
{
|
||||
@@ -142,7 +142,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
{
|
||||
List<ulong> funcs = [];
|
||||
|
||||
foreach (var profiledFunc in ProfiledFuncs)
|
||||
foreach (KeyValuePair<ulong, FuncProfile> profiledFunc in ProfiledFuncs)
|
||||
{
|
||||
if (profiledFunc.Value.Blacklist)
|
||||
{
|
||||
@@ -189,28 +189,10 @@ namespace ARMeilleure.Translation.PTC
|
||||
{
|
||||
OuterHeader outerHeader = DeserializeStructure<OuterHeader>(compressedStream);
|
||||
|
||||
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())
|
||||
if (!outerHeader.IsHeaderValid() ||
|
||||
outerHeader.Magic != _outerHeaderMagic ||
|
||||
outerHeader.InfoFileVersion != InternalVersion && !_migrateInternalVersions.Contains(outerHeader.InfoFileVersion) ||
|
||||
outerHeader.Endianness != Ptc.GetEndianness())
|
||||
{
|
||||
InvalidateCompressedStream(compressedStream);
|
||||
|
||||
@@ -272,7 +254,12 @@ namespace ARMeilleure.Translation.PTC
|
||||
|
||||
long fileSize = new FileInfo(fileName).Length;
|
||||
|
||||
Logger.Info?.Print(LogClass.Ptc, $"{(isBackup ? "Loaded Backup Profiling Info" : "Loaded Profiling Info")} (size: {fileSize} bytes, profiled functions: {ProfiledFuncs.Count}).");
|
||||
Logger.Info?.Print(
|
||||
LogClass.Ptc,
|
||||
$"{(isBackup ? "Loaded Backup Profiling Info" : "Loaded Profiling Info")} " +
|
||||
$"(pid: {_ptc.CacheInfo.ProcessId}, title: {_ptc.CacheInfo.TitleIdText}, version: '{_ptc.CacheInfo.DisplayVersion}', " +
|
||||
$"kind: {_ptc.CacheInfo.ProcessKind}, selector: '{_ptc.CacheInfo.CacheSelector}', key: '{_ptc.CacheInfo.CacheKey}', " +
|
||||
$"path: '{fileName}', size: {fileSize} bytes, profiled functions: {ProfiledFuncs.Count}).");
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -393,7 +380,11 @@ namespace ARMeilleure.Translation.PTC
|
||||
|
||||
if (fileSize != 0L)
|
||||
{
|
||||
Logger.Info?.Print(LogClass.Ptc, $"Saved Profiling Info (size: {fileSize} bytes, profiled functions: {profiledFuncsCount}).");
|
||||
Logger.Info?.Print(
|
||||
LogClass.Ptc,
|
||||
$"Saved Profiling Info (pid: {_ptc.CacheInfo.ProcessId}, title: {_ptc.CacheInfo.TitleIdText}, version: '{_ptc.CacheInfo.DisplayVersion}', " +
|
||||
$"kind: {_ptc.CacheInfo.ProcessKind}, selector: '{_ptc.CacheInfo.CacheSelector}', key: '{_ptc.CacheInfo.CacheKey}', " +
|
||||
$"path: '{fileName}', size: {fileSize} bytes, profiled functions: {profiledFuncsCount}).");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -465,8 +456,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
|
||||
public void Start()
|
||||
{
|
||||
if (_ptc.State == PtcState.Enabled ||
|
||||
_ptc.State == PtcState.Continuing)
|
||||
if (_ptc.State is PtcState.Enabled or PtcState.Continuing)
|
||||
{
|
||||
Enabled = true;
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ namespace ARMeilleure.Translation
|
||||
// This is required because we have a implicit context load at the start of the function,
|
||||
// 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 BasicBlock(cfg.Blocks.Count);
|
||||
BasicBlock newEntry = new(cfg.Blocks.Count);
|
||||
|
||||
cfg.UpdateEntry(newEntry);
|
||||
}
|
||||
|
||||
@@ -44,10 +44,10 @@ namespace ARMeilleure.Translation
|
||||
|
||||
public static void Construct(ControlFlowGraph cfg)
|
||||
{
|
||||
var globalDefs = new DefMap[cfg.Blocks.Count];
|
||||
var localDefs = new Operand[cfg.LocalsCount + RegisterConsts.TotalCount];
|
||||
DefMap[] globalDefs = new DefMap[cfg.Blocks.Count];
|
||||
Operand[] localDefs = new Operand[cfg.LocalsCount + RegisterConsts.TotalCount];
|
||||
|
||||
var dfPhiBlocks = new Queue<BasicBlock>();
|
||||
Queue<BasicBlock> dfPhiBlocks = new();
|
||||
|
||||
for (BasicBlock block = cfg.Blocks.First; block != null; block = block.ListNext)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using ARMeilleure.Common;
|
||||
using System;
|
||||
|
||||
namespace ARMeilleure.Translation
|
||||
{
|
||||
@@ -7,12 +6,12 @@ namespace ARMeilleure.Translation
|
||||
{
|
||||
private readonly GuestFunction _func; // Ensure that this delegate will not be garbage collected.
|
||||
|
||||
public IntPtr FuncPointer { get; }
|
||||
public nint FuncPointer { get; }
|
||||
public Counter<uint> CallCounter { get; }
|
||||
public ulong GuestSize { get; }
|
||||
public bool HighCq { get; }
|
||||
|
||||
public TranslatedFunction(GuestFunction func, IntPtr funcPointer, Counter<uint> callCounter, ulong guestSize, bool highCq)
|
||||
public TranslatedFunction(GuestFunction func, nint funcPointer, Counter<uint> callCounter, ulong guestSize, bool highCq)
|
||||
{
|
||||
_func = func;
|
||||
FuncPointer = funcPointer;
|
||||
|
||||
@@ -57,9 +57,9 @@ namespace ARMeilleure.Translation
|
||||
FunctionTable.Fill = (ulong)Stubs.SlowDispatchStub;
|
||||
}
|
||||
|
||||
public IPtcLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled, string cacheSelector)
|
||||
public IPtcLoadState LoadDiskCache(PtcCacheInfo cacheInfo, bool enabled)
|
||||
{
|
||||
_ptc.Initialize(titleIdText, displayVersion, enabled, Memory.Type, cacheSelector);
|
||||
_ptc.Initialize(cacheInfo, enabled, Memory.Type);
|
||||
return _ptc;
|
||||
}
|
||||
|
||||
@@ -168,6 +168,7 @@ namespace ARMeilleure.Translation
|
||||
|
||||
Statistics.StartTimer();
|
||||
|
||||
context.ResetCallDepth();
|
||||
ulong nextAddr = func.Execute(Stubs.ContextWrapper, context);
|
||||
|
||||
Statistics.StopTimer(address);
|
||||
@@ -221,7 +222,7 @@ namespace ARMeilleure.Translation
|
||||
|
||||
internal TranslatedFunction Translate(ulong address, ExecutionMode mode, bool highCq, bool singleStep = false, bool pptcTranslation = false)
|
||||
{
|
||||
var context = new ArmEmitterContext(
|
||||
ArmEmitterContext context = new(
|
||||
Memory,
|
||||
CountTable,
|
||||
FunctionTable,
|
||||
@@ -239,6 +240,7 @@ namespace ARMeilleure.Translation
|
||||
|
||||
Logger.StartPass(PassName.Translation);
|
||||
|
||||
InstEmitFlowHelper.EmitCallDepthCheckAndIncrement(context, Const(address));
|
||||
EmitSynchronization(context);
|
||||
|
||||
if (blocks[0].Address != address)
|
||||
@@ -263,10 +265,10 @@ namespace ARMeilleure.Translation
|
||||
|
||||
Logger.EndPass(PassName.RegisterUsage);
|
||||
|
||||
var retType = OperandType.I64;
|
||||
var argTypes = new OperandType[] { OperandType.I64 };
|
||||
OperandType retType = OperandType.I64;
|
||||
OperandType[] argTypes = new OperandType[] { OperandType.I64 };
|
||||
|
||||
var options = highCq ? CompilerOptions.HighCq : CompilerOptions.None;
|
||||
CompilerOptions options = highCq ? CompilerOptions.HighCq : CompilerOptions.None;
|
||||
|
||||
if (context.HasPtc && !singleStep)
|
||||
{
|
||||
@@ -282,7 +284,7 @@ namespace ARMeilleure.Translation
|
||||
_ptc.WriteCompiledFunction(address, funcSize, hash, highCq, compiledFunc);
|
||||
}
|
||||
|
||||
GuestFunction func = compiledFunc.MapWithPointer<GuestFunction>(out IntPtr funcPointer);
|
||||
GuestFunction func = compiledFunc.MapWithPointer<GuestFunction>(out nint funcPointer);
|
||||
|
||||
Allocators.ResetAll();
|
||||
|
||||
@@ -534,7 +536,7 @@ namespace ARMeilleure.Translation
|
||||
|
||||
List<TranslatedFunction> functions = Functions.AsList();
|
||||
|
||||
foreach (var func in functions)
|
||||
foreach (TranslatedFunction func in functions)
|
||||
{
|
||||
JitCache.Unmap(func.FuncPointer);
|
||||
|
||||
@@ -543,7 +545,7 @@ namespace ARMeilleure.Translation
|
||||
|
||||
Functions.Clear();
|
||||
|
||||
while (_oldFuncs.TryDequeue(out var kv))
|
||||
while (_oldFuncs.TryDequeue(out KeyValuePair<ulong, TranslatedFunction> kv))
|
||||
{
|
||||
JitCache.Unmap(kv.Value.FuncPointer);
|
||||
|
||||
@@ -564,7 +566,7 @@ namespace ARMeilleure.Translation
|
||||
{
|
||||
while (Queue.Count > 0 && Queue.TryDequeue(out RejitRequest request))
|
||||
{
|
||||
if (Functions.TryGetValue(request.Address, out var func) && func.CallCounter != null)
|
||||
if (Functions.TryGetValue(request.Address, out TranslatedFunction func) && func.CallCounter != null)
|
||||
{
|
||||
Volatile.Write(ref func.CallCounter.Value, 0);
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace ARMeilleure.Translation
|
||||
/// </summary>
|
||||
class TranslatorStubs : IDisposable
|
||||
{
|
||||
private readonly Lazy<IntPtr> _slowDispatchStub;
|
||||
private readonly Lazy<nint> _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 IntPtr DispatchStub
|
||||
public nint 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 IntPtr SlowDispatchStub
|
||||
public nint SlowDispatchStub
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -139,9 +139,9 @@ namespace ARMeilleure.Translation
|
||||
/// Generates a <see cref="DispatchStub"/>.
|
||||
/// </summary>
|
||||
/// <returns>Generated <see cref="DispatchStub"/></returns>
|
||||
private IntPtr GenerateDispatchStub()
|
||||
private nint GenerateDispatchStub()
|
||||
{
|
||||
var context = new EmitterContext();
|
||||
EmitterContext context = new();
|
||||
|
||||
Operand lblFallback = Label();
|
||||
Operand lblEnd = Label();
|
||||
@@ -160,7 +160,7 @@ namespace ARMeilleure.Translation
|
||||
|
||||
for (int i = 0; i < _functionTable.Levels.Length; i++)
|
||||
{
|
||||
ref var level = ref _functionTable.Levels[i];
|
||||
ref AddressTableLevel level = ref _functionTable.Levels[i];
|
||||
|
||||
// level.Mask is not used directly because it is more often bigger than 32-bits, so it will not
|
||||
// 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);
|
||||
|
||||
var cfg = context.GetControlFlowGraph();
|
||||
var retType = OperandType.I64;
|
||||
var argTypes = new[] { OperandType.I64 };
|
||||
ControlFlowGraph cfg = context.GetControlFlowGraph();
|
||||
OperandType retType = OperandType.I64;
|
||||
OperandType[] argTypes = new[] { OperandType.I64 };
|
||||
|
||||
var func = Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<GuestFunction>();
|
||||
GuestFunction func = Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<GuestFunction>();
|
||||
|
||||
return Marshal.GetFunctionPointerForDelegate(func);
|
||||
}
|
||||
@@ -197,9 +197,9 @@ namespace ARMeilleure.Translation
|
||||
/// Generates a <see cref="SlowDispatchStub"/>.
|
||||
/// </summary>
|
||||
/// <returns>Generated <see cref="SlowDispatchStub"/></returns>
|
||||
private IntPtr GenerateSlowDispatchStub()
|
||||
private nint GenerateSlowDispatchStub()
|
||||
{
|
||||
var context = new EmitterContext();
|
||||
EmitterContext context = new();
|
||||
|
||||
// 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);
|
||||
|
||||
var cfg = context.GetControlFlowGraph();
|
||||
var retType = OperandType.I64;
|
||||
var argTypes = new[] { OperandType.I64 };
|
||||
ControlFlowGraph cfg = context.GetControlFlowGraph();
|
||||
OperandType retType = OperandType.I64;
|
||||
OperandType[] argTypes = new[] { OperandType.I64 };
|
||||
|
||||
var func = Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<GuestFunction>();
|
||||
GuestFunction func = Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<GuestFunction>();
|
||||
|
||||
return Marshal.GetFunctionPointerForDelegate(func);
|
||||
}
|
||||
@@ -250,7 +250,7 @@ namespace ARMeilleure.Translation
|
||||
/// <returns><see cref="DispatchLoop"/> function</returns>
|
||||
private DispatcherFunction GenerateDispatchLoop()
|
||||
{
|
||||
var context = new EmitterContext();
|
||||
EmitterContext context = new();
|
||||
|
||||
Operand beginLbl = Label();
|
||||
Operand endLbl = Label();
|
||||
@@ -262,10 +262,18 @@ 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);
|
||||
@@ -278,9 +286,9 @@ namespace ARMeilleure.Translation
|
||||
|
||||
context.Return();
|
||||
|
||||
var cfg = context.GetControlFlowGraph();
|
||||
var retType = OperandType.None;
|
||||
var argTypes = new[] { OperandType.I64, OperandType.I64 };
|
||||
ControlFlowGraph cfg = context.GetControlFlowGraph();
|
||||
OperandType retType = OperandType.None;
|
||||
OperandType[] argTypes = new[] { OperandType.I64, OperandType.I64 };
|
||||
|
||||
return Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<DispatcherFunction>();
|
||||
}
|
||||
@@ -291,7 +299,7 @@ namespace ARMeilleure.Translation
|
||||
/// <returns><see cref="ContextWrapper"/> function</returns>
|
||||
private WrapperFunction GenerateContextWrapper()
|
||||
{
|
||||
var context = new EmitterContext();
|
||||
EmitterContext context = new();
|
||||
|
||||
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
|
||||
Operand guestMethod = context.LoadArgument(OperandType.I64, 1);
|
||||
@@ -302,9 +310,9 @@ namespace ARMeilleure.Translation
|
||||
|
||||
context.Return(returnValue);
|
||||
|
||||
var cfg = context.GetControlFlowGraph();
|
||||
var retType = OperandType.I64;
|
||||
var argTypes = new[] { OperandType.I64, OperandType.I64 };
|
||||
ControlFlowGraph cfg = context.GetControlFlowGraph();
|
||||
OperandType retType = OperandType.I64;
|
||||
OperandType[] 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(IntPtr managedMethod);
|
||||
public delegate int FpFlagsPInvokeTest(nint managedMethod);
|
||||
|
||||
private static bool SetPlatformFtz(EmitterContext context, bool ftz)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
.idea/
|
||||
*.iml
|
||||
.gradle
|
||||
local.properties
|
||||
.DS_Store
|
||||
build/
|
||||
captures
|
||||
.externalNativeBuild
|
||||
.cxx/
|
||||
|
||||
app/src/main/jniLibs/arm64-v8a/**
|
||||
!app/src/main/jniLibs/arm64-v8a/.gitkeep
|
||||
@@ -0,0 +1,175 @@
|
||||
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
@@ -0,0 +1,37 @@
|
||||
-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*();
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
<?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>
|
||||
@@ -0,0 +1,80 @@
|
||||
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}
|
||||
)
|
||||
@@ -0,0 +1,254 @@
|
||||
// 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;
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
//
|
||||
// Created by Emmanuel Hansen on 6/19/2023.
|
||||
//
|
||||
|
||||
#ifndef KENJINXNATIVE_KENJINX_H
|
||||
#define KENJINXNATIVE_KENJINX_H
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <dlfcn.h>
|
||||
#include <exception>
|
||||
#include <fcntl.h>
|
||||
#include <jni.h>
|
||||
#include <string>
|
||||
|
||||
#include <android/log.h>
|
||||
#include <android/native_window.h>
|
||||
#include <android/native_window_jni.h>
|
||||
|
||||
#include "vulkan_wrapper.h"
|
||||
#include <vulkan/vulkan_android.h>
|
||||
#include "adrenotools/driver.h"
|
||||
#include "native_window.h"
|
||||
|
||||
// Android log function wrappers
|
||||
static const char* TAG = "Kenjinx";
|
||||
#define LOGI(...) \
|
||||
((void)__android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__))
|
||||
#define LOGW(...) \
|
||||
((void)__android_log_print(ANDROID_LOG_WARN, TAG, __VA_ARGS__))
|
||||
#define LOGE(...) \
|
||||
((void)__android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__))
|
||||
|
||||
// A macro to pass call to Vulkan and check for return value for success
|
||||
#define CALL_VK(func) \
|
||||
if (VK_SUCCESS != (func)) { \
|
||||
__android_log_print(ANDROID_LOG_ERROR, "Tutorial ", \
|
||||
"Vulkan error. File[%s], line[%d]", __FILE__, \
|
||||
__LINE__); \
|
||||
assert(false); \
|
||||
}
|
||||
|
||||
// A macro to check value is VK_SUCCESS
|
||||
// Used also for non-vulkan functions but return VK_SUCCESS
|
||||
#define VK_CHECK(x) CALL_VK(x)
|
||||
|
||||
#define LoadLib(a) dlopen(a, RTLD_NOW)
|
||||
|
||||
void *_kenjinxNative = nullptr;
|
||||
|
||||
// Kenjinx imported functions
|
||||
bool (*initialize)(char *) = nullptr;
|
||||
|
||||
long _renderingThreadId = 0;
|
||||
JavaVM *_vm = nullptr;
|
||||
jobject _mainActivity = nullptr;
|
||||
jclass _mainActivityClass = nullptr;
|
||||
|
||||
#endif //KENJINXNATIVE_KENJINX_H
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user